$(function () { function APIError(code, message) { this.code = code; this.message = message; } APIError.prototype.toString = function () { return "APIError " + this.code + ": " + this.message; }; function default_api_error_handler(data, code) { throw new APIError(code, data); } function add_get_param(url, name, val) { name = encodeURIComponent(name); val = encodeURIComponent(val); var separator = url.match(/\?/) === null ? "?" : "&"; return url + separator + name + "=" + val; } function add_get_params(url, params) { for (var k in params) if (Object.prototype.hasOwnProperty.call(params, k)) url = add_get_param(url, k, params[k]); return url; } function query_api(method, url, arguments, handler, error_handler) { url = add_get_params(url, arguments); error_handler = error_handler || default_api_error_handler; var xhr = new XMLHttpRequest(); function wrap_handler(handler) { return function () { return handler(xhr.response, xhr.status, xhr); } } xhr.addEventListener("load", wrap_handler(handler)); xhr.addEventListener("error", wrap_handler(error_handler)); xhr.open(method, url); xhr.responseType = "json"; xhr.send(); } function Autocomplete() { var self = this; this.root = $("
") .addClass("autocomplete-root"); this.input = $("") .attr("type", "text") .appendTo(this.root) .on("keyup", function (ev) { if (ev.key === "ArrowDown") self.change_selection(1); if (ev.key === "ArrowUp") self.change_selection(-1); self.change_listener(); }) .on("keydown", function (ev) { if (ev.key === "Tab") { if (self.complete_current()) ev.preventDefault(); } }) .on("blur", function (ev) { var visible = false; if (ev.relatedTarget) { visible = $(ev.relatedTarget).closest(".autocomplete-root").get(0) === self.root.get(0); } self.set_visible(visible); }); this.options = $("