295 lines
6.9 KiB
JavaScript
295 lines
6.9 KiB
JavaScript
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
|
|
};
|
|
|
|
}());
|