text: 'use strict';
  
  Object.defineProperty(exports, "__esModule", {
    value: true
  });
  
  var _debug = require('debug');
  
  var _debug2 = _interopRequireDefault(_debug);
  
  var _getWindow = require('get-window');
  
  var _getWindow2 = _interopRequireDefault(_getWindow);
  
  var _reactDom = require('react-dom');
  
  var _hotkeys = require('../constants/hotkeys');
  
  var _hotkeys2 = _interopRequireDefault(_hotkeys);
  
  var _environment = require('../constants/environment');
  
  var _findNode = require('../utils/find-node');
  
  var _findNode2 = _interopRequireDefault(_findNode);
  
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  
  /**
   * Debug.
   *
   * @type {Function}
   */
  
  var debug = (0, _debug2.default)('slate:before');
  
  /**
   * The core before plugin.
   *
   * @return {Object}
   */
  
  function BeforePlugin() {
    var activeElement = null;
    var compositionCount = 0;
    var isComposing = false;
    var isCopying = false;
    var isDragging = false;
  
    /**
     * On before input.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onBeforeInput(event, change, editor) {
      if (editor.props.readOnly) return true;
  
      // COMPAT: React's `onBeforeInput` synthetic event is based on the native
      // `keypress` and `textInput` events. In browsers that support the native
      // `beforeinput` event, we instead use that event to trigger text insertion,
      // since it provides more useful information about the range being affected
      // and also preserves compatibility with iOS autocorrect, which would be
      // broken if we called `preventDefault()` on React's synthetic event here.
      // Since native `onbeforeinput` mainly benefits autocorrect and spellcheck
      // for mobile, on desktop it brings IME issue, limit its scope for now.
      if ((_environment.IS_IOS || _environment.IS_ANDROID) && _environment.SUPPORTED_EVENTS.beforeinput) return true;
  
      debug('onBeforeInput', { event: event });
    }
  
    /**
     * On blur.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onBlur(event, change, editor) {
      if (isCopying) return true;
      if (editor.props.readOnly) return true;
  
      var value = change.value;
      var relatedTarget = event.relatedTarget,
          target = event.target;
  
      var window = (0, _getWindow2.default)(target);
  
      // COMPAT: If the current `activeElement` is still the previous one, this is
      // due to the window being blurred when the tab itself becomes unfocused, so
      // we want to abort early to allow to editor to stay focused when the tab
      // becomes focused again.
      if (activeElement == window.document.activeElement) return true;
  
      // COMPAT: The `relatedTarget` can be null when the new focus target is not
      // a "focusable" element (eg. a `<div>` without `tabindex` set).
      if (relatedTarget) {
        var el = (0, _reactDom.findDOMNode)(editor);
  
        // COMPAT: The event should be ignored if the focus is returning to the
        // editor from an embedded editable element (eg. an <input> element inside
        // a void node).
        if (relatedTarget == el) return true;
  
        // COMPAT: The event should be ignored if the focus is moving from the
        // editor to inside a void node's spacer element.
        if (relatedTarget.hasAttribute('data-slate-spacer')) return true;
  
        // COMPAT: The event should be ignored if the focus is moving to a non-
        // editable section of an element that isn't a void node (eg. a list item
        // of the check list example).
        var node = (0, _findNode2.default)(relatedTarget, value);
        if (el.contains(relatedTarget) && node && !node.isVoid) return true;
      }
  
      debug('onBlur', { event: event });
    }
  
    /**
     * On change.
     *
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onChange(change, editor) {
      var value = change.value;
  
      // If the value's schema isn't the editor's schema, update it. This can
      // happen on the initialization of the editor, or if the schema changes.
      // This change isn't save into history since only schema is updated.
  
      if (value.schema != editor.schema) {
        change.setValue({ schema: editor.schema }, { save: false }).normalize();
      }
  
      debug('onChange');
    }
  
    /**
     * On composition end.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onCompositionEnd(event, change, editor) {
      var n = compositionCount;
  
      // The `count` check here ensures that if another composition starts
      // before the timeout has closed out this one, we will abort unsetting the
      // `isComposing` flag, since a composition is still in affect.
      setTimeout(function () {
        if (compositionCount > n) return;
        isComposing = false;
  
        // HACK: we need to re-render the editor here so that it will update its
        // placeholder in case one is currently rendered. This should be handled
        // differently ideally, in a less invasive way?
        editor.setState({ isComposing: false });
      });
  
      debug('onCompositionEnd', { event: event });
    }
  
    /**
     * On composition start.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onCompositionStart(event, change, editor) {
      isComposing = true;
      compositionCount++;
  
      // HACK: we need to re-render the editor here so that it will update its
      // placeholder in case one is currently rendered. This should be handled
      // differently ideally, in a less invasive way?
      editor.setState({ isComposing: true });
  
      debug('onCompositionStart', { event: event });
    }
  
    /**
     * On copy.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onCopy(event, change, editor) {
      var window = (0, _getWindow2.default)(event.target);
      isCopying = true;
      window.requestAnimationFrame(function () {
        return isCopying = false;
      });
  
      debug('onCopy', { event: event });
    }
  
    /**
     * On cut.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onCut(event, change, editor) {
      if (editor.props.readOnly) return true;
  
      var window = (0, _getWindow2.default)(event.target);
      isCopying = true;
      window.requestAnimationFrame(function () {
        return isCopying = false;
      });
  
      debug('onCut', { event: event });
    }
  
    /**
     * On drag end.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onDragEnd(event, change, editor) {
      isDragging = false;
  
      debug('onDragEnd', { event: event });
    }
  
    /**
     * On drag enter.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onDragEnter(event, change, editor) {
      debug('onDragEnter', { event: event });
    }
  
    /**
     * On drag exit.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onDragExit(event, change, editor) {
      debug('onDragExit', { event: event });
    }
  
    /**
     * On drag leave.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onDragLeave(event, change, editor) {
      debug('onDragLeave', { event: event });
    }
  
    /**
     * On drag over.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onDragOver(event, change, editor) {
      // If the target is inside a void node, and only in this case,
      // call `preventDefault` to signal that drops are allowed.
      // When the target is editable, dropping is already allowed by
      // default, and calling `preventDefault` hides the cursor.
      var node = (0, _findNode2.default)(event.target, editor.value);
      if (node.isVoid) event.preventDefault();
  
      // If a drag is already in progress, don't do this again.
      if (isDragging) return true;
  
      isDragging = true;
      event.nativeEvent.dataTransfer.dropEffect = 'move';
  
      debug('onDragOver', { event: event });
    }
  
    /**
     * On drag start.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onDragStart(event, change, editor) {
      isDragging = true;
  
      debug('onDragStart', { event: event });
    }
  
    /**
     * On drop.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onDrop(event, change, editor) {
      // Stop propagation so the event isn't visible to parent editors.
      event.stopPropagation();
  
      // Nothing happens in read-only mode.
      if (editor.props.readOnly) return true;
  
      // Prevent default so the DOM's value isn't corrupted.
      event.preventDefault();
  
      debug('onDrop', { event: event });
    }
  
    /**
     * On focus.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onFocus(event, change, editor) {
      if (isCopying) return true;
      if (editor.props.readOnly) return true;
  
      var el = (0, _reactDom.findDOMNode)(editor);
  
      // Save the new `activeElement`.
      var window = (0, _getWindow2.default)(event.target);
      activeElement = window.document.activeElement;
  
      // COMPAT: If the editor has nested editable elements, the focus can go to
      // those elements. In Firefox, this must be prevented because it results in
      // issues with keyboard navigation. (2017/03/30)
      if (_environment.IS_FIREFOX && event.target != el) {
        el.focus();
        return true;
      }
  
      debug('onFocus', { event: event });
    }
  
    /**
     * On input.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onInput(event, change, editor) {
      if (isComposing) return true;
      if (change.value.isBlurred) return true;
  
      debug('onInput', { event: event });
    }
  
    /**
     * On key down.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onKeyDown(event, change, editor) {
      if (editor.props.readOnly) return true;
  
      // When composing, we need to prevent all hotkeys from executing while
      // typing. However, certain characters also move the selection before
      // we're able to handle it, so prevent their default behavior.
      if (isComposing) {
        if (_hotkeys2.default.COMPOSING(event)) event.preventDefault();
        return true;
      }
  
      // Certain hotkeys have native behavior in contenteditable elements which
      // will cause our value to be out of sync, so prevent them.
      if (_hotkeys2.default.CONTENTEDITABLE(event)) {
        event.preventDefault();
      }
  
      debug('onKeyDown', { event: event });
    }
  
    /**
     * On paste.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onPaste(event, change, editor) {
      if (editor.props.readOnly) return true;
  
      // Prevent defaults so the DOM state isn't corrupted.
      event.preventDefault();
  
      debug('onPaste', { event: event });
    }
  
    /**
     * On select.
     *
     * @param {Event} event
     * @param {Change} change
     * @param {Editor} editor
     */
  
    function onSelect(event, change, editor) {
      if (isCopying) return true;
      if (isComposing) return true;
      if (editor.props.readOnly) return true;
  
      // Save the new `activeElement`.
      var window = (0, _getWindow2.default)(event.target);
      activeElement = window.document.activeElement;
  
      debug('onSelect', { event: event });
    }
  
    /**
     * Return the plugin.
     *
     * @type {Object}
     */
  
    return {
      onBeforeInput: onBeforeInput,
      onBlur: onBlur,
      onChange: onChange,
      onCompositionEnd: onCompositionEnd,
      onCompositionStart: onCompositionStart,
      onCopy: onCopy,
      onCut: onCut,
      onDragEnd: onDragEnd,
      onDragEnter: onDragEnter,
      onDragExit: onDragExit,
      onDragLeave: onDragLeave,
      onDragOver: onDragOver,
      onDragStart: onDragStart,
      onDrop: onDrop,
      onFocus: onFocus,
      onInput: onInput,
      onKeyDown: onKeyDown,
      onPaste: onPaste,
      onSelect: onSelect
    };
  }
  
  /**
   * Export.
   *
   * @type {Object}
   */
  
  exports.default = BeforePlugin;