<commit_before>function HandsontableTextEditorClass(instance) {
  this.instance = instance;
  this.createElements();
  this.bindEvents();
}

HandsontableTextEditorClass.prototype.createElements = function () {
  this.STATE_VIRGIN = 'STATE_VIRGIN'; //before editing
  this.STATE_EDITING = 'STATE_EDITING';
  this.STATE_WAITING = 'STATE_WAITING'; //waiting for async validation
  this.STATE_FINISHED = 'STATE_FINISHED';

  this.state = this.STATE_VIRGIN;
  this.waitingEvent = null;

  this.wtDom = new WalkontableDom();

  this.TEXTAREA = document.createElement('TEXTAREA');
  this.TEXTAREA.className = 'handsontableInput';
  this.textareaStyle = this.TEXTAREA.style;
  this.textareaStyle.width = 0;
  this.textareaStyle.height = 0;
  this.$textarea = $(this.TEXTAREA);

  this.TEXTAREA_PARENT = document.createElement('DIV');
  this.TEXTAREA_PARENT.className = 'handsontableInputHolder';
  this.textareaParentStyle = this.TEXTAREA_PARENT.style;
  this.textareaParentStyle.top = 0;
  this.textareaParentStyle.left = 0;
  this.textareaParentStyle.display = 'none';

  this.$body = $(document.body);

  this.TEXTAREA_PARENT.appendChild(this.TEXTAREA);
  this.instance.rootElement[0].appendChild(this.TEXTAREA_PARENT);

  var that = this;
  Handsontable.PluginHooks.add('afterRender', function () {
    that.instance.registerTimeout('refresh_editor_dimensions', function () {
      that.refreshDimensions();
    }, 0);
  });
};

HandsontableTextEditorClass.prototype.bindEvents = function () {
  var that = this;

  this.$textarea.off('.editor').on('keydown.editor', function (event) {
    if(that.state === that.STATE_WAITING) {
      event.stopImmediatePropagation();
    }
    else {
      that.waitingEvent = null;
    }

    if (that.state !== that.STATE_EDITING) {
      return;
    }

    var ctrlDown = (event.ctrlKey || event.metaKey) && !event.altKey; //catch CTRL but not right ALT (which in some systems triggers ALT+CTRL)

    if (event.keyCode === 17 || event.keyCode === 224 || event.keyCode === 91 || event.keyCode === 93) {
      //when CTRL or its equivalent is pressed and cell is edited, don't prepare selectable text in textarea
      event.stopImmediatePropagation();
      return;
    }

    switch (event.keyCode) {
      case 38: /* arrow up */
        that.finishEditing(false);
        break;

      case 40: /* arrow down */
        that.finishEditing(false);
        break;

      case 9: /* tab */
        that.finishEditing(false);
        event.preventDefault();
        break;

      case 39: /* arrow right */
        if (that.getCaretPosition(that.TEXTAREA) === that.TEXTAREA.value.length) {
          that.finishEditing(false);
        }
        else {
          event.stopImmediatePropagation();
        }
        break;

      case 37: /* arrow left */
        if (that.getCaretPosition(that.TEXTAREA) === 0) {
          that.finishEditing(false);
        }
        else {
          event.stopImmediatePropagation();
        }
        break;

      case 27: /* ESC */
        that.instance.destroyEditor(true);
        event.stopImmediatePropagation();
        break;

      case 13: /* return/enter */
        var selected = that.instance.getSelected();
        var isMultipleSelection = !(selected[0] === selected[2] && selected[1] === selected[3]);
        if ((event.ctrlKey && !isMultipleSelection) || event.altKey) { //if ctrl+enter or alt+enter, add new line
          that.TEXTAREA.value = that.TEXTAREA.value + '\n';
          that.TEXTAREA.focus();
          event.stopImmediatePropagation();
        }
        else {
          that.finishEditing(false, ctrlDown);
        }
        event.preventDefault(); //don't add newline to field
        break;

      default:
        event.stopImmediatePropagation(); //backspace, delete, home, end, CTRL+A, CTRL+C, CTRL+V, CTRL+X should only work locally when cell is edited (not in table context)
        break;
    }

    if (that.state !== that.STATE_FINISHED && !event.isImmediatePropagationStopped()) {
      that.waitingEvent = event;
      event.stopImmediatePropagation();
      event.preventDefault();
    }
  });
};

