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