$(function() { var resultCollector = Quagga.ResultCollector.create({ capture: true, capacity: 20, blacklist: [{ code: "WIWV8ETQZ1", format: "code_93" }, { code: "EH3C-%GU23RK3", format: "code_93" }, { code: "O308SIHQOXN5SA/PJ", format: "code_93" }, { code: "DG7Q$TV8JQ/EN", format: "code_93" }, { code: "VOFD1DB5A.1F6QU", format: "code_93" }, { code: "4SO64P4X8 U4YUU1T-", format: "code_93" }], filter: function(codeResult) { // only store results which match this constraint // e.g.: codeResult return true; } }); var App = { _initDebounceTimer: null, _pendingReinit: false, init: function() { var self = this; Quagga.init(this.state, function(err) { if (err) { return self.handleError(err); } //Quagga.registerResultCollector(resultCollector); App.attachListeners(); Quagga.start(); App.initCameraSelection(); App.checkCapabilities(); // Sync UI checkboxes to match actual state after init $('input[name="locate"]').prop('checked', App.state.locate); }); }, reinit: function() { // Debounced reinit: cancel pending and schedule a new one console.log('[App.reinit] Called, current timer:', App._initDebounceTimer !== null, 'Current state.locate:', App.state.locate); if (App._initDebounceTimer) { console.log('[App.reinit] Cancelling pending init'); clearTimeout(App._initDebounceTimer); } App._initDebounceTimer = setTimeout(function() { console.log('[App.reinit] Debounce expired, calling init() with state.locate:', App.state.locate); App._initDebounceTimer = null; App._pendingReinit = false; App.init(); }, 250); }, handleError: function(err) { console.log(err); // If we attempted to open a specific device and failed, revert to last known-good constraints try { var attempted = (App.state && App.state.inputStream && App.state.inputStream.constraints) || {}; var hadSpecificDevice = attempted && attempted.deviceId; if (hadSpecificDevice) { // Detach to avoid stacked handlers // Try reinitializing to restore a working stream Quagga.stop(); App.init(); } } catch(e2) { // Ignore and leave UI available for user to pick another device } }, checkCapabilities: function() { var track = Quagga.CameraAccess.getActiveTrack(); var capabilities = {}; if (typeof track.getCapabilities === 'function') { capabilities = track.getCapabilities(); } this.applySettingsVisibility('zoom', capabilities.zoom); this.applySettingsVisibility('torch', capabilities.torch); }, updateOptionsForMediaRange: function(node, range) { console.log('updateOptionsForMediaRange', node, range); var NUM_STEPS = 6; var stepSize = (range.max - range.min) / NUM_STEPS; var option; var value; while (node.firstChild) { node.removeChild(node.firstChild); } for (var i = 0; i <= NUM_STEPS; i++) { value = range.min + (stepSize * i); option = document.createElement('option'); option.value = value; option.innerHTML = value; node.appendChild(option); } }, applySettingsVisibility: function(setting, capability) { // depending on type of capability if (typeof capability === 'boolean') { var node = document.querySelector('input[name="settings_' + setting + '"]'); if (node) { node.parentNode.style.display = capability ? 'block' : 'none'; } return; } if (window.MediaSettingsRange && capability instanceof window.MediaSettingsRange) { var node = document.querySelector('select[name="settings_' + setting + '"]'); if (node) { this.updateOptionsForMediaRange(node, capability); node.parentNode.style.display = 'block'; } return; } }, initCameraSelection: function(){ var streamLabel = Quagga.CameraAccess.getActiveStreamLabel(); var selectedDeviceId = null; try { // Prefer explicit deviceId from current state if present var constraints = (this.state && this.state.inputStream && this.state.inputStream.constraints) || {}; if (constraints.deviceId) { selectedDeviceId = (typeof constraints.deviceId === 'object' && constraints.deviceId.exact) ? constraints.deviceId.exact : constraints.deviceId; } else { selectedDeviceId = null; } } catch(e) {} return Quagga.CameraAccess.enumerateVideoDevices() .then(function(devices) { function pruneText(text) { return text.length > 30 ? text.substr(0, 30) : text; } var $deviceSelection = document.getElementById("deviceSelection"); while ($deviceSelection.firstChild) { $deviceSelection.removeChild($deviceSelection.firstChild); } devices.forEach(function(device) { var $option = document.createElement("option"); $option.value = device.deviceId || device.id; $option.appendChild(document.createTextNode(pruneText(device.label || device.deviceId || device.id))); // Preserve explicit selection by deviceId when available; fallback to stream label if (selectedDeviceId) { $option.selected = ($option.value === selectedDeviceId); } else { $option.selected = (streamLabel === device.label); } $deviceSelection.appendChild($option); }); }); }, attachListeners: function() { var self = this; console.log('[App.attachListeners] Called - this should only happen once per init'); self.initCameraSelection(); // Remove any existing handlers to prevent stacking $(".controls").off("click", "button.stop"); $(".controls .reader-config-group").off("change", "input, select"); $(".controls").on("click", "button.stop", function(e) { e.preventDefault(); Quagga.stop(); self._printCollectedResults(); }); $(".controls .reader-config-group").on("change", "input, select", function(e) { e.preventDefault(); var $target = $(e.target), value = $target.attr("type") === "checkbox" ? $target.prop("checked") : $target.val(), name = $target.attr("name"), state = self._convertNameToState(name); console.log("Value of "+ state + " changed to " + value, "checkbox checked prop:", $target.prop("checked"), "target type:", $target.attr("type")); self.setState(state, value); }); }, _printCollectedResults: function() { var results = resultCollector.getResults(), $ul = $("#result_strip ul.collector"); results.forEach(function(result) { var $li = $('