live_w_locator.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. $(function() {
  2. var App = {
  3. init : function() {
  4. Quagga.init(this.state, function(err) {
  5. if (err) {
  6. console.log(err);
  7. return;
  8. }
  9. App.attachListeners();
  10. App.checkCapabilities();
  11. Quagga.start();
  12. });
  13. },
  14. checkCapabilities: function() {
  15. var track = Quagga.CameraAccess.getActiveTrack();
  16. var capabilities = {};
  17. if (typeof track.getCapabilities === 'function') {
  18. capabilities = track.getCapabilities();
  19. }
  20. this.applySettingsVisibility('zoom', capabilities.zoom);
  21. this.applySettingsVisibility('torch', capabilities.torch);
  22. },
  23. updateOptionsForMediaRange: function(node, range) {
  24. console.log('updateOptionsForMediaRange', node, range);
  25. var NUM_STEPS = 6;
  26. var stepSize = (range.max - range.min) / NUM_STEPS;
  27. var option;
  28. var value;
  29. while (node.firstChild) {
  30. node.removeChild(node.firstChild);
  31. }
  32. for (var i = 0; i <= NUM_STEPS; i++) {
  33. value = range.min + (stepSize * i);
  34. option = document.createElement('option');
  35. option.value = value;
  36. option.innerHTML = value;
  37. node.appendChild(option);
  38. }
  39. },
  40. applySettingsVisibility: function(setting, capability) {
  41. // depending on type of capability
  42. if (typeof capability === 'boolean') {
  43. var node = document.querySelector('input[name="settings_' + setting + '"]');
  44. if (node) {
  45. node.parentNode.style.display = capability ? 'block' : 'none';
  46. }
  47. return;
  48. }
  49. if (window.MediaSettingsRange && capability instanceof window.MediaSettingsRange) {
  50. var node = document.querySelector('select[name="settings_' + setting + '"]');
  51. if (node) {
  52. this.updateOptionsForMediaRange(node, capability);
  53. node.parentNode.style.display = 'block';
  54. }
  55. return;
  56. }
  57. },
  58. initCameraSelection: function(){
  59. var streamLabel = Quagga.CameraAccess.getActiveStreamLabel();
  60. return Quagga.CameraAccess.enumerateVideoDevices()
  61. .then(function(devices) {
  62. function pruneText(text) {
  63. return text.length > 30 ? text.substr(0, 30) : text;
  64. }
  65. var $deviceSelection = document.getElementById("deviceSelection");
  66. while ($deviceSelection.firstChild) {
  67. $deviceSelection.removeChild($deviceSelection.firstChild);
  68. }
  69. devices.forEach(function(device) {
  70. var $option = document.createElement("option");
  71. $option.value = device.deviceId || device.id;
  72. $option.appendChild(document.createTextNode(pruneText(device.label || device.deviceId || device.id)));
  73. $option.selected = streamLabel === device.label;
  74. $deviceSelection.appendChild($option);
  75. });
  76. });
  77. },
  78. attachListeners: function() {
  79. var self = this;
  80. self.initCameraSelection();
  81. $(".controls").on("click", "button.stop", function(e) {
  82. e.preventDefault();
  83. Quagga.stop();
  84. });
  85. $(".controls .reader-config-group").on("change", "input, select", function(e) {
  86. e.preventDefault();
  87. var $target = $(e.target),
  88. value = $target.attr("type") === "checkbox" ? $target.prop("checked") : $target.val(),
  89. name = $target.attr("name"),
  90. state = self._convertNameToState(name);
  91. console.log("Value of "+ state + " changed to " + value);
  92. self.setState(state, value);
  93. });
  94. $("#snapshot").on("click", function(e) {
  95. e.preventDefault();
  96. var code = $("#manual_code").val();
  97. if (self.addCode(code)) {
  98. $("#manual_code").val("");
  99. }
  100. });
  101. $("#submit_button").on("click", function(e) {
  102. $(".price input").each(function() {
  103. if ($(this).val() <= 0 || $(this).val() == "") {
  104. e.preventDefault();
  105. $(this).focus();
  106. }
  107. });
  108. });
  109. },
  110. _accessByPath: function(obj, path, val) {
  111. var parts = path.split('.'),
  112. depth = parts.length,
  113. setter = (typeof val !== "undefined") ? true : false;
  114. return parts.reduce(function(o, key, i) {
  115. if (setter && (i + 1) === depth) {
  116. if (typeof o[key] === "object" && typeof val === "object") {
  117. Object.assign(o[key], val);
  118. } else {
  119. o[key] = val;
  120. }
  121. }
  122. return key in o ? o[key] : {};
  123. }, obj);
  124. },
  125. _convertNameToState: function(name) {
  126. return name.replace("_", ".").split("-").reduce(function(result, value) {
  127. return result + value.charAt(0).toUpperCase() + value.substring(1);
  128. });
  129. },
  130. detachListeners: function() {
  131. $(".controls").off("click", "button.stop");
  132. $(".controls .reader-config-group").off("change", "input, select");
  133. },
  134. applySetting: function(setting, value) {
  135. var track = Quagga.CameraAccess.getActiveTrack();
  136. if (track && typeof track.getCapabilities === 'function') {
  137. switch (setting) {
  138. case 'zoom':
  139. return track.applyConstraints({advanced: [{zoom: parseFloat(value)}]});
  140. case 'torch':
  141. return track.applyConstraints({advanced: [{torch: !!value}]});
  142. }
  143. }
  144. },
  145. setState: function(path, value) {
  146. var self = this;
  147. if (typeof self._accessByPath(self.inputMapper, path) === "function") {
  148. value = self._accessByPath(self.inputMapper, path)(value);
  149. }
  150. if (path.startsWith('settings.')) {
  151. var setting = path.substring(9);
  152. return self.applySetting(setting, value);
  153. }
  154. self._accessByPath(self.state, path, value);
  155. console.log(JSON.stringify(self.state));
  156. App.detachListeners();
  157. Quagga.stop();
  158. App.init();
  159. },
  160. addCode: function(code) {
  161. if (code.match(/^\d\d\d\.\d\d\d$/)) {
  162. code = code.replace('.', '-');
  163. }
  164. if (!App.results.includes(code) && code.match(/^\d\d\d\-\d\d\d$/)) {
  165. App.results.push(code);
  166. var $node = null, canvas = Quagga.canvas.dom.image;
  167. $node = $('<li><div class="thumbnail">' +
  168. '<div class="close"><button type="button" class="btn-close" aria-label="entfernen"></button></div>' +
  169. '<div class="imgWrapper"><img /></div>' +
  170. '<div class="caption"><h4 class="code"></h4></div>' +
  171. '<div class="price"><input type="number" size="5" step="0.1"/></div>' +
  172. '</div></li>');
  173. $node.find("img").attr("src", canvas.toDataURL());
  174. $node.find("h4.code").html(code);
  175. $node.find("input").attr("name", code);
  176. $("#result_strip ul.thumbnails").append($node);
  177. $node.find("input").focus();
  178. $(".btn-close").on("click", function(e) {
  179. e.preventDefault();
  180. e.target.parentNode.parentNode.parentNode.remove();
  181. });
  182. return true;
  183. }
  184. return false;
  185. },
  186. inputMapper: {
  187. inputStream: {
  188. constraints: function(value){
  189. if (/^(\d+)x(\d+)$/.test(value)) {
  190. var values = value.split('x');
  191. return {
  192. width: {min: parseInt(values[0])},
  193. height: {min: parseInt(values[1])}
  194. };
  195. }
  196. return {
  197. deviceId: value
  198. };
  199. }
  200. },
  201. numOfWorkers: function(value) {
  202. return parseInt(value);
  203. },
  204. decoder: {
  205. readers: function(value) {
  206. if (value === 'ean_extended') {
  207. return [{
  208. format: "ean_reader",
  209. config: {
  210. supplements: [
  211. 'ean_5_reader', 'ean_2_reader'
  212. ]
  213. }
  214. }];
  215. }
  216. return [{
  217. format: value + "_reader",
  218. config: {}
  219. }];
  220. }
  221. }
  222. },
  223. state: {
  224. inputStream: {
  225. type : "LiveStream",
  226. constraints: {
  227. width: 640, //{min: 640, max: 640},
  228. height: 480, // {min: 480, max: 480},
  229. aspectRatio: 1, // min: 1, max: 50,
  230. facingMode: "environment" // "environment" // or user
  231. }
  232. },
  233. locator: {
  234. patchSize: "medium",
  235. halfSample: true
  236. },
  237. numOfWorkers: 2,
  238. frequency: 10,
  239. decoder: {
  240. readers : [{
  241. format: "code_128_reader",
  242. config: {}
  243. }]
  244. },
  245. locate: true
  246. },
  247. lastResult : null,
  248. results: []
  249. };
  250. App.init();
  251. Quagga.onProcessed(function(result) {
  252. var drawingCtx = Quagga.canvas.ctx.overlay,
  253. drawingCanvas = Quagga.canvas.dom.overlay;
  254. if (result) {
  255. if (result.boxes) {
  256. drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
  257. result.boxes.filter(function (box) {
  258. return box !== result.box;
  259. }).forEach(function (box) {
  260. Quagga.ImageDebug.drawPath(box, {x: 0, y: 1}, drawingCtx, {color: "green", lineWidth: 2});
  261. });
  262. }
  263. if (result.box) {
  264. Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1}, drawingCtx, {color: "#00F", lineWidth: 2});
  265. }
  266. if (result.codeResult && result.codeResult.code) {
  267. Quagga.ImageDebug.drawPath(result.line, {x: 'x', y: 'y'}, drawingCtx, {color: 'red', lineWidth: 3});
  268. }
  269. }
  270. });
  271. Quagga.onDetected(function(result) {
  272. App.addCode(result.codeResult.code)
  273. });
  274. });