/********** Javascript for Menus *********/

"use strict"; // Required to be at the top of the script, but used only for development.

// ********** You may modify the values of the variables below **********
const MainMenuText = "Main Menu"; // All of the header 1 tags are gathered and placed in a "main menu" at the top of the page; This is the text used to announced it
const allowedShortcuts = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZäöüß"; // Says what characters can be used for shortcuts
const emojiClipboardWithShortcut = "📋";
const emojiClipboardNoShortcut = "📌";
const emojiLinkWithShortcut = "⭐";
const emojiLinkNoShortcut = "🔗";
const emojiHeaderWithShortcut = "🔷";
const emojiHeaderNoShortcut = "🔶";
// ********** You may modify the values of the variables above **********

// <div id="openInNewTabByDefault"></div>
// <div id="tableOfContents"></div>
// <div id="docTitle">Keyboard Driven Menu System</div>

var menuGeneratedTree = null; // This is populated upon start up of page
var currentMenuNode = null; // This is populated upon start up of page
var timerIdNoticeWindow = null;
var timerIdMenuWindow = null;
var timerIdErrorWindow = null;
var eraseErrorWindow = true;
var floatingWindowMenu = null;
var floatingWindowNotice = null;
var floatingWindowError = null;

// Define custom elements and make them accessible in Javascript
class MenuGeneratedTag extends HTMLElement {constructor() {super();}}
customElements.define('menu-generated', MenuGeneratedTag);
class HeaderZeroTag extends HTMLElement {constructor() {super();}}
customElements.define('menu-h0', HeaderZeroTag);
class NameTag extends HTMLElement {constructor() {super();}}
customElements.define('menu-name', NameTag);
class DescriptionTag extends HTMLElement {constructor() {super();}}
customElements.define('menu-description', DescriptionTag);

