jquery-scannerdetection.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /*
  2. * jQuery Scanner Detection
  3. *
  4. * Copyright (c) 2013 Julien Maurel
  5. *
  6. * Licensed under the MIT license:
  7. * http://www.opensource.org/licenses/mit-license.php
  8. *
  9. * Project home:
  10. * https://github.com/julien-maurel/jQuery-Scanner-Detection
  11. *
  12. * Version: 1.2.1
  13. *
  14. */
  15. (function($){
  16. $.fn.scannerDetection=function(options){
  17. // If string given, call onComplete callback
  18. if(typeof options==="string"){
  19. this.each(function(){
  20. this.scannerDetectionTest(options);
  21. });
  22. return this;
  23. }
  24. // If false (boolean) given, deinitialize plugin
  25. if(options === false){
  26. this.each(function(){
  27. this.scannerDetectionOff();
  28. });
  29. return this;
  30. }
  31. var defaults={
  32. onComplete:false, // Callback after detection of a successfull scanning (scanned string in parameter)
  33. onError:false, // Callback after detection of a unsuccessfull scanning (scanned string in parameter)
  34. onReceive:false, // Callback after receiving and processing a char (scanned char in parameter)
  35. onKeyDetect:false, // Callback after detecting a keyDown (key char in parameter) - in contrast to onReceive, this fires for non-character keys like tab, arrows, etc. too!
  36. timeBeforeScanTest:100, // Wait duration (ms) after keypress event to check if scanning is finished
  37. avgTimeByChar:30, // Average time (ms) between 2 chars. Used to do difference between keyboard typing and scanning
  38. minLength:6, // Minimum length for a scanning
  39. endChar:[9,13], // Chars to remove and means end of scanning
  40. startChar:[], // Chars to remove and means start of scanning
  41. ignoreIfFocusOn:false, // do not handle scans if the currently focused element matches this selector
  42. scanButtonKeyCode:false, // Key code of the scanner hardware button (if the scanner button a acts as a key itself)
  43. scanButtonLongPressThreshold:3, // How many times the hardware button should issue a pressed event before a barcode is read to detect a longpress
  44. onScanButtonLongPressed:false, // Callback after detection of a successfull scan while the scan button was pressed and held down
  45. stopPropagation:false, // Stop immediate propagation on keypress event
  46. preventDefault:false // Prevent default action on keypress event
  47. };
  48. if(typeof options==="function"){
  49. options={onComplete:options}
  50. }
  51. if(typeof options!=="object"){
  52. options=$.extend({},defaults);
  53. }else{
  54. options=$.extend({},defaults,options);
  55. }
  56. this.each(function(){
  57. var self=this, $self=$(self), firstCharTime=0, lastCharTime=0, stringWriting='', callIsScanner=false, testTimer=false, scanButtonCounter=0;
  58. var initScannerDetection=function(){
  59. firstCharTime=0;
  60. stringWriting='';
  61. scanButtonCounter=0;
  62. };
  63. self.scannerDetectionOff=function(){
  64. $self.unbind('keydown.scannerDetection');
  65. $self.unbind('keypress.scannerDetection');
  66. }
  67. self.isFocusOnIgnoredElement=function(){
  68. if(!options.ignoreIfFocusOn) return false;
  69. if(typeof options.ignoreIfFocusOn === 'string') return $(':focus').is(options.ignoreIfFocusOn);
  70. if(typeof options.ignoreIfFocusOn === 'object' && options.ignoreIfFocusOn.length){
  71. var focused=$(':focus');
  72. for(var i=0; i<options.ignoreIfFocusOn.length; i++){
  73. if(focused.is(options.ignoreIfFocusOn[i])){
  74. return true;
  75. }
  76. }
  77. }
  78. return false;
  79. }
  80. self.scannerDetectionTest=function(s){
  81. // If string is given, test it
  82. if(s){
  83. firstCharTime=lastCharTime=0;
  84. stringWriting=s;
  85. }
  86. if (!scanButtonCounter){
  87. scanButtonCounter = 1;
  88. }
  89. // If all condition are good (length, time...), call the callback and re-initialize the plugin for next scanning
  90. // Else, just re-initialize
  91. if(stringWriting.length>=options.minLength && lastCharTime-firstCharTime<stringWriting.length*options.avgTimeByChar){
  92. if(options.onScanButtonLongPressed && scanButtonCounter > options.scanButtonLongPressThreshold) options.onScanButtonLongPressed.call(self,stringWriting,scanButtonCounter);
  93. else if(options.onComplete) options.onComplete.call(self,stringWriting,scanButtonCounter);
  94. $self.trigger('scannerDetectionComplete',{string:stringWriting});
  95. initScannerDetection();
  96. return true;
  97. }else{
  98. if(options.onError) options.onError.call(self,stringWriting);
  99. $self.trigger('scannerDetectionError',{string:stringWriting});
  100. initScannerDetection();
  101. return false;
  102. }
  103. }
  104. $self.data('scannerDetection',{options:options}).unbind('.scannerDetection').bind('keydown.scannerDetection',function(e){
  105. // If it's just the button of the scanner, ignore it and wait for the real input
  106. if(options.scanButtonKeyCode !== false && e.which==options.scanButtonKeyCode) {
  107. scanButtonCounter++;
  108. // Cancel default
  109. e.preventDefault();
  110. e.stopImmediatePropagation();
  111. }
  112. // Add event on keydown because keypress is not triggered for non character keys (tab, up, down...)
  113. // So need that to check endChar and startChar (that is often tab or enter) and call keypress if necessary
  114. else if((firstCharTime && options.endChar.indexOf(e.which)!==-1)
  115. || (!firstCharTime && options.startChar.indexOf(e.which)!==-1)){
  116. // Clone event, set type and trigger it
  117. var e2=jQuery.Event('keypress',e);
  118. e2.type='keypress.scannerDetection';
  119. $self.triggerHandler(e2);
  120. // Cancel default
  121. e.preventDefault();
  122. e.stopImmediatePropagation();
  123. }
  124. // Fire keyDetect event in any case!
  125. if(options.onKeyDetect) options.onKeyDetect.call(self,e);
  126. $self.trigger('scannerDetectionKeyDetect',{evt:e});
  127. }).bind('keypress.scannerDetection',function(e){
  128. if (this.isFocusOnIgnoredElement()) return;
  129. if(options.stopPropagation) e.stopImmediatePropagation();
  130. if(options.preventDefault) e.preventDefault();
  131. if(firstCharTime && options.endChar.indexOf(e.which)!==-1){
  132. e.preventDefault();
  133. e.stopImmediatePropagation();
  134. callIsScanner=true;
  135. }else if(!firstCharTime && options.startChar.indexOf(e.which)!==-1){
  136. e.preventDefault();
  137. e.stopImmediatePropagation();
  138. callIsScanner=false;
  139. }else{
  140. if (typeof(e.which) != 'undefined'){
  141. stringWriting+=String.fromCharCode(e.which);
  142. }
  143. callIsScanner=false;
  144. }
  145. if(!firstCharTime){
  146. firstCharTime=Date.now();
  147. }
  148. lastCharTime=Date.now();
  149. if(testTimer) clearTimeout(testTimer);
  150. if(callIsScanner){
  151. self.scannerDetectionTest();
  152. testTimer=false;
  153. }else{
  154. testTimer=setTimeout(self.scannerDetectionTest,options.timeBeforeScanTest);
  155. }
  156. if(options.onReceive) options.onReceive.call(self,e);
  157. $self.trigger('scannerDetectionReceive',{evt:e});
  158. });
  159. });
  160. return this;
  161. }
  162. })(jQuery);