(function () {
	globalThis.__alerts = [];
	globalThis.__confirms = [];
	globalThis.__confirmYes = true;
	globalThis.__copied = null;
	globalThis.__assigned = null;
	globalThis.__reloaded = false;
	globalThis.__lastScrolled = null;
	globalThis.__preloaded = [];
	globalThis.__requests = [];
	globalThis.__routes = [];
	globalThis.__pending = [];
	globalThis.__timers = [];
	globalThis.__raf = [];
	globalThis.__loaded = {};
	globalThis.__failSetData = false;
	globalThis.__failGetData = false;

	window.alert = function (msg) {
		globalThis.__alerts.push(String(msg));
	};
	window.confirm = function (msg) {
		globalThis.__confirms.push(String(msg));
		return !!globalThis.__confirmYes;
	};

	var clipboard = {
		writeText: function (text) {
			globalThis.__copied = String(text);
			return Promise.resolve();
		},
	};
	globalThis.__clipboard = clipboard;
	try {
		Object.defineProperty(navigator, "clipboard", {
			configurable: true,
			get: function () {
				return globalThis.__clipboard;
			},
			set: function (value) {
				globalThis.__clipboard = value;
			},
		});
	} catch (err) {
		navigator.clipboard = clipboard;
	}

	var nativeReady = Object.getOwnPropertyDescriptor(Document.prototype, "readyState");
	var readyOverride = null;
	Object.defineProperty(document, "readyState", {
		configurable: true,
		get: function () {
			return readyOverride == null ? nativeReady.get.call(document) : readyOverride;
		},
		set: function (value) {
			readyOverride = value;
		},
	});

	try {
		var scrollX = 0;
		var scrollY = 0;
		Object.defineProperty(window, "scrollX", {
			configurable: true,
			get: function () {
				return scrollX;
			},
			set: function (value) {
				scrollX = Number(value);
			},
		});
		Object.defineProperty(window, "scrollY", {
			configurable: true,
			get: function () {
				return scrollY;
			},
			set: function (value) {
				scrollY = Number(value);
			},
		});
	} catch (err) {
		/* scroll offsets stay at the browser default */
	}

	var nativeRect = Element.prototype.getBoundingClientRect;
	Element.prototype.getBoundingClientRect = function () {
		var rect = nativeRect.call(this);
		if (this._rectTop == null) return rect;
		var top = Number(this._rectTop);
		return {
			x: rect.x,
			y: top,
			top: top,
			left: rect.left,
			right: rect.right,
			bottom: top + rect.height,
			width: rect.width,
			height: rect.height,
		};
	};
	Element.prototype.scrollIntoView = function () {
		globalThis.__lastScrolled = this;
	};

	var srcDesc = Object.getOwnPropertyDescriptor(HTMLImageElement.prototype, "src");
	Object.defineProperty(HTMLImageElement.prototype, "src", {
		configurable: true,
		get: function () {
			if (Object.prototype.hasOwnProperty.call(this, "_src")) return this._src;
			return srcDesc.get.call(this);
		},
		set: function (value) {
			Object.defineProperty(this, "_src", { value: String(value), writable: true, configurable: true });
		},
	});

	var filesDesc = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "files");
	Object.defineProperty(HTMLInputElement.prototype, "files", {
		configurable: true,
		get: function () {
			if (Object.prototype.hasOwnProperty.call(this, "_files")) return this._files;
			return filesDesc.get.call(this);
		},
		set: function (value) {
			Object.defineProperty(this, "_files", { value: value, writable: true, configurable: true });
		},
	});

	function DataTransferShim() {
		this._data = {};
		this.effectAllowed = "all";
		this.dropEffect = "none";
		this.types = [];
		this.files = [];
	}
	DataTransferShim.prototype.setData = function (type, value) {
		if (globalThis.__failSetData) throw new Error("setData failed");
		this._data[type] = String(value);
		if (this.types.indexOf(type) < 0) this.types.push(type);
	};
	DataTransferShim.prototype.getData = function (type) {
		if (globalThis.__failGetData) throw new Error("getData failed");
		return this._data[type] || "";
	};

	var NativeEvent = window.Event;
	function Event(type, init) {
		var event = new NativeEvent(type, init);
		if (/^drag|^drop/.test(type)) {
			Object.defineProperty(event, "dataTransfer", {
				value: new DataTransferShim(),
				writable: true,
				configurable: true,
			});
		}
		return event;
	}
	Event.prototype = NativeEvent.prototype;
	window.Event = Event;

	var NativeImage = window.Image;
	window.Image = function Image() {
		var img = new NativeImage();
		var src = "";
		Object.defineProperty(img, "src", {
			configurable: true,
			get: function () {
				return src;
			},
			set: function (value) {
				src = String(value);
				globalThis.__preloaded.push(src);
			},
		});
		return img;
	};

	window.setTimeout = function (fn) {
		var id = globalThis.__timers.length + 1;
		globalThis.__timers.push({ id: id, fn: fn });
		return id;
	};
	window.clearTimeout = function (id) {
		globalThis.__timers = globalThis.__timers.filter(function (item) {
			return item.id !== id;
		});
	};
	window.requestAnimationFrame = function (fn) {
		globalThis.__raf.push(fn);
		return globalThis.__raf.length;
	};
	globalThis.__tick = function () {
		var guard = 0;
		while ((globalThis.__timers.length || globalThis.__raf.length) && guard++ < 1000) {
			var timers = globalThis.__timers.splice(0, globalThis.__timers.length);
			var raf = globalThis.__raf.splice(0, globalThis.__raf.length);
			timers.forEach(function (item) {
				item.fn();
			});
			raf.forEach(function (fn) {
				fn();
			});
		}
	};

	function abortError() {
		var err = new Error("The operation was aborted");
		err.name = "AbortError";
		return err;
	}
	function responseOf(spec) {
		var body = spec.text != null ? spec.text : spec.json != null ? JSON.stringify(spec.json) : "";
		return {
			ok: spec.status >= 200 && spec.status < 300,
			status: spec.status,
			statusText: spec.statusText || "",
			json: function () {
				if (spec.jsonError) return Promise.reject(new Error("invalid json"));
				return Promise.resolve(spec.json != null ? spec.json : JSON.parse(body || "null"));
			},
			text: function () {
				return Promise.resolve(body);
			},
		};
	}
	function matchRoute(url) {
		var found = null;
		for (var i = 0; i < globalThis.__routes.length; i++) {
			if (url.indexOf(globalThis.__routes[i].url) >= 0) found = globalThis.__routes[i];
		}
		return found;
	}
	globalThis.fetch = function (url, opts) {
		opts = opts || {};
		globalThis.__requests.push({
			url: String(url),
			method: (opts && opts.method) || "GET",
			body: opts && opts.body != null ? String(opts.body) : null,
		});
		if (opts.signal && opts.signal.aborted) return Promise.reject(abortError());
		var spec = matchRoute(String(url));
		if (spec && spec.reject) return Promise.reject(new Error(String(spec.reject)));
		if (!spec || spec.hang) {
			return new Promise(function (resolve, reject) {
				globalThis.__pending.push({ url: String(url), resolve: resolve, reject: reject, signal: opts.signal });
				if (opts.signal) opts.signal.addEventListener("abort", function () {
					reject(abortError());
				});
			});
		}
		return Promise.resolve(responseOf(spec));
	};
	globalThis.__fulfill = function (urlPart, spec) {
		var rest = [];
		globalThis.__pending.forEach(function (item) {
			if (item.url.indexOf(urlPart) >= 0) item.resolve(responseOf(spec));
			else rest.push(item);
		});
		globalThis.__pending = rest;
	};

	globalThis.__drain = async function () {
		for (var i = 0; i < 40; i++) await Promise.resolve();
	};
	globalThis.__run = async function (source) {
		(0, eval)(source);
		await globalThis.__drain();
	};
	globalThis.__value = function (expression) {
		var value = (0, eval)("(" + expression + ")");
		if (value === undefined) return null;
		return JSON.parse(JSON.stringify(value));
	};
	globalThis.__settle = async function (expression) {
		try {
			var value = await Promise.resolve((0, eval)("(" + expression + ")"));
			await globalThis.__drain();
			if (value === undefined) value = null;
			return JSON.parse(JSON.stringify(value));
		} catch (err) {
			return { __error: String((err && err.message) || err) };
		}
	};
	globalThis.__setHtml = function (html) {
		document.body.innerHTML = html;
	};
	globalThis.__setLocation = function (path) {
		history.replaceState(null, "", path);
	};
	globalThis.__load = async function (names) {
		for (var i = 0; i < names.length; i++) {
			globalThis.__loaded[names[i]] = await import("/static/" + names[i]);
		}
		await globalThis.__drain();
	};
	globalThis.__click = async function (selector) {
		document.querySelector(selector).click();
		await globalThis.__drain();
	};
	globalThis.__dispatch = async function (selector, type, fields) {
		var event = new Event(type, { bubbles: true, cancelable: true });
		if (fields) {
			for (var key in fields) {
				if (key === "bubbles" || key === "cancelable") continue;
				try {
					event[key] = fields[key];
				} catch (err) {
					/* native event fields that reject assignment */
				}
			}
		}
		document.querySelector(selector).dispatchEvent(event);
		await globalThis.__drain();
	};
	globalThis.__dispatchWindow = async function (type) {
		window.dispatchEvent(new Event(type, { bubbles: true }));
		await globalThis.__drain();
	};
	globalThis.__setProp = function (selector, name, value) {
		document.querySelector(selector)[name] = value;
	};
})();