Skip to content
Snippets Groups Projects
main.js 24.7 KiB
Newer Older
// This script will be run within the webview itself
// It cannot access the main VS Code APIs directly.
(function () {
    const vscode = acquireVsCodeApi();

    document.getElementById("constants").parentElement.style.height = "0";

    for (const element of document.querySelectorAll(".pane-view .pane")) {
        addCollapseBehaviour(element);
    }

    /**
     * @type {function()[]}
     */
    const revertButtons = [];

    /**
     * @type {HTMLSelectElement}
     */
    const toolDropDown = document.getElementById("tools");
    toolDropDown.addEventListener('change', event => {
s1995588's avatar
s1995588 committed
        var oldState = vscode.getState();
        var currentTool = toolDropDown.options[toolDropDown.selectedIndex].value;
        vscode.setState({ tools: oldState.tools, documentVars: oldState.documentVars, parameters: oldState.parameters, currentUri: oldState.currentUri, currentTool: currentTool });
s1995588's avatar
s1995588 committed
        vscode.postMessage({ type: "toolSelected", toolName: currentTool });

    // Handle messages sent from the extension to the webview
    window.addEventListener('message', event => {
        const message = event.data; // The json data that the extension sent
        console.log("Received message");
        switch (message.type) {
            case 'updateDocumentVars':
                    updateDocumentVars(message.documentVars, message.uri);
s1995588's avatar
s1995588 committed
            case 'updateParameters':
s1995588's avatar
s1995588 committed
                    updateParameters(message.parameters, message.toolName);
            case 'fillTools':
                {
                    fillTools(message.tools);
                    break;
                }
        for (const body of document.querySelectorAll(".pane-body")) {
            if (body.style.height) {
                expandHeight(body, 0);
            }
        }
    const runButton = document.getElementById("run-button");
    runButton.addEventListener("click", event => {
        const state = vscode.getState();
        for (const file of state.documentVars) {
            if (file.uri === state.currentUri) {
                for (const tool of state.parameters) {
                    if (tool.toolName === state.currentTool) {
                        vscode.postMessage({ type: "runTool", uri: state.currentUri, toolName: toolDropDown.options[toolDropDown.selectedIndex].value, constants: file.constants, distributions: file.distributions, parameters: tool.parameters });
    /**
    * @param {HTMLElement} body
    */
    function collapseHeight(body) {
        const height = body.scrollHeight;
        body.style.removeProperty("height");
        body.style.removeProperty("margin");
        if (body.parentElement.parentElement.classList.contains("pane-body")) {
            expandHeight(body.parentElement.parentElement, -height);
        }
    }

    /**
     * @param {HTMLElement} body
     * @param {number} amount
     */
    function expandHeight(body, additional) {
        let amount = 0;
        for (const child of body.childNodes) {
                amount += child.scrollHeight;
            }
        }

        // body.style.transition = "initial";
        // const previousHeight = body.style.height;
        // body.style.removeProperty("height");
        // const newHeight = (body.scrollHeight + amount) + "px";
        // body.style.height = previousHeight;
        // body.style.removeProperty("transition");
        body.style.margin = "0.2em 0 0.4em var(--container-paddding)";
        if (body.parentElement.parentElement.classList.contains("pane-body")) {
            expandHeight(body.parentElement.parentElement, amount + additional + parseFloat(getComputedStyle(body).fontSize) * 0.6);
    /**
     *
     * @param {HTMLElement} element
     */
    function addCollapseBehaviour(element) {
        const header = element.querySelector(".pane-header");
        const headerIcon = header.querySelector(".codicon");
        const body = element.querySelector(".pane-body");
        header.addEventListener("click", _ => {
            if (body.style.height) {
                collapseHeight(body);
                headerIcon.classList.remove("codicon-chevron-down");
                headerIcon.classList.add("codicon-chevron-right");
            } else {
                expandHeight(body, 0);
                headerIcon.classList.remove("codicon-chevron-right");
                headerIcon.classList.add("codicon-chevron-down");
            // if (body.classList.contains("hidden")) {
            //     body.classList.remove("hidden");
            //     body.style.height = body.scrollHeight + "px";
            //     headerIcon.classList.remove("codicon-chevron-right");
            //     headerIcon.classList.add("codicon-chevron-down");
            // } else {
            //     body.classList.add("hidden");
            //     body.style.height = "0px";
            //     headerIcon.classList.remove("codicon-chevron-down");
            //     headerIcon.classList.add("codicon-chevron-right");
            // }

    function fillTools(tools) {
        const select = document.querySelector("#tools");
        select.innerHTML = "";

        for (const tool of tools) {
            const option = document.createElement("option");
            option.value = tool;
            option.text = tool;
            select.appendChild(option);
        }
s1995588's avatar
s1995588 committed
        const oldState = vscode.getState();
        let index = tools.findIndex(x => x === oldState.currentTool);
        if (index === -1) {
            index = 0;
        }
        select.selectedIndex = index;
        vscode.postMessage({ type: "toolSelected", toolName: oldState.currentTool });

        vscode.setState({ tools: tools, documentVars: oldState.documentVars, parameters: oldState.parameters, currentUri: oldState.currentUri, currentTool: oldState.currentTool });
     * @param {Array<{ uri: string, constants: Array<{ name: string, value: string }>, distributions: Array<{ name: string, value: string }> }>} documentVars
     * @param {string} uri
    function updateDocumentVars(documentVars, uri) {
        const constantsUl = document.querySelector("#constants");
        const distributionsUl = document.querySelector("#distributions");
        const oldState = vscode.getState();

        // combines the oldstate constants and distributions with the "new" constants and distributions.
        for (const file of documentVars) {
            const index = oldState.documentVars.findIndex(x => x.uri === file.uri);
                    const oldConstantIndex = oldState.documentVars[index].constants.findIndex(x => x.name === constant.name);
                    if (oldConstantIndex !== -1) {
                        constant.value = oldState.documentVars[index].constants[oldConstantIndex].value;
                for (const distribution of file.distributions) {
                    const oldDistributionIndex = oldState.documentVars[index].distributions.findIndex(x => x.name === distribution.name);
                    if (oldDistributionIndex !== -1) {
                        distribution.value = oldState.documentVars[index].distributions[oldDistributionIndex].value;
                    }
                }
        for (const file of oldState.documentVars) {
            const index = documentVars.findIndex(x => x.uri === file.uri);
                documentVars.push(file);
        distributionsUl.innerHTML = "";
s1995588's avatar
s1995588 committed
        // adds the constants to the sidebar.
        for (const file of documentVars) {
            if (file.uri === uri) {
                for (const constant of file.constants) {
                    addConstantItem(constantsUl, constant.name, constant.value);
                }
                if (oldState.currentTool === "prohver (prohver)") {
                    for (const distribution of file.distributions) {
                        addDistributionItem(distributionsUl, distribution.name, distribution.value);
                    }
                }
        if (constantsUl.innerHTML === "") {
            constantsUl.innerHTML = "There are no undefined constants.";
        }
        if (distributionsUl.innerHTML === "") {
            distributionsUl.innerHTML = "There are no distributions.";
        }
        vscode.setState({ tools: oldState.tools, documentVars: documentVars, parameters: oldState.parameters, currentUri: uri, currentTool: oldState.currentTool });
     * @param {Array<{ toolName: string, parameters: Array<{ id: string, name: string, value: string, type: ParameterType, category: string}> }>} parameters
s1995588's avatar
s1995588 committed
     * @param {string} tool
s1995588's avatar
s1995588 committed
    function updateParameters(parameters, toolName) {
        const parametersUl = document.querySelector("#parameters");
        const oldState = vscode.getState();

        // combines the oldstate parameters with the "new" parameters.
        for (const tool of parameters) {
            const index = oldState.parameters.findIndex(x => x.toolName === tool.toolName);
            if (index !== -1) {
                for (const parameter of tool.parameters) {
                    const oldParameterIndex = oldState.parameters[index].parameters.findIndex(x => x.name === parameter.name);
s1995588's avatar
s1995588 committed
                    if (oldParameterIndex !== -1) {
s1995588's avatar
s1995588 committed
                        parameter.value = oldState.parameters[index].parameters[oldParameterIndex].value;
                    }
                }
            }
        for (const tool of oldState.parameters) {
            const index = parameters.findIndex(x => x.toolName === tool.toolName);
            if (index === -1) {
                parameters.push(tool);
            }
        }

s1995588's avatar
s1995588 committed
        parametersUl.innerHTML = parameters.length === 0 ? "There are no parameters." : "";
        // adds the parameters to the sidebar.
        for (const tool of parameters) {
            if (tool.toolName === toolName) {
                for (const parameter of tool.parameters) {
                    addParameterItem(parameter.name, parameter.value, parameter.category, parameter.type, parameter.description, parameter.possibleValues, parameter.defaultValue);
        vscode.setState({ tools: oldState.tools, documentVars: oldState.documentVars, parameters: parameters, currentUri: oldState.currentUri, currentTool: oldState.currentTool });
s1995588's avatar
s1995588 committed
    }
    // vscode.setState({ tools: [], documentVars: [], parameters: [], currentUri: "", currentTool: "" });
    const oldState = vscode.getState() || { tools: [], documentVars: [], parameters: [], currentUri: "", currentTool: "" };
    const tools = oldState.tools;
    const documentVars = oldState.documentVars;
s1995588's avatar
s1995588 committed
    const parameters = oldState.parameters;
    const currentUri = oldState.currentUri;
s1995588's avatar
s1995588 committed
    const currentTool = oldState.currentTool;

    if (oldState.tools.length === 0) {
        vscode.postMessage({ type: "init" });
    } else {
        fillTools(tools);
s1995588's avatar
s1995588 committed
        updateParameters(parameters, currentTool);
        updateDocumentVars(documentVars, currentUri);
        for (const body of document.querySelectorAll(".pane-body")) {
            if (body.style.height) {
                expandHeight(body, 0);
            }
        }

    //#region help functions
    function addConstantItem(ul, name, value) {
        const li = document.createElement("li");

        const nameBox = document.createElement("label");
        nameBox.id = "name-" + name;
        nameBox.appendChild(document.createTextNode(name));
        nameBox.classList.add("name");

        li.appendChild(nameBox);

        const valueBox = document.createElement("input");
        valueBox.id = "value-" + name;
        valueBox.type = "text";
        valueBox.value = value;
        valueBox.classList.add("value");
            let oldState = vscode.getState();
            for (const file of oldState.documentVars) {
                if (file.uri === oldState.currentUri) {
                    for (const constant of file.constants) {
                        if (constant.name === name) {
                            constant.value = valueBox.value;
                            break;
                        }
                    }
                    break;
                }
            }
            vscode.setState({ tools: oldState.tools, documentVars: oldState.documentVars, parameters: oldState.parameters, currentUri: oldState.currentUri, currentTool: oldState.currentTool });
        });

        li.appendChild(valueBox);

        ul.appendChild(li);
    }

    function addDistributionItem(ul, name, value) {
        const li = document.createElement("li");

        const nameBox = document.createElement("label");
        nameBox.id = "name-" + name;
        nameBox.appendChild(document.createTextNode(name));
        nameBox.classList.add("name");

        li.appendChild(nameBox);

        const valueBox = document.createElement("input");
        valueBox.id = "value-" + name;
        valueBox.type = "text";
        valueBox.value = value;
        valueBox.classList.add("value");
            let oldState = vscode.getState();
            for (const file of oldState.documentVars) {
                if (file.uri === oldState.currentUri) {
                    for (const distribution of file.distributions) {
                        if (distribution.name === name) {
                            distribution.value = valueBox.value;
                            break;
                        }
                    }
                    break;
                }
            }
            vscode.setState({ tools: oldState.tools, documentVars: oldState.documentVars, parameters: oldState.parameters, currentUri: oldState.currentUri, currentTool: oldState.currentTool });
        });

        li.appendChild(valueBox);

        ul.appendChild(li);
    }


    function addCategory(category) {
        const outerDiv = document.createElement("div");
        outerDiv.classList.add("pane", "vertical");

        const firstInnerDiv = document.createElement("div");
        firstInnerDiv.classList.add("pane-header");

        const codiconDiv = document.createElement("div");
        codiconDiv.classList.add("codicon", "codicon-chevron-right");

        const h3 = document.createElement("h3");
        h3.classList.add("title");
        h3.appendChild(document.createTextNode(category));

        const secondInnerDiv = document.createElement("div");
        secondInnerDiv.classList.add("pane-body");

        const categoryUl = document.createElement("ul");
        categoryUl.id = "parameter-" + category;
        categoryUl.classList.add("option-list");

        secondInnerDiv.appendChild(categoryUl);

        const revertButton = document.createElement("i");
        revertButton.classList.add("revert", "codicon", "codicon-discard", "hidden", "outer");

        firstInnerDiv.appendChild(codiconDiv);
        firstInnerDiv.appendChild(h3);
        firstInnerDiv.appendChild(revertButton);

        outerDiv.appendChild(firstInnerDiv);
        outerDiv.appendChild(secondInnerDiv);

        const parametersDiv = document.getElementById("parameters");
        parametersDiv.appendChild(outerDiv);

        addCollapseBehaviour(outerDiv);

        revertButton.addEventListener("click", e => {
            for (const revert of categoryUl.querySelectorAll(".revert:not(.hidden):not(.outer)")) {
                revert.click();
            }
            revertButton.classList.add("hidden");
            e.stopPropagation();
        });

        revertButtons.push(function(id, hidden) {
            for (const button of categoryUl.querySelectorAll(".revert")){
                if (button.id === id) {
                    if (hidden){
                        revertButton.classList.add("hidden");
                    } else {
                        revertButton.classList.remove("hidden");
                    }
                } else if (!button.classList.contains("hidden")) {
                    revertButton.classList.remove("hidden");
                    return;
                }
            }
        });

    function addParameterItem(name, value, category, type, description, possibleValues, defaultValue) {
        if (category === "") {
            category = "Other";
        }
        let categoryUl = document.getElementById("parameter-" + category);
        if (categoryUl === null) {
            categoryUl = addCategory(category);
        }

        const li = document.createElement("li");

        const nameBox = document.createElement("label");
        nameBox.id = "name-" + name;
        nameBox.appendChild(document.createTextNode(name));
        nameBox.classList.add("name");
            for (const value of possibleValues) {
                const option = document.createElement("option");
                option.value = value;
                option.text = value;
                valueBox.appendChild(option);
            }

            valueBox.value = value;

            valueBox.addEventListener("input", _ => {
                if (valueBox.value === defaultValue) {
                    valueBox.revertButton.classList.add("hidden");
                } else {
                    valueBox.revertButton.classList.remove("hidden");
                }
                revertButtons.forEach(x => x(valueBox.revertButton.id, valueBox.value === defaultValue));
                let oldState = vscode.getState();
                for (const tool of oldState.parameters) {
                    if (tool.toolName === oldState.currentTool) {
                        for (const parameter of tool.parameters) {
                            if (parameter.name === name) {
                                parameter.value = valueBox.options[valueBox.selectedIndex].value;
                                break;
                            }
                vscode.setState({ tools: oldState.tools, documentVars: oldState.documentVars, parameters: oldState.parameters, currentUri: oldState.currentUri, currentTool: oldState.currentTool });
            });
        } else {
            valueBox = document.createElement("input");
            if (type === "Boolean") {
                valueBox.type = "checkbox";
                valueBox.checked = value;
                valueBox.addEventListener("input", _ => {
                    if (valueBox.checked === defaultValue) {
                        valueBox.revertButton.classList.add("hidden");
                    } else {
                        valueBox.revertButton.classList.remove("hidden");
                    }

                    revertButtons.forEach(x => x(valueBox.revertButton.id, valueBox.checked === defaultValue));

                    let oldState = vscode.getState();
                    for (const tool of oldState.parameters) {
                        if (tool.toolName === oldState.currentTool) {
                            for (const parameter of tool.parameters) {
                                if (parameter.name === name) {
                                    parameter.value = valueBox.checked;
                                    break;
                                }
                            }
                            break;
                        }
                    }
                    vscode.setState({ tools: oldState.tools, documentVars: oldState.documentVars, parameters: oldState.parameters, currentUri: oldState.currentUri, currentTool: oldState.currentTool });
                });
            } else if (type === "Path") {
                valueBox.type = "file";
                valueBox.value = value;
                valueBox.addEventListener("input", _ => {
                    if (String(valueBox.value) === String(defaultValue)) {
                        valueBox.revertButton.classList.add("hidden");
                    } else {
                        valueBox.revertButton.classList.remove("hidden");
                    }

                    revertButtons.forEach(x => x(valueBox.revertButton.id, String(valueBox.value) === String(defaultValue)));

                    let oldState = vscode.getState();
                    for (const tool of oldState.parameters) {
                        if (tool.toolName === oldState.currentTool) {
                            for (const parameter of tool.parameters) {
                                if (parameter.name === name) {
                                    parameter.value = valueBox.value;
                                    break;
                                }
                            }
                            break;
                        }
                    }
                    vscode.setState({ tools: oldState.tools, documentVars: oldState.documentVars, parameters: oldState.parameters, currentUri: oldState.currentUri, currentTool: oldState.currentTool });
                });
            } else {
                valueBox.type = "text";
                valueBox.value = value;
                valueBox.addEventListener("input", _ => {
                    // TODO: Store as actual type using valueBox.value.asNumber and such?
                    if (type === "Real") {
                    if (String(valueBox.value) === String(defaultValue)) {
                        valueBox.revertButton.classList.add("hidden");
                    } else {
                        valueBox.revertButton.classList.remove("hidden");
                    }

                    revertButtons.forEach(x => x(valueBox.revertButton.id, String(valueBox.value) === String(defaultValue)));

                    let oldState = vscode.getState();
                    for (const tool of oldState.parameters) {
                        if (tool.toolName === oldState.currentTool) {
                            for (const parameter of tool.parameters) {
                                if (parameter.name === name) {
                    vscode.setState({ tools: oldState.tools, documentVars: oldState.documentVars, parameters: oldState.parameters, currentUri: oldState.currentUri, currentTool: oldState.currentTool });
                });
            }
        }
        nameBox.htmlFor = "value-" + name;
        valueBox.id = "value-" + name;
        valueBox.classList.add("value");

        const valueDiv = document.createElement("div");
        valueDiv.classList.add("value");
        valueDiv.appendChild(valueBox);
        const revertButton = document.createElement("button");
        revertButton.id = "revert-" + name;
        const codicon = document.createElement("i");
        codicon.classList.add("codicon", "codicon-discard");
        revertButton.appendChild(codicon);
        revertButton.classList.add("revert");
        if (String(value) === String(defaultValue)) {
            revertButton.classList.add("hidden");
            revertButtons.forEach(x => x(revertButton.id, false));
        revertButton.title = "Revert to default";
        valueBox.revertButton = revertButton;
        revertButton.addEventListener("click", _ => {

            revertButtons.forEach(x => x(valueBox.revertButton.id, valueBox.value === defaultValue));

            valueBox.dispatchEvent(new InputEvent("input"));
        });

        valueDiv.appendChild(revertButton);

        li.appendChild(valueDiv);

        categoryUl.appendChild(li);
    }
    //#endregion