annotate-image 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ "use strict";var AnnotateImage=(()=>{var M=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var Y=(e,t)=>{for(var n in t)M(e,n,{get:t[n],enumerable:!0})},U=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of V(t))!X.call(e,i)&&i!==n&&M(e,i,{get:()=>t[i],enumerable:!(o=F(t,i))||o.enumerable});return e};var $=e=>U(M({},"__esModule",{value:!0}),e);var at={};Y(at,{AnnotateImage:()=>I,DEFAULT_LABELS:()=>H,annotate:()=>ot});var j=30,K=30,W=30,q=30,D=class{constructor(t,n,o){this.busy=!1;this.image=t,this.handlers=t.handlers,n?this.note=n:this.note={id:"new",top:j,left:K,width:W,height:q,text:"",editable:!0},this.area=t.editOverlay.querySelector(".image-annotate-edit-area"),this.area.style.height=this.note.height+"px",this.area.style.width=this.note.width+"px",this.area.style.left=this.note.left+"px",this.area.style.top=this.note.top+"px",this.form=document.createElement("div"),this.form.className="image-annotate-edit-form";let i=document.createElement("form");this.textarea=document.createElement("textarea"),this.textarea.name="text",this.textarea.rows=3,this.textarea.cols=30,this.textarea.value=this.note.text;let r=this.image.options.labels?.placeholder??"";r&&(this.textarea.placeholder=r),i.appendChild(this.textarea),this.form.appendChild(i),this.area.appendChild(this.form),this.form.addEventListener("pointerdown",s=>s.stopPropagation());let a=this.area,p=s=>{a.style.left=s.left+"px",a.style.top=s.top+"px",a.style.width=s.width+"px",a.style.height=s.height+"px"};this.handlers.makeResizable(a,{containment:t.canvas,onResize:p,onStop:p}),this.handlers.makeDraggable(a,{containment:t.canvas,onDrag:s=>{a.style.left=s.left+"px",a.style.top=s.top+"px"},onStop:s=>{a.style.left=s.left+"px",a.style.top=s.top+"px"}}),this.textarea.focus(),this.form.addEventListener("keydown",s=>{if(s.key==="Escape"){let c=this.form.querySelector(".image-annotate-edit-close");c&&c.click()}});let l=document.createElement("div");l.className="image-annotate-edit-buttons",this.form.appendChild(l),this.addSaveButton(l,o),o&&this.addDeleteButton(l,o),this.addCancelButton(l)}destroy(){this.image.activeEdit=null,this.handlers.destroyResizable(this.area),this.handlers.destroyDraggable(this.area),this.area.style.height="",this.area.style.width="",this.area.style.left="",this.area.style.top="",this.form.remove()}addSaveButton(t,n){let o=document.createElement("button");o.className="image-annotate-edit-ok",o.textContent=this.image.options.labels?.save??"OK",o.type="button",o.addEventListener("click",()=>{if(this.busy)return;let i=this.textarea.value,r=()=>{this.image.setMode("view"),n?n.resetPosition(this,i):(this.note.editable=!0,new T(this.image,this.note).resetPosition(this,i),this.image.notes.push(this.note)),this.image.notifySave(C(this.note)),this.destroy()},a=P(this.area),p=O(this.area);this.note.top=a.top,this.note.left=a.left,this.note.width=p.width,this.note.height=p.height,this.note.text=i,this.image.api.save?(this.busy=!0,this.image.api.save(C(this.note)).then(l=>{l.annotation_id!=null&&(this.note.id=l.annotation_id),r()}).catch(l=>{this.busy=!1;let s=l instanceof Error?l:new Error(String(l));this.image.reportError({type:"save",error:s,note:this.note})})):r()}),t.appendChild(o)}addDeleteButton(t,n){let o=document.createElement("button");o.className="image-annotate-edit-delete",o.textContent=this.image.options.labels?.delete??"Delete",o.type="button",o.addEventListener("click",()=>{if(this.busy)return;let i=()=>{this.image.setMode("view"),this.destroy(),n.destroy();let r=this.image.notes.indexOf(this.note);r!==-1&&this.image.notes.splice(r,1),this.image.notifyDelete(C(this.note))};this.image.api.delete?(this.busy=!0,this.image.api.delete(C(this.note)).then(()=>{i()}).catch(r=>{this.busy=!1;let a=r instanceof Error?r:new Error(String(r));this.image.reportError({type:"delete",error:a,note:this.note})})):i()}),t.appendChild(o)}addCancelButton(t){let n=document.createElement("button");n.className="image-annotate-edit-close",n.textContent=this.image.options.labels?.cancel??"Cancel",n.type="button",n.addEventListener("click",()=>{this.image.cancelEdit()}),t.appendChild(n)}};function P(e){return{left:parseInt(e.style.left)||0,top:parseInt(e.style.top)||0}}function O(e){return{width:parseInt(e.style.width)||e.offsetWidth,height:parseInt(e.style.height)||e.offsetHeight}}var T=class{constructor(t,n){this.image=t,this.note=n,this.editable=!!(n.editable&&t.options.editable),this.area=document.createElement("div"),this.area.className="image-annotate-area"+(this.editable?" image-annotate-area-editable":"");let o=document.createElement("div");this.area.appendChild(o),t.viewOverlay.insertBefore(this.area,t.viewOverlay.firstChild),this.tooltip=document.createElement("div"),this.tooltip.className="image-annotate-note",this.tooltip.textContent=n.text,this.tooltip.style.display="none",this.area.appendChild(this.tooltip),this.setPosition(),this.area.addEventListener("mouseenter",()=>this.show()),this.area.addEventListener("mouseleave",()=>this.hide()),this.editable&&(this.area.setAttribute("tabindex","0"),this.area.setAttribute("role","button"),this.area.addEventListener("click",()=>this.edit()),this.area.addEventListener("keydown",i=>{(i.key==="Enter"||i.key===" ")&&(i.preventDefault(),this.edit())}))}setPosition(){let t=this.area.firstElementChild;t.style.height=this.note.height+"px",t.style.width=this.note.width+"px",this.area.style.left=this.note.left+"px",this.area.style.top=this.note.top+"px"}resetPosition(t,n){this.tooltip.textContent=n,this.tooltip.style.display="none";let o=P(t.area),i=O(t.area),r=this.area.firstElementChild;r.style.height=i.height+"px",r.style.width=i.width+"px",this.area.style.left=o.left+"px",this.area.style.top=o.top+"px",this.note.top=o.top,this.note.left=o.left,this.note.height=i.height,this.note.width=i.width,this.note.text=n,this.note.id=t.note.id,this.editable=!0}show(){this.tooltip.style.display="block",this.editable?this.area.classList.add("image-annotate-area-editable-hover"):this.area.classList.add("image-annotate-area-hover")}hide(){this.tooltip.style.display="none",this.area.classList.remove("image-annotate-area-hover"),this.area.classList.remove("image-annotate-area-editable-hover")}destroy(){this.area.remove(),this.tooltip.remove()}edit(){this.image.mode==="view"&&(this.image.setMode("edit"),this.image.activeEdit=new D(this.image,this.note,this))}};var S=new WeakMap,k=new WeakMap;function J(e,t){z(e);function n(o){if(o.button!==0)return;o.preventDefault(),e.setPointerCapture&&e.setPointerCapture(o.pointerId);let i=o.clientX,r=o.clientY,a=parseFloat(e.style.left)||0,p=parseFloat(e.style.top)||0,l=parseFloat(e.style.width)||e.offsetWidth,s=parseFloat(e.style.height)||e.offsetHeight,c=-1/0,L=-1/0,A=1/0,E=1/0;if(t.containment){let m=t.containment.getBoundingClientRect(),u=e.getBoundingClientRect(),y=a-(u.left-m.left),d=p-(u.top-m.top);c=y,L=d,A=m.width-l+y,E=m.height-s+d}function b(m,u,y){return Math.max(u,Math.min(y,m))}function w(m){let u=m.clientX-i,y=m.clientY-r,d=b(a+u,c,A),h=b(p+y,L,E);t.onDrag&&t.onDrag({left:d,top:h})}function x(m){e.releasePointerCapture&&e.releasePointerCapture(m.pointerId),e.removeEventListener("pointermove",w),e.removeEventListener("pointerup",x);let u=m.clientX-i,y=m.clientY-r,d=b(a+u,c,A),h=b(p+y,L,E);t.onStop&&t.onStop({left:d,top:h})}e.addEventListener("pointermove",w),e.addEventListener("pointerup",x)}e.addEventListener("pointerdown",n),S.set(e,()=>{e.removeEventListener("pointerdown",n)})}function z(e){let t=S.get(e);t&&(t(),S.delete(e))}var g=10,G=["nw","ne","sw","se"];function R(e,t,n,o,i,r,a){let p=t,l=n,s=o,c=i;return e==="nw"||e==="sw"?(p=t+r,s=o-r):s=o+r,e==="nw"||e==="ne"?(l=n+a,c=i-a):c=i+a,s<g&&((e==="nw"||e==="sw")&&(p=t+o-g),s=g),c<g&&((e==="nw"||e==="ne")&&(l=n+i-g),c=g),{left:p,top:l,width:s,height:c}}function Z(e,t){B(e);let n=[];for(let o of G){let i=document.createElement("div");i.className=`image-annotate-resize-handle image-annotate-resize-handle-${o}`,e.appendChild(i),n.push(i),i.addEventListener("pointerdown",function(a){if(a.button!==0)return;a.preventDefault(),a.stopPropagation(),i.setPointerCapture&&i.setPointerCapture(a.pointerId);let p=a.clientX,l=a.clientY,s=parseFloat(e.style.left)||0,c=parseFloat(e.style.top)||0,L=parseFloat(e.style.width)||0,A=parseFloat(e.style.height)||0,E=1/0,b=1/0,w=-1/0,x=-1/0;if(t.containment){let d=t.containment.getBoundingClientRect(),h=e.getBoundingClientRect(),f=s-(h.left-d.left),v=c-(h.top-d.top);w=f,x=v,E=d.width+f,b=d.height+v}function m(d){let{left:h,top:f,width:v,height:N}=d;return h<w&&(v-=w-h,h=w),f<x&&(N-=x-f,f=x),h+v>E&&(v=E-h),f+N>b&&(N=b-f),v<g&&(v=g),N<g&&(N=g),{left:h,top:f,width:v,height:N}}function u(d){let h=d.clientX-p,f=d.clientY-l,v=m(R(o,s,c,L,A,h,f));t.onResize?.(v)}function y(d){i.releasePointerCapture&&i.releasePointerCapture(d.pointerId),i.removeEventListener("pointermove",u),i.removeEventListener("pointerup",y);let h=d.clientX-p,f=d.clientY-l,v=m(R(o,s,c,L,A,h,f));t.onStop&&t.onStop(v)}i.addEventListener("pointermove",u),i.addEventListener("pointerup",y)})}k.set(e,()=>{for(let o of n)o.remove()})}function B(e){let t=k.get(e);t&&(t(),k.delete(e))}function _(){return{makeDraggable:J,makeResizable:Z,destroyDraggable:z,destroyResizable:B}}function C(e){let{view:t,editable:n,...o}=e;return o}function Q(e){return{load:typeof e.load=="string"?tt(e.load):e.load,save:typeof e.save=="string"?et(e.save):e.save,delete:typeof e.delete=="string"?nt(e.delete):e.delete}}function tt(e){return()=>fetch(e).then(t=>{if(!t.ok)throw new Error(`Load failed (HTTP ${t.status})`);return t.json()})}function et(e){return t=>fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}).then(n=>{if(!n.ok)throw new Error(`Save failed (HTTP ${n.status})`);return n.json()})}function nt(e){return t=>fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}).then(n=>{if(!n.ok)throw new Error(`Delete failed (HTTP ${n.status})`)})}var I=class{constructor(t,n){this._mode="view";this.activeEdit=null;this.destroyed=!1;this.options=n,this.handlers=_(),this.img=t;let o=t.width,i=t.height;if(o===0||i===0)throw new Error("image-annotate: image must have non-zero dimensions (is the image loaded?)");this.notes=n.notes.map(a=>({...a})),this.canvas=document.createElement("div"),this.canvas.className="image-annotate-canvas",this.viewOverlay=document.createElement("div"),this.viewOverlay.className="image-annotate-view",this.editOverlay=document.createElement("div"),this.editOverlay.className="image-annotate-edit",this.editOverlay.style.display="none";let r=document.createElement("div");if(r.className="image-annotate-edit-area",this.editOverlay.appendChild(r),this.canvas.appendChild(this.viewOverlay),this.canvas.appendChild(this.editOverlay),!t.parentNode)throw new Error("image-annotate: image must be in the DOM before initialization");t.parentNode.insertBefore(this.canvas,t.nextSibling),this.canvas.style.height=i+"px",this.canvas.style.width=o+"px",this.canvas.style.backgroundImage='url("'+t.src+'")',this.viewOverlay.style.height=i+"px",this.viewOverlay.style.width=o+"px",this.editOverlay.style.height=i+"px",this.editOverlay.style.width=o+"px",this.api=this.options.api?Q(this.options.api):{},this.api.load?this.loadFromApi():this.load(),this.options.editable&&this.createButton(),t.style.display="none"}get mode(){return this._mode}setMode(t){this._mode=t,t==="edit"?(this.canvas.classList.add("image-annotate-editing"),this.editOverlay.style.display="block"):(this.canvas.classList.remove("image-annotate-editing"),this.editOverlay.style.display="none")}getNotes(){return this.notes.map(C)}notifyChange(){this.options.onChange?.(this.getNotes())}notifySave(t){this.options.onSave?.(t),this.notifyChange()}notifyDelete(t){this.options.onDelete?.(t),this.notifyChange()}notifyLoad(){this.options.onLoad?.(this.getNotes()),this.notifyChange()}destroyViews(){this.cancelEdit();for(let t of this.notes)t.view?.destroy()}createViews(){for(let t of this.notes)t.view=new T(this,t)}load(){this.destroyViews(),this.createViews(),this.notifyLoad()}clear(){this.destroyViews(),this.notes=[],this.notifyChange()}destroy(){this.destroyed||(this.destroyed=!0,this.destroyViews(),this.notes=[],this.button&&this.button.remove(),this.canvas.remove(),this.img.style.display="")}cancelEdit(){this.activeEdit&&(this.activeEdit.destroy(),this.setMode("view"))}setNotes(t){this.destroyed||(this.destroyViews(),this.notes=t.map(n=>({...n})),this.createViews())}setEditable(t){this.destroyed||this.options.editable!==t&&(this.options.editable=t,t&&!this.button?this.createButton():!t&&this.button&&(this.button.remove(),this.button=void 0),this.destroyViews(),this.createViews())}createButton(){this.button=document.createElement("button"),this.button.className="image-annotate-add",this.button.title=this.options.labels?.addNote??"Add Note",this.button.type="button",this.button.addEventListener("click",()=>{this.add()}),this.canvas.appendChild(this.button)}reportError(t){this.options.onError?this.options.onError(t):console.error(`image-annotate: ${t.type} failed`,t.error)}loadFromApi(){this.api.load&&this.api.load().then(t=>{this.destroyViews(),this.notes=t,this.createViews(),this.notifyLoad()}).catch(t=>{let n=t instanceof Error?t:new Error(String(t));this.reportError({type:"load",error:n})})}add(){return this.mode==="view"?(this.setMode("edit"),this.activeEdit=new D(this),!0):!1}};var H={addNote:"Add Note",save:"OK",delete:"Delete",cancel:"Cancel",placeholder:""};var it={editable:!0,notes:[],labels:{...H}};function ot(e,t){let n={...it,...t,labels:{...H,...t?.labels}};return new I(e,n)}return $(at);})();
@@ -0,0 +1 @@
1
+ .image-annotate-add{--image-annotate-icon-add: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256' fill='%23fff'%3E%3Cpath d='M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z'/%3E%3C/svg%3E");appearance:none;background-color:#0006;background-image:var(--image-annotate-icon-add);background-repeat:no-repeat;background-position:center;background-size:20px 20px;border:1px solid rgba(255,255,255,.5);border-radius:4px;bottom:8px;color:#fff;cursor:pointer;height:32px;opacity:0;padding:0;position:absolute;right:8px;transition:opacity .2s,background-color .2s;width:32px;z-index:1}.image-annotate-add:hover{background-color:#0009}.image-annotate-add:focus{opacity:1;outline:2px solid var(--image-annotate-hover-editable-color);outline-offset:2px}.image-annotate-canvas{--image-annotate-font-family: Verdana, sans-serif;--image-annotate-font-size: 12px;--image-annotate-area-border: #000;--image-annotate-area-inner-border: #fff;--image-annotate-hover-color: yellow;--image-annotate-hover-editable-color: #00ad00;--image-annotate-note-bg: #e7ffe7;--image-annotate-note-border: #397f39;--image-annotate-note-text: #000;--image-annotate-edit-bg: #fffee3;--image-annotate-edit-border: #000;--image-annotate-button-bg: #fff;--image-annotate-button-bg-hover: #eee;--image-annotate-button-border: #ccc;--image-annotate-button-text: #000;--image-annotate-icon-save: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256' fill='%23333'%3E%3Cpath d='M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z'/%3E%3C/svg%3E");--image-annotate-icon-cancel: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256' fill='%23333'%3E%3Cpath d='M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z'/%3E%3C/svg%3E");--image-annotate-icon-delete: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256' fill='%23333'%3E%3Cpath d='M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z'/%3E%3C/svg%3E");border:solid 1px var(--image-annotate-button-border);max-width:100%;background-position:left top;background-repeat:no-repeat;display:block;margin:0;position:relative}.image-annotate-view{display:none;position:relative}@media(hover:hover){.image-annotate-canvas:hover:not(.image-annotate-editing) .image-annotate-view{display:block}}@media(hover:none){.image-annotate-canvas:not(.image-annotate-editing) .image-annotate-view{display:block}}@media(hover:hover){.image-annotate-canvas:hover:not(.image-annotate-editing) .image-annotate-add{opacity:1}}@media(hover:none){.image-annotate-canvas:not(.image-annotate-editing) .image-annotate-add{opacity:1}}.image-annotate-editing .image-annotate-add{display:none}.image-annotate-area{border:1px solid var(--image-annotate-area-border);position:absolute}.image-annotate-area div{border:1px solid var(--image-annotate-area-inner-border);box-sizing:border-box;display:block}.image-annotate-area-hover div{border-color:var(--image-annotate-hover-color)}.image-annotate-area-editable{cursor:pointer}.image-annotate-area-editable-hover div{border-color:var(--image-annotate-hover-editable-color)}.image-annotate-note{background-color:var(--image-annotate-note-bg);border:solid 1px var(--image-annotate-note-border);color:var(--image-annotate-note-text);display:none;font-family:var(--image-annotate-font-family);font-size:var(--image-annotate-font-size);left:-1px;max-width:200px;padding:3px 7px;position:absolute;top:calc(100% + 7px)}.image-annotate-note .actions{display:block;font-size:80%}.image-annotate-edit{display:none}.image-annotate-edit-form{background-color:var(--image-annotate-edit-bg);border:1px solid var(--image-annotate-edit-border);box-sizing:border-box;cursor:default;display:flex;flex-direction:column;gap:7px;left:-1px;min-width:250px;padding:7px;position:absolute;top:calc(100% + 7px);width:max-content}.image-annotate-edit-form form{margin:0;padding:0}.image-annotate-edit-form textarea{box-sizing:border-box;font-family:var(--image-annotate-font-family);font-size:var(--image-annotate-font-size);height:50px;width:100%}.image-annotate-edit-buttons{display:flex;flex-wrap:nowrap;gap:6px}.image-annotate-edit-form button{appearance:none;background-color:var(--image-annotate-button-bg);background-repeat:no-repeat;background-position:3px center;background-size:16px 16px;border:solid 1px var(--image-annotate-button-border);color:var(--image-annotate-button-text);cursor:pointer;font-family:var(--image-annotate-font-family);font-size:var(--image-annotate-font-size);line-height:18px;min-height:22px;padding:2px 6px 2px 24px;white-space:nowrap}.image-annotate-edit-form button:empty{background-position:center;min-width:24px;padding:2px 4px}.image-annotate-edit-form button:hover{background-color:var(--image-annotate-button-bg-hover)}.image-annotate-edit-area{border:1px solid var(--image-annotate-area-border);cursor:move;display:block;height:60px;left:10px;margin:0;padding:0;position:absolute;top:10px;width:60px}.image-annotate-resize-handle{background-color:var(--image-annotate-area-border);border:1px solid var(--image-annotate-area-inner-border);box-sizing:border-box;height:8px;position:absolute;width:8px}.image-annotate-resize-handle-nw{cursor:nw-resize;left:-4px;top:-4px}.image-annotate-resize-handle-ne{cursor:ne-resize;right:-4px;top:-4px}.image-annotate-resize-handle-sw{cursor:sw-resize;left:-4px;bottom:-4px}.image-annotate-resize-handle-se{cursor:se-resize;right:-4px;bottom:-4px}.image-annotate-edit-ok{background-image:var(--image-annotate-icon-save)}.image-annotate-edit-delete{background-image:var(--image-annotate-icon-delete)}.image-annotate-edit-close{background-image:var(--image-annotate-icon-cancel)}.image-annotate-area-editable:focus{outline:2px solid var(--image-annotate-hover-editable-color);outline-offset:2px}.image-annotate-edit-form button:focus{outline:2px solid var(--image-annotate-hover-editable-color);outline-offset:1px}