<commit_before>import invariant from 'invariant';
import twitter from 'twitter-text';
import closest from 'dom-closest';
import removeNode from 'dom-remove';
import findWhere from 'lodash.findwhere';
import getKeyCode from './utils/getKeyCode';

import {
  collectMentionIds,
  getEditorContent,
  getLastChar
} from './utils/tinyMCEUtils';

import {
  moveDown,
  moveUp,
  query,
  remove,
  resetMentions,
  resetQuery,
  select,
  syncEditorState
} from './actions/mentionActions';

const keyMap = {
  BACKSPACE: 8,
  DOWN: 40,
  ENTER: 13,
  TAB: 9,
  UP: 38
};

let editor;
let delimiter = '@';
let isFocused = false;
let store;

/**
 * A little state machine for tracking typed characters after `@ment|`.  Allows us
 * to determine if we are within a mention when `isFocued` is active.
 * @type {String}
 */
let typedMention = '';


export function initializePlugin(reduxStore, dataSource, delimiterConfig = delimiter) {

  invariant(reduxStore,
    'Plugin must be initialized with a Redux store.'
  );

  invariant(dataSource,
    'Plugin must be initialized with a dataSource.  Datasource can be an array or promise.'
  );

  return new Promise((resolve, reject) => {

    if (typeof window.tinymce === 'undefined') {
      return reject('Error initializing Mention plugin: `tinymce` is undefined.');
    }

    window.tinymce.PluginManager.add('mention', (activeEditor) => {
      editor = activeEditor;
      store = Object.freeze(reduxStore);
      delimiter = Object.freeze(delimiterConfig);

      // If promise, wait for it to resolve before resolving the
      // outer promise and initializing the app.
      if (typeof dataSource.then === 'function') {

        dataSource.then(response => {
          resolve({
            editor,
            resolvedDataSource: response
          });

          start();
        });

        // Check to see if promises follow spec, otherwise defer
        // to jQuery default
        if (dataSource.catch === 'function') {
          dataSource.catch(error => {
            throw new Error(error);
          });
        } else if (dataSource.fail === 'function') {
          dataSource.fail(error => {
            throw new Error(error);
          });
        }
      } else {
        resolve({
          editor,
          resolvedDataSource: dataSource
        });

        start();
      }
    });
  });
}

function start() {
  stop();

  editor.on('keypress', handleTopLevelEditorInput);
  editor.on('keyup', handleEditorBackspace);
}

function stop() {
  editor.off('keypress', handleTopLevelEditorInput);
  editor.off('keyup', handleEditorBackspace);
  editor.off('keydown', handleKeyPress);
}

/**
 * Add persistent top-level listener for delegating handlers for
 * binding and unbinding events related to UI interaction / querying.
 * @param  {jQuery.Event} event
 */
function handleTopLevelEditorInput(event) {
  const keyCode = getKeyCode(event);
  const character = String.fromCharCode(keyCode);
  const delimiterIndex = delimiter.indexOf(character);

  // User has typed `@` begin tracking
  if (!isFocused && delimiterIndex > -1) {
    startListeningForInput();

  // User has exited mentions, or there are no mentions to find; stop tracking
  } else if (!isFocused || character === ' ') {
    stopListeningAndCleanup();
  }
}

function startListeningForInput() {
  if (toggleFocus()) {
    editor.on('keydown', handleKeyPress);
  }
}

function stopListeningAndCleanup() {
  if (isFocused) {
    toggleFocus();
  }
  clearTypedMention();
  store.dispatch(resetQuery());
  editor.off('keydown', handleKeyPress);
}

/**
 * Validates input and checks to see if any intermediate actions should
 * be performed.
 *
 * @param  {Number} keyCode The current key being pressed
 * @param  {jQuery.Event} event
 * @return {Function}
 */
function performIntermediateActions(keyCode, event) {
  const { matchedSources } = store.getState().mention;

  if (matchedSources.length) {
    Object.keys(keyMap).forEach(key => {
      const keyValue = keyMap[key];

      // Override default behavior if we're using anything from our keyMap.
      if (keyCode === keyValue && keyValue !== keyMap.BACKSPACE) {
        event.preventDefault();
      }
    });

    return shouldSelectOrMove(keyCode);
  }
}

