Merge pull request #4106 from solgenomics/topic/wishlist
[sgn.git] / mason / barcode / read.mas
blob518668ad231a3fa0f5cd79930f96368aa4ff33b8
1 <%args>
2 </%args>
4 <!-- 
5                             BARCODE READER
6     This page can be used to read a barcode directly from the website
7     using the device's camera.  If a barcode is successfully read, the 
8     page will return to the provided return url with a query parameter
9     set to the URI encoded contents of the read barcode.
11     Usage: /barcode/read?return=/breeders/seedlot/maintenance&param=barcode
12     This will start the barcode reader and when a barcode is found, change 
13     the location of the browser to:
14         /breeders/seedlot/maintenance?barcode={{contents}}
16     The javascript qr code reader used: https://github.com/cozmo/jsQR
17     Code based on demo: https://cozmo.github.io/jsQR/
18 -->
20 <& /util/import_javascript.mas, classes => [ 'bootstrap_min.js', 'jquery', 'jsqr' ] &>
22 <& /page/page_title.mas, title => 'Barcode Reader' &>
24 <div class="panel panel-default">
25     <div class="panel-heading">Scan a Barcode<span id="panel-heading-return"></span></div>
26     <div class="panel-body" style="text-align: center">
27         <div id="loadingMessage">Starting...</div>
28         <canvas id="canvas" hidden></canvas>
29     </div>
30     <div class="panel-footer">Scanning for Barcode...</div>
31 </div>
33 <script type="text/javascript">
35 let RETURN_URL = undefined;
36 let RETURN_PARAM = undefined;
38 jQuery(document).ready(function() {
40     // Parse the query arguments
41     parseArgs();
43     // Start the Barcode Reader
44     startReader();
46 });
49 /**
50  * Parse the query params for the return url and param
51  */
52 function parseArgs() {
53     const urlSearchParams = new URLSearchParams(window.location.search);
54     if ( urlSearchParams.has('return') ) {
55         RETURN_URL = decodeURIComponent(urlSearchParams.get('return'));
56         jQuery("#panel-heading-return").html(" to return to <a href='" + RETURN_URL + "'>" + RETURN_URL + "</a>");
57     }
58     RETURN_PARAM = urlSearchParams.get('param');
62 /**
63  * Start the Barcode Reader
64  * - Start the camera feed
65  * - Start processing the images for a barcode
66  * - Call processCode(data) when a barcode has been found
67  */
68 function startReader() {
69     var video = document.createElement("video");
70     var canvasElement = document.getElementById("canvas");
71     var canvas = canvasElement.getContext("2d");
72     var loadingMessage = document.getElementById("loadingMessage");
73     loadingMessage.innerHTML = "<strong>Unable to access video stream</strong><br />Please make sure you have a webcam enabled and permissions have been granted for this site</p>";
75     // Use facingMode: environment to attemt to get the front camera on phones
76     navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).then(function(stream) {
77         video.srcObject = stream;
78         video.setAttribute("playsinline", true); // required to tell iOS safari we don't want fullscreen
79         video.play();
80         requestAnimationFrame(tick);
81     });
83     function tick() {
84         loadingMessage.innerText = "Loading video...";
85         if (video.readyState === video.HAVE_ENOUGH_DATA) {
86             loadingMessage.hidden = true;
87             canvasElement.hidden = false;
89             canvasElement.height = video.videoHeight;
90             canvasElement.width = video.videoWidth;
91             canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);
92             var imageData = canvas.getImageData(0, 0, canvasElement.width, canvasElement.height);
93             var code = jsQR(imageData.data, imageData.width, imageData.height, {
94                 inversionAttempts: "dontInvert",
95             });
96             if (code) {
97                 drawLine(code.location.topLeftCorner, code.location.topRightCorner, "#FF3B58");
98                 drawLine(code.location.topRightCorner, code.location.bottomRightCorner, "#FF3B58");
99                 drawLine(code.location.bottomRightCorner, code.location.bottomLeftCorner, "#FF3B58");
100                 drawLine(code.location.bottomLeftCorner, code.location.topLeftCorner, "#FF3B58");
101                 processCode(code.data);
102             } 
103         }
104         requestAnimationFrame(tick);
105     }
107     function drawLine(begin, end, color) {
108         canvas.beginPath();
109         canvas.moveTo(begin.x, begin.y);
110         canvas.lineTo(end.x, end.y);
111         canvas.lineWidth = 4;
112         canvas.strokeStyle = color;
113         canvas.stroke();
114     }
119  * Stop the camera feed
120  */
121 function stopReader() {
122     if ( video ) {
123         var stream = video.srcObject;
124         var tracks = stream.getTracks();
125         video.pause();
126         tracks.forEach(function(track) { track.stop(); });
127         video.srcObject = null;
128         canvas.clearRect(0, 0, canvasElement.width, canvasElement.height);
129         cancelAnimationFrame(animFrameId);
130     }
135  * Process the QR code data
136  * - If return and param args are set, return to the provided url
137  * @param {string} data QR Code data
138  */
139 function processCode(data) {
140     jQuery(".panel-footer").html("<strong>Barcode Found:</strong> " + data);
141     if ( data && data !== "" && RETURN_URL && RETURN_URL !== "" && RETURN_PARAM && RETURN_PARAM !== "" ) {
142         let q = RETURN_URL.includes('?') ? '&' : '?';
143         window.location = RETURN_URL + q + RETURN_PARAM + '=' + encodeURIComponent(data);
144         stopReader();
145     }
148 </script>