

window.makeMultiselect = (items => {
	class MultiSelect {
		#htmlSelectElement;
		#htmlRootElement;
		#htmlInput;
		#htmlInputList;
		#htmlWrapper;
		#htmlDropdown;
		#ignoreMouseOut = false;
		#options = [];
		#eventHandlers = [];

		constructor(htmlElement) {
			this.#htmlSelectElement = htmlElement;
			this.#htmlRootElement = htmlElement.parentElement;
			for (let i of this.#htmlSelectElement.querySelectorAll("option"))
				this.#options.push({
					value: i.textContent,
					checked: !!i.selected,
					indeterminate: !!i.indeterminate,
					htmlListItem: null
				});
		}
		addEventListener(fncHandler) {
			this.#eventHandlers.push(fncHandler);
		}
		#onUpdate() {
			for (let i of this.#eventHandlers)
				i(this.#options);
		}
		#updateTextInputValue() {
			this.#htmlInputList.textContent = "";
			this.#htmlInputList.appendChild(this.#htmlInput);
			for (let i in this.#options) {
				if (this.#options[i].checked) {
					let li = document.createElement("li");
					let closeBt = document.createElement("a");
					closeBt.href = "#";
					closeBt.textContent = "x";
					closeBt.addEventListener("click", e => {
						this.#options[i].checked = false;
						this.#options[i].indeterminate = false;
						this.#rebuildDropdownContent();
						this.#updateTextInputValue();
						this.#onUpdate();
						e.preventDefault();
					});
					li.textContent = this.#options[i].value;
					li.className = "checked";
					li.appendChild(closeBt);
					if (this.#options[i].indeterminate)
						li.classList.add("indeterminate");
					this.#htmlInputList.insertBefore(li, this.#htmlInput);
				}
			}
		}
		#rebuildDropdownContent() {
			this.#htmlDropdown.textContent = "";
			for (let i in this.#options) {
				let wrapper = document.createElement("li");
				let lbl = document.createElement("label");
				let chk = document.createElement("input");
				let span = document.createElement("span");
				span.textContent = this.#options[i].value;
				chk.type = "checkbox";
				chk.checked = !!this.#options[i].checked;
				chk.indeterminate = !!this.#options[i].indeterminate;
				chk.index = i;
				lbl.appendChild(chk);
				lbl.appendChild(span);
				wrapper.appendChild(lbl);
				this.#htmlDropdown.appendChild(wrapper);
				this.#options[i].htmlListItem = wrapper;
				chk.addEventListener("change", e => {
					let state = this.#options[e.currentTarget.index];
					if (!state.checked) {
						e.currentTarget.checked = true;
						e.currentTarget.indeterminate = false;
					} else if (!state.indeterminate) {
						e.currentTarget.checked = true;
						e.currentTarget.indeterminate = true;
					} else {
						e.currentTarget.checked = false;
						e.currentTarget.indeterminate = false;
					}
					state.checked = e.currentTarget.checked;
					state.indeterminate = e.currentTarget.indeterminate;
					e.stopImmediatePropagation();
					this.#updateTextInputValue();
					this.#onUpdate();
				});
			}
		}
		render() {
			this.#htmlSelectElement.style.display = "none";
			if (!this.#htmlInput) {
				this.#htmlInput = document.createElement("input");
				this.#htmlInput.className = "multiselect-input";
				this.#htmlInput.type = "text";
				this.#htmlInput.addEventListener("input", e => {
					let input = e.currentTarget.value.toLocaleLowerCase();
					for (let i of this.#options) {
						if (i.value.toLocaleLowerCase().indexOf(input) >= 0) {
							i.htmlListItem.classList.remove("hidden");
						} else {
							i.htmlListItem.classList.add("hidden");
						}
					}
				});
				this.#htmlInputList && this.#htmlInputList.appendChild(this.#htmlInput);
			}
			if (!this.#htmlInputList) {
				this.#htmlInputList = document.createElement("ul");
				this.#htmlInputList.className = "multiselect-inputlist";
				this.#htmlRootElement && this.#htmlRootElement.appendChild(this.#htmlInputList);
				this.#htmlInputList.appendChild(this.#htmlInput);
			}
			if (!this.#htmlDropdown) {
				this.#htmlDropdown = document.createElement("ul");
				this.#htmlDropdown.className = "multiselect-dropdown";
				this.#htmlDropdown.classList.add("hidden");
				this.#htmlRootElement && this.#htmlRootElement.appendChild(this.#htmlDropdown);
			}
			if (!this.#htmlWrapper) {
				this.#htmlWrapper = document.createElement("div");
				this.#htmlWrapper.className = "multiselect-wrapper";
				this.#htmlWrapper.appendChild(this.#htmlInputList);
				this.#htmlWrapper.appendChild(this.#htmlDropdown);
				this.#htmlRootElement.appendChild(this.#htmlWrapper);
				this.#htmlRootElement.addEventListener("mousedown", e => {
					this.#ignoreMouseOut = setTimeout(() => { this.#ignoreMouseOut = false; }, 500);
				});
				this.#htmlWrapper.addEventListener("focusin", e => {
					this.#htmlDropdown.classList.remove("hidden");
				});
				this.#htmlWrapper.addEventListener("focusout", e => {
					this.#ignoreMouseOut === false && this.#htmlDropdown.classList.add("hidden");
				});
				document.body.addEventListener("click", e => {
					this.#ignoreMouseOut === false && this.#htmlDropdown.classList.add("hidden");
				});
			}
			this.#rebuildDropdownContent();
			this.#updateTextInputValue();
		}
	}

	_makeMultiSelect = htmlElement => {
		let select = new MultiSelect(htmlElement);
		select.render();
		return select;
	};
	if (items instanceof HTMLElement)
		return _makeMultiSelect(items);
	else if (items.length)
		return Array.from(items).map(i => window.makeMultiselect(i));
});