function shouldSelectOrMove(keyCode) {
  switch(keyCode) {
  case keyMap.TAB:
    selectMention();
  case keyMap.ENTER:
    selectMention();
  case keyMap.DOWN:
    return store.dispatch(moveDown());
  case keyMap.UP:
    return store.dispatch(moveUp());
  default:
    return false;<commit_msg>Fixed format<commit_after>import invariant from 'invariant';
import twitter from 'twitter-text';
import closest from 'dom-closest';
import removeNode from 'dom-remove';
import findWhere from 'lodash.findwhere';
import getKeyCode from './utils/getKeyCode';

import {
  collectMentionIds,
  getEditorContent,
  getLastChar
} from './utils/tinyMCEUtils';

import {
  moveDown,
  moveUp,
  query,
  remove,
  resetMentions,
  resetQuery,
  select,
  syncEditorState
} from './actions/mentionActions';

const keyMap = {
  BACKSPACE: 8,
  DOWN: 40,
  ENTER: 13,
  TAB: 9,
  UP: 38
};

let editor;
let delimiter = '@';
let isFocused = false;
let store;

/**
 * A little state machine for tracking typed characters after `@ment|`.  Allows us
 * to determine if we are within a mention when `isFocued` is active.
 * @type {String}
 */
let typedMention = '';


export function initializePlugin(reduxStore, dataSource, delimiterConfig = delimiter) {

  invariant(reduxStore,
    'Plugin must be initialized with a Redux store.'
  );

  invariant(dataSource,
    'Plugin must be initialized with a dataSource.  Datasource can be an array or promise.'
  );

  return new Promise((resolve, reject) => {

    if (typeof window.tinymce === 'undefined') {
      return reject('Error initializing Mention plugin: `tinymce` is undefined.');
    }

    window.tinymce.PluginManager.add('mention', (activeEditor) => {
      editor = activeEditor;
      store = Object.freeze(reduxStore);
      delimiter = Object.freeze(delimiterConfig);

      // If promise, wait for it to resolve before resolving the
      // outer promise and initializing the app.
      if (typeof dataSource.then === 'function') {

        dataSource.then(response => {
          resolve({
            editor,
            resolvedDataSource: response
          });

          start();
        });

        // Check to see if promises follow spec, otherwise defer
        // to jQuery default
        if (dataSource.catch === 'function') {
          dataSource.catch(error => {
            throw new Error(error);
          });
        } else if (dataSource.fail === 'function') {
          dataSource.fail(error => {
            throw new Error(error);
          });
        }
      } else {
        resolve({
          editor,
          resolvedDataSource: dataSource
        });

        start();
      }
    });
  });
}

function start() {
  stop();

  editor.on('keypress', handleTopLevelEditorInput);
  editor.on('keyup', handleEditorBackspace);
}

function stop() {
  editor.off('keypress', handleTopLevelEditorInput);
  editor.off('keyup', handleEditorBackspace);
  editor.off('keydown', handleKeyPress);
}

/**
 * Add persistent top-level listener for delegating handlers for
 * binding and unbinding events related to UI interaction / querying.
 * @param  {jQuery.Event} event
 */
function handleTopLevelEditorInput(event) {
  const keyCode = getKeyCode(event);
  const character = String.fromCharCode(keyCode);
  const delimiterIndex = delimiter.indexOf(character);

  // User has typed `@` begin tracking
  if (!isFocused && delimiterIndex > -1) {
    startListeningForInput();

  // User has exited mentions, or there are no mentions to find; stop tracking
  } else if (!isFocused || character === ' ') {
    stopListeningAndCleanup();
  }
}

function startListeningForInput() {
  if (toggleFocus()) {
    editor.on('keydown', handleKeyPress);
  }
}

function stopListeningAndCleanup() {
  if (isFocused) {
    toggleFocus();
  }
  clearTypedMention();
  store.dispatch(resetQuery());
  editor.off('keydown', handleKeyPress);
}

/**
 * Validates input and checks to see if any intermediate actions should
 * be performed.
 *
 * @param  {Number} keyCode The current key being pressed
 * @param  {jQuery.Event} event
 * @return {Function}
 */
function performIntermediateActions(keyCode, event) {
  const { matchedSources } = store.getState().mention;

  if (matchedSources.length) {
    Object.keys(keyMap).forEach(key => {
      const keyValue = keyMap[key];

      // Override default behavior if we're using anything from our keyMap.
      if (keyCode === keyValue && keyValue !== keyMap.BACKSPACE) {
        event.preventDefault();
      }
    });

    return shouldSelectOrMove(keyCode);
  }
}

function shouldSelectOrMove(keyCode) {
  switch(keyCode) {
  case keyMap.TAB:
    return selectMention();
  case keyMap.ENTER:
    return selectMention();
  case keyMap.DOWN:
    return store.dispatch(moveDown());
  case keyMap.UP:
    return store.dispatch(moveUp());
  default:
    return false;
{'fraction_non_alphanumeric': 0.1188097068189305, 'fraction_numerical': 0.001976501592181838, 'mean_word_length': 3.121266968325792, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '197874', 'n_tokens_mistral': 2704, 'n_tokens_neox': 2628, 'n_words': 738}