theme-base/js/vendor/what-input.js

296 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
};
}());