| window.whatInput = (function() { |
| |
| 'use strict'; |
| |
| /* |
| --------------- |
| variables |
| --------------- |
| */ |
| |
| // array of actively pressed keys |
| var activeKeys = []; |
| |
| // cache document.body |
| var body; |
| |
| // boolean: true if touch buffer timer is running |
| var buffer = false; |
| |
| // the last used input type |
| var currentInput = null; |
| |
| // `input` types that don't accept text |
| var nonTypingInputs = [ |
| 'button', |
| 'checkbox', |
| 'file', |
| 'image', |
| 'radio', |
| 'reset', |
| 'submit' |
| ]; |
| |
| // detect version of mouse wheel event to use |
| // via https://developer.mozilla.org/en-US/docs/Web/Events/wheel |
| var mouseWheel = detectWheel(); |
| |
| // list of modifier keys commonly used with the mouse and |
| // can be safely ignored to prevent false keyboard detection |
| var ignoreMap = [ |
| 16, // shift |
| 17, // control |
| 18, // alt |
| 91, // Windows key / left Apple cmd |
| 93 // Windows menu / right Apple cmd |
| ]; |
| |
| // mapping of events to input types |
| var inputMap = { |
| 'keydown': 'keyboard', |
| 'keyup': 'keyboard', |
| 'mousedown': 'mouse', |
| 'mousemove': 'mouse', |
| 'MSPointerDown': 'pointer', |
| 'MSPointerMove': 'pointer', |
| 'pointerdown': 'pointer', |
| 'pointermove': 'pointer', |
| 'touchstart': 'touch' |
| }; |
| |
| // add correct mouse wheel event mapping to `inputMap` |
| inputMap[detectWheel()] = 'mouse'; |
| |
| // array of all used input types |
| var inputTypes = []; |
| |
| // mapping of key codes to a common name |
| var keyMap = { |
| 9: 'tab', |
| 13: 'enter', |
| 16: 'shift', |
| 27: 'esc', |
| 32: 'space', |
| 37: 'left', |
| 38: 'up', |
| 39: 'right', |
| 40: 'down' |
| }; |
| |
| // map of IE 10 pointer events |
| var pointerMap = { |
| 2: 'touch', |
| 3: 'touch', // treat pen like touch |
| 4: 'mouse' |
| }; |
| |
| // touch buffer timer |
| var timer; |
| |
| |
| /* |
| --------------- |
| functions |
| --------------- |
| */ |
| |
| // allows events that are also triggered to be filtered out for `touchstart` |
| function eventBuffer() { |
| clearTimer(); |
| setInput(event); |
| |
| buffer = true; |
| timer = window.setTimeout(function() { |
| buffer = false; |
| }, 650); |
| } |
| |
| function bufferedEvent(event) { |
| if (!buffer) setInput(event); |
| } |
| |
| function unBufferedEvent(event) { |
| clearTimer(); |
| setInput(event); |
| } |
| |
| function clearTimer() { |
| window.clearTimeout(timer); |
| } |
| |
| function setInput(event) { |
| var eventKey = key(event); |
| var value = inputMap[event.type]; |
| if (value === 'pointer') value = pointerType(event); |
| |
| // don't do anything if the value matches the input type already set |
| if (currentInput !== value) { |
| var eventTarget = target(event); |
| var eventTargetNode = eventTarget.nodeName.toLowerCase(); |
| var eventTargetType = (eventTargetNode === 'input') ? eventTarget.getAttribute('type') : null; |
| |
| if ( |
| (// only if the user flag to allow typing in form fields isn't set |
| !body.hasAttribute('data-whatinput-formtyping') && |
| |
| // only if currentInput has a value |
| currentInput && |
| |
| // only if the input is `keyboard` |
| value === 'keyboard' && |
| |
| // not if the key is `TAB` |
| keyMap[eventKey] !== 'tab' && |
| |
| // only if the target is a form input that accepts text |
| ( |
| eventTargetNode === 'textarea' || |
| eventTargetNode === 'select' || |
| (eventTargetNode === 'input' && nonTypingInputs.indexOf(eventTargetType) < 0) |
| )) || ( |
| // ignore modifier keys |
| ignoreMap.indexOf(eventKey) > -1 |
| ) |
| ) { |
| // ignore keyboard typing |
| } else { |
| switchInput(value); |
| } |
| } |
| |
| if (value === 'keyboard') logKeys(eventKey); |
| } |
| |
| function switchInput(string) { |
| currentInput = string; |
| body.setAttribute('data-whatinput', currentInput); |
| |
| if (inputTypes.indexOf(currentInput) === -1) inputTypes.push(currentInput); |
| } |
| |
| function key(event) { |
| return (event.keyCode) ? event.keyCode : event.which; |
| } |
| |
| function target(event) { |
| return event.target || event.srcElement; |
| } |
| |
| function pointerType(event) { |
| if (typeof event.pointerType === 'number') { |
| return pointerMap[event.pointerType]; |
| } else { |
| return (event.pointerType === 'pen') ? 'touch' : event.pointerType; // treat pen like touch |
| } |
| } |
| |
| // keyboard logging |
| function logKeys(eventKey) { |
| if (activeKeys.indexOf(keyMap[eventKey]) === -1 && keyMap[eventKey]) activeKeys.push(keyMap[eventKey]); |
| } |
| |
| function unLogKeys(event) { |
| var eventKey = key(event); |
| var arrayPos = activeKeys.indexOf(keyMap[eventKey]); |
| |
| if (arrayPos !== -1) activeKeys.splice(arrayPos, 1); |
| } |
| |
| function bindEvents() { |
| body = document.body; |
| |
| // pointer events (mouse, pen, touch) |
| if (window.PointerEvent) { |
| body.addEventListener('pointerdown', bufferedEvent); |
| body.addEventListener('pointermove', bufferedEvent); |
| } else if (window.MSPointerEvent) { |
| body.addEventListener('MSPointerDown', bufferedEvent); |
| body.addEventListener('MSPointerMove', bufferedEvent); |
| } else { |
| |
| // mouse events |
| body.addEventListener('mousedown', bufferedEvent); |
| body.addEventListener('mousemove', bufferedEvent); |
| |
| // touch events |
| if ('ontouchstart' in window) { |
| body.addEventListener('touchstart', eventBuffer); |
| } |
| } |
| |
| // mouse wheel |
| body.addEventListener(mouseWheel, bufferedEvent); |
| |
| // keyboard events |
| body.addEventListener('keydown', unBufferedEvent); |
| body.addEventListener('keyup', unBufferedEvent); |
| document.addEventListener('keyup', unLogKeys); |
| } |
| |
| |
| /* |
| --------------- |
| utilities |
| --------------- |
| */ |
| |
| // detect version of mouse wheel event to use |
| // via https://developer.mozilla.org/en-US/docs/Web/Events/wheel |
| function detectWheel() { |
| return mouseWheel = 'onwheel' in document.createElement('div') ? |
| 'wheel' : // Modern browsers support "wheel" |
| |
| document.onmousewheel !== undefined ? |
| 'mousewheel' : // Webkit and IE support at least "mousewheel" |
| 'DOMMouseScroll'; // let's assume that remaining browsers are older Firefox |
| } |
| |
| |
| /* |
| --------------- |
| init |
| |
| don't start script unless browser cuts the mustard, |
| also passes if polyfills are used |
| --------------- |
| */ |
| |
| if ( |
| 'addEventListener' in window && |
| Array.prototype.indexOf |
| ) { |
| |
| // if the dom is already ready already (script was placed at bottom of <body>) |
| if (document.body) { |
| bindEvents(); |
| |
| // otherwise wait for the dom to load (script was placed in the <head>) |
| } else { |
| document.addEventListener('DOMContentLoaded', bindEvents); |
| } |
| } |
| |
| |
| /* |
| --------------- |
| api |
| --------------- |
| */ |
| |
| return { |
| |
| // returns string: the current input type |
| ask: function() { return currentInput; }, |
| |
| // returns array: currently pressed keys |
| keys: function() { return activeKeys; }, |
| |
| // returns array: all the detected input types |
| types: function() { return inputTypes; }, |
| |
| // accepts string: manually set the input type |
| set: switchInput |
| }; |
| |
| }()); |