"use strict";
// *****************************************************************************
// Copyright (C) 2017 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var DialogOverlayService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SingleTextInputDialog = exports.SingleTextInputDialogProps = exports.confirmExitWithOrWithoutSaving = exports.ConfirmSaveDialog = exports.ConfirmSaveDialogProps = exports.confirmExit = exports.ConfirmDialog = exports.ConfirmDialogProps = exports.MessageDialogProps = exports.AbstractDialog = exports.DialogOverlayService = exports.Dialog = exports.DialogError = exports.DialogProps = void 0;
const inversify_1 = require("inversify");
const common_1 = require("../common");
const keys_1 = require("./keyboard/keys");
const widgets_1 = require("./widgets");
let DialogProps = class DialogProps {
};
DialogProps = __decorate([
    (0, inversify_1.injectable)()
], DialogProps);
exports.DialogProps = DialogProps;
var DialogError;
(function (DialogError) {
    function getResult(error) {
        if (typeof error === 'string') {
            return !error.length;
        }
        if (typeof error === 'boolean') {
            return error;
        }
        return error.result;
    }
    DialogError.getResult = getResult;
    function getMessage(error) {
        if (typeof error === 'string') {
            return error;
        }
        if (typeof error === 'boolean') {
            return '';
        }
        return error.message;
    }
    DialogError.getMessage = getMessage;
})(DialogError = exports.DialogError || (exports.DialogError = {}));
var Dialog;
(function (Dialog) {
    Dialog.YES = common_1.nls.localizeByDefault('Yes');
    Dialog.NO = common_1.nls.localizeByDefault('No');
    Dialog.OK = common_1.nls.localizeByDefault('OK');
    Dialog.CANCEL = common_1.nls.localizeByDefault('Cancel');
})(Dialog = exports.Dialog || (exports.Dialog = {}));
let DialogOverlayService = DialogOverlayService_1 = class DialogOverlayService {
    constructor() {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        this.dialogs = [];
        (0, widgets_1.addKeyListener)(document.body, keys_1.Key.ENTER, e => this.handleEnter(e));
        (0, widgets_1.addKeyListener)(document.body, keys_1.Key.ESCAPE, e => this.handleEscape(e));
    }
    static get() {
        return DialogOverlayService_1.INSTANCE;
    }
    initialize() {
        DialogOverlayService_1.INSTANCE = this;
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    get currentDialog() {
        return this.dialogs[0];
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    push(dialog) {
        this.dialogs.unshift(dialog);
        return common_1.Disposable.create(() => {
            const index = this.dialogs.indexOf(dialog);
            if (index > -1) {
                this.dialogs.splice(index, 1);
            }
        });
    }
    handleEscape(event) {
        const dialog = this.currentDialog;
        if (dialog) {
            return dialog['handleEscape'](event);
        }
        return false;
    }
    handleEnter(event) {
        const dialog = this.currentDialog;
        if (dialog) {
            return dialog['handleEnter'](event);
        }
        return false;
    }
};
DialogOverlayService = DialogOverlayService_1 = __decorate([
    (0, inversify_1.injectable)(),
    __metadata("design:paramtypes", [])
], DialogOverlayService);
exports.DialogOverlayService = DialogOverlayService;
let AbstractDialog = class AbstractDialog extends widgets_1.BaseWidget {
    constructor(props) {
        super();
        this.props = props;
        this.validateCancellationSource = new common_1.CancellationTokenSource();
        this.acceptCancellationSource = new common_1.CancellationTokenSource();
        this.id = 'theia-dialog-shell';
        this.addClass('dialogOverlay');
        this.toDispose.push(common_1.Disposable.create(() => {
            if (this.reject) {
                widgets_1.Widget.detach(this);
            }
        }));
        const container = document.createElement('div');
        container.classList.add('dialogBlock');
        if (props.maxWidth === undefined) {
            container.setAttribute('style', 'max-width: none');
        }
        else {
            container.setAttribute('style', `max-width: ${props.maxWidth}px; min-width: 0px`);
        }
        this.node.appendChild(container);
        const titleContentNode = document.createElement('div');
        titleContentNode.classList.add('dialogTitle');
        container.appendChild(titleContentNode);
        this.titleNode = document.createElement('div');
        this.titleNode.textContent = props.title;
        titleContentNode.appendChild(this.titleNode);
        this.closeCrossNode = document.createElement('i');
        this.closeCrossNode.classList.add(...(0, widgets_1.codiconArray)('close'));
        this.closeCrossNode.classList.add('closeButton');
        titleContentNode.appendChild(this.closeCrossNode);
        this.contentNode = document.createElement('div');
        this.contentNode.classList.add('dialogContent');
        if (props.wordWrap !== undefined) {
            this.contentNode.setAttribute('style', `word-wrap: ${props.wordWrap}`);
        }
        container.appendChild(this.contentNode);
        this.controlPanel = document.createElement('div');
        this.controlPanel.classList.add('dialogControl');
        container.appendChild(this.controlPanel);
        this.errorMessageNode = document.createElement('div');
        this.errorMessageNode.classList.add('error');
        this.errorMessageNode.setAttribute('style', 'flex: 2');
        this.controlPanel.appendChild(this.errorMessageNode);
        this.update();
    }
    appendCloseButton(text = Dialog.CANCEL) {
        return this.closeButton = this.appendButton(text, false);
    }
    appendAcceptButton(text = Dialog.OK) {
        return this.acceptButton = this.appendButton(text, true);
    }
    appendButton(text, primary) {
        const button = this.createButton(text);
        this.controlPanel.appendChild(button);
        button.classList.add(primary ? 'main' : 'secondary');
        return button;
    }
    createButton(text) {
        const button = document.createElement('button');
        button.classList.add('theia-button');
        button.textContent = text;
        return button;
    }
    onAfterAttach(msg) {
        super.onAfterAttach(msg);
        if (this.closeButton) {
            this.addCloseAction(this.closeButton, 'click');
        }
        if (this.acceptButton) {
            this.addAcceptAction(this.acceptButton, 'click');
        }
        this.addCloseAction(this.closeCrossNode, 'click');
        // TODO: use DI always to create dialog instances
        this.toDisposeOnDetach.push(DialogOverlayService.get().push(this));
    }
    handleEscape(event) {
        this.close();
    }
    handleEnter(event) {
        if (event.target instanceof HTMLTextAreaElement) {
            return false;
        }
        this.accept();
    }
    onActivateRequest(msg) {
        super.onActivateRequest(msg);
        if (this.acceptButton) {
            this.acceptButton.focus();
        }
    }
    open() {
        if (this.resolve) {
            return Promise.reject(new Error('The dialog is already opened.'));
        }
        this.activeElement = window.document.activeElement;
        return new Promise((resolve, reject) => {
            this.resolve = resolve;
            this.reject = reject;
            this.toDisposeOnDetach.push(common_1.Disposable.create(() => {
                this.resolve = undefined;
                this.reject = undefined;
            }));
            widgets_1.Widget.attach(this, document.body);
            this.activate();
        });
    }
    close() {
        if (this.resolve) {
            if (this.activeElement) {
                this.activeElement.focus({ preventScroll: true });
            }
            this.resolve(undefined);
        }
        this.activeElement = undefined;
        super.close();
    }
    onUpdateRequest(msg) {
        super.onUpdateRequest(msg);
        this.validate();
    }
    async validate() {
        if (!this.resolve) {
            return;
        }
        this.validateCancellationSource.cancel();
        this.validateCancellationSource = new common_1.CancellationTokenSource();
        const token = this.validateCancellationSource.token;
        const value = this.value;
        const error = await this.isValid(value, 'preview');
        if (token.isCancellationRequested) {
            return;
        }
        this.setErrorMessage(error);
    }
    async accept() {
        if (!this.resolve) {
            return;
        }
        this.acceptCancellationSource.cancel();
        this.acceptCancellationSource = new common_1.CancellationTokenSource();
        const token = this.acceptCancellationSource.token;
        const value = this.value;
        const error = await this.isValid(value, 'open');
        if (token.isCancellationRequested) {
            return;
        }
        if (!DialogError.getResult(error)) {
            this.setErrorMessage(error);
        }
        else {
            this.resolve(value);
            widgets_1.Widget.detach(this);
        }
    }
    /**
     * Return a string of zero-length or true if valid.
     */
    isValid(value, mode) {
        return '';
    }
    setErrorMessage(error) {
        if (this.acceptButton) {
            this.acceptButton.disabled = !DialogError.getResult(error);
        }
        this.errorMessageNode.innerText = DialogError.getMessage(error);
    }
    addAction(element, callback, ...additionalEventTypes) {
        this.addKeyListener(element, keys_1.Key.ENTER, callback, ...additionalEventTypes);
    }
    addCloseAction(element, ...additionalEventTypes) {
        this.addAction(element, () => this.close(), ...additionalEventTypes);
    }
    addAcceptAction(element, ...additionalEventTypes) {
        this.addAction(element, () => this.accept(), ...additionalEventTypes);
    }
};
AbstractDialog = __decorate([
    (0, inversify_1.injectable)(),
    __param(0, (0, inversify_1.inject)(DialogProps)),
    __metadata("design:paramtypes", [DialogProps])
], AbstractDialog);
exports.AbstractDialog = AbstractDialog;
let MessageDialogProps = class MessageDialogProps extends DialogProps {
};
MessageDialogProps = __decorate([
    (0, inversify_1.injectable)()
], MessageDialogProps);
exports.MessageDialogProps = MessageDialogProps;
let ConfirmDialogProps = class ConfirmDialogProps extends MessageDialogProps {
};
ConfirmDialogProps = __decorate([
    (0, inversify_1.injectable)()
], ConfirmDialogProps);
exports.ConfirmDialogProps = ConfirmDialogProps;
let ConfirmDialog = class ConfirmDialog extends AbstractDialog {
    constructor(props) {
        super(props);
        this.props = props;
        this.confirmed = true;
        this.contentNode.appendChild(this.createMessageNode(this.props.msg));
        this.appendCloseButton(props.cancel);
        this.appendAcceptButton(props.ok);
    }
    onCloseRequest(msg) {
        super.onCloseRequest(msg);
        this.confirmed = false;
        this.accept();
    }
    get value() {
        return this.confirmed;
    }
    createMessageNode(msg) {
        if (typeof msg === 'string') {
            const messageNode = document.createElement('div');
            messageNode.textContent = msg;
            return messageNode;
        }
        return msg;
    }
};
ConfirmDialog = __decorate([
    __param(0, (0, inversify_1.inject)(ConfirmDialogProps)),
    __metadata("design:paramtypes", [ConfirmDialogProps])
], ConfirmDialog);
exports.ConfirmDialog = ConfirmDialog;
async function confirmExit() {
    const safeToExit = await new ConfirmDialog({
        title: common_1.nls.localizeByDefault('Are you sure you want to quit?'),
        msg: common_1.nls.localize('theia/core/quitMessage', 'Any unsaved changes will not be saved.'),
        ok: Dialog.YES,
        cancel: Dialog.NO,
    }).open();
    return safeToExit === true;
}
exports.confirmExit = confirmExit;
class ConfirmSaveDialogProps extends MessageDialogProps {
}
exports.ConfirmSaveDialogProps = ConfirmSaveDialogProps;
// Dialog prompting the user to confirm whether they wish to save changes or not
let ConfirmSaveDialog = class ConfirmSaveDialog extends AbstractDialog {
    constructor(props) {
        super(props);
        this.props = props;
        this.result = false;
        // Append message and buttons to the dialog
        this.contentNode.appendChild(this.createMessageNode(this.props.msg));
        this.closeButton = this.appendButtonAndSetResult(props.cancel, false);
        this.appendButtonAndSetResult(props.dontSave, false, false);
        this.acceptButton = this.appendButtonAndSetResult(props.save, true, true);
    }
    get value() {
        return this.result;
    }
    createMessageNode(msg) {
        if (typeof msg === 'string') {
            const messageNode = document.createElement('div');
            messageNode.textContent = msg;
            return messageNode;
        }
        return msg;
    }
    appendButtonAndSetResult(text, primary, result) {
        const button = this.appendButton(text, primary);
        button.addEventListener('click', () => {
            this.result = result;
            this.accept();
        });
        return button;
    }
};
ConfirmSaveDialog = __decorate([
    __param(0, (0, inversify_1.inject)(ConfirmSaveDialogProps)),
    __metadata("design:paramtypes", [ConfirmSaveDialogProps])
], ConfirmSaveDialog);
exports.ConfirmSaveDialog = ConfirmSaveDialog;
// Asks the user to confirm whether they want to exit with or without saving the changes
async function confirmExitWithOrWithoutSaving(captionsToSave, performSave) {
    const div = document.createElement('div');
    div.innerText = common_1.nls.localizeByDefault("Your changes will be lost if you don't save them.");
    if (captionsToSave.length > 0) {
        const span = document.createElement('span');
        span.appendChild(document.createElement('br'));
        captionsToSave.forEach(cap => {
            const b = document.createElement('b');
            b.innerText = cap;
            span.appendChild(b);
            span.appendChild(document.createElement('br'));
        });
        span.appendChild(document.createElement('br'));
        div.appendChild(span);
        const result = await new ConfirmSaveDialog({
            title: common_1.nls.localizeByDefault('Do you want to save the changes to the following {0} files?', captionsToSave.length),
            msg: div,
            dontSave: common_1.nls.localizeByDefault("Don't Save"),
            save: common_1.nls.localizeByDefault('Save All'),
            cancel: Dialog.CANCEL
        }).open();
        if (result) {
            await performSave();
        }
        return result !== undefined;
    }
    else {
        // fallback if not passed with an empty caption-list.
        return confirmExit();
    }
}
exports.confirmExitWithOrWithoutSaving = confirmExitWithOrWithoutSaving;
let SingleTextInputDialogProps = class SingleTextInputDialogProps extends DialogProps {
};
SingleTextInputDialogProps = __decorate([
    (0, inversify_1.injectable)()
], SingleTextInputDialogProps);
exports.SingleTextInputDialogProps = SingleTextInputDialogProps;
let SingleTextInputDialog = class SingleTextInputDialog extends AbstractDialog {
    constructor(props) {
        super(props);
        this.props = props;
        this.inputField = document.createElement('input');
        this.inputField.type = 'text';
        this.inputField.className = 'theia-input';
        this.inputField.spellcheck = false;
        this.inputField.setAttribute('style', 'flex: 0;');
        this.inputField.placeholder = props.placeholder || '';
        this.inputField.value = props.initialValue || '';
        if (props.initialSelectionRange) {
            this.inputField.setSelectionRange(props.initialSelectionRange.start, props.initialSelectionRange.end, props.initialSelectionRange.direction);
        }
        else {
            this.inputField.select();
        }
        this.contentNode.appendChild(this.inputField);
        this.appendAcceptButton(props.confirmButtonLabel);
    }
    get value() {
        return this.inputField.value;
    }
    isValid(value, mode) {
        if (this.props.validate) {
            return this.props.validate(value, mode);
        }
        return super.isValid(value, mode);
    }
    onAfterAttach(msg) {
        super.onAfterAttach(msg);
        this.addUpdateListener(this.inputField, 'input');
    }
    onActivateRequest(msg) {
        this.inputField.focus();
    }
    handleEnter(event) {
        if (event.target instanceof HTMLInputElement) {
            return super.handleEnter(event);
        }
        return false;
    }
};
SingleTextInputDialog = __decorate([
    __param(0, (0, inversify_1.inject)(SingleTextInputDialogProps)),
    __metadata("design:paramtypes", [SingleTextInputDialogProps])
], SingleTextInputDialog);
exports.SingleTextInputDialog = SingleTextInputDialog;
//# sourceMappingURL=dialogs.js.map