/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    }
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
import './menu.css';
import * as nls from '../../../../nls';
import * as strings from '../../../common/strings';
import { Action } from '../../../common/actions';
import { ActionBar, Separator, ActionItem, BaseActionItem } from '../actionbar/actionbar';
import { KeyCodeUtils } from '../../../common/keyCodes';
import { addClass, EventType, EventHelper, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass, append, $, addClasses, getClientArea, removeClasses } from '../../dom';
import { StandardKeyboardEvent } from '../../keyboardEvent';
import { RunOnceScheduler } from '../../../common/async';
import { dispose } from '../../../common/lifecycle';
export var MENU_MNEMONIC_REGEX = /\(&{1,2}(.)\)|&{1,2}(.)/;
export var MENU_ESCAPED_MNEMONIC_REGEX = /(?:&amp;){1,2}(.)/;
var SubmenuAction = /** @class */ (function (_super) {
    __extends(SubmenuAction, _super);
    function SubmenuAction(label, entries, cssClass) {
        var _this = _super.call(this, !!cssClass ? cssClass : 'submenu', label, '', true) || this;
        _this.entries = entries;
        return _this;
    }
    return SubmenuAction;
}(Action));
export { SubmenuAction };
var Menu = /** @class */ (function (_super) {
    __extends(Menu, _super);
    function Menu(container, actions, options) {
        if (options === void 0) { options = {}; }
        var _this = this;
        addClass(container, 'monaco-menu-container');
        container.setAttribute('role', 'presentation');
        var menuContainer = document.createElement('div');
        addClass(menuContainer, 'monaco-menu');
        menuContainer.setAttribute('role', 'presentation');
        container.appendChild(menuContainer);
        _this = _super.call(this, menuContainer, {
            orientation: 2 /* VERTICAL */,
            actionItemProvider: function (action) { return _this.doGetActionItem(action, options, parentData); },
            context: options.context,
            actionRunner: options.actionRunner,
            ariaLabel: options.ariaLabel,
            triggerKeys: { keys: [3 /* Enter */], keyDown: true }
        }) || this;
        _this.actionsList.setAttribute('role', 'menu');
        _this.actionsList.tabIndex = 0;
        _this.menuDisposables = [];
        if (options.enableMnemonics) {
            _this.menuDisposables.push(addDisposableListener(menuContainer, EventType.KEY_DOWN, function (e) {
                var key = KeyCodeUtils.fromString(e.key);
                if (_this.mnemonics.has(key)) {
                    EventHelper.stop(e, true);
                    var actions_1 = _this.mnemonics.get(key);
                    if (actions_1.length === 1) {
                        if (actions_1[0] instanceof SubmenuActionItem) {
                            _this.focusItemByElement(actions_1[0].container);
                        }
                        actions_1[0].onClick(event);
                    }
                    if (actions_1.length > 1) {
                        var action = actions_1.shift();
                        _this.focusItemByElement(action.container);
                        actions_1.push(action);
                        _this.mnemonics.set(key, actions_1);
                    }
                }
            }));
        }
        _this._register(addDisposableListener(_this.domNode, EventType.MOUSE_OUT, function (e) {
            var relatedTarget = e.relatedTarget;
            if (!isAncestor(relatedTarget, _this.domNode)) {
                _this.focusedItem = undefined;
                _this.updateFocus();
                e.stopPropagation();
            }
        }));
        _this._register(addDisposableListener(_this.actionsList, EventType.MOUSE_OVER, function (e) {
            var target = e.target;
            if (!target || !isAncestor(target, _this.actionsList) || target === _this.actionsList) {
                return;
            }
            while (target.parentElement !== _this.actionsList) {
                target = target.parentElement;
            }
            if (hasClass(target, 'action-item')) {
                var lastFocusedItem = _this.focusedItem;
                _this.setFocusedItem(target);
                if (lastFocusedItem !== _this.focusedItem) {
                    _this.updateFocus();
                }
            }
        }));
        var parentData = {
            parent: _this
        };
        _this.mnemonics = new Map();
        _this.push(actions, { icon: true, label: true, isMenu: true });
        return _this;
    }
    Menu.prototype.style = function (style) {
        var container = this.getContainer();
        var fgColor = style.foregroundColor ? "" + style.foregroundColor : null;
        var bgColor = style.backgroundColor ? "" + style.backgroundColor : null;
        var border = style.borderColor ? "2px solid " + style.borderColor : null;
        var shadow = style.shadowColor ? "0 2px 4px " + style.shadowColor : null;
        container.style.border = border;
        this.domNode.style.color = fgColor;
        this.domNode.style.backgroundColor = bgColor;
        container.style.boxShadow = shadow;
        if (this.items) {
            this.items.forEach(function (item) {
                if (item instanceof MenuActionItem || item instanceof MenuSeparatorActionItem) {
                    item.style(style);
                }
            });
        }
    };
    Menu.prototype.focusItemByElement = function (element) {
        var lastFocusedItem = this.focusedItem;
        this.setFocusedItem(element);
        if (lastFocusedItem !== this.focusedItem) {
            this.updateFocus();
        }
    };
    Menu.prototype.setFocusedItem = function (element) {
        for (var i = 0; i < this.actionsList.children.length; i++) {
            var elem = this.actionsList.children[i];
            if (element === elem) {
                this.focusedItem = i;
                break;
            }
        }
    };
    Menu.prototype.doGetActionItem = function (action, options, parentData) {
        if (action instanceof Separator) {
            return new MenuSeparatorActionItem(options.context, action, { icon: true });
        }
        else if (action instanceof SubmenuAction) {
            var menuActionItem = new SubmenuActionItem(action, action.entries, parentData, options);
            if (options.enableMnemonics) {
                var mnemonic = menuActionItem.getMnemonic();
                if (mnemonic && menuActionItem.isEnabled()) {
                    var actionItems = [];
                    if (this.mnemonics.has(mnemonic)) {
                        actionItems = this.mnemonics.get(mnemonic);
                    }
                    actionItems.push(menuActionItem);
                    this.mnemonics.set(mnemonic, actionItems);
                }
            }
            return menuActionItem;
        }
        else {
            var menuItemOptions = { enableMnemonics: options.enableMnemonics };
            if (options.getKeyBinding) {
                var keybinding = options.getKeyBinding(action);
                if (keybinding) {
                    menuItemOptions.keybinding = keybinding.getLabel();
                }
            }
            var menuActionItem = new MenuActionItem(options.context, action, menuItemOptions);
            if (options.enableMnemonics) {
                var mnemonic = menuActionItem.getMnemonic();
                if (mnemonic && menuActionItem.isEnabled()) {
                    var actionItems = [];
                    if (this.mnemonics.has(mnemonic)) {
                        actionItems = this.mnemonics.get(mnemonic);
                    }
                    actionItems.push(menuActionItem);
                    this.mnemonics.set(mnemonic, actionItems);
                }
            }
            return menuActionItem;
        }
    };
    Menu.prototype.focus = function (selectFirst) {
        if (selectFirst === void 0) { selectFirst = true; }
        _super.prototype.focus.call(this, selectFirst);
    };
    return Menu;
}(ActionBar));
export { Menu };
var MenuActionItem = /** @class */ (function (_super) {
    __extends(MenuActionItem, _super);
    function MenuActionItem(ctx, action, options) {
        if (options === void 0) { options = {}; }
        var _this = this;
        options.isMenu = true;
        _this = _super.call(this, action, action, options) || this;
        _this.options = options;
        _this.options.icon = options.icon !== undefined ? options.icon : false;
        _this.options.label = options.label !== undefined ? options.label : true;
        _this.cssClass = '';
        // Set mnemonic
        if (_this.options.label && options.enableMnemonics) {
            var label = _this.getAction().label;
            if (label) {
                var matches = MENU_MNEMONIC_REGEX.exec(label);
                if (matches) {
                    _this.mnemonic = KeyCodeUtils.fromString((!!matches[1] ? matches[1] : matches[2]).toLocaleLowerCase());
                }
            }
        }
        return _this;
    }
    MenuActionItem.prototype.render = function (container) {
        var _this = this;
        _super.prototype.render.call(this, container);
        this.container = container;
        this.item = append(this.element, $('a.action-menu-item'));
        if (this._action.id === Separator.ID) {
            // A separator is a presentation item
            this.item.setAttribute('role', 'presentation');
        }
        else {
            this.item.setAttribute('role', 'menuitem');
            if (this.mnemonic) {
                this.item.setAttribute('aria-keyshortcuts', "" + this.mnemonic);
            }
        }
        this.check = append(this.item, $('span.menu-item-check'));
        this.check.setAttribute('role', 'none');
        this.label = append(this.item, $('span.action-label'));
        if (this.options.label && this.options.keybinding) {
            append(this.item, $('span.keybinding')).textContent = this.options.keybinding;
        }
        this._register(addDisposableListener(this.element, EventType.MOUSE_UP, function (e) {
            EventHelper.stop(e, true);
            _this.onClick(e);
        }));
        this.updateClass();
        this.updateLabel();
        this.updateTooltip();
        this.updateEnabled();
        this.updateChecked();
    };
    MenuActionItem.prototype.updateLabel = function () {
        if (this.options.label) {
            var label = this.getAction().label;
            if (label) {
                var cleanLabel = cleanMnemonic(label);
                if (!this.options.enableMnemonics) {
                    label = cleanLabel;
                }
                this.label.setAttribute('aria-label', cleanLabel);
                var matches = MENU_MNEMONIC_REGEX.exec(label);
                if (matches) {
                    label = strings.escape(label).replace(MENU_ESCAPED_MNEMONIC_REGEX, '<u aria-hidden="true">$1</u>');
                    this.item.setAttribute('aria-keyshortcuts', (!!matches[1] ? matches[1] : matches[2]).toLocaleLowerCase());
                }
            }
            this.label.innerHTML = label.trim();
        }
    };
    MenuActionItem.prototype.updateTooltip = function () {
        var title = null;
        if (this.getAction().tooltip) {
            title = this.getAction().tooltip;
        }
        else if (!this.options.label && this.getAction().label && this.options.icon) {
            title = this.getAction().label;
            if (this.options.keybinding) {
                title = nls.localize({ key: 'titleLabel', comment: ['action title', 'action keybinding'] }, "{0} ({1})", title, this.options.keybinding);
            }
        }
        if (title) {
            this.item.title = title;
        }
    };
    MenuActionItem.prototype.updateClass = function () {
        if (this.cssClass) {
            removeClasses(this.item, this.cssClass);
        }
        if (this.options.icon) {
            this.cssClass = this.getAction().class;
            addClass(this.label, 'icon');
            if (this.cssClass) {
                addClasses(this.label, this.cssClass);
            }
            this.updateEnabled();
        }
        else {
            removeClass(this.label, 'icon');
        }
    };
    MenuActionItem.prototype.updateEnabled = function () {
        if (this.getAction().enabled) {
            removeClass(this.element, 'disabled');
            removeClass(this.item, 'disabled');
            this.item.tabIndex = 0;
        }
        else {
            addClass(this.element, 'disabled');
            addClass(this.item, 'disabled');
            removeTabIndexAndUpdateFocus(this.item);
        }
    };
    MenuActionItem.prototype.updateChecked = function () {
        if (this.getAction().checked) {
            addClass(this.item, 'checked');
            this.item.setAttribute('role', 'menuitemcheckbox');
            this.item.setAttribute('aria-checked', 'true');
        }
        else {
            removeClass(this.item, 'checked');
            this.item.setAttribute('role', 'menuitem');
            this.item.setAttribute('aria-checked', 'false');
        }
    };
    MenuActionItem.prototype.getMnemonic = function () {
        return this.mnemonic;
    };
    MenuActionItem.prototype.applyStyle = function () {
        var isSelected = hasClass(this.element, 'focused');
        var fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor;
        var bgColor = isSelected && this.menuStyle.selectionBackgroundColor ? this.menuStyle.selectionBackgroundColor : this.menuStyle.backgroundColor;
        var border = isSelected && this.menuStyle.selectionBorderColor ? "1px solid " + this.menuStyle.selectionBorderColor : null;
        this.item.style.color = fgColor ? "" + fgColor : null;
        this.check.style.backgroundColor = fgColor ? "" + fgColor : null;
        this.item.style.backgroundColor = bgColor ? "" + bgColor : null;
        this.container.style.border = border;
    };
    MenuActionItem.prototype.style = function (style) {
        this.menuStyle = style;
        this.applyStyle();
    };
    return MenuActionItem;
}(BaseActionItem));
var SubmenuActionItem = /** @class */ (function (_super) {
    __extends(SubmenuActionItem, _super);
    function SubmenuActionItem(action, submenuActions, parentData, submenuOptions) {
        var _this = _super.call(this, action, action, submenuOptions) || this;
        _this.submenuActions = submenuActions;
        _this.parentData = parentData;
        _this.submenuOptions = submenuOptions;
        _this.submenuDisposables = [];
        _this.showScheduler = new RunOnceScheduler(function () {
            if (_this.mouseOver) {
                _this.cleanupExistingSubmenu(false);
                _this.createSubmenu(false);
            }
        }, 250);
        _this.hideScheduler = new RunOnceScheduler(function () {
            if ((!isAncestor(document.activeElement, _this.element) && _this.parentData.submenu === _this.mysubmenu)) {
                _this.parentData.parent.focus(false);
                _this.cleanupExistingSubmenu(true);
            }
        }, 750);
        return _this;
    }
    SubmenuActionItem.prototype.render = function (container) {
        var _this = this;
        _super.prototype.render.call(this, container);
        addClass(this.item, 'monaco-submenu-item');
        this.item.setAttribute('aria-haspopup', 'true');
        this.submenuIndicator = append(this.item, $('span.submenu-indicator'));
        this.submenuIndicator.setAttribute('aria-hidden', 'true');
        this._register(addDisposableListener(this.element, EventType.KEY_UP, function (e) {
            var event = new StandardKeyboardEvent(e);
            if (event.equals(17 /* RightArrow */) || event.equals(3 /* Enter */)) {
                EventHelper.stop(e, true);
                _this.createSubmenu(true);
            }
        }));
        this._register(addDisposableListener(this.element, EventType.KEY_DOWN, function (e) {
            var event = new StandardKeyboardEvent(e);
            if (event.equals(17 /* RightArrow */) || event.equals(3 /* Enter */)) {
                EventHelper.stop(e, true);
            }
        }));
        this._register(addDisposableListener(this.element, EventType.MOUSE_OVER, function (e) {
            if (!_this.mouseOver) {
                _this.mouseOver = true;
                _this.showScheduler.schedule();
            }
        }));
        this._register(addDisposableListener(this.element, EventType.MOUSE_LEAVE, function (e) {
            _this.mouseOver = false;
        }));
        this._register(addDisposableListener(this.element, EventType.FOCUS_OUT, function (e) {
            if (!isAncestor(document.activeElement, _this.element)) {
                _this.hideScheduler.schedule();
            }
        }));
    };
    SubmenuActionItem.prototype.onClick = function (e) {
        // stop clicking from trying to run an action
        EventHelper.stop(e, true);
        this.cleanupExistingSubmenu(false);
        this.createSubmenu(false);
    };
    SubmenuActionItem.prototype.cleanupExistingSubmenu = function (force) {
        if (this.parentData.submenu && (force || (this.parentData.submenu !== this.mysubmenu))) {
            this.parentData.submenu.dispose();
            this.parentData.submenu = null;
            if (this.submenuContainer) {
                this.submenuDisposables = dispose(this.submenuDisposables);
                this.submenuContainer = null;
            }
        }
    };
    SubmenuActionItem.prototype.createSubmenu = function (selectFirstItem) {
        var _this = this;
        if (selectFirstItem === void 0) { selectFirstItem = true; }
        if (!this.parentData.submenu) {
            this.submenuContainer = append(this.element, $('div.monaco-submenu'));
            addClasses(this.submenuContainer, 'menubar-menu-items-holder', 'context-view');
            this.submenuContainer.style.left = getClientArea(this.element).width + "px";
            this.submenuDisposables.push(addDisposableListener(this.submenuContainer, EventType.KEY_UP, function (e) {
                var event = new StandardKeyboardEvent(e);
                if (event.equals(15 /* LeftArrow */)) {
                    EventHelper.stop(e, true);
                    _this.parentData.parent.focus();
                    _this.parentData.submenu.dispose();
                    _this.parentData.submenu = null;
                    _this.submenuDisposables = dispose(_this.submenuDisposables);
                    _this.submenuContainer = null;
                }
            }));
            this.submenuDisposables.push(addDisposableListener(this.submenuContainer, EventType.KEY_DOWN, function (e) {
                var event = new StandardKeyboardEvent(e);
                if (event.equals(15 /* LeftArrow */)) {
                    EventHelper.stop(e, true);
                }
            }));
            this.parentData.submenu = new Menu(this.submenuContainer, this.submenuActions, this.submenuOptions);
            if (this.menuStyle) {
                this.parentData.submenu.style(this.menuStyle);
            }
            this.submenuDisposables.push(this.parentData.submenu.onDidCancel(function () {
                _this.parentData.parent.focus();
                _this.parentData.submenu.dispose();
                _this.parentData.submenu = null;
                _this.submenuDisposables = dispose(_this.submenuDisposables);
                _this.submenuContainer = null;
            }));
            this.parentData.submenu.focus(selectFirstItem);
            this.mysubmenu = this.parentData.submenu;
        }
        else {
            this.parentData.submenu.focus(false);
        }
    };
    SubmenuActionItem.prototype.applyStyle = function () {
        _super.prototype.applyStyle.call(this);
        var isSelected = hasClass(this.element, 'focused');
        var fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor;
        this.submenuIndicator.style.backgroundColor = fgColor ? "" + fgColor : null;
        if (this.parentData.submenu) {
            this.parentData.submenu.style(this.menuStyle);
        }
    };
    SubmenuActionItem.prototype.dispose = function () {
        _super.prototype.dispose.call(this);
        this.hideScheduler.dispose();
        if (this.mysubmenu) {
            this.mysubmenu.dispose();
            this.mysubmenu = null;
        }
        if (this.submenuContainer) {
            this.submenuDisposables = dispose(this.submenuDisposables);
            this.submenuContainer = null;
        }
    };
    return SubmenuActionItem;
}(MenuActionItem));
var MenuSeparatorActionItem = /** @class */ (function (_super) {
    __extends(MenuSeparatorActionItem, _super);
    function MenuSeparatorActionItem() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    MenuSeparatorActionItem.prototype.style = function (style) {
        this.label.style.borderBottomColor = style.separatorColor ? "" + style.separatorColor : null;
    };
    return MenuSeparatorActionItem;
}(ActionItem));
export function cleanMnemonic(label) {
    var regex = MENU_MNEMONIC_REGEX;
    var matches = regex.exec(label);
    if (!matches) {
        return label;
    }
    var mnemonicInText = matches[0].charAt(0) === '&';
    return label.replace(regex, mnemonicInText ? '$2' : '').trim();
}