HandsontableTextEditorClass.prototype.bindTemporaryEvents = function (td, row, col, prop, value, cellProperties) {
  var that = this;

  this.state = this.STATE_VIRGIN;

  function onDblClick() {
    that.TEXTAREA.value = that.originalValue;
    that.instance.destroyEditor();
    that.beginEditing(row, col, prop, true);
  }

  this.instance.view.wt.update('onCellDblClick', onDblClick);

  this.TD = td;
  this.row = row;
  this.col = col;
  this.prop = prop;
  this.originalValue = value;
  this.cellProperties = cellProperties;

  this.beforeKeyDownHook = function beforeKeyDownHook (event) {
    if (that.state !== that.STATE_VIRGIN) {
      return;
    }

    var ctrlDown = (event.ctrlKey || event.metaKey) && !event.altKey; //catch CTRL but not right ALT (which in some systems triggers ALT+CTRL)

    if (Handsontable.helper.isPrintableChar(event.keyCode)) {
      if (!ctrlDown) { //disregard CTRL-key shortcuts
        that.beginEditing(row, col, prop);
        event.stopImmediatePropagation();
      }
    }
    else if (event.keyCode === 113) { //f2
      that.beginEditing(row, col, prop, true); //show edit field
      event.stopImmediatePropagation();
      event.preventDefault(); //prevent Opera from opening Go to Page dialog
    }
    else if (event.keyCode === 13 && that.instance.getSettings().enterBeginsEditing) { //enter
      var selected = that.instance.getSelected();
      var isMultipleSelection = !(selected[0] === selected[2] && selected[1] === selected[3]);
      if ((ctrlDown && !isMultipleSelection) || event.altKey) { //if ctrl+enter or alt+enter, add new line
        that.beginEditing(row, col, prop, true, '\n'); //show edit field
      }
      else {
        that.beginEditing(row, col, prop, true); //show edit field
      }
      event.preventDefault(); //prevent new line at the end of textarea
      event.stopImmediatePropagation();
    } else if (event.keyCode == 16){ //shift
      that.instance.addHookOnce('beforeKeyDown', beforeKeyDownHook);
    }
  };
  that.instance.addHookOnce('beforeKeyDown', this.beforeKeyDownHook);
};

HandsontableTextEditorClass.prototype.unbindTemporaryEvents = function () {
  this.instance.removeHook('beforeKeyDown', this.beforeKeyDownHook);
  this.instance.view.wt.update('onCellDblClick', null);
};

/**
 * Returns caret position in edit proxy
 * @author http://stackoverflow.com/questions/263743/how-to-get-caret-position-in-textarea
 * @return {Number}
 */
HandsontableTextEditorClass.prototype.getCaretPosition = function (el) {<commit_msg>Bugfix #944. Cell editor should now open after typing a sequence of non-printable characters preceding a printable character (e.i. SHIFT+S, ALT+S, SHIFT+ALT+S)<commit_after>function HandsontableTextEditorClass(instance) {
  this.instance = instance;
  this.createElements();
  this.bindEvents();
}

HandsontableTextEditorClass.prototype.createElements = function () {
  this.STATE_VIRGIN = 'STATE_VIRGIN'; //before editing
  this.STATE_EDITING = 'STATE_EDITING';
  this.STATE_WAITING = 'STATE_WAITING'; //waiting for async validation
  this.STATE_FINISHED = 'STATE_FINISHED';

  this.state = this.STATE_VIRGIN;
  this.waitingEvent = null;

  this.wtDom = new WalkontableDom();

  this.TEXTAREA = document.createElement('TEXTAREA');
  this.TEXTAREA.className = 'handsontableInput';
  this.textareaStyle = this.TEXTAREA.style;
  this.textareaStyle.width = 0;
  this.textareaStyle.height = 0;
  this.$textarea = $(this.TEXTAREA);

  this.TEXTAREA_PARENT = document.createElement('DIV');
  this.TEXTAREA_PARENT.className = 'handsontableInputHolder';
  this.textareaParentStyle = this.TEXTAREA_PARENT.style;
  this.textareaParentStyle.top = 0;
  this.textareaParentStyle.left = 0;
  this.textareaParentStyle.display = 'none';

  this.$body = $(document.body);

  this.TEXTAREA_PARENT.appendChild(this.TEXTAREA);
  this.instance.rootElement[0].appendChild(this.TEXTAREA_PARENT);

  var that = this;
  Handsontable.PluginHooks.add('afterRender', function () {
    that.instance.registerTimeout('refresh_editor_dimensions', function () {
      that.refreshDimensions();
    }, 0);
  });
};

