Quizlet Live Cheat

Allows you to automatically answer questions in Quizlet Live

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        Quizlet Live Cheat
// @description Allows you to automatically answer questions in Quizlet Live
// @namespace   https://github.com/feketefh/QuizletLiveCheat
// @match       https://quizlet.com/live*
// @run-at      document-start
// @iconURL     https://assets.quizlet.com/a/j/dist/app/i/logo/2021/q-twilight.e27821d9baad165.png
// @author      feketefh
// @version     0.6.2
// @license     MIT
// @grant       unsafeWindow
// ==/UserScript==
(function () {
	'use strict';

	/** @returns {void} */
	function noop() {}

	function run(fn) {
		return fn();
	}

	function blank_object() {
		return Object.create(null);
	}

	/**
	 * @param {Function[]} fns
	 * @returns {void}
	 */
	function run_all(fns) {
		fns.forEach(run);
	}

	/**
	 * @param {any} thing
	 * @returns {thing is Function}
	 */
	function is_function(thing) {
		return typeof thing === 'function';
	}

	/** @returns {boolean} */
	function safe_not_equal(a, b) {
		return a != a ? b == b : a !== b || (a && typeof a === 'object') || typeof a === 'function';
	}

	/** @returns {boolean} */
	function is_empty(obj) {
		return Object.keys(obj).length === 0;
	}

	/**
	 * @param {Node} target
	 * @param {Node} node
	 * @returns {void}
	 */
	function append(target, node) {
		target.appendChild(node);
	}

	/**
	 * @param {Node} target
	 * @param {string} style_sheet_id
	 * @param {string} styles
	 * @returns {void}
	 */
	function append_styles(target, style_sheet_id, styles) {
		const append_styles_to = get_root_for_style(target);
		if (!append_styles_to.getElementById(style_sheet_id)) {
			const style = element('style');
			style.id = style_sheet_id;
			style.textContent = styles;
			append_stylesheet(append_styles_to, style);
		}
	}

	/**
	 * @param {Node} node
	 * @returns {ShadowRoot | Document}
	 */
	function get_root_for_style(node) {
		if (!node) return document;
		const root = node.getRootNode ? node.getRootNode() : node.ownerDocument;
		if (root && /** @type {ShadowRoot} */ (root).host) {
			return /** @type {ShadowRoot} */ (root);
		}
		return node.ownerDocument;
	}

	/**
	 * @param {ShadowRoot | Document} node
	 * @param {HTMLStyleElement} style
	 * @returns {CSSStyleSheet}
	 */
	function append_stylesheet(node, style) {
		append(/** @type {Document} */ (node).head || node, style);
		return style.sheet;
	}

	/**
	 * @param {Node} target
	 * @param {Node} node
	 * @param {Node} [anchor]
	 * @returns {void}
	 */
	function insert(target, node, anchor) {
		target.insertBefore(node, anchor || null);
	}

	/**
	 * @param {Node} node
	 * @returns {void}
	 */
	function detach(node) {
		if (node.parentNode) {
			node.parentNode.removeChild(node);
		}
	}

	/**
	 * @template {keyof HTMLElementTagNameMap} K
	 * @param {K} name
	 * @returns {HTMLElementTagNameMap[K]}
	 */
	function element(name) {
		return document.createElement(name);
	}

	/**
	 * @param {string} data
	 * @returns {Text}
	 */
	function text(data) {
		return document.createTextNode(data);
	}

	/**
	 * @returns {Text} */
	function space() {
		return text(' ');
	}

	/**
	 * @param {EventTarget} node
	 * @param {string} event
	 * @param {EventListenerOrEventListenerObject} handler
	 * @param {boolean | AddEventListenerOptions | EventListenerOptions} [options]
	 * @returns {() => void}
	 */
	function listen(node, event, handler, options) {
		node.addEventListener(event, handler, options);
		return () => node.removeEventListener(event, handler, options);
	}

	/**
	 * @returns {(event: any) => any} */
	function prevent_default(fn) {
		return function (event) {
			event.preventDefault();
			// @ts-ignore
			return fn.call(this, event);
		};
	}

	/**
	 * @returns {(event: any) => any} */
	function stop_propagation(fn) {
		return function (event) {
			event.stopPropagation();
			// @ts-ignore
			return fn.call(this, event);
		};
	}

	/**
	 * @param {Element} node
	 * @param {string} attribute
	 * @param {string} [value]
	 * @returns {void}
	 */
	function attr(node, attribute, value) {
		if (value == null) node.removeAttribute(attribute);
		else if (node.getAttribute(attribute) !== value) node.setAttribute(attribute, value);
	}

	/**
	 * @param {Element} element
	 * @returns {ChildNode[]}
	 */
	function children(element) {
		return Array.from(element.childNodes);
	}

	/**
	 * @param {Text} text
	 * @param {unknown} data
	 * @returns {void}
	 */
	function set_data(text, data) {
		data = '' + data;
		if (text.data === data) return;
		text.data = /** @type {string} */ (data);
	}

	/**
	 * @returns {void} */
	function set_style(node, key, value, important) {
		if (value == null) {
			node.style.removeProperty(key);
		} else {
			node.style.setProperty(key, value, '');
		}
	}

	/**
	 * @template T
	 * @param {string} type
	 * @param {T} [detail]
	 * @param {{ bubbles?: boolean, cancelable?: boolean }} [options]
	 * @returns {CustomEvent<T>}
	 */
	function custom_event(type, detail, { bubbles = false, cancelable = false } = {}) {
		return new CustomEvent(type, { detail, bubbles, cancelable });
	}

	/**
	 * @typedef {Node & {
	 * 	claim_order?: number;
	 * 	hydrate_init?: true;
	 * 	actual_end_child?: NodeEx;
	 * 	childNodes: NodeListOf<NodeEx>;
	 * }} NodeEx
	 */

	/** @typedef {ChildNode & NodeEx} ChildNodeEx */

	/** @typedef {NodeEx & { claim_order: number }} NodeEx2 */

	/**
	 * @typedef {ChildNodeEx[] & {
	 * 	claim_info?: {
	 * 		last_index: number;
	 * 		total_claimed: number;
	 * 	};
	 * }} ChildNodeArray
	 */

	let current_component;

	/** @returns {void} */
	function set_current_component(component) {
		current_component = component;
	}

	function get_current_component() {
		if (!current_component) throw new Error('Function called outside component initialization');
		return current_component;
	}

	/**
	 * The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM.
	 * It must be called during the component's initialisation (but doesn't need to live *inside* the component;
	 * it can be called from an external module).
	 *
	 * If a function is returned _synchronously_ from `onMount`, it will be called when the component is unmounted.
	 *
	 * `onMount` does not run inside a [server-side component](https://svelte.dev/docs#run-time-server-side-component-api).
	 *
	 * https://svelte.dev/docs/svelte#onmount
	 * @template T
	 * @param {() => import('./private.js').NotFunction<T> | Promise<import('./private.js').NotFunction<T>> | (() => any)} fn
	 * @returns {void}
	 */
	function onMount(fn) {
		get_current_component().$$.on_mount.push(fn);
	}

	/**
	 * Schedules a callback to run immediately before the component is unmounted.
	 *
	 * Out of `onMount`, `beforeUpdate`, `afterUpdate` and `onDestroy`, this is the
	 * only one that runs inside a server-side component.
	 *
	 * https://svelte.dev/docs/svelte#ondestroy
	 * @param {() => any} fn
	 * @returns {void}
	 */
	function onDestroy(fn) {
		get_current_component().$$.on_destroy.push(fn);
	}

	/**
	 * Creates an event dispatcher that can be used to dispatch [component events](https://svelte.dev/docs#template-syntax-component-directives-on-eventname).
	 * Event dispatchers are functions that can take two arguments: `name` and `detail`.
	 *
	 * Component events created with `createEventDispatcher` create a
	 * [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent).
	 * These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture).
	 * The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail)
	 * property and can contain any type of data.
	 *
	 * The event dispatcher can be typed to narrow the allowed event names and the type of the `detail` argument:
	 * ```ts
	 * const dispatch = createEventDispatcher<{
	 *  loaded: never; // does not take a detail argument
	 *  change: string; // takes a detail argument of type string, which is required
	 *  optional: number | null; // takes an optional detail argument of type number
	 * }>();
	 * ```
	 *
	 * https://svelte.dev/docs/svelte#createeventdispatcher
	 * @template {Record<string, any>} [EventMap=any]
	 * @returns {import('./public.js').EventDispatcher<EventMap>}
	 */
	function createEventDispatcher() {
		const component = get_current_component();
		return (type, detail, { cancelable = false } = {}) => {
			const callbacks = component.$$.callbacks[type];
			if (callbacks) {
				// TODO are there situations where events could be dispatched
				// in a server (non-DOM) environment?
				const event = custom_event(/** @type {string} */ (type), detail, { cancelable });
				callbacks.slice().forEach((fn) => {
					fn.call(component, event);
				});
				return !event.defaultPrevented;
			}
			return true;
		};
	}

	// TODO figure out if we still want to support
	// shorthand events, or if we want to implement
	// a real bubbling mechanism
	/**
	 * @param component
	 * @param event
	 * @returns {void}
	 */
	function bubble(component, event) {
		const callbacks = component.$$.callbacks[event.type];
		if (callbacks) {
			// @ts-ignore
			callbacks.slice().forEach((fn) => fn.call(this, event));
		}
	}

	const dirty_components = [];
	const binding_callbacks = [];

	let render_callbacks = [];

	const flush_callbacks = [];

	const resolved_promise = /* @__PURE__ */ Promise.resolve();

	let update_scheduled = false;

	/** @returns {void} */
	function schedule_update() {
		if (!update_scheduled) {
			update_scheduled = true;
			resolved_promise.then(flush);
		}
	}

	/** @returns {void} */
	function add_render_callback(fn) {
		render_callbacks.push(fn);
	}

	// flush() calls callbacks in this order:
	// 1. All beforeUpdate callbacks, in order: parents before children
	// 2. All bind:this callbacks, in reverse order: children before parents.
	// 3. All afterUpdate callbacks, in order: parents before children. EXCEPT
	//    for afterUpdates called during the initial onMount, which are called in
	//    reverse order: children before parents.
	// Since callbacks might update component values, which could trigger another
	// call to flush(), the following steps guard against this:
	// 1. During beforeUpdate, any updated components will be added to the
	//    dirty_components array and will cause a reentrant call to flush(). Because
	//    the flush index is kept outside the function, the reentrant call will pick
	//    up where the earlier call left off and go through all dirty components. The
	//    current_component value is saved and restored so that the reentrant call will
	//    not interfere with the "parent" flush() call.
	// 2. bind:this callbacks cannot trigger new flush() calls.
	// 3. During afterUpdate, any updated components will NOT have their afterUpdate
	//    callback called a second time; the seen_callbacks set, outside the flush()
	//    function, guarantees this behavior.
	const seen_callbacks = new Set();

	let flushidx = 0; // Do *not* move this inside the flush() function

	/** @returns {void} */
	function flush() {
		// Do not reenter flush while dirty components are updated, as this can
		// result in an infinite loop. Instead, let the inner flush handle it.
		// Reentrancy is ok afterwards for bindings etc.
		if (flushidx !== 0) {
			return;
		}
		const saved_component = current_component;
		do {
			// first, call beforeUpdate functions
			// and update components
			try {
				while (flushidx < dirty_components.length) {
					const component = dirty_components[flushidx];
					flushidx++;
					set_current_component(component);
					update(component.$$);
				}
			} catch (e) {
				// reset dirty state to not end up in a deadlocked state and then rethrow
				dirty_components.length = 0;
				flushidx = 0;
				throw e;
			}
			set_current_component(null);
			dirty_components.length = 0;
			flushidx = 0;
			while (binding_callbacks.length) binding_callbacks.pop()();
			// then, once components are updated, call
			// afterUpdate functions. This may cause
			// subsequent updates...
			for (let i = 0; i < render_callbacks.length; i += 1) {
				const callback = render_callbacks[i];
				if (!seen_callbacks.has(callback)) {
					// ...so guard against infinite loops
					seen_callbacks.add(callback);
					callback();
				}
			}
			render_callbacks.length = 0;
		} while (dirty_components.length);
		while (flush_callbacks.length) {
			flush_callbacks.pop()();
		}
		update_scheduled = false;
		seen_callbacks.clear();
		set_current_component(saved_component);
	}

	/** @returns {void} */
	function update($$) {
		if ($$.fragment !== null) {
			$$.update();
			run_all($$.before_update);
			const dirty = $$.dirty;
			$$.dirty = [-1];
			$$.fragment && $$.fragment.p($$.ctx, dirty);
			$$.after_update.forEach(add_render_callback);
		}
	}

	/**
	 * Useful for example to execute remaining `afterUpdate` callbacks before executing `destroy`.
	 * @param {Function[]} fns
	 * @returns {void}
	 */
	function flush_render_callbacks(fns) {
		const filtered = [];
		const targets = [];
		render_callbacks.forEach((c) => (fns.indexOf(c) === -1 ? filtered.push(c) : targets.push(c)));
		targets.forEach((c) => c());
		render_callbacks = filtered;
	}

	const outroing = new Set();

	/**
	 * @param {import('./private.js').Fragment} block
	 * @param {0 | 1} [local]
	 * @returns {void}
	 */
	function transition_in(block, local) {
		if (block && block.i) {
			outroing.delete(block);
			block.i(local);
		}
	}

	/** @typedef {1} INTRO */
	/** @typedef {0} OUTRO */
	/** @typedef {{ direction: 'in' | 'out' | 'both' }} TransitionOptions */
	/** @typedef {(node: Element, params: any, options: TransitionOptions) => import('../transition/public.js').TransitionConfig} TransitionFn */

	/**
	 * @typedef {Object} Outro
	 * @property {number} r
	 * @property {Function[]} c
	 * @property {Object} p
	 */

	/**
	 * @typedef {Object} PendingProgram
	 * @property {number} start
	 * @property {INTRO|OUTRO} b
	 * @property {Outro} [group]
	 */

	/**
	 * @typedef {Object} Program
	 * @property {number} a
	 * @property {INTRO|OUTRO} b
	 * @property {1|-1} d
	 * @property {number} duration
	 * @property {number} start
	 * @property {number} end
	 * @property {Outro} [group]
	 */

	/** @returns {void} */
	function mount_component(component, target, anchor) {
		const { fragment, after_update } = component.$$;
		fragment && fragment.m(target, anchor);
		// onMount happens before the initial afterUpdate
		add_render_callback(() => {
			const new_on_destroy = component.$$.on_mount.map(run).filter(is_function);
			// if the component was destroyed immediately
			// it will update the `$$.on_destroy` reference to `null`.
			// the destructured on_destroy may still reference to the old array
			if (component.$$.on_destroy) {
				component.$$.on_destroy.push(...new_on_destroy);
			} else {
				// Edge case - component was destroyed immediately,
				// most likely as a result of a binding initialising
				run_all(new_on_destroy);
			}
			component.$$.on_mount = [];
		});
		after_update.forEach(add_render_callback);
	}

	/** @returns {void} */
	function destroy_component(component, detaching) {
		const $$ = component.$$;
		if ($$.fragment !== null) {
			flush_render_callbacks($$.after_update);
			run_all($$.on_destroy);
			$$.fragment && $$.fragment.d(detaching);
			// TODO null out other refs, including component.$$ (but need to
			// preserve final state?)
			$$.on_destroy = $$.fragment = null;
			$$.ctx = [];
		}
	}

	/** @returns {void} */
	function make_dirty(component, i) {
		if (component.$$.dirty[0] === -1) {
			dirty_components.push(component);
			schedule_update();
			component.$$.dirty.fill(0);
		}
		component.$$.dirty[(i / 31) | 0] |= 1 << i % 31;
	}

	// TODO: Document the other params
	/**
	 * @param {SvelteComponent} component
	 * @param {import('./public.js').ComponentConstructorOptions} options
	 *
	 * @param {import('./utils.js')['not_equal']} not_equal Used to compare props and state values.
	 * @param {(target: Element | ShadowRoot) => void} [append_styles] Function that appends styles to the DOM when the component is first initialised.
	 * This will be the `add_css` function from the compiled component.
	 *
	 * @returns {void}
	 */
	function init(
		component,
		options,
		instance,
		create_fragment,
		not_equal,
		props,
		append_styles = null,
		dirty = [-1]
	) {
		const parent_component = current_component;
		set_current_component(component);
		/** @type {import('./private.js').T$$} */
		const $$ = (component.$$ = {
			fragment: null,
			ctx: [],
			// state
			props,
			update: noop,
			not_equal,
			bound: blank_object(),
			// lifecycle
			on_mount: [],
			on_destroy: [],
			on_disconnect: [],
			before_update: [],
			after_update: [],
			context: new Map(options.context || (parent_component ? parent_component.$$.context : [])),
			// everything else
			callbacks: blank_object(),
			dirty,
			skip_bound: false,
			root: options.target || parent_component.$$.root
		});
		append_styles && append_styles($$.root);
		let ready = false;
		$$.ctx = instance
			? instance(component, options.props || {}, (i, ret, ...rest) => {
					const value = rest.length ? rest[0] : ret;
					if ($$.ctx && not_equal($$.ctx[i], ($$.ctx[i] = value))) {
						if (!$$.skip_bound && $$.bound[i]) $$.bound[i](value);
						if (ready) make_dirty(component, i);
					}
					return ret;
			  })
			: [];
		$$.update();
		ready = true;
		run_all($$.before_update);
		// `false` as a special case of no DOM component
		$$.fragment = create_fragment ? create_fragment($$.ctx) : false;
		if (options.target) {
			if (options.hydrate) {
				// TODO: what is the correct type here?
				// @ts-expect-error
				const nodes = children(options.target);
				$$.fragment && $$.fragment.l(nodes);
				nodes.forEach(detach);
			} else {
				// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
				$$.fragment && $$.fragment.c();
			}
			if (options.intro) transition_in(component.$$.fragment);
			mount_component(component, options.target, options.anchor);
			flush();
		}
		set_current_component(parent_component);
	}

	/**
	 * Base class for Svelte components. Used when dev=false.
	 *
	 * @template {Record<string, any>} [Props=any]
	 * @template {Record<string, any>} [Events=any]
	 */
	class SvelteComponent {
		/**
		 * ### PRIVATE API
		 *
		 * Do not use, may change at any time
		 *
		 * @type {any}
		 */
		$$ = undefined;
		/**
		 * ### PRIVATE API
		 *
		 * Do not use, may change at any time
		 *
		 * @type {any}
		 */
		$$set = undefined;

		/** @returns {void} */
		$destroy() {
			destroy_component(this, 1);
			this.$destroy = noop;
		}

		/**
		 * @template {Extract<keyof Events, string>} K
		 * @param {K} type
		 * @param {((e: Events[K]) => void) | null | undefined} callback
		 * @returns {() => void}
		 */
		$on(type, callback) {
			if (!is_function(callback)) {
				return noop;
			}
			const callbacks = this.$$.callbacks[type] || (this.$$.callbacks[type] = []);
			callbacks.push(callback);
			return () => {
				const index = callbacks.indexOf(callback);
				if (index !== -1) callbacks.splice(index, 1);
			};
		}

		/**
		 * @param {Partial<Props>} props
		 * @returns {void}
		 */
		$set(props) {
			if (this.$$set && !is_empty(props)) {
				this.$$.skip_bound = true;
				this.$$set(props);
				this.$$.skip_bound = false;
			}
		}
	}

	/**
	 * @typedef {Object} CustomElementPropDefinition
	 * @property {string} [attribute]
	 * @property {boolean} [reflect]
	 * @property {'String'|'Boolean'|'Number'|'Array'|'Object'} [type]
	 */

	// generated during release, do not modify

	const PUBLIC_VERSION = '4';

	if (typeof window !== 'undefined')
		// @ts-ignore
		(window.__svelte || (window.__svelte = { v: new Set() })).v.add(PUBLIC_VERSION);

	/* src\hud.svelte generated by Svelte v4.2.20 */

	function add_css(target) {
		append_styles(target, "svelte-1l69rmg", ".hud.svelte-1l69rmg.svelte-1l69rmg{position:absolute;width:300px;height:200px;z-index:999999999999;background-color:rgba(0, 0, 0, 0.9);border-radius:0.5em;display:flex;flex-direction:column;justify-content:space-evenly;align-items:center;color:white;touch-action:none;user-select:none}.hud.svelte-1l69rmg .row.svelte-1l69rmg{display:flex;flex-direction:row;justify-content:space-between;align-items:space-between;width:100%}.hud.svelte-1l69rmg .answer.svelte-1l69rmg{width:70%;height:50px;font-family:Verdana, Geneva, Tahoma, sans-serif;font-size:1em;border-radius:0.5em;background-color:white;color:black;border:none;transition:transform 0.3s ease}.hud.svelte-1l69rmg .answer.svelte-1l69rmg:active{transform:scale(0.93)}.hud.svelte-1l69rmg .help.svelte-1l69rmg{display:flex;flex-direction:column;align-items:center;width:85%}.hud.svelte-1l69rmg .helpControl button.svelte-1l69rmg{width:25px;height:25px;border-radius:0.5em;background-color:white;border:none;transition:transform 0.3s ease;margin:5px;color:black}");
	}

	// (132:4) {#if visible}
	function create_if_block(ctx) {
		let div4;
		let button0;
		let t1;
		let div3;
		let div0;
		let t3;
		let div2;
		let button1;
		let t5;
		let div1;
		let t6_value = /*helpModes*/ ctx[4][/*helpMode*/ ctx[1]] + "";
		let t6;
		let t7;
		let button2;
		let mounted;
		let dispose;

		return {
			c() {
				div4 = element("div");
				button0 = element("button");
				button0.textContent = "Answer Question";
				t1 = space();
				div3 = element("div");
				div0 = element("div");
				div0.textContent = "Help Mode";
				t3 = space();
				div2 = element("div");
				button1 = element("button");
				button1.textContent = "<";
				t5 = space();
				div1 = element("div");
				t6 = text(t6_value);
				t7 = space();
				button2 = element("button");
				button2.textContent = ">";
				attr(button0, "class", "answer svelte-1l69rmg");
				attr(button1, "class", "svelte-1l69rmg");
				attr(div1, "class", "display");
				attr(button2, "class", "svelte-1l69rmg");
				attr(div2, "class", "row helpControl svelte-1l69rmg");
				attr(div3, "class", "help svelte-1l69rmg");
				attr(div4, "class", "hud svelte-1l69rmg");
				set_style(div4, "left", /*x*/ ctx[2] + "px");
				set_style(div4, "top", /*y*/ ctx[3] + "px");
				attr(div4, "role", "button");
				attr(div4, "tabindex", "0");
			},
			m(target, anchor) {
				insert(target, div4, anchor);
				append(div4, button0);
				append(div4, t1);
				append(div4, div3);
				append(div3, div0);
				append(div3, t3);
				append(div3, div2);
				append(div2, button1);
				append(div2, t5);
				append(div2, div1);
				append(div1, t6);
				append(div2, t7);
				append(div2, button2);

				if (!mounted) {
					dispose = [
						listen(button0, "touchstart", stop_propagation(/*touchstart_handler*/ ctx[15])),
						listen(button0, "click", /*handleAnswer*/ ctx[9]),
						listen(button1, "touchstart", stop_propagation(/*touchstart_handler_1*/ ctx[14])),
						listen(button1, "click", /*click_handler*/ ctx[16]),
						listen(button2, "touchstart", stop_propagation(/*touchstart_handler_2*/ ctx[13])),
						listen(button2, "click", /*click_handler_1*/ ctx[17]),
						listen(div4, "mousedown", /*handleStart*/ ctx[5]),
						listen(div4, "touchstart", prevent_default(/*handleStart*/ ctx[5]))
					];

					mounted = true;
				}
			},
			p(ctx, dirty) {
				if (dirty & /*helpMode*/ 2 && t6_value !== (t6_value = /*helpModes*/ ctx[4][/*helpMode*/ ctx[1]] + "")) set_data(t6, t6_value);

				if (dirty & /*x*/ 4) {
					set_style(div4, "left", /*x*/ ctx[2] + "px");
				}

				if (dirty & /*y*/ 8) {
					set_style(div4, "top", /*y*/ ctx[3] + "px");
				}
			},
			d(detaching) {
				if (detaching) {
					detach(div4);
				}

				mounted = false;
				run_all(dispose);
			}
		};
	}

	function create_fragment(ctx) {
		let section;
		let mounted;
		let dispose;
		let if_block = /*visible*/ ctx[0] && create_if_block(ctx);

		return {
			c() {
				section = element("section");
				if (if_block) if_block.c();
			},
			m(target, anchor) {
				insert(target, section, anchor);
				if (if_block) if_block.m(section, null);

				if (!mounted) {
					dispose = [
						listen(window, "keydown", /*onKeyDown*/ ctx[10]),
						listen(window, "mousemove", /*handleMove*/ ctx[6]),
						listen(window, "mouseup", /*handleEnd*/ ctx[7]),
						listen(window, "touchmove", prevent_default(/*handleMove*/ ctx[6])),
						listen(window, "touchend", /*handleEnd*/ ctx[7])
					];

					mounted = true;
				}
			},
			p(ctx, [dirty]) {
				if (/*visible*/ ctx[0]) {
					if (if_block) {
						if_block.p(ctx, dirty);
					} else {
						if_block = create_if_block(ctx);
						if_block.c();
						if_block.m(section, null);
					}
				} else if (if_block) {
					if_block.d(1);
					if_block = null;
				}
			},
			i: noop,
			o: noop,
			d(detaching) {
				if (detaching) {
					detach(section);
				}

				if (if_block) if_block.d();
				mounted = false;
				run_all(dispose);
			}
		};
	}

	const TAP_DELAY = 300; // ms between taps
	const RESET_DELAY = 400; // ms to reset tap count

	function instance($$self, $$props, $$invalidate) {
		let { onanswer = undefined } = $$props;
		let { onhelpMode = undefined } = $$props;
		let visible = true;

		const helpModes = [
			'None',
			'Auto Answer (instant)',
			'Auto Answer (wait)',
			'Outline Correct Answer'
		];

		let helpMode = 0;
		let lastHelpMode = 0;
		let tapCount = 0;
		let lastTapTime = 0;
		let tapTimeout = null;
		let x = 10;
		let y = 10;
		let isDragging = false;
		let startX;
		let startY;

		function handleTouchEnd(e) {
			const now = Date.now();
			const timeSinceLastTap = now - lastTapTime;

			if (tapTimeout) {
				clearTimeout(tapTimeout);
			}

			if (timeSinceLastTap < TAP_DELAY) {
				tapCount++;
			} else {
				tapCount = 1;
			}

			lastTapTime = now;

			if (tapCount === 3) {
				multiTouchHandler();
				tapCount = 0;
				lastTapTime = 0;
			} else {
				tapTimeout = setTimeout(
					() => {
						tapCount = 0;
					},
					RESET_DELAY
				);
			}
		}

		onMount(() => {
			document.addEventListener('touchend', handleTouchEnd);
		});

		onDestroy(() => {
			document.removeEventListener('touchend', handleTouchEnd);

			if (tapTimeout) {
				clearTimeout(tapTimeout);
			}
		});

		function handleStart(event) {
			isDragging = true;

			const clientX = event.type === 'mousedown'
			? event.clientX
			: event.touches[0].clientX;

			const clientY = event.type === 'mousedown'
			? event.clientY
			: event.touches[0].clientY;

			startX = clientX - x;
			startY = clientY - y;
		}

		function handleMove(event) {
			if (!isDragging) return;
			event.preventDefault();

			const clientX = event.type === 'mousemove'
			? event.clientX
			: event.touches[0].clientX;

			const clientY = event.type === 'mousemove'
			? event.clientY
			: event.touches[0].clientY;

			$$invalidate(2, x = clientX - startX);
			$$invalidate(3, y = clientY - startY);
		}

		function handleEnd() {
			isDragging = false;
		}

		let dispatch = createEventDispatcher();

		function changeHelpMode(change) {
			$$invalidate(1, helpMode += change);
			if (helpMode < 0) $$invalidate(1, helpMode += helpModes.length);
			$$invalidate(1, helpMode %= helpModes.length);
			dispatch('helpMode', helpMode);
			onhelpMode?.(new CustomEvent('helpMode', { detail: helpMode }));
		}

		function handleAnswer() {
			dispatch('answer');
			onanswer?.();
		}

		function onKeyDown(event) {
			if (event.key !== '\\') return;

			if (visible == true) {
				lastHelpMode = helpMode;
				$$invalidate(1, helpMode = 0);
			} else {
				$$invalidate(1, helpMode = lastHelpMode);
			}

			dispatch('helpMode', helpMode);
			$$invalidate(0, visible = !visible);
		}

		function multiTouchHandler() {
			if (visible == true) {
				lastHelpMode = helpMode;
				$$invalidate(1, helpMode = 0);
			} else {
				$$invalidate(1, helpMode = lastHelpMode);
			}

			dispatch('helpMode', helpMode);
			$$invalidate(0, visible = !visible);
		}

		function touchstart_handler_2(event) {
			bubble.call(this, $$self, event);
		}

		function touchstart_handler_1(event) {
			bubble.call(this, $$self, event);
		}

		function touchstart_handler(event) {
			bubble.call(this, $$self, event);
		}

		const click_handler = () => changeHelpMode(-1);
		const click_handler_1 = () => changeHelpMode(1);

		$$self.$$set = $$props => {
			if ('onanswer' in $$props) $$invalidate(11, onanswer = $$props.onanswer);
			if ('onhelpMode' in $$props) $$invalidate(12, onhelpMode = $$props.onhelpMode);
		};

		return [
			visible,
			helpMode,
			x,
			y,
			helpModes,
			handleStart,
			handleMove,
			handleEnd,
			changeHelpMode,
			handleAnswer,
			onKeyDown,
			onanswer,
			onhelpMode,
			touchstart_handler_2,
			touchstart_handler_1,
			touchstart_handler,
			click_handler,
			click_handler_1
		];
	}

	class Hud extends SvelteComponent {
		constructor(options) {
			super();
			init(this, options, instance, create_fragment, safe_not_equal, { onanswer: 11, onhelpMode: 12 }, add_css);
		}
	}

	var HelpModes;
	(function (HelpModes) {
	    HelpModes[HelpModes["None"] = 0] = "None";
	    HelpModes[HelpModes["AnswerInstant"] = 1] = "AnswerInstant";
	    HelpModes[HelpModes["AnswerDelayed"] = 2] = "AnswerDelayed";
	    HelpModes[HelpModes["Highlight"] = 3] = "Highlight";
	})(HelpModes || (HelpModes = {}));
	const cardSelector = "#__next > div > div:nth-child(3) > div > div:nth-child(2) > div";
	let messageCount = 0;
	let socket = null;
	let helpMode = 0;
	let cards = [];
	window.addEventListener('DOMContentLoaded', () => {
	    new Hud({
	        target: document.body,
	        props: {
	            onanswer: () => {
	                answer();
	            },
	            onhelpMode: (event) => {
	                helpMode = event.detail;
	                if (helpMode === HelpModes.Highlight) {
	                    setCardBorders();
	                }
	                else {
	                    document.querySelectorAll(cardSelector).forEach(card => {
	                        card.style.border = "";
	                    });
	                }
	            }
	        }
	    });
	});
	class NewWebSocket extends WebSocket {
	    constructor(url, protocols) {
	        super(url, protocols);
	        socket = this;
	        this.addEventListener('message', (event) => {
	            const data = event.data.toString();
	            if (!data.startsWith("42"))
	                return;
	            const message = data.substring(data.indexOf('['));
	            const messageObj = JSON.parse(message);
	            console.log("recieved", messageObj);
	            onMessage(messageObj[0], messageObj[1]);
	        });
	    }
	    send(data) {
	        let str = data.toString();
	        if (!str.startsWith("42"))
	            return super.send(data);
	        let message = str.substring(Math.min(str.indexOf('['), str.indexOf('{')));
	        const newMsg = `42${messageCount}${message}`;
	        super.send(newMsg);
	        messageCount++;
	    }
	}
	// override the WebSocket class
	unsafeWindow.WebSocket = NewWebSocket;
	function send(data) {
	    if (socket === null)
	        return;
	    const message = `42${messageCount}${JSON.stringify(data)}`;
	    socket.send(message);
	    messageCount++;
	}
	let streak;
	let streakNum = 0;
	let roundNum = 0;
	let cardAssignments = [];
	function checkAnswer() {
	    if (helpMode === HelpModes.AnswerInstant)
	        answer();
	    else if (helpMode === HelpModes.AnswerDelayed)
	        setTimeout(answer, 1900);
	}
	function onMessage(type, data) {
	    let playerId = getPlayerId();
	    switch (type) {
	        case "current-game-state-and-set":
	        case "current-game-state":
	            onGameState(data);
	            break;
	        case "matchteam.new-answer":
	            roundNum = data.roundNum + 1;
	            streakNum = data.streakNum;
	            cardAssignments = data.cardAssignments[playerId];
	            checkAnswer();
	            break;
	        case "matchteam.new-streak":
	            streak = data.streak;
	            streakNum++;
	            roundNum = 0;
	            cardAssignments = data.streak.cardAssignments[playerId];
	            checkAnswer();
	            break;
	    }
	}
	function onGameState(state) {
	    let playerId = getPlayerId();
	    if (!playerId)
	        return;
	    let team = Object.values(state.teams).find(team => team.playerIds.includes(playerId));
	    if (!team)
	        return;
	    streakNum = team.streak.length - 1;
	    streak = team.streak[streakNum];
	    if (!streak)
	        return;
	    cardAssignments = streak.cardAssignments[playerId];
	    roundNum = streak.answers.length;
	}
	function answer() {
	    let cardId = streak?.prompts?.[roundNum]?.cardId;
	    if (!cardId)
	        return;
	    if (!cardAssignments.includes(cardId))
	        return;
	    send(["gamehub.matchteam.submit-answer", {
	            playerId: getPlayerId(),
	            cardId,
	            roundNum,
	            streakNum
	        }]);
	}
	function setCardBorders() {
	    let correctId = streak?.prompts?.[roundNum]?.cardId;
	    if (!correctId)
	        return;
	    let correctIndex = cardAssignments.indexOf(correctId);
	    cards.forEach((card, index) => {
	        if (index === correctIndex) {
	            card.style.border = "2px solid green";
	        }
	        else {
	            card.style.border = "2px solid red";
	        }
	    });
	}
	// @ts-ignore
	unsafeWindow.answer = answer;
	const cardObserver = new MutationObserver((mutations) => {
	    for (let mutation of mutations) {
	        if (mutation.type !== "childList")
	            continue;
	        for (let i = 0; i < mutation.addedNodes.length; i++) {
	            const node = mutation.addedNodes[i];
	            if (!(node instanceof HTMLElement))
	                continue;
	            let foundCards;
	            if (node.matches(cardSelector)) {
	                foundCards = document.querySelectorAll(cardSelector);
	            }
	            else {
	                foundCards = node.querySelectorAll(cardSelector);
	            }
	            if (foundCards.length === 0)
	                continue;
	            cards = Array.from(foundCards);
	            if (helpMode === HelpModes.Highlight) {
	                setCardBorders();
	            }
	        }
	    }
	});
	window.addEventListener('DOMContentLoaded', () => {
	    cardObserver.observe(document.body, {
	        childList: true,
	        subtree: true
	    });
	});
	let uid;
	function getPlayerId() {
	    if (uid)
	        return uid;
	    let script = document.getElementById("__NEXT_DATA__");
	    let data = JSON.parse(script.textContent);
	    uid = data.props.pageProps.userId || data.props.pageProps.personId;
	    return uid;
	}

})();