<commit_before>// The initial version of this RTE was borrowed from https://github.com/jaredreich/pell
// and adapted to the GrapesJS's need

import { on, off } from 'utils/mixins';

const RTE_KEY = '_rte';

const btnState = {
  ACTIVE: 1,
  INACTIVE: 0,
  DISABLED: -1
};
const isValidAnchor = rte => {
  const anchor = rte.selection().anchorNode;
  const parentNode = anchor && anchor.parentNode;
  const nextSibling = anchor && anchor.nextSibling;
  return (
    (parentNode && parentNode.nodeName == 'A') ||
    (nextSibling && nextSibling.nodeName == 'A')
  );
};
const defActions = {
  bold: {
    name: 'bold',
    icon: '<b>B</b>',
    attributes: { title: 'Bold' },
    result: rte => rte.exec('bold')
  },
  italic: {
    name: 'italic',
    icon: '<i>I</i>',
    attributes: { title: 'Italic' },
    result: rte => rte.exec('italic')
  },
  underline: {
    name: 'underline',
    icon: '<u>U</u>',
    attributes: { title: 'Underline' },
    result: rte => rte.exec('underline')
  },
  strikethrough: {
    name: 'strikethrough',
    icon: '<strike>S</strike>',
    attributes: { title: 'Strike-through' },
    result: rte => rte.exec('strikeThrough')
  },
  link: {
    icon: `<span style="transform:rotate(45deg)">&supdsub;</span>`,
    name: 'link',
    attributes: {
      style: 'font-size:1.4rem;padding:0 4px 2px;',
      title: 'Link'
    },
    state: (rte, doc) => {
      if (rte && rte.selection()) {
        return isValidAnchor(rte) ? btnState.ACTIVE : btnState.INACTIVE;
      } else {
        return btnState.INACTIVE;
      }
    },
    result: rte => {
      if (isValidAnchor(rte)) {
        rte.exec('unlink');
      } else {
        rte.insertHTML(`<a class="link" href="">${rte.selection()}</a>`);
      }
    }
  }
};

export default class RichTextEditor {
  constructor(settings = {}) {
    const el = settings.el;

    if (el[RTE_KEY]) {
      return el[RTE_KEY];
    }

    el[RTE_KEY] = this;
    this.setEl(el);
    this.updateActiveActions = this.updateActiveActions.bind(this);

    const settAct = settings.actions || [];
    settAct.forEach((action, i) => {
      if (typeof action === 'string') {
        action = defActions[action];
      } else if (defActions[action.name]) {
        action = { ...defActions[action.name], ...action };
      }
      settAct[i] = action;
    });
    const actions = settAct.length
      ? settAct
      : Object.keys(defActions).map(action => defActions[action]);

    settings.classes = {
      ...{
        actionbar: 'actionbar',
        button: 'action',
        active: 'active',
        disabled: 'disabled',
        inactive: 'inactive'
      },
      ...settings.classes
    };

    const classes = settings.classes;
    let actionbar = settings.actionbar;
    this.actionbar = actionbar;
    this.settings = settings;
    this.classes = classes;
    this.actions = actions;

    if (!actionbar) {
      const actionbarCont = settings.actionbarContainer;
      actionbar = document.createElement('div');
      actionbar.className = classes.actionbar;
      actionbarCont.appendChild(actionbar);
      this.actionbar = actionbar;
      actions.forEach(action => this.addAction(action));
    }

    settings.styleWithCSS && this.exec('styleWithCSS');
    this.syncActions();

    return this;
  }

  setEl(el) {
    this.el = el;
    this.doc = el.ownerDocument;
  }

  updateActiveActions() {
    this.getActions().forEach(action => {
      const btn = action.btn;
      const update = action.update;
      const { active, inactive, disabled } = { ...this.classes };
      const state = action.state;
      const name = action.name;
      const doc = this.doc;
      btn.className = btn.className.replace(active, '').trim();
      btn.className = btn.className.replace(inactive, '').trim();
      btn.className = btn.className.replace(disabled, '').trim();

      // if there is a state function, which depicts the state,
      // i.e. `active`, `disabled`, then call it
      if (state) {
        switch (state(this, doc)) {
          case btnState.ACTIVE:
            btn.className += ` ${active}`;
            break;
          case btnState.INACTIVE:
            btn.className += ` ${inactive}`;
            break;
          case btnState.DISABLED:
            btn.className += ` ${disabled}`;
            break;
        }
      } else {
        // otherwise default to checking if the name command is supported & enabled
        if (doc.queryCommandSupported(name) && doc.queryCommandState(name)) {
          btn.className += ` ${active}`;
        }
      }
      update && update(this, action);
    });
  }

  enable() {
    if (this.enabled) {
      return this;
    }

    this.actionbarEl().style.display = '';
    this.el.contentEditable = true;
    on(this.el, 'mouseup keyup', this.updateActiveActions);
    this.syncActions();
    this.updateActiveActions();
    this.el.focus();
    this.enabled = 1;
    return this;
  }

  disable() {
    this.actionbarEl().style.display = 'none';
    this.el.contentEditable = false;
    off(this.el, 'mouseup keyup', this.updateActiveActions);
    this.enabled = 0;
    return this;
  }