HandsontableTextEditorClass.prototype.bindEvents = function () {
  var that = this;

  this.$textarea.off('.editor').on('keydown.editor', function (event) {
    if(that.state === that.STATE_WAITING) {
      event.stopImmediatePropagation();
    }
    else {
      that.waitingEvent = null;
    }

    if (that.state !== that.STATE_EDITING) {
      return;
    }

    var ctrlDown = (event.ctrlKey || event.metaKey) && !event.altKey; //catch CTRL but not right ALT (which in some systems triggers ALT+CTRL)

    if (event.keyCode === 17 || event.keyCode === 224 || event.keyCode === 91 || event.keyCode === 93) {
      //when CTRL or its equivalent is pressed and cell is edited, don't prepare selectable text in textarea
      event.stopImmediatePropagation();
      return;
    }

    switch (event.keyCode) {
      case 38: /* arrow up */
        that.finishEditing(false);
        break;

      case 40: /* arrow down */
        that.finishEditing(false);
        break;

      case 9: /* tab */
        that.finishEditing(false);
        event.preventDefault();
        break;

      case 39: /* arrow right */
        if (that.getCaretPosition(that.TEXTAREA) === that.TEXTAREA.value.length) {
          that.finishEditing(false);
        }
        else {
          event.stopImmediatePropagation();
        }
        break;

      case 37: /* arrow left */
        if (that.getCaretPosition(that.TEXTAREA) === 0) {
          that.finishEditing(false);
        }
        else {
          event.stopImmediatePropagation();
        }
        break;

      case 27: /* ESC */
        that.instance.destroyEditor(true);
        event.stopImmediatePropagation();
        break;

      case 13: /* return/enter */
        var selected = that.instance.getSelected();
        var isMultipleSelection = !(selected[0] === selected[2] && selected[1] === selected[3]);
        if ((event.ctrlKey && !isMultipleSelection) || event.altKey) { //if ctrl+enter or alt+enter, add new line
          that.TEXTAREA.value = that.TEXTAREA.value + '\n';
          that.TEXTAREA.focus();
          event.stopImmediatePropagation();
        }
        else {
          that.finishEditing(false, ctrlDown);
        }
        event.preventDefault(); //don't add newline to field
        break;

      default:
        event.stopImmediatePropagation(); //backspace, delete, home, end, CTRL+A, CTRL+C, CTRL+V, CTRL+X should only work locally when cell is edited (not in table context)
        break;
    }

    if (that.state !== that.STATE_FINISHED && !event.isImmediatePropagationStopped()) {
      that.waitingEvent = event;
      event.stopImmediatePropagation();
      event.preventDefault();
    }
  });
};

HandsontableTextEditorClass.prototype.bindTemporaryEvents = function (td, row, col, prop, value, cellProperties) {
  var that = this;

  this.state = this.STATE_VIRGIN;

  function onDblClick() {
    that.TEXTAREA.value = that.originalValue;
    that.instance.destroyEditor();
    that.beginEditing(row, col, prop, true);
  }

  this.instance.view.wt.update('onCellDblClick', onDblClick);

  this.TD = td;
  this.row = row;
  this.col = col;
  this.prop = prop;
  this.originalValue = value;
  this.cellProperties = cellProperties;

  this.beforeKeyDownHook = function beforeKeyDownHook (event) {
    if (that.state !== that.STATE_VIRGIN) {
      return;
    }

    var ctrlDown = (event.ctrlKey || event.metaKey) && !event.altKey; //catch CTRL but not right ALT (which in some systems triggers ALT+CTRL)

    if (Handsontable.helper.isPrintableChar(event.keyCode)) {
      if (!ctrlDown) { //disregard CTRL-key shortcuts
        that.beginEditing(row, col, prop);
        event.stopImmediatePropagation();
      }
    }
    else if (event.keyCode === 113) { //f2
      that.beginEditing(row, col, prop, true); //show edit field
      event.stopImmediatePropagation();
      event.preventDefault(); //prevent Opera from opening Go to Page dialog
    }
    else if (event.keyCode === 13 && that.instance.getSettings().enterBeginsEditing) { //enter
      var selected = that.instance.getSelected();
      var isMultipleSelection = !(selected[0] === selected[2] && selected[1] === selected[3]);
      if ((ctrlDown && !isMultipleSelection) || event.altKey) { //if ctrl+enter or alt+enter, add new line
        that.beginEditing(row, col, prop, true, '\n'); //show edit field
      }
      else {
        that.beginEditing(row, col, prop, true); //show edit field
      }
      event.preventDefault(); //prevent new line at the end of textarea
      event.stopImmediatePropagation();
    } else { // other non printable character
      that.instance.addHookOnce('beforeKeyDown', beforeKeyDownHook);
    }
  };
  that.instance.addHookOnce('beforeKeyDown', this.beforeKeyDownHook);
};

HandsontableTextEditorClass.prototype.unbindTemporaryEvents = function () {
  this.instance.removeHook('beforeKeyDown', this.beforeKeyDownHook);
  this.instance.view.wt.update('onCellDblClick', null);
};

/**
 * Returns caret position in edit proxy
 * @author http://stackoverflow.com/questions/263743/how-to-get-caret-position-in-textarea
 * @return {Number}
 */
HandsontableTextEditorClass.prototype.getCaretPosition = function (el) {
{'fraction_non_alphanumeric': 0.13078126163700007, 'fraction_numerical': 0.007671110449095107, 'mean_word_length': 3.113970588235294, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '208702', 'n_tokens_mistral': 4274, 'n_tokens_neox': 4099, 'n_words': 861}