| /**
 * FocusController.js
 *
 * Released under LGPL License.
 * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */
define(
  'tinymce.core.focus.FocusController',
  [
    'ephox.katamari.api.Fun',
    'global!document',
    'tinymce.core.api.FocusManager',
    'tinymce.core.dom.DOMUtils',
    'tinymce.core.selection.SelectionRestore',
    'tinymce.core.util.Delay'
  ],
  function (Fun, document, FocusManager, DOMUtils, SelectionRestore, Delay) {
    var documentFocusInHandler, DOM = DOMUtils.DOM;
    var isEditorUIElement = function (elm) {
      // Since this can be overridden by third party we need to use the API reference here
      return FocusManager.isEditorUIElement(elm);
    };
    var isUIElement = function (editor, elm) {
      var customSelector = editor ? editor.settings.custom_ui_selector : '';
      var parent = DOM.getParent(elm, function (elm) {
        return (
          isEditorUIElement(elm) ||
          (customSelector ? editor.dom.is(elm, customSelector) : false)
        );
      });
      return parent !== null;
    };
    var getActiveElement = function () {
      try {
        return document.activeElement;
      } catch (ex) {
        // IE sometimes fails to get the activeElement when resizing table
        // TODO: Investigate this
        return document.body;
      }
    };
    var registerEvents = function (editorManager, e) {
      var editor = e.editor;
      SelectionRestore.register(editor);
      editor.on('focusin', function () {
        var focusedEditor = editorManager.focusedEditor;
        if (focusedEditor != editor) {
          if (focusedEditor) {
            focusedEditor.fire('blur', { focusedEditor: editor });
          }
          editorManager.setActive(editor);
          editorManager.focusedEditor = editor;
          editor.fire('focus', { blurredEditor: focusedEditor });
          editor.focus(true);
        }
      });
      editor.on('focusout', function () {
        Delay.setEditorTimeout(editor, function () {
          var focusedEditor = editorManager.focusedEditor;
          // Still the same editor the blur was outside any editor UI
          if (!isUIElement(editor, getActiveElement()) && focusedEditor == editor) {
            editor.fire('blur', { focusedEditor: null });
            editorManager.focusedEditor = null;
          }
        });
      });
      // Check if focus is moved to an element outside the active editor by checking if the target node
      // isn't within the body of the activeEditor nor a UI element such as a dialog child control
      if (!documentFocusInHandler) {
        documentFocusInHandler = function (e) {
          var activeEditor = editorManager.activeEditor, target;
          target = e.target;
          if (activeEditor && target.ownerDocument === document) {
            // Fire a blur event if the element isn't a UI element
            if (target !== document.body && !isUIElement(activeEditor, target) && editorManager.focusedEditor === activeEditor) {
              activeEditor.fire('blur', { focusedEditor: null });
              editorManager.focusedEditor = null;
            }
          }
        };
        DOM.bind(document, 'focusin', documentFocusInHandler);
      }
    };
    var unregisterDocumentEvents = function (editorManager, e) {
      if (editorManager.focusedEditor == e.editor) {
        editorManager.focusedEditor = null;
      }
      if (!editorManager.activeEditor) {
        DOM.unbind(document, 'focusin', documentFocusInHandler);
        documentFocusInHandler = null;
      }
    };
    var setup = function (editorManager) {
      editorManager.on('AddEditor', Fun.curry(registerEvents, editorManager));
      editorManager.on('RemoveEditor', Fun.curry(unregisterDocumentEvents, editorManager));
    };
    return {
      setup: setup,
      isEditorUIElement: isEditorUIElement,
      isUIElement: isUIElement
    };
  }
);
 |