export default class MicroClientRouter {
  constructor(options = {}) {
    this._routes = [];

    if (options.onload) {
      this.setLoadHandler();
    }
    if (options.onpopstate) {
      this.setPopStateHandler();
    }
  }

  route(path, callback) {
    let regexp = pathToRegexp(path);
    this._routes.push({ regexp, callback });

    return this;
  }

  emit(path) {
    this._routes.forEach((route) => {
      let matches = exec(route.regexp, path);
      if (matches) {
        route.callback(matches.params);
      }
    });
  }

  pushState(state = null, title = null, url) {
    history.pushState(state, title, url);
    this.emit(url);
  }

  setLoadHandler(callback) {
    window.addEventListener('load', () => {
      if(callback) {
        callback();
      }
      this.emit(location.pathname);
    });
  }

  setPopStateHandler(callback) {
    window.addEventListener('popstate', () => {
      if(callback) {
        callback();
      }
      this.emit(location.pathname);
    });
  }
}

const PATH_REGEXP = new RegExp([
  '(\\\\.)',
  '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^()])+)\\))?|\\(((?:\\\\.|[^()])+)\\))([+*?])?|(\\*))',
].join('|'), 'g');

export function parse(str) {
  const tokens = [];
  let index = 0;
  let path = '';
  let res;

  /*eslint-disable */
  while ((res = PATH_REGEXP.exec(str)) !== null) {
    let offset = res.index;

    path += str.slice(index, offset);
    index = offset + res[0].length;

    // if not exist path or empty string
    if (path) {
      tokens.push(path);
    }
    path = '';

    const token = {
      name: res[3],
      pattern: '[^/]+?',
    };
    tokens.push(token);
  }
  /*eslint-enable */

  if (index < str.length) {
    path += str.substr(index);
  }
  if (path) {
    tokens.push(path);
  }

  return tokens;
}

export function tokensToRegexp(tokens) {
  let route = '';
  const lastToken = tokens[tokens.length - 1];
  const endsWithSlash = (typeof lastToken === 'string' && /\/$/.test(lastToken));

  tokens.forEach((token) => {
    if (typeof token === 'string') {
      route += token;
    } else {
      let capture = token.pattern;

      capture = '/(' + capture + ')';
      route += capture;
    }
  });
  route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\/(?=$))?';
  route += '$';

  return new RegExp('^' + route, 'i');
}

export function pathToRegexp(path) {
  const tokens = parse(path);
  const regexp = tokensToRegexp(tokens);

  regexp.keys = [];
  tokens.forEach((token) => {
    if (typeof token !== 'string') {
      regexp.keys.push(token);
    }
  });

  return regexp;
}

export function _getParams(keys, matches) {
  const params = {};

  if (matches) {
    keys.forEach((key, index) => {
      if (!params[key.name]) {
        params[key.name] = [];
      }
      params[key.name].push(matches[index + 1]);
    });
  }
  return params;
}

export function exec(regexp, path) {
  const matches = regexp.exec(path);
  const params = _getParams(regexp.keys, matches);

  if (!matches) {
    return null;
  }

  matches.params = params;
  return matches;
}

@@ -1,1012 +1,4 @@
-export default class MicroClientRouter %7B%0A  constructor(options = %7B%7D) %7B%0A    this._routes = %5B%5D;%0A%0A    if (options.onload) %7B%0A      this.setLoadHandler();%0A    %7D%0A    if (options.onpopstate) %7B%0A      this.setPopStateHandler();%0A    %7D%0A  %7D%0A%0A  route(path, callback) %7B%0A    let regexp = pathToRegexp(path);%0A    this._routes.push(%7B regexp, callback %7D);%0A%0A    return this;%0A  %7D%0A%0A  emit(path) %7B%0A    this._routes.forEach((route) =%3E %7B%0A      let matches = exec(route.regexp, path);%0A      if (matches) %7B%0A        route.callback(matches.params);%0A      %7D%0A    %7D);%0A  %7D%0A%0A  pushState(state = null, title = null, url) %7B%0A    history.pushState(state, title, url);%0A    this.emit(url);%0A  %7D%0A%0A  setLoadHandler(callback) %7B%0A    window.addEventListener('load', () =%3E %7B%0A      if(callback) %7B%0A        callback();%0A      %7D%0A      this.emit(location.pathname);%0A    %7D);%0A  %7D%0A%0A  setPopStateHandler(callback) %7B%0A    window.addEventListener('popstate', () =%3E %7B%0A      if(callback) %7B%0A        callback();%0A      %7D%0A      this.emit(location.pathname);%0A    %7D);%0A  %7D%0A%7D%0A%0A
 cons
@@ -2055,12 +2055,1026 @@
  matches;%0A%7D%0A
+%0Aexport default class MicroClientRouter %7B%0A  constructor(options = %7B%7D) %7B%0A    this._routes = %5B%5D;%0A%0A    if (options.onload) %7B%0A      this.setLoadHandler();%0A    %7D%0A    if (options.onpopstate) %7B%0A      this.setPopStateHandler();%0A    %7D%0A  %7D%0A%0A  route(path, callback) %7B%0A    const regexp = pathToRegexp(path);%0A    this._routes.push(%7B regexp, callback %7D);%0A%0A    return this;%0A  %7D%0A%0A  emit(path) %7B%0A    this._routes.forEach((route) =%3E %7B%0A      const matches = exec(route.regexp, path);%0A      if (matches) %7B%0A        route.callback(matches.params);%0A      %7D%0A    %7D);%0A  %7D%0A%0A  pushState(state = null, title = null, url) %7B%0A    history.pushState(state, title, url);%0A    this.emit(url);%0A  %7D%0A%0A  setLoadHandler(callback) %7B%0A    window.addEventListener('load', () =%3E %7B%0A      if (callback) %7B%0A        callback();%0A      %7D%0A      this.emit(location.pathname);%0A    %7D);%0A  %7D%0A%0A  setPopStateHandler(callback) %7B%0A    window.addEventListener('popstate', () =%3E %7B%0A      if (callback) %7B%0A        callback();%0A      %7D%0A      this.emit(location.pathname);%0A    %7D);%0A  %7D%0A%7D%0A

Support eslint