class MenuGeneratedTreeNode {
    constructor(topNode = true) {
        this.parent = this;
        this.superparentNode = this; // Super parent is the initial Menu Generated Object; All other sub-MenuGenerated objects should be created by "addMenuGenerated"
        this.desiredShortcut = ""; // Desired shortcut which will jump to this menu, but shortcut is not guaranteed
        this.desiredShortcutToBeUsed = false;
        this.linkAndClipboardArray = [];
        this.subNodeArray = [];
        this.activeShortcuts = new Map;
        this.originalElement = null;
        this.newElement = null;
        this.headerLevel = 0; // We will use an artificial header level of zero to represent a gathering of all H1 headers
        this.uniqueIdCounter = 0;
        this.availableShortcuts = allowedShortcuts; // Used for linkAndClipboardArray and subMenuGeneratedArray, but not for this.desiredShortcut
        if (topNode) {
            this.#fillInMissingHeaders();
            this.#buildInitialTreeNodeStructure();
            this.#populateTreeNodeStructure();
            this.#processAndActivateShortcuts();
            this.#addMenusToHTML();
            this.#makeInvisibleOriginalMenuInformation();
            this.#populateTableOfContents();
            this.#moveTitle();
        }
    }
    #addNode(originalElement) {
        let newMenuGeneratedTreeNode = new MenuGeneratedTreeNode(false);
        newMenuGeneratedTreeNode.parent = this;
        newMenuGeneratedTreeNode.superparentNode = this.superparentNode;
        newMenuGeneratedTreeNode.uniqueIdCounter = -9999;
        newMenuGeneratedTreeNode.headerLevel = this.headerLevel + 1;
        newMenuGeneratedTreeNode.originalElement = originalElement;
        newMenuGeneratedTreeNode.desiredShortcut = originalElement.getAttribute("shortcut");
        this.subNodeArray.push(newMenuGeneratedTreeNode);
        return newMenuGeneratedTreeNode;
    }
    #getUniqueIdCounter() {
        this.superparentNode.uniqueIdCounter++;
        return this.superparentNode.uniqueIdCounter;
    }
    #getFlattenedElements(currentElement, flatElementArray) {
        // Populates flatElementArray through recursion; initial currentElement should be document.body
        flatElementArray.push(currentElement);
        for (const child of currentElement.children) {
            this.#getFlattenedElements(child, flatElementArray);
        }
    }
    #fillInMissingHeaders(currentElement = null, headerStackArray = null) {
        if (currentElement === null) {
            this.#fillInMissingHeaders(document.body, [document.createElement("H0")]);
        } else { // currentElement and headerStackArray should have a value
            /* If any missing headers are inserted, then the list of children should not include the inserted headers. For this reason, we have to save the original list of children before any headers are inserted or else the algorithm will traverse the missing elements too */
            let children = [];
            for (let child of currentElement.children) {
                children.push(child);
            }
            // If the current element is a header...
            if (["H1", "H2", "H3", "H4", "H5", "H6"].includes(currentElement.tagName)) {
                let oldHeaderLevel = parseInt(headerStackArray[headerStackArray.length - 1].tagName.charAt(1));
                let newHeaderLevel = parseInt(currentElement.tagName.charAt(1));
                if (newHeaderLevel - oldHeaderLevel < 0) { // This handles cases like jumping from H5 to H2 or from H2 to H1
                    while (newHeaderLevel - oldHeaderLevel < 0) {
                        headerStackArray.pop();
                        oldHeaderLevel = parseInt(headerStackArray[headerStackArray.length - 1].tagName.charAt(1));
                    }
                } else if (newHeaderLevel - oldHeaderLevel == 1) { // This handles the cases like jumping from H2 to H3 which is permissible
                    headerStackArray.push(currentElement);
                } else if (newHeaderLevel - oldHeaderLevel > 1) { // This handles the cases like jumping from H2 to H5 (which is the problem we're trying to alert the user to) or H2 to H3 (which is typical and acceptable)
                    while (newHeaderLevel - oldHeaderLevel > 1) { // We deal with missing headers
                        let newText = "*** Missing H" + (oldHeaderLevel + 1) + " Header ***";
                        let newElement = document.createElement("H" + (oldHeaderLevel + 1));
                        newElement.textContent = newText;
                        currentElement.before(newElement); // Insert newElement right before currentElement
                        headerStackArray.push(newElement);
                        oldHeaderLevel = parseInt(headerStackArray[headerStackArray.length - 1].tagName.charAt(1));
                    }
                    // newHeaderLevel - oldHeaderLevel == 1; This kind of header is expected from the user
                    headerStackArray.push(currentElement);
                } else if (newHeaderLevel - oldHeaderLevel == 0) { // This handles the cases where headers remain the same
                    headerStackArray.pop();
                    headerStackArray.push(currentElement);
                } else {
                    console.error ("Something went wrong with header level calculations.");
                }
            }
            for (const child of children) {
                this.#fillInMissingHeaders(child, headerStackArray);
            }
        }
    }
    #buildInitialTreeNodeStructure() {
        /* This builds the tree node structure, and populates the originalElement, and linkAndClipboardArray variables */
        let currentNode = this;
        let flatElementArray = [];
        this.#getFlattenedElements(document.body, flatElementArray);
        for (let element of flatElementArray) {
            if (["H1", "H2", "H3", "H4", "H5", "H6"].includes(element.tagName)) {
                let newHeaderLevel = parseInt(element.tagName.charAt(1));
                let currentHeaderLevel = currentNode.headerLevel;
                if (newHeaderLevel - currentHeaderLevel < 0) { // This handles cases like jumping from H5 to H2
                    while (newHeaderLevel - currentHeaderLevel < 0) {
                        currentNode = currentNode.parent;
                        currentHeaderLevel = currentNode.headerLevel;
                    }
                    let newNode = currentNode.parent.#addNode(element);
                    currentNode = newNode;
                    currentHeaderLevel = currentNode.headerLevel;
                } else if (newHeaderLevel - currentHeaderLevel == 1) { // This handles cases like jumping from H2 to H3
                    let newNode = currentNode.#addNode(element);
                    currentNode = newNode;
                    currentHeaderLevel = currentNode.headerLevel;
                } else if (newHeaderLevel - currentHeaderLevel == 0) {
                    let newNode = currentNode.parent.#addNode(element);
                    currentNode = newNode;
                } else {
                    console.error ("Something went wrong with header level calculations.");
                }
            } else if (element.tagName === 'MENU') {
                currentNode.linkAndClipboardArray.push(element);
            }
        }
    }
    #populateTreeNodeStructure(currentTreeNode = null) {
        /* Using originalElement, this function populates the newElement variable of this object with the <menu-generated> tag, but the <menu-generated> is only sparsely filled out */
        if (currentTreeNode === null) {
            // We assume this is the superparent and that the superparent elements need to be properly initialized, then we can start the process of making menus starting with H0
            let H0OriginalElement = document.createElement('H0'); // H0 is an artificial header representing the a collection of all H1 elements
            H0OriginalElement.textContent = MainMenuText;
            document.body.insertBefore(H0OriginalElement, document.body.firstChild); // We place the Table of Contents at the very top
            this.originalElement = H0OriginalElement; // H0 needs the original element defined before the main algorithm starts because all children rely on their parents; H0's parent will be defined as itself.
            this.#populateTreeNodeStructure(this);
        } else {
            currentTreeNode.newElement = document.createElement('menu-generated');
            currentTreeNode.newElement.parentTreeNode = currentTreeNode;
            currentTreeNode.newElement.id = "menu-generated-id" + currentTreeNode.#getUniqueIdCounter();
            currentTreeNode.originalElement.parentNode.insertBefore(currentTreeNode.newElement, currentTreeNode.originalElement);
            let headerTag = document.createElement(currentTreeNode.originalElement.tagName);
            headerTag.textContent = currentTreeNode.originalElement.textContent;
            headerTag.id = "menu-generated-id" + currentTreeNode.#getUniqueIdCounter();
            currentTreeNode.newElement.appendChild(headerTag);
            for (let childTreeNode of currentTreeNode.subNodeArray) {
                this.#populateTreeNodeStructure(childTreeNode);
            }
        }
    }
    #processAndActivateShortcuts(currentTreeNode = null) {
        /* Fill out activeShortcuts in this object */
        if (currentTreeNode === null) {
            this.#processAndActivateShortcuts(this);
        } else {
            let errorNum = 0;
            // Process the shortcuts of the children first
            if (currentTreeNode.subNodeArray.length > 0) {
                for (let childTreeNode of currentTreeNode.subNodeArray) {
                    this.#processAndActivateShortcuts(childTreeNode);
                }
            }
            // The headers need to be added to the active shortcuts in order to preserve the order. Therefore, we must see which shortcuts are viable and which ones aren't before we add them to the active shortcuts 
            for (let childTreeNode of currentTreeNode.subNodeArray) {
                if (childTreeNode.desiredShortcut !== null && currentTreeNode.availableShortcuts.includes(childTreeNode.desiredShortcut)) {
                    // The shortcut is defined and not yet used. We remove the desired shortcut from the available list of shortcuts and mark the shortcut to be used
                    let shortcutIndex = currentTreeNode.availableShortcuts.indexOf(childTreeNode.desiredShortcut);
                    currentTreeNode.availableShortcuts = currentTreeNode.availableShortcuts.slice(0, shortcutIndex) + currentTreeNode.availableShortcuts.slice(shortcutIndex + 1);
                    childTreeNode.desiredShortcutToBeUsed = true;
                } else {
                    childTreeNode.desiredShortcutToBeUsed = false;
                }
            }
            // The links and clipboards will be added to the active shortcuts in order to preserve order. Therefore, we must see which shortcuts are viable and which ones aren't before we add them to the active shortcuts
            for (let linkAndClipboardElement of currentTreeNode.linkAndClipboardArray) {
                if (linkAndClipboardElement.getAttribute("shortcut") !== null && currentTreeNode.availableShortcuts.includes(linkAndClipboardElement.getAttribute("shortcut"))) {
                    // The shortcut is defined and not yet used. We remove the desired shortcut from the available list of shortcuts and mark the shortcut to be used
                    let shortcutIndex = currentTreeNode.availableShortcuts.indexOf(linkAndClipboardElement.getAttribute("shortcut"));
                    currentTreeNode.availableShortcuts = currentTreeNode.availableShortcuts.slice(0, shortcutIndex) + currentTreeNode.availableShortcuts.slice(shortcutIndex + 1);
                    linkAndClipboardElement.desiredShortcutToBeUsed = true;
                } else {
                    linkAndClipboardElement.desiredShortcutToBeUsed = false;
                }
            }
            // The headers will now be added in order
            for (let childTreeNode of currentTreeNode.subNodeArray) {
                let shortcut = "";
                if (childTreeNode.desiredShortcutToBeUsed) {
                    shortcut = childTreeNode.desiredShortcut
                    currentTreeNode.activeShortcuts.set(shortcut,childTreeNode);
                } else {
                    if (currentTreeNode.availableShortcuts.length === 0) {
                        // Not enough shortcuts are available
                        shortcut = "Error " + errorNum + ": Too many shortcuts used or not enough shortcuts allowed"
                        this.reportError(shortcut);
                        errorNum++;
                    } else {
                        // Pick the next available shortcut
                        shortcut = currentTreeNode.availableShortcuts[0];
                        // Remove the desired shortcut from the available list of shortcuts
                        currentTreeNode.availableShortcuts = currentTreeNode.availableShortcuts.slice(1);
                    }
                    // Define shortcut in node
                    currentTreeNode.activeShortcuts.set(shortcut, childTreeNode);
                }
            }
            // The links and clipboards will now be added in order
            for (let linkAndClipboardElement of currentTreeNode.linkAndClipboardArray) {
                let shortcut = "";
                if (linkAndClipboardElement.desiredShortcutToBeUsed) {
                    shortcut = linkAndClipboardElement.getAttribute("shortcut");
                    currentTreeNode.activeShortcuts.set(shortcut, linkAndClipboardElement);
                } else {
                    if (currentTreeNode.availableShortcuts.length === 0) {
                        // Not enough shortcuts are available
                        shortcut = "Error " + errorNum + ": Too many shortcuts used or not enough shortcuts allowed"
                        this.reportError(shortcut);
                        errorNum++;
                    } else {
                        // Pick the next available shortcut
                        shortcut = currentTreeNode.availableShortcuts[0];
                        // Remove the desired shortcut from the available list of shortcuts
                        currentTreeNode.availableShortcuts = currentTreeNode.availableShortcuts.slice(1);
                    }
                    // Define shortcut in node
                    currentTreeNode.activeShortcuts.set(shortcut, linkAndClipboardElement);
                }
            }
        }
    }
    #addMenusToHTML(currentTreeNode = null) {
        /* This function further populates the <menu-generated> tag in newElement with linkArray and clipboardArray information */
        if (currentTreeNode === null) {
            this.#addMenusToHTML(this);
        } else {
            if (currentTreeNode.subNodeArray.length > 0) {
                for (let childTreeNode of currentTreeNode.subNodeArray) {
                    this.#addMenusToHTML(childTreeNode);
                }
            }
            for (let activeShortcut of currentTreeNode.activeShortcuts.keys()) {
                let emoji = "";
                let element = currentTreeNode.activeShortcuts.get(activeShortcut);
                if (element instanceof HTMLElement) { // This is either a link or a clipboard
                    if (element.getAttribute("link") !== null) {
                        if (element.desiredShortcutToBeUsed) {
                            emoji = emoji + emojiLinkWithShortcut;
                        } else {
                            emoji = emoji + emojiLinkNoShortcut;
                        }
                    }
                    if (element.getAttribute("clipboard") !== null) {
                        if (element.desiredShortcutToBeUsed) {
                            emoji = emoji + emojiClipboardWithShortcut;										
                        } else {
                            emoji = emoji + emojiClipboardNoShortcut;
                        }
                    }
                    let newElementName = document.createElement('menu-name');
                    newElementName.onclick = function (event) {
                        if (currentMenuNode.newElement.id == event.target.parentNode.id) {
                            currentTreeNode.activateLinkOrClipboard(element);
                        } else {
                            let errorMessage = 'Mouse was clicked on an inactive menu. Please click on the active menu.';
                            currentMenuNode.reportError(errorMessage, 5000);
                        }
                    }
                    newElementName.textContent = emoji + " " + activeShortcut + ") " + element.textContent;
                    let newElementDescription = document.createElement('menu-description');
                    newElementDescription.textContent = element.getAttribute("description");
                    currentTreeNode.newElement.appendChild(newElementName);
                    currentTreeNode.newElement.appendChild(newElementDescription);
                } else { // This is a header
                    if (element.desiredShortcutToBeUsed) {
                        emoji = emojiHeaderWithShortcut;
                    } else {
                        emoji = emojiHeaderNoShortcut;
                    }
                    let newElementName = document.createElement('menu-name');
                    newElementName.onclick = function (event) {
                        if (currentMenuNode.newElement.id == event.target.parentNode.id) {
                            let childNode = currentTreeNode.activeShortcuts.get(activeShortcut);
                            currentTreeNode.updateHeader(childNode);
                        } else {
                            let errorMessage = 'Mouse was clicked on an inactive menu. Please click on the active menu.';
                            currentMenuNode.reportError(errorMessage, 5000);
                        }
                    }
                    newElementName.textContent = emoji + " " + activeShortcut + ") " + element.originalElement.textContent;
                    let newElementDescription = document.createElement('menu-description');
                    newElementDescription.textContent = element.originalElement.getAttribute("description");
                    currentTreeNode.newElement.appendChild(newElementName);
                    currentTreeNode.newElement.appendChild(newElementDescription);
                }
            }
        }
    }
    #makeInvisibleOriginalMenuInformation(currentTreeNode = null) {
        if (currentTreeNode === null) {
            this.#makeInvisibleOriginalMenuInformation(this);
        } else {
            if (currentTreeNode.subNodeArray.length > 0) {
                for (let childTreeNode of currentTreeNode.subNodeArray) {
                    this.#makeInvisibleOriginalMenuInformation(childTreeNode);
                }
            }
            for (let activeShortcut of currentTreeNode.activeShortcuts.keys()) {
                let element = currentTreeNode.activeShortcuts.get(activeShortcut);
                if (element instanceof HTMLMenuElement) {
                    element.style.display = 'none'; // Make menu element disappear
                }
            }
            currentTreeNode.originalElement.style.display = 'none'; // Make header disappear
        }
    }
    #populateTableOfContents(currentTreeNode = null){
        let output = "";
        if (currentTreeNode === null) {
            let divTableOfContents = document.getElementById('tableOfContents');
            if (divTableOfContents !== null) {
                output = this.#populateTableOfContents(this);
                let elementTableOfContents = document.createElement('div');
                output = "<h1>Table of Contents</h1>" + output;
                elementTableOfContents.innerHTML = output;
                document.body.insertBefore(elementTableOfContents, document.body.firstChild);
            } else {
                // Table of contents is not turned on; Do nothing
            }
        } else {
            let tocStyle = "toc" + parseInt(currentTreeNode.newElement.children[0].tagName.charAt(1));
            output = "<div class='" + tocStyle + " table-of-contents' onclick='let element=document.getElementById(&quot;" + currentTreeNode.newElement.children[0].id + "&quot;); element.scrollIntoView({behavior: &quot;smooth&quot;});'>" + currentTreeNode.newElement.children[0].textContent + "</div>";
            if (currentTreeNode.subNodeArray.length > 0) {
                for (let childTreeNode of currentTreeNode.subNodeArray) {
                    output = output + this.#populateTableOfContents(childTreeNode);
                }
            }
        }
        return output;
    }
    #moveTitle() {
        if (document.getElementById('docTitle') !== null) {
            document.body.insertBefore(document.getElementById('docTitle'), document.body.firstChild);
        }
    }
    reportError(errorMessage, millisecondsToShow=10000) {
        if (eraseErrorWindow) {
            floatingWindowError.innerHTML = "";	
        }
        eraseErrorWindow = false;
        floatingWindowError = document.getElementById('floatingWindowError');
        if (floatingWindowError.innerHTML.length > 0) {
            floatingWindowError.innerHTML = floatingWindowError.innerHTML + "<br>" + errorMessage;
        } else {
            floatingWindowError.innerHTML = errorMessage;
        }
        clearTimeout(timerIdErrorWindow);
        floatingWindowError.style.opacity = "1";
        timerIdErrorWindow = setTimeout(() => {floatingWindowError.style.opacity = "0"; eraseErrorWindow = true;}, millisecondsToShow); // Wait a certain number of milliseconds before making menu window disappear
    }
    getTraceInfoForUserInHTML(currentTreeNode = null) {
        let output = "";
        if (currentTreeNode === null) {
            output = this.getTraceInfoForUserInHTML(this);
        } else if (currentTreeNode === currentTreeNode.superparentNode) {
            output = "<div>" + currentTreeNode.newElement.children[0].textContent + "</div>";
        } else {
            output = this.getTraceInfoForUserInHTML(currentTreeNode.parent);
            output = output + "<hr><div>" + currentTreeNode.newElement.children[0].textContent + "</div>";
        }
        return output;
    }
    updateHeader(newMenuNode) {
        currentMenuNode.newElement.children[0].classList.remove('menu-selected');
        currentMenuNode = newMenuNode;
        currentMenuNode.newElement.scrollIntoView({ behavior: 'smooth' });
        currentMenuNode.newElement.focus();
        currentMenuNode.newElement.children[0].classList.add('menu-selected');
        floatingWindowMenu = document.getElementById('floatingWindowMenu');
        floatingWindowMenu.innerHTML = currentMenuNode.getTraceInfoForUserInHTML();
        clearTimeout(timerIdMenuWindow);
        floatingWindowMenu.style.opacity = "1";
        timerIdMenuWindow = setTimeout(() => {floatingWindowMenu.style.opacity = "0";}, 3000); // Wait a certain number of milliseconds before making menu window disappear
    }
    activateLinkOrClipboard(element) {
        if (element.getAttribute("clipboard") !== null) {
            let clipboardText = element.getAttribute("clipboard");
            navigator.clipboard.writeText(clipboardText);
            floatingWindowNotice = document.getElementById('floatingWindowNotice');
            floatingWindowNotice.textContent = "Copied to Clipboard: " + clipboardText;
            clearTimeout(timerIdNoticeWindow);
            floatingWindowNotice.style.opacity = "1"; // Make notice window appear
            timerIdNoticeWindow = setTimeout(() => {floatingWindowNotice.style.opacity = "0";}, 3000); // Wait a certain number of milliseconds before making notice window disappear
            currentMenuNode.newElement.children[0].classList.remove('menu-selected');
            currentMenuNode = currentMenuNode.superparentNode;
            currentMenuNode.newElement.scrollIntoView({ behavior: 'smooth' });
            currentMenuNode.newElement.focus();
        }
        if (element.getAttribute("link") !== null) {
            window.scroll({top: 0, behavior: 'instant'}); /* Scrolling must be behavior:"instant" or the focus won't work properly as the window opens */
            if (element.getAttribute("linkNew") !== null) {
                window.open(element.getAttribute("link"), '_blank');
            } else if (element.getAttribute("linkSame") !== null) {
                window.open(element.getAttribute("link"), '_self');
            } else if (document.getElementById('openInNewTabByDefault') === null) {
                window.open(element.getAttribute("link"), '_self');
            } else {
                window.open(element.getAttribute("link"), '_blank');
            }
            currentMenuNode.newElement.children[0].classList.remove('menu-selected');
            currentMenuNode = currentMenuNode.superparentNode;
            currentMenuNode.newElement.scrollIntoView({ behavior: 'smooth' });
            currentMenuNode.newElement.focus();
        }
    }
}

