Last active
October 13, 2015 07:57
-
-
Save arnehormann/4163916 to your computer and use it in GitHub Desktop.
javascript keycode finder
This file contains hidden or 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
<!DOCTYPE html> | |
<html><head><meta charset="utf-8"><!-- | |
Ok, let me be honest here… | |
DISCLAIMER: | |
I drew inspiration and a little CSS from the now defunct | |
http://whatthekeycode.com/ | |
Still, it's different in a lot of ways. | |
This is the perfect single page app by the way :-) | |
It really won't tax your network connection once it's loaded, | |
this file is all there is! | |
It is fully commented and – even better – narrated. | |
It uses all of HTML, CSS and Javascript comments. | |
So if you ever forget the syntax - here's your refresher! | |
--><title>Javascript Key Expander</title> | |
<style type="text/css"> | |
/* | |
Thou shalt please the eye! | |
*/ | |
body { | |
font-family: "Museo Sans", "Droid Sans", "Helvetica Neue", sans-serif; | |
background: #eee | |
} | |
#help { | |
position: fixed; | |
font-size: 40px; | |
line-height: 1.5em; | |
text-align: center; | |
width: 100%; | |
left: 0; | |
line-height: 1.5em; | |
top: 50%; | |
margin-top: -100px; | |
color: #888; | |
} | |
#keys { | |
line-height: 2em; | |
} | |
a { | |
text-decoration: none; | |
color: #888; | |
} | |
/* | |
Some semantics abuse on my part: I misappropriate "b" tags for keys. | |
At least in #help and #keys | |
*/ | |
#help b { | |
font-size: 30px; | |
} | |
#keys b, #help b { | |
display: inline-block; | |
font-family: DejaVu Sans Mono,Bitstream Vera Sans Mono,monospace; | |
line-height: 1em; | |
padding: 0.3em 0.5em; | |
margin-bottom: 0.5em; | |
color: #666; | |
border: 1px solid #666; | |
border-radius: 0.3em; | |
optimize: legibility; | |
/* | |
Do yourself a favor and never write stuff like this by hand. | |
Get aquainted with LESS or SASS or use one of the numerous sites out there. | |
*/ | |
background: #f7f7f7; | |
background: -moz-linear-gradient(top, #f7f7f7 0%, #e8e8e8 100%); | |
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f7f7f7), color-stop(100%,#e8e8e8)); | |
background: -webkit-linear-gradient(top, #f7f7f7 0%,#e8e8e8 100%); | |
background: -o-linear-gradient(top, #f7f7f7 0%,#e8e8e8 100%); | |
background: -ms-linear-gradient(top, #f7f7f7 0%,#e8e8e8 100%); | |
background: linear-gradient(to bottom, #f7f7f7 0%,#e8e8e8 100%); | |
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f7f7f7', endColorstr='#e8e8e8',GradientType=0 ); | |
-moz-box-shadow: 1px 1px 3px 0px #ccc; | |
-webkit-box-shadow: 1px 1px 3px 0px #ccc; | |
box-shadow: 1px 1px 3px 0px #ccc; | |
} | |
/* | |
When hovered or in container with the class "display-keycode", | |
we show the keycode (which sits snuggly in the title attribute) | |
*/ | |
#keys.display-keycode b:after, #keys b:hover:after { | |
content: attr(title); | |
display: inline-box; | |
margin-left: 0.5em; | |
padding: 2px; | |
font-size: 0.5em; | |
font-family: sans-serif; | |
font-weight: normal; | |
color: #eee; | |
background-color: #888; | |
border-radius: 2px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="help"> | |
Enter a key or a key combination.<br>Hover to see the keycode…<br> | |
or try <b class="key">Ctrl</b> + <b class="key">K</b><!-- | |
Toggles the display by adding a class display-keycode to "#keys". | |
See ctrlKHandler for how that's done! | |
--><br> | |
the source is <span title="Honestly! Just about 400 lines give or take. Well groomed.">short and simple</span> – | |
have a <a href="view-source:" title="Firefox does this. Chrome doesn't. I didn't test the others">look</a>!</div> | |
<div id="keys"></div><script> | |
(function(){ | |
// cache the only DOM node we need | |
var keys = document.getElementById('keys') | |
, keyMap = | |
/* | |
Here, we have 3 objects mapping from keycodes to strings. | |
We'll need them soon. | |
The first one is generic and may be overridden by the later ones. | |
Stay with me for a while, it will all be clear… Umm… | |
Scroll ↓, please | |
*/ | |
{ 8: 'Backspace' | |
, 9: 'Tab' | |
, 13: 'Enter' | |
, 16: 'Shift' | |
, 17: 'Ctrl' | |
, 18: 'Alt' | |
, 19: 'Break' | |
, 20: 'Caps Lock' | |
, 27: 'Esc' | |
, 32: 'Space' | |
, 33: 'Page ↑' | |
, 34: 'Page ↓' | |
, 35: 'End' | |
, 36: 'Home' | |
, 37: '←' | |
, 38: '↑' | |
, 39: '→' | |
, 40: '↓' | |
, 45: 'Insert' | |
, 46: 'Delete' | |
, 96: 'Num 0' | |
, 97: 'Num 1' | |
, 98: 'Num 2' | |
, 99: 'Num 3' | |
, 100: 'Num 4' | |
, 101: 'Num 5' | |
, 102: 'Num 6' | |
, 103: 'Num 7' | |
, 104: 'Num 8' | |
, 105: 'Num 9' | |
, 106: 'Num *' | |
, 107: 'Num +' | |
, 109: 'Num -' | |
, 110: 'Num .' | |
, 111: 'Num /' | |
, 112: 'F1' | |
, 113: 'F2' | |
, 114: 'F3' | |
, 115: 'F4' | |
, 116: 'F5' | |
, 117: 'F6' | |
, 118: 'F7' | |
, 119: 'F8' | |
, 120: 'F9' | |
, 121: 'F10' | |
, 122: 'F11' | |
, 123: 'F12' | |
, 144: 'Num Lock' | |
, 145: 'Scroll Lock' | |
} | |
/* | |
I'm from Germany and using Windows right now. | |
Here's some mappings specific to Windows. | |
↓ go on ↓ | |
*/ | |
, keyMapWindows = | |
{ 91: 'Windows Left' | |
, 92: 'Windows Right' | |
, 93: 'Context' | |
// special keys on multimedia keyboard - still missing Email and whatnot… | |
, 172: 'WWW Home' | |
, 173: 'Mute' | |
, 174: 'Volume down' | |
, 175: 'Volume up' | |
, 179: 'Play/Pause' | |
, 181: 'Media Player' | |
, 182: 'My Computer' | |
, 183: 'Calculator' | |
} | |
/* | |
As I said, I'm from Germany. | |
Our keyboard layouts are different! | |
There's more. | |
We use "," as a decimal separator, so that's on our | |
Num blocks instead of "." (we use "." for thousands *sigh*). | |
↓ just a little more ↓ | |
*/ | |
, keyMapDE_Win = | |
{ 110: 'Num ,' // different decimal separator in DE :-( | |
, 186: 'ü' | |
, 187: '+' | |
, 188: ',' | |
, 189: '-' | |
, 190: '.' | |
, 191: '#' | |
, 192: 'ö' | |
, 219: 'ß' | |
, 220: '^' | |
, 221: '´' | |
, 222: 'ä' | |
, 226: '<' | |
} | |
/* | |
We get a key but we want its name - when JS doesn't know how to convert it. | |
That's what this function does. Pass a keycode, get a string. | |
*/ | |
, keyToString = function(key) { | |
// Now we need the keyMaps from above. | |
// Starting from the leftmost one, the key is searched in the maps. | |
// If it's not found, the result is "undefined" (which is evaluated to false) | |
// and the next one is checked. | |
var expandedKey = keyMapDE_Win[key] || keyMapWindows[key] || keyMap[key] | |
if (expandedKey) { | |
// We found a long representation and use it | |
return expandedKey | |
} | |
// Use JS to convert the keycode to it's string representation | |
return String.fromCharCode(key) | |
} | |
/* | |
When we have a keycode, we want to display a nice little key! | |
That's done this way. | |
The title attribute contains the keycode, the text its description. | |
*/ | |
, keyToHTML = function(key) { | |
return '<b title="' + key + '">' + keyToString(key) + '</b>' | |
} | |
/* | |
Now it's getting slightly more complicated. | |
This thingy is a fully encapsulated key event stream handler. | |
There's no changing it from the outside - it just eats key presses. | |
And it's fed from outside. | |
Positive numbers indicate a "key pressed" keycode. | |
Negative numbers indicate that key was released. | |
We get by with numbers only! Neat! | |
*/ | |
, streamProcessor = (function() { | |
var keyBuffer = [] // buffers all keys still pressed | |
, activeCombination = false // some keys pressed, none released? | |
// remember: for (k) → positive = pressed; negative = released | |
return function(k) { | |
if (k > 0) { // key pressed | |
if (keyBuffer.indexOf(k) === -1) { // key's not pressed yet | |
// remember the new keycode | |
keyBuffer.push(k) | |
// the "if"s down there control formatting. | |
// If it's the first key, it should stand alone. | |
// If it's a new key, we have to prefix a "+" | |
// If some keys are still pressed but some were released, | |
// we end the prior ones with a "," and continue | |
// with the keys pressed so far. | |
if (keyBuffer.length <= 1) { | |
// it's the only key | |
keys.innerHTML = keyToHTML(k) | |
} else if (activeCombination) { | |
// some preceding keys | |
keys.innerHTML += ' + ' + keyToHTML(k) | |
} else { | |
// restart combination | |
var html = ' , ' + keyToHTML(keyBuffer[0]) | |
for (var i = 1; i < keyBuffer.length; i++) { | |
html += ' + ' + keyToHTML(keyBuffer[i]) | |
} | |
keys.innerHTML += html | |
} | |
// at least some keys are pressed and none were released, | |
// this is part of a combination | |
activeCombination = true | |
} | |
} else { // key released | |
var i = keyBuffer.indexOf(-k) // do we know it? It's a key release, so -k | |
if (i >= 0) { // Yeah, we know it | |
keyBuffer.splice(i, 1) // and we forget it | |
activeCombination = false // but it's no longer the same combination | |
} | |
} | |
} | |
// TODO feature idea: | |
// Per default, show lower case and | |
// *optionally* wrap [shift] + [letter] into [LETTER]? | |
})() | |
/* | |
With me so far? | |
This thingy converts key events into the positive / negative keycodes | |
and feeds them to a function. The one above, that is. | |
*/ | |
, streamKeys = function(factor, callback, cancelling) { | |
// factor should be 1 for "key pressed" and -1 for "key released" | |
if (cancelling) { | |
// when cancelling, we event processing to stop after we got it. | |
// in this case, Ctrl-C, Ctrl-V and some others don't work anymore. | |
// But the browser won't do funny stuff on combinations. | |
// By the way, here's an exciting read: http://unixpapa.com/js/key.html | |
return function(e) { | |
e = e || window.event | |
callback(factor * (e.keyCode || e.which)) | |
return false | |
} | |
} | |
// same without "return false" | |
return function(e) { | |
e = e || window.event | |
callback(factor * (e.keyCode || e.which)) | |
} | |
} | |
/* | |
listen for and react to Ctrl + K as hinted in help | |
*/ | |
, ctrlKInterceptor = function(wrapped){ | |
var last = 0 | |
return function(k) { | |
if (k === 17) { | |
last = 17 | |
} else if (last === 17 && k === 75) { | |
// when first key was "Ctrl" and now we got "k", toggle class | |
keys.className = keys.className === 'display-keycode' | |
? '' | |
: 'display-keycode' | |
} else { | |
last = 0 | |
} | |
wrapped(k) | |
} | |
} | |
/* | |
some fairy dust for those in the know! | |
↑↑↓↓←→←→BA - the Konami code. | |
We listen for it here | |
*/ | |
, konamiInterceptor = function(wrapped) { | |
var konamiCode = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65] | |
, konamiIdx = 0 | |
return function(k) { | |
if (k >= 0) { | |
if (k === konamiCode[konamiIdx]) { | |
konamiIdx++ | |
if (konamiIdx > 1) { | |
keys.innerHTML += ' - ' + keyToHTML(k) | |
if (konamiIdx === konamiCode.length) { | |
// Oh, oh, we could change this to a fading popover! | |
// with sparkles, rainbows, unicorns, … | |
alert('Ok, you win!') | |
} | |
} | |
} else { | |
konamiIdx = 0 | |
} | |
} else { | |
if (konamiIdx === konamiCode.length) { | |
keys.innerHTML = '' | |
} | |
if (konamiIdx > 0 && -k !== konamiCode[konamiIdx - 1]) { | |
konamiIdx = 0 | |
} | |
} | |
if (konamiIdx <= 1) { | |
wrapped(k) | |
} | |
} | |
} | |
/* | |
Yo dawg, I wrap wrappers in wrappers so you can wrap while you're wrapping… | |
*/ | |
, streamHandler = (function() { | |
var callback = konamiInterceptor(ctrlKInterceptor(streamProcessor)) | |
return function(isKeyPress) { | |
return streamKeys(isKeyPress ? 1 : -1, callback, true) | |
} | |
})() | |
, onDown = streamHandler(true) | |
, onUp = streamHandler(false) | |
/* | |
Now let's just register them and that's it! | |
*/ | |
document.body.onkeydown = onDown | |
document.body.onkeyup = onUp | |
})() | |
/* | |
THE END | |
I hope you liked it! | |
*/ | |
</script></body></html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment