diff --git a/public/app.js b/public/app.js index 12b2bfb..083ee4a 100644 --- a/public/app.js +++ b/public/app.js @@ -183,6 +183,8 @@ async function scanLabelPhotoOcr(form,help,file){let url,worker;try{const Tesser const photoOcrObserver=new MutationObserver(()=>{const input=$("#receiveForm .photo-input");if(input&&!input.dataset.ocr){input.dataset.ocr="true";input.onchange=()=>{if(input.files?.[0])scanLabelPhotoOcr($("#receiveForm"),$("#receiveForm .scan-help"),input.files[0]);input.value=""}}});photoOcrObserver.observe($("#modalBody"),{childList:true}); const normalizeMacValue=value=>{const hex=String(value||"").replace(/[^0-9a-f]/gi,"").slice(0,12);return hex.length===12?(hex.match(/.{2}/g)||[]).join(":"):value}; const macFormatObserver=new MutationObserver(()=>{const input=$("#receiveForm .photo-input");if(input&&!input.dataset.macfix){input.dataset.macfix="true";input.onchange=async()=>{if(input.files?.[0]){await scanLabelPhotoOcr($("#receiveForm"),$("#receiveForm .scan-help"),input.files[0]);$("#receiveForm").elements.mac.value=normalizeMacValue($("#receiveForm").elements.mac.value)}input.value=""}}});macFormatObserver.observe($("#modalBody"),{childList:true}); +async function scanLabelPhotoTargeted(form,help,file){let url,worker;try{const Tesseract=await import("https://cdn.jsdelivr.net/npm/tesseract.js@5/+esm");url=URL.createObjectURL(file);const image=new Image();image.src=url;await image.decode();const source=document.createElement("canvas"),sctx=source.getContext("2d");source.width=image.naturalWidth;source.height=image.naturalHeight;sctx.drawImage(image,0,0);const pixels=sctx.getImageData(0,0,source.width,source.height).data;let minX=source.width,minY=source.height,maxX=0,maxY=0;for(let y=0;y170&&pixels[i+1]>170&&pixels[i+2]>170){minX=Math.min(minX,x);minY=Math.min(minY,y);maxX=Math.max(maxX,x);maxY=Math.max(maxY,y)}}if(maxX<=minX||maxY<=minY)throw new Error("label not found");const pad=24,crop=document.createElement("canvas"),ctx=crop.getContext("2d"),x=Math.max(0,minX-pad),y=Math.max(0,minY-pad),w=Math.min(source.width-x,maxX-minX+pad*2),h=Math.min(source.height-y,maxY-minY+pad*2),scale=2;crop.width=w*scale;crop.height=h*scale;ctx.drawImage(source,x,y,w,h,0,0,crop.width,crop.height);const data=ctx.getImageData(0,0,crop.width,crop.height);for(let i=0;i185?255:gray<100?0:gray;data.data[i]=data.data[i+1]=data.data[i+2]=v}ctx.putImageData(data,0,0);worker=await Tesseract.createWorker("eng");await worker.setParameters({tessedit_pageseg_mode:"6",tessedit_char_whitelist:"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:- "});const result=await worker.recognize(crop.toDataURL("image/png")),text=String(result.data.text||"").replace(/\r/g," ");const uid=text.match(/UID\s*[:#]?\s*([A-Z0-9]+)/i)?.[1],sn=text.match(/SN\s*[:#]?\s*([A-Z0-9]+)/i)?.[1],raw=text.match(/MAC\s*[:#]?\s*([A-F0-9: -]{12,})/i)?.[1],mac=raw?normalizeMacValue(raw):null;if(uid)form.elements.uid.value=uid;if(sn)form.elements.sn.value=sn;if(mac)form.elements.mac.value=mac;const count=[uid,sn,mac].filter(Boolean).length;help.textContent=count?count+" labeled value"+(count===1?"":"s")+" read from the cropped label. Review before adding.":"No labeled values detected. Retake the photo closer and keep the white label fully visible."}catch(error){help.textContent="The label could not be read. Retake a close, well-lit photo with the white label filling the frame."}finally{await worker?.terminate().catch(()=>{});if(url)URL.revokeObjectURL(url)}} +const targetedOcrObserver=new MutationObserver(()=>{const input=$("#receiveForm .photo-input");if(input&&!input.dataset.targeted){input.dataset.targeted="true";input.onchange=()=>{if(input.files?.[0])scanLabelPhotoTargeted($("#receiveForm"),$("#receiveForm .scan-help"),input.files[0]);input.value=""}}});targetedOcrObserver.observe($("#modalBody"),{childList:true}); if(!("BarcodeDetector" in window))document.addEventListener("click",e=>{const button=e.target.closest(".scan-camera");if(button){e.stopPropagation();scanLabelWithLibrary(button.closest("form"),button.closest("form").querySelector(".scan-help"))}},true); applyTheme(); initialize();