document.addEventListener('keydown', (e) => {
    // Capture key presses and drive the menu with what you see
    let keyPressed = e.key;
    let unwantedPressedModifiers = ["Control", "Alt", "AltGraph"].filter(mod => e.getModifierState(mod));
    if (unwantedPressedModifiers.length > 0) {
        // Do nothing; By ignoring control, alt, and altGraph presses, we allow the webbrowser functionality to continue working instead of overriding the keys
    } else if (keyPressed.valueOf() == "Escape") {
        currentMenuNode.newElement.children[0].classList.remove('menu-selected');
        currentMenuNode = currentMenuNode.parent;
        currentMenuNode.newElement.scrollIntoView({ behavior: 'smooth' });
        currentMenuNode.newElement.focus();
        currentMenuNode.newElement.children[0].classList.add('menu-selected');
        floatingWindowMenu = document.getElementById('floatingWindowMenu');
        floatingWindowMenu.innerHTML = currentMenuNode.getTraceInfoForUserInHTML();
        clearTimeout(timerIdMenuWindow);
        floatingWindowMenu.style.opacity = "1";
        timerIdMenuWindow = setTimeout(() => {floatingWindowMenu.style.opacity = "0";}, 3000); // Wait a certain number of milliseconds before making menu window disappear
    } else if (keyPressed.valueOf() == "F1") {
        clearTimeout(timerIdMenuWindow);
        if (floatingWindowMenu.style.opacity > 0) {
            floatingWindowMenu.style.opacity = "0";
        } else {
            floatingWindowMenu.style.opacity = "1";
        }
        e.preventDefault();
    } else if (Array.from(currentMenuNode.activeShortcuts.keys()).includes(keyPressed)) {
        if (currentMenuNode.activeShortcuts.get(keyPressed) instanceof HTMLElement) { // link or clipboard detected
            let element = currentMenuNode.activeShortcuts.get(keyPressed);
            currentMenuNode.activateLinkOrClipboard(element);
        } else { // Header detected
            let newMenuNode = currentMenuNode.activeShortcuts.get(keyPressed);
            currentMenuNode.updateHeader(newMenuNode);
        } 
        e.preventDefault();
    } else if (currentMenuNode.availableShortcuts.includes(keyPressed)) {
        let errorMessage = 'Key pressed is not part of current menu<br><br>Key Pressed = "' + keyPressed + '"';
        currentMenuNode.reportError(errorMessage, 5000);
    }
});

