<commit_before>'use strict';
/* global CodeMirror $ */

var React = require('react/addons');
var TagTranslator = require('../helpers/tag-translator');
var ChoicesDropdown = require('../helpers/choices-dropdown');

/*
   Editor for tagged text. Renders text like "hello {{firstName}}"
   with replacement labels rendered in a pill box. Designed to load
   quickly when many separate instances of it are on the same
   page.

   Uses CodeMirror to edit text. To save memory the CodeMirror node is
   instantiated when the user moves the mouse into the edit area.
   Initially a read-only view using a simple div is shown.

   IMPLEMENTATION NOTE:

   To display the tags inside CodeMirror we are using CM's
   specialCharPlaceholder feature, to replace special characters with
   custom DOM nodes. This feature is designed for single character
   replacements, not tags like 'firstName'.  So we replace each tag
   with an unused character from the Unicode private use area, and
   tell CM to replace that with a DOM node display the tag label with
   the pill box effect.

   Is this evil? Perhaps a little, but delete, undo, redo, cut, copy
   and paste of the tag pill boxes just work because CM treats them as
   atomic single characters, and it's not much code on our part.
 */
module.exports = React.createClass({
  displayName: 'PrettyText',

  mixins: [require('../../mixins/field')],

  componentDidMount: function() {
    this.createEditor();
  },

  componentDidUpdate: function(prevProps, prevState) {
    if (prevState.codeMirrorMode !== this.state.codeMirrorMode) {
      // Changed from code mirror mode to read only mode or vice versa,
      // so setup the other editor.
      this.createEditor();
    }
  },

  componentWillUnmount: function() {
    this.removeCodeMirrorEditor();
  },

  getInitialState: function() {
    // TODO: using instance vars like this is probaby bad. Find another approach.
    this.tagToLabelMap = this.buildTagToLabelMap();
    this.translator = TagTranslator(this.tagToLabelMap);

    return {
      value: this.props.field.initialValue,
      codeMirrorMode: false
    };
  },

  componentWillReceiveProps: function(nextProps) {
    if (this.state.value !== nextProps.field.value) {
      this.setState({ value: nextProps.field.value });
    }
  },

  buildTagToLabelMap: function () {
    var map = {};
    this.props.field.replaceChoices.map(function (choice) {
      map[choice.value] = choice.label;
    });
    return map;
  },

  handleChoiceSelection: function (key) {
    var char = this.translator.encodeTag(key);

    // put the cursor at the end of the inserted tag.
    this.codeMirror.replaceSelection(char, 'end');
    this.codeMirror.focus();
  },

  render: function() {
    return this.renderWithConfig();
  },

  renderDefault: function () {
    var config = this.props.config;
    var field = this.props.field;
    var props = { field: field, plain: this.props.plain };
    var tabIndex = field.tabIndex;
    var choices = this.tagToLabelMap;
    var textBox = this.createTextBoxNode();

    // Render read-only version. We are using pure HTML via dangerouslySetInnerHTML, to avoid
    // the cost of the react nodes. This is probably a premature optimization.
    var element = (
      <div onMouseEnter={this.switchToCodeMirror}>
        <div className="pretty-text-box form-control" tabIndex={this.props.tabIndex}>
          {textBox}
        </div>
        <ChoicesDropdown ref="choicesDropdown" choices={choices} handleSelection={this.handleChoiceSelection} />
      </div>
    );

    return config.createElement('field', props, element);
  },

  createTextBoxNode: function () {
    if (this.state.codeMirrorMode) {
      return <div ref="textBox" />;
    } else {
      var html = this.translator.toHtml(this.state.value);
      return <div ref="textBox" dangerouslySetInnerHTML={{__html: html}} />;
    }
  },

  createEditor: function () {
    if (this.state.codeMirrorMode) {
      this.createCodeMirrorEditor();
    } else {
      this.createReadonlyEditor();
    }
  },

  createCodeMirrorEditor: function () {
    var cmValue = this.translator.encodeValue(this.state.value);

    var options = {
      tabindex: this.props.tabIndex,
      value: cmValue,
      specialChars: this.translator.specialCharsRegexp,
      specialCharPlaceholder: this.createTagNode
    };

    var textBox = this.refs.textBox.getDOMNode();
    textBox.innerHTML = ''; // release any previous read-only content so it can be GC'ed

    var codeMirror = CodeMirror(textBox, options);
    codeMirror.on('change', function () {
      var newValue = this.translator.decodeValue(codeMirror.getValue());
      this.onChangeValue(newValue);
      this.setState({value: newValue});
    }.bind(this));

    this.codeMirror = codeMirror;
  },<commit_msg>In PrettyText2 when user types in a tag turn it into a pretty pill<commit_after>'use strict';
/* global CodeMirror $ */

var React = require('react/addons');
var TagTranslator = require('../helpers/tag-translator');
var ChoicesDropdown = require('../helpers/choices-dropdown');

/*
   Editor for tagged text. Renders text like "hello {{firstName}}"
   with replacement labels rendered in a pill box. Designed to load
   quickly when many separate instances of it are on the same
   page.

   Uses CodeMirror to edit text. To save memory the CodeMirror node is
   instantiated when the user moves the mouse into the edit area.
   Initially a read-only view using a simple div is shown.

   IMPLEMENTATION NOTE:

   To display the tags inside CodeMirror we are using CM's
   specialCharPlaceholder feature, to replace special characters with
   custom DOM nodes. This feature is designed for single character
   replacements, not tags like 'firstName'.  So we replace each tag
   with an unused character from the Unicode private use area, and
   tell CM to replace that with a DOM node display the tag label with
   the pill box effect.

   Is this evil? Perhaps a little, but delete, undo, redo, cut, copy
   and paste of the tag pill boxes just work because CM treats them as
   atomic single characters, and it's not much code on our part.
 */
module.exports = React.createClass({
  displayName: 'PrettyText',

  mixins: [require('../../mixins/field')],

  componentDidMount: function() {
    this.createEditor();
  },

  componentDidUpdate: function(prevProps, prevState) {
    if (prevState.codeMirrorMode !== this.state.codeMirrorMode) {
      // Changed from code mirror mode to read only mode or vice versa,
      // so setup the other editor.
      this.createEditor();
    }

    // If they just typed in a tag like {{firstName}} we have to replace it
    if (this.state.codeMirrorMode && this.codeMirror.getValue().match(/\{\{.+\}\}/)) {
      // avoid recursive update cycle
      this.updatingCodeMirror = true;

      // get new encoded value for CodeMirror
      var cmValue = this.codeMirror.getValue();
      var decodedValue = this.translator.decodeValue(cmValue);
      var encodedValue = this.translator.encodeValue(decodedValue);

      // Grab the cursor so we can reset it.
      // The new length of the CM value will be shorter after replacing a tag like {{firstName}}
      // with a single special char, so adjust cursor position accordingly.
      var cursor = this.codeMirror.getCursor();
      cursor.ch -= cmValue.length - encodedValue.length;

      this.codeMirror.setValue(encodedValue);
      this.codeMirror.setCursor(cursor);
    }
  },

  componentWillUnmount: function() {
    this.removeCodeMirrorEditor();
  },

  getInitialState: function() {
    // TODO: using instance vars like this is probaby bad. Find another approach.
    this.tagToLabelMap = this.buildTagToLabelMap();
    this.translator = TagTranslator(this.tagToLabelMap);

    return {
      value: this.props.field.initialValue,
      codeMirrorMode: false
    };
  },

  componentWillReceiveProps: function(nextProps) {
    if (this.state.value !== nextProps.field.value) {
      this.setState({ value: nextProps.field.value });
    }
  },

  buildTagToLabelMap: function () {
    var map = {};
    this.props.field.replaceChoices.map(function (choice) {
      map[choice.value] = choice.label;
    });
    return map;
  },

  handleChoiceSelection: function (key) {
    var char = this.translator.encodeTag(key);

    // put the cursor at the end of the inserted tag.
    this.codeMirror.replaceSelection(char, 'end');
    this.codeMirror.focus();
  },

  render: function() {
    return this.renderWithConfig();
  },

  renderDefault: function () {
    var config = this.props.config;
    var field = this.props.field;
    var props = { field: field, plain: this.props.plain };
    var tabIndex = field.tabIndex;
    var choices = this.tagToLabelMap;
    var textBox = this.createTextBoxNode();

    // Render read-only version. We are using pure HTML via dangerouslySetInnerHTML, to avoid
    // the cost of the react nodes. This is probably a premature optimization.
    var element = (
      <div onMouseEnter={this.switchToCodeMirror}>
        <div className="pretty-text-box form-control" tabIndex={this.props.tabIndex}>
          {textBox}
        </div>
        <ChoicesDropdown ref="choicesDropdown" choices={choices} handleSelection={this.handleChoiceSelection} />
      </div>
    );

    return config.createElement('field', props, element);
  },

  createTextBoxNode: function () {
    if (this.state.codeMirrorMode) {
      return <div ref="textBox" />;
    } else {
      var html = this.translator.toHtml(this.state.value);
      return <div ref="textBox" dangerouslySetInnerHTML={{__html: html}} />;
    }
  },

  createEditor: function () {
    if (this.state.codeMirrorMode) {
      this.createCodeMirrorEditor();
    } else {
      this.createReadonlyEditor();
    }
  },

  createCodeMirrorEditor: function () {
    var cmValue = this.translator.encodeValue(this.state.value);

    var options = {
      tabindex: this.props.tabIndex,
      value: cmValue,
      specialChars: this.translator.specialCharsRegexp,
      specialCharPlaceholder: this.createTagNode
    };

    var textBox = this.refs.textBox.getDOMNode();
    textBox.innerHTML = ''; // release any previous read-only content so it can be GC'ed

    this.codeMirror = CodeMirror(textBox, options);
    this.codeMirror.on('change', this.onCodeMirrorChange);
  },

  onCodeMirrorChange: function () {
    if (this.updatingCodeMirror) {
      // avoid recursive update cycle, and mark the code mirror manual update as done
      this.updatingCodeMirror = false;
      return;
    }

    var newValue = this.translator.decodeValue(this.codeMirror.getValue());
    this.onChangeValue(newValue);
    this.setState({value: newValue});
  },
{'fraction_non_alphanumeric': 0.10650004662874196, 'fraction_numerical': 9.325748391308402e-05, 'mean_word_length': 3.187426786411558, 'pattern_counts': {'":': 0, '<': 17, '<?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': '3731123', 'n_tokens_mistral': 3136, 'n_tokens_neox': 3008, 'n_words': 941}