;(function(root, factory) { if (typeof define === 'function' && define.amd) { define([], function() { return (factory()); }); } else if (typeof exports === 'object') { module.exports = factory(); } else { root.whatInput = factory(); } } (this, function() { 'use strict'; /* --------------- variables --------------- */ // array of actively pressed keys var activeKeys = []; // cache document.body var body = document.body; // boolean: true if touch buffer timer is running var buffer = false; // the last used input type var currentInput = null; // array of form elements that take keyboard input var formInputs = [ 'input', 'select', 'textarea' ]; // user-set flag to allow typing in form fields to be recorded var formTyping = body.hasAttribute('data-whatinput-formtyping'); // mapping of events to input types var inputMap = { 'keydown': 'keyboard', 'mousedown': 'mouse', 'mouseenter': 'mouse', 'touchstart': 'touch', 'pointerdown': 'pointer', 'MSPointerDown': 'pointer' }; // array of all used input types var inputTypes = []; // mapping of key codes to 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 --------------- */ function bufferInput(event) { clearTimeout(timer); setInput(event); buffer = true; timer = setTimeout(function() { buffer = false; }, 1000); } function immediateInput(event) { if (!buffer) setInput(event); } function setInput(event) { var eventKey = key(event); var eventTarget = target(event); var value = inputMap[event.type]; if (value === 'pointer') value = pointerType(event); if (currentInput !== value) { if ( // only if the user flag isn't set !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 one of the elements in `formInputs` formInputs.indexOf(eventTarget.nodeName.toLowerCase()) >= 0 ) { // ignore keyboard typing on form elements } else { currentInput = value; body.setAttribute('data-whatinput', currentInput); if (inputTypes.indexOf(currentInput) === -1) inputTypes.push(currentInput); } } if (value === 'keyboard') logKeys(eventKey); } function key(event) { return (event.keyCode) ? event.keyCode : event.which; } function target(event) { return event.target || event.srcElement; } function pointerType(event) { return (typeof event.pointerType === 'number') ? pointerMap[event.pointerType] : event.pointerType; } // 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() { // pointer/mouse var mouseEvent = 'mousedown'; if (window.PointerEvent) { mouseEvent = 'pointerdown'; } else if (window.MSPointerEvent) { mouseEvent = 'MSPointerDown'; } body.addEventListener(mouseEvent, immediateInput); body.addEventListener('mouseenter', immediateInput); // touch if ('ontouchstart' in window) { body.addEventListener('touchstart', bufferInput); } // keyboard body.addEventListener('keydown', immediateInput); document.addEventListener('keyup', unLogKeys); } /* --------------- init don't start script unless browser cuts the mustard, also passes if polyfills are used --------------- */ if ('addEventListener' in window && Array.prototype.indexOf) { 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: setInput }; }));