var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import Autocomplete from "@tarekraafat/autocomplete.js";
import { EventEmitter } from "events";
import { pick } from "lodash-es";
import { addClass } from "@matdata/yasgui-utils";
import "./endpointSelect.scss";
import parse from "autosuggest-highlight/parse";
import DOMPurify from "dompurify";
const { sanitize } = DOMPurify;
function listElementIsFullyVissible(el) {
    const { top, bottom } = el.getBoundingClientRect();
    if (bottom < 0)
        return false;
    if (top > document.documentElement.clientHeight)
        return false;
    const ulRect = el.parentNode.getBoundingClientRect();
    if (bottom <= ulRect.bottom === false)
        return false;
    if (top <= ulRect.top)
        return false;
    return true;
}
function splitSearchString(searchString) {
    return searchString.match(/\S+/g) || [];
}
export class EndpointSelect extends EventEmitter {
    constructor(initialValue, container, options, history) {
        super();
        this.clearListSuggestionList = () => {
            const autocompleteList = this.container.querySelector(".autocompleteList");
            if (autocompleteList)
                autocompleteList.innerHTML = "";
        };
        this.container = container;
        this.options = options;
        this.value = initialValue;
        this.history = history.map((endpoint) => {
            return { endpoint: sanitize(endpoint), type: "history" };
        });
        if (this.options.keys.indexOf("endpoint") <= 0)
            this.options.keys.push("endpoint");
        this.draw();
    }
    draw() {
        const autocompleteWrapper = document.createElement("div");
        addClass(autocompleteWrapper, "autocompleteWrapper");
        this.container.appendChild(autocompleteWrapper);
        this.inputField = document.createElement("input");
        addClass(this.inputField, "autocomplete");
        this.inputField.value = this.value;
        autocompleteWrapper.appendChild(this.inputField);
        const clearBtn = document.createElement("button");
        clearBtn.title = "Clear endpoint";
        addClass(clearBtn, "clearEndpointBtn");
        clearBtn.innerText = "✖";
        clearBtn.addEventListener("click", () => {
            this.inputField.value = "";
            this.inputField.focus();
        });
        this.container.appendChild(clearBtn);
        new Autocomplete({
            placeholder: "Search or add an endpoint",
            highlight: false,
            maxResults: 100,
            trigger: {
                event: ["input", "focusin"],
                condition: () => true,
            },
            searchEngine: (query, record) => {
                if (!query || query.trim().length === 0) {
                    return true;
                }
                return splitSearchString(query).every((m) => record.indexOf(m) >= 0);
            },
            data: {
                src: () => __awaiter(this, void 0, void 0, function* () {
                    return [...this.history, ...this.options.getData()].map((item) => (Object.assign(Object.assign({}, item), { all: Object.values(pick(item, ["endpoint", ...this.options.keys])).join(" ") })));
                }),
                key: ["all"],
                cache: false,
            },
            selector: () => this.inputField,
            resultsList: {
                render: true,
                destination: this.inputField,
                container: (element) => {
                    element.id = "";
                    addClass(element, "autocompleteList");
                },
            },
            resultItem: {
                content: (data, source) => {
                    const endpoint = sanitize(data.value.endpoint);
                    if (data.value.type && data.value.type === "history") {
                        const resultsContainer = document.createElement("div");
                        resultsContainer.innerHTML = parse(endpoint, createHighlights(endpoint, this.inputField.value)).reduce((current, object) => (object.highlight ? current + object.text.bold() : current + object.text), "");
                        source.append(resultsContainer);
                        const removeBtn = document.createElement("button");
                        removeBtn.textContent = "✖";
                        addClass(removeBtn, "removeItem");
                        removeBtn.addEventListener("mousedown", (event) => {
                            this.history = this.history.filter((item) => item.endpoint !== endpoint);
                            this.emit("remove", this.value, this.history.map((value) => value.endpoint));
                            source.remove();
                            event.stopPropagation();
                        });
                        source.appendChild(removeBtn);
                    }
                    else {
                        const matches = { matches: {} };
                        for (const key of [...this.options.keys]) {
                            const val = data.value[key];
                            if (val) {
                                matches.matches[key] = parse(val, createHighlights(val, this.inputField.value));
                            }
                        }
                        this.options.renderItem(Object.assign(Object.assign({}, data), matches), source);
                    }
                },
                element: "li",
            },
            onSelection: (feedback) => {
                const item = feedback.selection.value;
                this.value = item.endpoint;
                this.inputField.value = this.value;
                this.emit("select", this.value, this.history.map((value) => value.endpoint));
            },
            noResults: () => {
                const container = this.container.querySelector(".autocompleteList");
                if (container) {
                    const noResults = document.createElement("div");
                    addClass(noResults, "noResults");
                    noResults.innerText = 'Press "enter" to add this endpoint';
                    container.appendChild(noResults);
                }
            },
        });
        this.inputField.addEventListener("keyup", (event) => {
            const target = event.target;
            if (event.keyCode === 13) {
                if (this.value === target.value) {
                    this.clearListSuggestionList();
                    this.inputField.blur();
                    return;
                }
                if (!target.value || !target.value.trim()) {
                    this.clearListSuggestionList();
                    this.inputField.blur();
                    return;
                }
                if (this.options.getData().find((i) => i.endpoint === this.inputField.value) ||
                    this.history.find((item) => item.endpoint === this.inputField.value)) {
                    this.value = target.value;
                    this.clearListSuggestionList();
                    this.emit("select", this.value, this.history.map((h) => h.endpoint));
                    this.inputField.blur();
                    return;
                }
                this.value = target.value;
                this.history.push({ endpoint: target.value, type: "history" });
                this.emit("select", this.value, this.history.map((value) => value.endpoint));
                this.clearListSuggestionList();
                this.inputField.blur();
            }
            if (event.keyCode === 27) {
                this.inputField.blur();
                this.inputField.value = this.value;
                this.clearListSuggestionList();
            }
            if (event.keyCode === 38 || event.keyCode === 40) {
                event.stopPropagation();
                const selected = this.container.querySelector(".autocompleteList .autoComplete_result.autoComplete_selected");
                if (selected && !listElementIsFullyVissible(selected)) {
                    selected.scrollIntoView(false);
                }
            }
        });
        this.inputField.addEventListener("blur", (event) => {
            const target = event.target;
            if (target.className === this.inputField.className && event.relatedTarget) {
                this.clearListSuggestionList();
                this.inputField.value = this.value;
            }
        });
        document.addEventListener("mousedown", (event) => {
            if (event.button !== 2) {
                const target = event.target;
                if (target.className === "removeItem" ||
                    target.className === "autoComplete_result" ||
                    target.className === "autocomplete")
                    return;
                this.clearListSuggestionList();
                this.inputField.value = this.value;
            }
        });
    }
    setEndpoint(endpoint, endpointHistory) {
        this.value = endpoint;
        if (endpointHistory) {
            this.history = endpointHistory.map((endpoint) => {
                return { endpoint, type: "history" };
            });
        }
        if (this.inputField === document.activeElement) {
            this.inputField.focus();
        }
        else {
            this.inputField.value = endpoint;
        }
    }
    destroy() {
        this.removeAllListeners();
        this.inputField.remove();
    }
}
function createHighlights(text, query) {
    return splitSearchString(query)
        .reduce((result, word) => {
        if (!word.length)
            return result;
        const wordLen = word.length;
        const index = text.indexOf(word);
        if (index > -1) {
            result.push([index, index + wordLen]);
            text = text.slice(0, index) + new Array(wordLen + 1).join(" ") + text.slice(index + wordLen);
        }
        return result;
    }, [])
        .sort((match1, match2) => {
        return match1[0] - match2[0];
    });
}
export default EndpointSelect;
//# sourceMappingURL=endpointSelect.js.map