'use strict';

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

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _lodash = require('lodash');

var _lodash2 = _interopRequireDefault(_lodash);

var _reactAddons = require('react/addons');

var _reactAddons2 = _interopRequireDefault(_reactAddons);

var _reactLibGetEventKey = require('react/lib/getEventKey');

var _reactLibGetEventKey2 = _interopRequireDefault(_reactLibGetEventKey);

var _mousetrap = require('mousetrap');

var _mousetrap2 = _interopRequireDefault(_mousetrap);

var _fluxEditorActions = require('../flux/EditorActions');

var _fluxEditorActions2 = _interopRequireDefault(_fluxEditorActions);

var _coreHtmlparser = require('../core/htmlparser');

var _coreHtmlparser2 = _interopRequireDefault(_coreHtmlparser);

var _coreDom = require('../core/dom');

var T = _reactAddons2['default'].PropTypes;
var MIME_TYPE_TEXT_PLAIN = 'text/plain';
var MIME_TYPE_TEXT_HTML = 'text/html';
var MIME_TYPE_RITZY_RICH_TEXT = 'application/x-ritzy-rt';

// alphabet lower case +
// alphabet upper case case +
// numerals +
// space +
// special chars (_KEYCODE_MAP in mousetrap) +
// keys requiring shift (_SHIFT_MAP in mousetrap) +
// { and } which were missing from mousetrap _SHIFT_MAP
// common special chars (TODO)
// TODO non-english language/char entry
/* eslint-disable comma-spacing */
var ALL_CHARS = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'space', '*', '+', '-', '.', '/', ';', '=', ',', '-', '.', '/', '`', '[', '\\', ']', '\'', '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', ':', '\"', '<', '>', '?', '|', '{', '}', 'é', 'è'];
/* eslint-enable comma-spacing */

