Created
December 2, 2017 03:26
-
-
Save ftc2/cfbecbffc9818626f58dbcad907663a0 to your computer and use it in GitHub Desktop.
jquery.autotype.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* jQuery.autotype - Simple, accurate, typing simulation for jQuery | |
* | |
* version 0.5.0 | |
* | |
* http://michaelmonteleone.net/projects/autotype | |
* http://github.com/mmonteleone/jquery.autotype | |
* | |
* Copyright (c) 2009 Michael Monteleone | |
* Licensed under terms of the MIT License (README.markdown) | |
*/ | |
(function($){ | |
// code type constants | |
var CHARACTER = 1, | |
NON_CHARACTER = 2, | |
MODIFIER_BEGIN = 3, | |
MODIFIER_END = 4, | |
isNullOrEmpty = function(val) { return val === null || val.length === 0; }, | |
isUpper = function(char) { return char.toUpperCase() === char; }, | |
isLower = function(char) { return char.toLowerCase() === char; }, | |
areDifferentlyCased = function(char1,char2) { | |
return (isUpper(char1) && isLower(char2)) || | |
(isLower(char1) && isUpper(char2)); | |
}, | |
convertCase = function(char) { | |
return isUpper(char) ? char.toLowerCase() : char.toUpperCase(); | |
}, | |
parseCodes = function(value, codeMap) { | |
// buffer to hold a collection of key/char code pairs corresponding to input string value | |
var codes = [], | |
// buffer to hold the name of a control key as it's being parsed | |
definingControlKey = false, | |
// hold a collection of currently pushed modifier keys | |
activeModifiers = { | |
alt: false, | |
meta: false, | |
shift: false, | |
ctrl: false | |
}, | |
explicitModifiers = $.extend({}, activeModifiers), | |
// buffer to hold construction of current control key | |
currentControlKey = '', | |
previousChar = '', | |
pushCode = function(opts) { | |
codes.push($.extend({}, opts, activeModifiers)); | |
}, | |
pushModifierBeginCode = function(modifierName) { | |
activeModifiers[modifierName] = true; | |
pushCode({ | |
keyCode: codeMap[modifierName], | |
charCode: 0, | |
char: '', | |
type: MODIFIER_BEGIN | |
}); | |
}, | |
pushModifierEndCode = function(modifierName) { | |
activeModifiers[modifierName] = false; | |
pushCode({ | |
keyCode: codeMap[modifierName], | |
charCode: 0, | |
char: '', | |
type: MODIFIER_END | |
}); | |
}; | |
for(var i=0;i<value.length;i++) { | |
// if the character is about to define a control key | |
if(!definingControlKey && | |
i <= value.length - 5 && | |
value.charAt(i) === '{' && | |
value.charAt(i+1) === '{') | |
{ | |
// skip the next "{" | |
i++; | |
definingControlKey = true; | |
} | |
// if the character is about to end definition of control key | |
else if (definingControlKey && | |
i <= value.length - 2 && | |
value.charAt(i) === '}' && | |
value.charAt(i+1) === '}') | |
{ | |
// skip the next "}" | |
i++; | |
// check if this key is a modifier-opener (is a ctrl,alt,del,shift) | |
if(activeModifiers[currentControlKey] !== undefined) | |
{ | |
explicitModifiers[currentControlKey] = true; | |
pushModifierBeginCode(currentControlKey); | |
} | |
// check if this key is a modifier-closer (is a /ctrl,/alt,/del,.shift) | |
else if(activeModifiers[currentControlKey.substring(1)] !== undefined) | |
{ | |
explicitModifiers[currentControlKey] = false; | |
pushModifierEndCode(currentControlKey.substring(1)); | |
} | |
// otherwise is some other kind of non-modifier control key | |
else | |
{ | |
pushCode({ | |
keyCode: codeMap[currentControlKey], | |
charCode: 0, | |
char: '', | |
type: NON_CHARACTER, | |
controlKeyName: currentControlKey | |
}); | |
} | |
definingControlKey = false; | |
currentControlKey = ''; | |
} | |
// currently defining control key | |
else if (definingControlKey) | |
{ | |
currentControlKey += value.charAt(i); | |
} | |
// otherwise is just a text character | |
else | |
{ | |
var character = value.charAt(i); | |
// check for any implicitly changing of cases, and register presses/releases | |
// of the shift key in accord with them. | |
if( | |
(!isNullOrEmpty(previousChar) && areDifferentlyCased(previousChar, character)) || | |
(isNullOrEmpty(previousChar) && isUpper(character)) | |
) | |
{ | |
if(isUpper(character) && !activeModifiers.shift) { | |
pushModifierBeginCode("shift"); | |
} else if (isLower(character) && activeModifiers.shift && !explicitModifiers.shift){ | |
pushModifierEndCode("shift"); | |
} | |
} | |
// modify the current character if there are active modifiers | |
if((activeModifiers.shift && isLower(character)) || | |
(!activeModifiers.shift && isUpper(character))) { | |
// shift converts case | |
character = convertCase(character); | |
} | |
var code = { | |
// if can't identify a keycode, just fudge with the char code. | |
// nope, this isn't ideal by any means. | |
keyCode: codeMap[character] || character.charCodeAt(0), | |
charCode: character.charCodeAt(0), | |
char: character, | |
type: CHARACTER | |
}; | |
// modify the current character if there are active modifiers | |
if(activeModifiers.alt || | |
activeModifiers.ctrl || | |
activeModifiers.meta) { | |
// alt, ctrl, meta make it so nothing is typed | |
code.char = ''; | |
} | |
pushCode(code); | |
if(code.char !== '') { previousChar = code.char; } | |
} | |
} | |
return codes; | |
}, | |
triggerCodeOnField = function(code, field) { | |
// build up base content that every event should contain | |
// with information about whether certain chord keys are | |
// simulated as being pressed | |
var evnt = { | |
altKey: code.alt, | |
metaKey: code.meta, | |
shiftKey: code.shift, | |
ctrlKey: code.ctrl | |
}; | |
// build out 3 event instances for all the steps of a key entry | |
var keyDownEvent = $.extend($.Event(), evnt, {type:'keydown', keyCode: code.keyCode, charCode: 0, which: code.keyCode}); | |
var keyPressEvent = $.extend($.Event(), evnt, {type:'keypress', keyCode: 0, charCode: code.charCode, which: code.charCode || code.keyCode}); | |
var keyUpEvent = $.extend($.Event(), evnt, {type:'keyup', keyCode: code.keyCode, charCode: 0, which: code.keyCode}); | |
// go ahead and trigger the first 2 (down and press) | |
// a keyup of a modifier shouldn't also re-trigger a keydown | |
if(code.type !== MODIFIER_END) { | |
field.trigger(keyDownEvent); | |
} | |
// modifier keys don't have a keypress event, only down or up | |
if(code.type !== MODIFIER_BEGIN && code.type !== MODIFIER_END) { | |
field.trigger(keyPressEvent); | |
} | |
// only actually add the new character to the input if the keydown or keypress events | |
// weren't cancelled by any consuming event handlers | |
if(!keyDownEvent.isPropagationStopped() && | |
!keyPressEvent.isPropagationStopped()) { | |
if(code.type === NON_CHARACTER) { | |
switch(code.controlKeyName) { | |
case 'enter': | |
field.val(field.val() + "\n"); | |
break; | |
case 'back': | |
field.val(field.val().substring(0,field.val().length-1)); | |
break; | |
} | |
} else { | |
field.val(field.val() + code.char); | |
} | |
} | |
// then also trigger the 3rd event (up) | |
// a keydown of a modifier shouldn't also trigger a keyup until coded | |
if(code.type !== MODIFIER_BEGIN) { | |
field.trigger(keyUpEvent); | |
} | |
}, | |
triggerCodesOnField = function(codes, field, delay, global) { | |
if(delay > 0) { | |
codes = codes.reverse(); | |
var keyInterval = global.setInterval(function(){ | |
var code = codes.pop(); | |
triggerCodeOnField(code, field); | |
if(codes.length === 0) { | |
global.clearInterval(keyInterval); | |
field.trigger('autotyped'); | |
} | |
}, delay); | |
} else { | |
$.each(codes,function(){ | |
triggerCodeOnField(this, field); | |
}); | |
field.trigger('autotyped'); | |
} | |
}; | |
$.fn.autotype = function(value, options) { | |
if(value === undefined || value === null) { throw("Value is required by jQuery.autotype plugin"); } | |
var settings = $.extend({}, $.fn.autotype.defaults, options); | |
// 1st Pass | |
// step through the input string and convert it into | |
// a logical sequence of steps, key, and charcodes to apply to the inputs | |
var codes = parseCodes(value, settings.keyCodes[settings.keyBoard]); | |
// 2nd Pass | |
// Run the translated codes against each input through a realistic | |
// and cancelable series of key down/press/up events | |
return this.each(function(){ triggerCodesOnField(codes, $(this), settings.delay, settings.global); }); | |
}; | |
$.fn.autotype.defaults = { | |
version: '0.5.0', | |
keyBoard: 'enUs', | |
delay: 0, | |
global: window, | |
keyCodes: { | |
enUs: { 'back':8,'ins':45,'del':46,'enter':13,'shift':16,'ctrl':17,'meta':224, | |
'alt':18,'pause':19,'caps':20,'esc':27,'pgup':33,'pgdn':34, | |
'end':35,'home':36,'left':37,'up':38,'right':39,'down':40, | |
'printscr':44,'num0':96,'num1':97,'num2':98,'num3':99,'num4':100, | |
'num5':101,'num6':102,'num7':103,'num8':104,'num9':105, | |
'multiply':106,'add':107,'subtract':109,'decimal':110, | |
'divide':111,'f1':112,'f2':113,'f3':114,'f4':115,'f5':116, | |
'f6':117,'f7':118,'f8':119,'f9':120,'f10':121,'f11':122, | |
'f12':123,'numlock':144,'scrolllock':145,' ':9,' ':32, | |
'tab':9,'space':32,'0':48,'1':49,'2':50,'3':51,'4':52, | |
'5':53,'6':54,'7':55,'8':56,'9':57,')':48,'!':49,'@':50, | |
'#':51,'$':52,'%':53,'^':54,'&':55,'*':56,'(':57,';':186, | |
'=':187,',':188,'-':189,'.':190,'/':191,'[':219,'\\':220, | |
']':221,"'":222,':':186,'+':187,'<':188,'_':189,'>':190, | |
'?':191,'{':219,'|':220,'}':221,'"':222,'a':65,'b':66,'c':67, | |
'd':68,'e':69,'f':70,'g':71,'h':72,'i':73,'j':74,'k':75, | |
'l':76,'m':77,'n':78,'o':79,'p':80,'q':81,'r':82,'s':83, | |
't':84,'u':85,'v':86,'w':87,'x':88,'y':89,'z':90,'A':65, | |
'B':66,'C':67,'D':68,'E':69,'F':70,'G':71,'H':72,'I':73, | |
'J':74,'K':75,'L':76,'M':77,'N':78,'O':79,'P':80,'Q':81, | |
'R':82,'S':83,'T':84,'U':85,'V':86,'W':87,'X':88,'Y':89,'Z':90 } | |
} | |
}; | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment