<commit_before>import 'lazysizes';

import moment from 'moment';

import TheatricsAPI from './api';
import Resolver from './resolver';
import Router from './router';
import * as handlers from './handlers';

import {capfirst} from '../utils/strings';
import {merge} from '../utils/objects';
import {isiOS} from '../utils/browser';
import {getMapURL} from '../utils/services';
import {
  formatURL,
  formatPriceRange,
  formatDuration,
  formatPhoneNumber
} from '../utils/formatting';
import {
  loader,
  bigLoader,
  preventBreakingRanges,
  unbreakable
} from '../utils/html';


export default class App {
  constructor() {
    this.api = new TheatricsAPI();

    this.resolver = new Resolver();
    this.resolver.addRoute('index', '/');
    this.resolver.addRoute('search', '/search/');
    this.resolver.addRoute('location', '/{location:[a-z\-]+}/');
    this.resolver.addRoute('event-list', '/{location:[a-z\-]+}/events/');
    this.resolver.addRoute(
      'event-list',
      '/{location:[a-z\-]+}/events/{date:\\d\\d\\d\\d-\\d\\d-\\d\\d}/');
    this.resolver.addRoute('place-list', '/{location:[a-z\-]+}/places/');
    this.resolver.addRoute('event', '/event/{id:\\d+}/');
    this.resolver.addRoute('place', '/place/{id:\\d+}/');

    this.router = new Router();
    this.router.addHandler('index', handlers.index);
    this.router.addHandler('search', handlers.search);
    this.router.addHandler('location', handlers.location);
    this.router.addHandler('event-list', handlers.eventList);
    this.router.addHandler('place-list', handlers.placeList);
    this.router.addHandler('event', handlers.event);
    this.router.addHandler('place', handlers.place);
    this.router.setNotFoundHandler(handlers.notFound);

    this.templateContext = {
      loader,
      bigLoader,
      moment,
      capfirst,
      isiOS,
      getMapURL,
      formatURL,
      formatPriceRange,
      formatDuration,
      formatPhoneNumber,
      preventBreakingRanges,
      unbreakable,
      merge,
      url: this.url.bind(this),
      app: this,
    }
  }

  url(name, args) {
    return this.resolver.reverse(name, args);
  }

  renderTemplate(template, context={}) {
    return template(merge(this.templateContext, context));
  }
}<commit_msg>Support query strings in App.url helper<commit_after>import 'lazysizes';

import moment from 'moment';
import qs from 'query-string';

import TheatricsAPI from './api';
import Resolver from './resolver';
import Router from './router';
import * as handlers from './handlers';

import {capfirst} from '../utils/strings';
import {merge} from '../utils/objects';
import {isiOS} from '../utils/browser';
import {getMapURL} from '../utils/services';
import {
  formatURL,
  formatPriceRange,
  formatDuration,
  formatPhoneNumber
} from '../utils/formatting';
import {
  loader,
  bigLoader,
  preventBreakingRanges,
  unbreakable
} from '../utils/html';


export default class App {
  constructor() {
    this.api = new TheatricsAPI();

    this.resolver = new Resolver();
    this.resolver.addRoute('index', '/');
    this.resolver.addRoute('search', '/search/');
    this.resolver.addRoute('location', '/{location:[a-z\-]+}/');
    this.resolver.addRoute('event-list', '/{location:[a-z\-]+}/events/');
    this.resolver.addRoute(
      'event-list',
      '/{location:[a-z\-]+}/events/{date:\\d\\d\\d\\d-\\d\\d-\\d\\d}/');
    this.resolver.addRoute('place-list', '/{location:[a-z\-]+}/places/');
    this.resolver.addRoute('event', '/event/{id:\\d+}/');
    this.resolver.addRoute('place', '/place/{id:\\d+}/');

    this.router = new Router();
    this.router.addHandler('index', handlers.index);
    this.router.addHandler('search', handlers.search);
    this.router.addHandler('location', handlers.location);
    this.router.addHandler('event-list', handlers.eventList);
    this.router.addHandler('place-list', handlers.placeList);
    this.router.addHandler('event', handlers.event);
    this.router.addHandler('place', handlers.place);
    this.router.setNotFoundHandler(handlers.notFound);

    this.templateContext = {
      loader,
      bigLoader,
      moment,
      capfirst,
      isiOS,
      getMapURL,
      formatURL,
      formatPriceRange,
      formatDuration,
      formatPhoneNumber,
      preventBreakingRanges,
      unbreakable,
      merge,
      url: this.url.bind(this),
      app: this,
    }
  }

  url(name, args, query=null) {
    const path = this.resolver.reverse(name, args);
    if (query) {
      return `${path}?${qs.stringify(query)}`;
    } else {
      return path;
    }
  }

  renderTemplate(template, context={}) {
    return template(merge(this.templateContext, context));
  }
}
{'fraction_non_alphanumeric': 0.18006430868167203, 'fraction_numerical': 0.0, 'mean_word_length': 3.2925482980680774, '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': '5288405', 'n_tokens_mistral': 1518, 'n_tokens_neox': 1476, 'n_words': 270}