document.addEventListener('DOMContentLoaded', () => {
    // Create floating windows at the very top of the DOM with Javascript so the div tags do not have to be written by the developer in the body tag
    // Creating menu window
    floatingWindowMenu = document.createElement('div');
    floatingWindowMenu.id = 'floatingWindowMenu';
    floatingWindowMenu.className = 'floating-window floating-window-menu';
    floatingWindowMenu.textContent = MainMenuText;
    document.body.insertBefore(floatingWindowMenu, document.body.firstChild);
    // Creating notification window
    floatingWindowNotice = document.createElement('div');
    floatingWindowNotice.id = 'floatingWindowNotice';
    floatingWindowNotice.className = 'floating-window floating-window-notice';
    floatingWindowNotice.textContent = '';
    document.body.insertBefore(floatingWindowNotice, document.body.firstChild);
    // Creating error window
    floatingWindowError = document.createElement('div');
    floatingWindowError.id = 'floatingWindowError';
    floatingWindowError.className = 'floating-window floating-window-error';
    floatingWindowError.textContent = '';
    document.body.insertBefore(floatingWindowError, document.body.firstChild);

    // Replace user defined elements and headers with a reactive menu system
    menuGeneratedTree = new MenuGeneratedTreeNode();
    currentMenuNode = this.menuGeneratedTree.superparentNode;
    currentMenuNode.newElement.children[0].classList.add('menu-selected');

});
