<commit_before>class TextileDecorator {
  blockquote(str) { return ["bq. ", str, ""]; }
  bold(str) { return ["<b>", str, "</b>"]; }
  emphasis(str) { return ["<i>", str, "</i>"]; }
  h1(str) { return ["h1. ", str, ""]; }
  h2(str) { return ["h2. ", str, ""]; }
  h3(str) { return ["h3. ", str, ""]; }
  h4(str) { return ["h4. ", str, ""]; }
  h5(str) { return ["h5. ", str, ""]; }
  h6(str) { return ["h6. ", str, ""]; }
  link(url, name) { return ["\"", name, `\":${url}`]; }
  email(address, name) { return ["\"", name, `\":mailto:${address}`]; }

  strToList(str, prefix) {
    return str.split("\n").map(l => prefix +  " " + l).join("\n");
  }

  list(str) {
    return ["", this.strToList(str, "*"), ""];
    return ["", str.split("\n").map(l => "* " + l).join("\n"), ""];
  }

  orderedList(str) {
    return ["", this.strToList(str, "#"), ""];
  }
}

class RichTextArea {
  constructor(textarea) {
    this.textarea = textarea;
    this.decorator = new TextileDecorator();
    this.toolbar = $("<ul class=\"rich-text-toolbar\"></ul>")
                      .insertBefore(this.textarea);
    this.addButtons();
  }

  adjustSelection(callback) {
    let length = this.getSelection().length;
    let [start, end] = this.getSelectionRange();
    let [replacementLength, prefixLength] = callback();
    let newEnd = (end + (replacementLength - length) + prefixLength);
    let newStart = start === end ? newEnd : (start + prefixLength);
    return this.setSelectionRange(newStart, newEnd);
  };

  getSelection() {
    return $(this.textarea).getSelection().text;
  }

  getSelectionRange() {
    if (typeof this.textarea.selectionStart !== "undefined") {
      return [this.textarea.selectionStart, this.textarea.selectionEnd];
    } else {
      return [0, 0];
    }
  };

  replaceSelection(prefix, replacement, postfix) {
    return this.adjustSelection(() => {
      $(this.textarea).replaceSelection(prefix + replacement + postfix);
      $(this.textarea).focus();
      return [replacement.length, prefix.length];
    });
  };

  // Sets a new selection range for object
  setSelectionRange(start, end) {
    if (typeof this.textarea.setSelectionRange !== "undefined") {
      return this.textarea.setSelectionRange(start, end);
    }
  };

  addButton(name, className, callback) {
    var link = $(
      `<a title=\"${name}\" class=\"${className}\">` +
      `<i class=\"fa fa-${className}\"></i></a>`
    );

    link.click(() => {
      let [prefix, replacement, postfix] = callback(this.getSelection());
      this.replaceSelection(prefix, replacement, postfix);
    });

    $("<li class=\"button\"></li>").append(link).appendTo(this.toolbar);
  };

  addButtons() {
    // Bold button
    this.addButton("Bold", "bold", s => this.decorator.bold(s));

    // Italic button
    this.addButton("Italics", "italic", s => this.decorator.emphasis(s));

    // Heading buttons
    this.addButton("Heading 2", "header h2", s => this.decorator.h2(s));
    this.addButton("Heading 3", "header h3", s => this.decorator.h3(s));
    this.addButton("Heading 4", "header h4", s => this.decorator.h4(s));

    // Block Quote
    this.addButton("Block Quote", "quote-left", s => this.decorator.blockquote(s))

    // List
    this.addButton("List", "list-ul", s => this.decorator.list(s));

    // Ordered list
    this.addButton("Ordered list", "list-ol", s => this.decorator.orderedList(s));

    // Link button
    this.addButton("Link", "link", selection => {
      let name = selection.length > 0 ? selection : "Link text";
      var url = prompt("Enter link URL", "");
      url = url.length > 0 ? url : "http://example.com/";
      url = url.replace(/^(?!(f|ht)tps?:\/\/)/, 'http://');
      return this.decorator.link(url, name);
    });

    // Email button
    this.addButton("Email link", "envelope", selection => {
      let name = selection.length > 0 ? selection : "Link text";
      var address = prompt("Enter email address", "");
      address = address.length > 0 ? address : "<EMAIL>";
      return this.decorator.email(address, name);
    });
  }
}

