(function() {
  const api = 'https://emojityper.appspot.com';

  function historicData() {
    let p = window.fetch('https://emojityper.com/res/js/emojimap.js').then(out => out.text());
    p = p.then(raw => {
      const first = raw.indexOf('{');
      if (first === -1) {
        throw new Error('invalid emojimap');
      }
      // FIXME: never do this :O
      eval('var emojimap = ' + raw.substr(first));
      return emojimap;
    })
    p = p.then(emojimap => {
      const out = {};
      Object.keys(emojimap).forEach(string => {
        const data = emojimap[string];
        string = string.replace(/\s+/g, '').replace(/[’’]/g, '');
        if (!(string in out)) {
          out[string] = [];
        }
        out[string].push(...data.map(details => details.emoji));
      });

      return out;
    });
    return p;
  }

  function futureData() {
    return window.fetch(api + '/popular').then(out => out.json());
  }

  // const data = historicData();
  const data = futureData();
  let indexed = data.then(emojimap => {
    const prefixLength = 3;   // generate prefixes up to this length
    const maxSuggestions = 10;  // only generate this many suggestions
    const prefixSuggest = {};

    for (let k in emojimap) {
      const prefix = k.substr(0, prefixLength);
      for (let i = 1; i <= prefix.length; ++i) {
        const part = prefix.substr(0, i);
        let opts = prefixSuggest[part];
        if (!opts) {
          opts = prefixSuggest[part] = [];
        }
        if (opts.length < maxSuggestions) {
          opts.push(k);
        }
      }
    }

    return function(typed) {
      typed = typed.toLowerCase();
      const rest = typed.substr(prefixLength);
      let all = prefixSuggest[typed.substr(0, prefixLength)] || [];

      if (rest) {
        all = all.filter(word => word.substr(prefixLength).startsWith(rest));
      }
      all = all.map(word => {
        return {'name': word, 'options': emojimap[word] || []};
      });

      return all.length ? all : null;
    }
  });

  let timeout;  // timeout handler for secondary query
  const performRequest = (text, prefix) => {
    window.clearTimeout(timeout);
    if (!text) {
      window.emojimanager.callback(null);
      return;
    }

    function send(out) {
      if (timeout === localTimeout) {
        window.emojimanager.callback(out);
      } else {
        console.debug('got results for old query');
      }
    }

    // TODO: only send extra query if there's not enough results, or the user hits 'more'
    localTimeout = window.setTimeout(_ => {
      const data = new FormData();
      data.append('q', text);
      data.append('prefix', prefix)
      window.fetch(api + '/query', {method: 'POST', data}).then(out => out.json()).then(send);
    }, 2000);
    timeout = localTimeout;

    indexed.then(suggest => {
      let results = suggest(text);
      if (!prefix) {
        results = results.filter(result => result['name'] === text);
      }
      send(results);
    });
  };

  window.emojimanager = {
    callback: function() {},
    request(text, prefix) {
      window.requestAnimationFrame(_ => {
        performRequest(text, prefix);
      });
    },
    submit(name, value) {
      // const data = `name=${name}&emoji=${value}`;
      // FIXME: we've disabled CORS for now; the response is actually useless anyway
      // FIXME: however, the client now thinks this succeeds even when it 400s
      const data = new FormData();
      data.append('name', name);
      data.append('emoji', value);
      return window.fetch(api + '/name', {method: 'POST', data, mode: 'no-cors'});
    },
  };

}())


@@ -62,937 +62,74 @@
 %0A%0A  
-function historicData() %7B%0A    let p = window.fetch('https://emojityper.com/res/js/emojimap.js').then(out =%3E out.text());%0A    p = p.then(raw =%3E %7B%0A      const first = raw.indexOf('%7B');%0A      if (first === -1) %7B%0A        throw new Error('invalid emojimap');%0A      %7D%0A      // FIXME: never do this :O%0A      eval('var emojimap = ' + raw.substr(first));%0A      return emojimap;%0A    %7D)%0A    p = p.then(emojimap =%3E %7B%0A      const out = %7B%7D;%0A      Object.keys(emojimap).forEach(string =%3E %7B%0A        const data = emojimap%5Bstring%5D;%0A        string = string.replace(/%5Cs+/g, '').replace(/%5B%E2%80%99%E2%80%99%5D/g, '');%0A        if (!(string in out)) %7B%0A          out%5Bstring%5D = %5B%5D;%0A        %7D%0A        out%5Bstring%5D.push(...data.map(details =%3E details.emoji));%0A      %7D);%0A%0A      return out;%0A    %7D);%0A    return p;%0A  %7D%0A%0A  function futureData() %7B%0A    return window.fetch(api + '/popular').then(out =%3E out.json());%0A  %7D%0A%0A  // const data = historicData();%0A  const data = futureData(
+const data = window.fetch(api + '/popular').then(out =%3E out.json()
 );%0A 

remove historic data