# Source: MenuItem.js

    /**
     * A menu entry of a {@link Menu}.
     * <p>Part of the XUI module which, for now, has an undocumented API.
     */
    export class MenuItem extends HTMLElement {
        static create(options) {
            // Needed because a custom HTML element constructor may not have
            // arguments.
            let menuItem  = document.createElement("xui-menu-item");

            if (options !== null) {
                // Configure. options is copied. We do not keep a reference to it.
                menuItem.options = options;
            }

            return menuItem;
        }

        constructor() {
            super();

            let shadow = this.attachShadow({mode: "open"});

            Util.addStylesheetLink(shadow);

            this._item = document.createElement("div");
            this._item.className = "xui-control xui-menu-item";
            shadow.appendChild(this._item);

            this._icon = document.createElement("div");
            this._icon.className = "xui-menu-item-icon";
            this._item.appendChild(this._icon);

            this._text = document.createElement("div");
            this._text.className = "xui-menu-item-text";
            this._item.appendChild(this._text);

            this._detail = document.createElement("div");
            this._detail.className = "xui-menu-item-detail";
            this._item.appendChild(this._detail);

            this._parentMenu = null;
            this._submenu = null;
            this._options = { name: null, type: null, buttonGroup: null,
                              icon: null, selectedIcon: null, tooltip: null,
                              text: null, detail: null, items: null,
                              selected: false, enabled: true, separator: false };
        }

        get contents() {
            return this._item;
        }

        get options() {
            return this._options;
        }

        set options(options) {
            // Merge. Do not replace.
            for (const key in options) {
                this._options[key] = options[key];
            }

            this.configure();
        }

        getOption(key) {
            return this._options[key];
        }

        setOption(key, value) {
            switch (key) {
            case "name":
                this.name = value;
                break;
            case "tooltip":
                this.tooltip = value;
                break;
            case "selected":
                this.selected = value;
                break;
            case "enabled":
                this.enabled = value;
                break;
            case "separator":
                this.separator = value;
                break;
            default:
                this._options[key] = value;
                this.configure();
                break;
            }
        }

        get itemType() {
            return this._options.type;
        }

        get isIconOnly() {
            // Hence cannot be a type=submenu as a submenu may not have an icon.
            // Plus it may not have a detail as it has no text.
            return (this._options.icon !== null && this._options.text === null);
        }

        get name() {
            return this._options.name;
        }

        set name(name) {
            this._options.name = MenuItem.normalizeString(name);
        }

        static normalizeString(value) {
            if (typeof value === "string") {
                // String.trim also trims '\u00A0'.
                const preserveNbsp = (value.indexOf("\u00A0") >= 0);
                if (preserveNbsp) {
                    value = value.replace("\u00A0", "\uEDCB");
                }
                value = value.trim();
                if (preserveNbsp) {
                    value = value.replace("\uEDCB", "\u00A0");
                }

                if (value.length === 0) {
                    value = null;
                }
            } else {
                value = null;
            }

            return value;
        }

        get tooltip() {
            return this._options.tooltip;
        }

        set tooltip(tooltip) {
            this._options.tooltip = MenuItem.normalizeString(tooltip);
            if (this._options.tooltip === null) {
                this.removeAttribute("title");
            } else {
                this.setAttribute("title", this._options.tooltip);
            }
        }

        get selected() {
            return this._options.selected;
        }

        set selected(selected) {
            this.setSelected(selected);
            if (this._options.selected) {
                this.onSelected();
            }
        }

        setSelected(selected) {
            this._options.selected = MenuItem.normalizeBoolean(selected);
            this.updateIcon(this._options.selected, this._options.enabled);
        }

        static normalizeBoolean(value) {
            return value? true : false;
        }

        updateIcon(selected, enabled) {
            this._icon.textContent = ""; // This also removes all child nodes.

            let icon = selected? this._options.selectedIcon : this._options.icon;
            if (icon !== null) {
                let iconName = null;
                let iconFg = null;
                let iconBg = null;

                if (icon.startsWith("url(") && icon.endsWith(")")) {
                    let iconElem =
                        MenuItem.createIcon(icon, MenuItem.SMALL_ICON_SIZE);
                    this._icon.appendChild(iconElem);
                } else if (icon.startsWith("icon(") && icon.endsWith(")")) {
                    let split =
                        icon.substring(5, icon.length-1).trim().split(/\s*[,]\s*/);
                    switch (split.length) {
                    case 3:
                        iconBg = split[2];
                        //FALLTHROUGH
                    case 2:
                        iconFg = split[1];
                        //FALLTHROUGH
                    case 1:
                        iconName = split[0];
                        break;
                    }
                } else {
                    iconName = icon;
                }

                if (iconName !== null) {
                    let iconElem =
                        MenuItem.createEditIcon(iconName, MenuItem.SMALL_ICON_SIZE);
                    if (iconElem === null) {
                        iconElem =
                            MenuItem.createStockIcon(iconName, iconFg, iconBg,
                                                     enabled);
                        if (iconElem === null) {
                            iconElem =
                                MenuItem.createStockIcon("picture", "red", iconBg,
                                                         enabled);
                        }
                    }
                    this._icon.appendChild(iconElem);
                }
            }
        }

        // Low-level utility.
        static createIcon(iconURL, iconSize) {
            let span = document.createElement("span");
            span.className =`xui-edit-icon-${iconSize}`;
            span.setAttribute("style", `background: \
    ${iconURL} 0px 0px/${iconSize}px ${iconSize}px no-repeat;`);
            return span;
        }

        // Low-level utility.
        static createEditIcon(iconName, iconSize) {
            let classPrefix = EditIcon[iconName];
            if (!classPrefix) {
                return null;
            }

            let span = document.createElement("span");
            span.className =`xui-edit-icon-${iconSize} ${classPrefix}${iconSize}`;

            return span;
        }

        static createStockIcon(iconName, iconFg, iconBg, enabled) {
            let iconChar = StockIcon[iconName];
            if (!iconChar) {
                return null;
            }

            let span = document.createElement("span");
            span.className = "xui-small-icon";
            if (enabled && (iconFg !== null || iconBg !== null)) {
                let style = "";
                if (iconFg !== null && iconFg !== "transparent") {
                    style += `color: ${iconFg};`;
                }
                if (iconBg !== null && iconBg !== "transparent") {
                    style += `background-color: ${iconBg};`;
                }
                span.setAttribute("style", style);
            }
            span.textContent = iconChar;

            return span;
        }

        onSelected() {
            const group = this._options.buttonGroup;
            if (group !== null && this._parentMenu !== null) {
                let all = this._parentMenu.getAllItems();
                for (let item of all) {
                    if (item !== this && item._options.buttonGroup === group) {
                        item.setSelected(false);
                    }
                }
            }
        }

        get enabled() {
            return this._options.enabled;
        }

        set enabled(enable) {
            this._options.enabled = MenuItem.normalizeBoolean(enable);

            this._item.classList.remove("xui-control-disabled");
            if (!this._options.enabled) {
                this._item.classList.add("xui-control-disabled");
            }
        }

        get separator() {
            return this._options.separator;
        }

        set separator(separ) {
            this._options.separator = MenuItem.normalizeBoolean(separ);
        }

        configure() {
            let opts = this._options;
            MenuItem.normalizeOptions(opts);

            this._text.textContent = "";
            if (opts.text !== null) {
                this._text.textContent = opts.text;
            }

            this._detail.textContent = "";
            if (opts.type === "submenu") {
                let span = document.createElement("span");
                span.className = "xui-small-icon";
                span.textContent = StockIcon["right-dir"];
                this._detail.appendChild(span);
            } else if (opts.detail !== null) {
                this._detail.textContent = opts.detail;
            }

            this.tooltip = opts.tooltip;
            this.enabled = opts.enabled;
            this.separator = opts.separator;

            // This updates the icon and uses buttonGroup.
            this.selected = opts.selected;

            this._item.classList.remove("xui-icon-only");
            if (this.isIconOnly) {
                this._item.classList.add("xui-icon-only");
            }
        }

        static normalizeOptions(opts) {
            if (!opts.items || !Array.isArray(opts.items)) {
                opts.items = null;
            }

            let type = opts.type;
            switch (type) {
            case "checkbox":
            case "radiobutton":
            case "submenu":
                break;
            default:
                if (opts.items !== null) {
                    type = "submenu";
                } else {
                    type = "button";
                }
                break;
            }
            opts.type = type;

            opts.icon = MenuItem.normalizeString(opts.icon);
            opts.selectedIcon = MenuItem.normalizeString(opts.selectedIcon);
            if (opts.icon === null) {
                switch (type) {
                case "checkbox":
                    opts.icon = "icon(check-empty)";
                    opts.selectedIcon = "icon(check)"
                    break;
                case "radiobutton":
                    opts.icon = "icon(circle-empty)";
                    opts.selectedIcon = "icon(dot-circled)";
                    break;
                }
            }

            if (opts.icon === null) {
                opts.selectedIcon = null;
            } else {
                if (opts.selectedIcon === null) {
                    opts.selectedIcon = opts.icon;
                }
            }

            opts.text = MenuItem.normalizeString(opts.text);
            opts.detail = MenuItem.normalizeString(opts.detail);

            opts.selected = MenuItem.normalizeBoolean(opts.selected);
            opts.enabled = MenuItem.normalizeBoolean(opts.enabled);
            opts.separator = MenuItem.normalizeBoolean(opts.separator);

            opts.name = MenuItem.normalizeString(opts.name);
            opts.tooltip = MenuItem.normalizeString(opts.tooltip);
            opts.buttonGroup = MenuItem.normalizeString(opts.buttonGroup);

            if (type === "submenu") {
                opts.icon = null;
                opts.selectedIcon = null;
                opts.detail = null;
                opts.buttonGroup = null;
                if (opts.items === null) {
                    opts.items = [];
                }
            } else {
                if (opts.text === null) {
                    opts.detail = null;
                }
                if (type !== "radiobutton") {
                    opts.buttonGroup = null;
                }
                opts.items = null;
            }
        }

        get parentMenu() {
            return this._parentMenu;
        }

        set parentMenu(menu) {
            assertOrError(this._parentMenu === null);
            this._parentMenu = menu;
        }

        get submenu() {
            if (this.itemType === "submenu" && this._submenu === null) {
                this._submenu = Menu.create(this._options.items);
                this._submenu.ownerItem = this;
            }

            return this._submenu;
        }
    }

    // Change this if you change --xui-small-icon-size in xui.css
    MenuItem.SMALL_ICON_SIZE = 16;

    window.customElements.define("xui-menu-item", MenuItem);