(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('prop-types'), require('exenv')) :
  typeof define === 'function' && define.amd ? define(['exports', 'react', 'prop-types', 'exenv'], factory) :
  (factory((global.ReactKeyHandler = {}),global.React,global.PropTypes,global.exenv));
}(this, (function (exports,React,PropTypes,exenv) { 'use strict';

  React = React && React.hasOwnProperty('default') ? React['default'] : React;
  PropTypes = PropTypes && PropTypes.hasOwnProperty('default') ? PropTypes['default'] : PropTypes;

  /**
   * Key event names.
   */

  var KEYDOWN = 'keydown';
  var KEYPRESS = 'keypress';
  var KEYUP = 'keyup';

  /**
   * Types.
   */

  /**
   * Constants.
   */

  var NORMALIZED_KEYS = {
    'Esc': 'Escape',
    'Spacebar': ' ',
    'Left': 'ArrowLeft',
    'Up': 'ArrowUp',
    'Right': 'ArrowRight',
    'Down': 'ArrowDown',
    'Del': 'Delete',
    'Win': 'OS',
    'Menu': 'ContextMenu',
    'Apps': 'ContextMenu',
    'Scroll': 'ScrollLock',
    'MozPrintableKey': 'Unidentified'
  };

  var KEY_CODE_KEYS = {
    '8': 'Backspace',
    '9': 'Tab',
    '12': 'Clear',
    '13': 'Enter',
    '16': 'Shift',
    '17': 'Control',
    '18': 'Alt',
    '19': 'Pause',
    '20': 'CapsLock',
    '27': 'Escape',
    '32': ' ',
    '33': 'PageUp',
    '34': 'PageDown',
    '35': 'End',
    '36': 'Home',
    '37': 'ArrowLeft',
    '38': 'ArrowUp',
    '39': 'ArrowRight',
    '40': 'ArrowDown',
    '45': 'Insert',
    '46': 'Delete',
    '112': 'F1',
    '113': 'F2',
    '114': 'F3',
    '115': 'F4',
    '116': 'F5',
    '117': 'F6',
    '118': 'F7',
    '119': 'F8',
    '120': 'F9',
    '121': 'F10',
    '122': 'F11',
    '123': 'F12',
    '144': 'NumLock',
    '145': 'ScrollLock',
    '224': 'Meta'
  };

  /**
   * Check if `given` element is an input / textarea form element or acts as one.
   */

  function isInput(element) {
    if (!element) {
      return false;
    }

    var tagName = element.tagName;

    var editable = isContentEditable(element);

    return tagName === 'INPUT' || tagName === 'TEXTAREA' || editable;
  }

  function isContentEditable(element) {
    if (typeof element.getAttribute !== 'function') {
      return false;
    }

    return !!element.getAttribute('contenteditable');
  }

  /**
   * Matches an event against a given keyboard key.
   */

  function matchesKeyboardEvent(event, _ref) {
    var keyCode = _ref.keyCode,
        keyValue = _ref.keyValue;

    if (!isNullOrUndefined(keyValue)) {
      return keyValue === eventKey(event);
    }

    if (!isNullOrUndefined(keyCode)) {
      // Firefox handles keyCode through which 
      var code = event.keyCode || event.which;
      return keyCode === code;
    }

    return false;
  }

  function isNullOrUndefined(value) {
    return value === undefined || value === null;
  }

  function eventKey(event) {
    var key = event.key,
        keyCode = event.keyCode,
        type = event.type;


    if (key) {
      var normalizedKey = NORMALIZED_KEYS[key] || key;

      if (normalizedKey !== 'Unidentified') {
        return normalizedKey;
      }
    }

    if (type === KEYPRESS) {
      var charCode = eventCharCode(event);

      return charCode === 13 ? 'Enter' : String.fromCharCode(charCode);
    }

    if (type === KEYDOWN || type === KEYUP) {
      return KEY_CODE_KEYS[String(keyCode)] || 'Unidentified';
    }

    return '';
  }

  function eventCharCode(event) {
    var charCode = event.charCode,
        keyCode = event.keyCode;


    if ('charCode' in event) {
      if (charCode === 0 && keyCode === 13) {
        return 13;
      }
    } else {
      charCode = keyCode;
    }

    if (charCode >= 32 || charCode === 13) {
      return charCode;
    }

    return 0;
  }

  var classCallCheck = function (instance, Constructor) {
    if (!(instance instanceof Constructor)) {
      throw new TypeError("Cannot call a class as a function");
    }
  };

  var createClass = function () {
    function defineProperties(target, props) {
      for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ("value" in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor);
      }
    }

    return function (Constructor, protoProps, staticProps) {
      if (protoProps) defineProperties(Constructor.prototype, protoProps);
      if (staticProps) defineProperties(Constructor, staticProps);
      return Constructor;
    };
  }();

  var _extends = Object.assign || function (target) {
    for (var i = 1; i < arguments.length; i++) {
      var source = arguments[i];

      for (var key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) {
          target[key] = source[key];
        }
      }
    }

    return target;
  };

  var inherits = function (subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
      throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
    }

    subClass.prototype = Object.create(superClass && superClass.prototype, {
      constructor: {
        value: subClass,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  };

  var objectWithoutProperties = function (obj, keys) {
    var target = {};

    for (var i in obj) {
      if (keys.indexOf(i) >= 0) continue;
      if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
      target[i] = obj[i];
    }

    return target;
  };

  var possibleConstructorReturn = function (self, call) {
    if (!self) {
      throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }

    return call && (typeof call === "object" || typeof call === "function") ? call : self;
  };

  var KeyHandler = function (_React$Component) {
    inherits(KeyHandler, _React$Component);
    createClass(KeyHandler, [{
      key: 'shouldComponentUpdate',
      value: function shouldComponentUpdate() {
        return false;
      }
    }]);

    function KeyHandler(props) {
      classCallCheck(this, KeyHandler);

      /* eslint-disable no-console */

      var _this = possibleConstructorReturn(this, (KeyHandler.__proto__ || Object.getPrototypeOf(KeyHandler)).call(this, props));

      _this.handleKey = function (event) {
        var _this$props = _this.props,
            keyValue = _this$props.keyValue,
            keyCode = _this$props.keyCode,
            onKeyHandle = _this$props.onKeyHandle;


        if (!onKeyHandle) {
          return;
        }

        var target = event.target;


        if (target instanceof window.HTMLElement && isInput(target)) {
          return;
        }

        if (!matchesKeyboardEvent(event, { keyValue: keyValue, keyCode: keyCode })) {
          return;
        }

        onKeyHandle(event);
      };

      if (!props.keyValue && !props.keyCode) {
        console.error('Warning: Failed propType: Missing prop `keyValue` or `keyCode` for `KeyHandler`.');
      }

      /* eslint-enable */
      return _this;
    }

    createClass(KeyHandler, [{
      key: 'componentDidMount',
      value: function componentDidMount() {
        if (!exenv.canUseDOM) return;

        window.document.addEventListener(this.props.keyEventName, this.handleKey);
      }
    }, {
      key: 'componentWillUnmount',
      value: function componentWillUnmount() {
        if (!exenv.canUseDOM) return;

        window.document.removeEventListener(this.props.keyEventName, this.handleKey);
      }
    }, {
      key: 'render',
      value: function render() {
        return null;
      }
    }]);
    return KeyHandler;
  }(React.Component);

  /**
   * Types.
   */

  KeyHandler.propTypes = {
    keyValue: PropTypes.string,
    keyCode: PropTypes.number,
    keyEventName: PropTypes.oneOf([KEYDOWN, KEYPRESS, KEYUP]),
    onKeyHandle: PropTypes.func
  };
  KeyHandler.defaultProps = {
    keyEventName: KEYUP
  };


  function keyHandleDecorator(matcher) {
    return function (props) {
      var _ref = props || {},
          keyValue = _ref.keyValue,
          keyCode = _ref.keyCode,
          keyEventName = _ref.keyEventName,
          otherProps = objectWithoutProperties(_ref, ['keyValue', 'keyCode', 'keyEventName']);

      return function (Component) {
        return function (_React$Component2) {
          inherits(KeyHandleDecorator, _React$Component2);

          function KeyHandleDecorator() {
            var _ref2;

            var _temp, _this2, _ret;

            classCallCheck(this, KeyHandleDecorator);

            for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
              args[_key] = arguments[_key];
            }

            return _ret = (_temp = (_this2 = possibleConstructorReturn(this, (_ref2 = KeyHandleDecorator.__proto__ || Object.getPrototypeOf(KeyHandleDecorator)).call.apply(_ref2, [this].concat(args))), _this2), _this2.state = {
              keyCode: null,
              keyValue: null
            }, _this2.handleKey = function (event) {
              if (matcher && matcher(event, _this2.state)) {
                _this2.setState({ keyValue: null, keyCode: null });
                return;
              }

              _this2.setState({ keyValue: eventKey(event), keyCode: event.keyCode });
            }, _temp), possibleConstructorReturn(_this2, _ret);
          }

          createClass(KeyHandleDecorator, [{
            key: 'render',
            value: function render() {
              return React.createElement(
                'div',
                null,
                React.createElement(KeyHandler, {
                  keyValue: keyValue,
                  keyCode: keyCode,
                  keyEventName: keyEventName,
                  onKeyHandle: this.handleKey
                }),
                React.createElement(Component, _extends({}, this.props, this.state))
              );
            }
          }]);
          return KeyHandleDecorator;
        }(React.Component);
      };
    };
  }

  var keyHandler = keyHandleDecorator();
  var keyToggleHandler = keyHandleDecorator(matchesKeyboardEvent);

  exports.default = KeyHandler;
  exports.KEYDOWN = KEYDOWN;
  exports.KEYPRESS = KEYPRESS;
  exports.KEYUP = KEYUP;
  exports.keyHandler = keyHandler;
  exports.keyToggleHandler = keyToggleHandler;

  Object.defineProperty(exports, '__esModule', { value: true });

})));
