# Source: Dialog.js

    /**
     * The base class of dialog boxes.
     * <p>Part of the XUI module which, for now, has an undocumented API.
     */
    export class Dialog {
        constructor(options) {
            this._testMode = false;

            let opts = Object.assign({
                type: "modal", movable: false, resizable: false,
                classes: "xui-control xui-dialog xui-dlg",
                title: null, closeable: false,
                template: null,
                buttons: null
            }, options);

            this._type = opts.type;
            this._classes = opts.classes;

            this._form = document.createElement("div");
            this._form.classList.add("xui-dlg-container");
            if (opts.resizable) {
                this._form.classList.add("xui-dlg-resizable");
            }
            this._form.appendChild(Dialog.TEMPLATE.content.cloneNode(true));
            this._titlePane = this._form.firstElementChild;
            this._contentPane = this._titlePane.nextElementSibling;
            this._buttonPane = this._form.lastElementChild;
            this._titleText = this._titlePane.firstElementChild;
            this._closeButton = this._titlePane.lastElementChild;

            if (opts.title === null && !opts.closeable) {
                this._titleText.style.display = "none";
                this._closeButton.style.display = "none";
            } else {
                if (opts.title !== null) {
                    this._titleText.textContent = opts.title;
                }

                if (opts.closeable) {
                    this._closeButton.textContent = StockIcon["cancel"];
                    this._closeButton.setAttribute("title", "Close");
                    this._closeButton.addEventListener("click", (e) => {
                        Util.consumeEvent(e);

                        this.close(null);
                    });
                }
            }

            if (opts.template !== null) {
               this._contentPane.appendChild(opts.template.content.cloneNode(true));
            }

            this._buttons = [];
            if (opts.buttons !== null) {
                let buttonSpecs = opts.buttons;
                if (!Array.isArray(buttonSpecs)) {
                    buttonSpecs = [ buttonSpecs ];
                }

                let containsSepar = false;
                let defaultAction = null;
                for (let buttonSpec of buttonSpecs) {
                    if (Object.keys(buttonSpec).length === 0) {
                        // Separator ---

                        let separ = document.createElement("span");
                        separ.setAttribute("class", "xui-dlg-button-separ");
                        this._buttonPane.appendChild(separ);

                        containsSepar = true;
                    } else {
                        // Button ---

                        let action = (typeof buttonSpec["action"] === "string")?
                            buttonSpec["action"] : null;
                        let label = (typeof buttonSpec["label"] === "string")?
                            buttonSpec["label"] : "???";
                        let isDefault = (buttonSpec["default"] === true);

                        let button = document.createElement("button");
                        button.setAttribute("type", "button");
                        button.classList.add("xui-control", "xui-dlg-button");
                        if (isDefault) {
                            button.classList.add("xui-dlg-default-button");
                            if (action !== null) {
                                defaultAction = this[action].bind(this);
                            }
                        }
                        button.textContent = label;
                        this._buttonPane.appendChild(button);

                        this._buttons.push(button);

                        if (action !== null) {
                            const buttonAction = this[action].bind(this);
                            button.addEventListener("click", (e) => {
                                Util.consumeEvent(e);
                                buttonAction();
                            });
                        }
                    }
                }

                if (defaultAction !== null) {
                    const onPressEnter = (e) => {
                        if (e.key === "Enter") {
                            Util.consumeEvent(e);
                            defaultAction();
                        }
                    };

                    const inputs = this._contentPane.querySelectorAll(
                        "input[type=text],input:not([type]),select,[tabindex]");
                    for (let input of inputs) {
                        input.addEventListener("keydown", onPressEnter);
                    }
                }

                if (!containsSepar) {
                    this._buttonPane.classList.add("xui-dlg-button-default-layout");
                }
            }

            if (opts.movable && opts.title !== null) {
                this._titleText.addEventListener("mousedown",
                                                 this.onDragStart.bind(this));

                // When the mousemove handler is set on this._titleText, dragging
                // stops when the mouse is moved quickly (because the mouse leaves
                // this._titleText. That's why the mousemove handler must be set
                // on the document.
                this._onDrag = this.onDrag.bind(this);

                this._titleText.addEventListener("mouseup",
                                                 this.onDragEnd.bind(this));

                this._dragOffsetX = this._dragOffsetY = -1;
                this._dragging = false;
            }
        }

        get testMode() {
            return this._testMode;
        }

        set testMode(testing) {
            this._testMode = testing;
        }

        get form() {
            return this._form;
        }

        get titlePane() {
            return this._titlePane;
        }

        get contentPane() {
            return this._contentPane;
        }

        get buttonPane() {
            return this._buttonPane;
        }

        get titleText() {
            return this._titleText;
        }

        get closeButton() {
            return this._closeButton;
        }

        get buttons() {
            return this._buttons;
        }

        open(position="center", reference=null) {
            this._dialog = Dialogs.open({
                type: this._type, classes: this._classes,
                form: this._form,
                position: position, reference: reference
            });

            this._dialog.addEventListener("dialogclosed", (e) => {
                this.dialogClosed(e.detail.dialogResult);
            });

            return this._dialog;
        }

        close(result=null) {
            Dialogs.close(this._dialog, result);
            this._dialog = null;
        }

        dialogClosed(result) {
            // Default implementation does nothing at all.
        }

        onDragStart(event) {
            if (event.buttons === 1) { // Primary button pressed.
                Util.consumeEvent(event);

                this._dragging = true;

                // Coordinates of the top/left of the dialog relative to this
                // initial mouse down.
                let dialogRect = this._dialog.getBoundingClientRect();
                this._dragOffsetX = event.clientX - dialogRect.left;
                this._dragOffsetY = event.clientY - dialogRect.top;

                this._titleText.style.cursor = "move";

                document.addEventListener("mousemove", this._onDrag);
            }
        }

        onDrag(event) {
            if (this._dragging) {
                Util.consumeEvent(event);

                this._dialog.style.left =
                    String(event.pageX - this._dragOffsetX) + "px";
                this._dialog.style.top =
                    String(event.pageY - this._dragOffsetY) + "px";
            }
        }

        onDragEnd(event) {
            if (this._dragging) {
                Util.consumeEvent(event);

                document.removeEventListener("mousemove", this._onDrag);

                this._titleText.style.cursor = null;

                this._dragOffsetX = this._dragOffsetY = -1;
                this._dragging = false;
            }
        }
    }

    Dialog.TEMPLATE = document.createElement("template");
    Dialog.TEMPLATE.innerHTML = `
    <div class="xui-dlg-title-pane">
      <span draggable="false" class="xui-dlg-title"></span>
      <span class="xui-small-icon xui-dlg-close-button"></span>
    </div>
    <div class="xui-dlg-content-pane"></div>
    <div class="xui-dlg-button-pane"></div>
    `;