Barcode Operations
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
var scanner = (function() {
|
||||
var _callback = null;
|
||||
var _allow_raw = true;
|
||||
var _allowed_types = null;
|
||||
var _camera = null;
|
||||
var _modal = null;
|
||||
var _buffer = '';
|
||||
var _last_key_ts = 0;
|
||||
var _max_key_gap = 0;
|
||||
var _HW_THRESHOLD = 50;
|
||||
|
||||
function _on_paste(e) {
|
||||
if (!_callback) return;
|
||||
var el = document.activeElement;
|
||||
if (el && el.tagName === 'TEXTAREA') return;
|
||||
var text = ((e.originalEvent || e).clipboardData || window.clipboardData).getData('text');
|
||||
if (!text) return;
|
||||
text = text.trim();
|
||||
if (text.length < 3) return;
|
||||
e.preventDefault();
|
||||
_resolve(text);
|
||||
}
|
||||
|
||||
function _on_keydown(e) {
|
||||
var now = Date.now();
|
||||
var gap = now - _last_key_ts;
|
||||
|
||||
if (gap > 500) {
|
||||
_buffer = '';
|
||||
_max_key_gap = 0;
|
||||
} else if (_buffer.length > 0) {
|
||||
_max_key_gap = Math.max(_max_key_gap, gap);
|
||||
}
|
||||
_last_key_ts = now;
|
||||
|
||||
if (e.key === 'Enter' && _buffer.length >= 3) {
|
||||
var barcode = _buffer;
|
||||
var is_scanner_input = _max_key_gap <= _HW_THRESHOLD;
|
||||
_buffer = '';
|
||||
_max_key_gap = 0;
|
||||
if (is_scanner_input) {
|
||||
e.preventDefault();
|
||||
_remove_typed_barcode(barcode);
|
||||
_resolve(barcode);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.key.length === 1) {
|
||||
_buffer += e.key;
|
||||
}
|
||||
}
|
||||
|
||||
function _remove_typed_barcode(barcode) {
|
||||
var el = document.activeElement;
|
||||
if (!el || !('value' in el)) return;
|
||||
if (el.tagName !== 'INPUT' && el.tagName !== 'TEXTAREA') return;
|
||||
|
||||
var value = String(el.value || '');
|
||||
if (!value.endsWith(barcode)) return;
|
||||
el.value = value.slice(0, value.length - barcode.length);
|
||||
$(el).trigger('input');
|
||||
}
|
||||
|
||||
function _resolve(barcode) {
|
||||
if (!barcode || !_callback) return;
|
||||
|
||||
$.ajax({
|
||||
url: server_url + 'ics/api/engine/barcode_lookup.php',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: { json: JSON.stringify({ barcode: barcode, action: 'read' }) },
|
||||
headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') },
|
||||
success: function(res) {
|
||||
if (res && res.success) {
|
||||
if ((!_allow_raw && res.type === 'raw') || (_allowed_types && _allowed_types.indexOf(res.type) === -1)) {
|
||||
_show_error('This page only accepts generated barcode labels.');
|
||||
return;
|
||||
}
|
||||
_callback(res.sku || '', res.product_name || '', res.uom || '', res);
|
||||
return;
|
||||
}
|
||||
_show_error('Barcode not found: ' + barcode);
|
||||
},
|
||||
error: function(xhr) {
|
||||
var msg = xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : 'Scanner error. Please try again.';
|
||||
_show_error(msg === 'Barcode not found.' ? 'Barcode not found: ' + barcode : msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _build_modal() {
|
||||
if (_modal) return;
|
||||
$('body').append(`
|
||||
<div class="modal fade" id="scanner_modal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="ti ti-scan me-2"></i>Scan Barcode</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="scanner_qr_reader"></div>
|
||||
<div id="scanner_error" class="alert alert-danger mt-3 d-none"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`);
|
||||
_modal = new bootstrap.Modal(document.getElementById('scanner_modal'));
|
||||
document.getElementById('scanner_modal').addEventListener('hidden.bs.modal', _stop_camera);
|
||||
}
|
||||
|
||||
function _start_camera() {
|
||||
if (_camera) return;
|
||||
if (typeof Html5Qrcode === 'undefined') {
|
||||
_show_error('Camera scanner library is not available.');
|
||||
return;
|
||||
}
|
||||
_camera = new Html5Qrcode('scanner_qr_reader');
|
||||
_camera.start(
|
||||
{ facingMode: 'environment' },
|
||||
{ fps: 10, qrbox: { width: 250, height: 120 } },
|
||||
function(decodedText) {
|
||||
_modal.hide();
|
||||
_resolve(decodedText);
|
||||
},
|
||||
function() {}
|
||||
).catch(function(err) {
|
||||
_show_error('Camera error: ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
function _stop_camera() {
|
||||
if (!_camera) return;
|
||||
_camera.stop().catch(function() {}).finally(function() {
|
||||
_camera = null;
|
||||
});
|
||||
}
|
||||
|
||||
function _show_error(msg) {
|
||||
var $err = $('#scanner_error');
|
||||
if ($err.length) {
|
||||
$err.text(msg).removeClass('d-none');
|
||||
setTimeout(function() { $err.addClass('d-none'); }, 3000);
|
||||
return;
|
||||
}
|
||||
if (typeof bootbox !== 'undefined') bootbox.alert(msg);
|
||||
}
|
||||
|
||||
return {
|
||||
init: function(options) {
|
||||
options = options || {};
|
||||
_callback = options.onScan || null;
|
||||
_allow_raw = options.allowRaw !== false;
|
||||
_allowed_types = Array.isArray(options.allowedTypes) ? options.allowedTypes : null;
|
||||
document.removeEventListener('keydown', _on_keydown);
|
||||
document.removeEventListener('paste', _on_paste);
|
||||
document.addEventListener('keydown', _on_keydown);
|
||||
document.addEventListener('paste', _on_paste);
|
||||
_build_modal();
|
||||
},
|
||||
open_camera: function() {
|
||||
if (!_modal) _build_modal();
|
||||
_modal.show();
|
||||
setTimeout(_start_camera, 400);
|
||||
},
|
||||
destroy: function() {
|
||||
document.removeEventListener('keydown', _on_keydown);
|
||||
document.removeEventListener('paste', _on_paste);
|
||||
_stop_camera();
|
||||
}
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user