/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2025 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
/** @module ui */
import {marked} from 'marked';
import DOMPurify from 'dompurify';
import $ from 'jquery';
/**
* Dialog parameters.
*
* @typedef {Object} module:ui.Dialog~Params
*
* @property {string} [className='dialog'] A class-name or multiple space separated.
* @property {'static'|true|false} [backdrop='static'] A backdrop.
* @property {boolean} [closeButton=true] A close button.
* @property {boolean} [collapseButton=false] A collapse button.
* @property {string|null} [header] A header HTML.
* @property {string} [body] A body HTML.
* @property {number|null} [width] A width.
* @property {boolean} [removeOnClose=true] To remove on close.
* @property {boolean} [draggable=false] Is draggable.
* @property {function(): void} [onRemove] An on-remove callback.
* @property {function(): void} [onClose] An on-close callback.
* @property {function(): void} [onBackdropClick] An on-backdrop-click callback.
* @property {string} [container='body'] A container selector.
* @property {boolean} [keyboard=true] Enable a keyboard control. The `Esc` key closes a dialog.
* @property {boolean} [footerAtTheTop=false] To display a footer at the top.
* @property {module:ui.Dialog~Button[]} [buttonList] Buttons.
* @property {Array} [dropdownItemList] Dropdown action items.
* @property {boolean} [fullHeight] Deprecated.
* @property {Number} [bodyDiffHeight]
* @property {Number} [screenWidthXs]
* @property {boolean} [maximizeButton] Is maximizable.
* @property {function()} [onMaximize] On maximize handler.
* @property {function()} [onMinimize] On minimize handler.
* @property {string} [backdropClassName] A backdrop class name. As of v9.1.0.
*/
/**
* A button or dropdown action item.
*
* @typedef {Object} module:ui.Dialog~Button
*
* @property {string} name A name.
* @property {boolean} [pullLeft=false] Deprecated. Use the `position` property.
* @property {'left'|'right'} [position='left'] A position.
* @property {string} [html] HTML.
* @property {string} [text] A text.
* @property {boolean} [disabled=false] Disabled.
* @property {boolean} [hidden=false] Hidden.
* @property {'default'|'danger'|'success'|'warning'} [style='default'] A style.
* @property {function(Espo.Ui.Dialog, JQueryEventObject): void} [onClick] An on-click callback.
* @property {string} [className] An additional class name.
* @property {string} [title] A title.
*/
/**
* @type {Espo.Ui.Dialog[]}
*/
const shownDialogList = [];
/**
* @alias Espo.Ui.Dialog
*/
class Dialog {
height
fitHeight
onRemove
onClose
onBackdropClick
buttons
screenWidthXs
backdropClassName
/**
* @private
* @type {boolean}
*/
maximizeButton = false
/**
* @private
* @type {HTMLAnchorElement}
*/
maximizeButtonElement
/**
* @private
* @type {HTMLAnchorElement}
*/
minimizeButtonElement
/**
* @private
* @type {function()}
*/
onMaximize
/**
* @private
* @type {function()}
*/
onMinimize
/**
* @param {module:ui.Dialog~Params} options Options.
*/
constructor(options) {
options = options || {};
/** @private */
this.className = 'dialog-confirm';
/** @private */
this.backdrop = 'static';
/** @private */
this.closeButton = true;
/** @private */
this.collapseButton = false;
/** @private */
this.header = null;
/** @private */
this.body = '';
/** @private */
this.width = null;
/**
* @private
* @type {module:ui.Dialog~Button[]}
*/
this.buttonList = [];
/**
* @private
* @type {Array}
*/
this.dropdownItemList = [];
/** @private */
this.removeOnClose = true;
/** @private */
this.draggable = false;
/** @private */
this.container = 'body';
/** @private */
this.options = options;
/** @private */
this.keyboard = true;
this.activeElement = document.activeElement;
const params = [
'className',
'backdrop',
'keyboard',
'closeButton',
'collapseButton',
'header',
'body',
'width',
'height',
'fitHeight',
'buttons',
'buttonList',
'dropdownItemList',
'removeOnClose',
'draggable',
'container',
'onRemove',
'onClose',
'onBackdropClick',
'maximizeButton',
'backdropClassName',
];
params.forEach(param => {
if (param in options) {
this[param] = options[param];
}
});
if (options.onMaximize) {
this.onMaximize = options.onMaximize;
}
if (options.onMinimize) {
this.onMinimize = options.onMinimize;
}
/** @private */
this.onCloseIsCalled = false;
if (this.buttons && this.buttons.length) {
/**
* @private
* @type {module:ui.Dialog~Button[]}
*/
this.buttonList = this.buttons;
}
this.id = 'dialog-' + Math.floor((Math.random() * 100000));
if (typeof this.backdrop === 'undefined') {
/** @private */
this.backdrop = 'static';
}
const $header = this.getHeader();
const $footer = this.getFooter();
const $body = $('