diff options
Diffstat (limited to 'examplesite/themes/hugof/static/js/what-input.js')
-rw-r--r-- | examplesite/themes/hugof/static/js/what-input.js | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/examplesite/themes/hugof/static/js/what-input.js b/examplesite/themes/hugof/static/js/what-input.js new file mode 100644 index 0000000..5d23671 --- /dev/null +++ b/examplesite/themes/hugof/static/js/what-input.js @@ -0,0 +1,295 @@ +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 + }; + +}()); |