diff --git a/demo/index.js b/demo/index.js
index ff40c54..282860a 100644
--- a/demo/index.js
+++ b/demo/index.js
@@ -1,4 +1,4 @@
-"use strict";(()=>{var dt=Object.defineProperty,mt=Object.defineProperties;var ut=Object.getOwnPropertyDescriptors;var Y=Object.getOwnPropertySymbols;var pt=Object.prototype.hasOwnProperty,vt=Object.prototype.propertyIsEnumerable;var j=(s,i,t)=>i in s?dt(s,i,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[i]=t,f=(s,i)=>{for(var t in i||(i={}))pt.call(i,t)&&j(s,t,i[t]);if(Y)for(var t of Y(i))vt.call(i,t)&&j(s,t,i[t]);return s},x=(s,i)=>mt(s,ut(i));var I=(s,i,t)=>new Promise((e,n)=>{var o=a=>{try{h(t.next(a))}catch(c){n(c)}},l=a=>{try{h(t.throw(a))}catch(c){n(c)}},h=a=>a.done?e(a.value):Promise.resolve(a.value).then(o,l);h((t=t.apply(s,i)).next())});function q(s,i){let t=document.createElement("button");t.type="button",t.style.margin="5px",t.style.float="right",t.title="Download current frame",t.innerHTML='',i.addEvent(t,"click",()=>{let e=i.frameToDataUrl();if(!e)return;let n=document.createElement("a");n.download=`frame_${String(i.activeTimeFrame).padStart(3,"0")}.png`,n.href=e,n.click()}),i.buttons.push(t),i.playerControlsContainer.appendChild(t)}var $='',K='';function _(s,i){let t=document.createElement("button");t.type="button",t.style.margin="5px",s.muted||s.volume===0?t.innerHTML=K:t.innerHTML=$,i.addEvent(s,"volumechange",()=>{s.muted||s.volume===0?t.innerHTML=$:t.innerHTML=K}),i.addEvent(t,"click",()=>{s.muted&&(s.muted=!1),s.volume===0?s.volume=1:s.volume=0}),i.buttons.push(t),i.playerControlsContainer.appendChild(t)}var J='',gt='';function G(s,i){let t=document.createElement("button");t.type="button",t.innerHTML=J,t.style.margin="5px";let e=function(n){i.referenceVideoElement&&n(i.referenceVideoElement)};i.addEvent(s,"play",()=>{t.innerHTML=gt}),i.addEvent(s,"pause",()=>{t.innerHTML=J,i.syncTime()}),i.addEvent(t,"click",()=>{s.paused?(e(n=>{n.paused&&(n.play(),i.syncTime())}),s.play()):(e(n=>{n.paused||n.pause()}),s.pause(),requestAnimationFrame(()=>{i.syncTime(),i.redrawFullCanvas()}))}),i.buttons.push(t),i.playerControlsContainer.appendChild(t)}function Z(s){return``}function tt(s,i){let t=[.25,.5,.75,1],e=document.createElement("button"),n=t[t.length-1],o=function(l){i.referenceVideoElement&&l(i.referenceVideoElement)};e.type="button",s.playbackRate=n,o(l=>{l.playbackRate=n}),e.innerHTML=Q(n),e.style.margin="5px",i.addEvent(e,"click",()=>{let l=t.indexOf(s.playbackRate),h=l+1>=t.length?0:l+1;s.playbackRate=t[h],o(a=>{a.playbackRate=t[h]}),e.innerHTML=Q(t[h])}),i.buttons.push(e),i.playerControlsContainer.appendChild(e)}var ft="#F3CE32";function et(){var m,g;let s=document.createElement("div");s.style.position="absolute",s.style.top="-40px",s.style.left="0",s.style.zIndex="2",(m=this.canvas.parentNode)==null||m.insertBefore(s,this.canvas);let i=document.createElement("div");i.style.position="relative",i.style.top="0",i.style.left="0",i.style.zIndex="2",(g=this.canvas.parentNode)==null||g.insertBefore(i,this.canvas.nextSibling),this.playerControlsContainer=i;let t=this.videoElement.tagName==="VIDEO"?this.videoElement:null;this.uiContainer=s;let e=(p,u,r=s)=>{let c=document.createElement("button");if(c.type="button",c.innerHTML=p,c.style.margin="5px",r.appendChild(c),this.buttons.push(c),typeof u=="function")this.addEvent(c,"click",u);else{c.dataset.tool=u;let v=()=>{this.currentTool===u?this.currentTool=null:this.currentTool=u};this.addEvent(c,"click",v)}return c},n=()=>{let p=document.createElement("div");return p.style.display="inline-flex",p.style.alignItems="center",p.style.margin="5px",s.appendChild(p),p};e('',"rectangle"),e('',"circle"),e('',"line"),e('',"curve"),e('',"arrow"),e('',"text"),e('',"eraser"),e('',"move"),e('',"compare"),e('',()=>{this.handleUndo()}),t&&(e('',()=>{this.prevFrame()},this.playerControlsContainer),Z(t,this),e('',()=>{this.nextFrame()},this.playerControlsContainer),J(t,this),tt(t,this),$(t,this));let o=document.createElement("input");o.type="color",o.value=ft,o.style.margin="5px",this.colorPicker=o,s.appendChild(o);let l=n(),h=document.createElement("input");h.type="number",h.step="1",h.min="1",h.max="10",h.value="5",h.style.margin="5px",l.appendChild(h);let a=p=>{this.ctx.lineWidth=p.target.valueAsNumber,this.focusOnMediaNode()};this.addEvent(h,"input",a);let d=p=>{this.ctx.strokeStyle=p.target.value,this.ctx.fillStyle=p.target.value,this.focusOnMediaNode()};if(this.addEvent(o,"input",d),this.colorPicker=o,this.strokeSizePicker=h,this.getButtonForTool("compare").style.display="none",t){this.hide(),this.addEvent(t,"pause",()=>{this.show()}),this.addEvent(t,"seek",()=>{t.paused&&this.show(),this.syncTime()}),this.addEvent(t,"timeupdate",()=>{t.currentTime<2e-4&&!t.paused&&this.playAnnotationsAsVideo(),this.syncTime()}),this.addEvent(t,"seeking",()=>{this.syncTime()}),this.addEvent(t,"error",()=>{this.hide()}),this.addEvent(t,"stalled",()=>{this.hide()}),this.addEvent(t,"waiting",()=>{this.hide()}),this.addEvent(t,"ended",()=>{this.hide()}),this.addEvent(t,"play",()=>{this.hideControls(),this.playAnnotationsAsVideo()});let p=r=>{let c=r.target===document.body,v=this.uiContainer.contains(r.target),w=this.playerControlsContainer.contains(r.target),b=this.videoElement.contains(r.target),C=this.canvas.contains(r.target);return v||w||b||C||c};this.addEvent(document,"copy",r=>{var c;p(r)&&(r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation(),(c=r.clipboardData)==null||c.setData("application/json",JSON.stringify(this.saveCurrentFrame())))}),this.addEvent(document,"cut",r=>{var v;if(!p(r))return;r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation();let c=this.saveCurrentFrame();this.replaceFrame(this.playbackFrame,[]),this.redrawFullCanvas(),(v=r.clipboardData)==null||v.setData("application/json",JSON.stringify(c))}),this.addEvent(document,"paste",r=>{var b,C,z,O,U;if(!p(r))return;let c=(C=(b=r.clipboardData)==null?void 0:b.types)!=null?C:[];if(console.log("dataTypes",JSON.stringify(c)),c.includes("application/json"))r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation();else if(c.includes("Files")){let S=(z=r.clipboardData)==null?void 0:z.files;if(S&&S.length>0){let X=S[0];if(X.type.startsWith("image/")){r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation();let I=new Image;I.addEventListener("load",()=>{let ct=I.naturalWidth/I.naturalHeight,Y=.25,dt=Y/ct*this.aspectRatio;this.addShapesToFrame(this.playbackFrame,[{type:"image",image:I,x:0,y:0,width:Y,height:dt,strokeStyle:"red",fillStyle:"red",lineWidth:2}]),this.redrawFullCanvas(),requestAnimationFrame(()=>{this.show()}),this.currentTool="move"},{once:!0}),I.src=URL.createObjectURL(X),this.redrawFullCanvas()}}}else if(c.includes("text/plain")){let S=(O=r.clipboardData)==null?void 0:O.getData("text/plain");S&&(console.log("text",S),r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation(),this.addShapesToFrame(this.playbackFrame,[{type:"text",text:S,x:.4,y:.4,strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth}]),this.show(),this.currentTool="move",this.redrawFullCanvas())}else return;let v=(U=r.clipboardData)==null?void 0:U.getData("application/json");if(!v)return;let w=JSON.parse(v);w&&w.shapes&&w.version===1&&(this.addShapesToFrame(this.playbackFrame,w.shapes),this.redrawFullCanvas())});let u=r=>{this.referenceVideoElement&&r(this.referenceVideoElement)};this.addEvent(document,"click",r=>{if(!p(r))return;let c=this.uiContainer.contains(r.target),v=this.playerControlsContainer.contains(r.target);c||v||t.paused||(this.currentTool=null,t.pause(),u(w=>{w.pause()}),requestAnimationFrame(()=>{this.syncTime(),this.redrawFullCanvas()}))}),this.addEvent(document,"keydown",r=>{p(r)&&(r.key==="ArrowLeft"||r.key==="ArrowRight"?(r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation(),r.key==="ArrowLeft"?this.prevFrame():r.key==="ArrowRight"&&this.nextFrame()):r.code==="Space"&&(r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation(),t.paused?(u(c=>{c.play().then(()=>{this.syncTime()})}),t.play().then(()=>{this.syncTime()})):(u(c=>{c.pause()}),t.pause(),requestAnimationFrame(()=>{this.syncTime(),this.redrawFullCanvas()}))))})}}function it(){var s;this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d"),(s=this.videoElement.parentNode)==null||s.insertBefore(this.canvas,this.videoElement.nextSibling),this.canvas.style.position="absolute",this.canvas.style.top="0",this.canvas.style.left="0",this.canvas.style.zIndex="1",this.addEvent(this.canvas,"pointerdown",this.handleMouseDown),this.addEvent(this.canvas,"pointermove",this.handleMouseMove),this.addEvent(this.canvas,"pointerup",this.handleMouseUp),this.addEvent(this.canvas,"pointercancel",this.handleMouseUp),this.addEvent(window,"resize",this.setCanvasSize),this.addEvent(document,"keydown",this.onKeyDown)}var y=class{constructor(i){this.startX=0;this.startY=0;this.isDrawing=!1;this.annotationTool=i}get ctx(){return this.annotationTool.ctx}onDeactivate(){}onActivate(){}reset(){this.startX=0,this.startY=0,this.isDrawing=!1}save(i){this.annotationTool.addShape(i)}};var k=class extends y{constructor(){super(...arguments);this.name="rectangle"}move(t,e,n){return t.x+=e,t.y+=n,t}normalize(t,e,n){return x(f({},t),{x:t.x/e,y:t.y/n,width:t.width/e,height:t.height/n})}onPointerDown(t){let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.startX=e,this.startY=n,this.isDrawing=!0}onPointerMove(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.drawRectangle(this.startX,this.startY,e-this.startX,n-this.startY)}onPointerUp(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.save({type:"rectangle",x:this.startX,y:this.startY,width:e-this.startX,height:n-this.startY,strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth}),this.drawRectangle(this.startX,this.startY,e-this.startX,n-this.startY),this.isDrawing=!1}drawRectangle(t,e,n,o){this.ctx.beginPath(),this.ctx.rect(t,e,n,o),this.ctx.stroke()}draw(t){this.drawRectangle(t.x,t.y,t.width,t.height)}};var F=class extends y{constructor(){super(...arguments);this.name="circle"}move(t,e,n){return t.x+=e,t.y+=n,t}normalize(t,e,n){return x(f({},t),{x:t.x/e,y:t.y/n,radius:t.radius/e})}onPointerDown(t){let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.startX=e,this.startY=n,this.isDrawing=!0}onPointerMove(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t),o=Math.sqrt(Math.pow(e-this.startX,2)+Math.pow(n-this.startY,2));this.drawCircle(this.startX,this.startY,o)}onPointerUp(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t),o=Math.sqrt(Math.pow(e-this.startX,2)+Math.pow(n-this.startY,2));this.save({type:"circle",x:this.startX,y:this.startY,radius:o,strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth}),this.drawCircle(this.startX,this.startY,o),this.isDrawing=!1}drawCircle(t,e,n){this.ctx.beginPath(),this.ctx.arc(t,e,n,0,2*Math.PI),this.ctx.stroke()}draw(t){this.drawCircle(t.x,t.y,t.radius)}};var M=class{constructor(i,t){this.x=i;this.y=t}distanceToLine(i,t){let e=t.x-i.x,n=t.y-i.y,o=Math.abs(n*this.x-e*this.y+t.x*i.y-t.y*i.x),l=Math.sqrt(n*n+e*e);return o/l}};function D(s,i){if(s.length<=2)return s;let t=s[0],e=s[s.length-1],n=-1,o=0;for(let l=1;lo&&(n=l,o=h)}if(o>i){let l=D(s.slice(0,n+1),i),h=D(s.slice(n),i);return l.slice(0,l.length-1).concat(h)}else return[t,e]}var L=class extends y{constructor(){super(...arguments);this.name="curve";this.curvePoints=[]}move(t,e,n){return t.points=t.points.map(o=>({x:o.x+e,y:o.y+n})),t}normalize(t,e,n){return x(f({},t),{points:t.points.map(o=>({x:o.x/e,y:o.y/n}))})}draw(t){this.drawCurve(t)}reset(){super.reset(),this.curvePoints=[]}onPointerDown(t){if(this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.curvePoints=[],this.startX=e,this.startY=n,this.isDrawing=!0,this.curvePoints.push({x:e,y:n})}onPointerMove(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.curvePoints.push({x:e,y:n}),this.drawCurve({points:this.curvePoints,lineWidth:this.ctx.lineWidth})}onPointerUp(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.curvePoints.push({x:e,y:n});let o=this.curvePoints.map(m=>new M(m.x,m.y)),d={type:"curve",points:D(o,2).map(m=>({x:m.x,y:m.y})),strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth};this.save(d),this.curvePoints=[],this.isDrawing=!1}drawCurve(t){if(t.points.length===2&&t.points[0].x===t.points[1].x&&t.points[0].y===t.points[1].y){let e=t.lineWidth/4,n=0,o=2*Math.PI;this.ctx.beginPath(),this.ctx.arc(t.points[0].x,t.points[0].y,e,n,o),this.ctx.stroke()}else{this.ctx.beginPath(),this.ctx.moveTo(t.points[0].x,t.points[0].y);for(let e=1;e0){let o=this.annotationTool.globalShapes[0];if(o.type==="compare"){let l=this.annotationTool.deserialize([o])[0];this.draw(l),this.annotationTool.addFrameSquareOverlay()}}return}let{x:e}=this.annotationTool.getRelativeCoords(t);this.comparisonLine=e;let n={type:"compare",strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth,x:e};this.draw(n),this.drawDelimiter(n)}onPointerUp(){this.isDrawing&&(this.save({type:"compare",strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth,x:this.comparisonLine}),this.isDrawing=!1)}save(t){this.annotationTool.globalShapes=this.annotationTool.globalShapes.filter(n=>n.type!=="compare");let e=this.annotationTool.serialize([t])[0];e.x<.05||e.x>.95||this.annotationTool.addGlobalShape(e)}drawDelimiter(t){this.ctx.beginPath(),this.ctx.moveTo(t.x,0),this.ctx.lineTo(t.x,this.annotationTool.canvasWidth),this.ctx.stroke()}draw(t){let e=this.annotationTool.videoElement,n=this.annotationTool.referenceVideoElement;if(!e||!n)return;this.annotationTool.syncTime();let o=this.ctx.globalAlpha,l=this.annotationTool.canvasWidth,h=this.annotationTool.canvasHeight,a=t.x;this.ctx.globalAlpha=this.leftOpacity;let d=a/l,m=a;this.ctx.drawImage(e,0,0,d*e.videoWidth,e.videoHeight,0,0,m,h);let g=a,p=l-g,u=p/l*e.videoWidth;this.ctx.globalAlpha=this.rightOpacity,this.ctx.drawImage(n,g/l*e.videoWidth,0,u,e.videoHeight,g,0,p,h),this.ctx.globalAlpha=o}};var nt=[k,F,B,R,A,V,L,H,N,W];function ot(s,i){let t,e,n,o=[],l=!0;function h(m,g){let p=Math.abs(g.mediaTime-t),u=Math.abs(g.presentedFrames-e),r=p/u;r&&r<1&&l&&o.length<50&&s.playbackRate===1&&document.hasFocus()&&(o.push(r),n=Math.round(1/d()),i(n,o.length*2)),l=!0,t=g.mediaTime,e=g.presentedFrames,s.requestVideoFrameCallback(h)}s.requestVideoFrameCallback(h);let a=()=>{o.pop(),l=!1};s.addEventListener("seeked",a);function d(){return o.reduce((m,g)=>m+g)/o.length}return()=>{s.removeEventListener("seeked",a)}}var st=25,E=class{constructor(i){this.isMouseDown=!1;this.activeTimeFrame=1;this.buttons=[];this.destructors=[];this.plugins=[];this.isDestroyed=!1;this.globalShapes=[];this.timeStack=new Map;this.undoTimeStack=new Map;this.annotatedFrameCoordinates=[];this.fps=st;this.lastNavigatedFrame=0;this.isProgressBarNavigation=!1;this.plugins=nt.map(t=>new t(this)),this.init(i)}prevFrame(){let i=this.activeTimeFrame,t=Math.max(1,i-1);this.playbackFrame=t}nextFrame(){let i=this.activeTimeFrame,t=Math.min(this.videoElement.duration*this.fps,i+1);this.playbackFrame=t}addGlobalShape(i){this.globalShapes.push(i)}get selectedColor(){return this.colorPicker.value}get selectedStrokeSize(){return this.strokeSizePicker.valueAsNumber}get currentTool(){return this._currentTool}set currentTool(i){let t=this._currentTool;t&&(this.getButtonForTool(t).classList.remove("active"),this.pluginForTool(t).onDeactivate()),this._currentTool=i,this.canvas.style.cursor=i?"pointer":"default",i&&(this.getButtonForTool(i).classList.add("active"),this.pluginForTool(i).onActivate())}enableFrameRateDetection(){if(this.destructors.find(e=>e.name==="frameRateDetector"))return;let i=this.videoElement;if(i.tagName==="IMG")return;let t=ot(i,e=>{this.fps=e});Object.defineProperty(t,"name",{value:"frameRateDetector"}),this.destructors.push(t)}get playbackFrame(){if(this.videoElement instanceof HTMLImageElement)return 1;let i=Math.round(this.videoElement.currentTime*this.fps);return Math.max(1,i)}set playbackFrame(i){if(this.videoElement instanceof HTMLImageElement)return;let t=i/this.fps;this.videoElement.currentTime=t,this.syncTime(),this.show()}get canvasWidth(){return this.canvas.width/this.pixelRatio}get canvasHeight(){return this.canvas.height/this.pixelRatio}get aspectRatio(){return this.canvasWidth/this.canvasHeight}get isMobile(){return window.innerWidth<960}get progressBarCoordinates(){let i=this.isMobile?30:10,t=5,e=55,n=this.canvasWidth-t-e,o=t,l=this.canvasHeight-i;return{x:o,y:l,width:n,height:i}}get videoClientRect(){return this.videoElement.getBoundingClientRect()}get shapes(){return this.timeStack.has(this.activeTimeFrame)||this.timeStack.set(this.activeTimeFrame,[]),this.timeStack.get(this.activeTimeFrame)}set shapes(i){this.timeStack.set(this.activeTimeFrame,i)}get undoStack(){return this.undoTimeStack.has(this.activeTimeFrame)||this.undoTimeStack.set(this.activeTimeFrame,[]),this.undoTimeStack.get(this.activeTimeFrame)}set undoStack(i){this.undoTimeStack.set(this.activeTimeFrame,i)}get pixelRatio(){return window.devicePixelRatio||1}hide(){this.stopAnnotationsAsVideo(),this.hideControls(),this.hideCanvas()}showControls(){this.uiContainer.style.display="block"}hideControls(){this.uiContainer.style.display="none"}showCanvas(){this.canvas.style.display="block"}hideCanvas(){this.canvas.style.display="none"}show(){this.stopAnnotationsAsVideo(),this.activeTimeFrame=this.playbackFrame,this.showCanvas(),this.showControls(),this.redrawFullCanvas()}setCanvasSettings(){this.ctx.strokeStyle=this.selectedColor,this.ctx.fillStyle=this.selectedColor,this.ctx.lineWidth=this.selectedStrokeSize}pluginForTool(i){if(this.isDestroyed)throw new Error("AnnotationTool is destroyed");let t=this.plugins.find(e=>e.name===i);if(!t)throw new Error(`No plugin found for tool ${i}`);return t}getButtonForTool(i){return this.buttons.find(t=>t.dataset.tool===i)}bindContext(){this.handleMouseDown=this.handleMouseDown.bind(this),this.handleMouseMove=this.handleMouseMove.bind(this),this.handleMouseUp=this.handleMouseUp.bind(this),this.setCanvasSize=this.setCanvasSize.bind(this),this.onKeyDown=this.onKeyDown.bind(this)}initProperties(){this.isDestroyed=!1,this.isProgressBarNavigation=!1,this.currentTool=null,this.shapes=[],this.globalShapes=[]}init(i){this.videoElement=i,this.videoElement.style.objectFit="cover",this.videoElement.style.objectPosition="left top",this.bindContext(),this.initCanvas(),this.initUI(),this.initProperties(),this.setCanvasSize(),this.fillCanvas(),this.setCanvasSettings(),this.currentTool=this.isMobile?null:"curve"}addEvent(i,t,e){let n=o=>{this.isDestroyed||e(o)};i.addEventListener(t,n),this.destructors.push(()=>{i.removeEventListener(t,n)})}initCanvas(){throw new Error("Method not implemented.")}onKeyDown(i){(i.ctrlKey||i.metaKey)&&i.key.toLowerCase()==="z"&&this.handleUndo()}removeLastShape(){this.shapes.pop(),this.redrawFullCanvas()}handleUndo(){this.undoStack.length>0&&(this.shapes=this.undoStack.pop(),this.redrawFullCanvas())}destroy(){var n,o,l,h,a,d;if(this.isDestroyed)return;this.destructors.forEach(m=>m()),this.stopAnnotationsAsVideo(),this.destructors=[],this.globalShapes=[],this._currentTool=null,this.plugins.forEach(m=>m.reset()),this.annotatedFrameCoordinates=[],this.setFrameRate(st),this.cleanFrameStacks();let i=this.strokeSizePicker.parentElement;if((n=i==null?void 0:i.parentNode)==null||n.removeChild(i),this.referenceVideoElement){let m=this.referenceVideoElement.parentElement;(o=m==null?void 0:m.parentNode)==null||o.removeChild(m),this.referenceVideoElement=null}let t=this.colorPicker.parentElement;(l=t==null?void 0:t.parentNode)==null||l.removeChild(t),this.buttons.forEach(m=>{var g;(g=m.parentNode)==null||g.removeChild(m)}),this.buttons=[],(h=this.uiContainer.parentNode)==null||h.removeChild(this.uiContainer),(a=this.canvas.parentNode)==null||a.removeChild(this.canvas),(d=this.playerControlsContainer.parentElement)==null||d.removeChild(this.playerControlsContainer),["strokeSizePicker","colorPicker","uiContainer","playerControlsContainer","canvas","ctx","videoElement"].forEach(m=>{delete this[m]}),this.activeTimeFrame=0,this.isDestroyed=!0}setCanvasSize(){let i=this.videoClientRect;this.canvas.width=i.width*this.pixelRatio,this.canvas.height=i.height*this.pixelRatio,this.canvas.style.width=`${i.width}px`,this.canvas.style.height=`${i.height}px`,this.ctx.scale(this.pixelRatio,this.pixelRatio),this.redrawFullCanvas(),this.setCanvasSettings(),this.syncVideoSizes()}isMultiTouch(i){return i.pointerType==="touch"&&i.isPrimary===!1}addShape(i){let t=this.serialize([i])[0];this.undoStack.push([...this.shapes]),this.shapes.push(t)}syncTime(i=!1){let t=this.videoElement;if(!t||t.tagName!=="VIDEO"||!this.referenceVideoElement||this.referenceVideoElement.readyState<4||!i&&!this.globalShapes.length)return;Math.abs(this.referenceVideoElement.currentTime-t.currentTime)>=this.msPerFrame/3&&(this.referenceVideoElement.currentTime=t.currentTime)}get msPerFrame(){return this.fps/1e3}syncVideoSizes(){if(!this.referenceVideoElement)return;let t=this.videoElement.getBoundingClientRect();this.referenceVideoElement.style.position="fixed",this.referenceVideoElement.style.top=`${t.top}px`,this.referenceVideoElement.style.left=`${t.left}px`}addReferenceVideoByURL(i){return P(this,null,function*(){let t=yield fetch(i).then(o=>o.blob()),e=new Blob([t],{type:"video/mp4"}),n=window.URL.createObjectURL(e);this.referenceVideoElement||(this.referenceVideoElement=document.createElement("video"),this.referenceVideoElement.style.zIndex="-1",this.referenceVideoElement.style.display="none",this.referenceVideoElement.style.objectFit="cover",this.referenceVideoElement.style.objectPosition="left top",this.referenceVideoElement.muted=!0,this.referenceVideoElement.playsInline=!0,this.referenceVideoElement.autoplay=!1,this.referenceVideoElement.controls=!1,this.referenceVideoElement.loop=!0,this.videoElement.after(this.referenceVideoElement),this.syncVideoSizes()),this.referenceVideoElement.src=n,this.getButtonForTool("compare").style.display=""})}addSingletonShape(i){let t=this.serialize([i])[0],e=this.shapes.filter(n=>n.type!==i.type);this.replaceFrame(this.playbackFrame,[...e,t])}serialize(i=this.shapes){let t=this.canvasWidth,e=this.canvasHeight;return i.map(n=>this.pluginForTool(n.type).normalize(n,t,e))}deserialize(i){let t=1/this.canvasWidth,e=1/this.canvasHeight;return i.map(n=>this.pluginForTool(n.type).normalize(n,t,e))}getRelativeCoords(i){let t=this.canvas.getBoundingClientRect();return{x:this.getEventX(i)-t.left,y:this.getEventY(i)-t.top}}handleMouseDown(i){if(i.preventDefault(),this.isMouseDown=!0,this.isMultiTouch(i))return;let t=this.frameFromProgressBar(i,!0);if(t){this.isProgressBarNavigation=!0;let e=this.getAnnotationFrame(i);this.isVideoPaused&&(e!==null?this.playbackFrame=e:this.playbackFrame=t);return}this.currentTool&&this.pluginForTool(this.currentTool).onPointerDown(i)}get isDrawing(){return this.currentTool?this.pluginForTool(this.currentTool).isDrawing:!1}get isVideoPaused(){return this.videoElement.tagName==="VIDEO"?this.videoElement.paused:!0}handleMouseMove(i){if(i.preventDefault(),!this.isMultiTouch(i)){if(this.isMouseDown){let t=this.isProgressBarNavigation?this.frameFromProgressBar(i,!1):null;if(t!==null&&!this.isDrawing){if(t===this.lastNavigatedFrame)return;this.lastNavigatedFrame=t,this.isVideoPaused&&(this.playbackFrame=t);return}else this.hideControls(),this.clearCanvas(),this.addVideoOverlay(),this.drawShapesOverlay()}else this.redrawFullCanvas();this.currentTool&&this.pluginForTool(this.currentTool).onPointerMove(i)}}getEventX(i){return i.clientX}getEventY(i){return i.clientY}handleMouseUp(i){this.isMouseDown=!1,this.isProgressBarNavigation=!1,this.showControls(),!this.isMultiTouch(i)&&(this.currentTool&&this.pluginForTool(this.currentTool).onPointerUp(i),this.redrawFullCanvas())}focusOnMediaNode(){this.videoElement.focus()}drawShapesOverlay(){let i={strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth};this.deserialize(this.globalShapes).forEach(t=>{this.ctx.strokeStyle=t.strokeStyle,this.ctx.fillStyle=t.fillStyle,this.ctx.lineWidth=t.lineWidth;try{this.pluginForTool(t.type).draw(t)}catch(e){console.error(e)}}),this.deserialize(this.shapes).forEach(t=>{this.ctx.strokeStyle=t.strokeStyle,this.ctx.fillStyle=t.fillStyle,this.ctx.lineWidth=t.lineWidth;try{this.pluginForTool(t.type).draw(t)}catch(e){console.error(e)}}),this.ctx.strokeStyle=i.strokeStyle,this.ctx.fillStyle=i.fillStyle,this.ctx.lineWidth=i.lineWidth}clearCanvas(){this.ctx.clearRect(0,0,this.canvasWidth,this.canvasHeight)}frameToDataUrl(){try{this.clearCanvas(),this.addVideoOverlay(),this.addFrameSquareOverlay(),this.drawShapesOverlay();let i=this.canvas.toDataURL("image/png");return this.redrawFullCanvas(),i}catch(i){return console.error(i),null}}redrawFullCanvas(){this.clearCanvas(),this.addVideoOverlay(),this.drawShapesOverlay(),this.addFrameSquareOverlay(),this.addProgressBarOverlay()}replaceFrame(i,t){this.timeStack.set(i,this.parseShapes(this.stringifyShapes(t)))}addShapesToFrame(i,t){let e=this.timeStack.get(i)||[];this.timeStack.set(i,[...e,...this.parseShapes(this.stringifyShapes(t))])}setFrameRate(i){var t;(t=this.destructors.find(e=>e.name==="frameRateDetector"))==null||t(),this.fps=i}stringifyShapes(i){return JSON.stringify(i,(t,e)=>t==="image"?e.src:e)}parseShapes(i){return JSON.parse(i,(t,e)=>{if(t==="image"){let n=new Image;return n.src=e,n}return e})}filterNonSerializableShapes(i){return i.filter(t=>t.type!=="image")}saveCurrentFrame(){return{frame:this.playbackFrame,version:1,fps:this.fps,shapes:this.parseShapes(this.stringifyShapes(this.filterNonSerializableShapes(this.shapes)))}}addFrameSquareOverlay(i=this.activeTimeFrame){throw new Error("Method not implemented.")}addVideoOverlay(){throw new Error("Method not implemented.")}cleanFrameStacks(){this.timeStack.clear(),this.undoTimeStack.clear()}loadAllFrames(i){this.cleanFrameStacks(),i.forEach(t=>{this.timeStack.set(t.frame,t.shapes)})}appendFrames(i){i.forEach(t=>{this.addShapesToFrame(t.frame,t.shapes)})}saveAllFrames(){return Array.from(this.timeStack.keys()).filter(n=>{var o;return(o=this.timeStack.get(n))==null?void 0:o.length}).map(n=>{var o;return{frame:n,fps:this.fps,version:1,shapes:this.filterNonSerializableShapes((o=this.timeStack.get(n))!=null?o:[])}})}getAnnotationFrame(i){var l,h;let t=i.offsetX,e=i.offsetY,n=this.isMobile?10:5;return(h=(l=this.annotatedFrameCoordinates.find(a=>t>=a.x-n&&t<=a.x+n&&e>=a.y-n&&e<=a.y+n))==null?void 0:l.frame)!=null?h:null}frameFromProgressBar(i,t=!0){let e=this.videoElement;if(e.tagName!=="VIDEO")return null;let{x:n,width:o,height:l,y:h}=this.progressBarCoordinates,a=i.offsetX,d=i.offsetY;return t?a>=n&&a<=n+o&&d>=h&&d<=h+l?Math.ceil((a-n)/o*(e.duration*this.fps)):null:a>=n&&a<=n+o?Math.ceil((a-n)/o*(e.duration*this.fps)):null}addProgressBarOverlay(){throw new Error("Method not implemented.")}initUI(){throw new Error("Method not implemented.")}stopAnnotationsAsVideo(){clearTimeout(this.playTimeout)}hasAnnotationsForFrame(i){if(this.globalShapes.length>0)return!0;if(this.timeStack.has(i)){let t=this.timeStack.get(i);return t&&t.length>0}return!1}playAnnotationsAsVideo(){this.stopAnnotationsAsVideo();let i=this.playbackFrame;this.hasAnnotationsForFrame(i)?(this.showCanvas(),this.activeTimeFrame=i,this.syncTime(),this.clearCanvas(),this.drawShapesOverlay()):this.hideCanvas();let t=1e3/this.fps;requestAnimationFrame(()=>{this.syncTime()}),this.playTimeout=window.setTimeout(()=>{this.playAnnotationsAsVideo()},t)}fillCanvas(){}};function rt(s=this.activeTimeFrame){this.ctx.save(),this.ctx.fillStyle="rgba(0, 0, 0, 0.5)";let i=50,t=30,e=20;this.ctx.fillRect(this.canvasWidth-i,this.canvasHeight-t,i,t),this.ctx.fillStyle="white",this.ctx.font=`${e}px sans-serif`,this.ctx.fillText(`${s}`.padStart(3,"0"),this.canvasWidth-40,this.canvasHeight-7),this.ctx.restore()}function at(){let s=this.videoElement;s.tagName==="VIDEO"&&this.ctx.drawImage(s,0,0,s.videoWidth,s.videoHeight,0,0,this.canvasWidth,this.canvasHeight)}function lt(){let s=this.videoElement;if(s.tagName!=="VIDEO")return;this.annotatedFrameCoordinates=[];let t=Array.from(this.timeStack.keys()).filter(r=>{var c;return(c=this.timeStack.get(r))==null?void 0:c.length}),e=s.duration*this.fps,{x:n,width:o,height:l,y:h}=this.progressBarCoordinates,a=t.map(r=>Math.ceil(r/e*o));this.ctx.save(),this.ctx.fillStyle="rgba(0, 0, 0, 0.5)",this.ctx.fillRect(n,h,o,l),this.ctx.fillStyle="#F3CE32";let d=this.isMobile?16:8;a.forEach((r,c)=>{this.ctx.beginPath();let v=n+r,w=this.canvasHeight-l/2;this.ctx.fillRect(v-d/2,w-d/2,d,d),this.annotatedFrameCoordinates.push({x:v,y:w,frame:t[c]})});let m=this.playbackFrame,g=Math.ceil(m/e*o)+n;this.ctx.fillStyle="white",this.ctx.beginPath();let p=g,u=this.canvasHeight-l/2;this.ctx.beginPath(),this.ctx.fillRect(p-d/2,u-d/2,d,d),this.ctx.fill(),this.ctx.restore()}E.prototype.initUI=et;E.prototype.initCanvas=it;E.prototype.addFrameSquareOverlay=rt;E.prototype.addVideoOverlay=at;E.prototype.addProgressBarOverlay=lt;new EventSource("/esbuild").addEventListener("change",()=>location.reload());var T=document.querySelector("video");function ht(){return P(this,null,function*(){let s=window.getComputedStyle(T),i=parseFloat(s.width),t=parseFloat(s.height),e=window.innerHeight-80;if(t>e){let u=i/t,r=e,c=r*u;T.style.width=`${c}px`,T.style.height=`${r}px`}let n=yield fetch(T.currentSrc).then(u=>u.blob()),o=new Promise(u=>{setTimeout(()=>{u(!0)},250),T.addEventListener("loadeddata",()=>{u(!0)},{once:!0})}),l=yield fetch("./frame_29.png").then(u=>P(this,null,function*(){let r=yield u.blob(),c=new Blob([r],{type:"image/png"}),v=new Image,w=new Promise(b=>{v.addEventListener("load",()=>{b(!0)},{once:!0})});return v.src=window.URL.createObjectURL(c),yield w,v}));T.paused||T.pause();let h=new Blob([n],{type:"video/mp4"});T.src=window.URL.createObjectURL(h),yield o;let a=new E(T);yield a.addReferenceVideoByURL("./mov_bbb_g.mp4"),a.setFrameRate(30),requestAnimationFrame(()=>{a.setCanvasSize()}),console.log({tool:a}),a.addShapesToFrame(29,[{type:"image",image:l,x:0,y:0,width:1,height:1,strokeStyle:"red",lineWidth:2,fillStyle:"red"}]),T.paused||T.pause(),setInterval(()=>{a.destroy(),a.init(T)},1e8),setInterval(()=>{console.log(a.saveAllFrames())},1e5);let d=document.getElementById("file"),m=document.getElementById("download"),g=document.getElementById("sample");document.getElementById("save-image").addEventListener("click",u=>{u.stopPropagation(),u.preventDefault();let r=a.frameToDataUrl();if(!r)return;let c=a.activeTimeFrame,v=document.createElement("a");v.href=r,v.download=`frame_${String(c).padStart(3,"0")}.png`,v.click()}),g.addEventListener("click",u=>{u.stopPropagation(),u.preventDefault(),fetch("./annotations-sample.json").then(r=>r.json()).then(r=>{a.loadAllFrames(r),a.redrawFullCanvas()})}),d.addEventListener("change",u=>{if(!d.files||d.files.length===0)return;let r=d.files[0],c=new FileReader;c.onload=v=>{if(!v.target)return;let w=v.target.result,b=JSON.parse(w);confirm("Append to existing annotations?")?a.appendFrames(b):a.loadAllFrames(b),a.redrawFullCanvas()},c.readAsText(r)}),m.addEventListener("click",u=>{u.stopPropagation(),u.preventDefault();let r=a.saveAllFrames(),c=document.createElement("a");c.href=URL.createObjectURL(new Blob([JSON.stringify(r)],{type:"application/json"}));let v=new Date().toISOString().replace(/:/g,"-");c.download=`annotations-${v}.json`,c.click()})})}T.readyState===0?T.addEventListener("loadedmetadata",()=>{requestAnimationFrame(()=>{ht()})},{once:!0}):ht();})();
diff --git a/dist/index.js b/dist/index.js
index ba8efbb..dacd47d 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1,4 +1,4 @@
-var ht=Object.defineProperty,ct=Object.defineProperties;var dt=Object.getOwnPropertyDescriptors;var X=Object.getOwnPropertySymbols;var mt=Object.prototype.hasOwnProperty,ut=Object.prototype.propertyIsEnumerable;var Y=(s,i,t)=>i in s?ht(s,i,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[i]=t,y=(s,i)=>{for(var t in i||(i={}))mt.call(i,t)&&Y(s,t,i[t]);if(X)for(var t of X(i))ut.call(i,t)&&Y(s,t,i[t]);return s},f=(s,i)=>ct(s,dt(i));var U=(s,i,t)=>new Promise((e,n)=>{var o=l=>{try{h(t.next(l))}catch(c){n(c)}},a=l=>{try{h(t.throw(l))}catch(c){n(c)}},h=l=>l.done?e(l.value):Promise.resolve(l.value).then(o,a);h((t=t.apply(s,i)).next())});function j(s,i){let t=document.createElement("button");t.type="button",t.style.margin="5px",t.style.float="right",t.title="Download current frame",t.innerHTML='',i.addEvent(t,"click",()=>{let e=i.frameToDataUrl();if(!e)return;let n=document.createElement("a");n.download=`frame_${String(i.activeTimeFrame).padStart(3,"0")}.png`,n.href=e,n.click()}),i.buttons.push(t),i.playerControlsContainer.appendChild(t)}var q='',K='';function $(s,i){let t=document.createElement("button");t.type="button",t.style.margin="5px",s.muted||s.volume===0?t.innerHTML=K:t.innerHTML=q,i.addEvent(s,"volumechange",()=>{s.muted||s.volume===0?t.innerHTML=q:t.innerHTML=K}),i.addEvent(t,"click",()=>{s.muted&&(s.muted=!1),s.volume===0?s.volume=1:s.volume=0}),i.buttons.push(t),i.playerControlsContainer.appendChild(t)}var _='',pt='';function J(s,i){let t=document.createElement("button");t.type="button",t.innerHTML=_,t.style.margin="5px";let e=function(n){i.referenceVideoElement&&n(i.referenceVideoElement)};i.addEvent(s,"play",()=>{t.innerHTML=pt}),i.addEvent(s,"pause",()=>{t.innerHTML=_,i.syncTime()}),i.addEvent(t,"click",()=>{s.paused?(e(n=>{n.paused&&(n.play(),i.syncTime())}),s.play()):(e(n=>{n.paused||n.pause()}),s.pause(),requestAnimationFrame(()=>{i.syncTime(),i.redrawFullCanvas()}))}),i.buttons.push(t),i.playerControlsContainer.appendChild(t)}function G(s){return`
+var ct=Object.defineProperty,dt=Object.defineProperties;var mt=Object.getOwnPropertyDescriptors;var Y=Object.getOwnPropertySymbols;var ut=Object.prototype.hasOwnProperty,pt=Object.prototype.propertyIsEnumerable;var U=(s,i,t)=>i in s?ct(s,i,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[i]=t,g=(s,i)=>{for(var t in i||(i={}))ut.call(i,t)&&U(s,t,i[t]);if(Y)for(var t of Y(i))pt.call(i,t)&&U(s,t,i[t]);return s},f=(s,i)=>dt(s,mt(i));var j=(s,i,t)=>new Promise((e,n)=>{var o=l=>{try{h(t.next(l))}catch(c){n(c)}},a=l=>{try{h(t.throw(l))}catch(c){n(c)}},h=l=>l.done?e(l.value):Promise.resolve(l.value).then(o,a);h((t=t.apply(s,i)).next())});function q(s,i){let t=document.createElement("button");t.type="button",t.style.margin="5px",t.style.float="right",t.title="Download current frame",t.innerHTML='',i.addEvent(t,"click",()=>{let e=i.frameToDataUrl();if(!e)return;let n=document.createElement("a");n.download=`frame_${String(i.activeTimeFrame).padStart(3,"0")}.png`,n.href=e,n.click()}),i.buttons.push(t),i.playerControlsContainer.appendChild(t)}var K='',$='';function _(s,i){let t=document.createElement("button");t.type="button",t.style.margin="5px",s.muted||s.volume===0?t.innerHTML=$:t.innerHTML=K,i.addEvent(s,"volumechange",()=>{s.muted||s.volume===0?t.innerHTML=K:t.innerHTML=$}),i.addEvent(t,"click",()=>{s.muted&&(s.muted=!1),s.volume===0?s.volume=1:s.volume=0}),i.buttons.push(t),i.playerControlsContainer.appendChild(t)}var J='',vt='';function G(s,i){let t=document.createElement("button");t.type="button",t.innerHTML=J,t.style.margin="5px";let e=function(n){i.referenceVideoElement&&n(i.referenceVideoElement)};i.addEvent(s,"play",()=>{t.innerHTML=vt}),i.addEvent(s,"pause",()=>{t.innerHTML=J,i.syncTime()}),i.addEvent(t,"click",()=>{s.paused?(e(n=>{n.paused&&n.play().then(()=>{i.syncTime()})}),s.play().then(()=>{i.syncTime()})):(e(n=>{n.paused||n.pause()}),s.pause(),requestAnimationFrame(()=>{i.syncTime(),i.redrawFullCanvas()}))}),i.buttons.push(t),i.playerControlsContainer.appendChild(t)}function Z(s){return`
${{"0.25":"\xBC","0.5":"\xBD","0.75":"\xBE",1:"1\xD7"}[String(s)]}
- `}function Z(s,i){let t=[.25,.5,.75,1],e=document.createElement("button"),n=t[t.length-1],o=function(a){i.referenceVideoElement&&a(i.referenceVideoElement)};e.type="button",s.playbackRate=n,o(a=>{a.playbackRate=n}),e.innerHTML=G(n),e.style.margin="5px",i.addEvent(e,"click",()=>{let a=t.indexOf(s.playbackRate),h=a+1>=t.length?0:a+1;s.playbackRate=t[h],o(l=>{l.playbackRate=t[h]}),e.innerHTML=G(t[h])}),i.buttons.push(e),i.playerControlsContainer.appendChild(e)}var vt="#F3CE32";function Q(){var d,g;let s=document.createElement("div");s.style.position="absolute",s.style.top="-40px",s.style.left="0",s.style.zIndex="2",(d=this.canvas.parentNode)==null||d.insertBefore(s,this.canvas);let i=document.createElement("div");i.style.position="relative",i.style.top="0",i.style.left="0",i.style.zIndex="2",(g=this.canvas.parentNode)==null||g.insertBefore(i,this.canvas.nextSibling),this.playerControlsContainer=i;let t=this.videoElement.tagName==="VIDEO"?this.videoElement:null;this.uiContainer=s;let e=(m,r,u=s)=>{let p=document.createElement("button");if(p.type="button",p.innerHTML=m,p.style.margin="5px",u.appendChild(p),this.buttons.push(p),typeof r=="function")this.addEvent(p,"click",r);else{p.dataset.tool=r;let x=()=>{this.currentTool===r?this.currentTool=null:this.currentTool=r};this.addEvent(p,"click",x)}return p},n=()=>{let m=document.createElement("div");return m.style.display="inline-flex",m.style.alignItems="center",m.style.margin="5px",s.appendChild(m),m};e('',"rectangle"),e('',"circle"),e('',"line"),e('',"curve"),e('',"arrow"),e('',"text"),e('',"eraser"),e('',"move"),e('',"compare"),e('',()=>{this.handleUndo()}),t&&(e('',()=>{this.prevFrame()},this.playerControlsContainer),J(t,this),e('',()=>{this.nextFrame()},this.playerControlsContainer),$(t,this),Z(t,this),j(t,this));let o=document.createElement("input");o.type="color",o.value=vt,o.style.margin="5px",this.colorPicker=o,s.appendChild(o);let a=n(),h=document.createElement("input");h.type="number",h.step="1",h.min="1",h.max="10",h.value="5",h.style.margin="5px",a.appendChild(h);let l=m=>{this.ctx.lineWidth=m.target.valueAsNumber,this.focusOnMediaNode()};this.addEvent(h,"input",l);let c=m=>{this.ctx.strokeStyle=m.target.value,this.ctx.fillStyle=m.target.value,this.focusOnMediaNode()};if(this.addEvent(o,"input",c),this.colorPicker=o,this.strokeSizePicker=h,this.getButtonForTool("compare").style.display="none",t){this.hide(),this.addEvent(t,"pause",()=>{this.show()}),this.addEvent(t,"seek",()=>{t.paused&&this.show(),this.syncTime()}),this.addEvent(t,"timeupdate",()=>{t.currentTime<2e-4&&!t.paused&&this.playAnnotationsAsVideo(),this.syncTime()}),this.addEvent(t,"seeking",()=>{this.syncTime()}),this.addEvent(t,"error",()=>{this.hide()}),this.addEvent(t,"stalled",()=>{this.hide()}),this.addEvent(t,"waiting",()=>{this.hide()}),this.addEvent(t,"ended",()=>{this.hide()}),this.addEvent(t,"play",()=>{this.hideControls(),this.playAnnotationsAsVideo()});let m=r=>{let u=r.target===document.body,p=this.uiContainer.contains(r.target),x=this.playerControlsContainer.contains(r.target),T=this.videoElement.contains(r.target),S=this.canvas.contains(r.target);return p||x||T||S||u};this.addEvent(document,"copy",r=>{var u;m(r)&&(r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation(),(u=r.clipboardData)==null||u.setData("application/json",JSON.stringify(this.saveCurrentFrame())))}),this.addEvent(document,"cut",r=>{var p;if(!m(r))return;r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation();let u=this.saveCurrentFrame();this.replaceFrame(this.playbackFrame,[]),this.redrawFullCanvas(),(p=r.clipboardData)==null||p.setData("application/json",JSON.stringify(u))}),this.addEvent(document,"paste",r=>{var T,S,H,N,W;if(!m(r))return;let u=(S=(T=r.clipboardData)==null?void 0:T.types)!=null?S:[];if(console.log("dataTypes",JSON.stringify(u)),u.includes("application/json"))r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation();else if(u.includes("Files")){let b=(H=r.clipboardData)==null?void 0:H.files;if(b&&b.length>0){let z=b[0];if(z.type.startsWith("image/")){r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation();let E=new Image;E.addEventListener("load",()=>{let at=E.naturalWidth/E.naturalHeight,O=.25,lt=O/at*this.aspectRatio;this.addShapesToFrame(this.playbackFrame,[{type:"image",image:E,x:0,y:0,width:O,height:lt,strokeStyle:"red",fillStyle:"red",lineWidth:2}]),this.redrawFullCanvas(),requestAnimationFrame(()=>{this.show()}),this.currentTool="move"},{once:!0}),E.src=URL.createObjectURL(z),this.redrawFullCanvas()}}}else if(u.includes("text/plain")){let b=(N=r.clipboardData)==null?void 0:N.getData("text/plain");b&&(console.log("text",b),r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation(),this.addShapesToFrame(this.playbackFrame,[{type:"text",text:b,x:.4,y:.4,strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth}]),this.show(),this.currentTool="move",this.redrawFullCanvas())}else return;let p=(W=r.clipboardData)==null?void 0:W.getData("application/json");if(!p)return;let x=JSON.parse(p);x&&x.shapes&&x.version===1&&(this.addShapesToFrame(this.playbackFrame,x.shapes),this.redrawFullCanvas())}),this.addEvent(document,"click",r=>{if(!m(r))return;let u=this.uiContainer.contains(r.target),p=this.playerControlsContainer.contains(r.target);u||p||t.paused||(this.currentTool=null,t.pause(),requestAnimationFrame(()=>{this.syncTime(),this.redrawFullCanvas()}))}),this.addEvent(document,"keydown",r=>{m(r)&&(r.key==="ArrowLeft"||r.key==="ArrowRight"?(r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation(),r.key==="ArrowLeft"?this.prevFrame():r.key==="ArrowRight"&&this.nextFrame()):r.code==="Space"&&(r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation(),t.paused?t.play():(t.pause(),requestAnimationFrame(()=>{this.syncTime(),this.redrawFullCanvas()}))))})}}function tt(){var s;this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d"),(s=this.videoElement.parentNode)==null||s.insertBefore(this.canvas,this.videoElement.nextSibling),this.canvas.style.position="absolute",this.canvas.style.top="0",this.canvas.style.left="0",this.canvas.style.zIndex="1",this.addEvent(this.canvas,"pointerdown",this.handleMouseDown),this.addEvent(this.canvas,"pointermove",this.handleMouseMove),this.addEvent(this.canvas,"pointerup",this.handleMouseUp),this.addEvent(this.canvas,"pointercancel",this.handleMouseUp),this.addEvent(window,"resize",this.setCanvasSize),this.addEvent(document,"keydown",this.onKeyDown)}var v=class{constructor(i){this.startX=0;this.startY=0;this.isDrawing=!1;this.annotationTool=i}get ctx(){return this.annotationTool.ctx}onDeactivate(){}onActivate(){}reset(){this.startX=0,this.startY=0,this.isDrawing=!1}save(i){this.annotationTool.addShape(i)}};var C=class extends v{constructor(){super(...arguments);this.name="rectangle"}move(t,e,n){return t.x+=e,t.y+=n,t}normalize(t,e,n){return f(y({},t),{x:t.x/e,y:t.y/n,width:t.width/e,height:t.height/n})}onPointerDown(t){let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.startX=e,this.startY=n,this.isDrawing=!0}onPointerMove(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.drawRectangle(this.startX,this.startY,e-this.startX,n-this.startY)}onPointerUp(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.save({type:"rectangle",x:this.startX,y:this.startY,width:e-this.startX,height:n-this.startY,strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth}),this.drawRectangle(this.startX,this.startY,e-this.startX,n-this.startY),this.isDrawing=!1}drawRectangle(t,e,n,o){this.ctx.beginPath(),this.ctx.rect(t,e,n,o),this.ctx.stroke()}draw(t){this.drawRectangle(t.x,t.y,t.width,t.height)}};var k=class extends v{constructor(){super(...arguments);this.name="circle"}move(t,e,n){return t.x+=e,t.y+=n,t}normalize(t,e,n){return f(y({},t),{x:t.x/e,y:t.y/n,radius:t.radius/e})}onPointerDown(t){let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.startX=e,this.startY=n,this.isDrawing=!0}onPointerMove(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t),o=Math.sqrt(Math.pow(e-this.startX,2)+Math.pow(n-this.startY,2));this.drawCircle(this.startX,this.startY,o)}onPointerUp(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t),o=Math.sqrt(Math.pow(e-this.startX,2)+Math.pow(n-this.startY,2));this.save({type:"circle",x:this.startX,y:this.startY,radius:o,strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth}),this.drawCircle(this.startX,this.startY,o),this.isDrawing=!1}drawCircle(t,e,n){this.ctx.beginPath(),this.ctx.arc(t,e,n,0,2*Math.PI),this.ctx.stroke()}draw(t){this.drawCircle(t.x,t.y,t.radius)}};var P=class{constructor(i,t){this.x=i;this.y=t}distanceToLine(i,t){let e=t.x-i.x,n=t.y-i.y,o=Math.abs(n*this.x-e*this.y+t.x*i.y-t.y*i.x),a=Math.sqrt(n*n+e*e);return o/a}};function I(s,i){if(s.length<=2)return s;let t=s[0],e=s[s.length-1],n=-1,o=0;for(let a=1;ao&&(n=a,o=h)}if(o>i){let a=I(s.slice(0,n+1),i),h=I(s.slice(n),i);return a.slice(0,a.length-1).concat(h)}else return[t,e]}var M=class extends v{constructor(){super(...arguments);this.name="curve";this.curvePoints=[]}move(t,e,n){return t.points=t.points.map(o=>({x:o.x+e,y:o.y+n})),t}normalize(t,e,n){return f(y({},t),{points:t.points.map(o=>({x:o.x/e,y:o.y/n}))})}draw(t){this.drawCurve(t)}reset(){super.reset(),this.curvePoints=[]}onPointerDown(t){if(this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.curvePoints=[],this.startX=e,this.startY=n,this.isDrawing=!0,this.curvePoints.push({x:e,y:n})}onPointerMove(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.curvePoints.push({x:e,y:n}),this.drawCurve({points:this.curvePoints,lineWidth:this.ctx.lineWidth})}onPointerUp(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.curvePoints.push({x:e,y:n});let o=this.curvePoints.map(d=>new P(d.x,d.y)),c={type:"curve",points:I(o,2).map(d=>({x:d.x,y:d.y})),strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth};this.save(c),this.curvePoints=[],this.isDrawing=!1}drawCurve(t){if(t.points.length===2&&t.points[0].x===t.points[1].x&&t.points[0].y===t.points[1].y){let e=t.lineWidth/4,n=0,o=2*Math.PI;this.ctx.beginPath(),this.ctx.arc(t.points[0].x,t.points[0].y,e,n,o),this.ctx.stroke()}else{this.ctx.beginPath(),this.ctx.moveTo(t.points[0].x,t.points[0].y);for(let e=1;e0){let o=this.annotationTool.globalShapes[0];if(o.type==="compare"){let a=this.annotationTool.deserialize([o])[0];this.draw(a),this.annotationTool.addFrameSquareOverlay()}}return}let{x:e}=this.annotationTool.getRelativeCoords(t);this.comparisonLine=e;let n={type:"compare",strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth,x:e};this.draw(n),this.drawDelimiter(n)}onPointerUp(){this.isDrawing&&(this.save({type:"compare",strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth,x:this.comparisonLine}),this.isDrawing=!1)}save(t){this.annotationTool.globalShapes=this.annotationTool.globalShapes.filter(n=>n.type!=="compare");let e=this.annotationTool.serialize([t])[0];e.x<.05||e.x>.95||this.annotationTool.addGlobalShape(e)}drawDelimiter(t){this.ctx.beginPath(),this.ctx.moveTo(t.x,0),this.ctx.lineTo(t.x,this.annotationTool.canvasWidth),this.ctx.stroke()}draw(t){let e=this.annotationTool.videoElement,n=this.annotationTool.referenceVideoElement;if(!e||!n)return;this.annotationTool.syncTime();let o=this.ctx.globalAlpha,a=this.annotationTool.canvasWidth,h=this.annotationTool.canvasHeight,l=t.x;this.ctx.globalAlpha=this.leftOpacity;let c=l/a,d=l;this.ctx.drawImage(e,0,0,c*e.videoWidth,e.videoHeight,0,0,d,h);let g=l,m=a-g,r=m/a*e.videoWidth;this.ctx.globalAlpha=this.rightOpacity,this.ctx.drawImage(n,g/a*e.videoWidth,0,r,e.videoHeight,g,0,m,h),this.ctx.globalAlpha=o}};var et=[C,k,F,D,L,B,M,R,A,V];function it(s,i){let t,e,n,o=[],a=!0;function h(d,g){let m=Math.abs(g.mediaTime-t),r=Math.abs(g.presentedFrames-e),u=m/r;u&&u<1&&a&&o.length<50&&s.playbackRate===1&&document.hasFocus()&&(o.push(u),n=Math.round(1/c()),i(n,o.length*2)),a=!0,t=g.mediaTime,e=g.presentedFrames,s.requestVideoFrameCallback(h)}s.requestVideoFrameCallback(h);let l=()=>{o.pop(),a=!1};s.addEventListener("seeked",l);function c(){return o.reduce((d,g)=>d+g)/o.length}return()=>{s.removeEventListener("seeked",l)}}var nt=25,w=class{constructor(i){this.isMouseDown=!1;this.activeTimeFrame=1;this.buttons=[];this.destructors=[];this.plugins=[];this.isDestroyed=!1;this.globalShapes=[];this.timeStack=new Map;this.undoTimeStack=new Map;this.annotatedFrameCoordinates=[];this.fps=nt;this.lastNavigatedFrame=0;this.isProgressBarNavigation=!1;this.plugins=et.map(t=>new t(this)),this.init(i)}prevFrame(){let i=this.activeTimeFrame,t=Math.max(1,i-1);this.playbackFrame=t}nextFrame(){let i=this.activeTimeFrame,t=Math.min(this.videoElement.duration*this.fps,i+1);this.playbackFrame=t}addGlobalShape(i){this.globalShapes.push(i)}get selectedColor(){return this.colorPicker.value}get selectedStrokeSize(){return this.strokeSizePicker.valueAsNumber}get currentTool(){return this._currentTool}set currentTool(i){let t=this._currentTool;t&&(this.getButtonForTool(t).classList.remove("active"),this.pluginForTool(t).onDeactivate()),this._currentTool=i,this.canvas.style.cursor=i?"pointer":"default",i&&(this.getButtonForTool(i).classList.add("active"),this.pluginForTool(i).onActivate())}enableFrameRateDetection(){if(this.destructors.find(e=>e.name==="frameRateDetector"))return;let i=this.videoElement;if(i.tagName==="IMG")return;let t=it(i,e=>{this.fps=e});Object.defineProperty(t,"name",{value:"frameRateDetector"}),this.destructors.push(t)}get playbackFrame(){if(this.videoElement instanceof HTMLImageElement)return 1;let i=Math.round(this.videoElement.currentTime*this.fps);return Math.max(1,i)}set playbackFrame(i){if(this.videoElement instanceof HTMLImageElement)return;let t=i/this.fps;this.videoElement.currentTime=t,this.syncTime(),this.show()}get canvasWidth(){return this.canvas.width/this.pixelRatio}get canvasHeight(){return this.canvas.height/this.pixelRatio}get aspectRatio(){return this.canvasWidth/this.canvasHeight}get isMobile(){return window.innerWidth<960}get progressBarCoordinates(){let i=this.isMobile?30:10,t=5,e=55,n=this.canvasWidth-t-e,o=t,a=this.canvasHeight-i;return{x:o,y:a,width:n,height:i}}get videoClientRect(){return this.videoElement.getBoundingClientRect()}get shapes(){return this.timeStack.has(this.activeTimeFrame)||this.timeStack.set(this.activeTimeFrame,[]),this.timeStack.get(this.activeTimeFrame)}set shapes(i){this.timeStack.set(this.activeTimeFrame,i)}get undoStack(){return this.undoTimeStack.has(this.activeTimeFrame)||this.undoTimeStack.set(this.activeTimeFrame,[]),this.undoTimeStack.get(this.activeTimeFrame)}set undoStack(i){this.undoTimeStack.set(this.activeTimeFrame,i)}get pixelRatio(){return window.devicePixelRatio||1}hide(){this.stopAnnotationsAsVideo(),this.hideControls(),this.hideCanvas()}showControls(){this.uiContainer.style.display="block"}hideControls(){this.uiContainer.style.display="none"}showCanvas(){this.canvas.style.display="block"}hideCanvas(){this.canvas.style.display="none"}show(){this.stopAnnotationsAsVideo(),this.activeTimeFrame=this.playbackFrame,this.showCanvas(),this.showControls(),this.redrawFullCanvas()}setCanvasSettings(){this.ctx.strokeStyle=this.selectedColor,this.ctx.fillStyle=this.selectedColor,this.ctx.lineWidth=this.selectedStrokeSize}pluginForTool(i){if(this.isDestroyed)throw new Error("AnnotationTool is destroyed");let t=this.plugins.find(e=>e.name===i);if(!t)throw new Error(`No plugin found for tool ${i}`);return t}getButtonForTool(i){return this.buttons.find(t=>t.dataset.tool===i)}bindContext(){this.handleMouseDown=this.handleMouseDown.bind(this),this.handleMouseMove=this.handleMouseMove.bind(this),this.handleMouseUp=this.handleMouseUp.bind(this),this.setCanvasSize=this.setCanvasSize.bind(this),this.onKeyDown=this.onKeyDown.bind(this)}initProperties(){this.isDestroyed=!1,this.isProgressBarNavigation=!1,this.currentTool=null,this.shapes=[],this.globalShapes=[]}init(i){this.videoElement=i,this.videoElement.style.objectFit="cover",this.videoElement.style.objectPosition="left top",this.bindContext(),this.initCanvas(),this.initUI(),this.initProperties(),this.setCanvasSize(),this.fillCanvas(),this.setCanvasSettings(),this.currentTool=this.isMobile?null:"curve"}addEvent(i,t,e){let n=o=>{this.isDestroyed||e(o)};i.addEventListener(t,n),this.destructors.push(()=>{i.removeEventListener(t,n)})}initCanvas(){throw new Error("Method not implemented.")}onKeyDown(i){(i.ctrlKey||i.metaKey)&&i.key.toLowerCase()==="z"&&this.handleUndo()}removeLastShape(){this.shapes.pop(),this.redrawFullCanvas()}handleUndo(){this.undoStack.length>0&&(this.shapes=this.undoStack.pop(),this.redrawFullCanvas())}destroy(){var n,o,a,h,l,c;if(this.isDestroyed)return;this.destructors.forEach(d=>d()),this.stopAnnotationsAsVideo(),this.destructors=[],this.globalShapes=[],this._currentTool=null,this.plugins.forEach(d=>d.reset()),this.annotatedFrameCoordinates=[],this.setFrameRate(nt),this.cleanFrameStacks();let i=this.strokeSizePicker.parentElement;if((n=i==null?void 0:i.parentNode)==null||n.removeChild(i),this.referenceVideoElement){let d=this.referenceVideoElement.parentElement;(o=d==null?void 0:d.parentNode)==null||o.removeChild(d),this.referenceVideoElement=null}let t=this.colorPicker.parentElement;(a=t==null?void 0:t.parentNode)==null||a.removeChild(t),this.buttons.forEach(d=>{var g;(g=d.parentNode)==null||g.removeChild(d)}),this.buttons=[],(h=this.uiContainer.parentNode)==null||h.removeChild(this.uiContainer),(l=this.canvas.parentNode)==null||l.removeChild(this.canvas),(c=this.playerControlsContainer.parentElement)==null||c.removeChild(this.playerControlsContainer),["strokeSizePicker","colorPicker","uiContainer","playerControlsContainer","canvas","ctx","videoElement"].forEach(d=>{delete this[d]}),this.activeTimeFrame=0,this.isDestroyed=!0}setCanvasSize(){let i=this.videoClientRect;this.canvas.width=i.width*this.pixelRatio,this.canvas.height=i.height*this.pixelRatio,this.canvas.style.width=`${i.width}px`,this.canvas.style.height=`${i.height}px`,this.ctx.scale(this.pixelRatio,this.pixelRatio),this.redrawFullCanvas(),this.setCanvasSettings(),this.syncVideoSizes()}isMultiTouch(i){return i.pointerType==="touch"&&i.isPrimary===!1}addShape(i){let t=this.serialize([i])[0];this.undoStack.push([...this.shapes]),this.shapes.push(t)}syncTime(i=!1){let t=this.videoElement;if(!t||t.tagName!=="VIDEO"||!this.referenceVideoElement||this.referenceVideoElement.readyState<4||!i&&!this.globalShapes.length)return;Math.abs(this.referenceVideoElement.currentTime-t.currentTime)>=this.msPerFrame/3&&(this.referenceVideoElement.currentTime=t.currentTime)}get msPerFrame(){return this.fps/1e3}syncVideoSizes(){if(!this.referenceVideoElement)return;let t=this.videoElement.getBoundingClientRect();this.referenceVideoElement.style.position="fixed",this.referenceVideoElement.style.top=`${t.top}px`,this.referenceVideoElement.style.left=`${t.left}px`}addReferenceVideoByURL(i){return U(this,null,function*(){let t=yield fetch(i).then(o=>o.blob()),e=new Blob([t],{type:"video/mp4"}),n=window.URL.createObjectURL(e);this.referenceVideoElement||(this.referenceVideoElement=document.createElement("video"),this.referenceVideoElement.style.zIndex="-1",this.referenceVideoElement.style.display="none",this.referenceVideoElement.style.objectFit="cover",this.referenceVideoElement.style.objectPosition="left top",this.referenceVideoElement.muted=!0,this.referenceVideoElement.playsInline=!0,this.referenceVideoElement.autoplay=!1,this.referenceVideoElement.controls=!1,this.referenceVideoElement.loop=!0,this.videoElement.after(this.referenceVideoElement),this.syncVideoSizes()),this.referenceVideoElement.src=n,this.getButtonForTool("compare").style.display=""})}addSingletonShape(i){let t=this.serialize([i])[0],e=this.shapes.filter(n=>n.type!==i.type);this.replaceFrame(this.playbackFrame,[...e,t])}serialize(i=this.shapes){let t=this.canvasWidth,e=this.canvasHeight;return i.map(n=>this.pluginForTool(n.type).normalize(n,t,e))}deserialize(i){let t=1/this.canvasWidth,e=1/this.canvasHeight;return i.map(n=>this.pluginForTool(n.type).normalize(n,t,e))}getRelativeCoords(i){let t=this.canvas.getBoundingClientRect();return{x:this.getEventX(i)-t.left,y:this.getEventY(i)-t.top}}handleMouseDown(i){if(i.preventDefault(),this.isMouseDown=!0,this.isMultiTouch(i))return;let t=this.frameFromProgressBar(i,!0);if(t){this.isProgressBarNavigation=!0;let e=this.getAnnotationFrame(i);this.isVideoPaused&&(e!==null?this.playbackFrame=e:this.playbackFrame=t);return}this.currentTool&&this.pluginForTool(this.currentTool).onPointerDown(i)}get isDrawing(){return this.currentTool?this.pluginForTool(this.currentTool).isDrawing:!1}get isVideoPaused(){return this.videoElement.tagName==="VIDEO"?this.videoElement.paused:!0}handleMouseMove(i){if(i.preventDefault(),!this.isMultiTouch(i)){if(this.isMouseDown){let t=this.isProgressBarNavigation?this.frameFromProgressBar(i,!1):null;if(t!==null&&!this.isDrawing){if(t===this.lastNavigatedFrame)return;this.lastNavigatedFrame=t,this.isVideoPaused&&(this.playbackFrame=t);return}else this.hideControls(),this.clearCanvas(),this.addVideoOverlay(),this.drawShapesOverlay()}else this.redrawFullCanvas();this.currentTool&&this.pluginForTool(this.currentTool).onPointerMove(i)}}getEventX(i){return i.clientX}getEventY(i){return i.clientY}handleMouseUp(i){this.isMouseDown=!1,this.isProgressBarNavigation=!1,this.showControls(),!this.isMultiTouch(i)&&(this.currentTool&&this.pluginForTool(this.currentTool).onPointerUp(i),this.redrawFullCanvas())}focusOnMediaNode(){this.videoElement.focus()}drawShapesOverlay(){let i={strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth};this.deserialize(this.globalShapes).forEach(t=>{this.ctx.strokeStyle=t.strokeStyle,this.ctx.fillStyle=t.fillStyle,this.ctx.lineWidth=t.lineWidth;try{this.pluginForTool(t.type).draw(t)}catch(e){console.error(e)}}),this.deserialize(this.shapes).forEach(t=>{this.ctx.strokeStyle=t.strokeStyle,this.ctx.fillStyle=t.fillStyle,this.ctx.lineWidth=t.lineWidth;try{this.pluginForTool(t.type).draw(t)}catch(e){console.error(e)}}),this.ctx.strokeStyle=i.strokeStyle,this.ctx.fillStyle=i.fillStyle,this.ctx.lineWidth=i.lineWidth}clearCanvas(){this.ctx.clearRect(0,0,this.canvasWidth,this.canvasHeight)}frameToDataUrl(){try{this.clearCanvas(),this.addVideoOverlay(),this.addFrameSquareOverlay(),this.drawShapesOverlay();let i=this.canvas.toDataURL("image/png");return this.redrawFullCanvas(),i}catch(i){return console.error(i),null}}redrawFullCanvas(){this.clearCanvas(),this.addVideoOverlay(),this.drawShapesOverlay(),this.addFrameSquareOverlay(),this.addProgressBarOverlay()}replaceFrame(i,t){this.timeStack.set(i,this.parseShapes(this.stringifyShapes(t)))}addShapesToFrame(i,t){let e=this.timeStack.get(i)||[];this.timeStack.set(i,[...e,...this.parseShapes(this.stringifyShapes(t))])}setFrameRate(i){var t;(t=this.destructors.find(e=>e.name==="frameRateDetector"))==null||t(),this.fps=i}stringifyShapes(i){return JSON.stringify(i,(t,e)=>t==="image"?e.src:e)}parseShapes(i){return JSON.parse(i,(t,e)=>{if(t==="image"){let n=new Image;return n.src=e,n}return e})}filterNonSerializableShapes(i){return i.filter(t=>t.type!=="image")}saveCurrentFrame(){return{frame:this.playbackFrame,version:1,fps:this.fps,shapes:this.parseShapes(this.stringifyShapes(this.filterNonSerializableShapes(this.shapes)))}}addFrameSquareOverlay(i=this.activeTimeFrame){throw new Error("Method not implemented.")}addVideoOverlay(){throw new Error("Method not implemented.")}cleanFrameStacks(){this.timeStack.clear(),this.undoTimeStack.clear()}loadAllFrames(i){this.cleanFrameStacks(),i.forEach(t=>{this.timeStack.set(t.frame,t.shapes)})}appendFrames(i){i.forEach(t=>{this.addShapesToFrame(t.frame,t.shapes)})}saveAllFrames(){return Array.from(this.timeStack.keys()).filter(n=>{var o;return(o=this.timeStack.get(n))==null?void 0:o.length}).map(n=>{var o;return{frame:n,fps:this.fps,version:1,shapes:this.filterNonSerializableShapes((o=this.timeStack.get(n))!=null?o:[])}})}getAnnotationFrame(i){var a,h;let t=i.offsetX,e=i.offsetY,n=this.isMobile?10:5;return(h=(a=this.annotatedFrameCoordinates.find(l=>t>=l.x-n&&t<=l.x+n&&e>=l.y-n&&e<=l.y+n))==null?void 0:a.frame)!=null?h:null}frameFromProgressBar(i,t=!0){let e=this.videoElement;if(e.tagName!=="VIDEO")return null;let{x:n,width:o,height:a,y:h}=this.progressBarCoordinates,l=i.offsetX,c=i.offsetY;return t?l>=n&&l<=n+o&&c>=h&&c<=h+a?Math.ceil((l-n)/o*(e.duration*this.fps)):null:l>=n&&l<=n+o?Math.ceil((l-n)/o*(e.duration*this.fps)):null}addProgressBarOverlay(){throw new Error("Method not implemented.")}initUI(){throw new Error("Method not implemented.")}stopAnnotationsAsVideo(){clearTimeout(this.playTimeout)}hasAnnotationsForFrame(i){if(this.globalShapes.length>0)return!0;if(this.timeStack.has(i)){let t=this.timeStack.get(i);return t&&t.length>0}return!1}playAnnotationsAsVideo(){this.stopAnnotationsAsVideo();let i=this.playbackFrame;this.hasAnnotationsForFrame(i)?(this.showCanvas(),this.activeTimeFrame=i,this.syncTime(),this.clearCanvas(),this.drawShapesOverlay()):this.hideCanvas();let t=1e3/this.fps;requestAnimationFrame(()=>{this.syncTime()}),this.playTimeout=window.setTimeout(()=>{this.playAnnotationsAsVideo()},t)}fillCanvas(){}};function ot(s=this.activeTimeFrame){this.ctx.save(),this.ctx.fillStyle="rgba(0, 0, 0, 0.5)";let i=50,t=30,e=20;this.ctx.fillRect(this.canvasWidth-i,this.canvasHeight-t,i,t),this.ctx.fillStyle="white",this.ctx.font=`${e}px sans-serif`,this.ctx.fillText(`${s}`.padStart(3,"0"),this.canvasWidth-40,this.canvasHeight-7),this.ctx.restore()}function st(){let s=this.videoElement;s.tagName==="VIDEO"&&this.ctx.drawImage(s,0,0,s.videoWidth,s.videoHeight,0,0,this.canvasWidth,this.canvasHeight)}function rt(){let s=this.videoElement;if(s.tagName!=="VIDEO")return;this.annotatedFrameCoordinates=[];let t=Array.from(this.timeStack.keys()).filter(u=>{var p;return(p=this.timeStack.get(u))==null?void 0:p.length}),e=s.duration*this.fps,{x:n,width:o,height:a,y:h}=this.progressBarCoordinates,l=t.map(u=>Math.ceil(u/e*o));this.ctx.save(),this.ctx.fillStyle="rgba(0, 0, 0, 0.5)",this.ctx.fillRect(n,h,o,a),this.ctx.fillStyle="#F3CE32";let c=this.isMobile?16:8;l.forEach((u,p)=>{this.ctx.beginPath();let x=n+u,T=this.canvasHeight-a/2;this.ctx.fillRect(x-c/2,T-c/2,c,c),this.annotatedFrameCoordinates.push({x,y:T,frame:t[p]})});let d=this.playbackFrame,g=Math.ceil(d/e*o)+n;this.ctx.fillStyle="white",this.ctx.beginPath();let m=g,r=this.canvasHeight-a/2;this.ctx.beginPath(),this.ctx.fillRect(m-c/2,r-c/2,c,c),this.ctx.fill(),this.ctx.restore()}w.prototype.initUI=Q;w.prototype.initCanvas=tt;w.prototype.addFrameSquareOverlay=ot;w.prototype.addVideoOverlay=st;w.prototype.addProgressBarOverlay=rt;export{w as SmAnnotate};
+ `}function Q(s,i){let t=[.25,.5,.75,1],e=document.createElement("button"),n=t[t.length-1],o=function(a){i.referenceVideoElement&&a(i.referenceVideoElement)};e.type="button",s.playbackRate=n,o(a=>{a.playbackRate=n}),e.innerHTML=Z(n),e.style.margin="5px",i.addEvent(e,"click",()=>{let a=t.indexOf(s.playbackRate),h=a+1>=t.length?0:a+1;s.playbackRate=t[h],o(l=>{l.playbackRate=t[h]}),e.innerHTML=Z(t[h])}),i.buttons.push(e),i.playerControlsContainer.appendChild(e)}var gt="#F3CE32";function tt(){var d,v;let s=document.createElement("div");s.style.position="absolute",s.style.top="-40px",s.style.left="0",s.style.zIndex="2",(d=this.canvas.parentNode)==null||d.insertBefore(s,this.canvas);let i=document.createElement("div");i.style.position="relative",i.style.top="0",i.style.left="0",i.style.zIndex="2",(v=this.canvas.parentNode)==null||v.insertBefore(i,this.canvas.nextSibling),this.playerControlsContainer=i;let t=this.videoElement.tagName==="VIDEO"?this.videoElement:null;this.uiContainer=s;let e=(u,y,r=s)=>{let m=document.createElement("button");if(m.type="button",m.innerHTML=u,m.style.margin="5px",r.appendChild(m),this.buttons.push(m),typeof y=="function")this.addEvent(m,"click",y);else{m.dataset.tool=y;let x=()=>{this.currentTool===y?this.currentTool=null:this.currentTool=y};this.addEvent(m,"click",x)}return m},n=()=>{let u=document.createElement("div");return u.style.display="inline-flex",u.style.alignItems="center",u.style.margin="5px",s.appendChild(u),u};e('',"rectangle"),e('',"circle"),e('',"line"),e('',"curve"),e('',"arrow"),e('',"text"),e('',"eraser"),e('',"move"),e('',"compare"),e('',()=>{this.handleUndo()}),t&&(e('',()=>{this.prevFrame()},this.playerControlsContainer),G(t,this),e('',()=>{this.nextFrame()},this.playerControlsContainer),_(t,this),Q(t,this),q(t,this));let o=document.createElement("input");o.type="color",o.value=gt,o.style.margin="5px",this.colorPicker=o,s.appendChild(o);let a=n(),h=document.createElement("input");h.type="number",h.step="1",h.min="1",h.max="10",h.value="5",h.style.margin="5px",a.appendChild(h);let l=u=>{this.ctx.lineWidth=u.target.valueAsNumber,this.focusOnMediaNode()};this.addEvent(h,"input",l);let c=u=>{this.ctx.strokeStyle=u.target.value,this.ctx.fillStyle=u.target.value,this.focusOnMediaNode()};if(this.addEvent(o,"input",c),this.colorPicker=o,this.strokeSizePicker=h,this.getButtonForTool("compare").style.display="none",t){this.hide(),this.addEvent(t,"pause",()=>{this.show()}),this.addEvent(t,"seek",()=>{t.paused&&this.show(),this.syncTime()}),this.addEvent(t,"timeupdate",()=>{t.currentTime<2e-4&&!t.paused&&this.playAnnotationsAsVideo(),this.syncTime()}),this.addEvent(t,"seeking",()=>{this.syncTime()}),this.addEvent(t,"error",()=>{this.hide()}),this.addEvent(t,"stalled",()=>{this.hide()}),this.addEvent(t,"waiting",()=>{this.hide()}),this.addEvent(t,"ended",()=>{this.hide()}),this.addEvent(t,"play",()=>{this.hideControls(),this.playAnnotationsAsVideo()});let u=r=>{let m=r.target===document.body,x=this.uiContainer.contains(r.target),w=this.playerControlsContainer.contains(r.target),S=this.videoElement.contains(r.target),C=this.canvas.contains(r.target);return x||w||S||C||m};this.addEvent(document,"copy",r=>{var m;u(r)&&(r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation(),(m=r.clipboardData)==null||m.setData("application/json",JSON.stringify(this.saveCurrentFrame())))}),this.addEvent(document,"cut",r=>{var x;if(!u(r))return;r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation();let m=this.saveCurrentFrame();this.replaceFrame(this.playbackFrame,[]),this.redrawFullCanvas(),(x=r.clipboardData)==null||x.setData("application/json",JSON.stringify(m))}),this.addEvent(document,"paste",r=>{var S,C,N,W,z;if(!u(r))return;let m=(C=(S=r.clipboardData)==null?void 0:S.types)!=null?C:[];if(console.log("dataTypes",JSON.stringify(m)),m.includes("application/json"))r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation();else if(m.includes("Files")){let E=(N=r.clipboardData)==null?void 0:N.files;if(E&&E.length>0){let O=E[0];if(O.type.startsWith("image/")){r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation();let b=new Image;b.addEventListener("load",()=>{let lt=b.naturalWidth/b.naturalHeight,X=.25,ht=X/lt*this.aspectRatio;this.addShapesToFrame(this.playbackFrame,[{type:"image",image:b,x:0,y:0,width:X,height:ht,strokeStyle:"red",fillStyle:"red",lineWidth:2}]),this.redrawFullCanvas(),requestAnimationFrame(()=>{this.show()}),this.currentTool="move"},{once:!0}),b.src=URL.createObjectURL(O),this.redrawFullCanvas()}}}else if(m.includes("text/plain")){let E=(W=r.clipboardData)==null?void 0:W.getData("text/plain");E&&(console.log("text",E),r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation(),this.addShapesToFrame(this.playbackFrame,[{type:"text",text:E,x:.4,y:.4,strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth}]),this.show(),this.currentTool="move",this.redrawFullCanvas())}else return;let x=(z=r.clipboardData)==null?void 0:z.getData("application/json");if(!x)return;let w=JSON.parse(x);w&&w.shapes&&w.version===1&&(this.addShapesToFrame(this.playbackFrame,w.shapes),this.redrawFullCanvas())});let y=r=>{this.referenceVideoElement&&r(this.referenceVideoElement)};this.addEvent(document,"click",r=>{if(!u(r))return;let m=this.uiContainer.contains(r.target),x=this.playerControlsContainer.contains(r.target);m||x||t.paused||(this.currentTool=null,t.pause(),y(w=>{w.pause()}),requestAnimationFrame(()=>{this.syncTime(),this.redrawFullCanvas()}))}),this.addEvent(document,"keydown",r=>{u(r)&&(r.key==="ArrowLeft"||r.key==="ArrowRight"?(r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation(),r.key==="ArrowLeft"?this.prevFrame():r.key==="ArrowRight"&&this.nextFrame()):r.code==="Space"&&(r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation(),t.paused?(y(m=>{m.play().then(()=>{this.syncTime()})}),t.play().then(()=>{this.syncTime()})):(y(m=>{m.pause()}),t.pause(),requestAnimationFrame(()=>{this.syncTime(),this.redrawFullCanvas()}))))})}}function et(){var s;this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d"),(s=this.videoElement.parentNode)==null||s.insertBefore(this.canvas,this.videoElement.nextSibling),this.canvas.style.position="absolute",this.canvas.style.top="0",this.canvas.style.left="0",this.canvas.style.zIndex="1",this.addEvent(this.canvas,"pointerdown",this.handleMouseDown),this.addEvent(this.canvas,"pointermove",this.handleMouseMove),this.addEvent(this.canvas,"pointerup",this.handleMouseUp),this.addEvent(this.canvas,"pointercancel",this.handleMouseUp),this.addEvent(window,"resize",this.setCanvasSize),this.addEvent(document,"keydown",this.onKeyDown)}var p=class{constructor(i){this.startX=0;this.startY=0;this.isDrawing=!1;this.annotationTool=i}get ctx(){return this.annotationTool.ctx}onDeactivate(){}onActivate(){}reset(){this.startX=0,this.startY=0,this.isDrawing=!1}save(i){this.annotationTool.addShape(i)}};var k=class extends p{constructor(){super(...arguments);this.name="rectangle"}move(t,e,n){return t.x+=e,t.y+=n,t}normalize(t,e,n){return f(g({},t),{x:t.x/e,y:t.y/n,width:t.width/e,height:t.height/n})}onPointerDown(t){let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.startX=e,this.startY=n,this.isDrawing=!0}onPointerMove(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.drawRectangle(this.startX,this.startY,e-this.startX,n-this.startY)}onPointerUp(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.save({type:"rectangle",x:this.startX,y:this.startY,width:e-this.startX,height:n-this.startY,strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth}),this.drawRectangle(this.startX,this.startY,e-this.startX,n-this.startY),this.isDrawing=!1}drawRectangle(t,e,n,o){this.ctx.beginPath(),this.ctx.rect(t,e,n,o),this.ctx.stroke()}draw(t){this.drawRectangle(t.x,t.y,t.width,t.height)}};var P=class extends p{constructor(){super(...arguments);this.name="circle"}move(t,e,n){return t.x+=e,t.y+=n,t}normalize(t,e,n){return f(g({},t),{x:t.x/e,y:t.y/n,radius:t.radius/e})}onPointerDown(t){let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.startX=e,this.startY=n,this.isDrawing=!0}onPointerMove(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t),o=Math.sqrt(Math.pow(e-this.startX,2)+Math.pow(n-this.startY,2));this.drawCircle(this.startX,this.startY,o)}onPointerUp(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t),o=Math.sqrt(Math.pow(e-this.startX,2)+Math.pow(n-this.startY,2));this.save({type:"circle",x:this.startX,y:this.startY,radius:o,strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth}),this.drawCircle(this.startX,this.startY,o),this.isDrawing=!1}drawCircle(t,e,n){this.ctx.beginPath(),this.ctx.arc(t,e,n,0,2*Math.PI),this.ctx.stroke()}draw(t){this.drawCircle(t.x,t.y,t.radius)}};var I=class{constructor(i,t){this.x=i;this.y=t}distanceToLine(i,t){let e=t.x-i.x,n=t.y-i.y,o=Math.abs(n*this.x-e*this.y+t.x*i.y-t.y*i.x),a=Math.sqrt(n*n+e*e);return o/a}};function M(s,i){if(s.length<=2)return s;let t=s[0],e=s[s.length-1],n=-1,o=0;for(let a=1;ao&&(n=a,o=h)}if(o>i){let a=M(s.slice(0,n+1),i),h=M(s.slice(n),i);return a.slice(0,a.length-1).concat(h)}else return[t,e]}var F=class extends p{constructor(){super(...arguments);this.name="curve";this.curvePoints=[]}move(t,e,n){return t.points=t.points.map(o=>({x:o.x+e,y:o.y+n})),t}normalize(t,e,n){return f(g({},t),{points:t.points.map(o=>({x:o.x/e,y:o.y/n}))})}draw(t){this.drawCurve(t)}reset(){super.reset(),this.curvePoints=[]}onPointerDown(t){if(this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.curvePoints=[],this.startX=e,this.startY=n,this.isDrawing=!0,this.curvePoints.push({x:e,y:n})}onPointerMove(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.curvePoints.push({x:e,y:n}),this.drawCurve({points:this.curvePoints,lineWidth:this.ctx.lineWidth})}onPointerUp(t){if(!this.isDrawing)return;let{x:e,y:n}=this.annotationTool.getRelativeCoords(t);this.curvePoints.push({x:e,y:n});let o=this.curvePoints.map(d=>new I(d.x,d.y)),c={type:"curve",points:M(o,2).map(d=>({x:d.x,y:d.y})),strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth};this.save(c),this.curvePoints=[],this.isDrawing=!1}drawCurve(t){if(t.points.length===2&&t.points[0].x===t.points[1].x&&t.points[0].y===t.points[1].y){let e=t.lineWidth/4,n=0,o=2*Math.PI;this.ctx.beginPath(),this.ctx.arc(t.points[0].x,t.points[0].y,e,n,o),this.ctx.stroke()}else{this.ctx.beginPath(),this.ctx.moveTo(t.points[0].x,t.points[0].y);for(let e=1;e0){let o=this.annotationTool.globalShapes[0];if(o.type==="compare"){let a=this.annotationTool.deserialize([o])[0];this.draw(a),this.annotationTool.addFrameSquareOverlay()}}return}let{x:e}=this.annotationTool.getRelativeCoords(t);this.comparisonLine=e;let n={type:"compare",strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth,x:e};this.draw(n),this.drawDelimiter(n)}onPointerUp(){this.isDrawing&&(this.save({type:"compare",strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth,x:this.comparisonLine}),this.isDrawing=!1)}save(t){this.annotationTool.globalShapes=this.annotationTool.globalShapes.filter(n=>n.type!=="compare");let e=this.annotationTool.serialize([t])[0];e.x<.05||e.x>.95||this.annotationTool.addGlobalShape(e)}drawDelimiter(t){this.ctx.beginPath(),this.ctx.moveTo(t.x,0),this.ctx.lineTo(t.x,this.annotationTool.canvasWidth),this.ctx.stroke()}draw(t){let e=this.annotationTool.videoElement,n=this.annotationTool.referenceVideoElement;if(!e||!n)return;this.annotationTool.syncTime();let o=this.ctx.globalAlpha,a=this.annotationTool.canvasWidth,h=this.annotationTool.canvasHeight,l=t.x;this.ctx.globalAlpha=this.leftOpacity;let c=l/a,d=l;this.ctx.drawImage(e,0,0,c*e.videoWidth,e.videoHeight,0,0,d,h);let v=l,u=a-v,y=u/a*e.videoWidth;this.ctx.globalAlpha=this.rightOpacity,this.ctx.drawImage(n,v/a*e.videoWidth,0,y,e.videoHeight,v,0,u,h),this.ctx.globalAlpha=o}};var it=[k,P,D,L,B,V,F,R,A,H];function nt(s,i){let t,e,n,o=[],a=!0;function h(d,v){let u=Math.abs(v.mediaTime-t),y=Math.abs(v.presentedFrames-e),r=u/y;r&&r<1&&a&&o.length<50&&s.playbackRate===1&&document.hasFocus()&&(o.push(r),n=Math.round(1/c()),i(n,o.length*2)),a=!0,t=v.mediaTime,e=v.presentedFrames,s.requestVideoFrameCallback(h)}s.requestVideoFrameCallback(h);let l=()=>{o.pop(),a=!1};s.addEventListener("seeked",l);function c(){return o.reduce((d,v)=>d+v)/o.length}return()=>{s.removeEventListener("seeked",l)}}var ot=25,T=class{constructor(i){this.isMouseDown=!1;this.activeTimeFrame=1;this.buttons=[];this.destructors=[];this.plugins=[];this.isDestroyed=!1;this.globalShapes=[];this.timeStack=new Map;this.undoTimeStack=new Map;this.annotatedFrameCoordinates=[];this.fps=ot;this.lastNavigatedFrame=0;this.isProgressBarNavigation=!1;this.plugins=it.map(t=>new t(this)),this.init(i)}prevFrame(){let i=this.activeTimeFrame,t=Math.max(1,i-1);this.playbackFrame=t}nextFrame(){let i=this.activeTimeFrame,t=Math.min(this.videoElement.duration*this.fps,i+1);this.playbackFrame=t}addGlobalShape(i){this.globalShapes.push(i)}get selectedColor(){return this.colorPicker.value}get selectedStrokeSize(){return this.strokeSizePicker.valueAsNumber}get currentTool(){return this._currentTool}set currentTool(i){let t=this._currentTool;t&&(this.getButtonForTool(t).classList.remove("active"),this.pluginForTool(t).onDeactivate()),this._currentTool=i,this.canvas.style.cursor=i?"pointer":"default",i&&(this.getButtonForTool(i).classList.add("active"),this.pluginForTool(i).onActivate())}enableFrameRateDetection(){if(this.destructors.find(e=>e.name==="frameRateDetector"))return;let i=this.videoElement;if(i.tagName==="IMG")return;let t=nt(i,e=>{this.fps=e});Object.defineProperty(t,"name",{value:"frameRateDetector"}),this.destructors.push(t)}get playbackFrame(){if(this.videoElement instanceof HTMLImageElement)return 1;let i=Math.round(this.videoElement.currentTime*this.fps);return Math.max(1,i)}set playbackFrame(i){if(this.videoElement instanceof HTMLImageElement)return;let t=i/this.fps;this.videoElement.currentTime=t,this.syncTime(),this.show()}get canvasWidth(){return this.canvas.width/this.pixelRatio}get canvasHeight(){return this.canvas.height/this.pixelRatio}get aspectRatio(){return this.canvasWidth/this.canvasHeight}get isMobile(){return window.innerWidth<960}get progressBarCoordinates(){let i=this.isMobile?30:10,t=5,e=55,n=this.canvasWidth-t-e,o=t,a=this.canvasHeight-i;return{x:o,y:a,width:n,height:i}}get videoClientRect(){return this.videoElement.getBoundingClientRect()}get shapes(){return this.timeStack.has(this.activeTimeFrame)||this.timeStack.set(this.activeTimeFrame,[]),this.timeStack.get(this.activeTimeFrame)}set shapes(i){this.timeStack.set(this.activeTimeFrame,i)}get undoStack(){return this.undoTimeStack.has(this.activeTimeFrame)||this.undoTimeStack.set(this.activeTimeFrame,[]),this.undoTimeStack.get(this.activeTimeFrame)}set undoStack(i){this.undoTimeStack.set(this.activeTimeFrame,i)}get pixelRatio(){return window.devicePixelRatio||1}hide(){this.stopAnnotationsAsVideo(),this.hideControls(),this.hideCanvas()}showControls(){this.uiContainer.style.display="block"}hideControls(){this.uiContainer.style.display="none"}showCanvas(){this.canvas.style.display="block"}hideCanvas(){this.canvas.style.display="none"}show(){this.stopAnnotationsAsVideo(),this.activeTimeFrame=this.playbackFrame,this.showCanvas(),this.showControls(),this.redrawFullCanvas()}setCanvasSettings(){this.ctx.strokeStyle=this.selectedColor,this.ctx.fillStyle=this.selectedColor,this.ctx.lineWidth=this.selectedStrokeSize}pluginForTool(i){if(this.isDestroyed)throw new Error("AnnotationTool is destroyed");let t=this.plugins.find(e=>e.name===i);if(!t)throw new Error(`No plugin found for tool ${i}`);return t}getButtonForTool(i){return this.buttons.find(t=>t.dataset.tool===i)}bindContext(){this.handleMouseDown=this.handleMouseDown.bind(this),this.handleMouseMove=this.handleMouseMove.bind(this),this.handleMouseUp=this.handleMouseUp.bind(this),this.setCanvasSize=this.setCanvasSize.bind(this),this.onKeyDown=this.onKeyDown.bind(this)}initProperties(){this.isDestroyed=!1,this.isProgressBarNavigation=!1,this.currentTool=null,this.shapes=[],this.globalShapes=[]}init(i){this.videoElement=i,this.videoElement.style.objectFit="cover",this.videoElement.style.objectPosition="left top",this.bindContext(),this.initCanvas(),this.initUI(),this.initProperties(),this.setCanvasSize(),this.fillCanvas(),this.setCanvasSettings(),this.currentTool=this.isMobile?null:"curve"}addEvent(i,t,e){let n=o=>{this.isDestroyed||e(o)};i.addEventListener(t,n),this.destructors.push(()=>{i.removeEventListener(t,n)})}initCanvas(){throw new Error("Method not implemented.")}onKeyDown(i){(i.ctrlKey||i.metaKey)&&i.key.toLowerCase()==="z"&&this.handleUndo()}removeLastShape(){this.shapes.pop(),this.redrawFullCanvas()}handleUndo(){this.undoStack.length>0&&(this.shapes=this.undoStack.pop(),this.redrawFullCanvas())}destroy(){var n,o,a,h,l,c;if(this.isDestroyed)return;this.destructors.forEach(d=>d()),this.stopAnnotationsAsVideo(),this.destructors=[],this.globalShapes=[],this._currentTool=null,this.plugins.forEach(d=>d.reset()),this.annotatedFrameCoordinates=[],this.setFrameRate(ot),this.cleanFrameStacks();let i=this.strokeSizePicker.parentElement;if((n=i==null?void 0:i.parentNode)==null||n.removeChild(i),this.referenceVideoElement){let d=this.referenceVideoElement.parentElement;(o=d==null?void 0:d.parentNode)==null||o.removeChild(d),this.referenceVideoElement=null}let t=this.colorPicker.parentElement;(a=t==null?void 0:t.parentNode)==null||a.removeChild(t),this.buttons.forEach(d=>{var v;(v=d.parentNode)==null||v.removeChild(d)}),this.buttons=[],(h=this.uiContainer.parentNode)==null||h.removeChild(this.uiContainer),(l=this.canvas.parentNode)==null||l.removeChild(this.canvas),(c=this.playerControlsContainer.parentElement)==null||c.removeChild(this.playerControlsContainer),["strokeSizePicker","colorPicker","uiContainer","playerControlsContainer","canvas","ctx","videoElement"].forEach(d=>{delete this[d]}),this.activeTimeFrame=0,this.isDestroyed=!0}setCanvasSize(){let i=this.videoClientRect;this.canvas.width=i.width*this.pixelRatio,this.canvas.height=i.height*this.pixelRatio,this.canvas.style.width=`${i.width}px`,this.canvas.style.height=`${i.height}px`,this.ctx.scale(this.pixelRatio,this.pixelRatio),this.redrawFullCanvas(),this.setCanvasSettings(),this.syncVideoSizes()}isMultiTouch(i){return i.pointerType==="touch"&&i.isPrimary===!1}addShape(i){let t=this.serialize([i])[0];this.undoStack.push([...this.shapes]),this.shapes.push(t)}syncTime(i=!1){let t=this.videoElement;if(!t||t.tagName!=="VIDEO"||!this.referenceVideoElement||this.referenceVideoElement.readyState<4||!i&&!this.globalShapes.length)return;Math.abs(this.referenceVideoElement.currentTime-t.currentTime)>=this.msPerFrame/3&&(this.referenceVideoElement.currentTime=t.currentTime)}get msPerFrame(){return this.fps/1e3}syncVideoSizes(){if(!this.referenceVideoElement)return;let t=this.videoElement.getBoundingClientRect();this.referenceVideoElement.style.position="fixed",this.referenceVideoElement.style.top=`${t.top}px`,this.referenceVideoElement.style.left=`${t.left}px`}addReferenceVideoByURL(i){return j(this,null,function*(){let t=yield fetch(i).then(o=>o.blob()),e=new Blob([t],{type:"video/mp4"}),n=window.URL.createObjectURL(e);this.referenceVideoElement||(this.referenceVideoElement=document.createElement("video"),this.referenceVideoElement.style.zIndex="-1",this.referenceVideoElement.style.display="none",this.referenceVideoElement.style.objectFit="cover",this.referenceVideoElement.style.objectPosition="left top",this.referenceVideoElement.muted=!0,this.referenceVideoElement.playsInline=!0,this.referenceVideoElement.autoplay=!1,this.referenceVideoElement.controls=!1,this.referenceVideoElement.loop=!0,this.videoElement.after(this.referenceVideoElement),this.syncVideoSizes()),this.referenceVideoElement.src=n,this.getButtonForTool("compare").style.display=""})}addSingletonShape(i){let t=this.serialize([i])[0],e=this.shapes.filter(n=>n.type!==i.type);this.replaceFrame(this.playbackFrame,[...e,t])}serialize(i=this.shapes){let t=this.canvasWidth,e=this.canvasHeight;return i.map(n=>this.pluginForTool(n.type).normalize(n,t,e))}deserialize(i){let t=1/this.canvasWidth,e=1/this.canvasHeight;return i.map(n=>this.pluginForTool(n.type).normalize(n,t,e))}getRelativeCoords(i){let t=this.canvas.getBoundingClientRect();return{x:this.getEventX(i)-t.left,y:this.getEventY(i)-t.top}}handleMouseDown(i){if(i.preventDefault(),this.isMouseDown=!0,this.isMultiTouch(i))return;let t=this.frameFromProgressBar(i,!0);if(t){this.isProgressBarNavigation=!0;let e=this.getAnnotationFrame(i);this.isVideoPaused&&(e!==null?this.playbackFrame=e:this.playbackFrame=t);return}this.currentTool&&this.pluginForTool(this.currentTool).onPointerDown(i)}get isDrawing(){return this.currentTool?this.pluginForTool(this.currentTool).isDrawing:!1}get isVideoPaused(){return this.videoElement.tagName==="VIDEO"?this.videoElement.paused:!0}handleMouseMove(i){if(i.preventDefault(),!this.isMultiTouch(i)){if(this.isMouseDown){let t=this.isProgressBarNavigation?this.frameFromProgressBar(i,!1):null;if(t!==null&&!this.isDrawing){if(t===this.lastNavigatedFrame)return;this.lastNavigatedFrame=t,this.isVideoPaused&&(this.playbackFrame=t);return}else this.hideControls(),this.clearCanvas(),this.addVideoOverlay(),this.drawShapesOverlay()}else this.redrawFullCanvas();this.currentTool&&this.pluginForTool(this.currentTool).onPointerMove(i)}}getEventX(i){return i.clientX}getEventY(i){return i.clientY}handleMouseUp(i){this.isMouseDown=!1,this.isProgressBarNavigation=!1,this.showControls(),!this.isMultiTouch(i)&&(this.currentTool&&this.pluginForTool(this.currentTool).onPointerUp(i),this.redrawFullCanvas())}focusOnMediaNode(){this.videoElement.focus()}drawShapesOverlay(){let i={strokeStyle:this.ctx.strokeStyle,fillStyle:this.ctx.fillStyle,lineWidth:this.ctx.lineWidth};this.deserialize(this.globalShapes).forEach(t=>{this.ctx.strokeStyle=t.strokeStyle,this.ctx.fillStyle=t.fillStyle,this.ctx.lineWidth=t.lineWidth;try{this.pluginForTool(t.type).draw(t)}catch(e){console.error(e)}}),this.deserialize(this.shapes).forEach(t=>{this.ctx.strokeStyle=t.strokeStyle,this.ctx.fillStyle=t.fillStyle,this.ctx.lineWidth=t.lineWidth;try{this.pluginForTool(t.type).draw(t)}catch(e){console.error(e)}}),this.ctx.strokeStyle=i.strokeStyle,this.ctx.fillStyle=i.fillStyle,this.ctx.lineWidth=i.lineWidth}clearCanvas(){this.ctx.clearRect(0,0,this.canvasWidth,this.canvasHeight)}frameToDataUrl(){try{this.clearCanvas(),this.addVideoOverlay(),this.addFrameSquareOverlay(),this.drawShapesOverlay();let i=this.canvas.toDataURL("image/png");return this.redrawFullCanvas(),i}catch(i){return console.error(i),null}}redrawFullCanvas(){this.clearCanvas(),this.addVideoOverlay(),this.drawShapesOverlay(),this.addFrameSquareOverlay(),this.addProgressBarOverlay()}replaceFrame(i,t){this.timeStack.set(i,this.parseShapes(this.stringifyShapes(t)))}addShapesToFrame(i,t){let e=this.timeStack.get(i)||[];this.timeStack.set(i,[...e,...this.parseShapes(this.stringifyShapes(t))])}setFrameRate(i){var t;(t=this.destructors.find(e=>e.name==="frameRateDetector"))==null||t(),this.fps=i}stringifyShapes(i){return JSON.stringify(i,(t,e)=>t==="image"?e.src:e)}parseShapes(i){return JSON.parse(i,(t,e)=>{if(t==="image"){let n=new Image;return n.src=e,n}return e})}filterNonSerializableShapes(i){return i.filter(t=>t.type!=="image")}saveCurrentFrame(){return{frame:this.playbackFrame,version:1,fps:this.fps,shapes:this.parseShapes(this.stringifyShapes(this.filterNonSerializableShapes(this.shapes)))}}addFrameSquareOverlay(i=this.activeTimeFrame){throw new Error("Method not implemented.")}addVideoOverlay(){throw new Error("Method not implemented.")}cleanFrameStacks(){this.timeStack.clear(),this.undoTimeStack.clear()}loadAllFrames(i){this.cleanFrameStacks(),i.forEach(t=>{this.timeStack.set(t.frame,t.shapes)})}appendFrames(i){i.forEach(t=>{this.addShapesToFrame(t.frame,t.shapes)})}saveAllFrames(){return Array.from(this.timeStack.keys()).filter(n=>{var o;return(o=this.timeStack.get(n))==null?void 0:o.length}).map(n=>{var o;return{frame:n,fps:this.fps,version:1,shapes:this.filterNonSerializableShapes((o=this.timeStack.get(n))!=null?o:[])}})}getAnnotationFrame(i){var a,h;let t=i.offsetX,e=i.offsetY,n=this.isMobile?10:5;return(h=(a=this.annotatedFrameCoordinates.find(l=>t>=l.x-n&&t<=l.x+n&&e>=l.y-n&&e<=l.y+n))==null?void 0:a.frame)!=null?h:null}frameFromProgressBar(i,t=!0){let e=this.videoElement;if(e.tagName!=="VIDEO")return null;let{x:n,width:o,height:a,y:h}=this.progressBarCoordinates,l=i.offsetX,c=i.offsetY;return t?l>=n&&l<=n+o&&c>=h&&c<=h+a?Math.ceil((l-n)/o*(e.duration*this.fps)):null:l>=n&&l<=n+o?Math.ceil((l-n)/o*(e.duration*this.fps)):null}addProgressBarOverlay(){throw new Error("Method not implemented.")}initUI(){throw new Error("Method not implemented.")}stopAnnotationsAsVideo(){clearTimeout(this.playTimeout)}hasAnnotationsForFrame(i){if(this.globalShapes.length>0)return!0;if(this.timeStack.has(i)){let t=this.timeStack.get(i);return t&&t.length>0}return!1}playAnnotationsAsVideo(){this.stopAnnotationsAsVideo();let i=this.playbackFrame;this.hasAnnotationsForFrame(i)?(this.showCanvas(),this.activeTimeFrame=i,this.syncTime(),this.clearCanvas(),this.drawShapesOverlay()):this.hideCanvas();let t=1e3/this.fps;requestAnimationFrame(()=>{this.syncTime()}),this.playTimeout=window.setTimeout(()=>{this.playAnnotationsAsVideo()},t)}fillCanvas(){}};function st(s=this.activeTimeFrame){this.ctx.save(),this.ctx.fillStyle="rgba(0, 0, 0, 0.5)";let i=50,t=30,e=20;this.ctx.fillRect(this.canvasWidth-i,this.canvasHeight-t,i,t),this.ctx.fillStyle="white",this.ctx.font=`${e}px sans-serif`,this.ctx.fillText(`${s}`.padStart(3,"0"),this.canvasWidth-40,this.canvasHeight-7),this.ctx.restore()}function rt(){let s=this.videoElement;s.tagName==="VIDEO"&&this.ctx.drawImage(s,0,0,s.videoWidth,s.videoHeight,0,0,this.canvasWidth,this.canvasHeight)}function at(){let s=this.videoElement;if(s.tagName!=="VIDEO")return;this.annotatedFrameCoordinates=[];let t=Array.from(this.timeStack.keys()).filter(r=>{var m;return(m=this.timeStack.get(r))==null?void 0:m.length}),e=s.duration*this.fps,{x:n,width:o,height:a,y:h}=this.progressBarCoordinates,l=t.map(r=>Math.ceil(r/e*o));this.ctx.save(),this.ctx.fillStyle="rgba(0, 0, 0, 0.5)",this.ctx.fillRect(n,h,o,a),this.ctx.fillStyle="#F3CE32";let c=this.isMobile?16:8;l.forEach((r,m)=>{this.ctx.beginPath();let x=n+r,w=this.canvasHeight-a/2;this.ctx.fillRect(x-c/2,w-c/2,c,c),this.annotatedFrameCoordinates.push({x,y:w,frame:t[m]})});let d=this.playbackFrame,v=Math.ceil(d/e*o)+n;this.ctx.fillStyle="white",this.ctx.beginPath();let u=v,y=this.canvasHeight-a/2;this.ctx.beginPath(),this.ctx.fillRect(u-c/2,y-c/2,c,c),this.ctx.fill(),this.ctx.restore()}T.prototype.initUI=tt;T.prototype.initCanvas=et;T.prototype.addFrameSquareOverlay=st;T.prototype.addVideoOverlay=rt;T.prototype.addProgressBarOverlay=at;export{T as SmAnnotate};
diff --git a/src/ui.ts b/src/ui.ts
index d78e1dc..d2d1e42 100644
--- a/src/ui.ts
+++ b/src/ui.ts
@@ -368,6 +368,12 @@ export function initUI(this: AnnotationTool) {
this.redrawFullCanvas();
});
+ const refVideo = (cb: (video: HTMLVideoElement) => void) => {
+ if (this.referenceVideoElement) {
+ cb(this.referenceVideoElement);
+ }
+ };
+
// add onclick event to pause playback
this.addEvent(document, "click", (event: PointerEvent) => {
if (!isTargetBelongsToVideo(event)) {
@@ -387,6 +393,10 @@ export function initUI(this: AnnotationTool) {
}
this.currentTool = null;
video.pause();
+ refVideo((v) => {
+ v.pause();
+ });
+
requestAnimationFrame(() => {
this.syncTime();
this.redrawFullCanvas();
@@ -414,8 +424,18 @@ export function initUI(this: AnnotationTool) {
event.stopPropagation();
event.stopImmediatePropagation();
if (video.paused) {
- video.play();
+ refVideo((v) => {
+ v.play().then(() => {
+ this.syncTime();
+ })
+ });
+ video.play().then(() => {
+ this.syncTime();
+ })
} else {
+ refVideo((v) => {
+ v.pause();
+ });
video.pause();
requestAnimationFrame(() => {
this.syncTime();
diff --git a/src/ui/play-pause-button.ts b/src/ui/play-pause-button.ts
index d2e4563..fac4349 100644
--- a/src/ui/play-pause-button.ts
+++ b/src/ui/play-pause-button.ts
@@ -35,11 +35,14 @@ export function createPlayPauseButton(
if (video.paused) {
refVideo((refVideo) => {
if (refVideo.paused) {
- refVideo.play();
- tool.syncTime();
+ refVideo.play().then(() => {
+ tool.syncTime();
+ })
}
});
- video.play();
+ video.play().then(() => {
+ tool.syncTime();
+ });
} else {
refVideo((refVideo) => {
if (!refVideo.paused) {