  /**
   * Sync actions with the current RTE
   */
  syncActions() {
    this.getActions().forEach(action => {
      if (!action.state || (action.state && action.state() >= 0)) {
        const event = action.event || 'click';<commit_msg>Request PR change<commit_after>// The initial version of this RTE was borrowed from https://github.com/jaredreich/pell
// and adapted to the GrapesJS's need

import { on, off } from 'utils/mixins';

const RTE_KEY = '_rte';

const btnState = {
  ACTIVE: 1,
  INACTIVE: 0,
  DISABLED: -1
};
const isValidAnchor = rte => {
  const anchor = rte.selection().anchorNode;
  const parentNode = anchor && anchor.parentNode;
  const nextSibling = anchor && anchor.nextSibling;
  return (
    (parentNode && parentNode.nodeName == 'A') ||
    (nextSibling && nextSibling.nodeName == 'A')
  );
};
const defActions = {
  bold: {
    name: 'bold',
    icon: '<b>B</b>',
    attributes: { title: 'Bold' },
    result: rte => rte.exec('bold')
  },
  italic: {
    name: 'italic',
    icon: '<i>I</i>',
    attributes: { title: 'Italic' },
    result: rte => rte.exec('italic')
  },
  underline: {
    name: 'underline',
    icon: '<u>U</u>',
    attributes: { title: 'Underline' },
    result: rte => rte.exec('underline')
  },
  strikethrough: {
    name: 'strikethrough',
    icon: '<strike>S</strike>',
    attributes: { title: 'Strike-through' },
    result: rte => rte.exec('strikeThrough')
  },
  link: {
    icon: `<span style="transform:rotate(45deg)">&supdsub;</span>`,
    name: 'link',
    attributes: {
      style: 'font-size:1.4rem;padding:0 4px 2px;',
      title: 'Link'
    },
    state: (rte, doc) => {
      if (rte && rte.selection()) {
        return isValidAnchor(rte) ? btnState.ACTIVE : btnState.INACTIVE;
      } else {
        return btnState.INACTIVE;
      }
    },
    result: rte => {
      if (isValidAnchor(rte)) {
        rte.exec('unlink');
      } else {
        rte.insertHTML(`<a class="link" href="">${rte.selection()}</a>`);
      }
    }
  }
};

export default class RichTextEditor {
  constructor(settings = {}) {
    const el = settings.el;

    if (el[RTE_KEY]) {
      return el[RTE_KEY];
    }

    el[RTE_KEY] = this;
    this.setEl(el);
    this.updateActiveActions = this.updateActiveActions.bind(this);

    const settAct = settings.actions || [];
    settAct.forEach((action, i) => {
      if (typeof action === 'string') {
        action = defActions[action];
      } else if (defActions[action.name]) {
        action = { ...defActions[action.name], ...action };
      }
      settAct[i] = action;
    });
    const actions = settAct.length
      ? settAct
      : Object.keys(defActions).map(action => defActions[action]);

    settings.classes = {
      ...{
        actionbar: 'actionbar',
        button: 'action',
        active: 'active',
        disabled: 'disabled',
        inactive: 'inactive'
      },
      ...settings.classes
    };

    const classes = settings.classes;
    let actionbar = settings.actionbar;
    this.actionbar = actionbar;
    this.settings = settings;
    this.classes = classes;
    this.actions = actions;

    if (!actionbar) {
      const actionbarCont = settings.actionbarContainer;
      actionbar = document.createElement('div');
      actionbar.className = classes.actionbar;
      actionbarCont.appendChild(actionbar);
      this.actionbar = actionbar;
      actions.forEach(action => this.addAction(action));
    }

    settings.styleWithCSS && this.exec('styleWithCSS');
    this.syncActions();

    return this;
  }

  setEl(el) {
    this.el = el;
    this.doc = el.ownerDocument;
  }

  updateActiveActions() {
    this.getActions().forEach(action => {
      const btn = action.btn;
      const update = action.update;
      const { active, inactive, disabled } = { ...this.classes };
      const state = action.state;
      const name = action.name;
      const doc = this.doc;
      btn.className = btn.className.replace(active, '').trim();
      btn.className = btn.className.replace(inactive, '').trim();
      btn.className = btn.className.replace(disabled, '').trim();

      // if there is a state function, which depicts the state,
      // i.e. `active`, `disabled`, then call it
      if (state) {
        switch (state(this, doc)) {
          case btnState.ACTIVE:
            btn.className += ` ${active}`;
            break;
          case btnState.INACTIVE:
            btn.className += ` ${inactive}`;
            break;
          case btnState.DISABLED:
            btn.className += ` ${disabled}`;
            break;
        }
      } else {
        // otherwise default to checking if the name command is supported & enabled
        if (doc.queryCommandSupported(name) && doc.queryCommandState(name)) {
          btn.className += ` ${active}`;
        }
      }
      update && update(this, action);
    });
  }

  enable() {
    if (this.enabled) {
      return this;
    }

    this.actionbarEl().style.display = '';
    this.el.contentEditable = true;
    on(this.el, 'mouseup keyup', this.updateActiveActions);
    this.syncActions();
    this.updateActiveActions();
    this.el.focus();
    this.enabled = 1;
    return this;
  }

  disable() {
    this.actionbarEl().style.display = 'none';
    this.el.contentEditable = false;
    off(this.el, 'mouseup keyup', this.updateActiveActions);
    this.enabled = 0;
    return this;
  }

  /**
   * Sync actions with the current RTE
   */
  syncActions() {
    this.getActions().forEach(action => {
      if (
        !action.state ||
        (action.state && action.state(this, this.doc) >= 0)
      ) {
        const event = action.event || 'click';
{'fraction_non_alphanumeric': 0.15564635958395245, 'fraction_numerical': 0.0024145616641901933, 'mean_word_length': 2.432897672935926, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 53, 'https://': 2, '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': '7548600', 'n_tokens_mistral': 3445, 'n_tokens_neox': 3430, 'n_words': 728}