<commit_before>import parser from './parser/javascript.js';
import Snap from 'snapsvg';
import Q from 'q';

export default class Regexper {
  constructor(root) {
    this.root = root;
    this.form = root.querySelector('#regexp-form');
    this.field = root.querySelector('#regexp-input');
    this.error = root.querySelector('#error');
    this.permalink = root.querySelector('a[data-glyph="link-intact"]');
    this.download = root.querySelector('a[data-glyph="data-transfer-download"]');
    this.svg = root.querySelector('#regexp-render svg');

    this.padding = 10;
    this.snap = Snap(this.svg);
  }

  keypressListener(event) {
    if (event.shiftKey && event.keyCode === 13) {
      event.preventDefault();
      this.form.dispatchEvent(new Event('submit'));
    }
  }

  submitListener(event) {
    event.preventDefault();

    location.hash = this.field.value;
  }

  hashchangeListener() {
    var expression = decodeURIComponent(location.hash.slice(1));

    if (expression !== '') {
      this.field.value = expression;

      this.setState('is-loading');

      this.renderRegexp(expression.replace(/[\r\n]/g, ''))
        .then((() => {
          this.setState('has-results');
          this.updateLinks();
        }).bind(this), this.showError.bind(this));
    }
  }

  bindListeners() {
    this.field.addEventListener('keypress', this.keypressListener.bind(this));

    this.form.addEventListener('submit', this.submitListener.bind(this));

    window.addEventListener('hashchange', this.hashchangeListener.bind(this));
  }

  setState(state) {
    var classList = this.root.classList;

    classList.remove('is-loading', 'has-results', 'has-error');
    classList.add(state);
  }

  showError(message) {
    this.setState('has-error');
    this.error.innerHTML = '';
    this.error.appendChild(document.createTextNode(message));
  }

  updateLinks() {
    var blob, url;

    blob = new Blob([this.svg.outerHTML], { type: 'image/svg+xml' });
    url = URL.createObjectURL(blob);

    this.download.setAttribute('href', url);
    this.permalink.setAttribute('href', location);
  }

  renderSvg(snap, expression) {
    var deferred = Q.defer(),
        result;

    snap.selectAll('g').remove();

    setTimeout(() => {
      try {
        result = parser.parse(expression);
      }
      catch(e) {
        deferred.reject(e);
      }

      if (result) {
        result.container = snap.group();
        result.render();
        deferred.resolve(result);
      }
    });

    return deferred.promise;
  }

  positionSvg(snap, parsed) {
    var box;

    parsed.position();

    box = parsed.container.getBBox();
    parsed.container.transform(Snap.matrix()
      .translate(this.padding - box.x, this.padding - box.y));
    snap.attr({
      width: box.width + this.padding * 2,
      height: box.height + this.padding * 2
    });
  }

  renderRegexp(expression) {
    var padding = this.padding,
        snap = Snap(this.svg);

    return this.renderSvg(snap, expression)
      .then(this.positionSvg.bind(this, snap));
  }
}<commit_msg>Further refactoring of Regexper class<commit_after>import parser from './parser/javascript.js';
import Snap from 'snapsvg';
import Q from 'q';

export default class Regexper {
  constructor(root) {
    this.root = root;
    this.form = root.querySelector('#regexp-form');
    this.field = root.querySelector('#regexp-input');
    this.error = root.querySelector('#error');
    this.permalink = root.querySelector('a[data-glyph="link-intact"]');
    this.download = root.querySelector('a[data-glyph="data-transfer-download"]');
    this.svg = root.querySelector('#regexp-render svg');

    this.padding = 10;
    this.snap = Snap(this.svg);
  }

  keypressListener(event) {
    if (event.shiftKey && event.keyCode === 13) {
      event.preventDefault();
      this.form.dispatchEvent(new Event('submit'));
    }
  }

  submitListener(event) {
    event.preventDefault();

    location.hash = this.field.value;
  }

  hashchangeListener() {
    var expression = decodeURIComponent(location.hash.slice(1));

    if (expression !== '') {
      this.field.value = expression;

      this.setState('is-loading');

      this.renderRegexp(expression.replace(/[\r\n]/g, ''))
        .then((() => {
          this.setState('has-results');
          this.updateLinks();
        }).bind(this))
        .done();
    }
  }

  bindListeners() {
    this.field.addEventListener('keypress', this.keypressListener.bind(this));

    this.form.addEventListener('submit', this.submitListener.bind(this));

    window.addEventListener('hashchange', this.hashchangeListener.bind(this));
  }

  setState(state) {
    var classList = this.root.classList;

    classList.remove('is-loading', 'has-results', 'has-error');
    classList.add(state);
  }

  showError(message) {
    this.setState('has-error');
    this.error.innerHTML = '';
    this.error.appendChild(document.createTextNode(message));

    throw message;
  }

  updateLinks() {
    var blob, url;

    blob = new Blob([this.svg.outerHTML], { type: 'image/svg+xml' });
    url = URL.createObjectURL(blob);

    this.download.setAttribute('href', url);
    this.permalink.setAttribute('href', location);
  }

  renderRegexp(expression) {
    var snap = Snap(this.svg),
        deferred = Q.defer(),
        padding = this.padding,
        result;

    snap.selectAll('g').remove();

    return Q.fcall(parser.parse.bind(parser), expression)
      .then((result) => {
        result.container = snap.group();
        result.render();
        return result;
      }, this.showError.bind(this))
      .then((result) => {
        var box;

        result.position();

        box = result.container.getBBox();
        result.container.transform(Snap.matrix()
          .translate(padding - box.x, padding - box.y));
        snap.attr({
          width: box.width + padding * 2,
          height: box.height + padding * 2
        });
      });
  }
}
{'fraction_non_alphanumeric': 0.1624305672445716, 'fraction_numerical': 0.00235650563878135, 'mean_word_length': 2.9040735873850196, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4358076', 'n_tokens_mistral': 1868, 'n_tokens_neox': 1825, 'n_words': 293}