@rr0/ufoathome 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-embed/{UfoRecorderMessages_fr-CWzFiWgC.js → UfoRecorderMessages_fr-DMDdf4N-.js} +1 -1
- package/dist-embed/rr0-ufo-recorder.mjs +612 -201
- package/dist-embed-eyewitness/rr0-eyewitness.mjs +581 -188
- package/dist-embed-scene/rr0-scene.mjs +579 -186
- package/dist-embed-ufo/rr0-ufo.mjs +3 -3
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const $=`
|
|
2
2
|
<div class="stage" id="stage">
|
|
3
3
|
<div class="frame" id="frame">
|
|
4
4
|
<canvas id="canvas" width="640" height="360"></canvas>
|
|
@@ -13,7 +13,7 @@ const z=`
|
|
|
13
13
|
<button id="loop" type="button" title="Auto-replay" aria-label="Auto-replay" aria-pressed="true">↻</button>
|
|
14
14
|
</div>
|
|
15
15
|
</div>
|
|
16
|
-
`,
|
|
16
|
+
`,J=`
|
|
17
17
|
:host {
|
|
18
18
|
display: block;
|
|
19
19
|
font-family: sans-serif;
|
|
@@ -164,4 +164,4 @@ input[type=range] {
|
|
|
164
164
|
pointer-events: none;
|
|
165
165
|
white-space: nowrap;
|
|
166
166
|
}
|
|
167
|
-
`;function $(i,e,t){const{bounds:s}=i;return e>=s.x&&e<=s.x+s.width&&t>=s.y&&t<=s.y+s.height}function f(i,e,t){return i+(e-i)*t}function D(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 J(i){return`#${i.map(e=>Math.round(e).toString(16).padStart(2,"0")).join("")}`}function K(i,e,t){const s=D(i),n=D(e);return!s||!n?t<1?i:e:J([f(s[0],n[0],t),f(s[1],n[1],t),f(s[2],n[2],t)])}function G(i,e,t){const s={x:f(i.bounds.x,e.bounds.x,t),y:f(i.bounds.y,e.bounds.y,t),width:f(i.bounds.width,e.bounds.width,t),height:f(i.bounds.height,e.bounds.height,t)},n=f(i.angle,e.angle,t),a=f(i.transparency,e.transparency,t),r=f(i.haloScale,e.haloScale,t),o=K(i.color,e.color,t);return i.kind==="polygon"&&e.kind==="polygon"&&i.points.length===e.points.length?{...i,bounds:s,angle:n,transparency:a,haloScale:r,color:o,points:i.points.map((l,h)=>({x:f(l.x,e.points[h].x,t),y:f(l.y,e.points[h].y,t)}))}:{...t<1?i:e,bounds:s,angle:n,transparency:a,haloScale:r,color:o}}class E{keyframes=[];order=[];groups=[];addKeyframe(e,t){const s=this.findInsertIndex(e);if(this.keyframes[s]?.t===e){const n=new Set(t.map(a=>a.sourceId));this.keyframes[s]={t:e,shapes:[...this.keyframes[s].shapes.filter(a=>!n.has(a.sourceId)),...t]}}else this.keyframes.splice(s,0,{t:e,shapes:[...t]});for(const n of t)this.order.includes(n.sourceId)||this.order.push(n.sourceId)}findInsertIndex(e){let t=0,s=this.keyframes.length;for(;t<s;){const n=t+s>>>1;this.keyframes[n].t<e?t=n+1:s=n}return t}getKeyframeAt(e){const t=this.findInsertIndex(e),s=this.keyframes[t];return s?.t===e?s:void 0}getShapeAt(e,t){return this.getKeyframeAt(e)?.shapes.find(s=>s.sourceId===t)?.shape}getLatestShapeAt(e,t){let s=this.findInsertIndex(e);for(this.keyframes[s]?.t!==e&&(s-=1);s>=0;s--){const n=this.keyframes[s].shapes.find(a=>a.sourceId===t);if(n)return n.shape}}getInterpolatedShapeAt(e,t){const s=this.findShapeAtOrBefore(e,t);if(s?.t===e)return s.shape;const n=this.findShapeAtOrAfter(e,t);return s?n?G(s.shape,n.shape,(e-s.t)/(n.t-s.t)):s.shape:n?.shape}findShapeAtOrBefore(e,t){let s=this.findInsertIndex(e);for(this.keyframes[s]?.t!==e&&(s-=1);s>=0;s--){const n=this.keyframes[s].shapes.find(a=>a.sourceId===t);if(n)return{t:this.keyframes[s].t,shape:n.shape}}}findShapeAtOrAfter(e,t){for(let s=this.findInsertIndex(e);s<this.keyframes.length;s++){const n=this.keyframes[s].shapes.find(a=>a.sourceId===t);if(n)return{t:this.keyframes[s].t,shape:n.shape}}}hitTest(e,t,s){const n=this.sourceIds;for(let a=n.length-1;a>=0;a--){const r=this.getInterpolatedShapeAt(e,n[a]);if(r&&$(r,t,s))return{sourceId:n[a],shape:r}}}removeSource(e){for(let t=this.keyframes.length-1;t>=0;t--){const s=this.keyframes[t].shapes.filter(n=>n.sourceId!==e);s.length===0?this.keyframes.splice(t,1):s.length!==this.keyframes[t].shapes.length&&(this.keyframes[t]={t:this.keyframes[t].t,shapes:s})}this.order=this.order.filter(t=>t!==e),this.removeFromGroup(e)}group(e){if(!(e.length<2)){for(const t of e)this.removeFromGroup(t);this.groups.push([...e])}}ungroup(e){const t=this.groups.findIndex(s=>s.includes(e));t!==-1&&this.groups.splice(t,1)}groupMembers(e){return this.groups.find(t=>t.includes(e))}removeFromGroup(e){const t=this.groups.findIndex(n=>n.includes(e));if(t===-1)return;const s=this.groups[t].filter(n=>n!==e);s.length<2?this.groups.splice(t,1):this.groups[t]=s}bringToFront(e){const t=this.order.indexOf(e);t!==-1&&(this.order.splice(t,1),this.order.push(e))}sendToBack(e){const t=this.order.indexOf(e);t!==-1&&(this.order.splice(t,1),this.order.unshift(e))}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get sourceIds(){return[...this.order]}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes,order:this.order,groups:this.groups}}static fromJSON(e){const t=new E;for(const s of e.keyframes)t.addKeyframe(s.t,s.shapes);if(e.order){const s=t.order.filter(n=>!e.order.includes(n));t.order=[...e.order,...s]}return e.groups&&(t.groups=e.groups.map(s=>s.filter(n=>t.order.includes(n))).filter(s=>s.length>=2)),t}}function q(i,e,t){return Math.max(e,Math.min(t,i))}function W(i,e,t){const s=((e-i)%360+540)%360-180;return((i+s*t)%360+360)%360}function v(i,e,t){return i+(e-i)*t}function Y(i,e,t){return{lat:i.lat===void 0||e.lat===void 0?void 0:v(i.lat,e.lat,t),lng:i.lng===void 0||e.lng===void 0?void 0:v(i.lng,e.lng,t),elevationM:v(i.elevationM,e.elevationM,t),headingDeg:i.headingDeg===void 0||e.headingDeg===void 0?void 0:W(i.headingDeg,e.headingDeg,t),pitchDeg:v(i.pitchDeg,e.pitchDeg,t),fovDeg:v(i.fovDeg,e.fovDeg,t)}}class b{keyframes=[];addKeyframe(e,t){const s=this.findInsertIndex(e);this.keyframes[s]?.t===e?this.keyframes[s]={t:e,pose:t}:this.keyframes.splice(s,0,{t:e,pose:t})}clear(){this.keyframes.length=0}removeKeyframeAt(e){const t=this.findInsertIndex(e);this.keyframes[t]?.t===e&&this.keyframes.splice(t,1)}findInsertIndex(e){let t=0,s=this.keyframes.length;for(;t<s;){const n=t+s>>>1;this.keyframes[n].t<e?t=n+1:s=n}return t}getLatestPoseAt(e){let t=this.findInsertIndex(e);return this.keyframes[t]?.t!==e&&(t-=1),t>=0?this.keyframes[t].pose:void 0}getInterpolatedPoseAt(e){const t=this.findInsertIndex(e),s=this.keyframes[t]?.t===e?this.keyframes[t]:this.keyframes[t-1];if(s?.t===e)return s.pose;const n=this.keyframes[t]?.t===e?void 0:this.keyframes[t];return s?n?Y(s.pose,n.pose,q((e-s.t)/(n.t-s.t),0,1)):s.pose:n?.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 b;for(const s of e.keyframes)t.addKeyframe(s.t,s.pose);return t}}function P(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 F(i){return(((i.day??0)*24+(i.hour??0))*60+(i.minute??0))*6e4+(i.second??0)*1e3}const Z=["year","month","day","hour","minute"],X=["day","hour","minute"];function B(i,e){return e.filter(t=>i[t]!==void 0).join(",")}function Q(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?Z:X,s=B(i.time,t);if(!(s===""||s!==B(i.endTime,t)))return e?P(i.endTime)-P(i.time):F(i.endTime)-F(i.time)}class T{constructor(e,t,s,n,a,r){this.event=e,this.timeline=t,this.witnessTrack=s,this.witness=n,this.caseId=a,this.weather=r}static create(e,t,s){return new T({eventType:"sighting",time:e,place:t},new E,new b,s)}}class V{constructor(e,t){this.timeline=e,this.onFrame=t}rafId=null;state="stopped";currentT=0;lastWallTime=0;playbackRate=1;loop=!1;durationOverrideMs=0;get seekableDuration(){return Math.max(this.timeline.duration,this.durationOverrideMs)}play(){if(this.state==="playing")return;this.currentT>=this.seekableDuration&&(this.currentT=0,this.resolveFrame(0)),this.state="playing",this.lastWallTime=performance.now();const e=()=>{if(this.state!=="playing")return;const t=performance.now();if(this.currentT+=(t-this.lastWallTime)*this.playbackRate,this.lastWallTime=t,this.currentT>=this.seekableDuration){if(this.loop&&this.seekableDuration>0){this.currentT%=this.seekableDuration,this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e);return}this.currentT=this.seekableDuration,this.stop(),this.resolveFrame(this.currentT);return}this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e)};this.rafId=requestAnimationFrame(e)}pause(){this.state==="playing"&&(this.state="paused",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null))}stop(){this.state="stopped",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}seek(e){this.currentT=Math.max(0,Math.min(e,this.seekableDuration)),this.resolveFrame(this.currentT)}get playbackState(){return this.state}get time(){return this.currentT}resolveFrame(e){const t=new Map;for(const s of this.timeline.sourceIds){const n=this.timeline.getInterpolatedShapeAt(e,s);n&&t.set(s,n)}this.onFrame(e,t)}}const N=["nw","n","ne","e","se","s","sw","w"],j=24,w=8;function L(i,e,t){const s=i.x-e.x,n=i.y-e.y,a=Math.cos(t),r=Math.sin(t);return{x:e.x+s*a-n*r,y:e.y+s*r+n*a}}function I(i){return{x:i.x+i.width/2,y:i.y+i.height/2}}const ee={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 m{static handlePointsFor(e){const{x:t,y:s,width:n,height:a}=e.bounds,r={nw:{x:t,y:s},n:{x:t+n/2,y:s},ne:{x:t+n,y:s},e:{x:t+n,y:s+a/2},se:{x:t+n,y:s+a},s:{x:t+n/2,y:s+a},sw:{x:t,y:s+a},w:{x:t,y:s+a/2},rotate:{x:t+n/2,y:s-j}},o=I(e.bounds),l={};for(const h of Object.keys(r))l[h]=L(r[h],o,e.angle);return l}static hitTestHandle(e,t,s=8,n=[...N,"rotate"]){const a=m.handlePointsFor(e);for(const r of n)if(Math.hypot(t.x-a[r].x,t.y-a[r].y)<=s)return r}static resizeBounds(e,t,s,n){const a=I(e),r=L(n,a,-t),{x:o,y:l,width:h,height:c}=e,u=ee[s];let p=o,y=l,d=o+h,g=l+c;return u.left&&(p=Math.min(r.x,d-w)),u.right&&(d=Math.max(r.x,p+w)),u.top&&(y=Math.min(r.y,g-w)),u.bottom&&(g=Math.max(r.y,y+w)),{x:p,y,width:d-p,height:g-y}}static resizeShape(e,t,s){if(t==="rotate")throw new Error("resizeShape does not accept the rotate handle");const{width:n,height:a}=e.bounds,r=m.resizeBounds(e.bounds,e.angle,t,s);if(e.kind==="oval")return{...e,bounds:r};const o=n===0?1:r.width/n,l=a===0?1:r.height/a;return{...e,bounds:r,points:e.points.map(h=>({x:h.x*o,y:h.y*l}))}}static rotateShape(e,t){const s=I(e.bounds),n=Math.atan2(t.y-s.y,t.x-s.x)+Math.PI/2;return{...e,angle:n}}static groupBoundsFor(e){const t=Math.min(...e.map(r=>r.x)),s=Math.min(...e.map(r=>r.y)),n=Math.max(...e.map(r=>r.x+r.width)),a=Math.max(...e.map(r=>r.y+r.height));return{x:t,y:s,width:n-t,height:a-s}}}const M=6,k=M/2,te=20;class se{constructor(e){this.ctx=e}clear(e,t){this.ctx.clearRect(0,0,e,t)}paintShape(e){this.ctx.save(),this.ctx.globalAlpha=1-e.transparency,e.haloScale>0&&this.paintHalo(e),this.paintBase(e),this.ctx.restore(),e.selected&&this.paintSelectionHandles(e)}paintBase(e){if(this.ctx.fillStyle=e.color,this.ctx.beginPath(),e.kind==="oval"){const{x:t,y:s,width:n,height:a}=e.bounds,r=n/2,o=a/2;this.ctx.ellipse(t+r,s+o,r,o,e.angle,0,2*Math.PI)}else{const{x:t,y:s,width:n,height:a}=e.bounds;this.ctx.save(),this.ctx.translate(t+n/2,s+a/2),this.ctx.rotate(e.angle),this.ctx.translate(-n/2,-a/2),e.points.forEach((r,o)=>{o===0?this.ctx.moveTo(r.x,r.y):this.ctx.lineTo(r.x,r.y)}),this.ctx.closePath(),this.ctx.restore()}this.ctx.fill()}paintHalo(e){this.ctx.save(),this.ctx.shadowColor=e.color,this.ctx.shadowBlur=te*e.haloScale,this.paintBase(e),this.ctx.restore()}paintSelectionHandles(e){this.paintHandleFrame(m.handlePointsFor(e),{includeRotate:!0})}paintMemberOutline(e){this.paintOutline(m.handlePointsFor(e))}paintGroupHandles(e){this.paintHandleFrame(m.handlePointsFor({bounds:e,angle:0}),{includeRotate:!0})}paintOutline(e){this.ctx.strokeStyle="lightgray",this.ctx.beginPath(),this.ctx.moveTo(e.nw.x,e.nw.y),this.ctx.lineTo(e.ne.x,e.ne.y),this.ctx.lineTo(e.se.x,e.se.y),this.ctx.lineTo(e.sw.x,e.sw.y),this.ctx.closePath(),this.ctx.stroke()}paintHandleFrame(e,{includeRotate:t}){this.paintOutline(e),this.ctx.fillStyle="lightgray";for(const s of N){const n=e[s];this.ctx.fillRect(n.x-k,n.y-k,M,M)}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,k+1,k+1,0,0,2*Math.PI),this.ctx.fill())}}function ie(i){return{version:1,time:i.event.time,endTime:i.event.endTime,durationSeconds:i.event.durationSeconds,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(),weather:i.weather}}function ne(i){return new T({eventType:"sighting",time:i.time,endTime:i.endTime,durationSeconds:i.durationSeconds,place:i.place,description:i.description,tags:i.tags},E.fromJSON(i.timeline),i.witnessTrack?b.fromJSON(i.witnessTrack):new b,i.witness,i.caseId,i.weather)}function re(i,e){for(const t of i){const s=t.toLowerCase().split("-")[0];if(e.includes(s))return s}return"en"}const ae="modulepreload",oe=function(i,e){return new URL(i,e).href},A={},C=function(e,t,s){let n=Promise.resolve();if(t&&t.length>0){let r=function(c){return Promise.all(c.map(u=>Promise.resolve(u).then(p=>({status:"fulfilled",value:p}),p=>({status:"rejected",reason:p}))))};const o=document.getElementsByTagName("link"),l=document.querySelector("meta[property=csp-nonce]"),h=l?.nonce||l?.getAttribute("nonce");n=r(t.map(c=>{if(c=oe(c,s),c in A)return;A[c]=!0;const u=c.endsWith(".css"),p=u?'[rel="stylesheet"]':"";if(!!s)for(let g=o.length-1;g>=0;g--){const x=o[g];if(x.href===c&&(!u||x.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${c}"]${p}`))return;const d=document.createElement("link");if(d.rel=u?"stylesheet":ae,u||(d.as="script"),d.crossOrigin="",d.href=c,h&&d.setAttribute("nonce",h),document.head.appendChild(d),u)return new Promise((g,x)=>{d.addEventListener("load",g),d.addEventListener("error",()=>x(new Error(`Unable to preload CSS for ${c}`)))})}))}function a(r){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=r,window.dispatchEvent(o),!o.defaultPrevented)throw r}return n.then(r=>{for(const o of r||[])o.status==="rejected"&&a(o.reason);return e().catch(a)})},le=["en","fr"],he={en:()=>C(()=>Promise.resolve().then(()=>ue),void 0,import.meta.url).then(i=>i.ufoMessages_en),fr:()=>C(()=>import("./UfoMessages_fr-NZrwNFCQ.js"),[],import.meta.url).then(i=>i.ufoMessages_fr)};function ce(i){return he[i]()}const H={play:"Play",pause:"Pause",noDuration:"No observation duration",autoReplay:"Auto-replay",currentPosition:"Current position",duration:"Duration",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen"},ue=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:H},Symbol.toStringTag,{value:"Module"})),de=new Set;class fe extends HTMLElement{static get observedAttributes(){return["src"]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;playPauseButton;loopButton;fullscreenButton;seekInput;timeStartLabel;timeEndLabel;currentSighting=T.create();player;loopEnabled=!0;highlightedSourceIds=new Set;enableClickToPlay=!0;fullscreenTarget;messages=H;realDurationMs;realStartMs;handleFullscreenChange=()=>this.updateFullscreenButton();handlePointerMove=e=>{const t=this.canvasPointFromEvent(e),s=t&&this.currentSighting.timeline.hitTest(this.currentTime,t.x,t.y);if(!s?.shape.title){this.tooltip.hidden=!0;return}this.tooltip.textContent=s.shape.title,this.tooltip.hidden=!1;const n=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-n.left+12}px`,this.tooltip.style.top=`${e.clientY-n.top+12}px`};handlePointerLeave=()=>{this.tooltip.hidden=!0};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${U}</style>${z}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new se(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,s){e==="src"&&s&&s!==t&&this.isConnected&&this.loadFromSrc(s)}async loadFromSrc(e){const t=await fetch(e);this.sightingData=await t.json()}get sightingData(){return ie(this.currentSighting)}set sightingData(e){this.player.stop(),this.currentSighting=ne(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(n=>this.highlightedSourceIds.has(n))||(this.highlightedSourceIds=t,this.refresh())}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 s=this.playbackState!=="playing"?this.highlightedSourceIds:de;for(const[n,a]of t){const r=s.has(n);r&&s.size===1?this.canvasRenderer.paintShape({...a,selected:!0}):(this.canvasRenderer.paintShape(a),r&&this.canvasRenderer.paintMemberOutline(a))}if(s.size>1){const n=m.groupBoundsFor([...t].filter(([a])=>s.has(a)).map(([,a])=>a.bounds));this.canvasRenderer.paintGroupHandles(n)}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 V(this.currentSighting.timeline,(t,s)=>this.onFrame(t,s));return e.loop=this.loopEnabled,e}togglePlayPause(){this.player.seekableDuration<=0||(this.player.playbackState==="playing"?(this.player.pause(),this.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 s=t?e?this.messages.pause:this.messages.play:this.messages.noDuration;this.playPauseButton.title=s,this.playPauseButton.setAttribute("aria-label",s),this.toolbar.classList.toggle("auto-hide",e),this.fullscreenButton.classList.toggle("auto-hide",e)}toggleLoop(){this.loopEnabled=!this.loopEnabled,this.loopButton.setAttribute("aria-pressed",String(this.loopEnabled)),this.player.loop=this.loopEnabled}toggleFullscreen(){document.fullscreenElement?document.exitFullscreen():this.fullscreenTarget.requestFullscreen().catch(e=>{console.error("<rr0-ufo>: requestFullscreen() failed —",e)})}updateFullscreenButton(){const e=document.fullscreenElement===this.fullscreenTarget;this.fullscreenButton.title=e?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}async loadLocaleMessages(){const e=re(navigator.languages,le);e!=="en"&&this.applyMessages(await ce(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=Q(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?P(e.time):void 0;const s=this.currentSighting.timeline.duration;this.player.playbackRate=this.realDurationMs!==void 0&&s>0?s/this.realDurationMs:1,this.player.durationOverrideMs=this.realDurationMs??0,this.timeEndLabel.textContent=this.formatEndOfTimeline(),this.timeStartLabel.textContent=this.formatPosition(this.player.time)}formatPosition(e){if(this.realDurationMs===void 0)return S(e);const t=this.currentSighting.timeline.duration,s=t>0&&e<=t?e/t*this.realDurationMs:e;return this.realStartMs!==void 0?R(O(this.realStartMs+s)):S(s)}formatEndOfTimeline(){return this.realDurationMs===void 0?S(this.currentSighting.timeline.duration):this.realStartMs!==void 0?R(O(this.realStartMs+this.realDurationMs)):S(this.realDurationMs)}}function O(i){const e=new Date(i);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function R(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 S(i){const e=Math.round(i/1e3),t=Math.floor(e/60),s=e%60;return`${t}:${String(s).padStart(2,"0")}`}const _="rr0-ufo";function pe(){customElements.get(_)||customElements.define(_,fe)}pe();
|
|
167
|
+
`;function K(i,e,t){const{bounds:s}=i;return e>=s.x&&e<=s.x+s.width&&t>=s.y&&t<=s.y+s.height}function f(i,e,t){return i+(e-i)*t}function F(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 W(i){return`#${i.map(e=>Math.round(e).toString(16).padStart(2,"0")).join("")}`}function G(i,e,t){const s=F(i),n=F(e);return!s||!n?t<1?i:e:W([f(s[0],n[0],t),f(s[1],n[1],t),f(s[2],n[2],t)])}function q(i,e,t){const s={x:f(i.bounds.x,e.bounds.x,t),y:f(i.bounds.y,e.bounds.y,t),width:f(i.bounds.width,e.bounds.width,t),height:f(i.bounds.height,e.bounds.height,t)},n=f(i.angle,e.angle,t),a=f(i.transparency,e.transparency,t),r=f(i.haloScale,e.haloScale,t),o=G(i.color,e.color,t);return i.kind==="polygon"&&e.kind==="polygon"&&i.points.length===e.points.length?{...i,bounds:s,angle:n,transparency:a,haloScale:r,color:o,points:i.points.map((l,h)=>({x:f(l.x,e.points[h].x,t),y:f(l.y,e.points[h].y,t)}))}:{...t<1?i:e,bounds:s,angle:n,transparency:a,haloScale:r,color:o}}class E{keyframes=[];order=[];groups=[];addKeyframe(e,t){const s=this.findInsertIndex(e);if(this.keyframes[s]?.t===e){const n=new Set(t.map(a=>a.sourceId));this.keyframes[s]={t:e,shapes:[...this.keyframes[s].shapes.filter(a=>!n.has(a.sourceId)),...t]}}else this.keyframes.splice(s,0,{t:e,shapes:[...t]});for(const n of t)this.order.includes(n.sourceId)||this.order.push(n.sourceId)}findInsertIndex(e){let t=0,s=this.keyframes.length;for(;t<s;){const n=t+s>>>1;this.keyframes[n].t<e?t=n+1:s=n}return t}getKeyframeAt(e){const t=this.findInsertIndex(e),s=this.keyframes[t];return s?.t===e?s:void 0}getShapeAt(e,t){return this.getKeyframeAt(e)?.shapes.find(s=>s.sourceId===t)?.shape}getLatestShapeAt(e,t){let s=this.findInsertIndex(e);for(this.keyframes[s]?.t!==e&&(s-=1);s>=0;s--){const n=this.keyframes[s].shapes.find(a=>a.sourceId===t);if(n)return n.shape}}getInterpolatedShapeAt(e,t){const s=this.findShapeAtOrBefore(e,t);if(s?.t===e)return s.shape;const n=this.findShapeAtOrAfter(e,t);return s?n?q(s.shape,n.shape,(e-s.t)/(n.t-s.t)):s.shape:n?.shape}findShapeAtOrBefore(e,t){let s=this.findInsertIndex(e);for(this.keyframes[s]?.t!==e&&(s-=1);s>=0;s--){const n=this.keyframes[s].shapes.find(a=>a.sourceId===t);if(n)return{t:this.keyframes[s].t,shape:n.shape}}}findShapeAtOrAfter(e,t){for(let s=this.findInsertIndex(e);s<this.keyframes.length;s++){const n=this.keyframes[s].shapes.find(a=>a.sourceId===t);if(n)return{t:this.keyframes[s].t,shape:n.shape}}}hitTest(e,t,s){const n=this.sourceIds;for(let a=n.length-1;a>=0;a--){const r=this.getInterpolatedShapeAt(e,n[a]);if(r&&K(r,t,s))return{sourceId:n[a],shape:r}}}removeSource(e){for(let t=this.keyframes.length-1;t>=0;t--){const s=this.keyframes[t].shapes.filter(n=>n.sourceId!==e);s.length===0?this.keyframes.splice(t,1):s.length!==this.keyframes[t].shapes.length&&(this.keyframes[t]={t:this.keyframes[t].t,shapes:s})}this.order=this.order.filter(t=>t!==e),this.removeFromGroup(e)}group(e){if(!(e.length<2)){for(const t of e)this.removeFromGroup(t);this.groups.push([...e])}}ungroup(e){const t=this.groups.findIndex(s=>s.includes(e));t!==-1&&this.groups.splice(t,1)}groupMembers(e){return this.groups.find(t=>t.includes(e))}removeFromGroup(e){const t=this.groups.findIndex(n=>n.includes(e));if(t===-1)return;const s=this.groups[t].filter(n=>n!==e);s.length<2?this.groups.splice(t,1):this.groups[t]=s}bringToFront(e){const t=this.order.indexOf(e);t!==-1&&(this.order.splice(t,1),this.order.push(e))}sendToBack(e){const t=this.order.indexOf(e);t!==-1&&(this.order.splice(t,1),this.order.unshift(e))}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get sourceIds(){return[...this.order]}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes,order:this.order,groups:this.groups}}static fromJSON(e){const t=new E;for(const s of e.keyframes)t.addKeyframe(s.t,s.shapes);if(e.order){const s=t.order.filter(n=>!e.order.includes(n));t.order=[...e.order,...s]}return e.groups&&(t.groups=e.groups.map(s=>s.filter(n=>t.order.includes(n))).filter(s=>s.length>=2)),t}}function Y(i,e,t){return Math.max(e,Math.min(t,i))}function Z(i,e,t){const s=((e-i)%360+540)%360-180;return((i+s*t)%360+360)%360}function v(i,e,t){return i+(e-i)*t}function X(i,e,t){return{lat:i.lat===void 0||e.lat===void 0?void 0:v(i.lat,e.lat,t),lng:i.lng===void 0||e.lng===void 0?void 0:v(i.lng,e.lng,t),elevationM:v(i.elevationM,e.elevationM,t),headingDeg:i.headingDeg===void 0||e.headingDeg===void 0?void 0:Z(i.headingDeg,e.headingDeg,t),pitchDeg:v(i.pitchDeg,e.pitchDeg,t),fovDeg:v(i.fovDeg,e.fovDeg,t)}}class b{keyframes=[];addKeyframe(e,t){const s=this.findInsertIndex(e);this.keyframes[s]?.t===e?this.keyframes[s]={t:e,pose:t}:this.keyframes.splice(s,0,{t:e,pose:t})}clear(){this.keyframes.length=0}removeKeyframeAt(e){const t=this.findInsertIndex(e);this.keyframes[t]?.t===e&&this.keyframes.splice(t,1)}findInsertIndex(e){let t=0,s=this.keyframes.length;for(;t<s;){const n=t+s>>>1;this.keyframes[n].t<e?t=n+1:s=n}return t}getLatestPoseAt(e){let t=this.findInsertIndex(e);return this.keyframes[t]?.t!==e&&(t-=1),t>=0?this.keyframes[t].pose:void 0}getInterpolatedPoseAt(e){const t=this.findInsertIndex(e),s=this.keyframes[t]?.t===e?this.keyframes[t]:this.keyframes[t-1];if(s?.t===e)return s.pose;const n=this.keyframes[t]?.t===e?void 0:this.keyframes[t];return s?n?X(s.pose,n.pose,Y((e-s.t)/(n.t-s.t),0,1)):s.pose:n?.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 b;for(const s of e.keyframes)t.addKeyframe(s.t,s.pose);return t}}function k(i,e,t){return i+(e-i)*t}function Q(i,e,t){const s=((e-i)%360+540)%360-180;return((i+s*t)%360+360)%360}function V(i,e,t){return Math.max(e,Math.min(t,i))}function j(i,e,t){return{cloudCover:k(i.cloudCover,e.cloudCover,t),cloudDarkness:k(i.cloudDarkness,e.cloudDarkness,t),precipitationType:t<1?i.precipitationType:e.precipitationType,precipitationIntensity:k(i.precipitationIntensity,e.precipitationIntensity,t),windDirectionDeg:Q(i.windDirectionDeg,e.windDirectionDeg,t),windSpeed:k(i.windSpeed,e.windSpeed,t),storm:t<1?i.storm:e.storm}}class x{keyframes=[];addKeyframe(e,t){const s=this.findInsertIndex(e);this.keyframes[s]?.t===e?this.keyframes[s]={t:e,weather:t}:this.keyframes.splice(s,0,{t:e,weather:t})}clear(){this.keyframes.length=0}removeKeyframeAt(e){const t=this.findInsertIndex(e);this.keyframes[t]?.t===e&&this.keyframes.splice(t,1)}findInsertIndex(e){let t=0,s=this.keyframes.length;for(;t<s;){const n=t+s>>>1;this.keyframes[n].t<e?t=n+1:s=n}return t}getLatestWeatherAt(e){let t=this.findInsertIndex(e);return this.keyframes[t]?.t!==e&&(t-=1),t>=0?this.keyframes[t].weather:void 0}getInterpolatedWeatherAt(e){const t=this.findInsertIndex(e),s=this.keyframes[t]?.t===e?this.keyframes[t]:this.keyframes[t-1];if(s?.t===e)return s.weather;const n=this.keyframes[t]?.t===e?void 0:this.keyframes[t];return s?n?j(s.weather,n.weather,V((e-s.t)/(n.t-s.t),0,1)):s.weather:n?.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 x;for(const s of e.keyframes)t.addKeyframe(s.t,s.weather);return t}}function D(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 L(i){return(((i.day??0)*24+(i.hour??0))*60+(i.minute??0))*6e4+(i.second??0)*1e3}const ee=["year","month","day","hour","minute"],te=["day","hour","minute"];function A(i,e){return e.filter(t=>i[t]!==void 0).join(",")}function se(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?ee:te,s=A(i.time,t);if(!(s===""||s!==A(i.endTime,t)))return e?D(i.endTime)-D(i.time):L(i.endTime)-L(i.time)}class P{constructor(e,t,s,n,a,r,o){this.event=e,this.timeline=t,this.witnessTrack=s,this.weatherTrack=n,this.witness=a,this.caseId=r,this.weather=o}static create(e,t,s){return new P({eventType:"sighting",time:e,place:t},new E,new b,new x,s)}}class ie{constructor(e,t){this.timeline=e,this.onFrame=t}rafId=null;state="stopped";currentT=0;lastWallTime=0;playbackRate=1;loop=!1;durationOverrideMs=0;get seekableDuration(){return Math.max(this.timeline.duration,this.durationOverrideMs)}play(){if(this.state==="playing")return;this.currentT>=this.seekableDuration&&(this.currentT=0,this.resolveFrame(0)),this.state="playing",this.lastWallTime=performance.now();const e=()=>{if(this.state!=="playing")return;const t=performance.now();if(this.currentT+=(t-this.lastWallTime)*this.playbackRate,this.lastWallTime=t,this.currentT>=this.seekableDuration){if(this.loop&&this.seekableDuration>0){this.currentT%=this.seekableDuration,this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e);return}this.currentT=this.seekableDuration,this.stop(),this.resolveFrame(this.currentT);return}this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e)};this.rafId=requestAnimationFrame(e)}pause(){this.state==="playing"&&(this.state="paused",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null))}stop(){this.state="stopped",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}seek(e){this.currentT=Math.max(0,Math.min(e,this.seekableDuration)),this.resolveFrame(this.currentT)}get playbackState(){return this.state}get time(){return this.currentT}resolveFrame(e){const t=new Map;for(const s of this.timeline.sourceIds){const n=this.timeline.getInterpolatedShapeAt(e,s);n&&t.set(s,n)}this.onFrame(e,t)}}const z=["nw","n","ne","e","se","s","sw","w"],ne=24,S=8;function O(i,e,t){const s=i.x-e.x,n=i.y-e.y,a=Math.cos(t),r=Math.sin(t);return{x:e.x+s*a-n*r,y:e.y+s*r+n*a}}function M(i){return{x:i.x+i.width/2,y:i.y+i.height/2}}const re={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 g{static handlePointsFor(e){const{x:t,y:s,width:n,height:a}=e.bounds,r={nw:{x:t,y:s},n:{x:t+n/2,y:s},ne:{x:t+n,y:s},e:{x:t+n,y:s+a/2},se:{x:t+n,y:s+a},s:{x:t+n/2,y:s+a},sw:{x:t,y:s+a},w:{x:t,y:s+a/2},rotate:{x:t+n/2,y:s-ne}},o=M(e.bounds),l={};for(const h of Object.keys(r))l[h]=O(r[h],o,e.angle);return l}static hitTestHandle(e,t,s=8,n=[...z,"rotate"]){const a=g.handlePointsFor(e);for(const r of n)if(Math.hypot(t.x-a[r].x,t.y-a[r].y)<=s)return r}static resizeBounds(e,t,s,n){const a=M(e),r=O(n,a,-t),{x:o,y:l,width:h,height:c}=e,u=re[s];let p=o,y=l,d=o+h,m=l+c;return u.left&&(p=Math.min(r.x,d-S)),u.right&&(d=Math.max(r.x,p+S)),u.top&&(y=Math.min(r.y,m-S)),u.bottom&&(m=Math.max(r.y,y+S)),{x:p,y,width:d-p,height:m-y}}static resizeShape(e,t,s){if(t==="rotate")throw new Error("resizeShape does not accept the rotate handle");const{width:n,height:a}=e.bounds,r=g.resizeBounds(e.bounds,e.angle,t,s);if(e.kind==="oval")return{...e,bounds:r};const o=n===0?1:r.width/n,l=a===0?1:r.height/a;return{...e,bounds:r,points:e.points.map(h=>({x:h.x*o,y:h.y*l}))}}static rotateShape(e,t){const s=M(e.bounds),n=Math.atan2(t.y-s.y,t.x-s.x)+Math.PI/2;return{...e,angle:n}}static groupBoundsFor(e){const t=Math.min(...e.map(r=>r.x)),s=Math.min(...e.map(r=>r.y)),n=Math.max(...e.map(r=>r.x+r.width)),a=Math.max(...e.map(r=>r.y+r.height));return{x:t,y:s,width:n-t,height:a-s}}}const B=6,I=B/2,ae=20;class oe{constructor(e){this.ctx=e}clear(e,t){this.ctx.clearRect(0,0,e,t)}paintShape(e){this.ctx.save(),this.ctx.globalAlpha=1-e.transparency,e.haloScale>0&&this.paintHalo(e),this.paintBase(e),this.ctx.restore(),e.selected&&this.paintSelectionHandles(e)}paintBase(e){if(this.ctx.fillStyle=e.color,this.ctx.beginPath(),e.kind==="oval"){const{x:t,y:s,width:n,height:a}=e.bounds,r=n/2,o=a/2;this.ctx.ellipse(t+r,s+o,r,o,e.angle,0,2*Math.PI)}else{const{x:t,y:s,width:n,height:a}=e.bounds;this.ctx.save(),this.ctx.translate(t+n/2,s+a/2),this.ctx.rotate(e.angle),this.ctx.translate(-n/2,-a/2),e.points.forEach((r,o)=>{o===0?this.ctx.moveTo(r.x,r.y):this.ctx.lineTo(r.x,r.y)}),this.ctx.closePath(),this.ctx.restore()}this.ctx.fill()}paintHalo(e){this.ctx.save(),this.ctx.shadowColor=e.color,this.ctx.shadowBlur=ae*e.haloScale,this.paintBase(e),this.ctx.restore()}paintSelectionHandles(e){this.paintHandleFrame(g.handlePointsFor(e),{includeRotate:!0})}paintMemberOutline(e){this.paintOutline(g.handlePointsFor(e))}paintGroupHandles(e){this.paintHandleFrame(g.handlePointsFor({bounds:e,angle:0}),{includeRotate:!0})}paintOutline(e){this.ctx.strokeStyle="lightgray",this.ctx.beginPath(),this.ctx.moveTo(e.nw.x,e.nw.y),this.ctx.lineTo(e.ne.x,e.ne.y),this.ctx.lineTo(e.se.x,e.se.y),this.ctx.lineTo(e.sw.x,e.sw.y),this.ctx.closePath(),this.ctx.stroke()}paintHandleFrame(e,{includeRotate:t}){this.paintOutline(e),this.ctx.fillStyle="lightgray";for(const s of z){const n=e[s];this.ctx.fillRect(n.x-I,n.y-I,B,B)}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,I+1,I+1,0,0,2*Math.PI),this.ctx.fill())}}function le(i){return{version:1,time:i.event.time,endTime:i.event.endTime,durationSeconds:i.event.durationSeconds,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}}function he(i){return new P({eventType:"sighting",time:i.time,endTime:i.endTime,durationSeconds:i.durationSeconds,place:i.place,description:i.description,tags:i.tags},E.fromJSON(i.timeline),i.witnessTrack?b.fromJSON(i.witnessTrack):new b,i.weatherTrack?x.fromJSON(i.weatherTrack):new x,i.witness,i.caseId,i.weather)}function ce(i,e){for(const t of i){const s=t.toLowerCase().split("-")[0];if(e.includes(s))return s}return"en"}const ue="modulepreload",de=function(i,e){return new URL(i,e).href},C={},R=function(e,t,s){let n=Promise.resolve();if(t&&t.length>0){let r=function(c){return Promise.all(c.map(u=>Promise.resolve(u).then(p=>({status:"fulfilled",value:p}),p=>({status:"rejected",reason:p}))))};const o=document.getElementsByTagName("link"),l=document.querySelector("meta[property=csp-nonce]"),h=l?.nonce||l?.getAttribute("nonce");n=r(t.map(c=>{if(c=de(c,s),c in C)return;C[c]=!0;const u=c.endsWith(".css"),p=u?'[rel="stylesheet"]':"";if(!!s)for(let m=o.length-1;m>=0;m--){const w=o[m];if(w.href===c&&(!u||w.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${c}"]${p}`))return;const d=document.createElement("link");if(d.rel=u?"stylesheet":ue,u||(d.as="script"),d.crossOrigin="",d.href=c,h&&d.setAttribute("nonce",h),document.head.appendChild(d),u)return new Promise((m,w)=>{d.addEventListener("load",m),d.addEventListener("error",()=>w(new Error(`Unable to preload CSS for ${c}`)))})}))}function a(r){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=r,window.dispatchEvent(o),!o.defaultPrevented)throw r}return n.then(r=>{for(const o of r||[])o.status==="rejected"&&a(o.reason);return e().catch(a)})},fe=["en","fr"],pe={en:()=>R(()=>Promise.resolve().then(()=>ge),void 0,import.meta.url).then(i=>i.ufoMessages_en),fr:()=>R(()=>import("./UfoMessages_fr-NZrwNFCQ.js"),[],import.meta.url).then(i=>i.ufoMessages_fr)};function me(i){return pe[i]()}const U={play:"Play",pause:"Pause",noDuration:"No observation duration",autoReplay:"Auto-replay",currentPosition:"Current position",duration:"Duration",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen"},ge=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:U},Symbol.toStringTag,{value:"Module"})),ye=new Set;class ve extends HTMLElement{static get observedAttributes(){return["src"]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;playPauseButton;loopButton;fullscreenButton;seekInput;timeStartLabel;timeEndLabel;currentSighting=P.create();player;loopEnabled=!0;highlightedSourceIds=new Set;enableClickToPlay=!0;fullscreenTarget;messages=U;realDurationMs;realStartMs;handleFullscreenChange=()=>this.updateFullscreenButton();handlePointerMove=e=>{const t=this.canvasPointFromEvent(e),s=t&&this.currentSighting.timeline.hitTest(this.currentTime,t.x,t.y);if(!s?.shape.title){this.tooltip.hidden=!0;return}this.tooltip.textContent=s.shape.title,this.tooltip.hidden=!1;const n=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-n.left+12}px`,this.tooltip.style.top=`${e.clientY-n.top+12}px`};handlePointerLeave=()=>{this.tooltip.hidden=!0};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${J}</style>${$}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new oe(this.canvas.getContext("2d")),this.tooltip=this.shadow.getElementById("tooltip"),this.toolbar=this.shadow.getElementById("toolbar"),this.playPauseButton=this.shadow.getElementById("play-pause"),this.loopButton=this.shadow.getElementById("loop"),this.fullscreenButton=this.shadow.getElementById("fullscreen"),this.seekInput=this.shadow.getElementById("seek"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end"),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,s){e==="src"&&s&&s!==t&&this.isConnected&&this.loadFromSrc(s)}async loadFromSrc(e){const t=await fetch(e);this.sightingData=await t.json()}get sightingData(){return le(this.currentSighting)}set sightingData(e){this.player.stop(),this.currentSighting=he(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(n=>this.highlightedSourceIds.has(n))||(this.highlightedSourceIds=t,this.refresh())}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 s=this.playbackState!=="playing"?this.highlightedSourceIds:ye;for(const[n,a]of t){const r=s.has(n);r&&s.size===1?this.canvasRenderer.paintShape({...a,selected:!0}):(this.canvasRenderer.paintShape(a),r&&this.canvasRenderer.paintMemberOutline(a))}if(s.size>1){const n=g.groupBoundsFor([...t].filter(([a])=>s.has(a)).map(([,a])=>a.bounds));this.canvasRenderer.paintGroupHandles(n)}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 ie(this.currentSighting.timeline,(t,s)=>this.onFrame(t,s));return e.loop=this.loopEnabled,e}togglePlayPause(){this.player.seekableDuration<=0||(this.player.playbackState==="playing"?(this.player.pause(),this.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 s=t?e?this.messages.pause:this.messages.play:this.messages.noDuration;this.playPauseButton.title=s,this.playPauseButton.setAttribute("aria-label",s),this.toolbar.classList.toggle("auto-hide",e),this.fullscreenButton.classList.toggle("auto-hide",e)}toggleLoop(){this.loopEnabled=!this.loopEnabled,this.loopButton.setAttribute("aria-pressed",String(this.loopEnabled)),this.player.loop=this.loopEnabled}toggleFullscreen(){document.fullscreenElement?document.exitFullscreen():this.fullscreenTarget.requestFullscreen().catch(e=>{console.error("<rr0-ufo>: requestFullscreen() failed —",e)})}updateFullscreenButton(){const e=document.fullscreenElement===this.fullscreenTarget;this.fullscreenButton.title=e?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}async loadLocaleMessages(){const e=ce(navigator.languages,fe);e!=="en"&&this.applyMessages(await me(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=se(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?D(e.time):void 0;const s=this.currentSighting.timeline.duration;this.player.playbackRate=this.realDurationMs!==void 0&&s>0?s/this.realDurationMs:1,this.player.durationOverrideMs=this.realDurationMs??0,this.timeEndLabel.textContent=this.formatEndOfTimeline(),this.timeStartLabel.textContent=this.formatPosition(this.player.time)}formatPosition(e){if(this.realDurationMs===void 0)return T(e);const t=this.currentSighting.timeline.duration,s=t>0&&e<=t?e/t*this.realDurationMs:e;return this.realStartMs!==void 0?N(_(this.realStartMs+s)):T(s)}formatEndOfTimeline(){return this.realDurationMs===void 0?T(this.currentSighting.timeline.duration):this.realStartMs!==void 0?N(_(this.realStartMs+this.realDurationMs)):T(this.realDurationMs)}}function _(i){const e=new Date(i);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function N(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 T(i){const e=Math.round(i/1e3),t=Math.floor(e/60),s=e%60;return`${t}:${String(s).padStart(2,"0")}`}const H="rr0-ufo";function be(){customElements.get(H)||customElements.define(H,ve)}be();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rr0/ufoathome",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.2",
|
|
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",
|