$(function() {
  $('textarea.rich').each(function() { new RichTextArea(this); });
});
<commit_msg>Fix focus loss in Chrome<commit_after>class TextileDecorator {
  blockquote(str) { return ["bq. ", str, ""]; }
  bold(str) { return ["<b>", str, "</b>"]; }
  emphasis(str) { return ["<i>", str, "</i>"]; }
  h1(str) { return ["h1. ", str, ""]; }
  h2(str) { return ["h2. ", str, ""]; }
  h3(str) { return ["h3. ", str, ""]; }
  h4(str) { return ["h4. ", str, ""]; }
  h5(str) { return ["h5. ", str, ""]; }
  h6(str) { return ["h6. ", str, ""]; }
  link(url, name) { return ["\"", name, `\":${url}`]; }
  email(address, name) { return ["\"", name, `\":mailto:${address}`]; }

  strToList(str, prefix) {
    return str.split("\n").map(l => prefix +  " " + l).join("\n");
  }

  list(str) {
    return ["", this.strToList(str, "*"), ""];
    return ["", str.split("\n").map(l => "* " + l).join("\n"), ""];
  }

  orderedList(str) {
    return ["", this.strToList(str, "#"), ""];
  }
}

class RichTextArea {
  constructor(textarea) {
    this.textarea = textarea;
    this.decorator = new TextileDecorator();
    this.toolbar = $("<ul class=\"rich-text-toolbar\"></ul>")
                      .insertBefore(this.textarea);
    this.addButtons();
  }

  adjustSelection(callback) {
    let length = this.getSelection().length;
    let [start, end] = this.getSelectionRange();
    let [replacementLength, prefixLength] = callback();
    let newEnd = (end + (replacementLength - length) + prefixLength);
    let newStart = start === end ? newEnd : (start + prefixLength);
    return this.setSelectionRange(newStart, newEnd);
  };

  getSelection() {
    return $(this.textarea).getSelection().text;
  }

  getSelectionRange() {
    if (typeof this.textarea.selectionStart !== "undefined") {
      return [this.textarea.selectionStart, this.textarea.selectionEnd];
    } else {
      return [0, 0];
    }
  };

  replaceSelection(prefix, replacement, postfix) {
    return this.adjustSelection(() => {
      $(this.textarea).replaceSelection(prefix + replacement + postfix);
      if (typeof this.textarea.focus !== "undefined") {
        this.textarea.focus({ preventScroll: true });
      }
      return [replacement.length, prefix.length];
    });
  };

  // Sets a new selection range for object
  setSelectionRange(start, end) {
    if (typeof this.textarea.setSelectionRange !== "undefined") {
      return this.textarea.setSelectionRange(start, end);
    }
  };

  addButton(name, className, callback) {
    var link = $(
      `<a title=\"${name}\" class=\"${className}\">` +
      `<i class=\"fa fa-${className}\"></i></a>`
    );

    link.click((evt) => {
      evt.preventDefault();
      let [prefix, replacement, postfix] = callback(this.getSelection());
      this.replaceSelection(prefix, replacement, postfix);
    });

    $("<li class=\"button\"></li>").append(link).appendTo(this.toolbar);
  };

  addButtons() {
    // Bold button
    this.addButton("Bold", "bold", s => this.decorator.bold(s));

    // Italic button
    this.addButton("Italics", "italic", s => this.decorator.emphasis(s));

    // Heading buttons
    this.addButton("Heading 2", "header h2", s => this.decorator.h2(s));
    this.addButton("Heading 3", "header h3", s => this.decorator.h3(s));
    this.addButton("Heading 4", "header h4", s => this.decorator.h4(s));

    // Block Quote
    this.addButton("Block Quote", "quote-left", s => this.decorator.blockquote(s))

    // List
    this.addButton("List", "list-ul", s => this.decorator.list(s));

    // Ordered list
    this.addButton("Ordered list", "list-ol", s => this.decorator.orderedList(s));

    // Link button
    this.addButton("Link", "link", selection => {
      let name = selection.length > 0 ? selection : "Link text";
      var url = prompt("Enter link URL", "");
      url = url.length > 0 ? url : "http://example.com/";
      url = url.replace(/^(?!(f|ht)tps?:\/\/)/, 'http://');
      return this.decorator.link(url, name);
    });

    // Email button
    this.addButton("Email link", "envelope", selection => {
      let name = selection.length > 0 ? selection : "Link text";
      var address = prompt("Enter email address", "");
      address = address.length > 0 ? address : "<EMAIL>";
      return this.decorator.email(address, name);
    });
  }
}

$(function() {
  $('textarea.rich').each(function() { new RichTextArea(this); });
});

{'fraction_non_alphanumeric': 0.21037089534609024, 'fraction_numerical': 0.0063784549964564135, 'mean_word_length': 3.1081999029597283, 'pattern_counts': {'":': 4, '<': 29, '<?xml version=': 0, '>': 65, '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': '370580', 'n_tokens_mistral': 2848, 'n_tokens_neox': 2775, 'n_words': 573}