exports['default'] = _reactAddons2['default'].createClass({
  displayName: 'TextInput',

  propTypes: {
    id: T.string.isRequired,
    position: T.number.isRequired,
    focused: T.bool.isRequired
  },

  componentDidMount: function componentDidMount() {
    var _this = this;

    this.input = _reactAddons2['default'].findDOMNode(this.refs.input);
    this.hiddenContainer = _reactAddons2['default'].findDOMNode(this.refs.hiddenContainer);
    this.ieClipboardDiv = _reactAddons2['default'].findDOMNode(this.refs.ieClipboardDiv);

    // for HTML paste support in IE -- ignored by Chrome/Firefox
    this.input.addEventListener('beforepaste', function () {
      if (document.activeElement !== _this.ieClipboardDiv) {
        _this._focusIeClipboardDiv();
      }
    }, true);

    var keyBindings = new _mousetrap2['default'](this.input);

    // TODO probably ALL_CHARS should be handled via onInput instead to avoid keycode translation
    keyBindings.bind(ALL_CHARS, this._handleKeyChar);
    keyBindings.bind(['up', 'down', 'left', 'right'], this._handleKeyArrow);
    keyBindings.bind(['shift+up', 'shift+down', 'shift+left', 'shift+right'], this._handleKeySelectionArrow);
    keyBindings.bind(['pageup', 'pagedown'], this._handleKeyNavigationPage);
    keyBindings.bind(['shift+pageup', 'shift+pagedown'], this._handleKeySelectionPage);
    keyBindings.bind(['ctrl+home', 'home', 'ctrl+end', 'end'], this._handleKeyNavigationHomeEnd);
    keyBindings.bind(['ctrl+shift+home', 'shift+home', 'ctrl+shift+end', 'shift+end'], this._handleKeySelectionHomeEnd);
    keyBindings.bind(['ctrl+left', 'ctrl+right'], this._handleKeyNavigationWord);
    keyBindings.bind(['shift+ctrl+left', 'shift+ctrl+right'], this._handleKeySelectionWord);
    keyBindings.bind('ctrl+a', this._handleKeySelectionAll);
    keyBindings.bind('backspace', this._handleKeyBackspace);
    keyBindings.bind('del', this._handleKeyDelete);
    keyBindings.bind('ctrl+backspace', this._handleKeyWordBackspace);
    keyBindings.bind('ctrl+del', this._handleKeyWordDelete);
    //keyBindings.bind('ctrl+s', this._handleKeySave)
    //keyBindings.bind('tab', this._handleKeyTab)
    keyBindings.bind('enter', this._handleKeyEnter);
    //keyBindings.bind('ctrl+z', this._handleUndo)
    //keyBindings.bind('ctrl+y', this._handleRedo)

    keyBindings.bind('ctrl+b', this._handleKeyBold);
    keyBindings.bind('ctrl+i', this._handleKeyItalics);
    keyBindings.bind('ctrl+u', this._handleKeyUnderline);
    keyBindings.bind('alt+shift+5', this._handleKeyStrikethrough);
    keyBindings.bind('ctrl+.', this._handleKeySuperscript);
    keyBindings.bind('ctrl+,', this._handleKeySubscript);

    if (this.props.focused) {
      this._focus();
    }
  },

  shouldComponentUpdate: function shouldComponentUpdate(nextProps) {
    return this.props.focused !== nextProps.focused || this.props.position !== nextProps.position;
  },

  componentDidUpdate: function componentDidUpdate() {
    if (this.props.focused) {
      this._focus();
    }
  },

  _focus: function _focus() {
    this._checkEmptyValue();

    this.ieClipboardDivFocus = false;
    this.focusHappening = true;
    try {
      // IE requires a non-empty selection in order to fire the copy event, annoying
      this.input.value = ' ';
      this.input.focus();
      this.input.select();
    } finally {
      this.focusHappening = false;
    }
  },

  _checkEmptyValue: function _checkEmptyValue() {
    var value = this.input.value;
    if (value.length > 0 && value !== ' ') {
      console.error('The hidden input area is not empty, missed an event? Value=', value);
    }
  },

  _handleKeyChar: function _handleKeyChar(e) {
    this._checkEmptyValue();

    // use React's DOM-L3 events polyfill to convert the native event into a key
    var key = (0, _reactLibGetEventKey2['default'])(e);
    if (key === 'Enter') {
      key = '\n';
    }
    _fluxEditorActions2['default'].insertChars(key);
    return false;
  },

  _handleKeyArrow: function _handleKeyArrow(e, key) {
    this._checkEmptyValue();

    if (key === 'left') {
      _fluxEditorActions2['default'].navigateLeft();
    } else if (key === 'right') {
      _fluxEditorActions2['default'].navigateRight();
    } else if (key === 'up') {
      _fluxEditorActions2['default'].navigateUp();
    } else if (key === 'down') {
      _fluxEditorActions2['default'].navigateDown();
    }
    return false;
  },

  _handleKeySelectionArrow: function _handleKeySelectionArrow(e, key) {
    this._checkEmptyValue();

    if (key === 'shift+left') {
      _fluxEditorActions2['default'].selectionLeft();
    } else if (key === 'shift+right') {
      _fluxEditorActions2['default'].selectionRight();
    } else if (key === 'shift+up') {
      _fluxEditorActions2['default'].selectionUp();
    } else if (key === 'shift+down') {
      _fluxEditorActions2['default'].selectionDown();
    }
    return false;
  },

  _handleKeyBackspace: function _handleKeyBackspace() {
    this._checkEmptyValue();

    _fluxEditorActions2['default'].eraseCharBack();
    return false;
  },

  _handleKeyDelete: function _handleKeyDelete() {
    this._checkEmptyValue();

    _fluxEditorActions2['default'].eraseCharForward();
    return false;
  },

  _handleKeyWordBackspace: function _handleKeyWordBackspace() {
    this._checkEmptyValue();

    _fluxEditorActions2['default'].eraseWordBack();
    return false;
  },

  _handleKeyWordDelete: function _handleKeyWordDelete() {
    this._checkEmptyValue();

    _fluxEditorActions2['default'].eraseWordForward();
    return false;
  },

  _handleKeyEnter: function _handleKeyEnter() {
    this._checkEmptyValue();

    _fluxEditorActions2['default'].insertChars('\n');
    return false;
  },

  _handleKeyNavigationPage: function _handleKeyNavigationPage(e, key) {
    if (key === 'pageup') {
      _fluxEditorActions2['default'].navigatePageUp();
    } else if (key === 'pagedown') {
      _fluxEditorActions2['default'].navigatePageDown();
    }
    return false;
  },

  _handleKeySelectionPage: function _handleKeySelectionPage(e, key) {
    if (key === 'shift+pageup') {
      _fluxEditorActions2['default'].selectionPageUp();
    } else if (key === 'shift+pagedown') {
      _fluxEditorActions2['default'].selectionPageDown();
    }
    return false;
  },

  _handleKeyNavigationHomeEnd: function _handleKeyNavigationHomeEnd(e, key) {
    if (key === 'ctrl+home') {
      _fluxEditorActions2['default'].navigateStart();
    } else if (key === 'home') {
      _fluxEditorActions2['default'].navigateStartLine();
    } else if (key === 'ctrl+end') {
      _fluxEditorActions2['default'].navigateEnd();
    } else if (key === 'end') {
      _fluxEditorActions2['default'].navigateEndLine();
    }
    return false;
  },

  _handleKeyNavigationWord: function _handleKeyNavigationWord(e, key) {
    this._checkEmptyValue();

    if (key === 'ctrl+left') {
      _fluxEditorActions2['default'].navigateWordLeft();
    } else if (key === 'ctrl+right') {
      _fluxEditorActions2['default'].navigateWordRight();
    }
    return false;
  },

  _handleKeySelectionHomeEnd: function _handleKeySelectionHomeEnd(e, key) {
    if (key === 'ctrl+shift+home') {
      _fluxEditorActions2['default'].selectionStart();
    } else if (key === 'shift+home') {
      _fluxEditorActions2['default'].selectionStartLine();
    } else if (key === 'ctrl+shift+end') {
      _fluxEditorActions2['default'].selectionEnd();
    } else if (key === 'shift+end') {
      _fluxEditorActions2['default'].selectionEndLine();
    }

    return false;
  },

  _handleKeySelectionWord: function _handleKeySelectionWord(e, key) {
    if (key === 'shift+ctrl+left') {
      _fluxEditorActions2['default'].selectionWordLeft();
    } else if (key === 'shift+ctrl+right') {
      _fluxEditorActions2['default'].selectionWordRight();
    }
    return false;
  },

  _handleKeySelectionAll: function _handleKeySelectionAll() {
    _fluxEditorActions2['default'].selectionAll();
    return false;
  },

  _handleKeyBold: function _handleKeyBold() {
    _fluxEditorActions2['default'].toggleBold();
    return false;
  },

  _handleKeyItalics: function _handleKeyItalics() {
    _fluxEditorActions2['default'].toggleItalics();
    return false;
  },

  _handleKeyUnderline: function _handleKeyUnderline() {
    _fluxEditorActions2['default'].toggleUnderline();
    return false;
  },

  _handleKeyStrikethrough: function _handleKeyStrikethrough() {
    _fluxEditorActions2['default'].toggleStrikethrough();
    return false;
  },

  _handleKeySuperscript: function _handleKeySuperscript() {
    _fluxEditorActions2['default'].toggleSuperscript();
    return false;
  },

  _handleKeySubscript: function _handleKeySubscript() {
    _fluxEditorActions2['default'].toggleSubscript();
    return false;
  },

  _onInputFocusLost: function _onInputFocusLost() {
    if (!this.ieClipboardDivFocus) {
      _fluxEditorActions2['default'].inputFocusLost();
    }
  },

  _onInputFocusGained: function _onInputFocusGained() {
    if (!this.focusHappening && !this.ieClipboardDivFocus && this.input === document.activeElement) {
      // regaining focus, perhaps the user switched windows and the browser is now focused again
      _fluxEditorActions2['default'].focusInput();
    }
  },

  _onCopy: function _onCopy(e) {
    var copyHandler = window.clipboardData ? _lodash2['default'].partial(this._handleIeCopy) : _lodash2['default'].partial(this._handleNormalCopy, e);

    _fluxEditorActions2['default'].copySelection(copyHandler);
  },

  _onCut: function _onCut(e) {
    this._onCopy(e);
    _fluxEditorActions2['default'].eraseSelection();
  },

  _handleNormalCopy: function _handleNormalCopy(e, copiedText, copiedHtml, copiedRich) {
    e.clipboardData.setData(MIME_TYPE_TEXT_PLAIN, copiedText);
    e.clipboardData.setData(MIME_TYPE_TEXT_HTML, copiedHtml);
    // some browsers e.g. Chrome support arbitrary MIME types, makes paste way more efficient
    // Firefox allows setting the type, but not pasting it -- see https://bugzilla.mozilla.org/show_bug.cgi?id=860857
    e.clipboardData.setData(MIME_TYPE_RITZY_RICH_TEXT, JSON.stringify(copiedRich));
    e.preventDefault();
    e.stopPropagation();
  },

  _handleIeCopy: function _handleIeCopy(copiedText, copiedHtml) {
    var _this2 = this;

    window.clipboardData.setData('Text', copiedText);
    this.ieClipboardDiv.innerHTML = copiedHtml;
    this._focusIeClipboardDiv();
    this._selectIeClipboardDivContents();
    setTimeout(function () {
      (0, _coreDom.emptyNode)(_this2.ieClipboardDiv);
      _this2._focus();
    }, 0);
  },

  _onPaste: function _onPaste(e) {
    this._checkEmptyValue();
    if (e.clipboardData.types) {
      this._handleNormalPaste(e);
    } else {
      // IE does not provide access to HTML in the paste event but does allow HTML to paste into a contentEditable
      // see http://stackoverflow.com/a/27279218/430128
      this._handleIePaste();
    }
  },

  _handleNormalPaste: function _handleNormalPaste(e) {
    var clipboardDataTypes = undefined;
    if (e.clipboardData.types.findIndex) {
      clipboardDataTypes = e.clipboardData.types;
    } else {
      // Firefox uses DOMStringList instead of an array type, convert it
      clipboardDataTypes = Array.from(e.clipboardData.types);
    }

    if (clipboardDataTypes.findIndex(function (t) {
      return t === MIME_TYPE_RITZY_RICH_TEXT;
    }) > -1) {
      var pasted = e.clipboardData.getData(MIME_TYPE_RITZY_RICH_TEXT);
      _fluxEditorActions2['default'].insertCharsBatch(JSON.parse(pasted));
    } else if (clipboardDataTypes.findIndex(function (t) {
      return t === MIME_TYPE_TEXT_HTML;
    }) > -1) {
      var pasted = e.clipboardData.getData(MIME_TYPE_TEXT_HTML);
      var pastedChunks = (0, _coreHtmlparser2['default'])(pasted, this.hiddenContainer);
      _fluxEditorActions2['default'].insertCharsBatch(pastedChunks);
    } else if (clipboardDataTypes.findIndex(function (t) {
      return t === MIME_TYPE_TEXT_PLAIN;
    }) > -1) {
      var pasted = e.clipboardData.getData(MIME_TYPE_TEXT_PLAIN);
      _fluxEditorActions2['default'].insertChars(pasted);
    } else {
      console.warn('Paste not supported yet for types: ' + JSON.stringify(clipboardDataTypes));
    }
    e.preventDefault();
    e.stopPropagation();
  },

  _handleIePaste: function _handleIePaste() {
    var _this3 = this;

    setTimeout(function () {
      var pasted = _this3.ieClipboardDiv.innerHTML;
      try {
        var pastedChunks = (0, _coreHtmlparser2['default'])(pasted, _this3.hiddenContainer);
        _fluxEditorActions2['default'].insertCharsBatch(pastedChunks);
      } finally {
        (0, _coreDom.emptyNode)(_this3.ieClipboardDiv);
        _this3._focus();
      }
    }, 0);
  },

  _focusIeClipboardDiv: function _focusIeClipboardDiv() {
    this.ieClipboardDivFocus = true;
    this.ieClipboardDiv.focus();
  },

  _selectIeClipboardDivContents: function _selectIeClipboardDivContents() {
    var range = document.createRange();
    range.selectNodeContents(this.ieClipboardDiv);
    var selection = window.getSelection();
    selection.removeAllRanges();
    selection.addRange(range);
  },

  _onInput: function _onInput(e) {
    // catch inputs that our keyboard handler doesn't catch e.g. compose key, IME inputs, etc.
    var value = e.target.value;
    _fluxEditorActions2['default'].insertChars(value);
    this._focus();
  },

  render: function render() {
    var divStyle = {
      position: 'absolute',
      overflow: 'hidden',
      height: 0,
      outline: 'none',
      left: 0,
      top: this.props.position
    };

    // we can't focus an element with display: none so wrap them in another invisible div
    return _reactAddons2['default'].createElement(
      'div',
      { style: divStyle },
      _reactAddons2['default'].createElement('textarea', { key: 'input', ref: 'input', onInput: this._onInput,
        onCopy: this._onCopy, onCut: this._onCut, onPaste: this._onPaste, onBlur: this._onInputFocusLost, onFocus: this._onInputFocusGained }),
      _reactAddons2['default'].createElement('div', { style: { display: 'none' }, ref: 'hiddenContainer' }),
      _reactAddons2['default'].createElement('div', { contentEditable: 'true', ref: 'ieClipboardDiv', onPaste: this._onPaste })
    );
  }

});
module.exports = exports['default'];