2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner = { } ;
2019-09-19 12:48:02 +02:00
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . LiveVideoSizeAdjusted = false ;
Grocy . Components . CameraBarcodeScanner . CameraSelectLoaded = false ;
Grocy . Components . CameraBarcodeScanner . TorchIsOn = false ;
Grocy . Components . CameraBarcodeScanner . CheckCapabilities = async function ( )
2020-03-29 14:25:04 +02:00
{
var track = Quagga . CameraAccess . getActiveTrack ( ) ;
var capabilities = { } ;
2020-08-30 12:18:16 +02:00
if ( typeof track . getCapabilities === 'function' )
{
2020-03-29 14:25:04 +02:00
capabilities = track . getCapabilities ( ) ;
}
2020-08-29 16:41:27 +02:00
2025-01-12 23:51:00 +01:00
// Init camera select dropdown
if ( ! Grocy . Components . CameraBarcodeScanner . CameraSelectLoaded )
2020-08-30 12:18:16 +02:00
{
2025-01-12 23:51:00 +01:00
var cameraSelect = document . querySelector ( '.cameraSelect' ) ;
var cameras = await Quagga . CameraAccess . enumerateVideoDevices ( ) ;
cameras . forEach ( camera =>
{
var option = document . createElement ( "option" ) ;
option . text = camera . label ;
option . value = camera . deviceId ;
if ( track . label == camera . label )
{
option . selected = "selected" ;
}
cameraSelect . appendChild ( option ) ;
} ) ;
Grocy . Components . CameraBarcodeScanner . CameraSelectLoaded = true ;
2020-04-13 22:07:38 +02:00
}
2020-08-29 16:41:27 +02:00
2025-01-12 23:51:00 +01:00
// Check if the camera is capable to turn on a torch
var hasTorch = typeof capabilities . torch === 'boolean' && capabilities . torch ;
// Remove the torch button if the select camera doesn't have a torch
if ( ! hasTorch )
2020-08-30 12:18:16 +02:00
{
2025-01-14 17:54:06 +01:00
document . querySelector ( '.camerabarcodescanner-modal .modal-footer' ) . setAttribute ( 'style' , 'display:none !important;' ) ;
2020-03-29 14:25:04 +02:00
}
2025-01-12 23:51:00 +01:00
else
2020-08-30 12:18:16 +02:00
{
2025-01-14 17:54:06 +01:00
document . querySelector ( '.camerabarcodescanner-modal .modal-footer' ) . setAttribute ( 'style' , 'flex;' ) ;
2020-03-29 14:25:04 +02:00
}
2021-03-31 21:12:51 +01:00
// Reduce the height of the video, if it's higher than then the viewport
2025-01-12 23:51:00 +01:00
if ( ! Grocy . Components . CameraBarcodeScanner . LiveVideoSizeAdjusted )
2020-04-13 22:34:52 +02:00
{
2025-01-14 17:54:06 +01:00
var bc = document . getElementById ( 'camerabarcodescanner-container' ) ;
2020-04-13 22:34:52 +02:00
if ( bc )
{
var bcAspectRatio = bc . offsetWidth / bc . offsetHeight ;
var settings = track . getSettings ( ) ;
if ( bcAspectRatio > settings . aspectRatio )
{
2025-01-14 17:54:06 +01:00
var v = document . querySelector ( '#camerabarcodescanner-livestream video' ) ;
2020-04-13 22:34:52 +02:00
if ( v )
{
2025-01-14 17:54:06 +01:00
var c = document . querySelector ( '#camerabarcodescanner-livestream canvas' )
2020-04-13 22:34:52 +02:00
var newWidth = v . clientWidth / bcAspectRatio * settings . aspectRatio + 'px' ;
v . style . width = newWidth ;
c . style . width = newWidth ;
}
2020-03-29 14:25:04 +02:00
}
2020-04-13 22:34:52 +02:00
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . LiveVideoSizeAdjusted = true ;
2020-03-29 14:25:04 +02:00
}
}
}
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . StartScanning = function ( )
2019-09-19 12:48:02 +02:00
{
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . DecodedCodesCount = 0 ;
Grocy . Components . CameraBarcodeScanner . DecodedCodesErrorCount = 0 ;
2019-09-21 20:46:23 +02:00
2019-09-19 12:48:02 +02:00
Quagga . init ( {
inputStream : {
name : "Live" ,
type : "LiveStream" ,
2025-01-14 17:54:06 +01:00
target : document . querySelector ( "#camerabarcodescanner-livestream" ) ,
2019-09-19 12:48:02 +02:00
constraints : {
2020-04-13 22:07:38 +02:00
facingMode : "environment" ,
2025-01-12 23:51:00 +01:00
... ( window . localStorage . getItem ( 'cameraId' ) && { deviceId : window . localStorage . getItem ( 'cameraId' ) } ) , // If preferred cameraId is set, request to use that specific camera
2019-09-19 12:48:02 +02:00
}
} ,
locator : {
2020-08-19 14:52:04 -03:00
patchSize : Grocy . UserSettings . quagga2 _patchsize ,
halfSample : Grocy . UserSettings . quagga2 _halfsample ,
2019-09-19 12:48:02 +02:00
debug : {
2020-08-19 14:52:04 -03:00
showCanvas : Grocy . UserSettings . quagga2 _debug ,
showPatches : Grocy . UserSettings . quagga2 _debug ,
showFoundPatches : Grocy . UserSettings . quagga2 _debug ,
showSkeleton : Grocy . UserSettings . quagga2 _debug ,
showLabels : Grocy . UserSettings . quagga2 _debug ,
showPatchLabels : Grocy . UserSettings . quagga2 _debug ,
showRemainingPatchLabels : Grocy . UserSettings . quagga2 _debug ,
2019-09-19 12:48:02 +02:00
boxFromPatches : {
2020-08-19 14:52:04 -03:00
showTransformed : Grocy . UserSettings . quagga2 _debug ,
showTransformedBox : Grocy . UserSettings . quagga2 _debug ,
showBB : Grocy . UserSettings . quagga2 _debug
2019-09-19 12:48:02 +02:00
}
}
} ,
2020-04-13 16:14:43 +02:00
numOfWorkers : Grocy . UserSettings . quagga2 _numofworkers ,
2020-08-19 14:52:04 -03:00
frequency : Grocy . UserSettings . quagga2 _frequency ,
2019-09-19 12:48:02 +02:00
decoder : {
readers : [
"ean_reader" ,
"ean_8_reader" ,
2021-06-12 17:21:12 +02:00
"code_128_reader" ,
2021-06-29 17:40:28 +02:00
"code_39_reader"
2019-09-19 12:48:02 +02:00
] ,
debug : {
2020-08-19 14:52:04 -03:00
showCanvas : Grocy . UserSettings . quagga2 _debug ,
showPatches : Grocy . UserSettings . quagga2 _debug ,
showFoundPatches : Grocy . UserSettings . quagga2 _debug ,
showSkeleton : Grocy . UserSettings . quagga2 _debug ,
showLabels : Grocy . UserSettings . quagga2 _debug ,
showPatchLabels : Grocy . UserSettings . quagga2 _debug ,
showRemainingPatchLabels : Grocy . UserSettings . quagga2 _debug ,
2019-09-19 12:48:02 +02:00
boxFromPatches : {
2020-08-19 14:52:04 -03:00
showTransformed : Grocy . UserSettings . quagga2 _debug ,
showTransformedBox : Grocy . UserSettings . quagga2 _debug ,
showBB : Grocy . UserSettings . quagga2 _debug
2019-09-19 12:48:02 +02:00
}
}
} ,
locate : true
} , function ( error )
{
if ( error )
{
Grocy . FrontendHelpers . ShowGenericError ( "Error while initializing the barcode scanning library" , error . message ) ;
2023-05-19 18:08:26 +02:00
toastr . info ( _ _t ( "Camera access is only possible when supported and allowed by your browser and when Grocy is served via a secure (https://) connection" ) ) ;
2020-04-13 22:07:38 +02:00
window . localStorage . removeItem ( "cameraId" ) ;
2019-09-20 13:37:53 +02:00
setTimeout ( function ( )
{
2025-01-10 17:15:09 +01:00
$ ( ".modal" ) . last ( ) . modal ( "hide" ) ;
2019-09-20 13:37:53 +02:00
} , 500 ) ;
2019-09-19 12:48:02 +02:00
return ;
}
2020-03-29 14:25:04 +02:00
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . CheckCapabilities ( ) ;
2019-09-19 12:48:02 +02:00
Quagga . start ( ) ;
2025-01-12 23:51:00 +01:00
if ( Grocy . FeatureFlags . GROCY _FEATURE _FLAG _AUTO _TORCH _ON _WITH _CAMERA )
{
setTimeout ( function ( )
{
Grocy . Components . CameraBarcodeScanner . TorchToggle ( Quagga . CameraAccess . getActiveTrack ( ) ) ;
} , 250 ) ;
}
2019-09-19 12:48:02 +02:00
} ) ;
}
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . StopScanning = function ( )
2019-09-19 12:48:02 +02:00
{
Quagga . stop ( ) ;
2020-08-29 16:41:27 +02:00
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . DecodedCodesCount = 0 ;
Grocy . Components . CameraBarcodeScanner . DecodedCodesErrorCount = 0 ;
Grocy . Components . CameraBarcodeScanner . LiveVideoSizeAdjusted = false ;
Grocy . Components . CameraBarcodeScanner . CameraSelectLoaded = false ;
Grocy . Components . CameraBarcodeScanner . TorchIsOn = false ;
2019-09-19 12:48:02 +02:00
}
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . TorchToggle = function ( track )
2020-03-29 14:25:04 +02:00
{
2020-08-30 12:18:16 +02:00
if ( track )
{
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . TorchIsOn = ! Grocy . Components . CameraBarcodeScanner . TorchIsOn ;
2020-08-29 16:41:27 +02:00
track . applyConstraints ( {
2020-03-29 14:25:04 +02:00
advanced : [
2020-08-29 16:41:27 +02:00
{
2025-01-12 23:51:00 +01:00
torch : Grocy . Components . CameraBarcodeScanner . TorchIsOn
2020-03-29 14:25:04 +02:00
}
]
} ) ;
}
}
2019-09-19 12:48:02 +02:00
Quagga . onDetected ( function ( result )
{
2019-09-21 20:46:23 +02:00
$ . each ( result . codeResult . decodedCodes , function ( id , error )
{
if ( error . error != undefined )
{
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . DecodedCodesCount ++ ;
Grocy . Components . CameraBarcodeScanner . DecodedCodesErrorCount += Number . parseFloat ( error . error ) ;
2019-09-21 20:46:23 +02:00
}
} ) ;
2025-01-12 23:51:00 +01:00
if ( ( Grocy . Components . CameraBarcodeScanner . DecodedCodesErrorCount / Grocy . Components . CameraBarcodeScanner . DecodedCodesCount < 0.15 ) ||
( Grocy . Components . CameraBarcodeScanner . DecodedCodesErrorCount == 0 && Grocy . Components . CameraBarcodeScanner . DecodedCodesCount == 0 && result . codeResult . code . length != 0 ) )
2019-09-21 20:46:23 +02:00
{
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . StopScanning ( ) ;
$ ( document ) . trigger ( "Grocy.BarcodeScanned" , [ result . codeResult . code , Grocy . Components . CameraBarcodeScanner . CurrentTarget ] ) ;
2025-01-13 00:09:28 +01:00
$ ( ".modal" ) . last ( ) . modal ( "hide" ) ;
2019-09-21 20:46:23 +02:00
}
2019-09-19 12:48:02 +02:00
} ) ;
Quagga . onProcessed ( function ( result )
{
var drawingCtx = Quagga . canvas . ctx . overlay ;
var drawingCanvas = Quagga . canvas . dom . overlay ;
if ( result )
{
if ( result . boxes )
{
2023-02-06 20:22:10 +01:00
drawingCtx . clearRect ( 0 , 0 , Number . parseInt ( drawingCanvas . getAttribute ( "width" ) ) , Number . parseInt ( drawingCanvas . getAttribute ( "height" ) ) ) ;
2019-09-19 12:48:02 +02:00
result . boxes . filter ( function ( box )
{
return box !== result . box ;
} ) . forEach ( function ( box )
{
2019-09-21 20:46:23 +02:00
Quagga . ImageDebug . drawPath ( box , { x : 0 , y : 1 } , drawingCtx , { color : "yellow" , lineWidth : 4 } ) ;
2019-09-19 12:48:02 +02:00
} ) ;
}
if ( result . box )
{
2019-09-21 20:46:23 +02:00
Quagga . ImageDebug . drawPath ( result . box , { x : 0 , y : 1 } , drawingCtx , { color : "green" , lineWidth : 4 } ) ;
2019-09-19 12:48:02 +02:00
}
if ( result . codeResult && result . codeResult . code )
{
Quagga . ImageDebug . drawPath ( result . line , { x : 'x' , y : 'y' } , drawingCtx , { color : "red" , lineWidth : 4 } ) ;
}
}
} ) ;
2025-01-14 17:54:06 +01:00
$ ( document ) . on ( "click" , "#camerabarcodescanner-start-button" , async function ( e )
2019-09-19 12:48:02 +02:00
{
e . preventDefault ( ) ;
2019-09-27 17:24:44 +02:00
var inputElement = $ ( e . currentTarget ) . prev ( ) ;
if ( inputElement . hasAttr ( "disabled" ) )
{
// Do nothing and disable the barcode scanner start button
$ ( e . currentTarget ) . addClass ( "disabled" ) ;
return ;
}
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . CurrentTarget = inputElement . attr ( "data-target" ) ;
2020-08-29 16:41:27 +02:00
2020-04-13 22:07:38 +02:00
var dialog = bootbox . dialog ( {
2025-01-14 17:54:06 +01:00
message : '<div id="camerabarcodescanner-container" class="col"><div id="camerabarcodescanner-livestream"></div></div>' ,
2019-09-19 12:48:02 +02:00
title : _ _t ( 'Scan a barcode' ) ,
2025-01-12 23:51:00 +01:00
size : 'large' ,
2019-09-19 12:48:02 +02:00
backdrop : true ,
2020-03-01 17:53:28 +01:00
closeButton : true ,
2025-01-14 17:54:06 +01:00
className : "form camerabarcodescanner-modal" ,
2019-09-19 12:48:02 +02:00
buttons : {
2020-03-01 17:53:28 +01:00
torch : {
2025-01-08 20:50:35 +01:00
label : '<i class="fa-solid fa-lightbulb"></i>' ,
2020-03-29 14:25:04 +02:00
className : 'btn-warning responsive-button torch' ,
2020-03-01 17:58:10 +01:00
callback : function ( )
{
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . TorchToggle ( Quagga . CameraAccess . getActiveTrack ( ) ) ;
2020-03-01 17:58:10 +01:00
return false ;
2020-08-30 12:18:16 +02:00
}
2019-09-20 13:37:53 +02:00
}
2025-01-12 23:51:00 +01:00
} ,
onHide : function ( e )
{
Grocy . Components . CameraBarcodeScanner . StopScanning ( ) ;
2019-09-19 12:48:02 +02:00
}
} ) ;
2020-08-29 16:41:27 +02:00
2020-04-13 22:07:38 +02:00
// Add camera select to existing dialog
2025-01-12 23:51:00 +01:00
dialog . find ( '.bootbox-body' ) . append ( '<div class="form-group py-0 my-1 d-block cameraSelect-wrapper"><select class="custom-control custom-select cameraSelect"></select></div>' ) ;
2020-04-13 22:07:38 +02:00
var cameraSelect = document . querySelector ( '.cameraSelect' ) ;
2020-08-30 12:18:16 +02:00
cameraSelect . onchange = function ( )
{
2020-04-13 22:07:38 +02:00
window . localStorage . setItem ( 'cameraId' , cameraSelect . value ) ;
Quagga . stop ( ) ;
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . StartScanning ( ) ;
2020-04-13 22:07:38 +02:00
} ;
2020-08-29 16:41:27 +02:00
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . StartScanning ( ) ;
2019-09-19 12:48:02 +02:00
} ) ;
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . InitDone = false ;
Grocy . Components . CameraBarcodeScanner . Init = function ( )
2019-09-19 12:48:02 +02:00
{
2025-01-12 23:51:00 +01:00
if ( Grocy . Components . CameraBarcodeScanner . InitDone )
2022-02-11 18:18:17 +01:00
{
return ;
}
2019-09-27 17:24:44 +02:00
$ ( ".barcodescanner-input:visible" ) . each ( function ( )
{
if ( $ ( this ) . hasAttr ( "disabled" ) )
{
2025-01-14 17:54:06 +01:00
$ ( this ) . after ( '<a id="camerabarcodescanner-start-button" class="btn btn-sm btn-primary text-white disabled" data-target="' + $ ( this ) . attr ( "data-target" ) + '"><i class="fa-solid fa-camera"></i></a>' ) ;
2019-09-27 17:24:44 +02:00
}
else
{
2025-01-14 17:54:06 +01:00
$ ( this ) . after ( '<a id="camerabarcodescanner-start-button" class="btn btn-sm btn-primary text-white" data-target="' + $ ( this ) . attr ( "data-target" ) + '"><i class="fa-solid fa-camera"></i></a>' ) ;
2019-09-27 17:24:44 +02:00
}
2022-02-11 18:18:17 +01:00
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . InitDone = true ;
2019-09-27 17:24:44 +02:00
} ) ;
2022-02-11 18:18:17 +01:00
}
setTimeout ( function ( )
{
2025-01-12 23:51:00 +01:00
Grocy . Components . CameraBarcodeScanner . Init ( ) ;
2019-09-19 12:48:02 +02:00
} , 50 ) ;