define("di", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.container = void 0;
  _exports.inject = inject;
  _exports.register = register;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  const registry = new Map();
  const container = _exports.container = new Map();

  /**
   * A DI container.
   */

  /**
   * A 'register' decorator.
   *
   * @param {*[]} argumentList Arguments.
   * @return {function(typeof Object)}
   */
  function register() {
    let argumentList = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
    return function (classObject) {
      registry.set(classObject, argumentList);
    };
  }

  /**
   * An 'inject' decorator.
   *
   * @param {typeof Object} classObject A class.
   * @return {(function(*, Object): void)}
   */
  function inject(classObject) {
    /**
     * @param {{addInitializer: function(function())}} context
     */
    return function (value, context) {
      context.addInitializer(function () {
        let instance = container.get(classObject);
        if (!instance) {
          instance = Reflect.construct(classObject, registry.get(classObject));
          container.set(classObject, instance);
        }
        this[context.name] = instance;
      });
    };
  }
});

define("view", ["exports", "bullbone"], function (_exports, _bullbone) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 view */

  /**
   * A base view. All views should extend this class.
   *
   * @see https://docs.espocrm.com/development/view/
   * @mixes Bull.Events
   */
  class View extends _bullbone.View {
    /**
     * @callback module:view~actionHandlerCallback
     * @param {MouseEvent} event A DOM event.
     * @param {HTMLElement} element A target element.
     */

    /**
     * A helper.
     *
     * @name _helper
     * @type {module:view-helper}
     * @memberOf View.prototype
     * @private
     */

    /**
     * A model.
     *
     * @type {import('model').default}
     */
    model;

    /**
     * A collection.
     *
     * @type {import('collection').default}
     */
    collection;

    /**
     * @param {Record<string, *>} options
     */
    constructor() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      super(options);
      if (options.model) {
        this.model = options.model;
      }
      if (options.collection) {
        this.collection = options.collection;
      }
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * When the view is ready. Can be useful to prevent race condition when re-initialization is needed
     * in-between initialization and render.
     *
     * @return Promise
     * @todo Move to Bull.View.
     */
    whenReady() {
      if (this.isReady) {
        return Promise.resolve();
      }
      return new Promise(resolve => {
        this.once('ready', () => resolve());
      });
    }

    /**
     * Add a DOM click event handler for a target defined by `data-action="{name}"` attribute.
     *
     * @param {string} action An action name.
     * @param {module:view~actionHandlerCallback} handler A handler.
     */
    addActionHandler(action, handler) {
      const fullAction = `click [data-action="${action}"]`;
      this.events[fullAction] = e => {
        // noinspection JSUnresolvedReference
        handler.call(this, e.originalEvent, e.currentTarget);
      };
    }

    /**
     * Escape a string.
     *
     * @param {string} string
     * @returns {string}
     */
    escapeString(string) {
      return Handlebars.Utils.escapeExpression(string);
    }

    /**
     * Show a notify-message.
     *
     * @deprecated Use `Espo.Ui.notify`.
     * @param {string|false} label
     * @param {string} [type]
     * @param {number} [timeout]
     * @param {string} [scope]
     */
    notify(label, type, timeout, scope) {
      if (!label) {
        Espo.Ui.notify(false);
        return;
      }
      scope = scope || null;
      timeout = timeout || 2000;
      if (!type) {
        timeout = void 0;
      }
      const text = this.getLanguage().translate(label, 'labels', scope);
      Espo.Ui.notify(text, type, timeout);
    }

    /**
     * Get a view-helper.
     *
     * @returns {module:view-helper}
     */
    getHelper() {
      return this._helper;
    }

    /**
     * Get a current user.
     *
     * @returns {module:models/user}
     */
    getUser() {
      return this._helper.user;
    }

    /**
     * Get the preferences.
     *
     * @returns {module:models/preferences}
     */
    getPreferences() {
      return this._helper.preferences;
    }

    /**
     * Get the config.
     *
     * @returns {module:models/settings}
     */
    getConfig() {
      return this._helper.settings;
    }

    /**
     * Get the ACL.
     *
     * @returns {module:acl-manager}
     */
    getAcl() {
      return this._helper.acl;
    }

    /**
     * Get the model factory.
     *
     * @returns {module:model-factory}
     */
    getModelFactory() {
      return this._helper.modelFactory;
    }

    /**
     * Get the collection factory.
     *
     * @returns {module:collection-factory}
     */
    getCollectionFactory() {
      return this._helper.collectionFactory;
    }

    /**
     * Get the router.
     *
     * @returns {module:router}
     */
    getRouter() {
      return this._helper.router;
    }

    /**
     * Get the storage-util.
     *
     * @returns {module:storage}
     */
    getStorage() {
      return this._helper.storage;
    }

    /**
     * Get the session-storage-util.
     *
     * @returns {module:session-storage}
     */
    getSessionStorage() {
      return this._helper.sessionStorage;
    }

    /**
     * Get the language-util.
     *
     * @returns {module:language}
     */
    getLanguage() {
      return this._helper.language;
    }

    /**
     * Get metadata.
     *
     * @returns {module:metadata}
     */
    getMetadata() {
      return this._helper.metadata;
    }

    /**
     * Get the cache-util.
     *
     * @returns {module:cache}
     */
    getCache() {
      return this._helper.cache;
    }

    /**
     * Get the date-time util.
     *
     * @returns {module:date-time}
     */
    getDateTime() {
      return this._helper.dateTime;
    }

    /**
     * Get the number-util.
     *
     * @returns {module:num-util}
     */
    getNumberUtil() {
      return this._helper.numberUtil;
    }

    /**
     * Get the field manager.
     *
     * @returns {module:field-manager}
     */
    getFieldManager() {
      return this._helper.fieldManager;
    }

    /**
     * Get the base-controller.
     *
     * @returns {module:controllers/base}
     */
    getBaseController() {
      return this._helper.baseController;
    }

    /**
     * Get the theme manager.
     *
     * @returns {module:theme-manager}
     */
    getThemeManager() {
      return this._helper.themeManager;
    }

    /**
     * Update a page title. Supposed to be overridden if needed.
     */
    updatePageTitle() {
      const title = this.getConfig().get('applicationName') || 'EspoCRM';
      this.setPageTitle(title);
    }

    /**
     * Set a page title.
     *
     * @param {string} title A title.
     */
    setPageTitle(title) {
      this.getHelper().pageTitle.setTitle(title);
    }

    /**
     * Translate a label.
     *
     * @param {string} label Label.
     * @param {string|'messages'|'labels'|'fields'|'links'|'scopeNames'|'scopeNamesPlural'} [category='labels'] Category.
     * @param {string} [scope='Global'] Scope.
     * @returns {string}
     */
    translate(label, category, scope) {
      return this.getLanguage().translate(label, category, scope);
    }

    /**
     * Get a base path.
     *
     * @returns {string}
     */
    getBasePath() {
      return this._helper.basePath || '';
    }

    /**
     * @typedef {Object} module:view~ConfirmOptions
     *
     * @property {string} message A message.
     * @property {string} [confirmText] A confirm-button text.
     * @property {string} [cancelText] A cancel-button text.
     * @property {'danger'|'success'|'warning'|'default'} [confirmStyle='danger'] A confirm-button style.
     * @property {'static'|boolean} [backdrop=false] A backdrop.
     * @property {function():void} [cancelCallback] A cancel-callback.
     */

    /**
     * Show a confirmation dialog.
     *
     * @param {string|module:view~ConfirmOptions} o A message or options.
     * @param [callback] A callback. Deprecated, use a promise.
     * @param [context] A context. Deprecated.
     * @returns {Promise} To be resolved if confirmed.
     */
    confirm(o, callback, context) {
      let message;
      if (typeof o === 'string' || o instanceof String) {
        message = o;
        o = /** @type {module:view~ConfirmOptions} */{};
      } else {
        o = o || {};
        message = o.message;
      }
      if (message) {
        message = this.getHelper().transformMarkdownText(message, {
          linksInNewTab: true
        }).toString();
      }
      const confirmText = o.confirmText || this.translate('Yes');
      const confirmStyle = o.confirmStyle || null;
      const cancelText = o.cancelText || this.translate('Cancel');
      return Espo.Ui.confirm(message, {
        confirmText: confirmText,
        cancelText: cancelText,
        confirmStyle: confirmStyle,
        backdrop: 'backdrop' in o ? o.backdrop : true,
        isHtml: true,
        cancelCallback: o.cancelCallback
      }, callback, context);
    }
  }
  var _default = _exports.default = View;
});

define("helpers/site/modal-bar-provider", ["exports", "di"], function (_exports, _di) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  var _temp;
  let _initClass;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * @internal
   */
  let _ModalBarProvider = _exports.default = void 0;
  class ModalBarProvider {
    static #_ = (_temp = [_ModalBarProvider, _initClass] = _applyDecs(this, [(0, _di.register)()], []).c, _exports.default = _ModalBarProvider, _temp);
    /**
     * @private
     * @type {import('views/collapsed-modal-bar').default|null}
     */
    view = null;

    /**
     * @internal
     * @return {import('views/collapsed-modal-bar').default|null}
     */
    get() {
      return this.view;
    }

    /**
     * @internal
     * @param {import('views/collapsed-modal-bar').default|null} view
     */
    set(view) {
      this.view = view;
    }
    static #_2 = _initClass();
  }
});

define("utils", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 utils */

  const IS_MAC = /Mac/.test(navigator.userAgent);

  /**
   * Utility functions.
   */
  Espo.Utils = {
    /**
     * Handle a click event action.
     *
     * @param {module:view} view A view.
     * @param {MouseEvent} event An event.
     * @param {HTMLElement} element An  element.
     * @param {{
     *     action?: string,
     *     handler?: string,
     *     actionFunction?: string,
     *     actionItems?: Array<{
     *         onClick?: function(),
     *         name?: string,
     *         handler?: string,
     *         actionFunction?: string,
     *     }>,
     *     className?: string,
     * }} [actionData] Data. If an action is not specified, it will be fetched from a target element.
     * @return {boolean} True if handled.
     */
    handleAction: function (view, event, element, actionData) {
      actionData = actionData || {};
      const $target = $(element);
      const action = actionData.action || $target.data('action');
      const name = $target.data('name') || action;
      let method;
      let handler;
      if (name && actionData.actionItems && (!actionData.className || element.classList.contains(actionData.className))) {
        const data = actionData.actionItems.find(item => {
          return item.name === name || item.action === name;
        });
        if (data && data.onClick) {
          data.onClick();
          return true;
        }
        if (data) {
          handler = data.handler;
          method = data.actionFunction;
        }
      }
      if (!action && !actionData.actionFunction && !method) {
        return false;
      }
      if (event.ctrlKey || event.metaKey || event.shiftKey) {
        const href = $target.attr('href');
        if (href && href !== 'javascript:') {
          return false;
        }
      }
      const data = $target.data();
      method = actionData.actionFunction || method || 'action' + Espo.Utils.upperCaseFirst(action);
      handler = actionData.handler || handler || data.handler;
      let fired = false;
      if (handler) {
        event.preventDefault();
        event.stopPropagation();
        fired = true;
        Espo.loader.require(handler, Handler => {
          const handler = new Handler(view);
          handler[method].call(handler, data, event);
        });
      } else if (typeof view[method] === 'function') {
        view[method].call(view, data, event);
        event.preventDefault();
        event.stopPropagation();
        fired = true;
      }
      if (!fired) {
        return false;
      }
      this._processAfterActionDropdown($target);
      return true;
    },
    /**
     * @private
     * @param {JQuery} $target
     */
    _processAfterActionDropdown: function ($target) {
      const $dropdown = $target.closest('.dropdown-menu');
      if (!$dropdown.length) {
        return;
      }
      const $dropdownToggle = $dropdown.parent().find('[data-toggle="dropdown"]');
      if (!$dropdownToggle.length) {
        return;
      }
      let isDisabled = false;
      if ($dropdownToggle.attr('disabled')) {
        isDisabled = true;
        $dropdownToggle.removeAttr('disabled').removeClass('disabled');
      }

      // noinspection JSUnresolvedReference
      $dropdownToggle.dropdown('toggle');
      $dropdownToggle.focus();
      if (isDisabled) {
        $dropdownToggle.attr('disabled', 'disabled').addClass('disabled');
      }
    },
    /**
     * @typedef {Object} Espo.Utils~ActionAvailabilityDefs
     *
     * @property {string|null} [configCheck] A config path to check. Path items are separated
     *   by the dot. If a config value is not empty, then the action is allowed.
     *   The `!` prefix reverses the check.
     */

    /**
     * Check action availability.
     *
     * @param {module:view-helper} helper A view helper.
     * @param {Espo.Utils~ActionAvailabilityDefs} item Definitions.
     * @returns {boolean}
     */
    checkActionAvailability: function (helper, item) {
      const config = helper.config;
      if (item.configCheck) {
        let configCheck = item.configCheck;
        let opposite = false;
        if (configCheck.substring(0, 1) === '!') {
          opposite = true;
          configCheck = configCheck.substring(1);
        }
        let configCheckResult = config.getByPath(configCheck.split('.'));
        if (opposite) {
          configCheckResult = !configCheckResult;
        }
        if (!configCheckResult) {
          return false;
        }
      }
      return true;
    },
    /**
     * @typedef {Object} Espo.Utils~ActionAccessDefs
     *
     * @property {'create'|'read'|'edit'|'stream'|'delete'|null} acl An ACL action to check.
     * @property {string|null} [aclScope] A scope to check.
     * @property {string|null} [scope] Deprecated. Use `aclScope`.
     */

    /**
     * Check access to an action.
     *
     * @param {module:acl-manager} acl An ACL manager.
     * @param {string|module:model|null} [obj] A scope or a model.
     * @param {Espo.Utils~ActionAccessDefs} item Definitions.
     * @param {boolean} [isPrecise=false] To return `null` if not enough data is set in a model.
     *   E.g. the `teams` field is not yet loaded.
     * @returns {boolean|null}
     */
    checkActionAccess: function (acl, obj, item, isPrecise) {
      let hasAccess = true;
      if (item.acl) {
        if (!item.aclScope) {
          if (obj) {
            if (typeof obj === 'string' || obj instanceof String) {
              hasAccess = acl.check(obj, item.acl);
            } else {
              hasAccess = acl.checkModel(obj, item.acl, isPrecise);
            }
          } else {
            hasAccess = acl.check(item.scope, item.acl);
          }
        } else {
          hasAccess = acl.check(item.aclScope, item.acl);
        }
      } else if (item.aclScope) {
        hasAccess = acl.checkScope(item.aclScope);
      }
      return hasAccess;
    },
    /**
     * @typedef {Object} Espo.Utils~AccessDefs
     *
     * @property {'create'|'read'|'edit'|'stream'|'delete'|null} action An ACL action to check.
     * @property {string|null} [scope] A scope to check.
     * @property {string[]} [portalIdList] A portal ID list. To check whether a user in one of portals.
     * @property {string[]} [teamIdList] A team ID list. To check whether a user in one of teams.
     * @property {boolean} [isPortalOnly=false] Allow for portal users only.
     * @property {boolean} [inPortalDisabled=false] Disable for portal users.
     * @property {boolean} [isAdminOnly=false] Allow for admin users only.
     */

    /**
     * Check access to an action.
     *
     * @param {module:utils~AccessDefs[]} dataList List of definitions.
     * @param {module:acl-manager} acl An ACL manager.
     * @param {module:models/user} user A user.
     * @param {module:model|null} [entity] A model.
     * @param {boolean} [allowAllForAdmin=false] Allow all for an admin.
     * @returns {boolean}
     */
    checkAccessDataList: function (dataList, acl, user, entity, allowAllForAdmin) {
      if (!dataList || !dataList.length) {
        return true;
      }
      for (const i in dataList) {
        const item = dataList[i];
        if (item.scope) {
          if (item.action) {
            if (!acl.check(item.scope, item.action)) {
              return false;
            }
          } else {
            if (!acl.checkScope(item.scope)) {
              return false;
            }
          }
        } else if (item.action) {
          if (entity) {
            if (!acl.check(entity, item.action)) {
              return false;
            }
          }
        }
        if (item.teamIdList) {
          if (user && !(allowAllForAdmin && user.isAdmin())) {
            let inTeam = false;
            user.getLinkMultipleIdList('teams').forEach(teamId => {
              if (~item.teamIdList.indexOf(teamId)) {
                inTeam = true;
              }
            });
            if (!inTeam) {
              return false;
            }
          }
        }
        if (item.portalIdList) {
          if (user && !(allowAllForAdmin && user.isAdmin())) {
            let inPortal = false;
            user.getLinkMultipleIdList('portals').forEach(portalId => {
              if (~item.portalIdList.indexOf(portalId)) {
                inPortal = true;
              }
            });
            if (!inPortal) {
              return false;
            }
          }
        }
        if (item.isPortalOnly) {
          if (user && !(allowAllForAdmin && user.isAdmin())) {
            if (!user.isPortal()) {
              return false;
            }
          }
        } else if (item.inPortalDisabled) {
          if (user && !(allowAllForAdmin && user.isAdmin())) {
            if (user.isPortal()) {
              return false;
            }
          }
        }
        if (item.isAdminOnly) {
          if (user) {
            if (!user.isAdmin()) {
              return false;
            }
          }
        }
      }
      return true;
    },
    /**
     * @private
     * @param {string} string
     * @param {string} p
     * @returns {string}
     */
    convert: function (string, p) {
      if (string === null) {
        return string;
      }
      let result = string;
      switch (p) {
        case 'c-h':
        case 'C-h':
          result = Espo.Utils.camelCaseToHyphen(string);
          break;
        case 'h-c':
          result = Espo.Utils.hyphenToCamelCase(string);
          break;
        case 'h-C':
          result = Espo.Utils.hyphenToUpperCamelCase(string);
          break;
      }
      return result;
    },
    /**
     * Is object.
     *
     * @param {*} obj What to check.
     * @returns {boolean}
     */
    isObject: function (obj) {
      if (obj === null) {
        return false;
      }
      return typeof obj === 'object';
    },
    /**
     * A shallow clone.
     *
     * @template {*} TObject
     * @param {TObject} obj An object.
     * @returns {TObject}
     */
    clone: function (obj) {
      if (!Espo.Utils.isObject(obj)) {
        return obj;
      }
      return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
    },
    /**
     * A deep clone.
     *
     * @template {*} TObject
     * @param {TObject} data An object.
     * @returns {TObject}
     */
    cloneDeep: function (data) {
      data = Espo.Utils.clone(data);
      if (Espo.Utils.isObject(data) || _.isArray(data)) {
        for (const i in data) {
          data[i] = this.cloneDeep(data[i]);
        }
      }
      return data;
    },
    /**
     * Deep comparison.
     *
     * @param {Object} a1 An argument 1.
     * @param {Object} a2 An argument 2.
     * @return {boolean}
     */
    areEqual: function (a1, a2) {
      return _.isEqual(a1, a2);
    },
    /**
     * Compose a class name.
     *
     * @param {string} module A module.
     * @param {string} name A name.
     * @param {string} [location=''] A location.
     * @return {string}
     */
    composeClassName: function (module, name, location) {
      if (module) {
        module = this.camelCaseToHyphen(module);
        name = this.camelCaseToHyphen(name).split('.').join('/');
        location = this.camelCaseToHyphen(location || '');
        return module + ':' + location + '/' + name;
      } else {
        name = this.camelCaseToHyphen(name).split('.').join('/');
        return location + '/' + name;
      }
    },
    /**
     * Compose a view class name.
     *
     * @param {string} name A name.
     * @returns {string}
     */
    composeViewClassName: function (name) {
      if (name && name[0] === name[0].toLowerCase()) {
        return name;
      }
      if (name.indexOf(':') !== -1) {
        const arr = name.split(':');
        let modPart = arr[0];
        let namePart = arr[1];
        modPart = this.camelCaseToHyphen(modPart);
        namePart = this.camelCaseToHyphen(namePart).split('.').join('/');
        return modPart + ':' + 'views' + '/' + namePart;
      } else {
        name = this.camelCaseToHyphen(name).split('.').join('/');
        return 'views' + '/' + name;
      }
    },
    /**
     * Convert a string from camelCase to hyphen and replace dots with hyphens.
     * Useful for setting to DOM attributes.
     *
     * @param {string} string A string.
     * @returns {string}
     */
    toDom: function (string) {
      return Espo.Utils.convert(string, 'c-h').split('.').join('-');
    },
    /**
     * Lower-case a first character.
     *
     * @param  {string} string A string.
     * @returns {string}
     */
    lowerCaseFirst: function (string) {
      if (string === null) {
        return string;
      }
      return string.charAt(0).toLowerCase() + string.slice(1);
    },
    /**
     * Upper-case a first character.
     *
     * @param  {string} string A string.
     * @returns {string}
     */
    upperCaseFirst: function (string) {
      if (string === null) {
        return string;
      }
      return string.charAt(0).toUpperCase() + string.slice(1);
    },
    /**
     * Hyphen to UpperCamelCase.
     *
     * @param {string} string A string.
     * @returns {string}
     */
    hyphenToUpperCamelCase: function (string) {
      if (string === null) {
        return string;
      }
      return this.upperCaseFirst(string.replace(/-([a-z])/g, function (g) {
        return g[1].toUpperCase();
      }));
    },
    /**
     * Hyphen to camelCase.
     *
     * @param {string} string A string.
     * @returns {string}
     */
    hyphenToCamelCase: function (string) {
      if (string === null) {
        return string;
      }
      return string.replace(/-([a-z])/g, function (g) {
        return g[1].toUpperCase();
      });
    },
    /**
     * CamelCase to hyphen.
     *
     * @param {string} string A string.
     * @returns {string}
     */
    camelCaseToHyphen: function (string) {
      if (string === null) {
        return string;
      }
      return string.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
    },
    /**
     * Trim an ending slash.
     *
     * @param {String} str A string.
     * @returns {string}
     */
    trimSlash: function (str) {
      if (str.slice(-1) === '/') {
        return str.slice(0, -1);
      }
      return str;
    },
    /**
     * Parse params in string URL options.
     *
     * @param {string} string An URL part.
     * @returns {Object.<string,string>}
     */
    parseUrlOptionsParam: function (string) {
      if (!string) {
        return {};
      }
      if (string.indexOf('&') === -1 && string.indexOf('=') === -1) {
        return {};
      }
      const options = {};
      if (typeof string !== 'undefined') {
        string.split('&').forEach(item => {
          const p = item.split('=');
          options[p[0]] = true;
          if (p.length > 1) {
            options[p[0]] = p[1];
          }
        });
      }
      return options;
    },
    /**
     * Key a key from a key-event.
     *
     * @param {JQueryKeyEventObject|KeyboardEvent} e A key event.
     * @return {string}
     */
    getKeyFromKeyEvent: function (e) {
      let key = e.code;
      key = keyMap[key] || key;
      if (e.shiftKey) {
        key = 'Shift+' + key;
      }
      if (e.altKey) {
        key = 'Alt+' + key;
      }
      if (IS_MAC ? e.metaKey : e.ctrlKey) {
        key = 'Control+' + key;
      }
      return key;
    },
    /**
     * Generate an ID. Not to be used by 3rd party code.
     *
     * @internal
     * @return {string}
     */
    generateId: function () {
      return Math.floor(Math.random() * 10000001).toString();
    },
    /**
     * Not to be used in custom code. Can be removed in future versions.
     * @internal
     * @return {string}
     */
    obtainBaseUrl: function () {
      let baseUrl = window.location.origin + window.location.pathname;
      if (baseUrl.slice(-1) !== '/') {
        baseUrl = window.location.pathname.includes('.') ? baseUrl.slice(0, baseUrl.lastIndexOf('/')) + '/' : baseUrl + '/';
      }
      return baseUrl;
    }
  };
  const keyMap = {
    'NumpadEnter': 'Enter'
  };

  /**
   * @deprecated Use `Espo.Utils`.
   */
  Espo.utils = Espo.Utils;
  var _default = _exports.default = Espo.Utils;
});

define("acl", ["exports", "bullbone"], function (_exports, _bullbone) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 acl */

  /**
   * Internal class for access checking. Can be extended to customize access checking
   * for a specific scope.
   */
  class Acl {
    /**
     * @param {module:models/user} user A user.
     * @param {string} scope A scope.
     * @param {Object} params Parameters.
     * @param {import('acl-manager').default} aclManager
     */
    constructor(user, scope, params, aclManager) {
      /**
       * A user.
       *
       * @type {module:models/user|null}
       * @protected
       */
      this.user = user || null;
      this.scope = scope;
      params = params || {};
      this.aclAllowDeleteCreated = params.aclAllowDeleteCreated;
      this.teamsFieldIsForbidden = params.teamsFieldIsForbidden;

      /**
       * @type {string[]}
       */
      this.forbiddenFieldList = params.forbiddenFieldList || [];

      /**
       * @protected
       * @type {boolean}
       */
      this.collaboratorsFieldIsForbidden = this.forbiddenFieldList.includes('collaborators');

      /**
       * @type {import('acl-manager').default}
       * @private
       */
      this._aclManager = aclManager;
    }

    /**
     * Get a user.
     *
     * @returns {module:models/user}
     * @protected
     */
    getUser() {
      return this.user;
    }

    /**
     * Check access to a scope.
     *
     * @param {string|boolean|Object.<string, string>} data Access data.
     * @param {module:acl-manager~action|null} [action=null] An action.
     * @param {boolean} [precise=false] To return `null` if `inTeam == null`.
     * @param {Record.<string, boolean|null>|null} [entityAccessData=null] Entity access data. `inTeam`, `isOwner`.
     * @returns {boolean|null} True if access allowed.
     */
    checkScope(data, action, precise, entityAccessData) {
      entityAccessData = entityAccessData || {};
      const inTeam = entityAccessData.inTeam;
      const isOwner = entityAccessData.isOwner;
      const isShared = entityAccessData.isShared;
      if (this.getUser().isAdmin()) {
        if (data === false) {
          return false;
        }
        return true;
      }
      if (data === false) {
        return false;
      }
      if (data === true) {
        return true;
      }
      if (typeof data === 'string') {
        return true;
      }
      if (data === null) {
        return false;
      }
      action = action || null;
      if (action === null) {
        return true;
      }
      if (!(action in data)) {
        return false;
      }
      const value = data[action];
      if (value === 'all') {
        return true;
      }
      if (value === 'yes') {
        return true;
      }
      if (value === 'no') {
        return false;
      }
      if (isOwner === undefined) {
        return true;
      }
      if (isOwner) {
        if (value === 'own' || value === 'team') {
          return true;
        }
      }
      if (isShared) {
        return true;
      }
      if (inTeam) {
        if (value === 'team') {
          return true;
        }
      }
      let result = false;
      if (value === 'team') {
        if (inTeam === null && precise) {
          result = null;
        }
      }
      if (isOwner === null && precise) {
        result = null;
      }
      if (isShared === null) {
        result = null;
      }
      return result;
    }

    /**
     * Check access to model (entity).
     *
     * @param {module:model} model A model.
     * @param {Object.<string, string>|string|null} data Access data.
     * @param {module:acl-manager~action|null} [action=null] Action to check.
     * @param {boolean} [precise=false] To return `null` if not enough data is set in a model.
     *   E.g. the `teams` field is not yet loaded.
     * @returns {boolean|null} True if access allowed, null if not enough data to determine.
     */
    checkModel(model, data, action, precise) {
      if (this.getUser().isAdmin()) {
        return true;
      }
      let isShared = false;
      if (action === 'read' || action === 'stream') {
        isShared = this.checkIsShared(model);
      }
      const entityAccessData = {
        isOwner: this.checkIsOwner(model),
        inTeam: this.checkInTeam(model),
        isShared: isShared
      };
      return this.checkScope(data, action, precise, entityAccessData);
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Check `delete` access to model.
     *
     * @param {module:model} model A model.
     * @param {Object.<string, string>|string|null} data Access data.
     * @param {boolean} [precise=false] To return `null` if not enough data is set in a model.
     *   E.g. the `teams` field is not yet loaded.
     * @returns {boolean} True if access allowed.
     */
    checkModelDelete(model, data, precise) {
      const result = this.checkModel(model, data, 'delete', precise);
      if (result) {
        return true;
      }
      if (data === false) {
        return false;
      }
      const d = data || {};
      if (d.read === 'no') {
        return false;
      }
      if (model.has('createdById') && model.get('createdById') === this.getUser().id && this.aclAllowDeleteCreated) {
        if (!model.has('assignedUserId')) {
          return true;
        }
        if (!model.get('assignedUserId')) {
          return true;
        }
        if (model.get('assignedUserId') === this.getUser().id) {
          return true;
        }
      }
      return result;
    }

    /**
     * Check if a user is owner to a model.
     *
     * @param {module:model} model A model.
     * @returns {boolean|null} True if owner. Null if not clear.
     */
    checkIsOwner(model) {
      let result = false;
      if (model.hasField('assignedUser')) {
        if (this.getUser().id === model.get('assignedUserId')) {
          return true;
        }
        if (!model.has('assignedUserId')) {
          result = null;
        }
      } else if (model.hasField('createdBy')) {
        if (this.getUser().id === model.get('createdById')) {
          return true;
        }
        if (!model.has('createdById')) {
          result = null;
        }
      }
      if (model.hasField('assignedUsers')) {
        if (!model.has('assignedUsersIds')) {
          return null;
        }
        if ((model.get('assignedUsersIds') || []).includes(this.getUser().id)) {
          return true;
        }
        result = false;
      }
      return result;
    }

    /**
     * Check if a user in a team of a model.
     *
     * @param {module:model} model A model.
     * @returns {boolean|null} True if in a team. Null if not enough data to determine.
     */
    checkInTeam(model) {
      const userTeamIdList = this.getUser().getTeamIdList();
      if (!model.has('teamsIds')) {
        if (this.teamsFieldIsForbidden) {
          return true;
        }
        if (!model.hasField('teams')) {
          return false;
        }
        return null;
      }
      const teamIdList = model.getTeamIdList();
      let inTeam = false;
      userTeamIdList.forEach(id => {
        if (teamIdList.includes(id)) {
          inTeam = true;
        }
      });
      return inTeam;
    }

    /**
     * Check if a record is shared with the user.
     *
     * @param {module:model} model A model.
     * @returns {boolean|null} True if shared. Null if not enough data to determine.
     */
    checkIsShared(model) {
      if (!model.has('collaboratorsIds')) {
        if (this.collaboratorsFieldIsForbidden) {
          return true;
        }
        if (!model.hasField('collaborators')) {
          return false;
        }
        return null;
      }
      const collaboratorsIds = model.getLinkMultipleIdList('collaborators');
      return collaboratorsIds.includes(this.user.id);
    }

    /**
     * Get a permission level.
     *
     * @protected
     * @param {string} permission A permission name.
     * @returns {'yes'|'all'|'team'|'no'}
     */
    getPermissionLevel(permission) {
      return this._aclManager.getPermissionLevel(permission);
    }
  }
  Acl.extend = _bullbone.View.extend;
  var _default = _exports.default = Acl;
});

define("views/modal", ["exports", "view", "di", "helpers/site/modal-bar-provider"], function (_exports, _view, _di, _modalBarProvider) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _modalBarProvider = _interopRequireDefault(_modalBarProvider);
  let _init_modalBarProvider, _init_extra_modalBarProvider;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/modal */
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * A base modal view. Can be extended or used directly.
   *
   * @see https://docs.espocrm.com/development/modal/
   */
  class ModalView extends _view.default {
    static #_ = [_init_modalBarProvider, _init_extra_modalBarProvider] = _applyDecs(this, [], [[(0, _di.inject)(_modalBarProvider.default), 0, "modalBarProvider"]], 0, void 0, _view.default).e;
    /**
     * A button or dropdown action item.
     *
     * @typedef {Object} module:views/modal~Button
     *
     * @property {string} name A name.
     * @property {string} [label] A label. To be translated
     *   (with a scope defined in the `scope` class property).
     * @property {string} [text] A text (not translated).
     * @property {string} [labelTranslation] A label translation path.
     * @property {string} [html] HTML.
     * @property {boolean} [pullLeft=false] Deprecated. Use the `position` property.
     * @property {'left'|'right'} [position='left'] A position.
     * @property {'default'|'danger'|'success'|'warning'|'info'} [style='default'] A style.
     * @property {boolean} [hidden=false] Is hidden.
     * @property {boolean} [disabled=false] Disabled.
     * @property {function(module:ui.Dialog): void} [onClick] Called on click. If not defined, then
     * the `action<Name>` class method will be called.
     * @property {string} [className] An additional class name.
     * @property {string} [title] A title text.
     * @property {'primary'|'danger'|'success'|'warning'|'text'} [style] A style.
     * @property {string} [iconHtml] An icon HTML.
     * @property {string} [iconClass] An icon class.
     * @property {number} [groupIndex] A group index. Only for the dropdown.
     */

    /**
     * @typedef {Object} module:views/modal~Options
     * @property {string} [headerText] A header text.
     * @property {HTMLElement} [headerElement] A header element.
     * @property {'static'|boolean} [backdrop] A backdrop.
     * @property {module:views/modal~Button} [buttonList] Buttons.
     * @property {module:views/modal~Button} [dropdownItemList] Buttons.
     * @property {boolean} [collapseDisabled] Not collapsible. As of v9.1.0.
     */

    /**
     * @param {module:views/modal~Options | Record} [options] Options.
     */
    constructor(options) {
      super(options), _init_extra_modalBarProvider(this);
    }

    /**
     * A CSS name.
     *
     * @protected
     */
    cssName = 'modal-dialog';

    /**
     * A class-name. Use `'dialog dialog-record'` for modals containing a record form.
     *
     * @protected
     */
    className = 'dialog';

    /**
     * @protected
     * @deprecated Use `headerHtml`
     */
    header;

    /**
     * A header HTML. Beware of XSS.
     *
     * @protected
     * @type {string|null}
     */
    headerHtml;

    /**
     * A header JQuery instance.
     *
     * @protected
     * @type {JQuery}
     */
    $header;

    /**
     * A header element.
     *
     * @protected
     * @type {Element}
     */
    headerElement;

    /**
     * A header text.
     *
     * @protected
     * @type {string}
     */
    headerText;

    /**
     * A dialog instance.
     *
     * @protected
     * @type {Espo.Ui.Dialog}
     */
    dialog;

    /**
     * A container selector.
     *
     * @protected
     * @type {string}
     */
    containerSelector = '';

    /**
     * A scope name. Used when translating button labels.
     *
     * @type {string|null}
     */
    scope = null;

    /**
     * A backdrop.
     *
     * @protected
     * @type {'static'|boolean}
     */
    backdrop = 'static';

    /**
     * Buttons.
     *
     * @protected
     * @type {module:views/modal~Button[]}
     */
    buttonList = [];

    /**
     * Dropdown action items.
     *
     * @protected
     * @type {Array<module:views/modal~Button|false>}
     */
    dropdownItemList = [];

    /**
     * @deprecated Use `buttonList`.
     * @protected
     * @todo Remove.
     */
    buttons = [];

    /**
     * A width. Do not use.
     * @todo Consider removing.
     *
     * @protected
     * @type {number|null}
     */
    width = null;

    /**
     * Not used.
     *
     * @deprecated
     */
    fitHeight = false;

    /**
     * To disable fitting to a window height.
     *
     * @protected
     * @type {boolean}
     */
    noFullHeight = false;

    /**
     * Disable the ability to close by pressing the `Esc` key.
     *
     * @protected
     * @type {boolean}
     */
    escapeDisabled = false;

    /**
     * Is draggable.
     *
     * @protected
     * @type {boolean}
     */
    isDraggable = false;

    /**
     * Is collapsable.
     *
     * @protected
     * @type {boolean}
     */
    isCollapsible = false;

    /**
     * Is maximizable.
     *
     * @protected
     * @type {boolean}
     * @since 9.1.0
     */
    isMaximizable = false;

    /**
     * Is collapsed. Do not change value. Only for reading.
     *
     * @protected
     * @type {boolean}
     */
    isCollapsed = false;

    /**
     * @type {HTMLElement}
     * @protected
     */
    bodyElement;

    /**
     * @inheritDoc
     */
    events = {
      /** @this module:views/modal */
      'click .action': function (e) {
        Espo.Utils.handleAction(this, e.originalEvent, e.currentTarget);
      }
    };

    /**
     * @protected
     * @type {boolean|null}
     */
    footerAtTheTop = null;

    /**
     * A shortcut-key => action map.
     *
     * @protected
     * @type {?Object.<string, string|function (KeyboardEvent): void>}
     */
    shortcutKeys = null;

    /**
     * @protected
     * @type {HTMLElement}
     * @since 9.0.0
     */
    containerElement;

    /**
     * @private
     * @type {ModalBarProvider}
     */
    modalBarProvider = _init_modalBarProvider(this);

    /**
     * @inheritDoc
     */
    init() {
      const id = this.cssName + '-container-' + Math.floor(Math.random() * 10000 + 1).toString();
      this.containerSelector = '#' + id;
      this.header = this.options.header || this.header;
      this.headerHtml = this.options.headerHtml || this.headerHtml;
      this.$header = this.options.$header || this.$header;
      this.headerElement = this.options.headerElement || this.headerElement;
      this.headerText = this.options.headerText || this.headerText;
      this.backdrop = this.options.backdrop || this.backdrop;
      this.setSelector(this.containerSelector);
      this.buttonList = this.options.buttonList || this.buttonList;
      this.dropdownItemList = this.options.dropdownItemList || this.dropdownItemList;
      this.buttonList = Espo.Utils.cloneDeep(this.buttonList);
      this.dropdownItemList = Espo.Utils.cloneDeep(this.dropdownItemList);
      if (this.shortcutKeys) {
        this.shortcutKeys = Espo.Utils.cloneDeep(this.shortcutKeys);
      }
      if (this.options.collapseDisabled) {
        this.isCollapsible = false;
      }
      this.on('render', () => {
        if (this.dialog) {
          this.dialog.close();
        }

        // Otherwise, re-render won't work.
        this.element = undefined;
        this.isCollapsed = false;
        $(this.containerSelector).remove();
        $('<div />').css('display', 'none').attr('id', id).addClass('modal-container').appendTo('body');
        let modalBodyDiffHeight = 92;
        if (this.getThemeManager().getParam('modalBodyDiffHeight') !== null) {
          modalBodyDiffHeight = this.getThemeManager().getParam('modalBodyDiffHeight');
        }
        let headerHtml = this.headerHtml || this.header;
        if (this.$header && this.$header.length) {
          headerHtml = this.$header.get(0).outerHTML;
        }
        if (this.headerElement) {
          headerHtml = this.headerElement.outerHTML;
        }
        if (this.headerText) {
          headerHtml = Handlebars.Utils.escapeExpression(this.headerText);
        }
        const footerAtTheTop = this.footerAtTheTop !== null ? this.footerAtTheTop : this.getThemeManager().getParam('modalFooterAtTheTop');
        this.dialog = new Espo.Ui.Dialog({
          backdrop: this.backdrop,
          header: headerHtml,
          container: this.containerSelector,
          body: '',
          buttonList: this.getDialogButtonList(),
          dropdownItemList: this.getDialogDropdownItemList(),
          width: this.width,
          keyboard: !this.escapeDisabled,
          fitHeight: this.fitHeight,
          draggable: this.isDraggable,
          className: this.className,
          bodyDiffHeight: modalBodyDiffHeight,
          footerAtTheTop: footerAtTheTop,
          fullHeight: !this.noFullHeight && this.getThemeManager().getParam('modalFullHeight'),
          screenWidthXs: this.getThemeManager().getParam('screenWidthXs'),
          fixedHeaderHeight: this.fixedHeaderHeight,
          closeButton: !this.noCloseButton,
          collapseButton: this.isCollapsible,
          maximizeButton: this.isMaximizable && !this.getHelper().isXsScreen(),
          onRemove: () => this.onDialogClose(),
          onBackdropClick: () => this.onBackdropClick(),
          onMaximize: () => this.onMaximize(),
          onMinimize: () => this.onMinimize()
        });
        this.containerElement = document.querySelector(this.containerSelector);
        this.setElement(this.containerSelector + ' .body');
        this.bodyElement = this.element;

        // @todo Review that the element is set back to the container afterwards.
        //     Force keeping set to the body?
      });
      this.on('after:render', () => {
        // Trick to delegate events for the whole modal.
        this.element = undefined;
        this.setElement(this.containerSelector);
        $(this.containerSelector).show();
        this.dialog.show();
        if (this.fixedHeaderHeight && this.flexibleHeaderFontSize) {
          this.adjustHeaderFontSize();
        }
        this.adjustButtons();
        if (!this.noFullHeight) {
          this.initBodyScrollListener();
        }
        if (this.getParentView()) {
          this.getParentView().trigger('modal-shown');
        }
      });
      this.once('remove', () => {
        if (this.dialog) {
          this.dialog.close();
        }
        $(this.containerSelector).remove();
      });
      if (this.isCollapsible) {
        this.addActionHandler('collapseModal', () => this.collapse());
      }
      this.on('after:expand', () => this.afterExpand());
    }
    setupFinal() {
      if (this.shortcutKeys) {
        this.events['keydown.modal-base'] = e => {
          const key = Espo.Utils.getKeyFromKeyEvent(e);
          if (typeof this.shortcutKeys[key] === 'function') {
            this.shortcutKeys[key].call(this, e.originalEvent);
            return;
          }
          const actionName = this.shortcutKeys[key];
          if (!actionName) {
            return;
          }
          if (this.hasActionItem(actionName) && !this.hasAvailableActionItem(actionName)) {
            return;
          }
          e.preventDefault();
          e.stopPropagation();
          const methodName = 'action' + Espo.Utils.upperCaseFirst(actionName);
          if (typeof this[methodName] === 'function') {
            this[methodName]();
            return;
          }
          this[actionName]();
        };
      }
    }

    /**
     * Get a button list for a dialog.
     *
     * @private
     * @return {module:ui.Dialog~Button[]}
     */
    getDialogButtonList() {
      const buttonListExt = [];

      // @todo remove it as deprecated.
      this.buttons.forEach(item => {
        const o = Espo.Utils.clone(item);
        if (!('text' in o) && 'label' in o) {
          o.text = this.getLanguage().translate(o.label);
        }
        buttonListExt.push(o);
      });
      this.buttonList.forEach(item => {
        let o = {};
        if (typeof item === 'string') {
          o.name = /** @type string */item;
        } else if (typeof item === 'object') {
          o = item;
        } else {
          return;
        }
        if (!o.text) {
          if (o.labelTranslation) {
            o.text = this.getLanguage().translatePath(o.labelTranslation);
          } else if ('label' in o) {
            o.text = this.translate(o.label, 'labels', this.scope);
          } else {
            o.text = this.translate(o.name, 'modalActions', this.scope);
          }
        }
        if (o.iconHtml && !o.html) {
          o.html = o.iconHtml + '<span>' + this.getHelper().escapeString(o.text) + '</span>';
        } else if (o.iconClass && !o.html) {
          o.html = `<span class="${o.iconClass}"></span>` + '<span>' + this.getHelper().escapeString(o.text) + '</span>';
        }
        o.onClick = o.onClick || ((d, e) => {
          const handler = o.handler || (o.data || {}).handler;
          Espo.Utils.handleAction(this, e.originalEvent, e.currentTarget, {
            action: o.name,
            handler: handler,
            actionFunction: o.actionFunction
          });
        });
        buttonListExt.push(o);
      });
      return buttonListExt;
    }

    /**
     * Get a dropdown item list for a dialog.
     *
     * @private
     * @return {Array<module:ui.Dialog~Button|false>}
     */
    getDialogDropdownItemList() {
      const dropdownItemListExt = [];
      this.dropdownItemList.forEach(item => {
        let o = {};
        if (typeof item === 'string') {
          o.name = /** @type string */item;
        } else if (typeof item === 'object') {
          o = item;
        } else {
          return;
        }
        if (!o.text) {
          if (o.labelTranslation) {
            o.text = this.getLanguage().translatePath(o.labelTranslation);
          } else if ('label' in o) {
            o.text = this.translate(o.label, 'labels', this.scope);
          } else {
            o.text = this.translate(o.name, 'modalActions', this.scope);
          }
        }
        o.onClick = o.onClick || ((d, e) => {
          // noinspection ES6ConvertLetToConst
          let handler = o.handler || (o.data || {}).handler;
          Espo.Utils.handleAction(this, e.originalEvent, e.currentTarget, {
            action: o.name,
            handler: handler,
            actionFunction: o.actionFunction
          });
        });
        dropdownItemListExt.push(o);
      });

      /** @type {Array<module:ui.Dialog~Button[]>} */
      const dropdownGroups = [];
      dropdownItemListExt.forEach(item => {
        // For bc.
        if (item === false) {
          return;
        }
        const index = (item.groupIndex === undefined ? 9999 : item.groupIndex) + 100;
        if (dropdownGroups[index] === undefined) {
          dropdownGroups[index] = [];
        }
        dropdownGroups[index].push(item);
      });
      const dropdownItemList = [];
      dropdownGroups.forEach(list => {
        list.forEach(it => dropdownItemList.push(it));
        dropdownItemList.push(false);
      });
      return dropdownItemList;
    }

    /** @private */
    updateDialog() {
      if (!this.dialog) {
        return;
      }
      this.dialog.setActionItems(this.getDialogButtonList(), this.getDialogDropdownItemList());
    }

    /** @private */
    onDialogClose() {
      if (!this.isBeingRendered() && !this.isCollapsed) {
        this.trigger('close');
        this.remove();
      }
    }

    /**
     * @protected
     */
    onBackdropClick() {}

    /**
     * A `cancel` action.
     */
    actionCancel() {
      this.trigger('cancel');
      this.close();
    }

    /**
     * A `close` action.
     */
    actionClose() {
      this.actionCancel();
    }

    /**
     * Close a dialog.
     */
    close() {
      this.dialog.close();
      if (!this.getParentView()) {
        return;
      }
      const key = this.getParentView().getViewKey(this);
      if (key) {
        this.getParentView().clearView(key);
      }
    }

    /**
     * Disable a button.
     *
     * @param {string} name A button name.
     */
    disableButton(name) {
      this.buttonList.forEach(d => {
        if (d.name !== name) {
          return;
        }
        d.disabled = true;
      });
      if (!this.isRendered()) {
        return;
      }
      if (!this.containerElement) {
        return;
      }
      $(this.containerElement).find(`footer button[data-name="${name}"]`).addClass('disabled').attr('disabled', 'disabled');
    }

    /**
     * Enable a button.
     *
     * @param {string} name A button name.
     */
    enableButton(name) {
      this.buttonList.forEach(d => {
        if (d.name !== name) {
          return;
        }
        d.disabled = false;
      });
      if (!this.isRendered()) {
        return;
      }
      if (!this.containerElement) {
        return;
      }
      $(this.containerElement).find('footer button[data-name="' + name + '"]').removeClass('disabled').removeAttr('disabled');
    }

    /**
     * Add a button.
     *
     * @param {module:views/modal~Button} o Button definitions.
     * @param {boolean|string} [position=false] True prepends, false appends. If a string
     *   then will be added after a button with a corresponding name.
     * @param {boolean} [doNotReRender=false] Do not re-render.
     */
    addButton(o, position, doNotReRender) {
      let index = -1;
      this.buttonList.forEach((item, i) => {
        if (item.name === o.name) {
          index = i;
        }
      });
      if (~index) {
        return;
      }
      if (position === true) {
        this.buttonList.unshift(o);
      } else if (typeof position === 'string') {
        index = -1;
        this.buttonList.forEach((item, i) => {
          if (item.name === position) {
            index = i;
          }
        });
        if (~index) {
          this.buttonList.splice(index, 0, o);
        } else {
          this.buttonList.push(o);
        }
      } else {
        this.buttonList.push(o);
      }
      if (!doNotReRender && this.isRendered()) {
        this.reRenderFooter();
      }
    }

    /**
     * Add a dropdown item.
     *
     * @param {module:views/modal~Button} o Button definitions.
     * @param {boolean} [toBeginning=false] To prepend.
     * @param {boolean} [doNotReRender=false] Do not re-render.
     */
    addDropdownItem(o, toBeginning, doNotReRender) {
      if (!o) {
        // For bc.
        return;
      }
      const name = o.name;
      if (!name) {
        return;
      }
      for (const item of this.dropdownItemList) {
        if (item.name === name) {
          return;
        }
      }
      toBeginning ? this.dropdownItemList.unshift(o) : this.dropdownItemList.push(o);
      if (!doNotReRender && this.isRendered()) {
        this.reRenderFooter();
      }
    }

    /** @private */
    reRenderFooter() {
      if (!this.dialog) {
        return;
      }
      this.updateDialog();
      const $footer = this.dialog.getFooter();
      $(this.containerElement).find('footer.modal-footer').empty().append($footer);
      this.dialog.initButtonEvents();
    }

    /**
     * Remove a button or a dropdown action item.
     *
     * @param {string} name A name.
     * @param {boolean} [doNotReRender=false] Do not re-render.
     */
    removeButton(name, doNotReRender) {
      let index = -1;
      for (const [i, item] of this.buttonList.entries()) {
        if (item.name === name) {
          index = i;
          break;
        }
      }
      if (~index) {
        this.buttonList.splice(index, 1);
      }
      for (const [i, item] of this.dropdownItemList.entries()) {
        if (item.name === name) {
          this.dropdownItemList.splice(i, 1);
          break;
        }
      }
      if (this.isRendered()) {
        $(this.containerElement).find(`.modal-footer [data-name="${name}"]`).remove();
      }
      if (!doNotReRender && this.isRendered()) {
        this.reRender();
      }
    }

    /**
     * @deprecated Use `showActionItem`.
     *
     * @protected
     * @param {string} name
     */
    showButton(name) {
      for (const item of this.buttonList) {
        if (item.name === name) {
          item.hidden = false;
          break;
        }
      }
      if (!this.isRendered()) {
        return;
      }
      if (!this.containerElement) {
        return;
      }
      $(this.containerElement).find(`footer button[data-name="${name}"]`).removeClass('hidden');
      this.adjustButtons();
    }

    /**
     * @deprecated Use `hideActionItem`.
     *
     * @protected
     * @param {string} name
     */
    hideButton(name) {
      for (const item of this.buttonList) {
        if (item.name === name) {
          item.hidden = true;
          break;
        }
      }
      if (!this.isRendered()) {
        return;
      }
      if (!this.containerElement) {
        return;
      }
      $(this.containerElement).find(`footer button[data-name="${name}"]`).addClass('hidden');
      this.adjustButtons();
    }

    /**
     * Show an action item (button or dropdown item).
     *
     * @param {string} name A name.
     */
    showActionItem(name) {
      for (const item of this.buttonList) {
        if (item.name === name) {
          item.hidden = false;
          break;
        }
      }
      for (const item of this.dropdownItemList) {
        if (item.name === name) {
          item.hidden = false;
          break;
        }
      }
      if (!this.isRendered()) {
        return;
      }
      if (!this.containerElement) {
        return;
      }
      const $el = $(this.containerElement);
      $el.find(`footer button[data-name="${name}"]`).removeClass('hidden');
      $el.find(`footer li > a[data-name="${name}"]`).parent().removeClass('hidden');
      if (!this.isDropdownItemListEmpty()) {
        const $dropdownGroup = $el.find('footer .main-btn-group > .btn-group');
        $dropdownGroup.removeClass('hidden');
        $dropdownGroup.find('> button').removeClass('hidden');
      }
      this.adjustButtons();
    }

    /**
     * Hide an action item (button or dropdown item).
     *
     * @param {string} name A name.
     */
    hideActionItem(name) {
      for (const item of this.buttonList) {
        if (item.name === name) {
          item.hidden = true;
          break;
        }
      }
      for (const item of this.dropdownItemList) {
        if (item.name === name) {
          item.hidden = true;
          break;
        }
      }
      if (!this.isRendered()) {
        return;
      }
      const $el = $(this.containerElement);
      $el.find(`footer button[data-name="${name}"]`).addClass('hidden');
      $el.find(`footer li > a[data-name="${name}"]`).parent().addClass('hidden');
      if (this.isDropdownItemListEmpty()) {
        const $dropdownGroup = $el.find('footer .main-btn-group > .btn-group');
        $dropdownGroup.addClass('hidden');
        $dropdownGroup.find('> button').addClass('hidden');
      }
      this.adjustButtons();
    }

    /**
     * Whether an action item exists (hidden, disabled or not).
     *
     * @param {string} name An action item name.
     */
    hasActionItem(name) {
      const hasButton = this.buttonList.findIndex(item => item.name === name) !== -1;
      if (hasButton) {
        return true;
      }
      return this.dropdownItemList.findIndex(item => item.name === name) !== -1;
    }

    /**
     * Whether an action item is visible and not disabled.
     *
     * @param {string} name An action item name.
     */
    hasAvailableActionItem(name) {
      const hasButton = this.buttonList.findIndex(item => item.name === name && !item.disabled && !item.hidden) !== -1;
      if (hasButton) {
        return true;
      }
      return this.dropdownItemList.findIndex(item => item.name === name && !item.disabled && !item.hidden) !== -1;
    }

    /**
     * @private
     * @return {boolean}
     */
    isDropdownItemListEmpty() {
      if (this.dropdownItemList.length === 0) {
        return true;
      }
      let isEmpty = true;
      this.dropdownItemList.forEach(item => {
        if (!item.hidden) {
          isEmpty = false;
        }
      });
      return isEmpty;
    }

    /**
     * @private
     * @param {number} [step=0]
     */
    adjustHeaderFontSize(step) {
      step = step || 0;
      if (!step) {
        this.fontSizePercentage = 100;
      }
      if (!this.containerElement) {
        return;
      }
      const $titleText = $(this.containerElement).find('.modal-title > .modal-title-text');
      const containerWidth = $titleText.parent().width();
      let textWidth = 0;
      $titleText.children().each((i, el) => {
        textWidth += $(el).outerWidth(true);
      });
      if (containerWidth < textWidth) {
        if (step > 5) {
          const $title = $(this.containerElement).find('.modal-title');
          $title.attr('title', $titleText.text());
          $title.addClass('overlapped');
          $titleText.children().each((i, el) => {
            $(el).removeAttr('title');
          });
          return;
        }
        this.fontSizePercentage -= 4;
        $(this.containerElement).find('.modal-title .font-size-flexible').css('font-size', this.fontSizePercentage + '%');
        this.adjustHeaderFontSize(step + 1);
      }
    }

    /**
     * Collapse.
     */
    async collapse() {
      let data = await this.beforeCollapse();
      if (!this.getParentView()) {
        throw new Error("Can't collapse w/o parent view.");
      }
      this.isCollapsed = true;
      data = data || {};
      let title;
      if (data.title) {
        title = data.title;
      } else {
        const titleElement = this.containerElement.querySelector('.modal-header .modal-title .modal-title-text');
        if (titleElement) {
          title = titleElement.textContent;
        }
      }
      this.dialog.close();
      let masterView = this;
      while (masterView.getParentView()) {
        masterView = masterView.getParentView();
      }
      this.unchainFromParent();
      const barView = this.modalBarProvider.get();
      if (!barView) {
        return;
      }
      await barView.addModalView(this, {
        title: title
      });
    }
    unchainFromParent() {
      const key = this.getParentView().getViewKey(this);
      this.getParentView().unchainView(key);
    }

    /**
     * Called before collapse. Can be extended to execute some logic, e.g. save form data.
     *
     * @protected
     * @return {Promise}
     */
    beforeCollapse() {
      return new Promise(resolve => resolve());
    }

    /**
     * Called after expanding.
     *
     * @protected
     * @since 9.1.0
     */
    afterExpand() {}

    /** @private */
    adjustButtons() {
      this.adjustLeftButtons();
      this.adjustRightButtons();
    }

    /** @private */
    adjustLeftButtons() {
      const $buttons = $(this.containerElement).find('footer.modal-footer > .main-btn-group button.btn');
      $buttons.removeClass('radius-left').removeClass('radius-right');
      const $buttonsVisible = $buttons.filter('button:not(.hidden)');
      $buttonsVisible.first().addClass('radius-left');
      $buttonsVisible.last().addClass('radius-right');
    }

    /** @private */
    adjustRightButtons() {
      const $buttons = $(this.containerElement).find('footer.modal-footer > .additional-btn-group button.btn:not(.btn-text)');
      $buttons.removeClass('radius-left').removeClass('radius-right').removeClass('margin-right');
      const $buttonsVisible = $buttons.filter('button:not(.hidden)');
      $buttonsVisible.first().addClass('radius-left');
      $buttonsVisible.last().addClass('radius-right');
      if ($buttonsVisible.last().next().hasClass('btn-text')) {
        $buttonsVisible.last().addClass('margin-right');
      }
    }

    /**
     * @private
     */
    initBodyScrollListener() {
      const $body = $(this.containerElement).find('> .dialog > .modal-dialog > .modal-content > .modal-body');
      const $footer = $body.parent().find('> .modal-footer');
      if (!$footer.length) {
        return;
      }
      $body.off('scroll.footer-shadow');
      $body.on('scroll.footer-shadow', () => {
        if ($body.scrollTop()) {
          $footer.addClass('shadowed');
          return;
        }
        $footer.removeClass('shadowed');
      });
    }

    /**
     * @protected
     * @since 9.1.0
     */
    onMaximize() {}

    /**
     * @protected
     * @since 9.1.0
     */
    onMinimize() {}
  }
  var _default = _exports.default = ModalView;
});

define("storage", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 storage */

  /**
   * A storage. Data is saved across browser sessions, has no expiration time.
   */
  class Storage {
    constructor() {}

    /** @protected */
    prefix = 'espo';

    /** @protected */
    storageObject = localStorage;

    /**
     * @private
     * @param {string} type
     * @returns {string}
     */
    composeFullPrefix(type) {
      return this.prefix + '-' + type;
    }

    /**
     * @private
     * @param {string} type
     * @param {string} name
     * @returns {string}
     */
    composeKey(type, name) {
      return this.composeFullPrefix(type) + '-' + name;
    }

    /**
     * @private
     * @param {string} type
     */
    checkType(type) {
      if (typeof type === 'undefined' && toString.call(type) !== '[object String]' || type === 'cache') {
        throw new TypeError("Bad type \"" + type + "\" passed to Espo.Storage.");
      }
    }

    /**
     * Has a value.
     *
     * @param {string} type A type (category).
     * @param {string} name A name.
     * @returns {boolean}
     */
    has(type, name) {
      this.checkType(type);
      const key = this.composeKey(type, name);
      return this.storageObject.getItem(key) !== null;
    }

    /**
     * Get a value.
     *
     * @param {string} type A type (category).
     * @param {string} name A name.
     * @returns {*} Null if not stored.
     */
    get(type, name) {
      this.checkType(type);
      const key = this.composeKey(type, name);
      let stored;
      try {
        stored = this.storageObject.getItem(key);
      } catch (error) {
        console.error(error);
        return null;
      }
      if (stored) {
        let result = stored;
        if (stored.length > 9 && stored.substring(0, 9) === '__JSON__:') {
          const jsonString = stored.slice(9);
          try {
            result = JSON.parse(jsonString);
          } catch (error) {
            result = stored;
          }
        } else if (stored[0] === "{" || stored[0] === "[") {
          // for backward compatibility
          try {
            result = JSON.parse(stored);
          } catch (error) {
            result = stored;
          }
        }
        return result;
      }
      return null;
    }

    /**
     * Set (store) a value.
     *
     * @param {string} type A type (category).
     * @param {string} name A name.
     * @param {*} value A value.
     */
    set(type, name, value) {
      this.checkType(type);
      if (value === null) {
        this.clear(type, name);
        return;
      }
      const key = this.composeKey(type, name);
      if (value instanceof Object || Array.isArray(value) || value === true || value === false || typeof value === 'number') {
        value = '__JSON__:' + JSON.stringify(value);
      }
      try {
        this.storageObject.setItem(key, value);
      } catch (error) {
        console.error(error);
        return null;
      }
    }

    /**
     * Clear a value.
     *
     * @param {string} type A type (category).
     * @param {string} name A name.
     */
    clear(type, name) {
      let reText;
      if (typeof type !== 'undefined') {
        if (typeof name === 'undefined') {
          reText = '^' + this.composeFullPrefix(type);
        } else {
          reText = '^' + this.composeKey(type, name);
        }
      } else {
        reText = '^' + this.prefix + '-';
      }
      const re = new RegExp(reText);
      for (const i in this.storageObject) {
        if (re.test(i)) {
          delete this.storageObject[i];
        }
      }
    }
  }
  var _default = _exports.default = Storage;
});

define("router", ["exports", "backbone"], function (_exports, _backbone) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _backbone = _interopRequireDefault(_backbone);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 router */

  /**
   * On route.
   *
   * @event Backbone.Router#route
   * @param {string} name A route name.
   * @param {any[]} args Arguments.
   */

  /**
   * After dispatch.
   *
   * @event module:router#routed
   * @param {{
   *   controller: string,
   *   action:string,
   *   options: Object.<string,*>,
   * }} data A route data.
   */

  /**
   * Subscribe.
   *
   * @function on
   * @memberof module:router#
   * @param {string} event An event.
   * @param {function(*): void} callback A callback.
   */

  /**
   * Subscribe once.
   *
   * @function once
   * @memberof module:router#
   * @param {string} event An event.
   * @param {function(): void} callback A callback.
   */

  /**
   * Unsubscribe.
   *
   * @function off
   * @memberof module:router#
   * @param {string} event An event.
   */

  /**
   * Trigger an event.
   *
   * @function trigger
   * @memberof module:router#
   * @param {string} event An event.
   */

  // noinspection JSUnusedGlobalSymbols
  /**
   * A router.
   *
   * @class
   * @mixes Espo.Events
   */
  const Router = _backbone.default.Router.extend(/** @lends Router# */{
    /**
     * @private
     */
    routeList: [{
      route: "clearCache",
      resolution: "clearCache"
    }, {
      route: ":controller/view/:id/:options",
      resolution: "view"
    }, {
      route: ":controller/view/:id",
      resolution: "view"
    }, {
      route: ":controller/edit/:id/:options",
      resolution: "edit"
    }, {
      route: ":controller/edit/:id",
      resolution: "edit"
    }, {
      route: ":controller/create",
      resolution: "create"
    }, {
      route: ":controller/related/:id/:link",
      resolution: "related"
    }, {
      route: ":controller/:action/:options",
      resolution: "action",
      order: 100
    }, {
      route: ":controller/:action",
      resolution: "action",
      order: 200
    }, {
      route: ":controller",
      resolution: "defaultAction",
      order: 300
    }, {
      route: "*actions",
      resolution: "home",
      order: 500
    }],
    /**
     * @private
     */
    _bindRoutes: function () {},
    /**
     * @private
     */
    setupRoutes: function () {
      this.routeParams = {};
      if (this.options.routes) {
        const routeList = [];
        Object.keys(this.options.routes).forEach(route => {
          const item = this.options.routes[route];
          routeList.push({
            route: route,
            resolution: item.resolution || 'defaultRoute',
            order: item.order || 0
          });
          this.routeParams[route] = item.params || {};
        });
        this.routeList = Espo.Utils.clone(this.routeList);
        routeList.forEach(item => {
          this.routeList.push(item);
        });
        this.routeList = this.routeList.sort((v1, v2) => {
          return (v1.order || 0) - (v2.order || 0);
        });
      }
      this.routeList.reverse().forEach(item => {
        this.route(item.route, item.resolution);
      });
    },
    /**
     * @private
     */
    _last: null,
    /**
     * Whether a confirm-leave-out was set.
     *
     * @public
     * @type {boolean}
     */
    confirmLeaveOut: false,
    /**
     * Whether back has been processed.
     *
     * @public
     * @type {boolean}
     */
    backProcessed: false,
    /**
     * @type {string}
     * @internal
     */
    confirmLeaveOutMessage: 'Are you sure?',
    /**
     * @type {string}
     * @internal
     */
    confirmLeaveOutConfirmText: 'Yes',
    /**
     * @type {string}
     * @internal
     */
    confirmLeaveOutCancelText: 'No',
    /**
     * @private
     */
    initialize: function (options) {
      this.options = options || {};
      this.setupRoutes();
      this._isReturn = false;
      this.history = [];
      let hashHistory = [window.location.hash];
      window.addEventListener('hashchange', () => {
        const hash = window.location.hash;
        if (hashHistory.length > 1 && hashHistory[hashHistory.length - 2] === hash) {
          hashHistory = hashHistory.slice(0, -1);
          this.backProcessed = true;
          setTimeout(() => this.backProcessed = false, 50);
          return;
        }
        hashHistory.push(hash);
      });
      this.on('route', () => {
        this.history.push(_backbone.default.history.fragment);
      });
      window.addEventListener('beforeunload', event => {
        event = event || window.event;
        if (this.confirmLeaveOut || this._leaveOutMap.size || this._windowLeaveOutMap.size) {
          event.preventDefault();
          event.returnValue = this.confirmLeaveOutMessage;
          return this.confirmLeaveOutMessage;
        }
      });

      /**
       * @private
       * @type {Map<Object, true>}
       */
      this._leaveOutMap = new Map();

      /**
       * @private
       * @type {Map<Object, true>}
       */
      this._windowLeaveOutMap = new Map();
    },
    /**
     * Get a current URL.
     *
     * @returns {string}
     */
    getCurrentUrl: function () {
      return '#' + _backbone.default.history.fragment;
    },
    /**
     * Whether there's any confirm-leave-out.
     *
     * @since 9.1.0
     * @return {boolean}
     */
    hasConfirmLeaveOut() {
      return this.confirmLeaveOut || this._leaveOutMap.size || this._windowLeaveOutMap.size;
    },
    /**
     * Refer an object (usually a view). Page won't be possible to close or change if there's at least one object.
     *
     * @param {Object} object
     * @since 9.1.0
     * @internal
     */
    addLeaveOutObject(object) {
      this._leaveOutMap.set(object, true);
    },
    /**
     * Un-refer an object.
     *
     * @param {Object} object
     * @since 9.1.0
     * @internal
     */
    removeLeaveOutObject(object) {
      this._leaveOutMap.delete(object);
    },
    /**
     * Refer an object (usually a view). Window won't be possible to close if there's at least one object.
     *
     * @param {Object} object
     * @since 9.1.0
     * @internal
     */
    addWindowLeaveOutObject(object) {
      this._windowLeaveOutMap.set(object, true);
    },
    /**
     * Un-refer an object.
     *
     * @param {Object} object
     * @since 9.1.0
     * @internal
     */
    removeWindowLeaveOutObject(object) {
      this._windowLeaveOutMap.delete(object);
    },
    /**
     * @callback module:router~checkConfirmLeaveOutCallback
     */

    /**
     * Process confirm-leave-out.
     *
     * @param {module:router~checkConfirmLeaveOutCallback} callback Proceed if confirmed.
     * @param {Object|null} [context] A context.
     * @param {boolean} [navigateBack] To navigate back if not confirmed.
     */
    checkConfirmLeaveOut: function (callback, context, navigateBack) {
      if (this.confirmLeaveOutDisplayed) {
        this.navigateBack({
          trigger: false
        });
        this.confirmLeaveOutCanceled = true;
        return;
      }
      context = context || this;
      if (this.confirmLeaveOut || this._leaveOutMap.size) {
        this.confirmLeaveOutDisplayed = true;
        this.confirmLeaveOutCanceled = false;
        Espo.Ui.confirm(this.confirmLeaveOutMessage, {
          confirmText: this.confirmLeaveOutConfirmText,
          cancelText: this.confirmLeaveOutCancelText,
          backdrop: true,
          cancelCallback: () => {
            this.confirmLeaveOutDisplayed = false;
            if (navigateBack) {
              this.navigateBack({
                trigger: false
              });
            }
          }
        }, () => {
          this.confirmLeaveOutDisplayed = false;
          this.confirmLeaveOut = false;
          this._leaveOutMap.clear();
          if (!this.confirmLeaveOutCanceled) {
            callback.call(context);
          }
        });
        return;
      }
      callback.call(context);
    },
    /**
     * @private
     */
    route: function (route, name /*, callback*/) {
      const routeOriginal = route;
      if (!_.isRegExp(route)) {
        route = this._routeToRegExp(route);
      }
      let callback;

      // @todo Revise.
      /*if (_.isFunction(name)) {
          callback = name;
          name = '';
      }*/

      /*if (!callback) {
          callback = this['_' + name];
      }*/
      callback = this['_' + name];
      const router = this;
      _backbone.default.history.route(route, function (fragment) {
        const args = router._extractParameters(route, fragment);
        const options = {};
        if (name === 'defaultRoute') {
          const keyList = [];
          routeOriginal.split('/').forEach(key => {
            if (key && key.indexOf(':') === 0) {
              keyList.push(key.substr(1));
            }
          });
          keyList.forEach((key, i) => {
            options[key] = args[i];
          });
        }

        // @todo Revise.
        router.execute(callback, args, name, routeOriginal, options);
        //if (router.execute(callback, args, name, routeOriginal, options) !== false) {
        router.trigger.apply(router, ['route:' + name].concat(args));
        router.trigger('route', name, args);
        _backbone.default.history.trigger('route', router, name, args);
        //}
      });
      return this;
    },
    /**
     * @private
     */
    execute: function (callback, args, name, routeOriginal, options) {
      this.checkConfirmLeaveOut(() => {
        if (name === 'defaultRoute') {
          this._defaultRoute(this.routeParams[routeOriginal], options);
          return;
        }
        _backbone.default.Router.prototype.execute.call(this, callback, args, name);
      }, null, true);
    },
    /**
     * Navigate.
     *
     * @param {string} fragment An URL fragment.
     * @param {{
     *     trigger?: boolean,
     *     replace?: boolean,
     *     isReturn?: boolean,
     * }} [options] Options.
     */
    navigate: function (fragment) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      if (!options.trigger) {
        this.history.push(fragment);
      }
      if (options.isReturn) {
        this._isReturn = true;
      }
      return _backbone.default.Router.prototype.navigate.call(this, fragment, options);
    },
    /**
     * Navigate back.
     *
     * @param {Object} [options] Options: trigger, replace.
     */
    navigateBack: function (options) {
      let url;
      url = this.history.length > 1 ? this.history[this.history.length - 2] : this.history[0];
      this.navigate(url, options);
    },
    /**
     * @private
     */
    _parseOptionsParams: function (string) {
      if (!string) {
        return {};
      }
      if (string.indexOf('&') === -1 && string.indexOf('=') === -1) {
        return string;
      }
      const options = {};
      if (typeof string !== 'undefined') {
        string.split('&').forEach(item => {
          const p = item.split('=');
          options[p[0]] = true;
          if (p.length > 1) {
            options[p[0]] = p[1];
          }
        });
      }
      return options;
    },
    /**
     * @private
     */
    _defaultRoute: function (params, options) {
      const controller = params.controller || options.controller;
      const action = params.action || options.action;
      this.dispatch(controller, action, options);
    },
    /**
     * @private
     */
    _record: function (controller, action, id, options) {
      options = this._parseOptionsParams(options);
      options.id = id;
      this.dispatch(controller, action, options);
    },
    /**
     * @private
     */
    _view: function (controller, id, options) {
      this._record(controller, 'view', id, options);
    },
    /**
     * @private
     */
    _edit: function (controller, id, options) {
      this._record(controller, 'edit', id, options);
    },
    /**
     * @private
     */
    _related: function (controller, id, link, options) {
      options = this._parseOptionsParams(options);
      options.id = id;
      options.link = link;
      this.dispatch(controller, 'related', options);
    },
    /**
     * @private
     */
    _create: function (controller, options) {
      this._record(controller, 'create', null, options);
    },
    /**
     * @private
     */
    _action: function (controller, action, options) {
      this.dispatch(controller, action, this._parseOptionsParams(options));
    },
    /**
     * @private
     */
    _defaultAction: function (controller) {
      this.dispatch(controller, null);
    },
    /**
     * @private
     */
    _home: function () {
      this.dispatch('Home', null);
    },
    /**
     * @private
     */
    _clearCache: function () {
      this.dispatch(null, 'clearCache');
    },
    /**
     * Process `logout` route.
     */
    logout: function () {
      this.dispatch(null, 'logout');
      this.navigate('', {
        trigger: false
      });
    },
    /**
     * Dispatch a controller action.
     *
     * @param {string|null} [controller] A controller.
     * @param {string|null} [action] An action.
     * @param {Object} [options] Options.
     * @fires module:router#routed
     */
    dispatch: function (controller, action, options) {
      if (this._isReturn) {
        options = {
          ...options
        };
        options.isReturn = true;
        this._isReturn = false;
      }
      const o = {
        controller: controller,
        action: action,
        options: options
      };
      if (controller && /[a-z]/.test(controller[0])) {
        o.controllerClassName = controller;
        delete o.controller;
      }
      this._last = o;
      this.trigger('routed', o);
    },
    /**
     * Get the last route data.
     *
     * @returns {Object}
     */
    getLast: function () {
      return this._last;
    }
  });
  var _default = _exports.default = Router;
  function isIOS9UIWebView() {
    const userAgent = window.navigator.userAgent;
    return /(iPhone|iPad|iPod).* OS 9_\d/.test(userAgent) && !/Version\/9\./.test(userAgent);
  }

  // Fixes issue that navigate with {trigger: false} fired
  // route change if there's a whitespace character.
  _backbone.default.history.getHash = function (window) {
    const match = (window || this).location.href.match(/#(.*)$/);
    return match ? this.decodeFragment(match[1]) : '';
  };

  // Override `backbone.history.loadUrl()` and `backbone.history.navigate()`
  // to fix the navigation issue (`location.hash` not changed immediately) on iOS9.
  if (isIOS9UIWebView()) {
    _backbone.default.history.loadUrl = function (fragment, oldHash) {
      fragment = this.fragment = this.getFragment(fragment);
      return _.any(this.handlers, function (handler) {
        if (handler.route.test(fragment)) {
          function runCallback() {
            handler.callback(fragment);
          }
          function wait() {
            if (oldHash === location.hash) {
              window.setTimeout(wait, 50);
            } else {
              runCallback();
            }
          }
          wait();
          return true;
        }
      });
    };
    _backbone.default.history.navigate = function (fragment, options) {
      const pathStripper = /#.*$/;
      if (!_backbone.default.History.started) {
        return false;
      }
      if (!options || options === true) {
        options = {
          trigger: !!options
        };
      }
      let url = this.root + '#' + (fragment = this.getFragment(fragment || ''));
      fragment = fragment.replace(pathStripper, '');
      if (this.fragment === fragment) {
        return;
      }
      this.fragment = fragment;
      if (fragment === '' && url !== '/') {
        url = url.slice(0, -1);
      }
      const oldHash = location.hash;
      if (this._hasPushState) {
        this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
      } else if (this._wantsHashChange) {
        this._updateHash(this.location, fragment, options.replace);
        if (this.iframe && fragment !== this.getFragment(this.getHash(this.iframe))) {
          if (!options.replace) {
            this.iframe.document.open().close();
          }
          this._updateHash(this.iframe.location, fragment, options.replace);
        }
      } else {
        return this.location.assign(url);
      }
      if (options.trigger) {
        return this.loadUrl(fragment, oldHash);
      }
    };
  }
});

define("model", ["exports", "bullbone", "underscore"], function (_exports, _bullbone, _underscore) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _underscore = _interopRequireDefault(_underscore);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 model */

  /**
   * When attributes have changed.
   *
   * @event Model#change
   * @param {Model} model A model.
   * @param {Record.<string, *> & {action?: string|'ui'|'save'|'fetch'|'cancel-edit'}} o Options.
   */

  /**
   * On sync with backend.
   *
   * @event Model#sync
   * @param {Model} model A model.
   * @param {Object} response Response from backend.
   * @param {Record.<string, *> & {action?: 'fetch'|'save'|'destroy'}} o Options.
   */

  /**
   * Definitions.
   *
   * @typedef module:model~defs
   * @type {Object}
   * @property {Object.<string, module:model~fieldDefs>} [fields] Fields.
   * @property {Object.<string, Object.<string, *>>} [links] Links.
   */

  /**
   * Field definitions.
   *
   * @typedef module:model~fieldDefs
   * @type {Object & Record}
   * @property {string} type A type.
   */

  /** @typedef {import('bullbone')} Bull */

  /**
   * A model.
   *
   * @mixes Bull.Events
   */
  class Model {
    /**
     * A root URL. An ID will be appended. Used for syncing with backend.
     *
     * @type {string|null}
     */
    urlRoot = null;

    /**
     * A URL. If not empty, then will be used for syncing instead of `urlRoot`.
     *
     * @type {string|null}
     */
    url = null;

    /**
     * A name.
     *
     * @type {string|null}
     */
    name = null;

    /**
     * An entity type.
     *
     * @type {string|null}
     */
    entityType = null;

    /**
     * A last request promise.
     *
     * @type {module:ajax.AjaxPromise|null}
     */
    lastSyncPromise = null;

    /** @private */
    _pending;
    /** @private */
    _changing;

    /**
     * @param {Object.<string, *>|Model} [attributes]
     * @param {{
     *     collection?: module:collection,
     *     entityType?: string,
     *     urlRoot?: string,
     *     url?: string,
     *     defs?: module:model~defs,
     *     user?: module:models/user,
     *     dateTime?: module:date-time,
     * }} [options]
     */
    constructor(attributes, options) {
      options = options || {};

      /**
       * An ID attribute.
       * @type {string}
       */
      this.idAttribute = 'id';

      /**
       * A record ID.
       * @type {string|null}
       */
      this.id = null;

      /**
       * An instance ID.
       * @type {string}
       */
      this.cid = _underscore.default.uniqueId('c');

      /**
       * Attribute values.
       * @type {Object.<string, *>}
       */
      this.attributes = {};
      if (options.collection) {
        this.collection = options.collection;
      }
      this.set(attributes || {});

      /**
       * Definitions.
       */
      this.defs = options.defs || {};
      if (!this.defs.fields) {
        this.defs.fields = {};
      }
      if (options.entityType) {
        this.entityType = options.entityType;
        this.name = options.entityType;
        this.urlRoot = options.entityType;
      }
      this.urlRoot = options.urlRoot || this.urlRoot;
      this.url = options.url || this.url;

      /** @private */
      this.dateTime = options.dateTime || null;

      /** @private */
      this.changed = {};
      /** @private */
      this._previousAttributes = null;
    }

    /**
     * @protected
     * @param {string} [method] HTTP method.
     * @param {Model} model
     * @param {Object.<string, *>} [options]
     * @returns {module:ajax.AjaxPromise|Promise}
     */
    sync(method, model, options) {
      const methodMap = {
        'create': 'POST',
        'update': 'PUT',
        'patch': 'PUT',
        'delete': 'DELETE',
        'read': 'GET'
      };
      const httpMethod = methodMap[method];
      if (!httpMethod) {
        throw new Error(`Bad request method '${method}'.`);
      }
      options = options || {};
      const url = this.composeSyncUrl();
      if (!url) {
        throw new Error(`No 'url'.`);
      }
      const data = model && ['create', 'update', 'patch'].includes(method) ? options.attributes || model.getClonedAttributes() : null;
      const error = options.error;
      options.error = (xhr, textStatus, errorThrown) => {
        options.textStatus = textStatus;
        options.errorThrown = errorThrown;
        if (error) {
          error.call(options.context, xhr, textStatus, errorThrown);
        }
      };
      const stringData = data ? JSON.stringify(data) : null;
      const ajaxPromise = !options.bypassRequest ? Espo.Ajax.request(url, httpMethod, stringData, options) : Promise.resolve();
      options.xhr = ajaxPromise.xhr;
      model.trigger('request', url, httpMethod, data, ajaxPromise, options);
      return ajaxPromise;
    }

    /**
     * Set an attribute value.
     *
     * @param {(string|Object)} attribute An attribute name or a {key => value} object.
     * @param {*} [value] A value or options if the first argument is an object.
     * @param {{silent?: boolean} & Object.<string, *>} [options] Options. `silent` won't trigger a `change` event.
     * @returns {this}
     * @fires Model#change Unless `{silent: true}`.
     */
    set(attribute, value, options) {
      if (attribute == null) {
        return this;
      }
      let attributes;
      if (typeof attribute === 'object') {
        return this.setMultiple(attribute, value);
      }
      attributes = {};
      attributes[attribute] = value;
      return this.setMultiple(attributes, options);
    }

    /**
     * Set attributes values.
     *
     * @param {Object.<string, *>} attributes
     * @param {{
     *     silent?: boolean,
     *     unset?: boolean,
     *     sync?: boolean,
     * } & Object.<string, *>} [options] Options. `silent` won't trigger a `change` event.
     *     `sync` can be used to emulate syncing.
     * @return {this}
     * @fires Model#change Unless `{silent: true}`.
     * @copyright Credits to Backbone.js.
     */
    setMultiple(attributes, options) {
      if (this.idAttribute in attributes) {
        this.id = attributes[this.idAttribute];
      }
      options = options || {};
      if (options.ui && !options.action) {
        options.action = 'ui';
      }
      if (!options.ui && options.action === 'ui') {
        options.ui = true;
      }
      const changes = [];
      const changing = this._changing;
      this._changing = true;
      if (!changing) {
        this._previousAttributes = _underscore.default.clone(this.attributes);
        this.changed = {};
      }
      const current = this.attributes;
      const changed = this.changed;
      const previous = this._previousAttributes;
      for (const attribute in attributes) {
        const value = attributes[attribute];
        if (!_underscore.default.isEqual(current[attribute], value)) {
          changes.push(attribute);
        }
        if (!_underscore.default.isEqual(previous[attribute], value)) {
          changed[attribute] = value;
        } else {
          delete changed[attribute];
        }
        options.unset ? delete current[attribute] : current[attribute] = value;
      }
      if (!options.silent) {
        if (changes.length) {
          this._pending = options;
        }
        for (let i = 0; i < changes.length; i++) {
          this.trigger('change:' + changes[i], this, current[changes[i]], options);
        }
      }
      if (options.sync) {
        if (this.collection) {
          const modelSyncOptions = {
            ...options,
            action: 'set'
          };
          this.collection.trigger('model-sync', this, modelSyncOptions);
        }
      }
      if (changing) {
        return this;
      }
      if (!options.silent) {
        // Changes can be recursively nested within `change` events.
        while (this._pending) {
          options = this._pending;
          this._pending = false;
          this.trigger('change', this, options);
        }
      }
      this._pending = false;
      this._changing = false;
      return this;
    }

    /**
     * Unset an attribute.
     *
     * @param {string} attribute An attribute.
     * @param {{silent?: boolean} & Object.<string, *>} [options] Options.
     * @return {Model}
     */
    unset(attribute, options) {
      options = {
        ...options,
        unset: true
      };
      const attributes = {};
      attributes[attribute] = null;
      return this.setMultiple(attributes, options);
    }

    /**
     * Get an attribute value.
     *
     * @param {string} attribute An attribute name.
     * @returns {*}
     */
    get(attribute) {
      if (attribute === this.idAttribute && this.id) {
        return this.id;
      }
      return this.attributes[attribute];
    }

    /**
     * Whether attribute is set.
     *
     * @param {string} attribute An attribute name.
     * @returns {boolean}
     */
    has(attribute) {
      const value = this.get(attribute);
      return typeof value !== 'undefined';
    }

    /**
     * Removes all attributes from the model.
     * Fires a `change` event unless `silent` is passed as an option.
     *
     * @param {{silent?: boolean} & Object.<string, *>} [options] Options.
     */
    clear(options) {
      const attributes = {};
      for (const key in this.attributes) {
        attributes[key] = void 0;
      }
      options = {
        ...options,
        unset: true
      };
      return this.set(attributes, options);
    }

    /**
     * Whether is new.
     *
     * @returns {boolean}
     */
    isNew() {
      return !this.id;
    }

    /**
     * Whether an attribute changed. To be called only within a 'change' event handler.
     *
     * @param {string} [attribute]
     * @return {boolean}
     */
    hasChanged(attribute) {
      if (!attribute) {
        return !_underscore.default.isEmpty(this.changed);
      }
      return _underscore.default.has(this.changed, attribute);
    }

    /**
     * Get changed attribute values. To be called only within a 'change' event handler.
     *
     * @return {Object.<string, *>}
     */
    changedAttributes() {
      return this.hasChanged() ? _underscore.default.clone(this.changed) : {};
    }

    /**
     * Get previous attributes. To be called only within a 'change' event handler.
     *
     * @return {Object.<string, *>}
     */
    previousAttributes() {
      return _underscore.default.clone(this._previousAttributes);
    }

    /**
     * Get a previous attribute value. To be called only within a 'change' event handler.
     *
     * @param attribute
     * @return {*}
     */
    previous(attribute) {
      if (!this._previousAttributes) {
        return null;
      }
      return this._previousAttributes[attribute];
    }

    /**
     * Fetch values from the backend.
     *
     * @param {Object.<string, *>} [options] Options.
     * @returns {Promise}
     * @fires Model#sync
     */
    fetch(options) {
      options = {
        ...options
      };
      options.action = 'fetch';

      // For bc.
      const success = options.success;
      options.success = response => {
        const serverAttributes = this.prepareAttributes(response, options);
        this.set(serverAttributes, options);
        if (success) {
          success.call(options.context, this, response, options);
        }
        this.trigger('sync', this, response, options);
        if (this.collection) {
          this.collection.trigger('model-sync', this, options);
        }
      };
      this.lastSyncPromise = this.sync('read', this, options);
      return this.lastSyncPromise;
    }

    /**
     * Save values to the backend.
     *
     * @param {Object.<string, *>} [attributes] Attribute values.
     * @param {{
     *     patch?: boolean,
     *     wait?: boolean,
     * } & Object.<string, *>} [options] Options. Use `patch` to send a PATCH request. If `wait`, attributes will be
     *     set only after the request is completed.
     * @returns {Promise<Object.<string, *>> & module:ajax.AjaxPromise}
     * @fires Model#sync
     * @copyright Credits to Backbone.js.
     */
    save(attributes, options) {
      options = {
        ...options
      };
      if (attributes && !options.wait) {
        this.setMultiple(attributes, options);
      }
      const success = options.success;
      const setAttributes = this.attributes;
      options.success = response => {
        this.attributes = setAttributes;
        let responseAttributes = this.prepareAttributes(response, options);
        if (options.wait) {
          responseAttributes = {
            ...setAttributes,
            ...responseAttributes
          };
        }
        options.action = 'save';
        if (responseAttributes) {
          this.setMultiple(responseAttributes, options);
        }
        if (success) {
          success.call(options.context, this, response, options);
        }
        this.trigger('sync', this, response, options);
        if (this.collection) {
          this.collection.trigger('model-sync', this, options);
        }
      };
      const error = options.error;
      options.error = response => {
        if (error) {
          error.call(options.context, this, response, options);
        }
        this.trigger('error', this, response, options);
      };
      if (attributes && options.wait) {
        // Set temporary attributes to properly find new IDs.
        this.attributes = {
          ...setAttributes,
          ...attributes
        };
      }
      const method = this.isNew() ? 'create' : options.patch ? 'patch' : 'update';
      if (method === 'patch') {
        options.attributes = attributes;
      }
      const result = this.sync(method, this, options);
      this.attributes = setAttributes;
      return result;
    }

    /**
     * Delete the record in the backend.
     *
     * @param {{wait?: boolean} & Object.<string, *>} [options] Options. If `wait`, unsubscribing and
     *     removal from the collection will wait for a successful response.
     * @returns {Promise}
     * @fires Model#sync
     * @copyright Credits to Backbone.js.
     */
    destroy() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      options = {
        ...options
      };
      const success = options.success;
      const collection = this.collection;
      const destroy = () => {
        this.stopListening();
        this.trigger('destroy', this, collection, options);
      };
      options.success = response => {
        if (options.wait) {
          destroy();
        }
        if (success) {
          success.call(options.context, this, response, options);
        }
        if (!this.isNew()) {
          const syncOptions = {
            ...options
          };
          syncOptions.action = 'destroy';
          this.trigger('sync', this, response, syncOptions);
          if (collection) {
            collection.trigger('model-sync', this, syncOptions);
          }
        }
      };
      if (this.isNew()) {
        _underscore.default.defer(options.success);
        if (!options.wait) {
          destroy();
        }
        return Promise.resolve();
      }
      const error = options.error;
      options.error = response => {
        if (error) {
          error.call(options.context, this, response, options);
        }
        this.trigger('error', this, response, options);
      };
      const result = this.sync('delete', this, options);
      if (!options.wait) {
        destroy();
      }
      return result;
    }

    /**
     * Compose a URL for syncing.
     *
     * @protected
     * @return {string}
     */
    composeSyncUrl() {
      if (this.url) {
        return this.url;
      }
      let urlRoot = this.urlRoot;
      if (!urlRoot && this.collection) {
        urlRoot = this.collection.urlRoot;
      }
      if (!urlRoot) {
        throw new Error("No urlRoot.");
      }
      if (this.isNew()) {
        return urlRoot;
      }
      const id = this.get(this.idAttribute);
      return urlRoot.replace(/[^\/]$/, '$&/') + encodeURIComponent(id);
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * Prepare attributes.
     *
     * @param {*} response A response from the backend.
     * @param {Object.<string, *>} options Options.
     * @return {*} Attributes.
     * @internal
     */
    prepareAttributes(response, options) {
      return response;
    }

    /**
     * Clone.
     *
     * @return {Model}
     */
    clone() {
      return new this.constructor(Espo.Utils.cloneDeep(this.attributes), {
        entityType: this.entityType,
        urlRoot: this.urlRoot,
        url: this.url,
        defs: this.defs,
        dateTime: this.dateTime
      });
    }

    /**
     * Set defs.
     *
     * @param {module:model~defs} defs
     */
    setDefs(defs) {
      this.defs = defs || {};
      if (!this.defs.fields) {
        this.defs.fields = {};
      }
    }

    /**
     * Get cloned attribute values.
     *
     * @returns {Object.<string, *>}
     */
    getClonedAttributes() {
      return Espo.Utils.cloneDeep(this.attributes);
    }

    /**
     * Populate default values.
     */
    populateDefaults() {
      let defaultHash = {};
      const fieldDefs = this.defs.fields;
      for (const field in fieldDefs) {
        if (this.hasFieldParam(field, 'default')) {
          try {
            defaultHash[field] = this.parseDefaultValue(this.getFieldParam(field, 'default'));
          } catch (e) {
            console.error(e);
          }
        }
        const defaultAttributes = this.getFieldParam(field, 'defaultAttributes');
        if (defaultAttributes) {
          for (const attribute in defaultAttributes) {
            defaultHash[attribute] = defaultAttributes[attribute];
          }
        }
      }
      defaultHash = Espo.Utils.cloneDeep(defaultHash);
      for (const attr in defaultHash) {
        if (this.has(attr)) {
          delete defaultHash[attr];
        }
      }
      this.set(defaultHash, {
        silent: true
      });
    }

    /**
     * @protected
     * @param {*} defaultValue
     * @returns {*}
     */
    parseDefaultValue(defaultValue) {
      if (typeof defaultValue === 'string' && defaultValue.indexOf('javascript:') === 0) {
        const code = defaultValue.substring(11);
        defaultValue = new Function("with(this) { " + code + "}").call(this);
      }
      return defaultValue;
    }

    /**
     * Get a link multiple column value.
     *
     * @param {string} field
     * @param {string} column
     * @param {string} id
     * @returns {*}
     */
    getLinkMultipleColumn(field, column, id) {
      return ((this.get(field + 'Columns') || {})[id] || {})[column];
    }

    /**
     * @typedef {Object} model:model~setRelateItem
     * @property {string} link A link.
     * @property {import('model').default} model A model.
     */

    /**
     * Set relate data (when creating a related record).
     *
     * @param {model:model~setRelateItem | model:model~setRelateItem[]} data
     */
    setRelate(data) {
      const setRelate = options => {
        const link = options.link;
        const model = /** @type {module:model} */options.model;
        if (!link || !model) {
          throw new Error('Bad related options');
        }
        const type = this.defs.links[link].type;
        switch (type) {
          case 'belongsToParent':
            this.set(link + 'Id', model.id);
            this.set(link + 'Type', model.entityType);
            this.set(link + 'Name', model.get('name'));
            break;
          case 'belongsTo':
            this.set(link + 'Id', model.id);
            this.set(link + 'Name', model.get('name'));
            break;
          case 'hasMany':
            const ids = [];
            ids.push(model.id);
            const names = {};
            names[model.id] = model.get('name');
            this.set(link + 'Ids', ids);
            this.set(link + 'Names', names);
            break;
        }
      };
      if (Object.prototype.toString.call(data) === '[object Array]') {
        data.forEach(options => {
          setRelate(options);
        });
        return;
      }
      setRelate(data);
    }

    /**
     * Get a field list.
     *
     * @return {string[]}
     */
    getFieldList() {
      if (!this.defs || !this.defs.fields) {
        return [];
      }
      return Object.keys(this.defs.fields);
    }

    /**
     * Get a field type.
     *
     * @param {string} field
     * @returns {string|null}
     */
    getFieldType(field) {
      if (!this.defs || !this.defs.fields) {
        return null;
      }
      if (field in this.defs.fields) {
        return this.defs.fields[field].type || null;
      }
      return null;
    }

    /**
     * Get a field param.
     *
     * @param {string} field
     * @param {string} param
     * @returns {*}
     */
    getFieldParam(field, param) {
      if (!this.defs || !this.defs.fields) {
        return null;
      }
      if (field in this.defs.fields) {
        if (param in this.defs.fields[field]) {
          return this.defs.fields[field][param];
        }
      }
      return null;
    }
    hasFieldParam(field, param) {
      if (!this.defs || !this.defs.fields) {
        return false;
      }
      if (field in this.defs.fields) {
        if (param in this.defs.fields[field]) {
          return true;
        }
      }
      return false;
    }

    /**
     * Get a link type.
     *
     * @param {string} link
     * @returns {string|null}
     */
    getLinkType(link) {
      if (!this.defs || !this.defs.links) {
        return null;
      }
      if (link in this.defs.links) {
        return this.defs.links[link].type || null;
      }
      return null;
    }

    /**
     * Get a link param.
     *
     * @param {string} link A link.
     * @param {string} param A param.
     * @returns {*}
     */
    getLinkParam(link, param) {
      if (!this.defs || !this.defs.links) {
        return null;
      }
      if (link in this.defs.links) {
        if (param in this.defs.links[link]) {
          return this.defs.links[link][param];
        }
      }
      return null;
    }

    /**
     * Is a field read-only.
     *
     * @param {string} field A field.
     * @returns {bool}
     */
    isFieldReadOnly(field) {
      return this.getFieldParam(field, 'readOnly') || false;
    }

    /**
     * If a field required.
     *
     * @param {string} field A field.
     * @returns {bool}
     */
    isRequired(field) {
      return this.getFieldParam(field, 'required') || false;
    }

    /**
     * Get IDs of a link-multiple field.
     *
     * @param {string} field A link-multiple field name.
     * @returns {string[]}
     */
    getLinkMultipleIdList(field) {
      return this.get(field + 'Ids') || [];
    }

    /**
     * Get team IDs.
     *
     * @returns {string[]}
     */
    getTeamIdList() {
      return this.get('teamsIds') || [];
    }

    /**
     * Whether it has a field.
     *
     * @param {string} field A field.
     * @returns {boolean}
     */
    hasField(field) {
      return 'defs' in this && 'fields' in this.defs && field in this.defs.fields;
    }

    /**
     * Has a link.
     *
     * @param {string} link A link.
     * @returns {boolean}
     */
    hasLink(link) {
      return 'defs' in this && 'links' in this.defs && link in this.defs.links;
    }

    /**
     * @returns {boolean}
     */
    isEditable() {
      return true;
    }

    /**
     * @returns {boolean}
     */
    isRemovable() {
      return true;
    }

    /**
     * Get an entity type.
     *
     * @returns {string}
     */
    getEntityType() {
      return this.name;
    }

    /**
     * Abort the last fetch.
     */
    abortLastFetch() {
      if (this.lastSyncPromise && this.lastSyncPromise.getReadyState() < 4) {
        this.lastSyncPromise.abort();
      }
    }
  }
  Object.assign(Model.prototype, _bullbone.Events);
  Model.extend = _bullbone.View.extend;
  var _default = _exports.default = Model;
});

define("metadata", ["exports", "bullbone"], function (_exports, _bullbone) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 metadata */

  /**
   * Application metadata.
   *
   * @mixes Bull.Events
   */
  class Metadata {
    /**
     * Application metadata.
     *
     * @param {module:cache} [cache] A cache.
     */
    constructor(cache) {
      /**
       * @private
       * @type {module:cache|null}
       */
      this.cache = cache || null;

      /**
       * @private
       * @type {Object}
       */
      this.data = {};
    }

    /** @private */
    url = 'Metadata';

    /**
     * Load from cache or the backend (if not yet cached).
     *
     * @param {Function|null} [callback] Deprecated. Use a promise.
     * @param {boolean} [disableCache=false] Deprecated.
     * @returns {Promise}
     */
    load(callback, disableCache) {
      if (!disableCache) {
        if (this.loadFromCache()) {
          this.trigger('sync');
          if (callback) {
            callback();
          }
          return Promise.resolve();
        }
      }
      return this.fetch().then(() => {
        if (callback) {
          callback();
        }
      });
    }

    /**
     * Load from the server.
     *
     * @returns {Promise}
     */
    loadSkipCache() {
      return this.load(null, true);
    }

    /**
     * @private
     * @returns {Promise}
     */
    fetch() {
      return Espo.Ajax.getRequest(this.url).then(data => {
        this.data = data;
        this.storeToCache();
        this.trigger('sync');
      });
    }

    /**
     * Get a value.
     *
     * @param {string[]|string} path A key path.
     * @param {*} [defaultValue] A value to return if not set.
     * @returns {*} Null if not set.
     */
    get(path, defaultValue) {
      defaultValue = defaultValue || null;
      let arr;
      if (Array && Array.isArray && Array.isArray(path)) {
        arr = path;
      } else {
        arr = path.split('.');
      }
      let pointer = this.data;
      let result = defaultValue;
      for (let i = 0; i < arr.length; i++) {
        const key = arr[i];
        if (pointer == null || !(key in pointer)) {
          result = defaultValue;
          break;
        }
        if (arr.length - 1 === i) {
          result = pointer[key];
        }
        pointer = pointer[key];
      }
      return result;
    }

    /**
     * @private
     * @returns {boolean|null} True if success.
     */
    loadFromCache() {
      if (this.cache) {
        const cached = this.cache.get('app', 'metadata');
        if (cached) {
          this.data = cached;
          return true;
        }
      }
      return null;
    }

    /** @private */
    storeToCache() {
      if (this.cache) {
        this.cache.set('app', 'metadata', this.data);
      }
    }

    /**
     * Clear cache.
     */
    clearCache() {
      if (!this.cache) {
        return;
      }
      this.cache.clear('app', 'metadata');
    }

    /**
     * Get a scope list.
     *
     * @returns {string[]}
     */
    getScopeList() {
      const scopes = this.get('scopes') || {};
      const scopeList = [];
      for (const scope in scopes) {
        const d = scopes[scope];
        if (d.disabled) {
          continue;
        }
        scopeList.push(scope);
      }
      return scopeList;
    }

    /**
     * Get an object-scope list. An object-scope represents a business entity.
     *
     * @returns {string[]}
     */
    getScopeObjectList() {
      const scopes = this.get('scopes') || {};
      const scopeList = [];
      for (const scope in scopes) {
        const d = scopes[scope];
        if (d.disabled) {
          continue;
        }
        if (!d.object) {
          continue;
        }
        scopeList.push(scope);
      }
      return scopeList;
    }

    /**
     * Get an entity-scope list. Scopes that represents entities.
     *
     * @returns {string[]}
     */
    getScopeEntityList() {
      const scopes = this.get('scopes') || {};
      const scopeList = [];
      for (const scope in scopes) {
        const d = scopes[scope];
        if (d.disabled) {
          continue;
        }
        if (!d.entity) {
          continue;
        }
        scopeList.push(scope);
      }
      return scopeList;
    }

    /**
     * Do not use.
     *
     * @internal
     * @param {Record} data
     */
    setData(data) {
      this.data = data;
    }
  }
  Object.assign(Metadata.prototype, _bullbone.Events);
  var _default = _exports.default = Metadata;
});

define("language", ["exports", "bullbone"], function (_exports, _bullbone) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 language */

  /**
   * A language.
   *
   * @mixes Bull.Events
   */
  class Language {
    /** @private */
    url = 'I18n';

    /**
     * @class
     * @param {module:cache} [cache] A cache.
     */
    constructor(cache) {
      /**
       * @private
       * @type {module:cache|null}
       */
      this.cache = cache || null;

      /**
       * @private
       * @type {Object}
       */
      this.data = {};

      /**
       * A name.
       *
       * @type {string}
       */
      this.name = 'default';
    }

    /**
     * Whether an item is set in language data.
     *
     * @param {string} scope A scope.
     * @param {string} category A category.
     * @param {string} name An item name.
     * @returns {boolean}
     */
    has(name, category, scope) {
      if (scope in this.data) {
        if (category in this.data[scope]) {
          if (name in this.data[scope][category]) {
            return true;
          }
        }
      }
      return false;
    }

    /**
     * Get a value set in language data.
     *
     * @param {string} scope A scope.
     * @param {string} category A category.
     * @param {string} name An item name.
     * @returns {*}
     */
    get(scope, category, name) {
      if (scope in this.data) {
        if (category in this.data[scope]) {
          if (name in this.data[scope][category]) {
            return this.data[scope][category][name];
          }
        }
      }
      if (scope === 'Global') {
        return name;
      }
      return false;
    }

    /**
     * Translate a label.
     *
     * @param {string} name An item name.
     * @param {string|'messages'|'labels'|'fields'|'links'|'scopeNames'|'scopeNamesPlural'} [category='labels'] A category.
     * @param {string} [scope='Global'] A scope.
     * @returns {string}
     */
    translate(name, category, scope) {
      scope = scope || 'Global';
      category = category || 'labels';
      let res = this.get(scope, category, name);
      if (res === false && scope !== 'Global') {
        res = this.get('Global', category, name);
      }
      return res;
    }

    /**
     * Translation an option item value.
     *
     * @param {string} value An option value.
     * @param {string} field A field name.
     * @param {string} [scope='Global'] A scope.
     * @returns {string}
     */
    translateOption(value, field, scope) {
      let translation = this.translate(field, 'options', scope);
      if (typeof translation !== 'object') {
        translation = {};
      }
      return translation[value] || value;
    }

    /**
     * @private
     */
    loadFromCache(loadDefault) {
      let name = this.name;
      if (loadDefault) {
        name = 'default';
      }
      if (this.cache) {
        const cached = this.cache.get('app', 'language-' + name);
        if (cached) {
          this.data = cached;
          return true;
        }
      }
      return null;
    }

    /**
     * Clear a language cache.
     */
    clearCache() {
      if (this.cache) {
        this.cache.clear('app', 'language-' + this.name);
      }
    }

    /**
     * @private
     */
    storeToCache(loadDefault) {
      let name = this.name;
      if (loadDefault) {
        name = 'default';
      }
      if (this.cache) {
        this.cache.set('app', 'language-' + name, this.data);
      }
    }

    /**
     * Load data from cache or backend (if not yet cached).
     *
     * @returns {Promise}
     */
    load() {
      return this._loadInternal();
    }

    /**
     * @private
     * @param {boolean} [disableCache=false]
     * @param {boolean} [loadDefault=false].
     * @returns {Promise}
     */
    _loadInternal(disableCache, loadDefault) {
      if (!disableCache && this.loadFromCache(loadDefault)) {
        this.trigger('sync');
        return Promise.resolve();
      }
      return this.fetch(loadDefault);
    }

    /**
     * Load default-language data from the backend.
     *
     * @returns {Promise}
     */
    loadDefault() {
      return this._loadInternal(false, true);
    }

    /**
     * Load data from the backend.
     *
     * @returns {Promise}
     */
    loadSkipCache() {
      return this._loadInternal(true);
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Load default-language data from the backend.
     *
     * @returns {Promise}
     */
    loadDefaultSkipCache() {
      return this._loadInternal(true, true);
    }

    /**
     * @private
     * @param {boolean} loadDefault
     * @returns {Promise}
     */
    fetch(loadDefault) {
      return Espo.Ajax.getRequest(this.url, {
        default: loadDefault
      }).then(data => {
        this.data = data;
        this.storeToCache(loadDefault);
        this.trigger('sync');
      });
    }

    /**
     * Sort a field list by a translated name.
     *
     * @param {string} scope An entity type.
     * @param {string[]} fieldList A field list.
     * @returns {string[]}
     */
    sortFieldList(scope, fieldList) {
      return fieldList.sort((v1, v2) => {
        return this.translate(v1, 'fields', scope).localeCompare(this.translate(v2, 'fields', scope));
      });
    }

    /**
     * Sort an entity type list by a translated name.
     *
     * @param {string[]} entityList An entity type list.
     * @param {boolean} [plural=false] Use a plural label.
     * @returns {string[]}
     */
    sortEntityList(entityList, plural) {
      let category = 'scopeNames';
      if (plural) {
        category += 'Plural';
      }
      return entityList.sort((v1, v2) => {
        return this.translate(v1, category).localeCompare(this.translate(v2, category));
      });
    }

    /**
     * Get a value by a path.
     *
     * @param {string[]|string} path A path.
     * @returns {*}
     */
    translatePath(path) {
      if (typeof path === 'string' || path instanceof String) {
        path = path.split('.');
      }
      let pointer = this.data;
      path.forEach(key => {
        if (key in pointer) {
          pointer = pointer[key];
        }
      });
      return pointer;
    }

    /**
     * Do not use.
     *
     * @param {string} [scope]
     * @param {Record} [data]
     * @internal
     */
    setScopeData(scope, data) {
      this.data[scope] = data;
    }
  }
  Object.assign(Language.prototype, _bullbone.Events);
  var _default = _exports.default = Language;
});

define("date-time", ["exports", "moment"], function (_exports, _moment) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _moment = _interopRequireDefault(_moment);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 date-time */

  /**
   * A date-time util.
   */
  class DateTime {
    constructor() {}

    /**
     * A system date format.
     *
     * @type {string}
     */
    internalDateFormat = 'YYYY-MM-DD';

    /**
     * A system date-time format.
     *
     * @type {string}
     */
    internalDateTimeFormat = 'YYYY-MM-DD HH:mm';

    /**
     * A system date-time format including seconds.
     *
     * @type {string}
     */
    internalDateTimeFullFormat = 'YYYY-MM-DD HH:mm:ss';

    /**
     * A date format for a current user.
     *
     * @type {string}
     */
    dateFormat = 'MM/DD/YYYY';

    /**
     * A time format for a current user.
     *
     * @type {string}
     */
    timeFormat = 'HH:mm';

    /**
     * A time zone for a current user.
     *
     * @type {string|null}
     */
    timeZone = null;

    /**
     * A system time zone.
     *
     * @type {string}
     */
    systemTimeZone;

    /**
     * A week start for a current user.
     *
     * @type {Number}
     */
    weekStart = 1;

    /** @private */
    readableDateFormatMap = {
      'DD.MM.YYYY': 'DD MMM',
      'DD/MM/YYYY': 'DD MMM'
    };

    /** @private */
    readableShortDateFormatMap = {
      'DD.MM.YYYY': 'D MMM',
      'DD/MM/YYYY': 'D MMM'
    };

    /**
     * Whether a time format has a meridian (am/pm).
     *
     * @returns {boolean}
     */
    hasMeridian() {
      return new RegExp('A', 'i').test(this.timeFormat);
    }

    /**
     * Get a date format.
     *
     * @returns {string}
     */
    getDateFormat() {
      return this.dateFormat;
    }

    /**
     * Get a time format.
     *
     * @returns {string}
     */
    getTimeFormat() {
      return this.timeFormat;
    }

    /**
     * Get a date-time format.
     *
     * @returns {string}
     */
    getDateTimeFormat() {
      return this.dateFormat + ' ' + this.timeFormat;
    }

    /**
     * Get a readable date format.
     *
     * @returns {string}
     */
    getReadableDateFormat() {
      return this.readableDateFormatMap[this.getDateFormat()] || 'MMM DD';
    }

    /**
     * Get a readable short date format.
     *
     * @returns {string}
     */
    getReadableShortDateFormat() {
      return this.readableShortDateFormatMap[this.getDateFormat()] || 'MMM D';
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Get a readable date-time format.
     *
     * @returns {string}
     */
    getReadableDateTimeFormat() {
      return this.getReadableDateFormat() + ' ' + this.timeFormat;
    }

    /**
     * Get a readable short date-time format.
     *
     * @returns {string}
     */
    getReadableShortDateTimeFormat() {
      return this.getReadableShortDateFormat() + ' ' + this.timeFormat;
    }

    /**
     * Convert a date from a display representation to system.
     *
     * @param {string} string A date value.
     * @returns {string|-1} A system date value.
     */
    fromDisplayDate(string) {
      const m = (0, _moment.default)(string, this.dateFormat);
      if (!m.isValid()) {
        return -1;
      }
      return m.format(this.internalDateFormat);
    }

    /**
     * Get a time-zone.
     *
     * @returns {string}
     */
    getTimeZone() {
      return this.timeZone ? this.timeZone : 'UTC';
    }

    /**
     * Convert a date from system to a display representation.
     *
     * @param {string} string A system date value.
     * @returns {string} A display date value.
     */
    toDisplayDate(string) {
      if (!string || typeof string !== 'string') {
        return '';
      }
      const m = (0, _moment.default)(string, this.internalDateFormat);
      if (!m.isValid()) {
        return '';
      }
      return m.format(this.dateFormat);
    }

    /**
     * Convert a date-time from system to a display representation.
     *
     * @param {string} string A system date-time value.
     * @returns {string|-1} A display date-time value.
     */
    fromDisplay(string) {
      let m;
      if (this.timeZone) {
        m = _moment.default.tz(string, this.getDateTimeFormat(), this.timeZone).utc();
      } else {
        m = _moment.default.utc(string, this.getDateTimeFormat());
      }
      if (!m.isValid()) {
        return -1;
      }
      return m.format(this.internalDateTimeFormat) + ':00';
    }

    /**
     * Convert a date-time from system to a display representation.
     *
     * @param {string} string A system date value.
     * @returns {string} A display date-time value.
     */
    toDisplay(string) {
      if (!string) {
        return '';
      }
      return this.toMoment(string).format(this.getDateTimeFormat());
    }

    /**
     * Get a now moment.
     *
     * @returns {moment.Moment}
     */
    getNowMoment() {
      return (0, _moment.default)().tz(this.getTimeZone());
    }

    /**
     * Convert a system-formatted date to a moment.
     *
     * @param {string} string A date value in a system representation.
     * @returns {moment.Moment}
     * @internal
     */
    toMomentDate(string) {
      return _moment.default.tz(string, this.internalDateFormat, this.systemTimeZone);
    }

    /**
     * Convert a system-formatted date-time to a moment.
     *
     * @param {string} string A date-time value in a system representation.
     * @returns {moment.Moment}
     * @internal
     */
    toMoment(string) {
      let m = _moment.default.utc(string, this.internalDateTimeFullFormat);
      if (this.timeZone) {
        // noinspection JSUnresolvedReference
        m = m.tz(this.timeZone);
      }
      return m;
    }

    /**
     * Convert a date-time value from ISO to a system representation.
     *
     * @param {string} string
     * @returns {string} A date-time value in a system representation.
     */
    fromIso(string) {
      if (!string) {
        return '';
      }
      const m = (0, _moment.default)(string).utc();
      return m.format(this.internalDateTimeFormat);
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Convert a date-time value from system to an ISO representation.
     *
     * @param string A date-time value in a system representation.
     * @returns {string} An ISO date-time value.
     */
    toIso(string) {
      return this.toMoment(string).format();
    }

    /**
     * Get a today date value in a system representation.
     *
     * @returns {string}
     */
    getToday() {
      return (0, _moment.default)().tz(this.getTimeZone()).format(this.internalDateFormat);
    }

    /**
     * Get a date-time value in a system representation, shifted from now.
     *
     * @param {Number} shift A number to shift by.
     * @param {'minutes'|'hours'|'days'|'weeks'|'months'|'years'} type A shift unit.
     * @param {Number} [multiplicity] A number of minutes a value will be aliquot to.
     * @returns {string} A date-time value in a system representation
     */
    getDateTimeShiftedFromNow(shift, type, multiplicity) {
      if (!multiplicity) {
        return _moment.default.utc().add(shift, type).format(this.internalDateTimeFormat);
      }
      let unix = (0, _moment.default)().unix();
      unix = unix - unix % (multiplicity * 60);
      return _moment.default.unix(unix).utc().add(shift, type).format(this.internalDateTimeFormat);
    }

    /**
     * Get a date value in a system representation, shifted from today.
     *
     * @param {Number} shift A number to shift by.
     * @param {'days'|'weeks'|'months'|'years'} type A shift unit.
     * @returns {string} A date value in a system representation
     */
    getDateShiftedFromToday(shift, type) {
      return _moment.default.tz(this.getTimeZone()).add(shift, type).format(this.internalDateFormat);
    }

    /**
     * Get a now date-time value in a system representation.
     *
     * @param {Number} [multiplicity] A number of minutes a value will be aliquot to.
     * @returns {string}
     */
    getNow(multiplicity) {
      if (!multiplicity) {
        return _moment.default.utc().format(this.internalDateTimeFormat);
      }
      let unix = (0, _moment.default)().unix();
      unix = unix - unix % (multiplicity * 60);
      return _moment.default.unix(unix).utc().format(this.internalDateTimeFormat);
    }

    /**
     * Set settings and preferences.
     *
     * @param {module:models/settings} settings Settings.
     * @param {module:models/preferences} preferences Preferences.
     * @internal
     */
    setSettingsAndPreferences(settings, preferences) {
      if (settings.has('dateFormat')) {
        this.dateFormat = settings.get('dateFormat');
      }
      if (settings.has('timeFormat')) {
        this.timeFormat = settings.get('timeFormat');
      }
      if (settings.has('timeZone')) {
        this.timeZone = settings.get('timeZone') || null;
        this.systemTimeZone = this.timeZone || 'UTC';
        if (this.timeZone === 'UTC') {
          this.timeZone = null;
        }
      }
      if (settings.has('weekStart')) {
        this.weekStart = settings.get('weekStart');
      }
      preferences.on('change', model => {
        if (model.has('dateFormat') && model.get('dateFormat')) {
          this.dateFormat = model.get('dateFormat');
        }
        if (model.has('timeFormat') && model.get('timeFormat')) {
          this.timeFormat = model.get('timeFormat');
        }
        if (model.has('timeZone') && model.get('timeZone')) {
          this.timeZone = model.get('timeZone');
        }
        if (model.has('weekStart') && model.get('weekStart') !== -1) {
          this.weekStart = model.get('weekStart');
        }
        if (this.timeZone === 'UTC') {
          this.timeZone = null;
        }
      });
    }

    /**
     * Set a language.
     *
     * @param {module:language} language A language.
     * @internal
     */
    setLanguage(language) {
      _moment.default.updateLocale('en', {
        months: language.translatePath(['Global', 'lists', 'monthNames']),
        monthsShort: language.translatePath(['Global', 'lists', 'monthNamesShort']),
        weekdays: language.translatePath(['Global', 'lists', 'dayNames']),
        weekdaysShort: language.translatePath(['Global', 'lists', 'dayNamesShort']),
        weekdaysMin: language.translatePath(['Global', 'lists', 'dayNamesMin'])
      });
      _moment.default.locale('en');
    }
  }
  var _default = _exports.default = DateTime;
});

define("acl-manager", ["exports", "acl", "utils", "bullbone"], function (_exports, _acl, _utils, _bullbone) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _acl = _interopRequireDefault(_acl);
  _utils = _interopRequireDefault(_utils);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 acl-manager */

  /**
   * An action.
   *
   * @typedef {'create'|'read'|'edit'|'delete'|'stream'} module:acl-manager~action
   */

  /**
   * An access checking class for a specific scope.
   */
  class AclManager {
    /** @protected */
    data = null;
    fieldLevelList = ['yes', 'no'];

    /**
     * @param {module:models/user} user A user.
     * @param {Object} implementationClassMap `acl` implementations.
     * @param {boolean} aclAllowDeleteCreated Allow a user to delete records they created regardless a
     *   role access level.
     */
    constructor(user, implementationClassMap, aclAllowDeleteCreated) {
      this.setEmpty();

      /** @protected */
      this.user = user || null;
      this.implementationClassMap = implementationClassMap || {};
      this.aclAllowDeleteCreated = aclAllowDeleteCreated;
    }

    /**
     * @protected
     */
    setEmpty() {
      this.data = {
        table: {},
        fieldTable: {},
        fieldTableQuickAccess: {}
      };
      this.implementationHash = {};
      this.forbiddenFieldsCache = {};
      this.implementationClassMap = {};
      this.forbiddenAttributesCache = {};
    }

    /**
     * Get an `acl` implementation.
     *
     * @protected
     * @param {string} scope A scope.
     * @returns {module:acl}
     */
    getImplementation(scope) {
      if (!(scope in this.implementationHash)) {
        let implementationClass = _acl.default;
        if (scope in this.implementationClassMap) {
          implementationClass = this.implementationClassMap[scope];
        }
        const forbiddenFieldList = this.getScopeForbiddenFieldList(scope);
        const params = {
          aclAllowDeleteCreated: this.aclAllowDeleteCreated,
          teamsFieldIsForbidden: forbiddenFieldList.includes('teams'),
          forbiddenFieldList: forbiddenFieldList
        };
        this.implementationHash[scope] = new implementationClass(this.getUser(), scope, params, this);
      }
      return this.implementationHash[scope];
    }

    /**
     * @return {import('models/user').default}
     * @protected
     */
    getUser() {
      return this.user;
    }

    /**
     * @internal
     */
    set(data) {
      data = data || {};
      this.data = data;
      this.data.table = this.data.table || {};
      this.data.fieldTable = this.data.fieldTable || {};
      this.data.attributeTable = this.data.attributeTable || {};
    }

    /**
     * @deprecated Use `getPermissionLevel`.
     *
     * @returns {string|null}
     */
    get(name) {
      return this.data[name] || null;
    }

    /**
     * Get a permission level.
     *
     * @param {string} permission A permission name.
     * @returns {'yes'|'all'|'team'|'no'}
     */
    getPermissionLevel(permission) {
      let permissionKey = permission;
      if (permission.slice(-10) !== 'Permission') {
        permissionKey = permission + 'Permission';
      }
      return this.data[permissionKey] || 'no';
    }

    /**
     * Get access level to a scope action.
     *
     * @param {string} scope A scope.
     * @param {module:acl-manager~action} action An action.
     * @returns {'yes'|'all'|'team'|'own'|'no'|null}
     */
    getLevel(scope, action) {
      if (!(scope in this.data.table)) {
        return null;
      }
      const scopeItem = this.data.table[scope];
      if (typeof scopeItem !== 'object' || !(action in scopeItem)) {
        return null;
      }
      return scopeItem[action];
    }

    /**
     * Clear access data.
     *
     * @internal
     */
    clear() {
      this.setEmpty();
    }

    /**
     * Check whether a scope has ACL.
     *
     * @param {string} scope A scope.
     * @returns {boolean}
     */
    checkScopeHasAcl(scope) {
      const data = (this.data.table || {})[scope];
      if (typeof data === 'undefined') {
        return false;
      }
      return true;
    }

    /**
     * Check access to a scope.
     *
     * @param {string} scope A scope.
     * @param {module:acl-manager~action|null} [action=null] An action.
     * @param {boolean} [precise=false] Deprecated. Not used.
     * @returns {boolean} True if access allowed.
     */
    checkScope(scope, action, precise) {
      let data = (this.data.table || {})[scope];
      if (typeof data === 'undefined') {
        data = null;
      }
      return this.getImplementation(scope).checkScope(data, action, precise);
    }

    /**
     * Check access to a model.
     *
     * @param {module:model} model A model.
     * @param {module:acl-manager~action|null} [action=null] An action.
     * @param {boolean} [precise=false] To return `null` if not enough data is set in a model.
     *   E.g. the `teams` field is not yet loaded.
     * @returns {boolean|null} True if access allowed, null if not enough data to determine.
     */
    checkModel(model, action, precise) {
      const scope = model.entityType;

      // todo move this to custom acl
      if (action === 'edit') {
        if (!model.isEditable()) {
          return false;
        }
      }
      if (action === 'delete') {
        if (!model.isRemovable()) {
          return false;
        }
      }
      let data = (this.data.table || {})[scope];
      if (typeof data === 'undefined') {
        data = null;
      }
      const impl = this.getImplementation(scope);
      if (action) {
        const methodName = 'checkModel' + _utils.default.upperCaseFirst(action);
        if (methodName in impl) {
          return impl[methodName](model, data, precise);
        }
      }
      return impl.checkModel(model, data, action, precise);
    }

    /**
     * Check access to a scope or a model.
     *
     * @param {string|module:model} subject What to check. A scope or a model.
     * @param {module:acl-manager~action|null} [action=null] An action.
     * @param {boolean} [precise=false]  To return `null` if not enough data is set in a model.
     *   E.g. the `teams` field is not yet loaded.
     * @returns {boolean|null} True if access allowed, null if not enough data to determine.
     */
    check(subject, action, precise) {
      if (typeof subject === 'string') {
        return this.checkScope(subject, action, precise);
      }
      return this.checkModel(subject, action, precise);
    }

    /**
     * Check if a user is owner to a model.
     *
     * @param {module:model} model A model.
     * @returns {boolean|null} True if owner, null if not clear.
     */
    checkIsOwner(model) {
      return this.getImplementation(model.entityType).checkIsOwner(model);
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Check if a user in a team of a model.
     *
     * @param {module:model} model A model.
     * @returns {boolean|null} True if in a team, null if not clear.
     */
    checkInTeam(model) {
      return this.getImplementation(model.entityType).checkInTeam(model);
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Check if a record is shared with the user.
     *
     * @param {module:model} model A model.
     * @returns {boolean|null} True if shared, null if not clear.
     */
    checkIsShared(model) {
      return this.getImplementation(model.entityType).checkIsShared(model);
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Check an assignment permission to a user.
     *
     * @param {module:models/user} user A user.
     * @returns {boolean} True if access allowed.
     */
    checkAssignmentPermission(user) {
      return this.checkPermission('assignmentPermission', user);
    }

    /**
     * Check a user permission to a user.
     *
     * @param {module:models/user} user A user.
     * @returns {boolean} True if access allowed.
     */
    checkUserPermission(user) {
      return this.checkPermission('userPermission', user);
    }

    /**
     * Check a specific permission to a user.
     *
     * @param {string} permission A permission name.
     * @param {module:models/user} user A user.
     * @returns {boolean|null} True if access allowed. Null if not enough data loaded to know for sure.
     */
    checkPermission(permission, user) {
      if (this.getUser().isAdmin()) {
        return true;
      }
      const level = this.getPermissionLevel(permission);
      if (level === 'no') {
        if (user.id === this.getUser().id) {
          return true;
        }
        return false;
      }
      if (level === 'team') {
        if (!user.has('teamsIds')) {
          return null;
        }
        let result = false;
        const teamsIds = user.get('teamsIds') || [];
        teamsIds.forEach(id => {
          if ((this.getUser().get('teamsIds') || []).includes(id)) {
            result = true;
          }
        });
        return result;
      }
      if (level === 'all') {
        return true;
      }
      if (level === 'yes') {
        return true;
      }
      return false;
    }

    /**
     * Get a list of forbidden fields for an entity type.
     *
     * @param {string} scope An entity type.
     * @param {'read'|'edit'} [action='read'] An action.
     * @param {'yes'|'no'} [thresholdLevel='no'] A threshold level.
     * @returns {string[]} A forbidden field list.
     */
    getScopeForbiddenFieldList(scope, action, thresholdLevel) {
      action = action || 'read';
      thresholdLevel = thresholdLevel || 'no';
      const key = scope + '_' + action + '_' + thresholdLevel;
      if (key in this.forbiddenFieldsCache) {
        return _utils.default.clone(this.forbiddenFieldsCache[key]);
      }
      const levelList = this.fieldLevelList.slice(this.fieldLevelList.indexOf(thresholdLevel));
      const fieldTableQuickAccess = this.data.fieldTableQuickAccess || {};
      const scopeData = fieldTableQuickAccess[scope] || {};
      const fieldsData = scopeData.fields || {};
      const actionData = fieldsData[action] || {};
      const fieldList = [];
      levelList.forEach(level => {
        const list = actionData[level] || [];
        list.forEach(field => {
          if (fieldList.includes(field)) {
            return;
          }
          fieldList.push(field);
        });
      });
      this.forbiddenFieldsCache[key] = fieldList;
      return _utils.default.clone(fieldList);
    }

    /**
     * Get a list of forbidden attributes for an entity type.
     *
     * @param {string} scope An entity type.
     * @param {'read'|'edit'} [action='read'] An action.
     * @param {'yes'|'no'} [thresholdLevel='no'] A threshold level.
     * @returns {string[]} A forbidden attribute list.
     */
    getScopeForbiddenAttributeList(scope, action, thresholdLevel) {
      action = action || 'read';
      thresholdLevel = thresholdLevel || 'no';
      const key = scope + '_' + action + '_' + thresholdLevel;
      if (key in this.forbiddenAttributesCache) {
        return _utils.default.clone(this.forbiddenAttributesCache[key]);
      }
      const levelList = this.fieldLevelList.slice(this.fieldLevelList.indexOf(thresholdLevel));
      const fieldTableQuickAccess = this.data.fieldTableQuickAccess || {};
      const scopeData = fieldTableQuickAccess[scope] || {};
      const attributesData = scopeData.attributes || {};
      const actionData = attributesData[action] || {};
      const attributeList = [];
      levelList.forEach(level => {
        const list = actionData[level] || [];
        list.forEach(attribute => {
          if (attributeList.includes(attribute)) {
            return;
          }
          attributeList.push(attribute);
        });
      });
      this.forbiddenAttributesCache[key] = attributeList;
      return _utils.default.clone(attributeList);
    }

    /**
     * Check an assignment permission to a team.
     *
     * @param {string} teamId A team ID.
     * @returns {boolean} True if access allowed.
     */
    checkTeamAssignmentPermission(teamId) {
      if (this.getPermissionLevel('assignmentPermission') === 'all') {
        return true;
      }
      return this.getUser().getLinkMultipleIdList('teams').includes(teamId);
    }

    /**
     * Check access to a field.
     * @param {string} scope An entity type.
     * @param {string} field A field.
     * @param {'read'|'edit'} [action='read'] An action.
     * @returns {boolean} True if access allowed.
     */
    checkField(scope, field, action) {
      return !this.getScopeForbiddenFieldList(scope, action).includes(field);
    }
  }
  AclManager.extend = _bullbone.View.extend;
  var _default = _exports.default = AclManager;
});

define("views/modals/edit", ["exports", "views/modal", "backbone"], function (_exports, _modal, _backbone) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _backbone = _interopRequireDefault(_backbone);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/modals/edit */

  /**
   * A quick edit modal.
   */
  class EditModalView extends _modal.default {
    template = 'modals/edit';
    cssName = 'edit-modal';
    /** @protected */
    saveDisabled = false;
    /** @protected */
    fullFormDisabled = false;
    /** @protected */
    editView = null;
    escapeDisabled = true;
    className = 'dialog dialog-record';
    /** @protected */
    sideDisabled = false;
    /** @protected */
    bottomDisabled = false;
    isCollapsible = true;

    /**
     * @private
     * @type {boolean}
     */
    wasModified = false;

    /**
     * @private
     * @type {string}
     */
    nameAttribute;
    shortcutKeys = {
      /** @this EditModalView */
      'Control+Enter': function (e) {
        if (this.saveDisabled) {
          return;
        }
        if (this.buttonList.findIndex(item => item.name === 'save' && !item.hidden) === -1) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        if (document.activeElement instanceof HTMLInputElement) {
          // Fields may need to fetch data first.
          document.activeElement.dispatchEvent(new Event('change', {
            bubbles: true
          }));
        }
        this.actionSave();
      },
      /** @this EditModalView */
      'Control+KeyS': function (e) {
        if (this.saveDisabled) {
          return;
        }
        if (this.buttonList.findIndex(item => item.name === 'save' && !item.hidden) === -1) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        this.actionSaveAndContinueEditing();
      },
      /** @this EditModalView */
      'Escape': function (e) {
        if (this.saveDisabled) {
          return;
        }
        e.stopPropagation();
        e.preventDefault();
        const focusedFieldView = this.getRecordView().getFocusedFieldView();
        if (focusedFieldView) {
          this.model.set(focusedFieldView.fetch(), {
            skipReRender: true
          });
        }
        if (this.getRecordView().isChanged) {
          this.confirm(this.translate('confirmLeaveOutMessage', 'messages')).then(() => this.actionClose());
          return;
        }
        this.actionClose();
      },
      /** @this EditModalView */
      'Control+Backslash': function (e) {
        this.getRecordView().handleShortcutKeyControlBackslash(e);
      }
    };

    /**
     * @typedef {Record} module:views/modals/edit~options
     *
     * @property {string} entityType An entity type.
     * @property {string} [id] An ID.
     * @property {string} [layoutName] A layout name.
     * @property {Record} [attributes] Attributes.
     * @property {model:model~setRelateItem | model:model~setRelateItem[]} [relate] A relate data.
     * @property {import('view-record-helper')} [recordHelper] A record helper.
     * @property {boolean} [saveDisabled] Disable save.
     * @property {boolean} [fullFormDisabled] Disable full-form.
     * @property {string} [headerText] A header text.
     * @property {boolean} [focusForCreate] Focus for create.
     * @property {string} [rootUrl] A root URL.
     * @property {string} [returnUrl] A return URL.
     * @property {{
     *     controller: string,
     *     action: string|null,
     *     options: {isReturn?: boolean} & Record,
     * }} [returnDispatchParams] Return dispatch params.
     * @property {string} [fullFormUrl] A full-form URL. As of v9.0.
     */

    /**
     * @param {module:views/modals/edit~options} options
     */
    constructor(options) {
      super(options);
    }
    setup() {
      this.buttonList = [];
      if ('saveDisabled' in this.options) {
        this.saveDisabled = this.options.saveDisabled;
      }
      if (!this.saveDisabled) {
        this.buttonList.push({
          name: 'save',
          label: 'Save',
          style: 'primary',
          title: 'Ctrl+Enter',
          onClick: () => this.actionSave()
        });
      }
      this.fullFormDisabled = this.options.fullFormDisabled || this.fullFormDisabled;
      this.layoutName = this.options.layoutName || this.layoutName;
      if (!this.fullFormDisabled) {
        this.buttonList.push({
          name: 'fullForm',
          label: 'Full Form',
          onClick: () => this.actionFullForm()
        });
      }
      this.buttonList.push({
        name: 'cancel',
        label: 'Cancel',
        title: 'Esc'
      });
      this.scope = this.scope || this.options.scope || this.options.entityType;
      this.entityType = this.options.entityType || this.scope;
      this.id = this.options.id;
      this.nameAttribute = this.getMetadata().get(`clientDefs.${this.entityType}.nameAttribute`) || 'name';
      if (this.options.headerText !== undefined) {
        this.headerHtml = undefined;
        this.headerText = this.options.headerText;
      }
      this.sourceModel = this.model;
      this.waitForView('edit');
      this.getModelFactory().create(this.entityType, model => {
        if (this.id) {
          if (this.sourceModel) {
            model = this.model = this.sourceModel.clone();
          } else {
            this.model = model;
            model.id = this.id;
          }
          model.fetch().then(() => {
            if (!this.headerText) {
              this.headerHtml = this.composeHeaderHtml();
            }
            this.createRecordView(model);
          });
          return;
        }
        this.model = model;
        if (this.options.relate) {
          model.setRelate(this.options.relate);
        }
        if (this.options.attributes) {
          model.set(this.options.attributes);
        }
        if (!this.headerText) {
          this.headerHtml = this.composeHeaderHtml();
        }
        this.createRecordView(model);
      });
      this.listenTo(this.model, 'change', (m, o) => {
        if (o.ui) {
          this.wasModified = true;
        }
      });
    }

    /**
     * @param {module:model} model
     * @param {function} [callback]
     */
    createRecordView(model, callback) {
      const viewName = this.editView || this.getMetadata().get(['clientDefs', model.entityType, 'recordViews', 'editSmall']) || this.getMetadata().get(['clientDefs', model.entityType, 'recordViews', 'editQuick']) || 'views/record/edit-small';
      const options = {
        model: model,
        fullSelector: this.containerSelector + ' .edit-container',
        type: 'editSmall',
        layoutName: this.layoutName || 'detailSmall',
        buttonsDisabled: true,
        sideDisabled: this.sideDisabled,
        bottomDisabled: this.bottomDisabled,
        focusForCreate: this.options.focusForCreate,
        recordHelper: this.options.recordHelper,
        exit: () => {}
      };
      this.handleRecordViewOptions(options);
      this.createView('edit', viewName, options, callback).then(/** import('views/fields/base').default */view => {
        this.listenTo(view, 'before:save', () => this.trigger('before:save', model));
        if (this.options.relate && 'link' in this.options.relate) {
          const link = this.options.relate.link;
          if (model.hasField(link) && ['link'].includes(model.getFieldType(link))) {
            view.setFieldReadOnly(link);
          }
        }
      });
    }
    handleRecordViewOptions(options) {}

    /**
     * @return {module:views/record/edit}
     */
    getRecordView() {
      return this.getView('edit');
    }
    onBackdropClick() {
      if (this.getRecordView().isChanged) {
        return;
      }
      this.close();
    }

    /**
     * @protected
     * @return {string}
     */
    composeHeaderHtml() {
      let html;
      if (!this.id) {
        html = $('<span>').text(this.getLanguage().translate('Create ' + this.scope, 'labels', this.scope)).get(0).outerHTML;
      } else {
        const wrapper = document.createElement('span');
        const scope = document.createElement('span');
        scope.textContent = this.getLanguage().translate(this.scope, 'scopeNames');
        const separator = document.createElement('span');
        separator.classList.add('chevron-right');
        const name = this.model.attributes[this.nameAttribute];
        wrapper.append(document.createTextNode(this.getLanguage().translate('Edit') + ' · '), scope);
        if (name) {
          wrapper.append(' ', separator, ' ', name);
        }
        html = wrapper.outerHTML;
      }
      if (!this.fullFormDisabled) {
        const url = this.id ? '#' + this.scope + '/edit/' + this.id : '#' + this.scope + '/create';
        html = $('<a>').attr('href', url).addClass('action').attr('title', this.translate('Full Form')).attr('data-action', 'fullForm').append(html).get(0).outerHTML;
      }
      html = this.getHelper().getScopeColorIconHtml(this.scope) + html;
      return html;
    }

    /**
     * @protected
     * @param {{bypassClose?: boolean}} [data]
     */
    actionSave() {
      let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      const editView = this.getRecordView();
      const model = editView.model;
      const $buttons = this.dialog.$el.find('.modal-footer button');
      $buttons.addClass('disabled').attr('disabled', 'disabled');
      editView.save().then(() => {
        const wasNew = !this.id;
        if (wasNew) {
          this.id = model.id;
        }
        this.trigger('after:save', model, {
          bypassClose: data.bypassClose
        });
        if (!data.bypassClose) {
          this.dialog.close();
          if (wasNew) {
            const url = `#${this.scope}/view/${model.id}`;
            const name = model.attributes[this.nameAttribute] || this.model.id;
            const msg = this.translate('Created') + '\n' + `[${name}](${url})`;
            Espo.Ui.notify(msg, 'success', 4000, {
              suppress: true
            });
          }
          return;
        }
        $(this.containerElement).find('.modal-header .modal-title-text').html(this.composeHeaderHtml());
        $buttons.removeClass('disabled').removeAttr('disabled');
      }).catch(() => {
        $buttons.removeClass('disabled').removeAttr('disabled');
      });
    }
    actionSaveAndContinueEditing() {
      this.actionSave({
        bypassClose: true
      });
    }
    actionFullForm() {
      let url;
      const router = this.getRouter();
      let attributes;
      let model;
      let options;
      if (!this.id) {
        url = this.options.fullFormUrl || `#${this.scope}/create`;
        attributes = this.getRecordView().fetch();
        model = this.getRecordView().model;
        attributes = {
          ...attributes,
          ...model.getClonedAttributes()
        };
        options = {
          attributes: attributes,
          relate: this.options.relate,
          returnUrl: this.options.returnUrl || _backbone.default.history.fragment,
          returnDispatchParams: this.options.returnDispatchParams || null
        };
        if (this.options.rootUrl) {
          options.rootUrl = this.options.rootUrl;
        }
        setTimeout(() => {
          router.dispatch(this.scope, 'create', options);
          router.navigate(url, {
            trigger: false
          });
        }, 10);
      } else {
        url = this.options.fullFormUrl || `#${this.scope}/edit/${this.id}`;
        attributes = this.getRecordView().fetch();
        model = this.getRecordView().model;
        attributes = {
          ...attributes,
          ...model.getClonedAttributes()
        };
        options = {
          attributes: attributes,
          returnUrl: this.options.returnUrl || _backbone.default.history.fragment,
          returnDispatchParams: this.options.returnDispatchParams || null,
          model: this.sourceModel,
          id: this.id
        };
        if (this.options.rootUrl) {
          options.rootUrl = this.options.rootUrl;
        }
        setTimeout(() => {
          router.dispatch(this.scope, 'edit', options);
          router.navigate(url, {
            trigger: false
          });
        }, 10);
      }
      this.trigger('leave');
      this.dialog.close();
    }
    async beforeCollapse() {
      if (this.wasModified) {
        this.getRecordView().setConfirmLeaveOut(false);
        this.getRouter().addWindowLeaveOutObject(this);
      }
    }
    afterExpand() {
      if (this.wasModified) {
        this.getRecordView().setConfirmLeaveOut(true);
      }
      this.getRouter().removeWindowLeaveOutObject(this);
    }
  }
  var _default = _exports.default = EditModalView;
});

define("view-helper", ["exports", "marked", "dompurify", "handlebars"], function (_exports, _marked, _dompurify, _handlebars) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _dompurify = _interopRequireDefault(_dompurify);
  _handlebars = _interopRequireDefault(_handlebars);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 view-helper */

  /**
   * A view helper.
   */
  class ViewHelper {
    constructor() {
      this._registerHandlebarsHelpers();

      /** @private */
      this.mdBeforeList = [
      /*{
          regex: /```\n?([\s\S]*?)```/g,
          value: (s, string) => {
              return '```\n' + string.replace(/\\\>/g, '>') + '```';
          },
      },*/
      {
        // Also covers triple-backtick blocks.
        regex: /`([\s\S]*?)`/g,
        value: (s, string) => {
          // noinspection RegExpRedundantEscape
          return '`' + string.replace(/\\\</g, '<') + '`';
        }
      }];
      _marked.marked.setOptions({
        breaks: true,
        tables: false,
        headerIds: false
      });

      // @todo Review after updating the marked library.
      _marked.marked.use({
        tokenizer: {
          tag(src) {
            const cap = _marked.Lexer.rules.inline.tag.exec(src);
            if (!cap) {
              return;
            }
            return {
              type: 'text',
              raw: cap[0],
              inLink: this.lexer.state.inLink,
              inRawBlock: this.lexer.state.inRawBlock,
              text: _handlebars.default.Utils.escapeExpression(cap[0])
            };
          },
          html(src) {
            const cap = _marked.Lexer.rules.block.html.exec(src);
            if (!cap) {
              return;
            }
            const token = {
              type: 'paragraph',
              raw: cap[0],
              pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
              text: _handlebars.default.Utils.escapeExpression(cap[0])
            };
            token.tokens = [];
            this.lexer.inline(token.text, token.tokens);
            return token;
          }
        }
      });
      _dompurify.default.addHook('beforeSanitizeAttributes', function (node) {
        if (node instanceof HTMLAnchorElement) {
          if (node.getAttribute('target')) {
            node.targetBlank = true;
          } else {
            node.targetBlank = false;
          }
        }
        if (node instanceof HTMLOListElement && node.start && node.start > 99) {
          node.removeAttribute('start');
        }
        if (node instanceof HTMLFormElement) {
          if (node.action) {
            node.removeAttribute('action');
          }
          if (node.hasAttribute('method')) {
            node.removeAttribute('method');
          }
        }
        if (node instanceof HTMLButtonElement) {
          if (node.type === 'submit') {
            node.type = 'button';
          }
        }
      });
      _dompurify.default.addHook('afterSanitizeAttributes', function (node) {
        if (node instanceof HTMLAnchorElement) {
          const href = node.getAttribute('href');
          if (href && !href.startsWith('#')) {
            node.setAttribute('rel', 'noopener noreferrer');
          }
          if (node.targetBlank) {
            node.setAttribute('target', '_blank');
            node.setAttribute('rel', 'noopener noreferrer');
          }
        }
      });
      _dompurify.default.addHook('uponSanitizeAttribute', (node, data) => {
        if (data.attrName === 'style') {
          const style = data.attrValue.split(';').map(s => s.trim()).filter(rule => {
            const [property, value] = rule.split(':').map(s => s.trim().toLowerCase());
            if (property === 'position' && ['absolute', 'fixed', 'sticky'].includes(value)) {
              return false;
            }
            return true;
          });
          data.attrValue = style.join('; ');
        }
      });
    }

    /**
     * A layout manager.
     *
     * @type {module:layout-manager}
     */
    layoutManager = null;

    /**
     * A config.
     *
     * @type {module:models/settings}
     */
    settings = null;

    /**
     * A config.
     *
     * @type {module:models/settings}
     */
    config = null;

    /**
     * A current user.
     *
     * @type {module:models/user}
     */
    user = null;

    /**
     * A preferences.
     *
     * @type {module:models/preferences}
     */
    preferences = null;

    /**
     * An ACL manager.
     *
     * @type {module:acl-manager}
     */
    acl = null;

    /**
     * A model factory.
     *
     * @type {module:model-factory}
     */
    modelFactory = null;

    /**
     * A collection factory.
     *
     * @type {module:collection-factory}
     */
    collectionFactory = null;

    /**
     * A router.
     *
     * @type {module:router}
     */
    router = null;

    /**
     * A storage.
     *
     * @type {module:storage}
     */
    storage = null;

    /**
     * A session storage.
     *
     * @type {module:session-storage}
     */
    sessionStorage = null;

    /**
     * A date-time util.
     *
     * @type {module:date-time}
     */
    dateTime = null;

    /**
     * A language.
     *
     * @type {module:language}
     */
    language = null;

    /**
     * A metadata.
     *
     * @type {module:metadata}
     */
    metadata = null;

    /**
     * A field-manager util.
     *
     * @type {module:field-manager}
     */
    fieldManager = null;

    /**
     * A cache.
     *
     * @type {module:cache}
     */
    cache = null;

    /**
     * A theme manager.
     *
     * @type {module:theme-manager}
     */
    themeManager = null;

    /**
     * A web-socket manager. Null if not enabled.
     *
     * @type {?module:web-socket-manager}
     */
    webSocketManager = null;

    /**
     * A number util.
     *
     * @type {module:num-util}
     */
    numberUtil = null;

    /**
     * A page-title util.
     *
     * @type {module:page-title}
     */
    pageTitle = null;

    /**
     * A broadcast channel.
     *
     * @type {?module:broadcast-channel}
     */
    broadcastChannel = null;

    /**
     * A base path.
     *
     * @type {string}
     */
    basePath = '';

    /**
     * Application parameters.
     *
     * @type {import('app-params').default|null}
     */
    appParams = null;

    /**
     * @private
     */
    _registerHandlebarsHelpers() {
      _handlebars.default.registerHelper('img', img => {
        return new _handlebars.default.SafeString(`<img src="img/${img}" alt="img">`);
      });
      _handlebars.default.registerHelper('prop', (object, name) => {
        if (object === undefined) {
          console.warn(`Undefined value passed to 'prop' helper.`);
          return undefined;
        }
        if (name in object) {
          return object[name];
        }
        return undefined;
      });
      _handlebars.default.registerHelper('var', (name, context, options) => {
        if (typeof context === 'undefined') {
          return null;
        }
        let contents = context[name];
        if (options.hash.trim) {
          contents = contents.trim();
        }
        return new _handlebars.default.SafeString(contents);
      });
      _handlebars.default.registerHelper('concat', function (left, right) {
        return left + right;
      });
      _handlebars.default.registerHelper('ifEqual', function (left, right, options) {
        // noinspection EqualityComparisonWithCoercionJS
        if (left == right) {
          return options.fn(this);
        }
        return options.inverse(this);
      });
      _handlebars.default.registerHelper('ifNotEqual', function (left, right, options) {
        // noinspection EqualityComparisonWithCoercionJS
        if (left != right) {
          return options.fn(this);
        }
        return options.inverse(this);
      });
      _handlebars.default.registerHelper('ifPropEquals', function (object, property, value, options) {
        // noinspection EqualityComparisonWithCoercionJS
        if (object[property] == value) {
          return options.fn(this);
        }
        return options.inverse(this);
      });
      _handlebars.default.registerHelper('ifAttrEquals', function (model, attr, value, options) {
        // noinspection EqualityComparisonWithCoercionJS
        if (model.get(attr) == value) {
          return options.fn(this);
        }
        return options.inverse(this);
      });
      _handlebars.default.registerHelper('ifAttrNotEmpty', function (model, attr, options) {
        const value = model.get(attr);
        if (value !== null && typeof value !== 'undefined') {
          return options.fn(this);
        }
        return options.inverse(this);
      });
      _handlebars.default.registerHelper('get', (model, name) => model.get(name));
      _handlebars.default.registerHelper('length', arr => arr.length);
      _handlebars.default.registerHelper('translate', (name, options) => {
        const scope = options.hash.scope || null;
        const category = options.hash.category || null;
        if (name === 'null') {
          return '';
        }
        return this.language.translate(name, category, scope);
      });
      _handlebars.default.registerHelper('dropdownItem', (name, options) => {
        const scope = options.hash.scope || null;
        const label = options.hash.label;
        const labelTranslation = options.hash.labelTranslation;
        const data = options.hash.data;
        const hidden = options.hash.hidden;
        const disabled = options.hash.disabled;
        const title = options.hash.title;
        const link = options.hash.link;
        const action = options.hash.action || name;
        const iconHtml = options.hash.iconHtml;
        const iconClass = options.hash.iconClass;
        let html = options.hash.html || options.hash.text || (labelTranslation ? this.language.translatePath(labelTranslation) : this.language.translate(label, 'labels', scope));
        if (!options.hash.html) {
          html = this.escapeString(html);
        }
        if (iconHtml) {
          html = iconHtml + ' ' + html;
        } else if (iconClass) {
          const iconHtml = $('<span>').addClass(iconClass).get(0).outerHTML;
          html = iconHtml + ' ' + html;
        }
        const $li = $('<li>').addClass(hidden ? 'hidden' : '').addClass(disabled ? 'disabled' : '');
        const $a = $('<a>').attr('role', 'button').attr('tabindex', '0').attr('data-name', name).addClass(options.hash.className || '').addClass('action').html(html);
        if (action) {
          $a.attr('data-action', action);
        }
        $li.append($a);
        link ? $a.attr('href', link) : $a.attr('role', 'button');
        if (data) {
          for (const key in data) {
            $a.attr('data-' + Espo.Utils.camelCaseToHyphen(key), data[key]);
          }
        }
        if (disabled) {
          $li.attr('disabled', 'disabled');
        }
        if (title) {
          $a.attr('title', title);
        }
        return new _handlebars.default.SafeString($li.get(0).outerHTML);
      });
      _handlebars.default.registerHelper('button', (name, options) => {
        const style = options.hash.style || 'default';
        const scope = options.hash.scope || null;
        const label = options.hash.label || name;
        const labelTranslation = options.hash.labelTranslation;
        const link = options.hash.link;
        const iconHtml = options.hash.iconHtml;
        const iconClass = options.hash.iconClass;
        let html = options.hash.html || options.hash.text || (labelTranslation ? this.language.translatePath(labelTranslation) : this.language.translate(label, 'labels', scope));
        if (!options.hash.html) {
          html = this.escapeString(html);
        }
        if (iconHtml) {
          html = iconHtml + ' ' + '<span>' + html + '</span>';
        } else if (iconClass) {
          const iconHtml = $('<span>').addClass(iconClass).get(0).outerHTML;
          html = iconHtml + ' ' + '<span>' + html + '</span>';
        }
        const tag = link ? '<a>' : '<button>';
        const $button = $(tag).addClass('btn action').addClass(options.hash.className || '').addClass(options.hash.hidden ? 'hidden' : '').addClass(options.hash.disabled ? 'disabled' : '').attr('data-action', name).attr('data-name', name).addClass('btn-' + style).html(html);
        link ? $button.href(link) : $button.attr('type', 'button');
        if (options.hash.disabled) {
          $button.attr('disabled', 'disabled');
        }
        if (options.hash.title) {
          $button.attr('title', options.hash.title);
        }
        return new _handlebars.default.SafeString($button.get(0).outerHTML);
      });
      _handlebars.default.registerHelper('hyphen', string => {
        return Espo.Utils.convert(string, 'c-h');
      });
      _handlebars.default.registerHelper('toDom', string => {
        return Espo.Utils.toDom(string);
      });

      // noinspection SpellCheckingInspection
      _handlebars.default.registerHelper('breaklines', text => {
        text = _handlebars.default.Utils.escapeExpression(text || '');
        text = text.replace(/(\r\n|\n|\r)/gm, '<br>');
        return new _handlebars.default.SafeString(text);
      });
      _handlebars.default.registerHelper('complexText', (text, options) => {
        if (typeof text !== 'string' && !(text instanceof String)) {
          return '';
        }
        return this.transformMarkdownText(text, options.hash);
      });
      _handlebars.default.registerHelper('translateOption', (name, options) => {
        const scope = options.hash.scope || null;
        const field = options.hash.field || null;
        if (!field) {
          return '';
        }
        let translationHash = options.hash.translatedOptions || null;
        if (translationHash === null) {
          translationHash = this.language.translate(/** @type {string} */field, 'options', scope) || {};
          if (typeof translationHash !== 'object') {
            translationHash = {};
          }
        }
        if (name === null) {
          name = '';
        }
        return translationHash[name] || name;
      });
      _handlebars.default.registerHelper('options', (/** any[] */list, value, options) => {
        if (typeof value === 'undefined') {
          value = false;
        }
        list = list || [];
        let html = '';
        const multiple = Object.prototype.toString.call(value) === '[object Array]';
        const checkOption = name => {
          if (multiple) {
            return value.indexOf(name) !== -1;
          }
          return value === name || !value && !name && name !== 0;
        };
        options.hash = /** @type {Record} */options.hash || {};
        const scope = options.hash.scope || false;
        const category = options.hash.category || false;
        const field = options.hash.field || false;
        const styleMap = options.hash.styleMap || {};
        if (!multiple && options.hash.includeMissingOption && (value || value === '')) {
          if (!list.includes(value)) {
            list = Espo.Utils.clone(list);
            list.push(value);
          }
        }
        let translationHash = options.hash.translationHash || options.hash.translatedOptions || null;
        if (translationHash === null) {
          translationHash = {};
          if (!category && field) {
            translationHash = this.language.translate(/** @type {string} */field, 'options', /** @type {string} */scope) || {};
            if (typeof translationHash !== 'object') {
              translationHash = {};
            }
          }
        }
        const translate = name => {
          if (!category) {
            return translationHash[name] || name;
          }
          return this.language.translate(name, category, /** @type {string} */scope);
        };
        for (const key in list) {
          const value = list[key];
          const label = translate(value);
          const $option = $('<option>').attr('value', value).addClass(styleMap[value] ? 'text-' + styleMap[value] : '').text(label);
          if (checkOption(list[key])) {
            $option.attr('selected', 'selected');
          }
          html += $option.get(0).outerHTML;
        }
        return new _handlebars.default.SafeString(html);
      });
      _handlebars.default.registerHelper('basePath', () => {
        return this.basePath || '';
      });
    }

    /**
     * Get an application parameter.
     *
     * @param {string} name
     * @returns {*}
     */
    getAppParam(name) {
      if (!this.appParams) {
        return undefined;
      }
      return this.appParams.get(name);
    }

    /**
     * Escape a string.
     *
     * @param {string} text A string.
     * @returns {string}
     */
    escapeString(text) {
      return _handlebars.default.Utils.escapeExpression(text);
    }

    /**
     * Get a user avatar HTML.
     *
     * @param {string} id A user ID.
     * @param {'small'|'medium'|'large'} [size='small'] A size.
     * @param {int} [width=16]
     * @param {string} [additionalClassName]  An additional class-name.
     * @returns {string}
     */
    getAvatarHtml(id, size, width, additionalClassName) {
      if (this.config.get('avatarsDisabled')) {
        return '';
      }
      const t = this.cache ? this.cache.get('app', 'timestamp') : this.settings.get('cacheTimestamp');
      const basePath = this.basePath || '';
      size = size || 'small';
      width = width || 16;
      let className = 'avatar';
      if (additionalClassName) {
        className += ' ' + additionalClassName;
      }

      // noinspection RequiredAttributes,HtmlRequiredAltAttribute
      return $(`<img>`).attr('src', `${basePath}?entryPoint=avatar&size=${size}&id=${id}&t=${t}`).attr('alt', 'avatar').addClass(className).attr('data-width', width.toString()).css('width', `var(--${width.toString()}px)`).attr('draggable', 'false').get(0).outerHTML;
    }

    /**
     * A Markdown text to HTML (one-line).
     *
     * @param {string} text A text.
     * @returns {Handlebars.SafeString} HTML.
     */
    transformMarkdownInlineText(text) {
      return this.transformMarkdownText(text, {
        inline: true
      });
    }

    /**
     * A Markdown text to HTML.
     *
     * @param {string} text A text.
     * @param {{inline?: boolean, linksInNewTab?: boolean}} [options] Options.
     * @returns {Handlebars.SafeString} HTML.
     */
    transformMarkdownText(text, options) {
      text = text || '';
      this.mdBeforeList.forEach(item => {
        text = text.replace(item.regex, item.value);
      });
      options = options || {};
      text = options.inline ? _marked.marked.parseInline(text) : _marked.marked.parse(text);
      text = _dompurify.default.sanitize(text, {}).toString();
      if (options.linksInNewTab) {
        text = text.replace(/<a href=/gm, '<a target="_blank" rel="noopener noreferrer" href=');
      }
      text = text.replace(/<a href="mailto:([^"]*)"/gm, '<a role="button" class="selectable" data-email-address="$1" data-action="mailTo"');
      return new _handlebars.default.SafeString(text);
    }

    /**
     * Get a color-icon HTML for a scope.
     *
     * @param {string} scope A scope.
     * @param {boolean} [noWhiteSpace=false] No white space.
     * @param {string} [additionalClassName] An additional class-name.
     * @returns {string}
     */
    getScopeColorIconHtml(scope, noWhiteSpace, additionalClassName) {
      if (this.config.get('scopeColorsDisabled') || this.preferences.get('scopeColorsDisabled')) {
        return '';
      }
      const color = this.metadata.get(['clientDefs', scope, 'color']);
      let html = '';
      if (color) {
        const $span = $('<span class="color-icon fas fa-square">');
        $span.css('color', color);
        if (additionalClassName) {
          $span.addClass(additionalClassName);
        }
        html = $span.get(0).outerHTML;
      }
      if (!noWhiteSpace) {
        if (html) {
          html += `<span style="user-select: none;">&nbsp;</span>`;
        }
      }
      return html;
    }

    /**
     * Sanitize HTML.
     *
     * @param {string} text HTML.
     * @param {Object} [options] Options.
     * @returns {string}
     */
    sanitizeHtml(text, options) {
      return _dompurify.default.sanitize(text, options);
    }

    /**
     * Moderately sanitize HTML.
     *
     * @param {string} value HTML.
     * @returns {string}
     */
    moderateSanitizeHtml(value) {
      value = value || '';
      value = value.replace(/<\/?(base)[^><]*>/gi, '');
      value = value.replace(/<\/?(object)[^><]*>/gi, '');
      value = value.replace(/<\/?(embed)[^><]*>/gi, '');
      value = value.replace(/<\/?(applet)[^><]*>/gi, '');
      value = value.replace(/<\/?(iframe)[^><]*>/gi, '');
      value = value.replace(/<\/?(script)[^><]*>/gi, '');
      value = value.replace(/<[^><]*([^a-z]on[a-z]+)=[^><]*>/gi, function (match) {
        return match.replace(/[^a-z]on[a-z]+=/gi, ' data-handler-stripped=');
      });
      value = this.stripEventHandlersInHtml(value);
      value = value.replace(/href=" *javascript:(.*?)"/gi, () => {
        return 'removed=""';
      });
      value = value.replace(/href=' *javascript:(.*?)'/gi, () => {
        return 'removed=""';
      });
      value = value.replace(/src=" *javascript:(.*?)"/gi, () => {
        return 'removed=""';
      });
      value = value.replace(/src=' *javascript:(.*?)'/gi, () => {
        return 'removed=""';
      });
      return value;
    }

    /**
     * Strip event handlers in HTML.
     *
     * @param {string} html HTML.
     * @returns {string}
     */
    stripEventHandlersInHtml(html) {
      let j; // @todo Revise.

      function stripHTML() {
        html = html.slice(0, strip) + html.slice(j);
        j = strip;
        strip = false;
      }
      function isValidTagChar(str) {
        return str.match(/[a-z?\\\/!]/i);
      }
      let strip = false;
      let lastQuote = false;
      for (let i = 0; i < html.length; i++) {
        if (html[i] === '<' && html[i + 1] && isValidTagChar(html[i + 1])) {
          i++;
          for (let j = i; j < html.length; j++) {
            if (!lastQuote && html[j] === '>') {
              if (strip) {
                stripHTML();
              }
              i = j;
              break;
            }

            // noinspection JSIncompatibleTypesComparison
            if (lastQuote === html[j]) {
              lastQuote = false;
              continue;
            }
            if (!lastQuote && html[j - 1] === "=" && (html[j] === "'" || html[j] === '"')) {
              lastQuote = html[j];
            }
            if (!lastQuote && html[j - 2] === " " && html[j - 1] === "o" && html[j] === "n") {
              strip = j - 2;
            }
            if (strip && html[j] === " " && !lastQuote) {
              stripHTML();
            }
          }
        }
      }
      return html;
    }

    /**
     * Calculate a content container height.
     *
     * @param {HTMLElement|JQuery} element An element.
     * @returns {number}
     */
    calculateContentContainerHeight(element) {
      const smallScreenWidth = this.themeManager.getParam('screenWidthXs');
      const $window = $(window);
      const footerHeight = $('#footer').height() || 26;
      let top = 0;
      element = $(element).get(0);
      if (element) {
        top = element.getBoundingClientRect().top;
        if ($window.width() < smallScreenWidth) {
          const $navbarCollapse = $('#navbar .navbar-body');
          if ($navbarCollapse.hasClass('in') || $navbarCollapse.hasClass('collapsing')) {
            top -= $navbarCollapse.height();
          }
        }
      }
      const spaceHeight = top + footerHeight;
      return $window.height() - spaceHeight - 20;
    }

    /**
     * Process view-setup-handlers.
     *
     * @param {module:view} view A view.
     * @param {string} type A view-setup-handler type.
     * @param {string} [scope] A scope.
     * @return Promise
     */
    processSetupHandlers(view, type, scope) {
      // noinspection JSUnresolvedReference
      scope = scope || view.scope || view.entityType;
      let handlerIdList = this.metadata.get(['clientDefs', 'Global', 'viewSetupHandlers', type]) || [];
      if (scope) {
        handlerIdList = handlerIdList.concat(this.metadata.get(['clientDefs', scope, 'viewSetupHandlers', type]) || []);
      }
      if (handlerIdList.length === 0) {
        return Promise.resolve();
      }

      /**
       * @interface
       * @name ViewHelper~Handler
       */

      /**
       * @function
       * @name ViewHelper~Handler#process
       * @param {module:view} [view] Deprecated.
       */
      const promiseList = [];
      for (const id of handlerIdList) {
        const promise = new Promise(resolve => {
          Espo.loader.require(id, /** typeof ViewHelper~Handler */Handler => {
            const result = new Handler(view).process(view);
            if (result && Object.prototype.toString.call(result) === '[object Promise]') {
              result.then(() => resolve());
              return;
            }
            resolve();
          });
        });
        promiseList.push(promise);
      }
      return Promise.all(promiseList);
    }

    /** @private */
    _isXsScreen;

    /**
     * Is xs screen width.
     *
     * @return {boolean}
     */
    isXsScreen() {
      if (this._isXsScreen == null) {
        this._isXsScreen = window.innerWidth < this.themeManager.getParam('screenWidthXs');
      }
      return this._isXsScreen;
    }
  }
  var _default = _exports.default = ViewHelper;
});

define("search-manager", ["exports", "di", "date-time", "storage"], function (_exports, _di, _dateTime, _storage) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _dateTime = _interopRequireDefault(_dateTime);
  _storage = _interopRequireDefault(_storage);
  let _init_dateTime, _init_extra_dateTime, _init_storage, _init_extra_storage;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 search-manager */
  /**
   * Search data.
   *
   * @typedef {Object} module:search-manager~data
   *
   * @property {string} [presetName] A preset.
   * @property {string} [textFilter] A text filter.
   * @property {string} [primary] A primary filter.
   * @property {Object.<string, boolean>} [bool] Bool filters.
   * @property {Record<module:search-manager~advancedFilter>} [advanced] Advanced filters (field filters).
   *     Contains data needed for both the backend and frontend. Keys are field names.
   */
  /**
   * A where item. Sent to the backend.
   *
   * @typedef {Object} module:search-manager~whereItem
   *
   * @property {string} type A type.
   * @property {string} [attribute] An attribute (field).
   * @property {module:search-manager~whereItem[]|string|number|boolean|null} [value] A value.
   * @property {boolean} [dateTime] Is a date-time item.
   * @property {string} [timeZone] A time-zone.
   */
  /**
   * An advanced filter
   *
   * @typedef {Object} module:search-manager~advancedFilter
   *
   * @property {string} type A type. E.g. `equals`.
   * @property {string} [attribute] An attribute.
   * @property {*} [value] A value.
   * @property {Object.<string, *>} [data] Additional data for UI.
   */
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * A search manager.
   */
  class SearchManager {
    static #_ = [_init_dateTime, _init_extra_dateTime, _init_storage, _init_extra_storage] = _applyDecs(this, [], [[(0, _di.inject)(_dateTime.default), 0, "dateTime"], [(0, _di.inject)(_storage.default), 0, "storage"]]).e;
    /**
     * @type {string|null}
     * @private
     */
    timeZone = null;

    /**
     * @private
     * @type {module:search-manager~data}
     */
    defaultData;

    /**
     * @private
     * @type {DateTime}
     */
    dateTime = _init_dateTime(this);

    /**
     * @private
     * @type {Storage}
     */
    storage = (_init_extra_dateTime(this), _init_storage(this));

    /**
     * @typedef {Object} module:search-manager~Options
     * @property {string} [storageKey] A storage key. If not specified, the storage won't be used.
     * @property {module:search-manager~data} [defaultData] Default data.
     * @property {boolean} [emptyOnReset] To empty on reset.
     */

    /**
     * @param {module:collection} collection A collection.
     * @param {module:search-manager~Options} [options] Options. As of 9.1.
     */
    constructor(collection) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      _init_extra_storage(this);
      /**
       * @private
       * @type {module:collection}
       */
      this.collection = collection;

      /**
       * An entity type.
       *
       * @private
       * @type {string}
       */
      this.scope = collection.entityType;

      /**
       * @private
       * @type {string}
       */
      this.storageKey = options.storageKey;

      /**
       * @private
       * @type {boolean}
       */
      this.useStorage = !!this.storageKey;

      /**
       * @private
       * @type {boolean}
       */
      this.emptyOnReset = options.emptyOnReset || false;

      /**
       * @private
       * @type {Object}
       */
      this.emptyData = {
        textFilter: '',
        bool: {},
        advanced: {},
        primary: null
      };
      let defaultData = options.defaultData;
      if (!defaultData && arguments[4]) {
        // For bc.
        defaultData = arguments[4];
      }
      if (defaultData) {
        this.defaultData = defaultData;
        for (const key in this.emptyData) {
          if (!(key in defaultData)) {
            defaultData[key] = Espo.Utils.clone(this.emptyData[key]);
          }
        }
      }

      /**
       * @type {module:search-manager~data}
       * @private
       */
      this.data = Espo.Utils.clone(defaultData) || this.emptyData;
      this.sanitizeData();
    }

    /**
     * @private
     */
    sanitizeData() {
      if (!('advanced' in this.data)) {
        this.data.advanced = {};
      }
      if (!('bool' in this.data)) {
        this.data.bool = {};
      }
      if (!('textFilter' in this.data)) {
        this.data.textFilter = '';
      }
    }

    /**
     * Get a where clause. The where clause to be sent to the backend.
     *
     * @returns {module:search-manager~whereItem[]}
     */
    getWhere() {
      const where = [];
      if (this.data.textFilter && this.data.textFilter !== '') {
        where.push({
          type: 'textFilter',
          value: this.data.textFilter
        });
      }
      if (this.data.bool) {
        const o = {
          type: 'bool',
          value: []
        };
        for (const name in this.data.bool) {
          if (this.data.bool[name]) {
            o.value.push(name);
          }
        }
        if (o.value.length) {
          where.push(o);
        }
      }
      if (this.data.primary) {
        const o = {
          type: 'primary',
          value: this.data.primary
        };
        if (o.value.length) {
          where.push(o);
        }
      }
      if (this.data.advanced) {
        for (const name in this.data.advanced) {
          const defs = this.data.advanced[name];
          if (!defs) {
            continue;
          }
          const part = this.getWherePart(name, defs);
          where.push(part);
        }
      }
      return where;
    }

    /**
     * @private
     */
    getWherePart(name, defs) {
      let attribute = name;
      if (typeof defs !== 'object') {
        console.error('Bad where clause');
        return {};
      }
      if ('where' in defs) {
        return defs.where;
      }
      const type = defs.type;
      let value;
      if (type === 'or' || type === 'and') {
        const a = [];
        value = defs.value || {};
        for (const n in value) {
          a.push(this.getWherePart(n, value[n]));
        }
        return {
          type: type,
          value: a
        };
      }
      if ('field' in defs) {
        // for backward compatibility
        attribute = defs.field;
      }
      if ('attribute' in defs) {
        attribute = defs.attribute;
      }
      if (defs.dateTime || defs.date) {
        const timeZone = this.timeZone !== undefined ? this.timeZone : this.dateTime.getTimeZone();
        const data = {
          type: type,
          attribute: attribute,
          value: defs.value
        };
        if (defs.dateTime) {
          data.dateTime = true;
        }
        if (defs.date) {
          data.date = true;
        }
        if (timeZone) {
          data.timeZone = timeZone;
        }
        return data;
      }
      value = defs.value;
      return {
        type: type,
        attribute: attribute,
        value: value
      };
    }

    /**
     * Load stored data.
     *
     * @returns {module:search-manager}
     */
    loadStored() {
      this.data = this.getFromStorageIfEnabled() || Espo.Utils.clone(this.defaultData) || Espo.Utils.clone(this.emptyData);
      this.sanitizeData();
      return this;
    }

    /**
     * @private
     * @return {module:search-manager~data|null}
     */
    getFromStorageIfEnabled() {
      if (!this.useStorage) {
        return null;
      }
      return this.storage.get(`${this.storageKey}Search`, this.scope);
    }

    /**
     * Get data.
     *
     * @returns {module:search-manager~data}
     */
    get() {
      return this.data;
    }

    /**
     * Set advanced filters.
     *
     * @param {Object.<string, module:search-manager~advancedFilter>} advanced Advanced filters.
     *   Pairs of field => advancedFilter.
     */
    setAdvanced(advanced) {
      this.data = Espo.Utils.clone(this.data);
      this.data.advanced = advanced;
    }

    /**
     * Set bool filters.
     *
     * @param {Record.<string, boolean>|string[]} bool Bool filters.
     */
    setBool(bool) {
      if (Array.isArray(bool)) {
        const data = {};
        bool.forEach(it => data[it] = true);
        bool = data;
      }
      this.data = Espo.Utils.clone(this.data);
      this.data.bool = bool;
    }

    /**
     * Set a primary filter.
     *
     * @param {string} primary A filter.
     */
    setPrimary(primary) {
      this.data = Espo.Utils.clone(this.data);
      this.data.primary = primary;
    }

    /**
     * Set data.
     *
     * @param {module:search-manager~data} data Data.
     */
    set(data) {
      this.data = data;
      if (this.useStorage) {
        data = Espo.Utils.clone(data);
        delete data['textFilter'];
        this.storage.set(this.storageKey + 'Search', this.scope, data);
      }
    }
    clearPreset() {
      delete this.data.presetName;
    }

    /**
     * Empty data.
     */
    empty() {
      this.data = Espo.Utils.clone(this.emptyData);
      if (this.useStorage) {
        this.storage.clear(this.storageKey + 'Search', this.scope);
      }
    }

    /**
     * Reset.
     */
    reset() {
      if (this.emptyOnReset) {
        this.empty();
        return;
      }
      this.data = Espo.Utils.clone(this.defaultData) || Espo.Utils.clone(this.emptyData);
      if (this.useStorage) {
        this.storage.clear(this.storageKey + 'Search', this.scope);
      }
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Set a time zone. Null will not add a time zone.
     *
     * @type {string|null}
     * @internal Is used. Do not remove.
     */
    setTimeZone(timeZone) {
      this.timeZone = timeZone;
    }
  }
  var _default = _exports.default = SearchManager;
});

define("models/user", ["exports", "model"], function (_exports, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 models/user */

  /**
   * A user.
   */
  class User extends _model.default {
    name = 'User';
    entityType = 'User';
    urlRoot = 'User';

    /**
     * Is admin.
     *
     * @returns {boolean}
     */
    isAdmin() {
      return this.get('type') === 'admin' || this.isSuperAdmin();
    }

    /**
     * Is portal.
     *
     * @returns {boolean}
     */
    isPortal() {
      return this.get('type') === 'portal';
    }

    /**
     * Is API.
     *
     * @returns {boolean}
     */
    isApi() {
      return this.get('type') === 'api';
    }

    /**
     * Is regular.
     *
     * @returns {boolean}
     */
    isRegular() {
      return this.get('type') === 'regular';
    }

    /**
     * Is system.
     *
     * @returns {boolean}
     */
    isSystem() {
      return this.get('type') === 'system';
    }

    /**
     * Is super-admin.
     *
     * @returns {boolean}
     */
    isSuperAdmin() {
      return this.get('type') === 'super-admin';
    }
  }
  var _default = _exports.default = User;
});

define("models/settings", ["exports", "model"], function (_exports, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 models/settings */

  /**
   * A config.
   */
  class Settings extends _model.default {
    name = 'Settings';
    entityType = 'Settings';
    urlRoot = 'Settings';

    /**
     * Load.
     *
     * @returns {Promise}
     */
    load() {
      return new Promise(resolve => {
        this.fetch().then(() => resolve());
      });
    }

    /**
     * Get a value by a path.
     *
     * @param {string[]} path A path.
     * @returns {*} Null if not set.
     */
    getByPath(path) {
      if (!path.length) {
        return null;
      }
      let p;
      for (let i = 0; i < path.length; i++) {
        const item = path[i];
        if (i === 0) {
          p = this.get(item);
        } else {
          if (item in p) {
            p = p[item];
          } else {
            return null;
          }
        }
        if (i === path.length - 1) {
          return p;
        }
        if (p === null || typeof p !== 'object') {
          return null;
        }
      }
    }
  }
  var _default = _exports.default = Settings;
});

define("models/preferences", ["exports", "model"], function (_exports, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 models/preferences */

  /**
   * User preferences.
   */
  class Preferences extends _model.default {
    name = 'Preferences';
    entityType = 'Preferences';
    urlRoot = 'Preferences';

    /**
     * @private
     * @type {import('models/settings').default}
     */
    settings;

    /**
     * Get dashlet options.
     *
     * @param {string} id A dashlet ID.
     * @returns {Object|null}
     */
    getDashletOptions(id) {
      const value = this.get('dashletsOptions') || {};
      return value[id] || null;
    }

    /**
     * Whether a user is portal.
     *
     * @returns {boolean}
     */
    isPortal() {
      return this.get('isPortalUser');
    }

    /**
     * @internal
     * @param {import('models/settings').default} settings
     */
    setSettings(settings) {
      this.settings = settings;
    }
  }
  var _default = _exports.default = Preferences;
});

define("helpers/record-modal", ["exports", "di", "metadata", "acl-manager", "router", "helpers/site/modal-bar-provider", "views/modals/edit", "language"], function (_exports, _di, _metadata, _aclManager, _router, _modalBarProvider, _edit, _language) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _metadata = _interopRequireDefault(_metadata);
  _aclManager = _interopRequireDefault(_aclManager);
  _router = _interopRequireDefault(_router);
  _modalBarProvider = _interopRequireDefault(_modalBarProvider);
  _edit = _interopRequireDefault(_edit);
  _language = _interopRequireDefault(_language);
  let _init_metadata, _init_extra_metadata, _init_acl, _init_extra_acl, _init_router, _init_extra_router, _init_language, _init_extra_language, _init_modalBarProvider, _init_extra_modalBarProvider;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * A record-modal helper. Use to render the quick view and quick edit modals.
   */
  class RecordModalHelper {
    static #_ = [_init_metadata, _init_extra_metadata, _init_acl, _init_extra_acl, _init_router, _init_extra_router, _init_language, _init_extra_language, _init_modalBarProvider, _init_extra_modalBarProvider] = _applyDecs(this, [], [[(0, _di.inject)(_metadata.default), 0, "metadata"], [(0, _di.inject)(_aclManager.default), 0, "acl"], [(0, _di.inject)(_router.default), 0, "router"], [(0, _di.inject)(_language.default), 0, "language"], [(0, _di.inject)(_modalBarProvider.default), 0, "modalBarProvider"]]).e;
    constructor() {
      _init_extra_modalBarProvider(this);
    }
    /**
     * @private
     * @type {Metadata}
     */
    metadata = _init_metadata(this);

    /**
     * @private
     * @type {AclManager}
     */
    acl = (_init_extra_metadata(this), _init_acl(this));

    /**
     * @private
     * @type {Router}
     */
    router = (_init_extra_acl(this), _init_router(this));

    /**
     * @private
     * @type {Language}
     */
    language = (_init_extra_router(this), _init_language(this));

    /**
     * @private
     * @type {ModalBarProvider}
     */
    modalBarProvider = (_init_extra_language(this), _init_modalBarProvider(this));

    /**
     * Show the 'detail' modal.
     *
     * @param {import('view').default} view
     * @param {{
     *   id: string,
     *   entityType: string,
     *   model?: import('model').default,
     *   editDisabled?: boolean,
     *   removeDisabled?: boolean,
     *   fullFormDisabled?: boolean,
     *   rootUrl?: string,
     *   fullFormUrl?: string,
     *   layoutName?: string,
     *   afterSave?: function(import('model').default, {bypassClose: boolean} & Record),
     *   afterDestroy?: function(import('model').default),
     *   beforeRender?: function(import('views/modals/detail').default),
     *   onClose?: function(),
     *   collapseDisabled: boolean,
     * }} params
     * @return {Promise<import('views/modals/detail').default>}
     */
    async showDetail(view, params) {
      const id = params.id;
      // noinspection JSUnresolvedReference
      const entityType = params.entityType || params.scope;
      const model = params.model;
      if (!id || !entityType) {
        console.error("Bad data.");
        return Promise.reject();
      }
      if (model && !this.acl.checkScope(model.entityType, 'read')) {
        return Promise.reject();
      }
      const viewName = this.metadata.get(`clientDefs.${entityType}.modalViews.detail`) || 'views/modals/detail';
      Espo.Ui.notifyWait();

      /** @type {module:views/modals/detail~options & module:views/modal~Options} */
      const options = {
        entityType: entityType,
        model: model,
        id: id,
        quickEditDisabled: params.editDisabled,
        rootUrl: params.rootUrl,
        removeDisabled: params.removeDisabled,
        layoutName: params.layoutName,
        fullFormDisabled: params.fullFormDisabled,
        fullFormUrl: params.fullFormUrl,
        collapseDisabled: params.collapseDisabled
      };
      Espo.Ui.notifyWait();
      const modalView = /** @type {import('views/modals/detail').default} */
      await view.createView('modal', viewName, options);

      // @todo Revise.
      view.listenToOnce(modalView, 'remove', () => view.clearView('modal'));
      if (params.afterSave) {
        modalView.listenTo(modalView, 'after:save', (model, /** Record */o) => {
          params.afterSave(model, {
            ...o
          });
        });
      }
      if (params.afterDestroy) {
        modalView.listenToOnce(modalView, 'after:destroy', model => params.afterDestroy(model));
      }
      if (params.beforeRender) {
        params.beforeRender(modalView);
      }
      if (params.onClose) {
        view.listenToOnce(modalView, 'close', () => params.onClose());
      }
      await modalView.render();
      Espo.Ui.notify();
      return modalView;
    }

    /**
     * Show the 'edit' modal.
     *
     * @param {import('view').default} view
     * @param {{
     *   entityType: string,
     *   id?: string,
     *   model?: import('model').default,
     *   rootUrl?: string,
     *   fullFormDisabled?: boolean,
     *   fullFormUrl?: string,
     *   returnUrl?: string,
     *   layoutName?: string,
     *   afterSave?: function(import('model').default, {bypassClose: boolean} & Record),
     *   beforeRender?: function(import('views/modals/edit').default),
     *   onClose?: function(),
     *   returnDispatchParams?: {
     *       controller: string,
     *       action: string|null,
     *       options: {isReturn?: boolean} & Record,
     *   },
     *   collapseDisabled: boolean,
     * }} params
     * @return {Promise<import('views/modals/edit').default>}
     * @since 9.1.0
     */
    async showEdit(view, params) {
      const id = params.id;
      const entityType = params.entityType;
      const model = params.model;
      if (this.modalBarProvider.get()) {
        const barView = this.modalBarProvider.get();
        const foundModalView = barView.getModalViewList().find(view => {
          return view instanceof _edit.default && view.id === id && view.entityType === entityType;
        });
        if (foundModalView) {
          const message = this.language.translate('sameRecordIsAlreadyBeingEdited', 'messages');
          Espo.Ui.warning(message);
          throw new Error();
        }
      }
      const viewName = this.metadata.get(`clientDefs.${entityType}.modalViews.edit`) || 'views/modals/edit';

      /** @type {module:views/modals/edit~options & module:views/modal~Options} */
      const options = {
        entityType: entityType,
        id: id,
        model: model,
        fullFormDisabled: params.fullFormDisabled,
        returnUrl: params.returnUrl || this.router.getCurrentUrl(),
        returnDispatchParams: params.returnDispatchParams,
        layoutName: params.layoutName,
        fullFormUrl: params.fullFormUrl,
        collapseDisabled: params.collapseDisabled
      };
      if (params.rootUrl) {
        options.rootUrl = params.rootUrl;
      }
      Espo.Ui.notifyWait();
      const modalView = /** @type {import('views/modals/edit').default} */
      await view.createView('modal', viewName, options);

      // @todo Revise.
      modalView.listenToOnce(modalView, 'remove', () => view.clearView('modal'));
      if (params.afterSave) {
        modalView.listenTo(modalView, 'after:save', (model, /** Record */o) => {
          params.afterSave(model, {
            ...o
          });
        });
      }
      if (params.beforeRender) {
        params.beforeRender(modalView);
      }
      if (params.onClose) {
        view.listenToOnce(modalView, 'close', () => params.onClose());
      }
      await modalView.render();
      Espo.Ui.notify();
      return modalView;
    }

    /**
     * Show the 'create' modal.
     *
     * @param {import('view').default} view
     * @param {{
     *   entityType: string,
     *   rootUrl?: string,
     *   fullFormDisabled?: boolean,
     *   fullFormUrl?: string,
     *   returnUrl?: string,
     *   relate?: model:model~setRelateItem | model:model~setRelateItem[],
     *   attributes?: Record.<string, *>,
     *   afterSave?: function(import('model').default, {bypassClose: boolean} & Record),
     *   beforeRender?: function(import('views/modals/edit').default),
     *   onClose?: function(),
     *   focusForCreate?: boolean,
     *   layoutName?: string,
     *   returnDispatchParams?: {
     *       controller: string,
     *       action: string|null,
     *       options: {isReturn?: boolean} & Record,
     *   },
     *   collapseDisabled?: boolean,
     * }} params
     * @return {Promise<import('views/modals/edit').default>}
     * @since 9.1.0
     */
    async showCreate(view, params) {
      const entityType = params.entityType;
      const viewName = this.metadata.get(`clientDefs.${entityType}.modalViews.edit`) || 'views/modals/edit';

      /** @type {module:views/modals/edit~options & module:views/modal~Options} */
      const options = {
        entityType: entityType,
        fullFormDisabled: params.fullFormDisabled,
        returnUrl: params.returnUrl || this.router.getCurrentUrl(),
        returnDispatchParams: params.returnDispatchParams,
        relate: params.relate,
        attributes: params.attributes,
        focusForCreate: params.focusForCreate,
        layoutName: params.layoutName,
        fullFormUrl: params.fullFormUrl,
        collapseDisabled: params.collapseDisabled
      };
      if (params.rootUrl) {
        options.rootUrl = params.rootUrl;
      }
      Espo.Ui.notifyWait();
      const modalView = /** @type {import('views/modals/edit').default} */
      await view.createView('modal', viewName, options);

      // @todo Revise.
      modalView.listenToOnce(modalView, 'remove', () => view.clearView('modal'));
      if (params.afterSave) {
        modalView.listenTo(modalView, 'after:save', (model, /** Record */o) => {
          params.afterSave(model, {
            ...o
          });
        });
      }
      if (params.beforeRender) {
        params.beforeRender(modalView);
      }
      if (params.onClose) {
        view.listenToOnce(modalView, 'close', () => params.onClose());
      }
      await modalView.render();
      Espo.Ui.notify();
      return modalView;
    }
  }
  var _default = _exports.default = RecordModalHelper;
});

define("view-record-helper", ["exports", "bullbone"], function (_exports, _bullbone) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 view-record-helper */

  /**
   * @mixes Bull.Events
   */
  class ViewRecordHelper {
    /**
     * @param {Object.<string, *>} [defaultFieldStates] Default field states.
     * @param {Object.<string, *>} [defaultPanelStates] Default panel states.
     */
    constructor(defaultFieldStates, defaultPanelStates) {
      /**
       * @private
       * @type {Object}
       */
      this.defaultFieldStates = defaultFieldStates || {};
      /**
       * @private
       * @type {Object}
       */
      this.defaultPanelStates = defaultPanelStates || {};
      /** @private */
      this.fieldStateMap = {};
      /** @private */
      this.panelStateMap = {};
      /** @private */
      this.hiddenFields = {};
      /** @private */
      this.hiddenPanels = {};
      /** @private */
      this.fieldOptionListMap = {};
    }

    /**
     * Get hidden fields.
     *
     * @returns {Object.<string, boolean>}
     */
    getHiddenFields() {
      return this.hiddenFields;
    }

    /**
     * Get hidden panels.
     *
     * @returns {Object.<string,boolean>}
     */
    getHiddenPanels() {
      return this.hiddenPanels;
    }

    /**
     * Set a field-state parameter.
     *
     * @param {string} field A field name.
     * @param {string|'hidden'} name A parameter.
     * @param {*} value A value.
     */
    setFieldStateParam(field, name, value) {
      switch (name) {
        case 'hidden':
          if (value) {
            this.hiddenFields[field] = true;
          } else {
            delete this.hiddenFields[field];
          }
          break;
      }
      this.fieldStateMap[field] = this.fieldStateMap[field] || {};
      this.fieldStateMap[field][name] = value;
      this.trigger('field-change');
    }

    /**
     * Get a field-state parameter.
     *
     * @param {string} field A field name.
     * @param {string} name A parameter.
     * @returns {*} A value.
     */
    getFieldStateParam(field, name) {
      if (field in this.fieldStateMap) {
        if (name in this.fieldStateMap[field]) {
          return this.fieldStateMap[field][name];
        }
      }
      if (name in this.defaultFieldStates) {
        return this.defaultFieldStates[name];
      }
      return null;
    }

    /**
     * Set a panel-state parameter.
     *
     * @param {string} panel A panel name.
     * @param {string|'hidden'} name A parameter.
     * @param {*} value A value.
     */
    setPanelStateParam(panel, name, value) {
      switch (name) {
        case 'hidden':
          if (value) {
            this.hiddenPanels[panel] = true;
          } else {
            delete this.hiddenPanels[panel];
          }
          break;
      }
      this.panelStateMap[panel] = this.panelStateMap[panel] || {};
      this.panelStateMap[panel][name] = value;
    }

    /**
     * Get a panel-state parameter.
     *
     * @param {string} panel A panel name.
     * @param {string|'hidden'} name A parameter.
     * @returns {*} A value.
     */
    getPanelStateParam(panel, name) {
      if (panel in this.panelStateMap) {
        if (name in this.panelStateMap[panel]) {
          return this.panelStateMap[panel][name];
        }
      }
      if (name in this.defaultPanelStates) {
        return this.defaultPanelStates[name];
      }
      return null;
    }

    /**
     * Set a field option list.
     *
     * @param {string} field A field name.
     * @param {string[]} list An option list.
     */
    setFieldOptionList(field, list) {
      this.fieldOptionListMap[field] = list;
    }

    /**
     * Clear a field option list.
     *
     * @param {string} field A field name.
     */
    clearFieldOptionList(field) {
      delete this.fieldOptionListMap[field];
    }

    /**
     * Get a field option list.
     *
     * @param {string} field A field name.
     * @returns {string[]|null} Null if not set.
     */
    getFieldOptionList(field) {
      return this.fieldOptionListMap[field] || null;
    }

    /**
     * Whether a field option list is set.
     *
     * @param {string} field A field name.
     * @returns {boolean}
     */
    hasFieldOptionList(field) {
      return field in this.fieldOptionListMap;
    }
  }
  Object.assign(ViewRecordHelper.prototype, _bullbone.Events);
  var _default = _exports.default = ViewRecordHelper;
});

define("dynamic-logic", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 dynamic-logic */

  /**
   * Dynamic logic. Handles form appearance and behaviour depending on conditions.
   *
   * @internal Instantiated in advanced-pack.
   */
  class DynamicLogic {
    /**
     * @param {Object} defs Definitions.
     * @param {module:views/record/base} recordView A record view.
     */
    constructor(defs, recordView) {
      /**
       * @type {Object} Definitions.
       * @private
       */
      this.defs = defs || {};

      /**
       *
       * @type {module:views/record/base}
       * @private
       */
      this.recordView = recordView;

      /**
       * @type {string[]}
       * @private
       */
      this.fieldTypeList = ['visible', 'required', 'readOnlySaved', 'readOnly'];

      /**
       * @type {string[]}
       * @private
       */
      this.panelTypeList = ['visible', 'styled'];
    }

    /**
     * Process.
     */
    process() {
      const fields = this.defs.fields || {};
      Object.keys(fields).forEach(field => {
        /** @type {Record} */
        const item = fields[field] || {};
        let readOnlyIsProcessed = false;
        this.fieldTypeList.forEach(type => {
          if (!(type in item) || !item[type]) {
            return;
          }

          /** @type {Record} */
          const typeItem = item[type] || {};
          if (!typeItem.conditionGroup) {
            return;
          }
          if (type === 'readOnlySaved') {
            if (this.recordView.model.isNew()) {
              return;
            }
            if (this.checkConditionGroupInternal(typeItem.conditionGroup, 'and', true)) {
              this.makeFieldReadOnlyTrue(field);
              readOnlyIsProcessed = true;
            } else {
              this.makeFieldReadOnlyFalse(field);
            }
            return;
          }
          const result = this.checkConditionGroupInternal(typeItem.conditionGroup);
          if (type === 'required' && !readOnlyIsProcessed) {
            result ? this.makeFieldRequiredTrue(field) : this.makeFieldRequiredFalse(field);
            return;
          }
          if (type === 'readOnly') {
            result ? this.makeFieldReadOnlyTrue(field) : this.makeFieldReadOnlyFalse(field);
            return;
          }
          if (type === 'visible') {
            result ? this.makeFieldVisibleTrue(field) : this.makeFieldVisibleFalse(field);
          }
        });
      });
      const panels = this.defs.panels || {};
      Object.keys(panels).forEach(panel => {
        this.panelTypeList.forEach(type => {
          this.processPanel(panel, type);
        });
      });
      const options = this.defs.options || {};
      Object.keys(options).forEach(field => {
        const itemList = options[field];
        if (!options[field]) {
          return;
        }
        let isMet = false;
        for (const i in itemList) {
          const item = itemList[i];
          if (this.checkConditionGroupInternal(item.conditionGroup)) {
            this.setOptionList(field, item.optionList || []);
            isMet = true;
            break;
          }
        }
        if (!isMet) {
          this.resetOptionList(field);
        }
      });
    }

    /**
     * @param {string} panel A panel name.
     * @param {string} type A type.
     * @private
     */
    processPanel(panel, type) {
      const panels = this.defs.panels || {};
      const item = panels[panel] || {};
      if (!(type in item)) {
        return;
      }
      const typeItem = item[type] || {};
      if (!typeItem.conditionGroup) {
        return;
      }
      const result = this.checkConditionGroupInternal(typeItem.conditionGroup);
      if (type === 'visible') {
        result ? this.makePanelVisibleTrue(panel) : this.makePanelVisibleFalse(panel);
      }
      if (type === 'styled') {
        result ? this.makePanelStyledTrue(panel) : this.makePanelStyledFalse(panel);
      }
    }

    /**
     * Check a condition group.
     *
     * @param {Object} data A condition group.
     * @returns {boolean}
     */
    checkConditionGroup(data) {
      return this.checkConditionGroupInternal(data);
    }

    /**
     * @private
     * @param {Object} data
     * @param {'and'|'or'|'not'} [type='and']
     * @param {boolean} [preSave]
     * @returns {boolean}
     */
    checkConditionGroupInternal(data, type) {
      let preSave = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
      type = type || 'and';
      let list;
      let result = false;
      if (type === 'and') {
        list = data || [];
        result = true;
        for (const i in list) {
          if (!this.checkCondition(list[i], preSave)) {
            result = false;
            break;
          }
        }
      } else if (type === 'or') {
        list = data || [];
        for (const i in list) {
          if (this.checkCondition(list[i], preSave)) {
            result = true;
            break;
          }
        }
      } else if (type === 'not') {
        if (data) {
          result = !this.checkCondition(data, preSave);
        }
      }
      return result;
    }

    /**
     * @private
     * @param {string} attribute
     * @param {boolean} preSave
     * @return {*}
     */
    getAttributeValue(attribute, preSave) {
      if (attribute.startsWith('$')) {
        if (attribute === '$user.id') {
          return this.recordView.getUser().id;
        }
        if (attribute === '$user.teamsIds') {
          return this.recordView.getUser().getTeamIdList();
        }
      }
      if (preSave) {
        return this.recordView.attributes[attribute];
      }
      if (!this.recordView.model.has(attribute)) {
        return undefined;
      }
      return this.recordView.model.get(attribute);
    }

    /**
     * @private
     * @param {Object} defs
     * @param {boolean} preSave
     * @returns {boolean}
     */
    checkCondition(defs, preSave) {
      defs = defs || {};
      const type = defs.type || 'equals';
      if (['or', 'and', 'not'].includes(type)) {
        return this.checkConditionGroupInternal(defs.value, /** @type {'or'|'and'|'not'} */type, preSave);
      }
      const attribute = defs.attribute;
      const value = defs.value;
      if (!attribute) {
        return false;
      }
      const setValue = this.getAttributeValue(attribute, preSave);
      if (type === 'equals') {
        return setValue === value;
      }
      if (type === 'notEquals') {
        return setValue !== value;
      }
      if (type === 'isEmpty') {
        if (Array.isArray(setValue)) {
          return !setValue.length;
        }
        return setValue === null || setValue === '' || typeof setValue === 'undefined';
      }
      if (type === 'isNotEmpty') {
        if (Array.isArray(setValue)) {
          return !!setValue.length;
        }
        return setValue !== null && setValue !== '' && typeof setValue !== 'undefined';
      }
      if (type === 'isTrue') {
        return !!setValue;
      }
      if (type === 'isFalse') {
        return !setValue;
      }
      if (type === 'contains' || type === 'has') {
        if (!setValue) {
          return false;
        }
        return !!~setValue.indexOf(value);
      }
      if (type === 'notContains' || type === 'notHas') {
        if (!setValue) {
          return true;
        }
        return !~setValue.indexOf(value);
      }
      if (type === 'startsWith') {
        if (!setValue) {
          return false;
        }
        return setValue.indexOf(value) === 0;
      }
      if (type === 'endsWith') {
        if (!setValue) {
          return false;
        }
        return setValue.indexOf(value) === setValue.length - value.length;
      }
      if (type === 'matches') {
        if (!setValue) {
          return false;
        }
        const match = /^\/(.*)\/([a-z]*)$/.exec(value);
        if (!match || match.length < 2) {
          return false;
        }
        return new RegExp(match[1], match[2]).test(setValue);
      }
      if (type === 'greaterThan') {
        return setValue > value;
      }
      if (type === 'lessThan') {
        return setValue < value;
      }
      if (type === 'greaterThanOrEquals') {
        return setValue >= value;
      }
      if (type === 'lessThanOrEquals') {
        return setValue <= value;
      }
      if (type === 'in') {
        return !!~value.indexOf(setValue);
      }
      if (type === 'notIn') {
        return !~value.indexOf(setValue);
      }
      if (type === 'isToday') {
        const dateTime = this.recordView.getDateTime();
        if (!setValue) {
          return false;
        }
        if (setValue.length > 10) {
          return dateTime.toMoment(setValue).isSame(dateTime.getNowMoment(), 'day');
        }
        return dateTime.toMomentDate(setValue).isSame(dateTime.getNowMoment(), 'day');
      }
      if (type === 'inFuture') {
        const dateTime = this.recordView.getDateTime();
        if (!setValue) {
          return false;
        }
        if (setValue.length > 10) {
          return dateTime.toMoment(setValue).isAfter(dateTime.getNowMoment(), 'second');
        }
        return dateTime.toMomentDate(setValue).isAfter(dateTime.getNowMoment(), 'day');
      }
      if (type === 'inPast') {
        const dateTime = this.recordView.getDateTime();
        if (!setValue) {
          return false;
        }
        if (setValue.length > 10) {
          return dateTime.toMoment(setValue).isBefore(dateTime.getNowMoment(), 'second');
        }
        return dateTime.toMomentDate(setValue).isBefore(dateTime.getNowMoment(), 'day');
      }
      return false;
    }

    /**
     * @param {string} field
     * @param {string[]} optionList
     * @private
     */
    setOptionList(field, optionList) {
      this.recordView.setFieldOptionList(field, optionList);
    }

    /**
     * @param {string} field
     * @private
     */
    resetOptionList(field) {
      this.recordView.resetFieldOptionList(field);
    }

    /**
     * @param {string} field
     * @private
     */
    makeFieldVisibleTrue(field) {
      this.recordView.showField(field);
    }

    /**
     * @param {string} field
     * @private
     */
    makeFieldVisibleFalse(field) {
      this.recordView.hideField(field);
    }

    /**
     * @param {string} field
     * @private
     */
    makeFieldRequiredTrue(field) {
      this.recordView.setFieldRequired(field);
    }

    /**
     * @param {string} field
     * @private
     */
    makeFieldRequiredFalse(field) {
      this.recordView.setFieldNotRequired(field);
    }

    /**
     * @param {string} field
     * @private
     */
    makeFieldReadOnlyTrue(field) {
      this.recordView.setFieldReadOnly(field);
    }

    /**
     * @param {string} field
     * @private
     */
    makeFieldReadOnlyFalse(field) {
      this.recordView.setFieldNotReadOnly(field);
    }

    /**
     * @param {string} panel
     * @private
     */
    makePanelVisibleTrue(panel) {
      this.recordView.showPanel(panel, 'dynamicLogic');
    }

    /**
     * @param {string} panel
     * @private
     */
    makePanelVisibleFalse(panel) {
      this.recordView.hidePanel(panel, false, 'dynamicLogic');
    }

    /**
     * @param {string} panel
     * @private
     */
    makePanelStyledTrue(panel) {
      this.recordView.stylePanel(panel);
    }

    /**
     * @param {string} panel
     * @private
     */
    makePanelStyledFalse(panel) {
      this.recordView.unstylePanel(panel);
    }

    /**
     * Add a panel-visible condition.
     *
     * @param {string} name A panel name.
     * @param {Object} item Condition definitions.
     */
    addPanelVisibleCondition(name, item) {
      this.defs.panels = this.defs.panels || {};
      this.defs.panels[name] = this.defs.panels[name] || {};
      this.defs.panels[name].visible = item;
      this.processPanel(name, 'visible');
    }

    /**
     * Add a panel-styled condition.
     *
     * @param {string} name A panel name.
     * @param {Object} item Condition definitions.
     */
    addPanelStyledCondition(name, item) {
      this.defs.panels = this.defs.panels || {};
      this.defs.panels[name] = this.defs.panels[name] || {};
      this.defs.panels[name].styled = item;
      this.processPanel(name, 'styled');
    }
  }
  var _default = _exports.default = DynamicLogic;
});

define("views/modals/select-records", ["exports", "views/modal", "search-manager", "helpers/record-modal"], function (_exports, _modal, _searchManager, _recordModal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _searchManager = _interopRequireDefault(_searchManager);
  _recordModal = _interopRequireDefault(_recordModal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/modals/select-records */

  /**
   * A select-records modal.
   */
  class SelectRecordsModalView extends _modal.default {
    template = 'modals/select-records';
    cssName = 'select-modal';
    className = 'dialog dialog-record';
    multiple = false;
    createButton = true;
    searchPanel = true;
    scope = '';
    noCreateScopeList = ['User', 'Team', 'Role', 'Portal'];
    layoutName = 'listSmall';

    /** @inheritDoc */
    shortcutKeys = {
      /** @this SelectRecordsModalView */
      'Control+Enter': function (e) {
        this.handleShortcutKeyCtrlEnter(e);
      },
      /** @this SelectRecordsModalView */
      'Control+Space': function (e) {
        this.handleShortcutKeyCtrlSpace(e);
      },
      /** @this SelectRecordsModalView */
      'Control+Slash': function (e) {
        this.handleShortcutKeyCtrlSlash(e);
      },
      /** @this SelectRecordsModalView */
      'Control+Comma': function (e) {
        this.handleShortcutKeyCtrlComma(e);
      },
      /** @this SelectRecordsModalView */
      'Control+Period': function (e) {
        this.handleShortcutKeyCtrlPeriod(e);
      }
    };
    events = {
      /** @this SelectRecordsModalView */
      'click button[data-action="create"]': function () {
        this.create();
      },
      /** @this SelectRecordsModalView */
      'click .list a': function (e) {
        e.preventDefault();
      }
    };

    /**
     * @typedef {Object} module:views/modals/select-records~Options
     * @property {string} entityType An entity type.
     * @property {Object.<string, module:search-manager~advancedFilter>} [filters] Filters.
     * @property {string[]} [boolFilterList] Bool filters.
     * @property {string} [primaryFilterName] A primary filter.
     * @property {string[]} [filterList] A filter list.
     * @property {string} [layoutName] A layout name.
     * @property {boolean} [multiple] Allow select multiple.
     * @property {boolean} [createButton] A create button.
     * @property {boolean} [massRelateEnabled] Mass-relate.
     * @property {string} [orderBy] An order-by.
     * @property {'asc'|'desc'} [orderDirection] An order direction.
     * @property {boolean} [forceSelectAllAttributes] Force select all attributes.
     * @property {string[]} [mandatorySelectAttributeList] Mandatory attributes to select.
     * @property {function(): Promise<Record>} [createAttributesProvider] Create-attributes provider.
     * @property {Record} [createAttributes] Create-attributes.
     * @property {function(import('model').default[])} [onSelect] On record select. As of 9.0.0.
     * @property {function({where: Record[], searchParams: module:collection~Data})} [onMassSelect]
     *     On record select. As of 9.1.0.
     * @property {function()} [onCreate] On create click. As of 9.0.5.
     * @property {boolean} [searchPanelDisabled] Disable the search panel.
     */

    /**
     *
     * @param {module:views/modals/select-records~Options | module:views/modal~Options} options
     */
    constructor(options) {
      super(options);
      if (options.onSelect) {
        /** @private */
        this.onSelect = options.onSelect;
      }
      if (options.onMassSelect) {
        /** @private */
        this.onMassSelect = options.onMassSelect;
      }
      if (options.onCreate) {
        /** @private */
        this.onCreate = options.onCreate;
      }

      /** @private */
      this.createAttributesProvider = options.createAttributesProvider;
      this.options = options;
    }
    data() {
      return {
        createButton: this.createButton,
        createText: this.translate('Create ' + this.scope, 'labels', this.scope)
      };
    }
    setup() {
      this.filters = this.options.filters || {};
      this.boolFilterList = this.options.boolFilterList;
      this.primaryFilterName = this.options.primaryFilterName || null;
      this.filterList = this.options.filterList || this.filterList || null;
      this.layoutName = this.options.layoutName || this.layoutName;
      if ('multiple' in this.options) {
        this.multiple = this.options.multiple;
      }
      if ('createButton' in this.options) {
        this.createButton = this.options.createButton;
      }
      this.massRelateEnabled = this.options.massRelateEnabled;
      this.buttonList = [{
        name: 'cancel',
        label: 'Cancel'
      }];
      if (this.multiple) {
        this.buttonList.unshift({
          name: 'select',
          style: 'danger',
          label: 'Select',
          disabled: true,
          title: 'Ctrl+Enter'
        });
      }

      // noinspection JSUnresolvedReference
      this.scope = this.entityType = this.options.scope || this.scope || this.options.entityType;
      const orderBy = this.options.orderBy || this.getMetadata().get(['clientDefs', this.scope, 'selectRecords', 'orderBy']);
      const order = this.options.orderDirection || this.getMetadata().get(['clientDefs', this.scope, 'selectRecords', 'order']);
      if (orderBy) {
        this.defaultOrderBy = orderBy;
        this.defaultOrder = order || false;
      }
      if (this.noCreateScopeList.indexOf(this.scope) !== -1) {
        this.createButton = false;
      }
      if (this.createButton) {
        if (!this.getAcl().check(this.scope, 'create') || this.getMetadata().get(['clientDefs', this.scope, 'createDisabled'])) {
          this.createButton = false;
        }
      }
      if (this.createButton) {
        this.addButton({
          name: 'create',
          position: 'right',
          onClick: () => this.create(),
          iconClass: 'fas fa-plus fa-sm',
          label: 'Create'
        });
      }
      if (this.getMetadata().get(['clientDefs', this.scope, 'searchPanelDisabled'])) {
        this.searchPanel = false;
      }
      if (this.getUser().isPortal()) {
        if (this.getMetadata().get(['clientDefs', this.scope, 'searchPanelInPortalDisabled'])) {
          this.searchPanel = false;
        }
      }
      if (this.options.searchPanelDisabled) {
        this.searchPanel = false;
      }
      if (!this.options.headerText) {
        this.$header = $('<span>');
        this.$header.append($('<span>').text(this.translate('Select') + ' · ' + this.getLanguage().translate(this.scope, 'scopeNamesPlural')));
        this.$header.prepend(this.getHelper().getScopeColorIconHtml(this.scope));
      }
      this.waitForView('list');
      if (this.searchPanel) {
        this.waitForView('search');
      }
      this.getCollectionFactory().create(this.scope, collection => {
        collection.maxSize = this.getConfig().get('recordsPerPageSelect') || 5;
        this.collection = collection;
        if (this.defaultOrderBy) {
          this.collection.setOrder(this.defaultOrderBy, this.defaultOrder || 'asc', true);
        }
        this.setupSearch();
        this.setupList();
      });

      // If the list not yet loaded.
      this.once('close', () => {
        if (this.collection.lastSyncPromise && this.collection.lastSyncPromise.getStatus() < 4) {
          Espo.Ui.notify(false);
        }
        this.collection.abortLastFetch();
      });
    }
    setupSearch() {
      this.searchManager = new _searchManager.default(this.collection, {
        emptyOnReset: true
      });
      if (this.filters) {
        this.searchManager.setAdvanced(this.filters);
      }
      const boolFilterList = this.boolFilterList || this.getMetadata().get(`clientDefs.${this.scope}.selectDefaultFilters.boolFilterList`);
      if (boolFilterList) {
        this.searchManager.setBool(boolFilterList);
      }
      const primaryFilterName = this.primaryFilterName || this.getMetadata().get(`clientDefs.${this.scope}.selectDefaultFilters.filter`);
      if (primaryFilterName) {
        this.searchManager.setPrimary(primaryFilterName);
      }
      this.collection.where = this.searchManager.getWhere();
      if (this.searchPanel) {
        this.createView('search', 'views/record/search', {
          collection: this.collection,
          fullSelector: this.containerSelector + ' .search-container',
          searchManager: this.searchManager,
          disableSavePreset: true,
          filterList: this.filterList
        }, view => {
          this.listenTo(view, 'reset', () => {});
        });
      }
    }
    setupList() {
      const viewName = this.getMetadata().get(`clientDefs.${this.scope}.recordViews.listSelect`) || this.getMetadata().get(`clientDefs.${this.scope}.recordViews.list`) || 'views/record/list';

      /** @type {module:views/record/list~options} */
      const options = {
        collection: this.collection,
        fullSelector: this.containerSelector + ' .list-container',
        selectable: true,
        checkboxes: this.multiple,
        massActionsDisabled: true,
        rowActionsView: null,
        layoutName: this.layoutName,
        checkAllResultDisabled: !this.massRelateEnabled,
        buttonsDisabled: true,
        skipBuildRows: true,
        pagination: this.getMetadata().get(['clientDefs', this.scope, 'listPagination']) || null
      };
      const promise = this.createView('list', viewName, options, view => {
        this.listenToOnce(view, 'select', models => {
          this.trigger('select', models);
          if (this.onSelect) {
            if (!Array.isArray(models)) {
              models = [models];
            }
            this.onSelect(models);
          }
          this.close();
        });
        if (this.multiple) {
          this.listenTo(view, 'check', () => {
            if (view.checkedList.length) {
              this.enableButton('select');
            } else {
              this.disableButton('select');
            }
          });
          this.listenTo(view, 'select-all-results', () => {
            this.enableButton('select');
          });
        }
        const fetch = () => {
          this.whenRendered().then(() => {
            Espo.Ui.notifyWait();
            this.collection.fetch().then(() => Espo.Ui.notify(false));
          });
          // Timeout to make notify work.
          /*setTimeout(() => {
              Espo.Ui.notifyWait();
                this.collection.fetch()
                  .then(() => Espo.Ui.notify(false));
          }, 1);*/
        };
        if (this.options.forceSelectAllAttributes || this.forceSelectAllAttributes) {
          fetch();
          return;
        }
        view.getSelectAttributeList(selectAttributeList => {
          if (!~selectAttributeList.indexOf('name')) {
            selectAttributeList.push('name');
          }
          const mandatorySelectAttributeList = this.options.mandatorySelectAttributeList || this.mandatorySelectAttributeList || [];
          mandatorySelectAttributeList.forEach(attribute => {
            if (!~selectAttributeList.indexOf(attribute)) {
              selectAttributeList.push(attribute);
            }
          });
          if (selectAttributeList) {
            this.collection.data.select = selectAttributeList.join(',');
          }
          fetch();
        });
      });
      this.wait(promise);
    }
    async create() {
      if (this.onCreate) {
        this.onCreate();
        return;
      }

      // @todo Remove in v10.0. Kept bc.
      // noinspection JSUnresolvedReference
      if (this.options.triggerCreateEvent) {
        this.trigger('create');
        return;
      }
      let attributes;
      if (this.options.createAttributesProvider) {
        attributes = await this.createAttributesProvider();
      } else {
        attributes = this.options.createAttributes || {};
      }
      const helper = new _recordModal.default();
      await helper.showCreate(this, {
        entityType: this.entityType,
        fullFormDisabled: true,
        attributes: attributes,
        afterSave: model => {
          this.trigger('select', model);
          if (this.onSelect) {
            this.onSelect([model]);
          }
          setTimeout(() => this.close(), 10);
        },
        beforeRender: view => {
          this.listenToOnce(view, 'leave', () => {
            view.close();
            this.close();
          });
        }
      });
    }
    actionSelect() {
      if (!this.multiple) {
        return;
      }
      const listView = this.getRecordView();
      if (listView.allResultIsChecked) {
        const data = {
          massRelate: true,
          where: this.collection.getWhere(),
          searchParams: this.collection.data
        };
        this.trigger('select', data);
        if (this.onMassSelect) {
          this.onMassSelect(data);
        }
        this.close();
        return;
      }
      const list = listView.getSelected();
      if (list.length) {
        this.trigger('select', list);
        if (this.onSelect) {
          this.onSelect(list);
        }
      }
      this.close();
    }

    /**
     * @protected
     * @return {import('views/record/search').default}
     */
    getSearchView() {
      return this.getView('search');
    }

    /**
     * @protected
     * @return {module:views/record/list}
     */
    getRecordView() {
      return this.getView('list');
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlSlash(e) {
      if (!this.searchPanel) {
        return;
      }
      const $search = this.$el.find('input.text-filter').first();
      if (!$search.length) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      $search.focus();
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlEnter(e) {
      if (!this.multiple) {
        return;
      }
      if (!this.hasAvailableActionItem('select')) {
        return;
      }
      e.stopPropagation();
      e.preventDefault();
      this.actionSelect();
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlSpace(e) {
      if (!this.createButton) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      this.create();
    }

    /**
     * @protected
     */
    handleShortcutKeyCtrlComma() {
      if (!this.getSearchView()) {
        return;
      }
      this.getSearchView().selectPreviousPreset();
    }

    /**
     * @protected
     */
    handleShortcutKeyCtrlPeriod() {
      if (!this.getSearchView()) {
        return;
      }
      this.getSearchView().selectNextPreset();
    }
  }
  var _default = _exports.default = SelectRecordsModalView;
});

define("helpers/model/defaults-populator", ["exports", "di", "metadata", "view-helper", "models/settings", "models/user", "acl-manager", "models/preferences"], function (_exports, _di, _metadata, _viewHelper, _settings, _user, _aclManager, _preferences) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _metadata = _interopRequireDefault(_metadata);
  _viewHelper = _interopRequireDefault(_viewHelper);
  _settings = _interopRequireDefault(_settings);
  _user = _interopRequireDefault(_user);
  _aclManager = _interopRequireDefault(_aclManager);
  _preferences = _interopRequireDefault(_preferences);
  let _init_metadata, _init_extra_metadata, _init_viewHelper, _init_extra_viewHelper, _init_config, _init_extra_config, _init_user, _init_extra_user, _init_preferences, _init_extra_preferences, _init_acl, _init_extra_acl;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * Defaults populator.
   */
  class DefaultsPopulator {
    static #_ = [_init_metadata, _init_extra_metadata, _init_viewHelper, _init_extra_viewHelper, _init_config, _init_extra_config, _init_user, _init_extra_user, _init_preferences, _init_extra_preferences, _init_acl, _init_extra_acl] = _applyDecs(this, [], [[(0, _di.inject)(_metadata.default), 0, "metadata"], [(0, _di.inject)(_viewHelper.default), 0, "viewHelper"], [(0, _di.inject)(_settings.default), 0, "config"], [(0, _di.inject)(_user.default), 0, "user"], [(0, _di.inject)(_preferences.default), 0, "preferences"], [(0, _di.inject)(_aclManager.default), 0, "acl"]]).e;
    constructor() {
      _init_extra_acl(this);
    }
    /**
     * @private
     * @type {Metadata}
     */
    metadata = _init_metadata(this);

    /**
     * @private
     * @type {ViewHelper}
     */
    viewHelper = (_init_extra_metadata(this), _init_viewHelper(this));

    /**
     * @private
     * @type {Settings}
     */
    config = (_init_extra_viewHelper(this), _init_config(this));

    /**
     * @private
     * @type {User}
     */
    user = (_init_extra_config(this), _init_user(this));

    /**
     * @private
     * @type {Preferences}
     */
    preferences = (_init_extra_user(this), _init_preferences(this));

    /**
     * @private
     * @type {AclManager}
     */
    acl = (_init_extra_preferences(this), _init_acl(this));

    /**
     * Populate default values.
     *
     * @param {module:model} model A model.
     * @return {Promise|undefined}
     */
    populate(model) {
      model.populateDefaults();
      const defaultHash = {};
      if (!this.user.isPortal()) {
        this.prepare(model, defaultHash);
      }
      if (this.user.isPortal()) {
        this.prepareForPortal(model, defaultHash);
      }
      this.prepareFields(model, defaultHash);
      for (const attr in defaultHash) {
        if (model.has(attr)) {
          delete defaultHash[attr];
        }
      }
      model.set(defaultHash, {
        silent: true
      });
      const preparatorClass = this.metadata.get(`clientDefs.${model.entityType}.modelDefaultsPreparator`);
      if (!preparatorClass) {
        return undefined;
      }
      return Espo.loader.requirePromise(preparatorClass).then(Class => {
        /** @type {import('handlers/model/defaults-preparator').default} */
        const preparator = new Class(this.viewHelper);
        return preparator.prepare(model);
      }).then(attributes => {
        model.set(attributes, {
          silent: true
        });
      });
    }

    /**
     * @param {module:model} model
     * @param {Object.<string, *>} defaultHash
     * @private
     */
    prepare(model, defaultHash) {
      const hasAssignedUsers = model.hasField('assignedUsers') && model.getLinkParam('assignedUsers', 'entity') === 'User';
      if (model.hasField('assignedUser') || hasAssignedUsers) {
        let assignedUserField = 'assignedUser';
        if (hasAssignedUsers) {
          assignedUserField = 'assignedUsers';
        }
        if (this.toFillAssignedUser(model, assignedUserField)) {
          if (hasAssignedUsers) {
            defaultHash['assignedUsersIds'] = [this.user.id];
            defaultHash['assignedUsersNames'] = {};
            defaultHash['assignedUsersNames'][this.user.id] = this.user.get('name');
          } else {
            defaultHash['assignedUserId'] = this.user.id;
            defaultHash['assignedUserName'] = this.user.get('name');
          }
        }
      }
      const defaultTeamId = this.user.get('defaultTeamId');
      if (defaultTeamId) {
        if (model.hasField('teams') && !model.getFieldParam('teams', 'default') && Espo.Utils.lowerCaseFirst(model.getLinkParam('teams', 'relationName') || '') === 'entityTeam') {
          defaultHash['teamsIds'] = [defaultTeamId];
          defaultHash['teamsNames'] = {};
          defaultHash['teamsNames'][defaultTeamId] = this.user.get('defaultTeamName');
        }
      }
      const hasCollaborators = model.hasField('collaborators') && model.getLinkParam('collaborators', 'entity') === 'User' && this.metadata.get(`scopes.${model.entityType}.collaborators`);
      if (hasCollaborators) {
        defaultHash.collaboratorsIds = [this.user.id];
        defaultHash.collaboratorsNames = {
          [this.user.id]: this.user.attributes.name
        };
      }
    }

    /**
     *
     * @param {import('model').default} model
     * @param {string} assignedUserField
     */
    toFillAssignedUser(model, assignedUserField) {
      if (!this.preferences.get('doNotFillAssignedUserIfNotRequired')) {
        return true;
      }
      if (model.getFieldParam(assignedUserField, 'required')) {
        return true;
      }
      if (this.acl.getPermissionLevel('assignmentPermission') === 'no') {
        return true;
      }
      if (this.acl.getPermissionLevel('assignmentPermission') === 'team' && !this.user.get('defaultTeamId')) {
        return true;
      }
      if (this.acl.getLevel(model.entityType, 'read') === 'own') {
        return true;
      }
      if (!this.acl.checkField(model.entityType, assignedUserField, 'edit')) {
        return true;
      }
      return false;
    }

    /**
     * @param {module:model} model
     * @param {Object.<string, *>} defaultHash
     * @private
     */
    prepareForPortal(model, defaultHash) {
      const accountLink = this.metadata.get(`aclDefs.${model.entityType}.accountLink`);
      const contactLink = this.metadata.get(`aclDefs.${model.entityType}.contactLink`);
      if (accountLink && model.hasField(accountLink) && ['belongsTo', 'hasOne'].includes(model.getLinkType(accountLink)) && model.getLinkParam(accountLink, 'entity') === 'Account') {
        if (this.user.attributes.accountId) {
          defaultHash[accountLink + 'Id'] = this.user.attributes.accountId;
          defaultHash[accountLink + 'Name'] = this.user.attributes.accuntName;
        }
      }
      if (contactLink && model.hasField(contactLink) && ['belongsTo', 'hasOne'].includes(model.getLinkType(contactLink)) && model.getLinkParam(contactLink, 'entity') === 'Contact') {
        if (this.user.attributes.contactId) {
          defaultHash[contactLink + 'Id'] = this.user.attributes.contactId;
          defaultHash[contactLink + 'Name'] = this.user.attributes.contactName;
        }
      }
      if (accountLink && model.hasField(accountLink) && model.getLinkType(accountLink) === 'hasMany' && model.getLinkParam(accountLink, 'entity') === 'Account') {
        if (this.user.attributes.accountsIds) {
          defaultHash['accountsIds'] = [...this.user.attributes.accountsIds];
          defaultHash['accountsNames'] = {
            ...this.user.attributes.accountsNames
          };
        }
      }
      if (contactLink && model.hasField(contactLink) && model.getLinkType(contactLink) === 'hasMany' && model.getLinkParam(contactLink, 'entity') === 'Contact') {
        if (this.user.attributes.contactId) {
          defaultHash['contactsIds'] = [this.user.attributes.contactId];
          defaultHash['contactsNames'] = {
            [this.user.attributes.contactId]: this.user.attributes.contactName
          };
        }
      }
      if (model.hasField('parent') && model.getLinkType('parent') === 'belongsToParent') {
        if (!this.config.get('b2cMode')) {
          if (this.user.attributes.accountId && (model.getFieldParam('parent', 'entityList') || []).includes('Account')) {
            defaultHash['parentId'] = this.user.attributes.accountId;
            defaultHash['parentName'] = this.user.attributes.accountName;
            defaultHash['parentType'] = 'Account';
          }
        } else {
          if (this.user.attributes.contactId && (model.getFieldParam('parent', 'entityList') || []).includes('Contact')) {
            defaultHash['parentId'] = this.user.attributes.contactId;
            defaultHash['parentName'] = this.user.attributes.contactName;
            defaultHash['parentType'] = 'Contact';
          }
        }
      }
    }

    /**
     * @param {module:model} model
     * @param {Object.<string, *>} defaultHash
     * @private
     */
    prepareFields(model, defaultHash) {
      const set = (attribute, value) => {
        if (attribute in defaultHash || model.has(attribute)) {
          return;
        }
        defaultHash[attribute] = value;
      };
      model.getFieldList().forEach(field => {
        const type = model.getFieldType(field);
        if (!type) {
          return;
        }
        if (model.getFieldParam(field, 'disabled') || model.getFieldParam(field, 'utility')) {
          return;
        }
        if (type === 'enum') {
          /** @type {string[]} */
          const options = model.getFieldParam(field, 'options') || [];
          let value = options[0] || '';
          value = value !== '' ? value : null;
          if (value) {
            set(field, value);
          }
        }
      });
    }
  }
  var _default = _exports.default = DefaultsPopulator;
});

define("views/record/base", ["exports", "view", "view-record-helper", "dynamic-logic", "underscore", "jquery", "helpers/model/defaults-populator"], function (_exports, _view, _viewRecordHelper, _dynamicLogic, _underscore, _jquery, _defaultsPopulator) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _viewRecordHelper = _interopRequireDefault(_viewRecordHelper);
  _dynamicLogic = _interopRequireDefault(_dynamicLogic);
  _underscore = _interopRequireDefault(_underscore);
  _jquery = _interopRequireDefault(_jquery);
  _defaultsPopulator = _interopRequireDefault(_defaultsPopulator);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/base */

  /**
   * A base record view. To be extended.
   */
  class BaseRecordView extends _view.default {
    /**
     * A type.
     */
    type = 'edit';

    /**
     * An entity type.
     *
     * @type {string|null}
     */
    entityType = null;

    /**
     * A scope.
     *
     * @type {string|null}
     */
    scope = null;

    /**
     * Is new. Is set automatically.
     */
    isNew = false;

    /**
     * @deprecated
     * @protected
     */
    dependencyDefs = {};

    /**
     * Dynamic logic.
     *
     * @protected
     * @type {Object}
     */
    dynamicLogicDefs = {};

    /**
     * A field list.
     *
     * @protected
     */
    fieldList = null;

    /**
     * A mode.
     *
     * @type {'detail'|'edit'|null}
     */
    mode = null;

    /**
     * A last save cancel reason.
     *
     * @protected
     * @type {string|null}
     */
    lastSaveCancelReason = null;

    /**
     * Pre-save attribute values.
     *
     * @type {Record.<string, *>|undefined}
     */
    attributes;

    /**
     * A record-helper.
     *
     * @type {module:view-record-helper}
     */
    recordHelper = null;

    /** @const */
    MODE_DETAIL = 'detail';
    /** @const */
    MODE_EDIT = 'edit';

    /** @const */
    TYPE_DETAIL = 'detail';
    // noinspection JSUnusedGlobalSymbols
    /** @const  */
    TYPE_EDIT = 'edit';

    /**
     * Hide a field.
     *
     * @param {string} name A field name.
     * @param {boolean } [locked] To lock. Won't be able to un-hide.
     */
    hideField(name, locked) {
      this.recordHelper.setFieldStateParam(name, 'hidden', true);
      if (locked) {
        this.recordHelper.setFieldStateParam(name, 'hiddenLocked', true);
      }
      const processHtml = () => {
        const fieldView = this.getFieldView(name);
        if (fieldView) {
          const $field = fieldView.$el;
          const $cell = $field.closest('.cell[data-name="' + name + '"]');
          const $label = $cell.find('label.control-label[data-name="' + name + '"]');
          $field.addClass('hidden');
          $label.addClass('hidden');
          $cell.addClass('hidden-cell');
        } else {
          this.$el.find('.cell[data-name="' + name + '"]').addClass('hidden-cell');
          this.$el.find('.field[data-name="' + name + '"]').addClass('hidden');
          this.$el.find('label.control-label[data-name="' + name + '"]').addClass('hidden');
        }
      };
      if (this.isRendered()) {
        processHtml();
      } else {
        this.once('after:render', () => {
          processHtml();
        });
      }
      const view = this.getFieldView(name);
      if (view) {
        view.setDisabled(locked);
      }
    }

    /**
     * Show a field.
     *
     * @param {string} name A field name.
     */
    showField(name) {
      if (this.recordHelper.getFieldStateParam(name, 'hiddenLocked')) {
        return;
      }
      this.recordHelper.setFieldStateParam(name, 'hidden', false);
      const processHtml = () => {
        const fieldView = this.getFieldView(name);
        if (fieldView) {
          const $field = fieldView.$el;
          const $cell = $field.closest('.cell[data-name="' + name + '"]');
          const $label = $cell.find('label.control-label[data-name="' + name + '"]');
          $field.removeClass('hidden');
          $label.removeClass('hidden');
          $cell.removeClass('hidden-cell');
          return;
        }
        this.$el.find('.cell[data-name="' + name + '"]').removeClass('hidden-cell');
        this.$el.find('.field[data-name="' + name + '"]').removeClass('hidden');
        this.$el.find('label.control-label[data-name="' + name + '"]').removeClass('hidden');
      };
      if (this.isRendered()) {
        processHtml();
      } else {
        this.once('after:render', () => {
          processHtml();
        });
      }
      const view = this.getFieldView(name);
      if (view) {
        if (!view.disabledLocked) {
          view.setNotDisabled();
        }
      }
    }

    /**
     * Set a field as read-only.
     *
     * @param {string} name A field name.
     * @param {boolean } [locked] To lock. Won't be able to un-set.
     */
    setFieldReadOnly(name, locked) {
      const previousValue = this.recordHelper.getFieldStateParam(name, 'readOnly');
      this.recordHelper.setFieldStateParam(name, 'readOnly', true);
      if (locked) {
        this.recordHelper.setFieldStateParam(name, 'readOnlyLocked', true);
      }
      const view = this.getFieldView(name);
      if (view) {
        view.setReadOnly(locked).catch(() => {});
      }
      if (!previousValue) {
        this.trigger('set-field-read-only', name);
      }

      /**
       * @todo
       *   Move to fields/base. Listen to recordHelper 'field-change' (if recordHelper is available).
       *   Same for set state methods.
       *   Issue is that sometimes state is changed in between view initialization (for bottom views with fields).
       */

      if (!view && !this.isReady) {
        this.once('ready', () => {
          const view = this.getFieldView(name);
          if (view && !view.readOnly && this.recordHelper.getFieldStateParam(name, 'readOnly')) {
            view.setReadOnly(locked);
          }
        });
      }
    }

    /**
     * Set a field as not read-only.
     *
     * @param {string} name A field name.
     */
    setFieldNotReadOnly(name) {
      const previousValue = this.recordHelper.getFieldStateParam(name, 'readOnly');
      this.recordHelper.setFieldStateParam(name, 'readOnly', false);
      if (this.readOnly) {
        return;
      }
      const view = this.getFieldView(name);
      if (view && view.readOnly) {
        view.setNotReadOnly();
        if (this.mode === this.MODE_EDIT) {
          if (!view.readOnlyLocked && view.isDetailMode()) {
            view.setEditMode().then(() => view.reRender());
          }
        }
      }
      if (previousValue) {
        this.trigger('set-field-not-read-only', name);
      }
      if (!view && !this.isReady) {
        this.once('ready', () => {
          const view = this.getFieldView(name);
          if (view && view.readOnly && !this.recordHelper.getFieldStateParam(name, 'readOnly')) {
            view.setNotReadOnly();
          }
        });
      }
    }

    /**
     * Set a field as required.
     *
     * @param {string} name A field name.
     */
    setFieldRequired(name) {
      const previousValue = this.recordHelper.getFieldStateParam(name, 'required');
      this.recordHelper.setFieldStateParam(name, 'required', true);
      const view = this.getFieldView(name);
      if (view) {
        view.setRequired();
      }
      if (!previousValue) {
        this.trigger('set-field-required', name);
      }
    }

    /**
     * Set a field as not required.
     *
     * @param {string} name A field name.
     */
    setFieldNotRequired(name) {
      const previousValue = this.recordHelper.getFieldStateParam(name, 'required');
      this.recordHelper.setFieldStateParam(name, 'required', false);
      const view = this.getFieldView(name);
      if (view) {
        view.setNotRequired();
      }
      if (previousValue) {
        this.trigger('set-field-not-required', name);
      }
    }

    /**
     * Set an option list for a field.
     *
     * @param {string} name A field name.
     * @param {string[]} list Options.
     */
    setFieldOptionList(name, list) {
      const had = this.recordHelper.hasFieldOptionList(name);
      const previousList = this.recordHelper.getFieldOptionList(name);
      this.recordHelper.setFieldOptionList(name, list);
      const view = this.getFieldView(name);
      if (view) {
        if ('setOptionList' in view) {
          view.setOptionList(list);
        }
      }
      if (!had || !(0, _underscore.default)(previousList).isEqual(list)) {
        this.trigger('set-field-option-list', name, list);
      }
    }

    /**
     * Reset field options (revert to default).
     *
     * @param {string} name A field name.
     */
    resetFieldOptionList(name) {
      const had = this.recordHelper.hasFieldOptionList(name);
      this.recordHelper.clearFieldOptionList(name);
      const view = this.getFieldView(name);
      if (view) {
        if ('resetOptionList' in view) {
          view.resetOptionList();
        }
      }
      if (had) {
        this.trigger('reset-field-option-list', name);
      }
    }

    /**
     * Show a panel.
     *
     * @param {string} name A panel name.
     * @param [softLockedType] Omitted.
     */
    showPanel(name, softLockedType) {
      this.recordHelper.setPanelStateParam(name, 'hidden', false);
      if (this.isRendered()) {
        this.$el.find('.panel[data-name="' + name + '"]').removeClass('hidden');
      }
    }

    /**
     * Hide a panel.
     *
     * @param {string} name A panel name.
     * @param {boolean} [locked=false] Won't be able to un-hide.
     * @param {module:views/record/detail~panelSoftLockedType} [softLockedType='default']
     */
    hidePanel(name, locked, softLockedType) {
      this.recordHelper.setPanelStateParam(name, 'hidden', true);
      if (this.isRendered()) {
        this.$el.find('.panel[data-name="' + name + '"]').addClass('hidden');
      }
    }

    /**
     * Style a panel. Style is set in the `data-style` DOM attribute.
     *
     * @param {string} name A panel name.
     */
    stylePanel(name) {
      this.recordHelper.setPanelStateParam(name, 'styled', true);
      const process = () => {
        const $panel = this.$el.find('.panel[data-name="' + name + '"]');
        const $btn = $panel.find('> .panel-heading .btn');
        const style = $panel.attr('data-style');
        if (!style) {
          return;
        }
        $panel.removeClass('panel-default');
        $panel.addClass('panel-' + style);
        $btn.removeClass('btn-default');
        $btn.addClass('btn-' + style);
      };
      if (this.isRendered()) {
        process();
        return;
      }
      this.once('after:render', () => {
        process();
      });
    }

    /**
     * Un-style a panel.
     *
     * @param {string} name A panel name.
     */
    unstylePanel(name) {
      this.recordHelper.setPanelStateParam(name, 'styled', false);
      const process = () => {
        const $panel = this.$el.find('.panel[data-name="' + name + '"]');
        const $btn = $panel.find('> .panel-heading .btn');
        const style = $panel.attr('data-style');
        if (!style) {
          return;
        }
        $panel.removeClass('panel-' + style);
        $panel.addClass('panel-default');
        $btn.removeClass('btn-' + style);
        $btn.addClass('btn-default');
      };
      if (this.isRendered()) {
        process();
        return;
      }
      this.once('after:render', () => {
        process();
      });
    }

    /**
     * Set/unset a confirmation upon leaving the form.
     *
     * @param {boolean} value True sets a required confirmation.
     */
    setConfirmLeaveOut(value) {
      if (!this.getRouter()) {
        return;
      }
      if (value) {
        this.getRouter().addLeaveOutObject(this);
      } else {
        this.getRouter().removeLeaveOutObject(this);
      }
    }

    /**
     * Get field views.
     *
     * @param {boolean} [withHidden] With hidden.
     * @return {Object.<string, module:views/fields/base>}
     */
    getFieldViews(withHidden) {
      const fields = {};
      this.fieldList.forEach(item => {
        const view = this.getFieldView(item);
        if (view) {
          fields[item] = view;
        }
      });
      return fields;
    }

    /**
     * Get a field view.
     *
     * @param {string} name A field name.
     * @return {module:views/fields/base|null}
     */
    getFieldView(name) {
      /** @type {module:views/fields/base|null} */
      let view = this.getView(name + 'Field') || null;

      // @todo Remove.
      if (!view) {
        view = this.getView(name) || null;
      }
      return view;
    }

    /**
     * @deprecated Use `getFieldView`.
     * @return {module:views/fields/base|null}
     */
    getField(name) {
      return this.getFieldView(name);
    }

    /**
     * Get a field list.
     *
     * @return {string[]}
     */
    getFieldList() {
      return Object.keys(this.getFieldViews());
    }

    /**
     * Get a field view list.
     *
     * @return {module:views/fields/base[]}
     */
    getFieldViewList() {
      return this.getFieldList().map(field => this.getFieldView(field)).filter(view => view !== null);
    }

    /**
     * @inheritDoc
     */
    data() {
      return {
        scope: this.scope,
        entityType: this.entityType,
        hiddenPanels: this.recordHelper.getHiddenPanels(),
        hiddenFields: this.recordHelper.getHiddenFields()
      };
    }

    /**
     * @todo Remove.
     * @private
     */
    handleDataBeforeRender(data) {
      this.getFieldList().forEach(field => {
        const viewKey = field + 'Field';
        data[field] = data[viewKey];
      });
    }

    /**
     * @inheritDoc
     * Warning. Is not called by record/detail.
     */
    setup() {
      if (typeof this.model === 'undefined') {
        throw new Error('Model has not been injected into record view.');
      }

      /** @type {module:view-record-helper} */
      this.recordHelper = this.options.recordHelper || new _viewRecordHelper.default();
      this.dynamicLogicDefs = this.options.dynamicLogicDefs || this.dynamicLogicDefs;
      this.on('remove', () => {
        if (this.isChanged) {
          this.resetModelChanges();
        }
        this.setIsNotChanged();
      });
      this.entityType = this.model.entityType || this.model.name || 'Common';
      this.scope = this.options.scope || this.entityType;
      this.fieldList = this.options.fieldList || this.fieldList || [];
      this.numId = Math.floor(Math.random() * 10000 + 1);
      this.id = Espo.Utils.toDom(this.entityType) + '-' + Espo.Utils.toDom(this.type) + '-' + this.numId;
      if (this.model.isNew()) {
        this.isNew = true;
      }
      this.setupBeforeFinal();
    }

    /**
     * Set up before final.
     *
     * @protected
     */
    setupBeforeFinal() {
      this.attributes = this.model.getClonedAttributes();
      this.listenTo(this.model, 'change', (m, o) => {
        if (o.sync) {
          for (const attribute in m.attributes) {
            if (!m.hasChanged(attribute)) {
              continue;
            }
            this.attributes[attribute] = Espo.Utils.cloneDeep(m.get(attribute));
          }
          return;
        }
        if (this.mode === this.MODE_EDIT) {
          this.setIsChanged();
        }
      });
      if (this.options.attributes) {
        this.model.set(this.options.attributes);
      }
      this.listenTo(this.model, 'sync', () => {
        this.attributes = this.model.getClonedAttributes();
      });
      this.initDependency();
      this.initDynamicLogic();
    }

    /**
     * Set an initial attribute value.
     *
     * @protected
     * @param {string} attribute An attribute name.
     * @param {*} value
     */
    setInitialAttributeValue(attribute, value) {
      this.attributes[attribute] = value;
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Check whether a current attribute value differs from initial.
     *
     * @param {string} name An attribute name.
     * @return {boolean}
     */
    checkAttributeIsChanged(name) {
      return !_underscore.default.isEqual(this.attributes[name], this.model.get(name));
    }

    /**
     * Reset model changes.
     */
    resetModelChanges() {
      if (this.updatedAttributes) {
        this.attributes = this.updatedAttributes;
        this.updatedAttributes = null;
      }
      const attributes = this.model.attributes;
      for (const attr in attributes) {
        if (!(attr in this.attributes)) {
          this.model.unset(attr);
        }
      }
      this.model.set(this.attributes, {
        skipReRenderInEditMode: true
      });
    }

    /**
     * Init dynamic logic.
     *
     * @protected
     */
    initDynamicLogic() {
      this.dynamicLogicDefs = Espo.Utils.clone(this.dynamicLogicDefs || {});
      this.dynamicLogicDefs.fields = Espo.Utils.clone(this.dynamicLogicDefs.fields);
      this.dynamicLogicDefs.panels = Espo.Utils.clone(this.dynamicLogicDefs.panels);
      this.dynamicLogic = new _dynamicLogic.default(this.dynamicLogicDefs, this);
      this.listenTo(this.model, 'sync', (m, a, /** Record */o) => {
        if (o && o.action !== 'save' && o.action !== 'fetch') {
          return;
        }

        // Pre-save attributes not yet prepared.
        setTimeout(() => this.processDynamicLogic(), 0);
      });
      this.listenTo(this.model, 'change', (m, /** Record */o) => {
        if (o.action === 'save' || o.action === 'fetch') {
          // To be processed by the 'sync' handler.
          return;
        }
        this.processDynamicLogic();
      });
      this.processDynamicLogic();
    }

    /**
     * Process dynamic logic.
     *
     * @protected
     */
    processDynamicLogic() {
      this.dynamicLogic.process();
    }

    /**
     * @protected
     * @internal
     */
    initDependency() {
      // noinspection JSDeprecatedSymbols
      Object.keys(this.dependencyDefs || {}).forEach(attr => {
        this.listenTo(this.model, 'change:' + attr, () => {
          this._handleDependencyAttribute(attr);
        });
      });
      this._handleDependencyAttributes();
    }

    /**
     * Set up a field level security.
     *
     * @protected
     */
    setupFieldLevelSecurity() {
      const forbiddenFieldList = this.getAcl().getScopeForbiddenFieldList(this.entityType, 'read');
      forbiddenFieldList.forEach(field => {
        this.hideField(field, true);
      });
      const readOnlyFieldList = this.getAcl().getScopeForbiddenFieldList(this.entityType, 'edit');
      readOnlyFieldList.forEach(field => {
        this.setFieldReadOnly(field, true);
      });
    }

    /**
     * Set is changed.
     *
     * @protected
     */
    setIsChanged() {
      this.isChanged = true;
    }

    /**
     * Set is not changed.
     *
     * @protected
     */
    setIsNotChanged() {
      this.isChanged = false;
    }

    /**
     * Validate.
     *
     * @return {boolean} True if not valid.
     */
    validate() {
      const invalidFieldList = [];
      this.getFieldList().forEach(field => {
        const fieldIsInvalid = this.validateField(field);
        if (fieldIsInvalid) {
          invalidFieldList.push(field);
        }
      });
      if (!!invalidFieldList.length) {
        this.onInvalid(invalidFieldList);
      }
      return !!invalidFieldList.length;
    }

    /**
     * @protected
     * @param {string[]} invalidFieldList Invalid fields.
     */
    onInvalid(invalidFieldList) {}

    /**
     * Validate a specific field.
     *
     * @param {string} field A field name.
     * @return {boolean} True if not valid.
     */
    validateField(field) {
      const msg = this.translate('fieldInvalid', 'messages').replace('{field}', this.translate(field, 'fields', this.entityType));
      const fieldView = this.getFieldView(field);
      if (!fieldView) {
        return false;
      }
      let notValid = false;
      if (fieldView.isEditMode() && !fieldView.disabled && !fieldView.readOnly) {
        notValid = fieldView.validate() || notValid;
      }
      if (notValid) {
        if (fieldView.$el) {
          const rect = fieldView.$el.get(0).getBoundingClientRect();
          if (rect.top === 0 && rect.bottom === 0 && rect.left === 0 && fieldView.$el.closest('.panel.hidden').length) {
            setTimeout(() => {
              const msg = this.translate('Not valid') + ': ' + (fieldView.lastValidationMessage || this.translate(field, 'fields', this.entityType));
              Espo.Ui.error(msg, true);
            }, 10);
          }
        }
        return true;
      }
      if (this.dynamicLogic && this.dynamicLogicDefs && this.dynamicLogicDefs.fields && this.dynamicLogicDefs.fields[field] && this.dynamicLogicDefs.fields[field].invalid && this.dynamicLogicDefs.fields[field].invalid.conditionGroup) {
        const invalidConditionGroup = this.dynamicLogicDefs.fields[field].invalid.conditionGroup;
        const fieldInvalid = this.dynamicLogic.checkConditionGroup(invalidConditionGroup);
        notValid = fieldInvalid || notValid;
        if (fieldInvalid) {
          fieldView.showValidationMessage(msg);
          fieldView.trigger('invalid');
        }
      }
      return notValid;
    }

    /**
     * Processed after save.
     *
     * @protected
     */
    afterSave() {
      if (this.isNew) {
        Espo.Ui.success(this.translate('Created'));
      } else {
        Espo.Ui.success(this.translate('Saved'));
      }
      this.setIsNotChanged();
    }

    /**
     * Processed before before-save.
     *
     * @protected
     */
    beforeBeforeSave() {}

    /**
     * Processed before save.
     *
     * @protected
     */
    beforeSave() {
      Espo.Ui.notify(this.translate('saving', 'messages'));
    }

    /**
     * Processed after save error.
     *
     * @protected
     */
    afterSaveError() {}

    /**
     * Processed after save a not modified record.
     *
     * @protected
     */
    afterNotModified() {
      Espo.Ui.warning(this.translate('notModified', 'messages'));
      this.setIsNotChanged();
    }

    /**
     * Processed after save not valid.
     *
     * @protected
     */
    afterNotValid() {
      Espo.Ui.error(this.translate('Not valid'));
    }

    /**
     * Get changed attribute values. For new record, returns all attributes.
     *
     * @protected
     * @param {string[]} [attributeList]
     * @return {Record}
     */
    getChangedAttributes() {
      let attributeList = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
      const attributes = this.model.getClonedAttributes();
      if (this.model.isNew()) {
        return attributes;
      }
      const setAttributes = {};
      for (const attr in attributes) {
        if (Espo.Utils.areEqual(this.attributes[attr], attributes[attr])) {
          continue;
        }
        setAttributes[attr] = attributes[attr];
      }

      /** @type {Record.<string, string[]>} */
      const map = this.forcePatchAttributeDependencyMap || {};
      for (const attr in map) {
        if (attr in setAttributes) {
          continue;
        }
        if (attributeList && !attributeList.includes(attr)) {
          continue;
        }
        const depAttributes = map[attr];
        const treatAsChanged = !!depAttributes.find(attr => attr in setAttributes);
        if (treatAsChanged) {
          setAttributes[attr] = attributes[attr];
        }
      }
      return setAttributes;
    }

    /**
     * Save options.
     *
     * @typedef {Object} module:views/record/base~saveOptions
     *
     * @property {Object.<string,string>} [headers] HTTP headers.
     * @property {boolean} [skipNotModifiedWarning] Don't show a not-modified warning.
     * @property {function():void} [afterValidate] A callback called after validate.
     * @property {boolean} [bypassClose] Bypass closing. Only for inline-edit.
     */

    /**
     * Save.
     *
     * @param {module:views/record/base~saveOptions} [options] Options.
     * @return {Promise}
     */
    save(options) {
      options = options || {};
      const headers = options.headers || {};
      const model = this.model;
      this.lastSaveCancelReason = null;
      this.beforeBeforeSave();

      // A model is supposed to always contain actual values.
      // Fetch may not be needed, but some field views may not have data sync implemented.
      // We resort to fetching the entire form.

      const fetchedAttributes = this.fetch();
      this.model.set(fetchedAttributes, {
        silent: true
      });
      const setAttributes = this.getChangedAttributes(Object.keys(fetchedAttributes));
      if (Object.keys(setAttributes).length === 0) {
        if (!options.skipNotModifiedWarning) {
          this.afterNotModified();
        }
        this.lastSaveCancelReason = 'notModified';
        this.trigger('cancel:save', {
          reason: 'notModified'
        });
        return Promise.reject('notModified');
      }
      if (this.validate()) {
        this.afterNotValid();
        this.lastSaveCancelReason = 'invalid';
        this.trigger('cancel:save', {
          reason: 'invalid'
        });
        return Promise.reject('invalid');
      }
      if (options.afterValidate) {
        options.afterValidate();
      }
      const optimisticConcurrencyControl = this.getMetadata().get(['entityDefs', this.entityType, 'optimisticConcurrencyControl']);
      if (optimisticConcurrencyControl && this.model.get('versionNumber') !== null) {
        headers['X-Version-Number'] = this.model.get('versionNumber');
      }
      if (this.model.isNew() && this.options.duplicateSourceId) {
        headers['X-Duplicate-Source-Id'] = this.options.duplicateSourceId;
      }
      this.beforeSave();
      this.trigger('before:save');
      model.trigger('before:save');
      const initialAttributes = this.attributes;
      return new Promise((resolve, reject) => {
        const ajaxPromise = model.save(setAttributes, {
          patch: !model.isNew(),
          headers: headers
        });
        ajaxPromise.then(() => {
          this.trigger('save', initialAttributes, Object.keys(setAttributes));
          this.afterSave();
          if (this.isNew) {
            this.isNew = false;
          }
          this.trigger('after:save');
          model.trigger('after:save');
          if (ajaxPromise.xhr.getResponseHeader('X-Record-Link-Updated')) {
            model.trigger('update-all');
          }
          resolve();
        }).catch(xhr => {
          this.handleSaveError(xhr, options, resolve, reject).then(skipReject => {
            if (skipReject) {
              return;
            }
            reject('error');
          });
          this.afterSaveError();
          this.lastSaveCancelReason = 'error';
          this.trigger('error:save');
          this.trigger('cancel:save', {
            reason: 'error'
          });
        });
      });
    }

    /**
     * Handle a save error.
     *
     * @param {module:ajax.Xhr} xhr XHR.
     * @param {module:views/record/base~saveOptions} [options] Options.
     * @param {function} saveResolve Resolve the save promise.
     * @param {function} saveReject Reject the same promise.
     * @return {Promise<boolean>}
     *
     * @protected
     */
    handleSaveError(xhr, options, saveResolve, saveReject) {
      let handlerData = null;
      if (~[409, 500].indexOf(xhr.status)) {
        const statusReason = xhr.getResponseHeader('X-Status-Reason');
        if (!statusReason) {
          return Promise.resolve(false);
        }
        try {
          handlerData = JSON.parse(statusReason);
        } catch (e) {}
        if (!handlerData) {
          handlerData = {
            reason: statusReason.toString()
          };
          if (xhr.responseText) {
            let data;
            try {
              data = JSON.parse(xhr.responseText);
            } catch (e) {
              console.error('Could not parse error response body.');
              return Promise.resolve(false);
            }
            handlerData.data = data;
          }
        }
      }
      if (!handlerData || !handlerData.reason) {
        return Promise.resolve(false);
      }
      const reason = handlerData.reason;
      const handlerName = this.getMetadata().get(['clientDefs', this.scope, 'saveErrorHandlers', reason]) || this.getMetadata().get(['clientDefs', 'Global', 'saveErrorHandlers', reason]);
      return new Promise(resolve => {
        if (handlerName) {
          Espo.loader.require(handlerName, Handler => {
            const handler = new Handler(this);
            handler.process(handlerData.data, options);
            resolve(false);
          });
          xhr.errorIsHandled = true;
          return;
        }
        const methodName = 'errorHandler' + Espo.Utils.upperCaseFirst(reason);
        if (methodName in this) {
          xhr.errorIsHandled = true;
          const skipReject = this[methodName](handlerData.data, options, saveResolve, saveReject);
          resolve(skipReject || false);
          return;
        }
        resolve(false);
      });
    }

    /**
     * Fetch data from the form.
     *
     * @return {Object.<string, *>}
     */
    fetch() {
      let data = {};
      const fieldViews = this.getFieldViews();
      for (const i in fieldViews) {
        const view = fieldViews[i];
        if (!view.isEditMode()) {
          continue;
        }
        if (!view.disabled && !view.readOnly && view.isFullyRendered()) {
          data = {
            ...data,
            ...view.fetch()
          };
        }
      }
      return data;
    }

    /**
     * Process fetch.
     *
     * @return {Object<string,*>|null}
     */
    processFetch() {
      const data = this.fetch();
      this.model.set(data);
      if (this.validate()) {
        return null;
      }
      return data;
    }

    /**
     * Populate defaults.
     *
     * @return {Promise|undefined}
     */
    populateDefaults() {
      const populator = new _defaultsPopulator.default();
      return populator.populate(this.model);
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @protected
     * @param duplicates
     */
    errorHandlerDuplicate(duplicates) {}

    /**
     * @private
     */
    _handleDependencyAttributes() {
      // noinspection JSDeprecatedSymbols
      Object.keys(this.dependencyDefs || {}).forEach(attr => {
        this._handleDependencyAttribute(attr);
      });
    }

    /**
     * @private
     */
    _handleDependencyAttribute(attr) {
      // noinspection JSDeprecatedSymbols
      const data = this.dependencyDefs[attr];
      const value = this.model.get(attr);
      if (value in (data.map || {})) {
        (data.map[value] || []).forEach(item => {
          this._doDependencyAction(item);
        });
        return;
      }
      if ('default' in data) {
        (data.default || []).forEach(item => {
          this._doDependencyAction(item);
        });
      }
    }

    /**
     * @private
     */
    _doDependencyAction(data) {
      const action = data.action;
      const methodName = 'dependencyAction' + Espo.Utils.upperCaseFirst(action);
      if (methodName in this && typeof this.methodName === 'function') {
        this.methodName(data);
        return;
      }
      const fieldList = data.fieldList || data.fields || [];
      const panelList = data.panelList || data.panels || [];
      switch (action) {
        case 'hide':
          panelList.forEach(item => {
            this.hidePanel(item);
          });
          fieldList.forEach(item => {
            this.hideField(item);
          });
          break;
        case 'show':
          panelList.forEach(item => {
            this.showPanel(item);
          });
          fieldList.forEach(item => {
            this.showField(item);
          });
          break;
        case 'setRequired':
          fieldList.forEach(field => {
            this.setFieldRequired(field);
          });
          break;
        case 'setNotRequired':
          fieldList.forEach(field => {
            this.setFieldNotRequired(field);
          });
          break;
        case 'setReadOnly':
          fieldList.forEach(field => {
            this.setFieldReadOnly(field);
          });
          break;
        case 'setNotReadOnly':
          fieldList.forEach(field => {
            this.setFieldNotReadOnly(field);
          });
          break;
      }
    }

    /**
     * Create a field view.
     *
     * @protected
     * @param {string} name A field name.
     * @param {string|null} [view] A view name/path.
     * @param {Object<string,*>} [params] Field params.
     * @param {'detail'|'edit'} [mode='edit'] A mode.
     * @param {boolean} [readOnly] Read-only.
     * @param {Object<string,*>} [options] View options.
     */
    createField(name, view, params, mode, readOnly, options) {
      const o = {
        model: this.model,
        mode: mode || 'edit',
        selector: '.field[data-name="' + name + '"]',
        defs: {
          name: name,
          params: params || {}
        }
      };
      if (readOnly) {
        o.readOnly = true;
      }
      view = view || this.model.getFieldParam(name, 'view');
      if (!view) {
        const type = this.model.getFieldType(name) || 'base';
        view = this.getFieldManager().getViewName(type);
      }
      if (options) {
        for (const param in options) {
          o[param] = options[param];
        }
      }
      if (this.recordHelper.getFieldStateParam(name, 'hidden')) {
        o.disabled = true;
      }
      if (this.recordHelper.getFieldStateParam(name, 'readOnly')) {
        o.readOnly = true;
      }
      if (this.recordHelper.getFieldStateParam(name, 'required') !== null) {
        o.defs.params.required = this.recordHelper.getFieldStateParam(name, 'required');
      }
      if (this.recordHelper.hasFieldOptionList(name)) {
        o.customOptionList = this.recordHelper.getFieldOptionList(name);
      }
      const viewKey = name + 'Field';
      this.createView(viewKey, view, o);
      if (!~this.fieldList.indexOf(name)) {
        this.fieldList.push(name);
      }
    }

    /**
     * Get a currently focused field view.
     *
     * @return {module:views/fields/base|null}
     */
    getFocusedFieldView() {
      const $active = (0, _jquery.default)(window.document.activeElement);
      if (!$active.length) {
        return null;
      }
      const $field = $active.closest('.field');
      if (!$field.length) {
        return null;
      }
      const name = $field.attr('data-name');
      if (!name) {
        return null;
      }
      return this.getFieldView(name);
    }

    /**
     * Process exit.
     *
     * @param {string} [after] An exit parameter.
     */
    exit(after) {}
  }
  var _default = _exports.default = BaseRecordView;
});

define("views/modals/select-template", ["exports", "views/modals/select-records"], function (_exports, _selectRecords) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _selectRecords = _interopRequireDefault(_selectRecords);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class SelectTemplateModalView extends _selectRecords.default {
    multiple = false;
    createButton = false;
    searchPanel = false;
    scope = 'Template';
    backdrop = true;
    setupSearch() {
      super.setupSearch();
      this.searchManager.setAdvanced({
        entityType: {
          type: 'equals',
          value: this.options.entityType
        }
      });
      this.collection.where = this.searchManager.getWhere();
    }
    afterRender() {
      super.afterRender();
      const firstLinkElement = this.$el.find('a.link').first().get(0);
      if (firstLinkElement) {
        // noinspection JSUnresolvedReference
        setTimeout(() => firstLinkElement.focus({
          preventScroll: true
        }), 10);
      }
    }
  }
  var _default = _exports.default = SelectTemplateModalView;
});

define("ui/select", ["exports", "lib!selectize"], function (_exports, _libSelectize) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _libSelectize = _interopRequireDefault(_libSelectize);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 module:ui/select */

  /**
   * @typedef module:ui/select~Options
   * @type {Object}
   * @property {boolean} [selectOnTab=false] To select on tab.
   * @property {boolean} [matchAnyWord=false] To match any word when searching.
   * @property {function(string, module:ui/select~OptionItemsCallback): void} [load] Loads additional items
   *   when typing in search.
   * @property {function(string, module:ui/select~OptionItemFull): Number} [score] A score function scoring
   *   searched items.
   * @property {'value'|'text'|'$order'|'$score'} [sortBy='$order'] Item sorting.
   * @property {'asc'|'desc'} [sortDirection='asc'] Sort direction.
   * @property {function()} [onFocus] On-focus callback.
   */

  /**
   * @callback  module:ui/select~OptionItemsCallback
   * @param {module:ui/select~OptionItem[]} list An option item list.
   */

  /**
   * @typedef module:ui/select~OptionItem
   * @type {Object}
   * @property {string} value A value.
   * @property {string} text A label.
   */

  /**
   * @typedef module:ui/select~OptionItemFull
   * @type {Object}
   * @property {string} value A value.
   * @property {string} text A label.
   * @property {Number} $order An order index.
   */

  /**
   * @module ui/select
   *
   * Important. The Selectize library is heavily customized to fix multitude of UIX issues.
   * Upgrading is not advisable. Consider forking.
   */
  const Select = {
    /**
     * @param {Element|JQuery} element An element.
     * @param {module:ui/select~Options} [options] Options.
     */
    init: function (element) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      const score = options.score;
      const $el = $(element);
      options = Select.applyDefaultOptions(options || {});
      const plugins = [];
      Select.loadEspoSelectPlugin();
      plugins.push('espo_select');
      const itemClasses = {};
      const allowedValues = $el.children().toArray().map(item => {
        const value = item.getAttributeNode('value').value;
        if (item.classList) {
          itemClasses[value] = item.classList.toString();
        }
        return value;
      });
      let $relativeParent = null;
      const $modalBody = $el.closest('.modal-body');
      if ($modalBody.length) {
        $relativeParent = $modalBody;
      }

      // noinspection JSUnusedGlobalSymbols
      const selectizeOptions = {
        sortField: [{
          field: options.sortBy,
          direction: options.sortDirection
        }],
        load: options.load,
        loadThrottle: 1,
        plugins: plugins,
        highlight: false,
        selectOnTab: options.selectOnTab,
        copyClassesToDropdown: false,
        allowEmptyOption: allowedValues.includes(''),
        showEmptyOptionInDropdown: true,
        $relativeParent: $relativeParent,
        render: {
          item: function (data) {
            return $('<div>').addClass('item').addClass(itemClasses[data.value] || '').text(data.text).get(0).outerHTML;
          },
          option: function (data) {
            const $div = $('<div>').addClass('option').addClass(data.value === '' ? 'selectize-dropdown-emptyoptionlabel' : '').addClass(itemClasses[data.value] || '').val(data.value).text(data.text);
            if (data.text === '') {
              $div.html('&nbsp;');
            }
            return $div.get(0).outerHTML;
          }
        },
        onDelete: function (values) {
          while (values.length) {
            this.removeItem(values.pop(), true);
          }

          // noinspection JSUnresolvedReference
          this.showInput();
          this.positionDropdown();
          this.refreshOptions(true);
        }
      };
      if (options.onFocus) {
        selectizeOptions.onFocus = function () {
          options.onFocus();
        };
      }
      if (!options.matchAnyWord) {
        /** @this Selectize */
        selectizeOptions.score = function (search) {
          // noinspection JSUnresolvedReference
          const score = this.getScoreFunction(search);
          search = search.toLowerCase();
          return function (item) {
            if (item.text.toLowerCase().indexOf(search) === 0) {
              return score(item);
            }
            return 0;
          };
        };
      }
      if (options.matchAnyWord) {
        /** @this Selectize */
        selectizeOptions.score = function (search) {
          // noinspection JSUnresolvedReference
          const score = this.getScoreFunction(search);
          search = search.toLowerCase();
          return function (item) {
            const text = item.text.toLowerCase();
            if (!text.split(' ').find(item => item.startsWith(search)) && !text.startsWith(search)) {
              return 0;
            }
            return score(item);
          };
        };
      }
      if (options.score) {
        selectizeOptions.score = function (search) {
          return function (item) {
            return score(search, item);
          };
        };
      }
      $el.selectize(selectizeOptions);
    },
    /**
     * Focus.
     *
     * @param {Element|JQuery} element An element.
     * @param {{noTrigger?: boolean}} [options] Options.
     */
    focus: function (element, options) {
      const $el = $(element);
      options = options || {};
      if (!$el[0] || !$el[0].selectize) {
        return;
      }
      const selectize = $el[0].selectize;
      if (options.noTrigger) {
        selectize.focusNoTrigger = true;
      }
      selectize.focus();
      if (options.noTrigger) {
        setTimeout(() => selectize.focusNoTrigger = false, 100);
      }
    },
    /**
     * Set options.
     * @todo Side effects may occur if called multiple times. Workaround is to clone and re-initialize. To be fixed.
     *
     * @param {Element|JQuery} element An element.
     * @param {{value: string, text: string}[]} options Options.
     */
    setOptions: function (element, options) {
      const $el = $(element);
      const selectize = $el.get(0).selectize;
      selectize.clearOptions(true);
      selectize.load(callback => {
        callback(options.map(item => {
          return {
            value: item.value,
            text: item.text || item.label
          };
        }));
      });
    },
    /**
     * Set value.
     *
     * @param {HTMLElement|JQuery} element An element.
     * @param {string} value A value.
     */
    setValue: function (element, value) {
      if (!(element instanceof HTMLElement)) {
        element = $(element).get(0);
      }
      const selectize = element.selectize;
      selectize.setValue(value, true);
    },
    /**
     * Destroy.
     *
     * @param {HTMLElement|JQuery} element An element.
     */
    destroy: function (element) {
      if (!element) {
        return;
      }
      if (!(element instanceof HTMLElement)) {
        element = $(element).get(0);
      }
      if (!element || !element.selectize) {
        return;
      }
      element.selectize.destroy();
    },
    /**
     * @private
     * @param {module:ui/select~Options} options
     * @return {module:ui/select~Options}
     */
    applyDefaultOptions: function (options) {
      options = Espo.Utils.clone(options);
      const defaults = {
        selectOnTab: false,
        matchAnyWord: false,
        sortBy: '$order',
        sortDirection: 'asc'
      };
      for (const key in defaults) {
        if (key in options) {
          continue;
        }
        options[key] = defaults[key];
      }
      return options;
    },
    /**
     * @private
     */
    loadEspoSelectPlugin: function () {
      if ('espo_select' in _libSelectize.default.plugins) {
        return;
      }
      const IS_MAC = /Mac/.test(navigator.userAgent);
      const KEY_BACKSPACE = 8;
      _libSelectize.default.define('espo_select', function () {
        const self = this;
        this.setup = function () {
          const original = self.setup;
          return function () {
            original.apply(this, arguments);
            self.selectedValue = self.items[0];
            self.$dropdown.on('mouseup', '[data-selectable]', function () {
              $(document).off('mouseup.select');
              return self.onOptionSelect.apply(self, arguments);
            });
            self.$dropdown.on('mousedown', '[data-selectable]', function () {
              // Prevent issue when down inside, up outside.
              $(document).one('mouseup.select', function () {
                self.focusOnControlSilently();
              });
            });
            self.$control_input.css({
              'width': '4px'
            });
          };
        }();
        this.focusOnControlSilently = function () {
          self.preventReOpenOnFocus = true;
          self.$control_input[0].focus();
          self.preventReOpenOnFocus = false;
        };

        /*this.positionDropdown = (function () {
            let original = self.positionDropdown;
              return function () {
                original.apply(this, arguments);
                  this.$dropdown.css({margin: 'unset'});
            };
        })();*/

        this.refreshOptions = function () {
          const original = self.refreshOptions;
          return function () {
            if (self.focusNoTrigger) {
              original.apply(this, [false]);
              return;
            }
            original.apply(this, arguments);
          };
        }();
        this.blur = function () {
          const original = self.blur;
          return function () {
            // Prevent closing on mouse down.
            if (self.preventClose) {
              return;
            }
            original.apply(this, arguments);
          };
        }();
        this.close = function () {
          const original = self.close;
          return function () {
            if (self.preventClose) {
              return;
            }
            original.apply(this, arguments);
          };
        }();
        this.onOptionSelect = function () {
          const original = self.onOptionSelect;
          return function (e) {
            if (e.type === 'mousedown' || e.type === 'click') {
              self.preventClose = true;
              setTimeout(() => self.preventClose = false, 100);
              return;
            }
            self.preventClose = false;
            if (e.type === 'mouseup') {
              setTimeout(() => self.focusOnControlSilently(), 50);
            }
            original.apply(this, arguments);
            self.selectedValue = $(e.currentTarget).attr('data-value');
          };
        }();
        this.open = function () {
          const original = self.open;
          return function () {
            const toProcess = !(self.isLocked || self.isOpen);
            original.apply(this, arguments);
            if (!toProcess) {
              return;
            }
            const $dropdownContent = self.$dropdown.children().first();
            const $selected = $dropdownContent.find('.selected');
            if (!$selected.length) {
              return;
            }
            let scrollTo = $selected.get(0).offsetTop - $dropdownContent.get(0).clientHeight;
            scrollTo = scrollTo >= 0 ? scrollTo : 0;
            $dropdownContent.find('.selectize-dropdown-content').scrollTop(scrollTo);
          };
        }();
        this.onMouseDown = function () {
          const original = self.onMouseDown;
          return function (e) {
            // Prevent flicking when clicking on input.
            if (!self.isOpen && !self.isInputHidden && self.$control_input.val()) {
              return;
            }
            if (self.isOpen) {
              self.closedByMouseDown = true;
            }
            return original.apply(this, arguments);
          };
        }();
        this.onFocus = function () {
          const original = self.onFocus;
          return function (e) {
            if (self.preventReOpenOnFocus) {
              return;
            }
            if (self.closedByMouseDown) {
              self.closedByMouseDown = false;
              return;
            }
            self.closedByMouseDown = false;
            return original.apply(this, arguments);
          };
        }();
        this.restoreSelectedValue = function () {
          if (this.preventRevertLoop) {
            return;
          }
          this.preventRevertLoop = true;
          setTimeout(() => this.preventRevertLoop = false, 10);
          this.setValue(this.selectedValue, true);
        };
        this.onBlur = function () {
          const original = self.onBlur;
          return function () {
            // Prevent closing on mouse down.
            if (self.preventClose) {
              return;
            }
            self.restoreSelectedValue();
            self.$control_input.css({
              width: '4px'
            });
            return original.apply(this, arguments);
          };
        }();
        this.onKeyDown = function () {
          const original = self.onKeyDown;
          return function (e) {
            if (IS_MAC ? e.metaKey : e.ctrlKey) {
              if (!self.items.length) {
                self.restoreSelectedValue();
                self.focus();
              }
              return;
            }
            if (e.code === 'Escape') {
              if (self.isOpen || !self.isInputHidden) {
                e.stopPropagation();
              }
              if (self.isOpen) {
                self.close();
              }
              if (!self.isInputHidden) {
                self.hideInput();
              }
              self.addItem(this.selectedValue, true);
            }
            if (self.isFull() || self.isInputHidden) {
              if (e.key.length === 1 && (e.code.match(/Key[A-Z]/i) || e.key.match(/[0-9]/) || RegExp(/^\p{L}/, 'u').test(e.key) // is letter
              )) {
                const keyCode = e.keyCode;
                e.keyCode = KEY_BACKSPACE;
                self.deleteSelection(e);
                e.keyCode = keyCode;
                self.$control_input.width(15);
              }
            }
            return original.apply(this, arguments);
          };
        }();
        this.positionDropdown = function () {
          const POSITION = {
            top: 'top',
            bottom: 'bottom'
          };
          return function () {
            const $control = self.$control;
            const offset = this.settings.dropdownParent === 'body' ? $control.offset() : $control.position();
            offset.top += $control.outerHeight(true);
            const dropdownHeight = self.$dropdown.prop('scrollHeight') + 5;
            const controlPosTop = self.$control.get(0).getBoundingClientRect().top;
            const wrapperHeight = self.$wrapper.height();
            const controlPosBottom = self.$control.get(0).getBoundingClientRect().bottom;
            const boundaryTop = !this.settings.$relativeParent ? 0 : this.settings.$relativeParent.get(0).getBoundingClientRect().top;
            const position = controlPosTop + dropdownHeight + wrapperHeight > window.innerHeight && controlPosBottom - dropdownHeight - wrapperHeight >= boundaryTop ? POSITION.top : POSITION.bottom;
            const styles = {
              width: $control.outerWidth(),
              left: offset.left
            };
            if (position === POSITION.top) {
              Object.assign(styles, {
                bottom: offset.top,
                top: 'unset',
                margin: '0 0 0 0'
              });
              self.$dropdown.addClass('selectize-position-top');
            } else {
              Object.assign(styles, {
                top: offset.top,
                bottom: 'unset',
                margin: '0 0 0 0'
              });
              self.$dropdown.removeClass('selectize-position-top');
            }
            self.$dropdown.css(styles);
          };
        }();
      });
    }
  };
  var _default = _exports.default = Select;
});

define("helpers/action-item-setup", ["exports", "di", "metadata", "view-helper", "acl-manager", "language"], function (_exports, _di, _metadata, _viewHelper, _aclManager, _language) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _metadata = _interopRequireDefault(_metadata);
  _viewHelper = _interopRequireDefault(_viewHelper);
  _aclManager = _interopRequireDefault(_aclManager);
  _language = _interopRequireDefault(_language);
  let _init_metadata, _init_extra_metadata, _init_viewHelper, _init_extra_viewHelper, _init_acl, _init_extra_acl, _init_language, _init_extra_language;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /** @module helpers/action-item-setup */

  class ActionItemSetupHelper {
    static #_ = [_init_metadata, _init_extra_metadata, _init_viewHelper, _init_extra_viewHelper, _init_acl, _init_extra_acl, _init_language, _init_extra_language] = _applyDecs(this, [], [[(0, _di.inject)(_metadata.default), 0, "metadata"], [(0, _di.inject)(_viewHelper.default), 0, "viewHelper"], [(0, _di.inject)(_aclManager.default), 0, "acl"], [(0, _di.inject)(_language.default), 0, "language"]]).e;
    constructor() {
      _init_extra_language(this);
    }
    /**
     * @private
     * @type {Metadata}
     */
    metadata = _init_metadata(this);

    /**
     * @private
     * @type {ViewHelper}
     */
    viewHelper = (_init_extra_metadata(this), _init_viewHelper(this));

    /**
     * @private
     * @type {AclManager}
     */
    acl = (_init_extra_viewHelper(this), _init_acl(this));

    /**
     * @private
     * @type {Language}
     */
    language = (_init_extra_acl(this), _init_language(this));

    /**
     * @param {module:view} view
     * @param {string} type
     * @param {function(Promise): void} waitFunc
     * @param {function(Object): void} addFunc
     * @param {function(string): void} showFunc
     * @param {function(string): void} hideFunc
     * @param {{listenToViewModelSync?: boolean}} [options]
     */
    setup(view, type, waitFunc, addFunc, showFunc, hideFunc, options) {
      options = options || {};
      const actionList = [];

      // noinspection JSUnresolvedReference
      const scope = view.scope || view.model.entityType;
      if (!scope) {
        throw new Error();
      }
      const actionDefsList = [...(this.metadata.get(['clientDefs', 'Global', type + 'ActionList']) || []), ...(this.metadata.get(['clientDefs', scope, type + 'ActionList']) || [])];
      actionDefsList.forEach(item => {
        if (typeof item === 'string') {
          item = {
            name: item
          };
        }
        item = Espo.Utils.cloneDeep(item);
        const name = item.name;
        if (!item.label) {
          item.html = this.language.translate(name, 'actions', scope);
        }
        item.data = item.data || {};
        const handlerName = item.handler || item.data.handler;
        if (handlerName && !item.data.handler) {
          item.data.handler = handlerName;
        }
        addFunc(item);
        if (!Espo.Utils.checkActionAvailability(this.viewHelper, item)) {
          return;
        }
        if (!Espo.Utils.checkActionAccess(this.acl, view.model, item, true)) {
          item.hidden = true;
        }
        actionList.push(item);
        if (!handlerName) {
          return;
        }
        if (!item.initFunction && !item.checkVisibilityFunction) {
          return;
        }
        waitFunc(new Promise(resolve => {
          Espo.loader.require(handlerName, Handler => {
            const handler = new Handler(view);
            if (item.initFunction) {
              handler[item.initFunction].call(handler);
            }
            if (item.checkVisibilityFunction) {
              const isNotVisible = !handler[item.checkVisibilityFunction].call(handler);
              if (isNotVisible) {
                hideFunc(item.name);
              }
            }
            item.handlerInstance = handler;
            resolve();
          });
        }));
      });
      if (!actionList.length) {
        return;
      }
      const onSync = () => {
        actionList.forEach(item => {
          if (item.handlerInstance && item.checkVisibilityFunction) {
            const isNotVisible = !item.handlerInstance[item.checkVisibilityFunction].call(item.handlerInstance);
            if (isNotVisible) {
              hideFunc(item.name);
              return;
            }
          }
          if (Espo.Utils.checkActionAccess(this.acl, view.model, item, true)) {
            showFunc(item.name);
            return;
          }
          hideFunc(item.name);
        });
      };
      if (options.listenToViewModelSync) {
        view.listenTo(view, 'model-sync', () => onSync());
        return;
      }
      view.listenTo(view.model, 'sync', () => onSync());
    }
  }
  var _default = _exports.default = ActionItemSetupHelper;
});

define("helpers/util/debounce", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * A debounce helper.
   *
   * @since 9.1.0
   */
  class DebounceHelper {
    /**
     * @type {boolean}
     * @private
     */
    _blocked = false;

    /**
     *
     * @type {boolean}
     * @private
     */
    _calledWhenBlocked = false;

    /**
     * @param {{
     *     handler: function(...*),
     *     interval: number,
     * }} options
     * @param options
     */
    constructor(options) {
      /**
       * @private
       * @type {function(...*)}
       */
      this.handler = options.handler;

      /**
       * @private
       * @type {number}
       */
      this.interval = options.interval;
    }

    /**
     * Process.
     *
     * @param {...*} [arguments]
     */
    process() {
      const handle = () => {
        if (this._blocked) {
          this._calledWhenBlocked = true;
          return;
        }
        this.handler(arguments);
        this._blocked = true;
        setTimeout(() => {
          const reRun = this._calledWhenBlocked;
          this._blocked = false;
          this._calledWhenBlocked = false;
          if (reRun) {
            handle();
          }
        }, this.interval);
      };
      handle();
    }
  }
  _exports.default = DebounceHelper;
});

define("helpers/record/misc/sticky-bar", ["exports", "jquery"], function (_exports, _jquery) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _jquery = _interopRequireDefault(_jquery);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * @internal
   */
  class StickyBarHelper {
    /**
     * @param {import('views/record/detail').default} view
     * @param {boolean} stickButtonsFormBottomSelector
     * @param {boolean} stickButtonsContainerAllTheWay
     * @param {number} numId
     */
    constructor(view, stickButtonsFormBottomSelector, stickButtonsContainerAllTheWay, numId) {
      this.view = view;
      this.stickButtonsFormBottomSelector = stickButtonsFormBottomSelector;
      this.stickButtonsContainerAllTheWay = stickButtonsContainerAllTheWay;
      this.numId = numId;
      this.themeManager = view.getThemeManager();
      this.$el = view.$el;
    }
    init() {
      const $containers = this.$el.find('.detail-button-container');
      const $container = this.$el.find('.detail-button-container.record-buttons');
      if (!$container.length) {
        return;
      }
      const navbarHeight = this.themeManager.getParam('navbarHeight') * this.themeManager.getFontSizeFactor();
      const screenWidthXs = this.themeManager.getParam('screenWidthXs');
      const isSmallScreen = (0, _jquery.default)(window.document).width() < screenWidthXs;
      const getOffsetTop = (/** JQuery */$element) => {
        let element = /** @type {HTMLElement} */$element.get(0);
        let value = 0;
        while (element) {
          value += !isNaN(element.offsetTop) ? element.offsetTop : 0;
          element = element.offsetParent;
        }
        if (isSmallScreen) {
          return value;
        }
        return value - navbarHeight;
      };
      let stickTop = getOffsetTop($container);
      const blockHeight = $container.outerHeight();
      stickTop -= 5; // padding;

      const $block = (0, _jquery.default)('<div>').css('height', blockHeight + 'px').html('&nbsp;').hide().insertAfter($container);
      let $middle = this.view.getMiddleView().$el;
      const $window = (0, _jquery.default)(window);
      const $navbarRight = (0, _jquery.default)('#navbar .navbar-right');
      if (this.stickButtonsFormBottomSelector) {
        const $bottom = this.$el.find(this.stickButtonsFormBottomSelector);
        if ($bottom.length) {
          $middle = $bottom;
        }
      }
      $window.off('scroll.detail-' + this.numId);
      $window.on('scroll.detail-' + this.numId, () => {
        const edge = $middle.position().top + $middle.outerHeight(false) - blockHeight;
        const scrollTop = $window.scrollTop();
        if (scrollTop >= edge && !this.stickButtonsContainerAllTheWay) {
          $containers.hide();
          $navbarRight.removeClass('has-sticked-bar');
          $block.show();
          return;
        }
        if (isSmallScreen && (0, _jquery.default)('#navbar .navbar-body').hasClass('in')) {
          return;
        }
        if (scrollTop > stickTop) {
          if (!$containers.hasClass('stick-sub')) {
            $containers.addClass('stick-sub');
            $block.show();
          }
          $navbarRight.addClass('has-sticked-bar');
          $containers.show();
          return;
        }
        if ($containers.hasClass('stick-sub')) {
          $containers.removeClass('stick-sub');
          $navbarRight.removeClass('has-sticked-bar');
          $block.hide();
        }
        $containers.show();
      });
    }
  }
  var _default = _exports.default = StickyBarHelper;
});

define("views/record/detail", ["exports", "views/record/base", "view-record-helper", "helpers/action-item-setup", "helpers/record/misc/sticky-bar", "views/modals/select-template", "helpers/util/debounce"], function (_exports, _base, _viewRecordHelper, _actionItemSetup, _stickyBar, _selectTemplate, _debounce) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _viewRecordHelper = _interopRequireDefault(_viewRecordHelper);
  _actionItemSetup = _interopRequireDefault(_actionItemSetup);
  _stickyBar = _interopRequireDefault(_stickyBar);
  _selectTemplate = _interopRequireDefault(_selectTemplate);
  _debounce = _interopRequireDefault(_debounce);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/detail */

  /**
   * A detail record view.
   */
  class DetailRecordView extends _base.default {
    /**
     * @typedef {Object} module:views/record/detail~options
     *
     * @property {module:model} model A model.
     * @property {string} [scope] A scope.
     * @property {string} [layoutName] A layout name.
     * @property {module:views/record/detail~panelDefs[]} [detailLayout] A detail layout.
     * @property {boolean} [readOnly] Read-only.
     * @property {string} [rootUrl]
     * @property {string} [returnUrl]
     * @property {boolean} [returnAfterCreate]
     * @property {boolean} [editModeDisabled]
     * @property {boolean} [confirmLeaveDisabled]
     * @property {boolean} [isWide]
     * @property {string|null} [sideView]
     * @property {string|null} [bottomView]
     * @property {string} [inlineEditDisabled] Disable inline edit.
     * @property {boolean} [buttonsDisabled] Disable buttons.
     * @property {string} [navigateButtonsDisabled]
     * @property {Object} [dynamicLogicDefs]
     * @property {module:view-record-helper} [recordHelper] A record helper. For a form state management.
     * @property {Object.<string, *>} [attributes]
     * @property {module:views/record/detail~button[]} [buttonList] Buttons.
     * @property {module:views/record/detail~dropdownItem[]} [dropdownItemList] Dropdown items.
     * @property {Object.<string, *>} [dataObject] Additional data.
     * @property {Record} [rootData] Data from the root view.
     * @property {boolean} [shortcutKeysEnabled] Enable shortcut keys.
     */

    /**
     * @param {module:views/record/detail~options | Object.<string, *>} options Options.
     */
    constructor(options) {
      super(options);
    }

    /** @inheritDoc */
    template = 'record/detail';

    /** @inheritDoc */
    type = 'detail';

    /**
     * A layout name. Can be overridden by an option parameter.
     *
     * @protected
     * @type {string}
     */
    layoutName = 'detail';

    /**
     * Panel definitions.
     *
     * @typedef {Object} module:views/record/detail~panelDefs
     * @property {string} [label] A translatable label.
     * @property {string} [customLabel] A custom label.
     * @property {string} [name] A name. Useful to be able to show/hide by a name.
     * @property {'default'|'success'|'danger'|'warning'|'info'} [style] A style.
     * @property {boolean} [tabBreak] Is a tab-break.
     * @property {string} [tabLabel] A tab label. If starts with `$`, a translation
     *   of the `tabs` category is used.
     * @property {module:views/record/detail~rowDefs[]} [rows] Rows.
     * @property {module:views/record/detail~rowDefs[]} [columns] Columns.
     * @property {string} [noteText] A note text.
     * @property {'success'|'danger'|'warning'|'info'} [noteStyle] A note style.
     */

    /**
     * A row.
     *
     * @typedef {Array<module:views/record/detail~cellDefs|false>} module:views/record/detail~rowDefs
     */

    /**
     * Cell definitions.
     *
     * @typedef {Object} module:views/record/detail~cellDefs
     * @property {string} [name] A name (usually a field name).
     * @property {string|module:views/fields/base} [view] An overridden field view name or a view instance.
     * @property {string} [type] An overridden field type.
     * @property {boolean} [readOnly] Read-only.
     * @property {boolean} [inlineEditDisabled] Disable inline edit.
     * @property {Object.<string, *>} [params] Overridden field parameters.
     * @property {Object.<string, *>} [options] Field view options.
     * @property {string} [labelText] A label text (not-translatable).
     * @property {boolean} [noLabel] No label.
     * @property {string} [label] A translatable label (using the `fields` category).
     * @property {1|2|3|4} [span] A width.
     */

    /**
     * A layout. If null, then will be loaded from the backend (using the `layoutName` value).
     * Can be overridden by an option parameter.
     *
     * @protected
     * @type {module:views/record/detail~panelDefs[]|null}
     */
    detailLayout = null;

    /**
     * A fields mode.
     *
     * @protected
     * @type {'detail'|'edit'|'list'}
     */
    fieldsMode = 'detail';

    /**
     * A current mode. Only for reading.
     *
     * @protected
     * @type {'detail'|'edit'}
     */
    mode = 'detail';

    /**
     * @private
     */
    gridLayout = null;

    /**
     * Disable buttons. Can be overridden by an option parameter.
     *
     * @protected
     * @type {boolean}
     */
    buttonsDisabled = false;

    /**
     * Is record new. Only for reading.
     *
     * @protected
     */
    isNew = false;

    /**
     * A button. Handled by an `action{Name}` method, a click handler or a handler class.
     *
     * @typedef module:views/record/detail~button
     *
     * @property {string} name A name.
     * @property {string} [label] A label.
     * @property {string} [labelTranslation] A label translation path.
     * @property {string} [html] An HTML.
     * @property {string} [text] A text.
     * @property {'default'|'danger'|'success'|'warning'|'primary'} [style] A style.
     * @property {boolean} [hidden] Hidden.
     * @property {string} [title] A title (not translatable).
     * @property {boolean} [disabled] Disabled.
     * @property {function()} [onClick] A click handler.
     */

    /**
     * A dropdown item. Handled by an `action{Name}` method, a click handler or a handler class.
     *
     * @typedef module:views/record/detail~dropdownItem
     *
     * @property {string} name A name.
     * @property {string} [label] A label.
     * @property {string} [labelTranslation] A label translation path.
     * @property {string} [html] An HTML.
     * @property {string} [text] A text.
     * @property {boolean} [hidden] Hidden.
     * @property {Object.<string, string>} [data] Data attributes.
     * @property {string} [title] A title (not translatable).
     * @property {boolean} [disabled] Disabled.
     * @property {function()} [onClick] A click handler.
     * @property {number} [groupIndex] A group index.
     */

    /**
     * A button list.
     *
     * @protected
     * @type {module:views/record/detail~button[]}
     */
    buttonList = [{
      name: 'edit',
      label: 'Edit',
      title: 'Ctrl+Space'
    }];

    /**
     * A dropdown item list.
     *
     * @protected
     * @type {Array<module:views/record/detail~dropdownItem>}
     */
    dropdownItemList = [{
      name: 'delete',
      label: 'Remove',
      groupIndex: 0
    }];

    /**
     * A button list for edit mode.
     *
     * @protected
     * @type {module:views/record/detail~button[]}
     */
    buttonEditList = [{
      name: 'save',
      label: 'Save',
      style: 'primary',
      edit: true,
      title: 'Ctrl+Enter'
    }, {
      name: 'cancelEdit',
      label: 'Cancel',
      edit: true,
      title: 'Esc'
    }];

    /**
     * A dropdown item list for edit mode.
     *
     * @protected
     * @type {module:views/record/detail~dropdownItem[]}
     */
    dropdownEditItemList = [];

    /**
     * All action items disabled;
     *
     * @protected
     */
    allActionItemsDisabled = false;

    /**
     * A DOM element ID. Only for reading.
     *
     * @private
     * @type {string|null}
     */
    id = null;

    /**
     * A return-URL. Can be overridden by an option parameter.
     *
     * @protected
     * @type {string|null}
     */
    returnUrl = null;

    /**
     * A return dispatch params. Can be overridden by an option parameter.
     *
     * @protected
     * @type {Object|null}
     */
    returnDispatchParams = null;

    /**
     * A middle view name.
     *
     * @protected
     */
    middleView = 'views/record/detail-middle';

    /**
     * A side view name.
     *
     * @protected
     */
    sideView = 'views/record/detail-side';

    /**
     * A bottom view name.
     *
     * @protected
     */
    bottomView = 'views/record/detail-bottom';

    /**
     * Disable a side view. Can be overridden by an option parameter.
     *
     * @protected
     */
    sideDisabled = false;

    /**
     * Disable a bottom view. Can be overridden by an option parameter.
     *
     * @protected
     */
    bottomDisabled = false;

    /**
     * @protected
     */
    gridLayoutType = 'record';

    /**
     * Disable edit mode. Can be overridden by an option parameter.
     *
     * @protected
     */
    editModeDisabled = false;

    /**
     * Disable navigate (prev, next) buttons. Can be overridden by an option parameter.
     *
     * @protected
     */
    navigateButtonsDisabled = false;

    /**
     * Read-only. Can be overridden by an option parameter.
     */
    readOnly = false;

    /**
     * Middle view expanded to full width (no side view).
     * Can be overridden by an option parameter.
     *
     * @protected
     */
    isWide = false;

    /**
     * Enable a duplicate action.
     *
     * @protected
     */
    duplicateAction = true;

    /**
     * Enable a self-assign action.
     *
     * @protected
     */
    selfAssignAction = false;

    /**
     * Enable a print-pdf action.
     *
     * @protected
     */
    printPdfAction = true;

    /**
     * Enable a convert-currency action.
     *
     * @protected
     */
    convertCurrencyAction = true;

    /**
     * Enable a save-and-continue-editing action.
     *
     * @protected
     */
    saveAndContinueEditingAction = true;

    /**
     * Disable the inline-edit. Can be overridden by an option parameter.
     *
     * @protected
     */
    inlineEditDisabled = false;

    /**
     * Disable a portal layout usage. Can be overridden by an option parameter.
     *
     * @protected
     */
    portalLayoutDisabled = false;

    /**
     * A panel soft-locked type.
     *
     * @typedef {'default'|'acl'|'delimiter'|'dynamicLogic'
     * } module:views/record/detail~panelSoftLockedType
     */

    /**
     * @private
     * @type {module:views/record/detail~panelSoftLockedType[]}
     */
    panelSoftLockedTypeList = ['default', 'acl', 'delimiter', 'dynamicLogic'];

    /**
     * Dynamic logic. Can be overridden by an option parameter.
     *
     * @protected
     * @type {Object}
     * @todo Add typedef.
     */
    dynamicLogicDefs = {};

    /**
     * Disable confirm leave-out processing.
     *
     * @protected
     */
    confirmLeaveDisabled = false;

    /**
     * @protected
     */
    setupHandlerType = 'record/detail';

    /**
     * @protected
     */
    currentTab = 0;

    /**
     * @protected
     * @type {Object.<string,*>|null}
     */
    middlePanelDefs = null;

    /**
     * @protected
     * @type {Object.<string,*>[]|null}
     */
    middlePanelDefsList = null;

    /**
     * @protected
     * @type {JQuery|null}
     */
    $middle = null;

    /**
     * @protected
     * @type {JQuery|null}
     */
    $bottom = null;

    /**
     * @private
     * @type {JQuery|null}
     */
    $detailButtonContainer = null;

    /** @private */
    blockUpdateWebSocketPeriod = 500;

    /**
     * @internal
     * @protected
     */
    stickButtonsFormBottomSelector;

    /**
     * @protected
     * @type {string}
     */
    dynamicHandlerClassName;

    /**
     * Disable access control.
     *
     * @protected
     * @type {boolean}
     */
    accessControlDisabled;

    /**
     * @protected
     * @type {boolean}
     */
    inlineEditModeIsOn = false;

    /**
     * A Ctrl+Enter shortcut action.
     *
     * @protected
     * @type {?string}
     */
    shortcutKeyCtrlEnterAction = 'save';

    /**
     * Additional data. Passed to sub-views and fields.
     *
     * @protected
     * @type {Object.<string, *>}
     * @since 9.0.0
     */
    dataObject;

    /**
     * Data from the root view.
     *
     * @protected
     * @type {Record}
     * @since 9.0.0
     */
    rootData;

    /**
     * @private
     * @type {DebounceHelper}
     */
    _webSocketDebounceHelper;

    /**
     * @private
     * @type {number}
     */
    _webSocketDebounceInterval = 500;

    /**
     * A shortcut-key => action map.
     *
     * @protected
     * @type {?Object.<string, string|function (KeyboardEvent): void>}
     */
    shortcutKeys = {
      /** @this DetailRecordView */
      'Control+Enter': function (e) {
        this.handleShortcutKeyCtrlEnter(e);
      },
      /** @this DetailRecordView */
      'Control+Alt+Enter': function (e) {
        this.handleShortcutKeyCtrlAltEnter(e);
      },
      /** @this DetailRecordView */
      'Control+KeyS': function (e) {
        this.handleShortcutKeyCtrlS(e);
      },
      /** @this DetailRecordView */
      'Control+Space': function (e) {
        this.handleShortcutKeyCtrlSpace(e);
      },
      /** @this DetailRecordView */
      'Escape': function (e) {
        this.handleShortcutKeyEscape(e);
      },
      /** @this DetailRecordView */
      'Control+Backslash': function (e) {
        this.handleShortcutKeyControlBackslash(e);
      },
      /** @this DetailRecordView */
      'Control+ArrowLeft': function (e) {
        this.handleShortcutKeyControlArrowLeft(e);
      },
      /** @this DetailRecordView */
      'Control+ArrowRight': function (e) {
        this.handleShortcutKeyControlArrowRight(e);
      }
    };

    /**
     * @inheritDoc
     */
    events = {
      /** @this DetailRecordView */
      'click .button-container .action': function (e) {
        const target = /** @type {HTMLElement} */e.currentTarget;
        let actionItems = undefined;
        if (target.classList.contains('detail-action-item')) {
          actionItems = [...this.buttonList, ...this.dropdownItemList];
        } else if (target.classList.contains('edit-action-item')) {
          actionItems = [...this.buttonEditList, ...this.dropdownEditItemList];
        }
        Espo.Utils.handleAction(this, e.originalEvent, target, {
          actionItems: actionItems
        });
      },
      /** @this DetailRecordView */
      'click [data-action="showMoreDetailPanels"]': function () {
        this.showMoreDetailPanels();
      },
      /** @this DetailRecordView */
      'click .middle-tabs > button': function (e) {
        const tab = parseInt($(e.currentTarget).attr('data-tab'));
        this.selectTab(tab);
      }
    };

    /**
     * An `edit` action.
     */
    actionEdit() {
      if (!this.editModeDisabled) {
        this.setEditMode();
        this.focusOnFirstDiv();
        $(window).scrollTop(0);
        return;
      }
      const options = {
        id: this.model.id,
        model: this.model.clone()
      };
      if (this.model.collection) {
        const index = this.model.collection.indexOf(this.model);
        if (index > -1) {
          options.model.collection = this.model.collection;
          options.model.collection.models[index] = options.model;
        }
      }
      if (this.options.rootUrl) {
        options.rootUrl = this.options.rootUrl;
      }
      if (this.inlineEditModeIsOn) {
        options.attributes = this.getChangedAttributes();
        this.resetModelChanges();
      }
      this.getRouter().navigate(`#${this.scope}/edit/${this.model.id}`, {
        trigger: false
      });
      this.getRouter().dispatch(this.scope, 'edit', options);
    }

    // noinspection JSUnusedGlobalSymbols
    actionDelete() {
      this.delete();
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * A `save` action.
     *
     * @param {{options?: module:views/record/base~saveOptions}} [data] Data.
     * @return Promise
     */
    actionSave(data) {
      data = data || {};
      const modeBeforeSave = this.mode;
      const promise = this.save(data.options).catch(reason => {
        if (modeBeforeSave === this.MODE_EDIT && ['error', 'cancel'].includes(reason)) {
          this.setEditMode();
        }
        return Promise.reject(reason);
      });
      if (!this.lastSaveCancelReason || this.lastSaveCancelReason === 'notModified') {
        this.setDetailMode();
        this.focusOnFirstDiv();
        $(window).scrollTop(0);
      }
      return promise;
    }
    actionCancelEdit() {
      this.cancelEdit();
      this.focusOnFirstDiv();
      $(window).scrollTop(0);
    }
    focusOnFirstDiv() {
      const element = /** @type {HTMLElement} */this.$el.find('> div').get(0);
      if (element) {
        element.focus({
          preventScroll: true
        });
      }
    }

    /**
     * A `save-and-continue-editing` action.
     */
    actionSaveAndContinueEditing(data) {
      data = data || {};
      this.save(data.options).catch(() => {});
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * A `self-assign` action.
     */
    actionSelfAssign() {
      const attributes = {
        assignedUserId: this.getUser().id,
        assignedUserName: this.getUser().get('name')
      };
      if ('getSelfAssignAttributes' in this) {
        const attributesAdditional = this.getSelfAssignAttributes();
        if (attributesAdditional) {
          for (const i in attributesAdditional) {
            attributes[i] = attributesAdditional[i];
          }
        }
      }
      this.model.save(attributes, {
        patch: true
      }).then(() => {
        Espo.Ui.success(this.translate('Self-Assigned'));
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * A `convert-currency` action.
     */
    actionConvertCurrency() {
      this.createView('modalConvertCurrency', 'views/modals/convert-currency', {
        entityType: this.entityType,
        model: this.model
      }, view => {
        view.render();
        this.listenToOnce(view, 'after:update', attributes => {
          let isChanged = false;
          for (const a in attributes) {
            if (attributes[a] !== this.model.get(a)) {
              isChanged = true;
              break;
            }
          }
          if (!isChanged) {
            Espo.Ui.warning(this.translate('notUpdated', 'messages'));
            return;
          }
          this.model.fetch().then(() => {
            Espo.Ui.success(this.translate('done', 'messages'));
          });
        });
      });
    }

    /**
     * Compose attribute values for a self-assignment.
     *
     * @protected
     * @return {Object.<string,*>|null}
     */
    getSelfAssignAttributes() {
      return null;
    }

    /**
     * Set up action items.
     *
     * @protected
     */
    setupActionItems() {
      if (this.model.isNew()) {
        this.isNew = true;
        this.removeActionItem('delete');
      } else if (this.getMetadata().get(['clientDefs', this.scope, 'removeDisabled'])) {
        this.removeActionItem('delete');
      }
      if (this.duplicateAction) {
        if (this.getAcl().check(this.entityType, 'create') && !this.getMetadata().get(['clientDefs', this.scope, 'duplicateDisabled'])) {
          this.addDropdownItem({
            label: 'Duplicate',
            name: 'duplicate',
            groupIndex: 0
          });
        }
      }
      if (this.selfAssignAction) {
        if (this.getAcl().check(this.entityType, 'edit') && !~this.getAcl().getScopeForbiddenFieldList(this.entityType).indexOf('assignedUser') && !this.getUser().isPortal()) {
          if (this.model.has('assignedUserId')) {
            this.addDropdownItem({
              label: 'Self-Assign',
              name: 'selfAssign',
              hidden: !!this.model.get('assignedUserId'),
              groupIndex: 0
            });
            this.listenTo(this.model, 'change:assignedUserId', () => {
              if (!this.model.get('assignedUserId')) {
                this.showActionItem('selfAssign');
              } else {
                this.hideActionItem('selfAssign');
              }
            });
          }
        }
      }
      if (this.type === this.TYPE_DETAIL && this.printPdfAction) {
        let printPdfAction = true;
        if (!~(this.getHelper().getAppParam('templateEntityTypeList') || []).indexOf(this.entityType)) {
          printPdfAction = false;
        }
        if (printPdfAction) {
          this.addDropdownItem({
            label: 'Print to PDF',
            name: 'printPdf',
            groupIndex: 6
          });
        }
      }
      if (this.type === this.TYPE_DETAIL && this.convertCurrencyAction) {
        if (this.getAcl().check(this.entityType, 'edit') && !this.getMetadata().get(['clientDefs', this.scope, 'convertCurrencyDisabled'])) {
          const currencyFieldList = this.getFieldManager().getEntityTypeFieldList(this.entityType, {
            type: 'currency',
            acl: 'edit'
          });
          if (currencyFieldList.length) {
            this.addDropdownItem({
              label: 'Convert Currency',
              name: 'convertCurrency',
              groupIndex: 5
            });
          }
        }
      }
      if (this.type === this.TYPE_DETAIL && this.getMetadata().get(['scopes', this.scope, 'hasPersonalData'])) {
        if (this.getAcl().getPermissionLevel('dataPrivacyPermission') === 'yes') {
          this.dropdownItemList.push({
            label: 'View Personal Data',
            name: 'viewPersonalData',
            groupIndex: 4
          });
        }
      }
      if (this.type === this.TYPE_DETAIL && this.getMetadata().get(['scopes', this.scope, 'stream'])) {
        this.addDropdownItem({
          label: 'View Followers',
          name: 'viewFollowers',
          groupIndex: 4
        });
      }
      if (this.type === this.TYPE_DETAIL) {
        const actionItemSetup = new _actionItemSetup.default();
        actionItemSetup.setup(this, this.type, promise => this.wait(promise), item => this.addDropdownItem(item), name => this.showActionItem(name), name => this.hideActionItem(name));
        if (this.saveAndContinueEditingAction) {
          this.dropdownEditItemList.push({
            name: 'saveAndContinueEditing',
            label: 'Save & Continue Editing',
            title: 'Ctrl+S',
            groupIndex: 0
          });
        }
      }
    }

    /**
     * Disable action items.
     */
    disableActionItems() {
      // noinspection JSDeprecatedSymbols
      this.disableButtons();
    }

    /**
     * Enable action items.
     */
    enableActionItems() {
      // noinspection JSDeprecatedSymbols
      this.enableButtons();
    }

    /**
     * Hide a button or dropdown action item.
     *
     * @param {string} name A name.
     */
    hideActionItem(name) {
      for (const item of this.buttonList) {
        if (item.name === name) {
          item.hidden = true;
          break;
        }
      }
      for (const item of this.dropdownItemList) {
        if (item.name === name) {
          item.hidden = true;
          break;
        }
      }
      for (const item of this.dropdownEditItemList) {
        if (item.name === name) {
          item.hidden = true;
          break;
        }
      }
      for (const item of this.buttonEditList) {
        if (item.name === name) {
          item.hidden = true;
          break;
        }
      }
      if (this.isRendered()) {
        this.$detailButtonContainer.find('li > .action[data-action="' + name + '"]').parent().addClass('hidden');
        this.$detailButtonContainer.find('button.action[data-action="' + name + '"]').addClass('hidden');
        if (this.isDropdownItemListEmpty()) {
          this.$dropdownItemListButton.addClass('hidden');
        }
        if (this.isDropdownEditItemListEmpty()) {
          this.$dropdownEditItemListButton.addClass('hidden');
        }
        this.adjustButtons();
      }
    }

    /**
     * Show a button or dropdown action item.
     *
     * @param {string} name A name.
     */
    showActionItem(name) {
      for (const item of this.buttonList) {
        if (item.name === name) {
          item.hidden = false;
          break;
        }
      }
      for (const item of this.dropdownItemList) {
        if (item.name === name) {
          item.hidden = false;
          break;
        }
      }
      for (const item of this.dropdownEditItemList) {
        if (item.name === name) {
          item.hidden = false;
          break;
        }
      }
      for (const item of this.buttonEditList) {
        if (item.name === name) {
          item.hidden = false;
          break;
        }
      }
      if (this.isRendered()) {
        this.$detailButtonContainer.find('li > .action[data-action="' + name + '"]').parent().removeClass('hidden');
        this.$detailButtonContainer.find('button.action[data-action="' + name + '"]').removeClass('hidden');
        if (!this.isDropdownItemListEmpty()) {
          this.$dropdownItemListButton.removeClass('hidden');
        }
        if (!this.isDropdownEditItemListEmpty()) {
          this.$dropdownEditItemListButton.removeClass('hidden');
        }
        this.adjustButtons();
      }
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Disable a button or dropdown action item.
     *
     * @param {string} name A name.
     */
    disableActionItem(name) {
      for (const item of this.buttonList) {
        if (item.name === name) {
          item.disabled = true;
          break;
        }
      }
      for (const item of this.dropdownItemList) {
        if (item.name === name) {
          item.disabled = true;
          break;
        }
      }
      for (const item of this.dropdownEditItemList) {
        if (item.name === name) {
          item.disabled = true;
          break;
        }
      }
      for (const item of this.buttonEditList) {
        if (item.name === name) {
          item.disabled = true;
          break;
        }
      }
      if (this.isRendered()) {
        this.$detailButtonContainer.find('li > .action[data-action="' + name + '"]').parent().addClass('disabled').attr('disabled', 'disabled');
        this.$detailButtonContainer.find('button.action[data-action="' + name + '"]').addClass('disabled').attr('disabled', 'disabled');
      }
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Enable a button or dropdown action item.
     *
     * @param {string} name A name.
     */
    enableActionItem(name) {
      for (const item of this.buttonList) {
        if (item.name === name) {
          item.disabled = false;
          break;
        }
      }
      for (const item of this.dropdownItemList) {
        if (item.name === name) {
          item.disabled = false;
          break;
        }
      }
      for (const item of this.dropdownEditItemList) {
        if (item.name === name) {
          item.disabled = false;
          break;
        }
      }
      for (const item of this.buttonEditList) {
        if (item.name === name) {
          item.disabled = false;
          break;
        }
      }
      if (this.isRendered()) {
        this.$detailButtonContainer.find('li > .action[data-action="' + name + '"]').parent().removeClass('disabled').removeAttr('disabled');
        this.$detailButtonContainer.find('button.action[data-action="' + name + '"]').removeClass('disabled').removeAttr('disabled');
      }
    }

    /**
     * Whether an action item is visible and not disabled.
     *
     * @param {string} name An action item name.
     */
    hasAvailableActionItem(name) {
      if (this.allActionItemsDisabled) {
        return false;
      }
      if (this.type === this.TYPE_DETAIL && this.mode === this.MODE_EDIT) {
        const hasButton = this.buttonEditList.findIndex(item => item.name === name && !item.disabled && !item.hidden) !== -1;
        if (hasButton) {
          return true;
        }
        return this.dropdownEditItemList.findIndex(item => item.name === name && !item.disabled && !item.hidden) !== -1;
      }
      const hasButton = this.buttonList.findIndex(item => item.name === name && !item.disabled && !item.hidden) !== -1;
      if (hasButton) {
        return true;
      }
      return this.dropdownItemList.findIndex(item => item.name === name && !item.disabled && !item.hidden) !== -1;
    }

    /**
     * Show a panel.
     *
     * @param {string} name A panel name.
     * @param {module:views/record/detail~panelSoftLockedType} [softLockedType='default']
     *   A soft-locked type.
     */
    showPanel(name, softLockedType) {
      if (this.recordHelper.getPanelStateParam(name, 'hiddenLocked')) {
        return;
      }
      softLockedType = softLockedType || 'default';
      const softLockedParam = 'hidden' + Espo.Utils.upperCaseFirst(softLockedType) + 'Locked';
      this.recordHelper.setPanelStateParam(name, softLockedParam, false);
      if (softLockedType === 'dynamicLogic' && this.recordHelper.getPanelStateParam(name, 'hidden') === false) {
        return;
      }
      for (let i = 0; i < this.panelSoftLockedTypeList.length; i++) {
        const iType = this.panelSoftLockedTypeList[i];
        if (iType === softLockedType) {
          continue;
        }
        const iParam = 'hidden' + Espo.Utils.upperCaseFirst(iType) + 'Locked';
        if (this.recordHelper.getPanelStateParam(name, iParam)) {
          return;
        }
      }
      const middleView = this.getMiddleView();
      if (middleView) {
        middleView.showPanelInternal(name);
      }
      const bottomView = this.getBottomView();
      if (bottomView) {
        if ('showPanel' in bottomView) {
          bottomView.showPanel(name);
        }
      } else if (this.bottomView) {
        this.once('ready', () => {
          const view = this.getBottomView();
          if (view) {
            if ('processShowPanel' in view) {
              view.processShowPanel(name);
              return;
            }
            if ('showPanel' in view) {
              view.showPanel(name);
            }
          }
        });
      }
      const sideView = this.getSideView();
      if (sideView) {
        if ('showPanel' in sideView) {
          sideView.showPanel(name);
        }
      } else if (this.sideView) {
        this.once('ready', () => {
          const view = this.getSideView();
          if (view) {
            if ('processShowPanel' in view) {
              view.processShowPanel(name);
              return;
            }
            if ('showPanel' in view) {
              view.showPanel(name);
            }
          }
        });
      }
      this.recordHelper.setPanelStateParam(name, 'hidden', false);
      if (this.middlePanelDefs[name]) {
        this.controlTabVisibilityShow(this.middlePanelDefs[name].tabNumber);
        this.adjustMiddlePanels();
      }
      this.recordHelper.trigger('panel-show');
    }

    /**
     * Hide a panel.
     *
     * @param {string} name A panel name.
     * @param {boolean} [locked=false] Won't be able to un-hide.
     * @param {module:views/record/detail~panelSoftLockedType} [softLockedType='default']
     *   A soft-locked type.
     */
    hidePanel(name, locked, softLockedType) {
      softLockedType = softLockedType || 'default';
      if (locked) {
        this.recordHelper.setPanelStateParam(name, 'hiddenLocked', true);
      }
      const softLockedParam = 'hidden' + Espo.Utils.upperCaseFirst(softLockedType) + 'Locked';
      this.recordHelper.setPanelStateParam(name, softLockedParam, true);
      if (softLockedType === 'dynamicLogic' && this.recordHelper.getPanelStateParam(name, 'hidden') === true) {
        return;
      }
      const middleView = this.getMiddleView();
      if (middleView) {
        middleView.hidePanelInternal(name);
      }
      const bottomView = this.getBottomView();
      if (bottomView) {
        if ('hidePanel' in bottomView) {
          bottomView.hidePanel(name);
        }
      } else if (this.bottomView) {
        this.once('ready', () => {
          const view = this.getBottomView();
          if (view) {
            if ('processHidePanel' in view) {
              view.processHidePanel(name);
              return;
            }
            if ('hidePanel' in view) {
              view.hidePanel(name);
            }
          }
        });
      }
      const sideView = this.getSideView();
      if (sideView) {
        if ('hidePanel' in sideView) {
          sideView.hidePanel(name);
        }
      } else if (this.sideView) {
        this.once('ready', () => {
          const view = this.getSideView();
          if (view) {
            if ('processHidePanel' in view) {
              view.processHidePanel(name);
              return;
            }
            if ('hidePanel' in view) {
              view.hidePanel(name);
            }
          }
        });
      }
      this.recordHelper.setPanelStateParam(name, 'hidden', true);
      if (this.middlePanelDefs[name]) {
        this.controlTabVisibilityHide(this.middlePanelDefs[name].tabNumber);
        this.adjustMiddlePanels();
      }
    }
    afterRender() {
      this.$middle = this.$el.find('.middle').first();
      if (this.bottomView) {
        this.$bottom = this.$el.find('.bottom').first();
      }
      this.initElementReferences();
      this.adjustMiddlePanels();
      this.adjustButtons();
      this.initStickableButtonsContainer();
      this.initFieldsControlBehaviour();
    }

    /**
     * @private
     */
    initFieldsControlBehaviour() {
      const fields = this.getFieldViews();
      let fieldInEditMode = null;
      for (const field in fields) {
        const fieldView = fields[field];
        this.listenTo(fieldView, 'edit', view => {
          if (fieldInEditMode && fieldInEditMode.isEditMode()) {
            fieldInEditMode.inlineEditClose();
          }
          fieldInEditMode = view;
        });
        this.listenTo(fieldView, 'inline-edit-on', () => {
          this.inlineEditModeIsOn = true;
        });
        this.listenTo(fieldView, 'inline-edit-off', o => {
          o = o || {};
          if (o.all) {
            return;
          }
          this.inlineEditModeIsOn = false;
          this.setIsNotChanged();
        });
        this.listenTo(fieldView, 'after:inline-edit-off', o => {
          if (this.updatedAttributes && !o.noReset) {
            this.resetModelChanges();
          }
        });
      }
    }

    /** @private */
    initStickableButtonsContainer() {
      const helper = new _stickyBar.default(this, this.stickButtonsFormBottomSelector, this.stickButtonsContainerAllTheWay, this.numId);
      helper.init();
    }
    fetch() {
      let data = super.fetch();
      if (this.hasView('side')) {
        const view = this.getSideView();
        if ('fetch' in view) {
          data = _.extend(data, view.fetch());
        }
      }
      if (this.hasView('bottom')) {
        const view = this.getBottomView();
        if ('fetch' in view) {
          data = _.extend(data, view.fetch());
        }
      }
      return data;
    }
    setEditMode() {
      this.trigger('before:set-edit-mode');
      this.inlineEditModeIsOn = false;
      this.$el.find('.record-buttons').addClass('hidden');
      this.$el.find('.edit-buttons').removeClass('hidden');
      return new Promise(resolve => {
        const fields = this.getFieldViews(true);
        const promiseList = [];
        for (const field in fields) {
          const fieldView = fields[field];
          if (fieldView.readOnly) {
            continue;
          }
          if (fieldView.isEditMode()) {
            fieldView.fetchToModel();
            fieldView.removeInlineEditLinks();
            fieldView.setIsInlineEditMode(false);
          }
          promiseList.push(fieldView.setEditMode().then(() => {
            return fieldView.render();
          }));
        }
        this.mode = this.MODE_EDIT;
        this.trigger('after:set-edit-mode');
        this.trigger('after:mode-change', this.MODE_EDIT);
        Promise.all(promiseList).then(() => resolve());
      });
    }
    setDetailMode() {
      this.trigger('before:set-detail-mode');
      this.$el.find('.edit-buttons').addClass('hidden');
      this.$el.find('.record-buttons').removeClass('hidden');
      this.inlineEditModeIsOn = false;
      return new Promise(resolve => {
        const fields = this.getFieldViews(true);
        const promiseList = [];
        for (const field in fields) {
          const fieldView = fields[field];
          if (!fieldView.isDetailMode()) {
            if (fieldView.isEditMode()) {
              fieldView.trigger('inline-edit-off', {
                all: true
              });
            }
            promiseList.push(fieldView.setDetailMode().then(() => fieldView.render()));
          }
        }
        this.mode = this.MODE_DETAIL;
        this.trigger('after:set-detail-mode');
        this.trigger('after:mode-change', this.MODE_DETAIL);
        Promise.all(promiseList).then(() => resolve());
      });
    }
    cancelEdit() {
      this.resetModelChanges();
      this.setDetailMode();
      this.setIsNotChanged();
    }

    /**
     * Whether in edit mode.
     * @return {boolean}
     */
    isEditMode() {
      return this.mode === 'edit';
    }
    resetModelChanges() {
      let skipReRender = true;
      if (this.updatedAttributes) {
        this.attributes = this.updatedAttributes;
        this.updatedAttributes = null;
        skipReRender = false;
      }
      const attributes = this.model.attributes;
      for (const attr in attributes) {
        if (!(attr in this.attributes)) {
          this.model.unset(attr);
        }
      }
      this.model.set(this.attributes, {
        skipReRenderInEditMode: skipReRender,
        action: 'cancel-edit'
      });
    }
    delete() {
      this.confirm({
        message: this.translate('removeRecordConfirmation', 'messages', this.scope),
        confirmText: this.translate('Remove')
      }, () => {
        this.trigger('before:delete');
        this.trigger('delete');
        Espo.Ui.notifyWait();
        const collection = this.model.collection;
        this.model.destroy({
          wait: true
        }).then(() => {
          if (collection) {
            if (collection.total > 0) {
              collection.total--;
            }
          }
          this.model.set('deleted', true, {
            silent: true
          });
          Espo.Ui.success(this.translate('Removed'), {
            suppress: true
          });
          this.trigger('after:delete');
          this.exit('delete');
        });
      });
    }

    /**
     * Get field views.
     *
     * @param {boolean} [withHidden] With hidden.
     * @return {Object.<string, module:views/fields/base>}
     */
    getFieldViews(withHidden) {
      const fields = {};
      if (this.hasView('middle')) {
        if ('getFieldViews' in this.getMiddleView()) {
          _.extend(fields, Espo.Utils.clone(this.getMiddleView().getFieldViews()));
        }
      }
      if (this.hasView('side')) {
        if ('getFieldViews' in this.getSideView()) {
          _.extend(fields, this.getSideView().getFieldViews(withHidden));
        }
      }
      if (this.hasView('bottom')) {
        if ('getFieldViews' in this.getBottomView()) {
          _.extend(fields, this.getBottomView().getFieldViews(withHidden));
        }
      }
      return fields;
    }

    /**
     * Get a field view.
     *
     * @param {string} name A field name.
     * @return {module:views/fields/base|null}
     */
    getFieldView(name) {
      let view;
      if (this.hasView('middle')) {
        view = (this.getMiddleView().getFieldViews() || {})[name];
      }
      if (!view && this.hasView('side')) {
        view = (this.getSideView().getFieldViews(true) || {})[name];
      }
      if (!view && this.hasView('bottom')) {
        view = (this.getBottomView().getFieldViews(true) || {})[name];
      }
      return view || null;
    }

    // @todo Remove.
    handleDataBeforeRender(data) {}
    data() {
      let navigateButtonsEnabled = !this.navigateButtonsDisabled && !!this.model.collection;
      let previousButtonEnabled = false;
      let nextButtonEnabled = false;
      if (navigateButtonsEnabled) {
        if (this.indexOfRecord > 0 || this.model.collection.offset) {
          previousButtonEnabled = true;
        }
        const total = this.model.collection.total !== undefined ? this.model.collection.total : this.model.collection.length;
        if (this.indexOfRecord < total - 1 - this.model.collection.offset) {
          nextButtonEnabled = true;
        } else {
          if (total === -1) {
            nextButtonEnabled = true;
          } else if (total === -2) {
            if (this.indexOfRecord < this.model.collection.length - 1 - this.model.collection.offset) {
              nextButtonEnabled = true;
            }
          }
        }
        if (!previousButtonEnabled && !nextButtonEnabled) {
          navigateButtonsEnabled = false;
        }
      }
      const hasMiddleTabs = this.hasTabs();
      const middleTabDataList = hasMiddleTabs ? this.getMiddleTabDataList() : [];
      return {
        scope: this.scope,
        entityType: this.entityType,
        buttonList: this.buttonList,
        buttonEditList: this.buttonEditList,
        dropdownItemList: this.getDropdownItemDataList(),
        dropdownEditItemList: this.dropdownEditItemList,
        dropdownItemListEmpty: this.isDropdownItemListEmpty(),
        dropdownEditItemListEmpty: this.isDropdownEditItemListEmpty(),
        buttonsDisabled: this.buttonsDisabled,
        id: this.id,
        isWide: this.isWide,
        isSmall: this.type === 'editSmall' || this.type === 'detailSmall',
        navigateButtonsEnabled: navigateButtonsEnabled,
        previousButtonEnabled: previousButtonEnabled,
        nextButtonEnabled: nextButtonEnabled,
        hasMiddleTabs: hasMiddleTabs,
        middleTabDataList: middleTabDataList
      };
    }

    /**
     * @private
     * @return {Array<module:views/record/detail~dropdownItem|false>}
     */
    getDropdownItemDataList() {
      /** @type {Array<module:views/record/detail~dropdownItem[]>} */
      const dropdownGroups = [];
      this.dropdownItemList.forEach(item => {
        // For bc.
        if (item === false) {
          return;
        }
        const index = (item.groupIndex === undefined ? 9999 : item.groupIndex) + 100;
        if (dropdownGroups[index] === undefined) {
          dropdownGroups[index] = [];
        }
        dropdownGroups[index].push(item);
      });
      const dropdownItemList = [];
      dropdownGroups.forEach(list => {
        list.forEach(it => dropdownItemList.push(it));
        dropdownItemList.push(false);
      });
      return dropdownItemList;
    }
    init() {
      this.entityType = this.model.entityType || this.model.name || 'Common';
      this.scope = this.options.scope || this.entityType;
      this.layoutName = this.options.layoutName || this.layoutName;
      this.detailLayout = this.options.detailLayout || this.detailLayout;
      this.type = this.options.type || this.type;
      this.buttonList = this.options.buttonList || this.buttonList;
      this.dropdownItemList = this.options.dropdownItemList || this.dropdownItemList;
      this.buttonList = Espo.Utils.cloneDeep(this.buttonList);
      this.buttonEditList = Espo.Utils.cloneDeep(this.buttonEditList);
      this.dropdownItemList = Espo.Utils.cloneDeep(this.dropdownItemList);
      this.dropdownEditItemList = Espo.Utils.cloneDeep(this.dropdownEditItemList);
      this.returnAfterCreate = this.options.returnAfterCreate;
      this.returnUrl = this.options.returnUrl || this.returnUrl;
      this.returnDispatchParams = this.options.returnDispatchParams || this.returnDispatchParams;
      this.exit = this.options.exit || this.exit;
      if (this.shortcutKeys) {
        this.shortcutKeys = Espo.Utils.cloneDeep(this.shortcutKeys);
      }
    }
    isDropdownItemListEmpty() {
      if (this.dropdownItemList.length === 0) {
        return true;
      }
      let isEmpty = true;
      this.dropdownItemList.forEach(item => {
        if (!item.hidden) {
          isEmpty = false;
        }
      });
      return isEmpty;
    }
    isDropdownEditItemListEmpty() {
      if (this.dropdownEditItemList.length === 0) {
        return true;
      }
      let isEmpty = true;
      this.dropdownEditItemList.forEach(item => {
        if (!item.hidden) {
          isEmpty = false;
        }
      });
      return isEmpty;
    }
    setup() {
      if (typeof this.model === 'undefined') {
        throw new Error('Model has not been injected into record view.');
      }
      this.recordHelper = this.options.recordHelper || new _viewRecordHelper.default(this.defaultFieldStates, this.defaultPanelStates);
      this._initInlineEditSave();
      const collection = this.collection = this.model.collection;
      if (collection) {
        this.listenTo(this.model, 'destroy', () => {
          collection.remove(this.model.id);
          collection.trigger('sync', collection, {}, {});
        });
        if ('indexOfRecord' in this.options) {
          this.indexOfRecord = this.options.indexOfRecord;
        } else {
          this.indexOfRecord = collection.indexOf(this.model);
        }
      }

      /** @type {Object.<string,*>|null} */
      this.middlePanelDefs = {};

      /** @type {Object.<string,*>[]} */
      this.middlePanelDefsList = [];
      if (this.getUser().isPortal() && !this.portalLayoutDisabled) {
        if (this.getMetadata().get(['clientDefs', this.scope, 'additionalLayouts', this.layoutName + 'Portal'])) {
          this.layoutName += 'Portal';
        }
      }
      this.numId = Math.floor(Math.random() * 10000 + 1);

      // For testing purpose.
      $(window).on('fetch-record.' + this.cid, () => this._webSocketDebounceHelper.process());
      this.once('remove', () => {
        if (this.isChanged) {
          this.resetModelChanges();
        }
        this.setIsNotChanged();
        $(window).off('scroll.detail-' + this.numId);
        $(window).off('fetch-record.' + this.cid);
      });
      this.id = Espo.Utils.toDom(this.entityType) + '-' + Espo.Utils.toDom(this.type) + '-' + this.numId;
      this.isNew = this.model.isNew();
      if (!this.editModeDisabled) {
        if ('editModeDisabled' in this.options) {
          this.editModeDisabled = this.options.editModeDisabled;
        }
      }
      this.confirmLeaveDisabled = this.options.confirmLeaveDisabled || this.confirmLeaveDisabled;
      this.buttonsDisabled = this.options.buttonsDisabled || this.buttonsDisabled;

      // for backward compatibility
      // @todo remove
      if ('buttonsPosition' in this.options && !this.options.buttonsPosition) {
        this.buttonsDisabled = true;
      }
      if ('isWide' in this.options) {
        this.isWide = this.options.isWide;
      }
      if ('sideView' in this.options) {
        this.sideView = this.options.sideView;
      }
      if ('bottomView' in this.options) {
        this.bottomView = this.options.bottomView;
      }
      this.sideDisabled = this.options.sideDisabled || this.sideDisabled;
      this.bottomDisabled = this.options.bottomDisabled || this.bottomDisabled;
      this.readOnly = this.options.readOnly || this.readOnly;
      if (!this.readOnly && !this.isNew) {
        this.readOnly = this.getMetadata().get(['clientDefs', this.scope, 'editDisabled']) || false;
      }
      if (this.getMetadata().get(['clientDefs', this.scope, 'createDisabled'])) {
        this.duplicateAction = false;
      }
      if ((this.getConfig().get('currencyList') || []).length <= 1) {
        this.convertCurrencyAction = false;
      }
      this.readOnlyLocked = this.readOnly;
      this.inlineEditDisabled = this.inlineEditDisabled || this.getMetadata().get(['clientDefs', this.scope, 'inlineEditDisabled']) || false;
      this.inlineEditDisabled = this.options.inlineEditDisabled || this.inlineEditDisabled;
      this.navigateButtonsDisabled = this.options.navigateButtonsDisabled || this.navigateButtonsDisabled;
      this.portalLayoutDisabled = this.options.portalLayoutDisabled || this.portalLayoutDisabled;
      this.dynamicLogicDefs = this.options.dynamicLogicDefs || this.dynamicLogicDefs;
      this.accessControlDisabled = this.options.accessControlDisabled || this.accessControlDisabled;
      this.dataObject = this.options.dataObject || {};
      this.rootData = this.options.rootData || {};
      this.setupActionItems();
      this.setupBeforeFinal();
      this.setupDynamicBehavior();
      this.on('after:render', () => {
        this.initElementReferences();
      });
      this._webSocketDebounceHelper = new _debounce.default({
        interval: this._webSocketDebounceInterval,
        handler: () => this.handleRecordUpdate()
      });
      if (!this.isNew && !!this.getHelper().webSocketManager && this.getMetadata().get(['scopes', this.entityType, 'object'])) {
        this.subscribeToWebSocket();
        this.once('remove', () => {
          if (this.isSubscribedToWebSocket) {
            this.unsubscribeFromWebSocket();
          }
        });
      }
      this.wait(this.getHelper().processSetupHandlers(this, this.setupHandlerType));
      this.initInlineEditDynamicWithLogicInteroperability();
      this.forcePatchAttributeDependencyMap = this.getMetadata().get(['clientDefs', this.scope, 'forcePatchAttributeDependencyMap']) || {};
    }
    setupBeforeFinal() {
      if (!this.accessControlDisabled) {
        this.manageAccess();
      }
      this.attributes = this.model.getClonedAttributes();
      if (this.options.attributes) {
        this.model.set(this.options.attributes);
      }
      this.listenTo(this.model, 'sync', () => {
        this.attributes = this.model.getClonedAttributes();
      });
      this.listenTo(this.model, 'change', (m, o) => {
        if (o.sync) {
          for (const attribute in m.attributes) {
            if (!m.hasChanged(attribute)) {
              continue;
            }
            this.attributes[attribute] = Espo.Utils.cloneDeep(m.get(attribute));
          }
          return;
        }
        if (this.mode === this.MODE_EDIT || this.inlineEditModeIsOn) {
          this.setIsChanged();
        }
      });
    }

    /**
     * @protected
     */
    setupDynamicBehavior() {
      const dependencyDefs = Espo.Utils.clone(this.getMetadata().get(['clientDefs', this.entityType, 'formDependency']) || {});

      // noinspection JSDeprecatedSymbols
      this.dependencyDefs = _.extend(dependencyDefs, this.dependencyDefs);
      this.initDependency();
      const dynamicLogic = {
        ...this.getMetadata().get(`logicDefs.${this.entityType}`, {})
      };
      this.dynamicLogicDefs = _.extend(dynamicLogic, this.dynamicLogicDefs);
      this.initDynamicLogic();
      this.setupFieldLevelSecurity();
      this.initDynamicHandler();
    }

    /**
     * @private
     */
    _initInlineEditSave() {
      this.listenTo(this.recordHelper, 'inline-edit-save', (field, o) => {
        this.inlineEditSave(field, o);
      });
    }

    /**
     * @param {string} field
     * @param {module:views/record/base~saveOptions} [options]
     */
    inlineEditSave(field, options) {
      const view = this.getFieldView(field);
      if (!view) {
        throw new Error(`No field '${field}'.`);
      }
      options = _.extend({
        inline: true,
        field: field,
        afterValidate: () => {
          if (options.bypassClose) {
            return;
          }
          view.inlineEditClose(true);
        }
      }, options || {});
      this.save(options).then(() => {
        view.trigger('after:inline-save');
        view.trigger('after:save');
        if (options.bypassClose) {
          view.initialAttributes = this.model.getClonedAttributes();
        }
      }).catch(reason => {
        if (reason === 'notModified') {
          if (options.bypassClose) {
            return;
          }
          view.inlineEditClose(true);
          return;
        }
        if (reason === 'error') {
          if (options.bypassClose) {
            return;
          }
          const initialAttributes = {
            ...view.initialAttributes
          };
          view.inlineEdit().then(() => view.initialAttributes = initialAttributes);
        }
      });
    }

    /**
     * @private
     */
    initInlineEditDynamicWithLogicInteroperability() {
      let blockEdit = false;
      const process = (type, field) => {
        if (!this.inlineEditModeIsOn || this.editModeDisabled) {
          return;
        }
        if (blockEdit) {
          return;
        }
        if (type === 'required') {
          const fieldView = this.getFieldView(field);
          if (fieldView.validateRequired) {
            fieldView.suspendValidationMessage();
            try {
              if (!fieldView.validateRequired()) {
                return;
              }
            } catch (e) {}
          }
        }
        blockEdit = true;
        setTimeout(() => blockEdit = false, 300);
        setTimeout(() => {
          this.setEditMode();
          this.getFieldViewList().forEach(view => view.removeInlineEditLinks());
        }, 10);
      };
      this.on('set-field-required', field => process('required', field));
      this.on('set-field-option-list', field => process('options', field));
      this.on('reset-field-option-list', field => process('options', field));
    }

    /**
     * @private
     */
    initDynamicHandler() {
      const dynamicHandlerClassName = this.dynamicHandlerClassName || this.getMetadata().get(['clientDefs', this.scope, 'dynamicHandler']);
      const init = /** import('dynamic-handler').default */dynamicHandler => {
        this.listenTo(this.model, 'change', (model, o) => {
          if ('onChange' in dynamicHandler) {
            dynamicHandler.onChange.call(dynamicHandler, model, o);
          }
          const changedAttributes = model.changedAttributes();
          for (const attribute in changedAttributes) {
            const methodName = 'onChange' + Espo.Utils.upperCaseFirst(attribute);
            if (methodName in dynamicHandler) {
              dynamicHandler[methodName].call(dynamicHandler, model, changedAttributes[attribute], o);
            }
          }
        });
        if ('init' in dynamicHandler) {
          dynamicHandler.init();
        }
      };
      if (dynamicHandlerClassName) {
        this.wait(new Promise(resolve => {
          Espo.loader.require(dynamicHandlerClassName, DynamicHandler => {
            const dynamicHandler = this.dynamicHandler = new DynamicHandler(this);
            init(dynamicHandler);
            resolve();
          });
        }));
      }
      const handlerList = this.getMetadata().get(['clientDefs', this.scope, 'dynamicHandlerList']) || [];
      if (handlerList.length) {
        const self = this;
        const promiseList = [];
        handlerList.forEach(className => {
          promiseList.push(new Promise(resolve => {
            Espo.loader.require(className, DynamicHandler => {
              resolve(new DynamicHandler(self));
            });
          }));
        });
        this.wait(Promise.all(promiseList).then(list => {
          list.forEach(dynamicHandler => {
            init(dynamicHandler);
          });
        }));
      }
    }
    setupFinal() {
      this.build();
      if (this.shortcutKeys && this.options.shortcutKeysEnabled) {
        this.events['keydown.record-detail'] = e => {
          const key = Espo.Utils.getKeyFromKeyEvent(e);
          if (typeof this.shortcutKeys[key] === 'function') {
            this.shortcutKeys[key].call(this, e.originalEvent);
            return;
          }
          const actionName = this.shortcutKeys[key];
          if (!actionName) {
            return;
          }
          e.preventDefault();
          e.stopPropagation();
          const methodName = 'action' + Espo.Utils.upperCaseFirst(actionName);
          if (typeof this[methodName] === 'function') {
            this[methodName]();
            return;
          }
          this[actionName]();
        };
      }
      if (!this.options.focusForCreate) {
        this.once('after:render', () => this.focusOnFirstDiv());
      }
    }
    setIsChanged() {
      this.isChanged = true;
      if (this.confirmLeaveDisabled) {
        return;
      }
      this.setConfirmLeaveOut(true);
    }
    setIsNotChanged() {
      this.isChanged = false;
      if (this.confirmLeaveDisabled) {
        return;
      }
      this.setConfirmLeaveOut(false);
    }

    /**
     * @protected
     * @param {number} indexOfRecord
     */
    switchToModelByIndex(indexOfRecord) {
      const collection = this.model.collection || this.collection;
      if (!collection) {
        return;
      }
      const model = collection.at(indexOfRecord);
      if (!model) {
        console.error("Model is not found in collection by index.");
        return;
      }
      const id = model.id;
      const scope = this.entityType || this.scope;
      this.getRouter().navigate(`#${scope}/view/${id}`, {
        trigger: false
      });
      this.getRouter().dispatch(scope, 'view', {
        id: id,
        model: model,
        indexOfRecord: indexOfRecord,
        rootUrl: this.options.rootUrl
      });
    }
    actionPrevious() {
      this.model.abortLastFetch();
      if (!this.model.collection) {
        return;
      }
      const collection = this.model.collection;
      if (this.indexOfRecord <= 0 && !collection.offset) {
        return;
      }
      if (this.indexOfRecord === 0 && collection.offset > 0 && collection.maxSize) {
        collection.offset = Math.max(0, collection.offset - collection.maxSize);
        collection.fetch().then(() => {
          const indexOfRecord = collection.length - 1;
          if (indexOfRecord < 0) {
            return;
          }
          this.switchToModelByIndex(indexOfRecord);
        });
        return;
      }
      const indexOfRecord = this.indexOfRecord - 1;
      this.switchToModelByIndex(indexOfRecord);
    }
    actionNext() {
      this.model.abortLastFetch();
      if (!this.model.collection) {
        return;
      }
      const collection = this.model.collection;
      if (!(this.indexOfRecord < collection.total - 1 - collection.offset) && collection.total >= 0) {
        return;
      }
      if (collection.total === -2 && this.indexOfRecord >= collection.length - 1 - collection.offset) {
        return;
      }
      const indexOfRecord = this.indexOfRecord + 1;
      if (indexOfRecord <= collection.length - 1 - collection.offset) {
        this.switchToModelByIndex(indexOfRecord);
        return;
      }
      collection.fetch({
        more: true,
        remove: false
      }).then(() => {
        this.switchToModelByIndex(indexOfRecord);
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionViewPersonalData() {
      this.createView('viewPersonalData', 'views/personal-data/modals/personal-data', {
        model: this.model
      }, view => {
        view.render();
        this.listenToOnce(view, 'erase', () => {
          this.clearView('viewPersonalData');
          this.model.fetch();
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionViewFollowers(data) {
      const viewName = this.getMetadata().get(['clientDefs', this.entityType, 'relationshipPanels', 'followers', 'viewModalView']) || this.getMetadata().get(['clientDefs', 'User', 'modalViews', 'relatedList']) || 'views/modals/followers-list';
      const selectDisabled = !this.getUser().isAdmin() && this.getAcl().getPermissionLevel('followerManagementPermission') === 'no' && this.getAcl().getPermissionLevel('portalPermission') === 'no';
      const options = {
        model: this.model,
        link: 'followers',
        scope: 'User',
        title: this.translate('Followers'),
        filtersDisabled: true,
        url: this.entityType + '/' + this.model.id + '/followers',
        createDisabled: true,
        selectDisabled: selectDisabled,
        rowActionsView: 'views/user/record/row-actions/relationship-followers'
      };
      if (data.viewOptions) {
        for (const item in data.viewOptions) {
          options[item] = data.viewOptions[item];
        }
      }
      Espo.Ui.notifyWait();
      this.createView('modalRelatedList', viewName, options, view => {
        Espo.Ui.notify(false);
        view.render();
        this.listenTo(view, 'action', (event, element) => {
          Espo.Utils.handleAction(this, event, element);
        });
        this.listenToOnce(view, 'close', () => {
          this.clearView('modalRelatedList');
        });
        view.listenTo(this.model, 'after:relate:followers', () => {
          this.model.fetch();
        });
        view.listenTo(this.model, 'after:unrelate:followers', () => {
          this.model.fetch();
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    async actionPrintPdf() {
      const view = new _selectTemplate.default({
        entityType: this.entityType,
        onSelect: models => {
          const model = models[0];
          const url = `?entryPoint=pdf&entityType=${this.entityType}&entityId=${this.model.id}` + `&templateId=${model.id}`;
          window.open(url, '_blank');
        }
      });
      await this.assignView('modal', view);
      await view.render();
    }
    afterSave() {
      if (this.isNew) {
        Espo.Ui.success(this.translate('Created'));
      } else {
        Espo.Ui.success(this.translate('Saved'));
      }
      this.enableActionItems();
      this.setIsNotChanged();
      setTimeout(() => {
        this.unblockUpdateWebSocket();
      }, this.blockUpdateWebSocketPeriod);
    }
    beforeSave() {
      Espo.Ui.notify(this.translate('saving', 'messages'));
      this.blockUpdateWebSocket();
    }
    beforeBeforeSave() {
      this.disableActionItems();
    }
    afterSaveError() {
      this.enableActionItems();
    }
    afterNotModified() {
      const msg = this.translate('notModified', 'messages');
      Espo.Ui.warning(msg);
      this.enableActionItems();
      this.setIsNotChanged();
    }
    afterNotValid() {
      Espo.Ui.error(this.translate('Not valid'));
      this.enableActionItems();
    }

    /**
     * @protected
     * @param duplicates
     * @param {Object} options
     * @param {function} resolve
     * @param {function} reject
     * @return {boolean}
     */
    errorHandlerDuplicate(duplicates, options, resolve, reject) {
      Espo.Ui.notify(false);
      this.createView('duplicate', 'views/modals/duplicate', {
        scope: this.entityType,
        duplicates: duplicates,
        model: this.model
      }, view => {
        view.render();
        this.listenToOnce(view, 'save', () => {
          this.actionSave({
            options: {
              headers: {
                'X-Skip-Duplicate-Check': 'true'
              }
            }
          }).then(() => resolve()).catch(() => reject('error'));
        });
        this.listenToOnce(view, 'cancel', () => reject('cancel'));
      });
      return true;
    }

    // noinspection JSUnusedGlobalSymbols
    errorHandlerModified(data, options) {
      Espo.Ui.notify(false);
      const versionNumber = data.versionNumber;
      const values = data.values || {};
      const attributeList = Object.keys(values);
      const diffAttributeList = [];
      attributeList.forEach(attribute => {
        if (this.attributes[attribute] !== values[attribute]) {
          diffAttributeList.push(attribute);
        }
      });
      if (diffAttributeList.length === 0) {
        setTimeout(() => {
          this.model.set('versionNumber', versionNumber, {
            silent: true
          });
          this.attributes.versionNumber = versionNumber;
          if (options.inline && options.field) {
            this.inlineEditSave(options.field);
            return;
          }
          this.actionSave();
        }, 5);
        return;
      }
      this.createView('dialog', 'views/modals/resolve-save-conflict', {
        model: this.model,
        attributeList: diffAttributeList,
        currentAttributes: Espo.Utils.cloneDeep(this.model.attributes),
        originalAttributes: Espo.Utils.cloneDeep(this.attributes),
        actualAttributes: Espo.Utils.cloneDeep(values)
      }).then(view => {
        view.render();
        this.listenTo(view, 'resolve', () => {
          this.model.set('versionNumber', versionNumber, {
            silent: true
          });
          this.attributes.versionNumber = versionNumber;
          for (const attribute in values) {
            this.setInitialAttributeValue(attribute, values[attribute]);
          }
        });
      });
    }

    /**
     * Get a middle view.
     *
     * @return {module:views/record/detail-middle}
     */
    getMiddleView() {
      return this.getView('middle');
    }

    /**
     * Get a side view.
     *
     * @protected
     * @return {module:views/record/detail-side}
     */
    getSideView() {
      return this.getView('side');
    }

    /**
     * Get a bottom view.
     *
     * @protected
     * @return {module:views/record/detail-bottom}
     */
    getBottomView() {
      return this.getView('bottom');
    }
    setReadOnly() {
      if (!this.readOnlyLocked) {
        this.readOnly = true;
      }
      const bottomView = this.getBottomView();
      if (bottomView && 'setReadOnly' in bottomView) {
        bottomView.setReadOnly();
      }
      const sideView = this.getSideView();
      if (sideView && 'setReadOnly' in sideView) {
        sideView.setReadOnly();
      }
      this.getFieldList().forEach(field => {
        this.setFieldReadOnly(field);
      });
    }
    setNotReadOnly(onlyNotSetAsReadOnly) {
      if (!this.readOnlyLocked) {
        this.readOnly = false;
      }
      const bottomView = this.getBottomView();
      if (bottomView && 'setNotReadOnly' in bottomView) {
        bottomView.setNotReadOnly(onlyNotSetAsReadOnly);
      }
      const sideView = this.getSideView();
      if (sideView && 'setNotReadOnly' in sideView) {
        sideView.setNotReadOnly(onlyNotSetAsReadOnly);
      }
      this.getFieldList().forEach(field => {
        if (onlyNotSetAsReadOnly) {
          if (this.recordHelper.getFieldStateParam(field, 'readOnly')) {
            return;
          }
        }
        this.setFieldNotReadOnly(field);
      });
    }
    manageAccessEdit(second) {
      if (this.isNew) {
        return;
      }
      const editAccess = this.getAcl().checkModel(this.model, 'edit', true);
      if (!editAccess || this.readOnlyLocked) {
        this.readOnly = true;
        this.hideActionItem('edit');
        if (this.selfAssignAction) {
          this.hideActionItem('selfAssign');
        }
      } else {
        this.showActionItem('edit');
        if (this.selfAssignAction) {
          this.hideActionItem('selfAssign');
          if (this.model.has('assignedUserId')) {
            if (!this.model.get('assignedUserId')) {
              this.showActionItem('selfAssign');
            }
          }
        }
        if (!this.readOnlyLocked) {
          if (this.readOnly && second) {
            if (this.isReady) {
              this.setNotReadOnly(true);
            } else {
              this.on('ready', () => this.setNotReadOnly(true));
            }
          }
          this.readOnly = false;
        }
      }
      if (editAccess === null) {
        this.listenToOnce(this.model, 'sync', () => {
          this.model.trigger('acl-edit-ready');
          this.manageAccessEdit(true);
        });
      }
    }
    manageAccessDelete() {
      if (this.isNew) {
        return;
      }
      const deleteAccess = this.getAcl().checkModel(this.model, 'delete', true);
      if (!deleteAccess) {
        this.hideActionItem('delete');
      } else {
        this.showActionItem('delete');
      }
      if (deleteAccess === null) {
        this.listenToOnce(this.model, 'sync', () => {
          this.manageAccessDelete(true);
        });
      }
    }
    manageAccessStream() {
      if (this.isNew) {
        return;
      }
      if (~['no', 'own'].indexOf(this.getAcl().getLevel('User', 'read')) && this.getAcl().getPermissionLevel('portalPermission') === 'no') {
        this.hideActionItem('viewFollowers');
        return;
      }
      const streamAccess = this.getAcl().checkModel(this.model, 'stream', true);
      if (!streamAccess) {
        this.hideActionItem('viewFollowers');
      } else {
        this.showActionItem('viewFollowers');
      }
      if (streamAccess === null) {
        this.listenToOnce(this.model, 'sync', () => {
          this.manageAccessStream(true);
        });
      }
    }
    manageAccess() {
      this.manageAccessEdit();
      this.manageAccessDelete();
      this.manageAccessStream();
    }

    /**
     * Add a button.
     *
     * @param {module:views/record/detail~button} o
     * @param {boolean} [toBeginning]
     */
    addButton(o, toBeginning) {
      const name = o.name;
      if (!name) {
        return;
      }
      for (const item of this.buttonList) {
        if (item.name === name) {
          return;
        }
      }
      toBeginning ? this.buttonList.unshift(o) : this.buttonList.push(o);
    }

    /**
     * Add a dropdown item.
     *
     * @param {module:views/record/detail~dropdownItem} o
     * @param {boolean} [toBeginning]
     */
    addDropdownItem(o, toBeginning) {
      if (!o) {
        // For bc.
        return;
      }
      const name = o.name;
      if (!name) {
        return;
      }
      for (const item of this.dropdownItemList) {
        if (item.name === name) {
          return;
        }
      }
      toBeginning ? this.dropdownItemList.unshift(o) : this.dropdownItemList.push(o);
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Add an 'edit' mode button.
     *
     * @param {module:views/record/detail~button} o
     * @param {boolean} [toBeginning]
     */
    addButtonEdit(o, toBeginning) {
      const name = o.name;
      if (!name) {
        return;
      }
      for (const item of this.buttonEditList) {
        if (item.name === name) {
          return;
        }
      }
      toBeginning ? this.buttonEditList.unshift(o) : this.buttonEditList.push(o);
    }

    /**
     * @deprecated Use `enableActionItems`.
     */
    enableButtons() {
      this.allActionItemsDisabled = false;
      this.$el.find(".button-container .actions-btn-group .action").removeAttr('disabled').removeClass('disabled');
      this.$el.find(".button-container .actions-btn-group .dropdown-toggle").removeAttr('disabled').removeClass('disabled');
      this.buttonList.filter(item => item.disabled).forEach(item => {
        this.$detailButtonContainer.find(`button.action[data-action="${item.name}"]`).addClass('disabled').attr('disabled', 'disabled');
      });
      this.buttonEditList.filter(item => item.disabled).forEach(item => {
        this.$detailButtonContainer.find(`button.action[data-action="${item.name}"]`).addClass('disabled').attr('disabled', 'disabled');
      });
      this.dropdownItemList.filter(item => item.disabled).forEach(item => {
        this.$detailButtonContainer.find(`li > .action[data-action="${item.name}"]`).parent().addClass('disabled').attr('disabled', 'disabled');
      });
      this.dropdownEditItemList.filter(item => item.disabled).forEach(item => {
        this.$detailButtonContainer.find(`li > .action[data-action="${item.name}"]`).parent().addClass('disabled').attr('disabled', 'disabled');
      });
    }

    /**
     * @deprecated Use `disableActionItems`.
     */
    disableButtons() {
      this.allActionItemsDisabled = true;
      this.$el.find(".button-container .actions-btn-group .action").attr('disabled', 'disabled').addClass('disabled');
      this.$el.find(".button-container .actions-btn-group .dropdown-toggle").attr('disabled', 'disabled').addClass('disabled');
    }

    /**
     * Remove a button or dropdown item.
     *
     * @param {string} name A name.
     */
    removeActionItem(name) {
      // noinspection JSDeprecatedSymbols
      this.removeButton(name);
    }

    /**
     * @deprecated Use `removeActionItem`.
     *
     * @param {string} name A name.
     */
    removeButton(name) {
      for (const [i, item] of this.buttonList.entries()) {
        if (item.name === name) {
          this.buttonList.splice(i, 1);
          break;
        }
      }
      for (const [i, item] of this.dropdownItemList.entries()) {
        if (item.name === name) {
          this.dropdownItemList.splice(i, 1);
          break;
        }
      }
      if (!this.isRendered()) {
        return;
      }
      const $container = this.$el.find('.detail-button-container');
      const $action = $container.find(`ul > li > a.action[data-action="${name}"]`);
      if ($action.length) {
        $action.parent().remove();
        $container.find(`ul > .divider:last-child`).remove();
        return;
      }
      const $button = $container.find(`button.action[data-action="${name}"]`);
      if ($button.length) {
        $button.remove();
      }
    }

    /**
     * Convert a detail layout to an internal layout.
     *
     * @protected
     * @param {module:views/record/detail~panelDefs[]} simplifiedLayout A detail layout.
     * @return {Object[]}
     */
    convertDetailLayout(simplifiedLayout) {
      const layout = [];
      const el = this.getSelector() || '#' + this.id;
      this.panelFieldListMap = {};
      let tabNumber = -1;
      for (let p = 0; p < simplifiedLayout.length; p++) {
        const item = simplifiedLayout[p];
        const panel = {};
        const tabBreak = item.tabBreak || p === 0;
        if (tabBreak) {
          tabNumber++;
        }
        if ('customLabel' in item) {
          panel.label = item.customLabel;
          if (panel.label) {
            panel.label = this.translate(panel.label, 'panelCustomLabels', this.entityType);
          }
        } else {
          panel.label = item.label || null;
          if (panel.label) {
            panel.label = panel.label[0] === '$' ? this.translate(panel.label.substring(1), 'panels', this.entityType) : this.translate(panel.label, 'labels', this.entityType);
          }
        }
        panel.name = item.name || 'panel-' + p.toString();
        panel.style = item.style || 'default';
        panel.rows = [];
        panel.tabNumber = tabNumber;
        panel.noteText = item.noteText;
        panel.noteStyle = item.noteStyle || 'info';
        if (panel.noteText) {
          if (panel.noteText.startsWith('$') && !panel.noteText.includes(' ')) {
            const label = panel.noteText.substring(1);
            panel.noteText = this.translate(label, 'panelNotes', this.entityType);
          }
          panel.noteText = this.getHelper().transformMarkdownText(panel.noteText);
        }
        this.middlePanelDefs[panel.name] = {
          name: panel.name,
          style: panel.style,
          tabNumber: panel.tabNumber,
          tabBreak: tabBreak,
          tabLabel: item.tabLabel
        };
        this.middlePanelDefsList.push(this.middlePanelDefs[panel.name]);

        // noinspection JSUnresolvedReference
        if (item.dynamicLogicVisible && this.dynamicLogic) {
          this.dynamicLogic.addPanelVisibleCondition(panel.name, item.dynamicLogicVisible);
        }

        // noinspection JSUnresolvedReference
        if (item.dynamicLogicStyled && this.dynamicLogic) {
          this.dynamicLogic.addPanelStyledCondition(panel.name, item.dynamicLogicStyled);
        }

        // noinspection JSUnresolvedReference
        if (item.hidden && tabNumber === 0) {
          panel.hidden = true;
          this.hidePanel(panel.name);
          this.underShowMoreDetailPanelList = this.underShowMoreDetailPanelList || [];
          this.underShowMoreDetailPanelList.push(panel.name);
        }
        let lType = 'rows';
        if (item.columns) {
          lType = 'columns';
          panel.columns = [];
        }
        if (panel.name) {
          this.panelFieldListMap[panel.name] = [];
        }
        for (const [i, itemI] of item[lType].entries()) {
          const row = [];
          for (const cellDefs of itemI) {
            if (cellDefs === false) {
              row.push(false);
              continue;
            }
            let view = cellDefs.view;
            let name = cellDefs.name;
            if (!name && view && typeof view === 'object') {
              name = view.name;
            }
            if (!name) {
              console.warn(`No 'name' specified in detail layout cell.`);
              continue;
            }
            let selector;
            if (view && typeof view === 'object') {
              view.model = this.model;
              view.mode = this.fieldsMode;
              if (this.readOnly) {
                view.setReadOnly();
              }
              selector = `.field[data-name="${name}"]`;
            }
            if (panel.name) {
              this.panelFieldListMap[panel.name].push(name);
            }
            const type = cellDefs.type || this.model.getFieldType(name) || 'base';
            view = view || this.model.getFieldParam(name, 'view') || this.getFieldManager().getViewName(type);
            const o = {
              fullSelector: el + ' .middle .field[data-name="' + name + '"]',
              defs: {
                name: name,
                params: cellDefs.params || {}
              },
              mode: this.fieldsMode
            };
            if (this.readOnly) {
              o.readOnly = true;
            }
            if (cellDefs.readOnly) {
              o.readOnly = true;
              o.readOnlyLocked = true;
            }
            if (this.readOnlyLocked) {
              o.readOnlyLocked = true;
            }
            if (this.inlineEditDisabled || cellDefs.inlineEditDisabled) {
              o.inlineEditDisabled = true;
            }

            // noinspection JSUnresolvedReference
            let fullWidth = cellDefs.fullWidth || false;
            if (!fullWidth) {
              if (item[lType][i].length === 1) {
                fullWidth = true;
              }
            }
            if (this.recordHelper.getFieldStateParam(name, 'hidden')) {
              o.disabled = true;
            }
            if (this.recordHelper.getFieldStateParam(name, 'hiddenLocked')) {
              o.disabledLocked = true;
            }
            if (this.recordHelper.getFieldStateParam(name, 'readOnly')) {
              o.readOnly = true;
            }
            if (!o.readOnlyLocked && this.recordHelper.getFieldStateParam(name, 'readOnlyLocked')) {
              o.readOnlyLocked = true;
            }
            if (this.recordHelper.getFieldStateParam(name, 'required') !== null) {
              o.defs.params = o.defs.params || {};
              o.defs.params.required = this.recordHelper.getFieldStateParam(name, 'required');
            }
            if (this.recordHelper.hasFieldOptionList(name)) {
              o.customOptionList = this.recordHelper.getFieldOptionList(name);
            }
            o.validateCallback = () => this.validateField(name);
            o.recordHelper = this.recordHelper;
            o.dataObject = this.dataObject;
            if (cellDefs.options) {
              for (const optionName in cellDefs.options) {
                if (typeof o[optionName] !== 'undefined') {
                  continue;
                }
                o[optionName] = cellDefs.options[optionName];
              }
            }
            const cell = {
              name: name + 'Field',
              view: view,
              field: name,
              fullSelector: el + ' .middle .field[data-name="' + name + '"]',
              fullWidth: fullWidth,
              options: o
            };
            if (selector) {
              cell.selector = selector;
            }
            if ('labelText' in cellDefs) {
              o.labelText = cellDefs.labelText;
              cell.customLabel = cellDefs.labelText;
            }
            if ('customLabel' in cellDefs) {
              cell.customLabel = cellDefs.customLabel;
            }
            if ('label' in cellDefs) {
              cell.label = cellDefs.label;
            }
            if (view && typeof view === 'object' && !cell.customLabel && !cell.label && view.getLabelText()) {
              cell.customLabel = view.getLabelText();
            }
            if ('customCode' in cellDefs) {
              cell.customCode = cellDefs.customCode;
            }
            if ('noLabel' in cellDefs) {
              cell.noLabel = cellDefs.noLabel;
            }
            if ('span' in cellDefs) {
              cell.span = cellDefs.span;
            }
            row.push(cell);
          }
          panel[lType].push(row);
        }
        layout.push(panel);
      }
      return layout;
    }

    /**
     * @private
     * @param {function(Object[]): void}callback
     */
    getGridLayout(callback) {
      if (this.gridLayout !== null) {
        callback(this.gridLayout);
        return;
      }
      if (this.detailLayout) {
        this.gridLayout = {
          type: this.gridLayoutType,
          layout: this.convertDetailLayout(this.detailLayout)
        };
        callback(this.gridLayout);
        return;
      }
      this.getHelper().layoutManager.get(this.entityType, this.layoutName, detailLayout => {
        if (typeof this.modifyDetailLayout === 'function') {
          detailLayout = Espo.Utils.cloneDeep(detailLayout);
          this.modifyDetailLayout(detailLayout);
        }
        this.detailLayout = detailLayout;
        this.gridLayout = {
          type: this.gridLayoutType,
          layout: this.convertDetailLayout(this.detailLayout)
        };
        callback(this.gridLayout);
      });
    }

    /**
     * Create a side view.
     *
     * @protected
     */
    createSideView() {
      const el = this.getSelector() || '#' + this.id;
      this.createView('side', this.sideView, {
        model: this.model,
        scope: this.scope,
        fullSelector: el + ' .side',
        type: this.type,
        readOnly: this.readOnly,
        inlineEditDisabled: this.inlineEditDisabled,
        recordHelper: this.recordHelper,
        recordViewObject: this,
        isReturn: this.options.isReturn,
        dataObject: this.dataObject
      });
    }

    /**
     * Create a middle view.
     *
     * @protected
     */
    createMiddleView(callback) {
      const el = this.getSelector() || '#' + this.id;
      this.waitForView('middle');
      this.getGridLayout(layout => {
        if (this.hasTabs() && this.options.isReturn && this.isStoredTabForThisRecord()) {
          this.selectStoredTab();
        }
        this.createView('middle', this.middleView, {
          model: this.model,
          scope: this.scope,
          type: this.type,
          layoutDefs: layout,
          fullSelector: el + ' .middle',
          layoutData: {
            model: this.model
          },
          recordHelper: this.recordHelper,
          recordViewObject: this,
          panelFieldListMap: this.panelFieldListMap
        }, callback);
      });
    }

    /**
     * Create a bottom view.
     *
     * @protected
     */
    createBottomView() {
      const el = this.getSelector() || '#' + this.id;
      this.createView('bottom', this.bottomView, {
        model: this.model,
        scope: this.scope,
        fullSelector: el + ' .bottom',
        readOnly: this.readOnly,
        type: this.type,
        inlineEditDisabled: this.inlineEditDisabled,
        recordHelper: this.recordHelper,
        recordViewObject: this,
        portalLayoutDisabled: this.portalLayoutDisabled,
        isReturn: this.options.isReturn,
        dataObject: this.dataObject
      });
    }

    /**
     * Create views.
     *
     * @protected
     * @param {function(module:views/record/detail-middle): void} [callback]
     */
    build(callback) {
      if (!this.sideDisabled && this.sideView) {
        this.createSideView();
      }
      if (this.middleView) {
        this.createMiddleView(callback);
      }
      if (!this.bottomDisabled && this.bottomView) {
        this.createBottomView();
      }
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Called after create.
     *
     * @return {boolean} True if redirecting is processed.
     */
    exitAfterCreate() {
      if (!this.returnAfterCreate && this.model.id) {
        const url = '#' + this.scope + '/view/' + this.model.id;
        this.getRouter().navigate(url, {
          trigger: false
        });
        this.getRouter().dispatch(this.scope, 'view', {
          id: this.model.id,
          rootUrl: this.options.rootUrl,
          model: this.model,
          isAfterCreate: true
        });
        return true;
      }
      return false;
    }

    /**
     * Called after save or cancel. By default, redirects a page. Can be overridden in options.
     *
     * @param {string|'create'|'save'|'cancel'|'delete'} [after] Name of an action after which #exit is invoked.
     */
    exit(after) {
      if (after) {
        const methodName = 'exitAfter' + Espo.Utils.upperCaseFirst(after);
        if (methodName in this) {
          const result = this[methodName]();
          if (result) {
            return;
          }
        }
      }
      let url;
      let options;
      if (this.returnUrl) {
        url = this.returnUrl;
      } else {
        if (after === 'delete') {
          url = this.options.rootUrl || '#' + this.scope;
          if (this.options.rootUrl) {
            this.getRouter().navigate(url, {
              trigger: true
            });
            return;
          }
          this.getRouter().navigate(url, {
            trigger: false
          });
          this.getRouter().dispatch(this.scope, null, {
            isReturn: true
          });
          return;
        }
        if (this.model.id) {
          url = `#${this.scope}/view/${this.model.id}`;
          if (!this.returnDispatchParams) {
            this.getRouter().navigate(url, {
              trigger: false
            });
            options = {
              id: this.model.id,
              model: this.model
            };
            if (this.options.rootUrl) {
              options.rootUrl = this.options.rootUrl;
            }
            this.getRouter().dispatch(this.scope, 'view', options);
          }
        } else {
          url = this.options.rootUrl || '#' + this.scope;
        }
      }
      if (this.returnDispatchParams) {
        const controller = this.returnDispatchParams.controller;
        const action = this.returnDispatchParams.action;
        options = this.returnDispatchParams.options || {};
        this.getRouter().navigate(url, {
          trigger: false
        });
        this.getRouter().dispatch(controller, action, options);
        return;
      }
      this.getRouter().navigate(url, {
        trigger: true
      });
    }

    /**
     * @protected
     */
    subscribeToWebSocket() {
      const topic = `recordUpdate.${this.entityType}.${this.model.id}`;
      this.recordUpdateWebSocketTopic = topic;
      this.isSubscribedToWebSocket = true;
      this.getHelper().webSocketManager.subscribe(topic, () => this._webSocketDebounceHelper.process());
    }

    /**
     * @protected
     */
    unsubscribeFromWebSocket() {
      if (!this.isSubscribedToWebSocket) {
        return;
      }
      this.getHelper().webSocketManager.unsubscribe(this.recordUpdateWebSocketTopic);
    }

    /**
     * @private
     */
    async handleRecordUpdate() {
      if (this.updateWebSocketIsBlocked) {
        return;
      }
      if (this.inlineEditModeIsOn || this.mode === this.MODE_EDIT) {
        const m = this.model.clone();
        await m.fetch();
        if (this.inlineEditModeIsOn || this.mode === this.MODE_EDIT) {
          this.updatedAttributes = Espo.Utils.cloneDeep(m.attributes);
        }
        return;
      }
      await this.model.fetch({
        highlight: true
      });
    }

    /**
     * @internal
     * @param {boolean} [toUnblock]
     */
    blockUpdateWebSocket() {
      let toUnblock = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
      this.updateWebSocketIsBlocked = true;
      if (toUnblock) {
        setTimeout(() => {
          this.unblockUpdateWebSocket();
        }, this.blockUpdateWebSocketPeriod);
      }
    }

    /**
     * @private
     */
    unblockUpdateWebSocket() {
      this.updateWebSocketIsBlocked = false;
    }

    /**
     * Show more detail panels.
     */
    showMoreDetailPanels() {
      this.hidePanel('showMoreDelimiter');
      this.underShowMoreDetailPanelList.forEach(item => {
        this.showPanel(item);
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @protected
     * @return {Number}
     */
    getTabCount() {
      if (!this.hasTabs()) {
        return 0;
      }
      let count = 1;
      (this.detailLayout || []).forEach(item => {
        if (item.tabBreak) {
          count++;
        }
      });
      return count;
    }

    /**
     * @protected
     * @return {boolean}
     */
    hasTabs() {
      if (typeof this._hasMiddleTabs !== 'undefined') {
        return this._hasMiddleTabs;
      }
      if (!this.detailLayout) {
        return false;
      }
      for (const item of this.detailLayout) {
        if (item.tabBreak) {
          this._hasMiddleTabs = true;
          return true;
        }
      }
      this._hasMiddleTabs = false;
      return false;
    }

    /**
     * @private
     * @return {{label: string}[]}
     */
    getMiddleTabDataList() {
      const currentTab = this.currentTab;
      const panelDataList = this.middlePanelDefsList;
      return panelDataList.filter((item, i) => i === 0 || item.tabBreak).map((item, i) => {
        let label = item.tabLabel;
        let hidden = false;
        if (i > 0) {
          hidden = panelDataList.filter(panel => panel.tabNumber === i).findIndex(panel => !this.recordHelper.getPanelStateParam(panel.name, 'hidden')) === -1;
        }
        if (!label) {
          label = i === 0 ? this.translate('Overview') : (i + 1).toString();
        } else if (label.substring(0, 7) === '$label:') {
          label = this.translate(label.substring(7), 'labels', this.scope);
        } else if (label[0] === '$') {
          label = this.translate(label.substring(1), 'tabs', this.scope);
        }
        return {
          label: label,
          isActive: currentTab === i,
          hidden: hidden
        };
      });
    }

    /**
     * Select a tab.
     *
     * @protected
     * @param {number} tab
     */
    selectTab(tab) {
      this.currentTab = tab;
      $('.popover.in').removeClass('in');
      this.whenRendered().then(() => {
        this.$el.find('.middle-tabs > button').removeClass('active');
        this.$el.find(`.middle-tabs > button[data-tab="${tab}"]`).addClass('active');
        this.$el.find('.middle > .panel[data-tab]').addClass('tab-hidden');
        this.$el.find(`.middle > .panel[data-tab="${tab}"]`).removeClass('tab-hidden');
        this.adjustMiddlePanels();
        this.recordHelper.trigger('panel-show');
      });
      this.storeTab();
    }

    /**
     * @private
     */
    storeTab() {
      const key = 'tab_middle';
      const keyRecord = 'tab_middle_record';
      this.getSessionStorage().set(key, this.currentTab);
      this.getSessionStorage().set(keyRecord, this.entityType + '_' + this.model.id);
    }

    /**
     * @private
     */
    selectStoredTab() {
      const key = 'tab_middle';
      const tab = this.getSessionStorage().get(key);
      if (tab > 0) {
        this.selectTab(tab);
      }
    }

    /**
     * @private
     */
    isStoredTabForThisRecord() {
      const keyRecord = 'tab_middle_record';
      return this.getSessionStorage().get(keyRecord) === this.entityType + '_' + this.model.id;
    }

    /**
     * @inheritDoc
      */
    onInvalid(invalidFieldList) {
      if (!this.hasTabs()) {
        return;
      }
      const tabList = [];
      for (const field of invalidFieldList) {
        const view = this.getMiddleView().getFieldView(field);
        if (!view) {
          continue;
        }
        const tabString = view.$el.closest('.panel.tab-hidden').attr('data-tab');
        const tab = parseInt(tabString);
        if (tabList.indexOf(tab) !== -1) {
          continue;
        }
        tabList.push(tab);
      }
      if (!tabList.length) {
        return;
      }
      const $tabs = this.$el.find('.middle-tabs');
      tabList.forEach(tab => {
        const $tab = $tabs.find(`> [data-tab="${tab.toString()}"]`);
        $tab.addClass('invalid');
        $tab.one('click', () => {
          $tab.removeClass('invalid');
        });
      });
    }

    /**
     * @private
     */
    controlTabVisibilityShow(tab) {
      if (!this.hasTabs() || tab === 0) {
        return;
      }
      if (this.isBeingRendered()) {
        this.once('after:render', () => this.controlTabVisibilityShow(tab));
        return;
      }
      this.$el.find(`.middle-tabs > [data-tab="${tab.toString()}"]`).removeClass('hidden');
    }

    /**
     * @private
     */
    controlTabVisibilityHide(tab) {
      if (!this.hasTabs() || tab === 0) {
        return;
      }
      if (this.isBeingRendered()) {
        this.once('after:render', () => this.controlTabVisibilityHide(tab));
        return;
      }
      const panelList = this.middlePanelDefsList.filter(panel => panel.tabNumber === tab);
      const allIsHidden = panelList.findIndex(panel => !this.recordHelper.getPanelStateParam(panel.name, 'hidden')) === -1;
      if (!allIsHidden) {
        return;
      }
      const $tab = this.$el.find(`.middle-tabs > [data-tab="${tab.toString()}"]`);
      $tab.addClass('hidden');
      if (this.currentTab === tab) {
        this.selectTab(0);
      }
    }

    /**
     * @private
     */
    adjustMiddlePanels() {
      if (!this.isRendered() || !this.$middle.length) {
        return;
      }
      const $panels = this.$middle.find('> .panel');
      const $bottomPanels = this.$bottom ? this.$bottom.find('> .panel') : null;
      $panels.removeClass('first').removeClass('last').removeClass('in-middle');
      const $visiblePanels = $panels.filter(`:not(.tab-hidden):not(.hidden)`);
      $visiblePanels.each((i, el) => {
        const $el = $(el);
        if (i === $visiblePanels.length - 1) {
          if ($bottomPanels && $bottomPanels.first().hasClass('sticked')) {
            if (i === 0) {
              $el.addClass('first');
              return;
            }
            $el.addClass('in-middle');
            return;
          }
          if (i === 0) {
            return;
          }
          $el.addClass('last');
          return;
        }
        if (i > 0 && i < $visiblePanels.length - 1) {
          $el.addClass('in-middle');
          return;
        }
        if (i === 0) {
          $el.addClass('first');
        }
      });
    }

    /**
     * @private
     */
    adjustButtons() {
      const $buttons = this.$detailButtonContainer.filter('.record-buttons').find('button.btn');
      $buttons.removeClass('radius-left').removeClass('radius-right');
      const $buttonsVisible = $buttons.filter('button:not(.hidden)');
      $buttonsVisible.first().addClass('radius-left');
      $buttonsVisible.last().addClass('radius-right');
      this.adjustEditButtons();
    }

    /**
     * @private
     */
    adjustEditButtons() {
      const $buttons = this.$detailButtonContainer.filter('.edit-buttons').find('button.btn');
      $buttons.removeClass('radius-left').removeClass('radius-right');
      const $buttonsVisible = $buttons.filter('button:not(.hidden)');
      $buttonsVisible.first().addClass('radius-left');
      $buttonsVisible.last().addClass('radius-right');
    }

    /**
     * @private
     */
    initElementReferences() {
      if (this.$detailButtonContainer && this.$detailButtonContainer.length) {
        return;
      }
      this.$detailButtonContainer = this.$el.find('.detail-button-container');
      this.$dropdownItemListButton = this.$detailButtonContainer.find('.dropdown-item-list-button');
      this.$dropdownEditItemListButton = this.$detailButtonContainer.find('.dropdown-edit-item-list-button');
    }

    /**
     * @protected
     */
    focusForEdit() {
      this.$el.find('.field:not(.hidden) .form-control:not([disabled])').first().focus();
    }

    /**
     * @protected
     */
    focusForCreate() {
      this.$el.find('.form-control:not([disabled])').first().focus();
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlEnter(e) {
      const action = this.shortcutKeyCtrlEnterAction;
      if (this.inlineEditModeIsOn || this.buttonsDisabled || !action) {
        return;
      }
      if (this.mode !== this.MODE_EDIT) {
        return;
      }
      if (!this.hasAvailableActionItem(action)) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      if (document.activeElement instanceof HTMLInputElement) {
        // Fields may need to fetch data first.
        document.activeElement.dispatchEvent(new Event('change', {
          bubbles: true
        }));
      }
      const methodName = 'action' + Espo.Utils.upperCaseFirst(action);
      this[methodName]();
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlS(e) {
      if (this.inlineEditModeIsOn || this.buttonsDisabled) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      if (this.mode !== this.MODE_EDIT) {
        return;
      }
      if (!this.saveAndContinueEditingAction) {
        return;
      }
      if (!this.hasAvailableActionItem('saveAndContinueEditing')) {
        return;
      }
      this.actionSaveAndContinueEditing();
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlSpace(e) {
      if (this.inlineEditModeIsOn || this.buttonsDisabled) {
        return;
      }
      if (this.type !== this.TYPE_DETAIL || this.mode !== this.MODE_DETAIL) {
        return;
      }
      if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT') {
        return;
      }
      if (!this.hasAvailableActionItem('edit')) {
        return;
      }
      $(e.currentTarget);
      e.preventDefault();
      e.stopPropagation();
      this.actionEdit();
      if (!this.editModeDisabled) {
        setTimeout(() => this.focusForEdit(), 200);
      }
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyEscape(e) {
      if (this.inlineEditModeIsOn || this.buttonsDisabled) {
        return;
      }
      if (this.type !== this.TYPE_DETAIL || this.mode !== this.MODE_EDIT) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();

      // Fetching a currently edited form element.
      this.model.set(this.fetch());
      if (this.isChanged) {
        this.confirm(this.translate('confirmLeaveOutMessage', 'messages')).then(() => this.actionCancelEdit());
        return;
      }
      this.actionCancelEdit();
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlAltEnter(e) {}

    /**
     * @public
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyControlBackslash(e) {
      if (!this.hasTabs()) {
        return;
      }
      const $buttons = this.$el.find('.middle-tabs > button:not(.hidden)');
      if ($buttons.length === 1) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      let index = $buttons.toArray().findIndex(el => $(el).hasClass('active'));
      index++;
      if (index >= $buttons.length) {
        index = 0;
      }
      const $tab = $($buttons.get(index));
      const tab = parseInt($tab.attr('data-tab'));
      this.selectTab(tab);
      if (this.mode === this.MODE_EDIT) {
        setTimeout(() => {
          this.$middle.find(`.panel[data-tab="${tab}"] .cell:not(.hidden)`).first().focus();
        }, 50);
        return;
      }
      this.$el.find(`.middle-tabs button[data-tab="${tab}"]`).focus();
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyControlArrowLeft(e) {
      if (this.inlineEditModeIsOn || this.buttonsDisabled) {
        return;
      }
      if (this.navigateButtonsDisabled) {
        return;
      }
      if (this.type !== this.TYPE_DETAIL || this.mode !== this.MODE_DETAIL) {
        return;
      }
      if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT') {
        return;
      }
      const $button = this.$el.find('button[data-action="previous"]');
      if (!$button.length || $button.hasClass('disabled')) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      this.actionPrevious();
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyControlArrowRight(e) {
      if (this.inlineEditModeIsOn || this.buttonsDisabled) {
        return;
      }
      if (this.navigateButtonsDisabled) {
        return;
      }
      if (this.type !== this.TYPE_DETAIL || this.mode !== this.MODE_DETAIL) {
        return;
      }
      if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT') {
        return;
      }
      const $button = this.$el.find('button[data-action="next"]');
      if (!$button.length || $button.hasClass('disabled')) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      this.actionNext();
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Get a current mode.
     *
     * @since 8.0.0
     * @return {'detail'|'edit'}
     */
    getMode() {
      return this.mode;
    }
  }
  var _default = _exports.default = DetailRecordView;
});

define("views/fields/base", ["exports", "view", "ui/select", "jquery"], function (_exports, _view, _select, _jquery) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _select = _interopRequireDefault(_select);
  _jquery = _interopRequireDefault(_jquery);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/base */

  /**
   * A base field view. Can be in different modes. Each mode uses a separate template.
   *
   * @todo Document events.
   * @template TParams
   */
  class BaseFieldView extends _view.default {
    /**
     * @typedef {Object} module:views/fields/base~options
     * @property {string} name A field name.
     * @property {module:model} [model] A model.
     * @property {module:views/fields/base~params | Object.<string, *>} [params] Parameters.
     * @property {boolean} [inlineEditDisabled] Disable inline edit.
     * @property {boolean} [readOnly] Read-only.
     * @property {string} [labelText] A custom label text.
     * @property {'detail'|'edit'|'list'|'search'} [mode] A mode.
     * @property {import('view-record-helper').default} [recordHelper] A record helper.
     */

    /**
     * @typedef {Object} module:views/fields/base~params
     * @property {boolean} [inlineEditDisabled] Disable inline edit.
     * @property {boolean} [readOnly] Read-only.
     */

    /**
     * @param {module:views/fields/base~options | Object.<string, *>} options Options.
     */
    constructor(options) {
      super(options);
      this.name = options.name;
      this.labelText = options.labelText;
    }

    /**
     * A field type.
     *
     * @type {string}
     */
    type = 'base';

    /**
     * List mode template.
     *
     * @protected
     * @type {string}
     */
    listTemplate = 'fields/base/list';

    // noinspection JSUnusedGlobalSymbols
    /**
     * List-link mode template.
     *
     * @protected
     * @type {string}
     */
    listLinkTemplate = 'fields/base/list-link';

    /**
     * Detail mode template.
     *
     * @protected
     * @type {string}
     */
    detailTemplate = 'fields/base/detail';

    /**
     * Edit mode template.
     *
     * @protected
     * @type {string}
     */
    editTemplate = 'fields/base/edit';

    /**
     * Search mode template.
     *
     * @protected
     * @type {string}
     */
    searchTemplate = 'fields/base/search';

    // noinspection JSUnusedGlobalSymbols
    /**
     * @protected
     * @type {string}
     */
    listTemplateContent;

    // noinspection JSUnusedGlobalSymbols
    /**
     * @protected
     * @type {string}
     */
    detailTemplateContent;

    // noinspection JSUnusedGlobalSymbols
    /**
     * @protected
     * @type {string}
     */
    editTemplateContent;

    /**
     * A validation list. A function returning true if non-valid, or a name.
     * For the latter, there should be a `validate{Name}` method in the class.
     *
     * Functions are supported as of v8.3.
     *
     * @type {Array<(function (): boolean)|string>}
     */
    validations = ['required'];

    /**
     * @const
     */
    MODE_LIST = 'list';

    /**
     * @const
     */
    MODE_LIST_LINK = 'listLink';

    /**
     * @const
     */
    MODE_DETAIL = 'detail';

    /**
     * @const
     */
    MODE_EDIT = 'edit';

    /**
     * @const
     */
    MODE_SEARCH = 'search';

    /**
     * A field name.
     *
     * @type {string}
     */
    name;

    /**
     * A field parameter list. To be used for custom fields not defined in metadata > fields.
     *
     * @type {string[]}
     * @since 9.0.0
     */
    paramList;

    /**
     * Definitions.
     *
     * @type {Object}
     */
    defs = null;

    /**
     * Field params.
     *
     * @type {TParams & module:views/fields/base~params & Object.<string, *>}
     */
    params = null;

    /**
     * A mode.
     *
     * @type {'list'|'listLink'|'detail'|'edit'|'search'}
     */
    mode = 'detail';

    /**
     * Search params.
     *
     * @type {Object.<string,*>|null}
     */
    searchParams = null;

    /**
     * Inline edit disabled.
     *
     * @type {boolean}
     */
    inlineEditDisabled = false;

    /**
     * Field is disabled.
     *
     * @type {boolean}
     */
    disabled = false;

    /**
     * Field is read-only.
     *
     * @type {boolean}
     */
    readOnly = false;

    /**
     * A label text.
     *
     * @type {string}
     * @protected
     */
    labelText;

    /**
     * @type {string[]|null}
     */
    attributeList = null;

    /**
     * Attribute values before edit.
     *
     * @type {Object.<string, *>|{}}
     */
    initialAttributes = null;

    /**
     * @const
     */
    VALIDATION_POPOVER_TIMEOUT = 3000;

    /**
     * @type {(function():boolean)}
     * @private
     * @internal
     */
    validateCallback;

    /**
     * An element selector to point validation popovers to.
     *
     * @type {string}
     * @protected
     */
    validationElementSelector;

    /**
     * A view-record helper.
     *
     * @type {module:view-record-helper}
     */
    recordHelper;

    /**
     * @type {JQuery|null}
     * @private
     * @internal
     */
    $label = null;

    /**
     * A form element.
     *
     * @type {JQuery|null}
     * @protected
     */
    $element = null;

    /**
     * Is searchable once a search filter is added (no need to type or selecting anything).
     * Actual for search mode.
     *
     * @public
     * @type {boolean}
     */
    initialSearchIsNotIdle = false;

    /**
     * An entity type.
     *
     * @private
     * @type {string|null}
     */
    entityType = null;

    /**
     * A last validation message;
     *
     * @type {?string}
     */
    lastValidationMessage = null;

    /**
     * Additional data.
     *
     * @type {Object.<string, *>}
     */
    dataObject;

    /**
     * Is the field required.
     *
     * @returns {boolean}
     */
    isRequired() {
      return this.params.required;
    }

    /**
     * Get a cell element. Available only after the view is  rendered.
     *
     * @private
     * @returns {JQuery}
     */
    get$cell() {
      return this.$el.parent();
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @deprecated
     * @returns {JQuery}
     */
    getCellElement() {
      return this.get$cell();
    }

    /**
     * Is in inline-edit mode.
     *
     * @return {boolean}
     */
    isInlineEditMode() {
      return !!this._isInlineEditMode;
    }

    /**
     * Set disabled.
     *
     * @param {boolean} [locked] Won't be able to set back.
     */
    setDisabled(locked) {
      this.disabled = true;
      if (locked) {
        this.disabledLocked = true;
      }
    }

    /**
     * Set not-disabled.
     */
    setNotDisabled() {
      if (this.disabledLocked) {
        return;
      }
      this.disabled = false;
    }

    /**
     * Set required.
     */
    setRequired() {
      this.params.required = true;
      if (this.isEditMode()) {
        if (this.isRendered()) {
          this.showRequiredSign();
        } else {
          this.once('after:render', () => {
            this.showRequiredSign();
          });
        }
      }
    }

    /**
     * Set not required.
     */
    setNotRequired() {
      this.params.required = false;
      this.get$cell().removeClass('has-error');
      if (this.isEditMode()) {
        if (this.isRendered()) {
          this.hideRequiredSign();
        } else {
          this.once('after:render', () => {
            this.hideRequiredSign();
          });
        }
      }
    }

    /**
     * Set read-only.
     *
     * @param {boolean} [locked] Won't be able to set back.
     * @return {Promise}
     */
    setReadOnly(locked) {
      if (this.readOnlyLocked) {
        return Promise.reject();
      }
      this.readOnly = true;
      if (locked) {
        this.readOnlyLocked = true;
      }
      if (!this.isReady) {
        this.mode = 'detail';
        return Promise.resolve();
      }
      if (this.isEditMode()) {
        if (this.isInlineEditMode()) {
          return this.inlineEditClose();
        }
        return this.setDetailMode().then(() => this.reRender());
      }
      return Promise.resolve();
    }

    /**
     * Set not read only.
     */
    setNotReadOnly() {
      if (this.readOnlyLocked) {
        return;
      }
      this.readOnly = false;
    }

    /**
     * Get a label element. Available only after the view is rendered.
     *
     * @return {JQuery}
     */
    getLabelElement() {
      if (this.$label && this.$label.get(0) && !document.contains(this.$label.get(0))) {
        this.$label = undefined;
      }
      if (!this.$label || !this.$label.length) {
        this.$label = this.$el.parent().children('label');
      }
      return this.$label;
    }

    /**
     * Hide field and label. Available only after the view is rendered.
     */
    hide() {
      this.$el.addClass('hidden');
      const $cell = this.get$cell();
      $cell.children('label').addClass('hidden');
      $cell.addClass('hidden-cell');
    }

    /**
     * Show field and label. Available only after the view is rendered.
     */
    show() {
      this.$el.removeClass('hidden');
      const $cell = this.get$cell();
      $cell.children('label').removeClass('hidden');
      $cell.removeClass('hidden-cell');
    }

    /**
     * @inheritDoc
     * @return {Record<string, *>}
     */
    data() {
      const data = {
        scope: this.model.entityType || this.model.name,
        name: this.name,
        defs: this.defs,
        params: this.params,
        value: this.getValueForDisplay()
      };
      if (this.isSearchMode()) {
        data.searchParams = this.searchParams;
        data.searchData = this.searchData;
        data.searchValues = this.getSearchValues();
        data.searchType = this.getSearchType();
        data.searchTypeList = this.getSearchTypeList();
      }
      return data;
    }

    /**
     * Get a value for display. Is available by using a `{value}` placeholder in templates.
     *
     * @return {*}
     */
    getValueForDisplay() {
      return this.model.get(this.name);
    }

    /**
     * Is in list, detail or list-link mode.
     *
     * @returns {boolean}
     */
    isReadMode() {
      return this.mode === this.MODE_LIST || this.mode === this.MODE_DETAIL || this.mode === this.MODE_LIST_LINK;
    }

    /**
     * Is in list or list-link mode.
     *
     * @returns {boolean}
     */
    isListMode() {
      return this.mode === this.MODE_LIST || this.mode === this.MODE_LIST_LINK;
    }

    /**
     * Is in detail mode.
     *
     * @returns {boolean}
     */
    isDetailMode() {
      return this.mode === this.MODE_DETAIL;
    }

    /**
     * Is in edit mode.
     *
     * @returns {boolean}
     */
    isEditMode() {
      return this.mode === this.MODE_EDIT;
    }

    /**
     * Is in search mode.
     *
     * @returns {boolean}
     */
    isSearchMode() {
      return this.mode === this.MODE_SEARCH;
    }

    /**
     * Set detail mode.
     *
     * @returns {Promise}
     */
    setDetailMode() {
      return this.setMode(this.MODE_DETAIL) || Promise.resolve();
    }

    /**
     * Set edit mode.
     *
     * @returns {Promise}
     */
    setEditMode() {
      return this.setMode(this.MODE_EDIT) || Promise.resolve();
    }

    /**
     * Set a mode.
     *
     * @internal
     * @returns {Promise}
     */
    setMode(mode) {
      const modeIsChanged = this.mode !== mode && this.mode;
      const modeBefore = this.mode;
      this.mode = mode;
      const property = mode + 'Template';
      if (!(property in this)) {
        this[property] = 'fields/' + Espo.Utils.camelCaseToHyphen(this.type) + '/' + this.mode;
      }
      if (!this._hasTemplateContent) {
        this.setTemplate(this[property]);
      }
      const contentProperty = mode + 'TemplateContent';
      if (!this._hasTemplateContent) {
        if (contentProperty in this && this[contentProperty] != null) {
          this.setTemplateContent(this[contentProperty]);
        }
      }
      if (modeIsChanged) {
        if (modeBefore) {
          this.trigger('mode-changed');
        }
        return this._onModeSet();
      }
      return Promise.resolve();
    }

    /**
     * Called on mode change and on value change before re-rendering.
     * To be used for additional initialization that depends on field
     * values or mode.
     *
     * @protected
     * @returns {Promise|undefined}
     */
    prepare() {}

    /**
     * @private
     * @returns {Promise}
     */
    _onModeSet() {
      if (this.isListMode()) {
        return this.onListModeSet() || Promise.resolve();
      }
      if (this.isDetailMode()) {
        return this.onDetailModeSet() || Promise.resolve();
      }
      if (this.isEditMode()) {
        return this.onEditModeSet() || Promise.resolve();
      }
      return Promise.resolve();
    }

    /**
     * Additional initialization for the detail mode.
     *
     * @protected
     * @returns {Promise|undefined}
     */
    onDetailModeSet() {
      return this.prepare();
    }

    /**
     * Additional initialization for the edit mode.
     *
     * @protected
     * @returns {Promise|undefined}
     */
    onEditModeSet() {
      return this.prepare();
    }

    /**
     * Additional initialization for the list mode.
     *
     * @protected
     * @returns {Promise|undefined}
     */
    onListModeSet() {
      return this.prepare();
    }

    /** @inheritDoc */
    init() {
      this.validations = Espo.Utils.clone(this.validations);
      this.searchTypeList = Espo.Utils.clone(this.searchTypeList);
      this._hasTemplateContent = !!this.templateContent;
      this.defs = this.options.defs || {};
      this.name = this.options.name || this.defs.name;
      this.params = this.options.params || this.defs.params || {};
      this.validateCallback = this.options.validateCallback;
      this.fieldType = this.model.getFieldParam(this.name, 'type') || this.type;
      this.entityType = this.model.entityType || this.model.name;
      this.recordHelper = this.options.recordHelper;
      this.dataObject = Espo.Utils.clone(this.options.dataObject || {});
      if (!this.labelText) {
        this.labelText = this.translate(this.name, 'fields', this.entityType);
      }
      const paramList = this.paramList || this.getFieldManager().getParamList(this.type).map(it => it.name);
      paramList.forEach(name => {
        if (name in this.params) {
          return;
        }
        this.params[name] = this.model.getFieldParam(this.name, name);
        if (typeof this.params[name] === 'undefined') {
          this.params[name] = null;
        }
      });
      const additionalParamList = ['inlineEditDisabled'];
      additionalParamList.forEach(item => {
        this.params[item] = this.model.getFieldParam(this.name, item) || null;
      });
      this.readOnly = this.readOnly || this.params.readOnly || this.model.getFieldParam(this.name, 'readOnly') || this.model.getFieldParam(this.name, 'clientReadOnly');
      if (!this.model.isNew() && this.model.getFieldParam(this.name, 'readOnlyAfterCreate')) {
        this.readOnly = true;
      }
      this.readOnlyLocked = this.options.readOnlyLocked || this.readOnly;
      this.inlineEditDisabled = this.options.inlineEditDisabled || this.params.inlineEditDisabled || this.inlineEditDisabled;
      this.readOnly = this.readOnlyLocked || this.options.readOnly || false;
      this.tooltip = this.options.tooltip || this.params.tooltip || this.model.getFieldParam(this.name, 'tooltip') || this.tooltip;
      if (this.options.readOnlyDisabled) {
        this.readOnly = false;
      }
      this.disabledLocked = this.options.disabledLocked || false;
      this.disabled = this.disabledLocked || this.options.disabled || this.disabled;
      let mode = this.options.mode || this.mode || this.MODE_DETAIL;
      if (mode === this.MODE_EDIT && this.readOnly) {
        mode = this.MODE_DETAIL;
      }
      this.mode = undefined;
      this.wait(this.setMode(mode));
      if (this.isSearchMode()) {
        this.searchParams = _.clone(this.options.searchParams || {});
        this.searchData = {};
        this.setupSearch();
        this.events['keydown.' + this.cid] = /** JQueryKeyEventObject */e => {
          if (Espo.Utils.getKeyFromKeyEvent(e) === 'Control+Enter') {
            this.trigger('search');
          }
        };
      }
      this.on('highlight', () => {
        const $cell = this.get$cell();
        $cell.addClass('highlighted');
        $cell.addClass('transition');
        setTimeout(() => {
          $cell.removeClass('highlighted');
        }, 3000);
        setTimeout(() => {
          $cell.removeClass('transition');
        }, 3000 + 2000);
      });
      this.on('invalid', () => {
        const $cell = this.get$cell();
        $cell.addClass('has-error');
        this.$el.one('click', () => {
          $cell.removeClass('has-error');
        });
        this.once('render', () => {
          $cell.removeClass('has-error');
        });
      });
      this.on('after:render', () => {
        if (this.isEditMode()) {
          if (this.hasRequiredMarker()) {
            this.showRequiredSign();
            return;
          }
          this.hideRequiredSign();
          return;
        }
        if (this.hasRequiredMarker()) {
          this.hideRequiredSign();
        }
        if (this.isSearchMode()) {
          const $searchType = this.$el.find('select.search-type');
          if ($searchType.length) {
            _select.default.init($searchType, {
              matchAnyWord: true
            });
          }
        }
      });
      if ((this.isDetailMode() || this.isEditMode()) && this.tooltip) {
        this.initTooltip();
      }
      if (this.isDetailMode()) {
        if (!this.inlineEditDisabled) {
          this.listenToOnce(this, 'after:render', () => this.initInlineEdit());
        }
      }
      if (!this.isSearchMode()) {
        this.attributeList = this.getAttributeList(); // for backward compatibility, to be removed

        this.listenTo(this.model, 'change', (model, options) => {
          if (options.ui && (!options.fromField || options.fromField === this.name)) {
            return;
          }
          let changed = false;
          for (const attribute of this.getAttributeList()) {
            if (model.hasChanged(attribute)) {
              changed = true;
              break;
            }
          }
          if (!changed) {
            return;
          }
          if (options.fromField === this.name) {
            return;
          }
          if (options.skipReRenderInEditMode && this.isEditMode()) {
            return;
          }
          if (options.skipReRender) {
            return;
          }
          if (this.isEditMode() && this.toSkipReRenderOnChange()) {
            return;
          }
          const reRender = () => {
            if (!this.isRendered() && !this.isBeingRendered()) {
              return;
            }
            this.reRender();
            if (options.highlight) {
              this.trigger('highlight');
            }
          };
          if (!this.isReady) {
            this.once('ready', () => {
              const promise = this.prepare();
              if (promise) {
                promise.then(() => reRender());
              }
            });
            return;
          }
          const promise = this.prepare();
          if (promise) {
            promise.then(() => reRender());
            return;
          }
          reRender();
        });
        this.listenTo(this, 'change', () => {
          const attributes = this.fetch();
          this.model.set(attributes, {
            ui: true,
            fromView: this,
            fromField: this.name,
            action: 'ui'
          });
        });
      }
    }
    highlight() {
      const $cell = this.get$cell();
      $cell.addClass('highlighted');
    }

    /** @inheritDoc */
    setupFinal() {
      this.wait(this._onModeSet());
    }

    /**
     * @internal
     * @private
     */
    initTooltip() {
      let $a;
      this.once('after:render', () => {
        $a = (0, _jquery.default)('<a>').attr('role', 'button').attr('tabindex', '-1').addClass('text-muted field-info').append((0, _jquery.default)('<span>').addClass('fas fa-info-circle'));
        const $label = this.getLabelElement();
        $label.append(' ');
        this.getLabelElement().append($a);
        let tooltipText = this.options.tooltipText || this.tooltipText;
        if (!tooltipText && typeof this.tooltip === 'string') {
          const [scope, field] = this.tooltip.includes('.') ? this.tooltip.split('.') : [this.entityType, this.tooltip];
          tooltipText = this.translate(field, 'tooltips', scope);
        }
        tooltipText = tooltipText || this.translate(this.name, 'tooltips', this.entityType) || '';
        tooltipText = this.getHelper().transformMarkdownText(tooltipText, {
          linksInNewTab: true
        }).toString();
        Espo.Ui.popover($a, {
          placement: 'bottom',
          content: tooltipText,
          preventDestroyOnRender: true
        }, this);
      });
    }

    /**
     * Show a required-field sign.
     *
     * @private
     */
    showRequiredSign() {
      const $label = this.getLabelElement();
      let $sign = $label.find('span.required-sign');
      if ($label.length && !$sign.length) {
        const $text = $label.find('span.label-text');
        (0, _jquery.default)('<span class="required-sign"> *</span>').insertAfter($text);
        $sign = $label.find('span.required-sign');
      }
      $sign.show();
    }

    /**
     * Hide a required-field sign.
     *
     * @private
     */
    hideRequiredSign() {
      const $label = this.getLabelElement();
      const $sign = $label.find('span.required-sign');
      $sign.hide();
    }

    /**
     * Get search-params data.
     *
     * @protected
     * @return {Object.<string,*>}
     */
    getSearchParamsData() {
      return this.searchParams.data || {};
    }

    /**
     * Get search values.
     *
     * @protected
     * @return {Object.<string,*>}
     */
    getSearchValues() {
      return this.getSearchParamsData().values || {};
    }

    /**
     * Get a current search type.
     *
     * @protected
     * @return {string}
     */
    getSearchType() {
      return this.getSearchParamsData().type || this.searchParams.type;
    }

    /**
     * Get the search type list.
     *
     * @protected
     * @returns {string[]}
     */
    getSearchTypeList() {
      return this.searchTypeList;
    }

    /**
     * @private
     * @internal
     */
    initInlineEdit() {
      const $cell = this.get$cell();
      const $editLink = (0, _jquery.default)('<a>').attr('role', 'button').addClass('pull-right inline-edit-link hidden').append((0, _jquery.default)('<span>').addClass('fas fa-pencil-alt fa-sm'));
      if ($cell.length === 0) {
        this.listenToOnce(this, 'after:render', () => this.initInlineEdit());
        return;
      }
      $cell.prepend($editLink);
      $editLink.on('click', () => this.inlineEdit());
      $cell.on('mouseenter', e => {
        e.stopPropagation();
        if (this.disabled || this.readOnly) {
          return;
        }
        if (this.isDetailMode()) {
          $editLink.removeClass('hidden');
        }
      }).on('mouseleave', e => {
        e.stopPropagation();
        if (this.isDetailMode()) {
          $editLink.addClass('hidden');
        }
      });
      this.on('after:render', () => {
        if (!this.isDetailMode()) {
          $editLink.addClass('hidden');
        }
      });
    }

    /**
     * Initializes a form element reference.
     *
     * @protected
     */
    initElement() {
      this.$element = this.$el.find('[data-name="' + this.name + '"]');
      if (!this.$element.length) {
        this.$element = this.$el.find('[name="' + this.name + '"]');
      }
      if (!this.$element.length) {
        this.$element = this.$el.find('.main-element');
      }
      if (this.isEditMode()) {
        this.$element.on('change', () => {
          this.trigger('change');
        });
      }
    }

    /** @inheritDoc */
    afterRender() {
      if (this.isEditMode() || this.isSearchMode()) {
        this.initElement();
      }
      if (this.isReadMode()) {
        this.afterRenderRead();
      }
      if (this.isListMode()) {
        this.afterRenderList();
      }
      if (this.isDetailMode()) {
        this.afterRenderDetail();
      }
      if (this.isEditMode()) {
        this.afterRenderEdit();
      }
      if (this.isSearchMode()) {
        this.afterRenderSearch();
      }
    }

    /**
     * Called after the view is rendered in list or read mode.
     *
     * @protected
     */
    afterRenderRead() {}

    /**
     * Called after the view is rendered in list mode.
     *
     * @protected
     */
    afterRenderList() {}

    /**
     * Called after the view is rendered in detail mode.
     *
     * @protected
     */
    afterRenderDetail() {}

    /**
     * Called after the view is rendered in edit mode.
     *
     * @protected
     */
    afterRenderEdit() {}

    /**
     * Called after the view is rendered in search mode.
     *
     * @protected
     */
    afterRenderSearch() {}

    /**
     * Initialization.
     */
    setup() {}

    /**
     * Initialization for search mode.
     *
     * @protected
     */
    setupSearch() {}

    /**
     * Get list of model attributes that relate to the field.
     * Changing of any attributes makes the field to re-render.
     *
     * @return {string[]}
     */
    getAttributeList() {
      return this.getFieldManager().getAttributeList(this.fieldType, this.name);
    }

    /**
     * Invoke inline-edit saving.
     *
     * @param {{[bypassClose]: boolean}} [options]
     */
    inlineEditSave(options) {
      options = options || {};
      if (this.recordHelper) {
        this.recordHelper.trigger('inline-edit-save', this.name, options);
        return;
      }

      // Code below supposed not to be executed.

      let data = this.fetch();
      const model = this.model;
      const prev = this.initialAttributes;
      model.set(data, {
        silent: true
      });
      data = model.attributes;
      let attrs = false;
      for (const attr in data) {
        if (_.isEqual(prev[attr], data[attr])) {
          continue;
        }
        (attrs || (attrs = {}))[attr] = data[attr];
      }
      if (!attrs) {
        this.inlineEditClose();
      }
      const isInvalid = this.validateCallback ? this.validateCallback() : this.validate();
      if (isInvalid) {
        Espo.Ui.error(this.translate('Not valid'));

        // @todo Revise.
        model.set(prev, {
          silent: true
        });
        return;
      }
      Espo.Ui.notify(this.translate('saving', 'messages'));
      model.save(/** @type Object */attrs, {
        patch: true
      }).then(() => {
        this.trigger('after:inline-save');
        this.trigger('after:save');
        model.trigger('after:save');
        Espo.Ui.success(this.translate('Saved'));
      }).catch(() => {
        Espo.Ui.error(this.translate('Error occurred'));

        // @todo Revise.
        model.set(prev, {
          silent: true
        });
        this.reRender();
      });
      if (!options.bypassClose) {
        this.inlineEditClose(true);
      }
    }

    /**
     * @public
     */
    removeInlineEditLinks() {
      const $cell = this.get$cell();
      $cell.find('.inline-save-link').remove();
      $cell.find('.inline-cancel-link').remove();
      $cell.find('.inline-edit-link').addClass('hidden');
    }

    /**
     * @private
     */
    addInlineEditLinks() {
      const $cell = this.get$cell();
      const saveLink = document.createElement('a');
      saveLink.role = 'button';
      saveLink.tabIndex = -1;
      saveLink.title = this.translate('Update') + ' · ' + 'Ctrl+Enter';
      saveLink.innerHTML = `<span class="fas fa-check"></span>`;
      saveLink.classList.add('inline-save-link');
      const cancelLink = document.createElement('a');
      cancelLink.role = 'button';
      cancelLink.tabIndex = -1;
      cancelLink.title = this.translate('Cancel') + ' · ' + 'Esc';
      cancelLink.innerHTML = `<span class="fas fa-arrow-right-to-bracket"></span>`;
      cancelLink.classList.add('inline-cancel-link');
      $cell.prepend(saveLink);
      $cell.prepend(cancelLink);
      $cell.find('.inline-edit-link').addClass('hidden');
      saveLink.onclick = () => this.inlineEditSave();
      cancelLink.onclick = () => this.inlineEditClose();
    }

    /**
     * @public
     * @param {boolean} value
     * @internal
     */
    setIsInlineEditMode(value) {
      this._isInlineEditMode = value;
    }

    /**
     * Exist inline-edit mode.
     *
     * @param {boolean} [noReset]
     * @return {Promise}
     */
    inlineEditClose(noReset) {
      this.trigger('inline-edit-off', {
        noReset: noReset
      });
      this.$el.off('keydown.inline-edit');
      this._isInlineEditMode = false;
      if (!this.isEditMode()) {
        return Promise.resolve();
      }
      if (!noReset) {
        this.model.set(this.initialAttributes, {
          skipReRenderInEditMode: true,
          action: 'cancel-edit'
        });
      }
      const promise = this.setDetailMode().then(() => this.reRender(true)).then(() => this.removeInlineEditLinks());
      this.trigger('after:inline-edit-off', {
        noReset: noReset
      });
      return promise;
    }

    /**
     * Switch to inline-edit mode.
     *
     * @return {Promise}
     */
    inlineEdit() {
      this.trigger('edit', this);
      this.initialAttributes = this.model.getClonedAttributes();
      this._isInlineEditMode = true;
      const promise = this.setEditMode().then(() => this.reRender(true)).then(() => this.addInlineEditLinks()).then(() => {
        this.$el.on('keydown.inline-edit', e => {
          const key = Espo.Utils.getKeyFromKeyEvent(e);
          if (key === 'Control+Enter') {
            e.stopPropagation();
            if (document.activeElement instanceof HTMLInputElement) {
              // Fields may need to fetch data first.
              document.activeElement.dispatchEvent(new Event('change', {
                bubbles: true
              }));
            }
            this.fetchToModel();
            this.inlineEditSave();
            setTimeout(() => {
              this.get$cell().focus();
            }, 100);
            return;
          }
          if (key === 'Escape') {
            e.stopPropagation();
            this.inlineEditClose().then(() => {
              this.get$cell().focus();
            });
            return;
          }
          if (key === 'Control+KeyS') {
            e.preventDefault();
            e.stopPropagation();
            this.fetchToModel();
            this.inlineEditSave({
              bypassClose: true
            });
          }
        });
        setTimeout(() => this.focusOnInlineEdit(), 10);
      });
      this.trigger('inline-edit-on');
      return promise;
    }

    /**
     * @protected
     */
    focusOnInlineEdit() {
      const $element = this.$element && this.$element.length ? this.$element : this.$el.find('.form-control').first();
      if (!$element) {
        return;
      }
      $element.first().focus();
    }

    /**
     * Suspend a validation message.
     *
     * @internal
     * @param {number} [time=200]
     */
    suspendValidationMessage(time) {
      this.validationMessageSuspended = true;
      setTimeout(() => this.validationMessageSuspended = false, time || 200);
    }

    /**
     * Show a validation message.
     *
     * @param {string} message A message.
     * @param {string|JQuery|Element} [target] A target element or selector.
     * @param {module:view} [view] A child view that contains the target. The closest view should to passed.
     *   Should be omitted if there is no child views or the target is not rendered by a child view.
     */
    showValidationMessage(message, target, view) {
      if (this.validationMessageSuspended) {
        return;
      }
      let $el;
      target = target || this.validationElementSelector || '.main-element';
      if (typeof target === 'string' || target instanceof String) {
        $el = this.$el.find(target);
      } else {
        $el = (0, _jquery.default)(target);
      }
      if (!$el.length && this.$element) {
        $el = this.$element;
      }
      if (!$el.length) {
        $el = this.$el;
      }
      if ($el.length) {
        const rect = $el.get(0).getBoundingClientRect();
        this.lastValidationMessage = message;
        if (rect.top === 0 && rect.bottom === 0 && rect.left === 0) {
          return;
        }
      }
      this._popoverMap = this._popoverMap || new WeakMap();
      const element = $el.get(0);
      if (!element) {
        return;
      }
      if (this._popoverMap.has(element)) {
        try {
          this._popoverMap.get(element).detach();
        } catch (e) {}
      }
      const popover = Espo.Ui.popover($el, {
        placement: 'bottom',
        container: 'body',
        content: this.getHelper().transformMarkdownText(message).toString(),
        trigger: 'manual',
        noToggleInit: true,
        noHideOnOutsideClick: true
      }, view || this);
      popover.show();
      this._popoverMap.set(element, popover);
      $el.closest('.field').one('mousedown click', () => popover.destroy());
      this.once('render remove', () => popover.destroy());
      this._timeoutMap = this._timeoutMap || new WeakMap();
      if (this._timeoutMap.has(element)) {
        clearTimeout(this._timeoutMap.get(element));
      }
      const timeout = setTimeout(() => {
        popover.destroy();
      }, this.VALIDATION_POPOVER_TIMEOUT);
      this._timeoutMap.set(element, timeout);
    }

    /**
     * Validate field values.
     *
     * @return {boolean} True if not valid.
     */
    validate() {
      this.lastValidationMessage = null;
      for (const item of this.validations) {
        let notValid = false;
        if (typeof item === 'function') {
          notValid = item();
        } else {
          const method = 'validate' + Espo.Utils.upperCaseFirst(item);
          notValid = this[method].call(this);
        }
        if (notValid) {
          this.trigger('invalid');
          return true;
        }
      }
      return false;
    }

    /**
     * Get a label text.
     *
     * @returns {string}
     */
    getLabelText() {
      return this.labelText;
    }

    /**
     * Validate required.
     *
     * @return {boolean}
     */
    validateRequired() {
      if (this.isRequired()) {
        if (this.model.get(this.name) === '' || this.model.get(this.name) === null) {
          const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
          this.showValidationMessage(msg);
          return true;
        }
      }
    }

    /**
     * Defines whether the field should have a required-marker rendered.
     *
     * @protected
     * @return {boolean}
     */
    hasRequiredMarker() {
      return this.isRequired();
    }

    /**
     * Fetch field values to the model.
     */
    fetchToModel() {
      this.model.set(this.fetch(), {
        silent: true
      });
    }

    /**
     * Fetch field values from DOM.
     *
     * @return {Object.<string, *>}
     */
    fetch() {
      if (!this.$element.length) {
        return {};
      }
      const data = {};
      data[this.name] = this.$element.val().trim();
      return data;
    }

    /**
     * Fetch search data from DOM.
     *
     * @return {Object.<string, *>|null}
     */
    fetchSearch() {
      const value = this.$element.val().toString().trim();
      if (value) {
        return {
          type: 'equals',
          value: value
        };
      }
      return null;
    }

    /**
     * Fetch a search type from DOM.
     *
     * @return {string}
     */
    fetchSearchType() {
      return this.$el.find('select.search-type').val();
    }

    /**
     * To skip re-render on change in edit mode.
     *
     * @protected
     * @since 9.1.2
     */
    toSkipReRenderOnChange() {
      return false;
    }
  }
  var _default = _exports.default = BaseFieldView;
});

define("ui/multi-select", ["exports", "lib!selectize"], function (_exports, _libSelectize) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _libSelectize = _interopRequireDefault(_libSelectize);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 module:ui/multi-select */

  /**
   * @typedef module:ui/multi-select~Options
   * @type {Object}
   * @property {{value: string, text: string, style?: string, small?: boolean}[]} items
   * @property {string} [delimiter=':,:']
   * @property {boolean} [restoreOnBackspace=false]
   * @property {boolean} [removeButton=true]
   * @property {boolean} [draggable=false]
   * @property {boolean} [selectOnTab=false]
   * @property {boolean} [matchAnyWord=false]
   * @property {boolean} [allowCustomOptions=false]
   * @property {function (string): {value: string, text: string}|null} [create]
   * @property {string[]} [values] Values. As of v9.1.0.
   */

  /**
   * @module ui/multi-select
   */
  const MultiSelect = {
    /**
     * @const
     */
    defaultDelimiter: ':,:',
    /**
     * @param {Element|JQuery} element An element.
     * @param {module:ui/multi-select~Options} options Options.
     */
    init: function (element, options) {
      const $el = $(element);
      const delimiter = options.delimiter || this.defaultDelimiter;
      if (options.values) {
        $el.val(options.values.join(delimiter));
      }
      options = MultiSelect.applyDefaultOptions(options);
      const plugins = {};
      if (options.removeButton) {
        plugins.remove_button = {
          title: ''
        };
      }
      if (options.draggable) {
        plugins.drag_drop = {};
      }
      if (options.restoreOnBackspace) {
        MultiSelect.loadRestoreOnBackspacePlugin();
        plugins.restore_on_backspace_espo = {};
      }
      MultiSelect.loadBypassCtrlEnterPlugin();
      plugins.bypass_ctrl_enter = {};
      const selectizeOptions = {
        options: options.items,
        plugins: plugins,
        delimiter: delimiter,
        labelField: 'text',
        valueField: 'value',
        searchField: ['text'],
        highlight: false,
        selectOnTab: options.selectOnTab
      };
      if (!options.matchAnyWord) {
        // noinspection JSUnresolvedReference
        /** @this Selectize */
        selectizeOptions.score = function (search) {
          // noinspection JSUnresolvedReference
          const score = this.getScoreFunction(search);
          search = search.toLowerCase();
          return function (item) {
            if (item.text.toLowerCase().indexOf(search) === 0) {
              return score(item);
            }
            return 0;
          };
        };
      }
      if (options.matchAnyWord) {
        /** @this Selectize */
        selectizeOptions.score = function (search) {
          // noinspection JSUnresolvedReference
          const score = this.getScoreFunction(search);
          search = search.toLowerCase();
          return function (item) {
            const text = item.text.toLowerCase();
            if (!text.split(' ').find(item => item.startsWith(search)) && !text.startsWith(search)) {
              return 0;
            }
            return score(item);
          };
        };
      }
      selectizeOptions.render = {};
      if (options.allowCustomOptions) {
        selectizeOptions.persist = false;
        selectizeOptions.create = options.create;
        selectizeOptions.render.option_create = data => {
          return $('<div>').addClass('create').append($('<span>').text(data.input).addClass('text-bold')).append('&hellip;').get(0).outerHTML;
        };
      }
      const classMap = {};
      const styleMap = {};
      (options.items || []).forEach(it => {
        if (it.small) {
          classMap[it.value] = 'small';
        }
        if (it.style) {
          styleMap[it.value] = it.style;
        }
      });
      (options.items || []).forEach(it => {
        if (it.small) {
          classMap[it.value] = 'small';
        }
      });
      selectizeOptions.render.item = (/** {text: string, value: string} */data, escape) => {
        const text = escape(data.text);
        const style = escape(styleMap[data.value] || '');
        const classString = escape(classMap[data.value] || '');
        return `<div class="item ${style}"><span class="${classString}">${text}</span> ` + `<a href="javascript:" class="remove">&times;</a></div>`;
      };
      selectizeOptions.render.option = (/** {text: string, value: string} */data, escape) => {
        const value = data.value;
        let classes = '';
        if (value === '') {
          classes += ' selectize-dropdown-emptyoptionlabel';
        }
        const style = styleMap[data.value];
        if (style) {
          classes += ' ' + escape('text-' + style);
        }
        const text = escape(data.text);
        return `<div class="option ${classes}">${text}</div>`;
      };
      $el.selectize(selectizeOptions);
      if ($el[0]) {
        // Check for compatibility with existing code where initialing is called against non-existing element.

        $el[0].selectize.on('item_before_remove', (v, $item) => {
          // Otherwise, the item is left active.
          $item.removeClass('active');
        });
      }
    },
    /**
     * Focus.
     *
     * @param {Element|JQuery} element An element.
     */
    focus: function (element) {
      const $el = $(element);
      if (!$el[0] || !$el[0].selectize) {
        return;
      }
      const selectize = $el[0].selectize;
      selectize.focus();
    },
    /**
     * @private
     * @param {module:ui/multi-select~Options} options
     * @return {module:ui/multi-select~Options}
     */
    applyDefaultOptions: function (options) {
      options = Espo.Utils.clone(options);
      const defaults = {
        removeButton: true,
        draggable: false,
        selectOnTab: false,
        delimiter: MultiSelect.defaultDelimiter,
        matchAnyWord: false,
        allowCustomOptions: false
      };
      for (const key in defaults) {
        if (key in options) {
          continue;
        }
        options[key] = defaults[key];
      }
      return options;
    },
    /**
     * @private
     */
    loadBypassCtrlEnterPlugin: function () {
      if ('bypass_ctrl_enter' in _libSelectize.default.plugins) {
        return;
      }
      const IS_MAC = /Mac/.test(navigator.userAgent);
      _libSelectize.default.define('bypass_ctrl_enter', function () {
        const self = this;
        this.onKeyDown = function () {
          const original = self.onKeyDown;
          return function (e) {
            if (e.code === 'Enter' && (IS_MAC ? e.metaKey : e.ctrlKey)) {
              return;
            }
            return original.apply(this, arguments);
          };
        }();
      });
    },
    /**
     * @private
     */
    loadRestoreOnBackspacePlugin: function () {
      if ('restore_on_backspace_espo' in _libSelectize.default.plugins) {
        return;
      }
      _libSelectize.default.define('restore_on_backspace_espo', function (options) {
        options.text = options.text || function (option) {
          return option[this.settings.labelField];
        };
        const self = this;
        this.onKeyDown = function () {
          const original = self.onKeyDown;
          return function (e) {
            let index, option;
            if (e.code === 'Backspace' && this.$control_input.val() === '' && !this.$activeItems.length) {
              index = this.caretPos - 1;
              if (index >= 0 && index < this.items.length) {
                option = this.options[this.items[index]];
                option = {
                  value: option.value,
                  $order: option.$order,
                  text: option.value
                };

                // noinspection JSUnresolvedReference
                if (this.deleteSelection(e)) {
                  // noinspection JSUnresolvedReference
                  this.setTextboxValue(options.text.apply(this, [option]));
                  this.refreshOptions(true);
                }
                e.preventDefault();
                return;
              }
            }
            return original.apply(this, arguments);
          };
        }();
      });
    }
  };
  var _default = _exports.default = MultiSelect;
});

define("ui/autocomplete", ["exports", "jquery", "handlebars"], function (_exports, _jquery, _handlebars) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _jquery = _interopRequireDefault(_jquery);
  _handlebars = _interopRequireDefault(_handlebars);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * An autocomplete.
   */
  class Autocomplete {
    /** @module ui/autocomplete */

    /**
     * @typedef {Object} module:ui/autocomplete~item
     * @property {string} value
     */

    /**
     * @typedef {{
     *     name?: string,
     *     forceHide?: boolean,
     *     lookup?: string[],
     *     lookupFunction?: function (string): Promise<Array<module:ui/autocomplete~item & Record>>,
     *     minChars?: Number,
     *     formatResult?: function (module:ui/autocomplete~item & Record): string,
     *     onSelect?: function (module:ui/autocomplete~item & Record): void,
     *     beforeRender?: function (HTMLElement): void,
     *     triggerSelectOnValidInput?: boolean,
     *     autoSelectFirst?: boolean,
     *     handleFocusMode?: 1|2|3,
     *     focusOnSelect?: boolean,
     * }} module:ui/autocomplete~options
     */

    /**
     * @param {HTMLInputElement} element
     * @param {module:ui/autocomplete~options} options
     */
    constructor(element, options) {
      /** @private */
      this.$element = (0, _jquery.default)(element);
      this.$element.on('keydown', e => {
        if (e.code === 'Tab' && !this.$element.val()) {
          e.stopImmediatePropagation();
        }
      });
      const lookup = options.lookupFunction ? (query, done) => {
        options.lookupFunction(query).then(items => {
          done({
            suggestions: items
          });
        });
      } : options.lookup;
      const lookupFilter = !options.lookupFunction ? (/** (module:ui/autocomplete~item */suggestion, /** string */query, /** string */queryLowerCase) => {
        if (suggestion.value.toLowerCase().indexOf(queryLowerCase) === 0) {
          return suggestion.value.length !== queryLowerCase.length;
        }
        return false;
      } : undefined;
      const $modalBody = this.$element.closest('.modal-body');
      const isModal = !!$modalBody.length;
      this.$element.autocomplete({
        beforeRender: $container => {
          if (options.beforeRender) {
            options.beforeRender($container.get(0));
          }
          if (this.$element.hasClass('input-sm')) {
            $container.addClass('small');
          }
          if (options.forceHide) {
            // Prevent an issue that suggestions are shown and not hidden
            // when clicking outside the window and then focusing back on the document.
            if (this.$element.get(0) !== document.activeElement) {
              setTimeout(() => this.$element.autocomplete('hide'), 30);
            }
          }
          if (isModal) {
            // Fixes dropdown dissapearing when clicking scrollbar.
            $container.on('mousedown', e => {
              e.preventDefault();
            });
          }
        },
        lookup: lookup,
        minChars: options.minChars || 0,
        noCache: true,
        autoSelectFirst: options.autoSelectFirst,
        appendTo: $modalBody.length ? $modalBody : 'body',
        forceFixPosition: true,
        maxHeight: 308,
        formatResult: item => {
          if (options.formatResult) {
            return options.formatResult(item);
          }
          return _handlebars.default.Utils.escapeExpression(item.value);
        },
        lookupFilter: lookupFilter,
        onSelect: item => {
          if (options.onSelect) {
            options.onSelect(item);
          }
          if (options.focusOnSelect) {
            this.$element.focus();
          }
        },
        triggerSelectOnValidInput: options.triggerSelectOnValidInput || false
      });
      this.$element.attr('autocomplete', 'espo-' + (options.name || 'dummy'));
      if (options.handleFocusMode) {
        this.initHandleFocus(options);
      }
    }

    /**
     * @private
     * @param {module:ui/autocomplete~options} options
     */
    initHandleFocus(options) {
      this.$element.off('focus.autocomplete');
      this.$element.on('focus', () => {
        if (options.handleFocusMode === 1) {
          if (this.$element.val()) {
            return;
          }
          this.$element.autocomplete('onValueChange');
          return;
        }
        if (this.$element.val()) {
          // noinspection JSUnresolvedReference
          this.$element.get(0).select();
          return;
        }
        this.$element.autocomplete('onFocus');
      });
      if (options.handleFocusMode === 3) {
        this.$element.on('change', () => this.$element.val(''));
      }
    }

    /**
     * Dispose.
     */
    dispose() {
      this.$element.autocomplete('dispose');
    }

    /**
     * Hide.
     */
    hide() {
      this.$element.autocomplete('hide');
    }

    /**
     * Clear.
     */
    clear() {
      this.$element.autocomplete('clear');
    }
  }
  var _default = _exports.default = Autocomplete;
});

define("helpers/reg-exp-pattern", ["exports", "di", "metadata", "language"], function (_exports, _di, _metadata, _language) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _metadata = _interopRequireDefault(_metadata);
  _language = _interopRequireDefault(_language);
  let _init_metadata, _init_extra_metadata, _init_language, _init_extra_language;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * A regular expression pattern helper.
   */
  class RegExpPatternHelper {
    static #_ = [_init_metadata, _init_extra_metadata, _init_language, _init_extra_language] = _applyDecs(this, [], [[(0, _di.inject)(_metadata.default), 0, "metadata"], [(0, _di.inject)(_language.default), 0, "language"]]).e;
    constructor() {
      _init_extra_language(this);
    }
    /**
     * @private
     * @type {Metadata}
     */
    metadata = _init_metadata(this);

    /**
     * @private
     * @type {Language}
     */
    language = (_init_extra_metadata(this), _init_language(this));

    /**
     *
     * @param {string} pattern
     * @param {string|null} value
     * @param {string} [field]
     * @param {string} [entityType]
     * @return {{message: string}|null}
     */
    validate(pattern, value, field, entityType) {
      if (value === '' || value === null) {
        return null;
      }
      let messageKey = 'fieldNotMatchingPattern';
      if (pattern[0] === '$') {
        const patternName = pattern.slice(1);
        const foundPattern = this.metadata.get(['app', 'regExpPatterns', patternName, 'pattern']);
        if (foundPattern) {
          messageKey += '$' + patternName;
          pattern = foundPattern;
        }
      }
      const regExp = new RegExp('^' + pattern + '$');
      if (regExp.test(value)) {
        return null;
      }
      let message = this.language.translate(messageKey, 'messages').replace('{pattern}', pattern);
      if (field && entityType) {
        message = message.replace('{field}', this.language.translate(field, 'fields', entityType));
      }
      return {
        message: message
      };
    }
  }
  var _default = _exports.default = RegExpPatternHelper;
});

define("views/record/edit", ["exports", "views/record/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/edit */

  /**
   * An edit-record view. Used for create and edit.
   */
  class EditRecordView extends _detail.default {
    /** @inheritDoc */
    template = 'record/edit';

    /** @inheritDoc */
    type = 'edit';
    /** @inheritDoc */
    fieldsMode = 'edit';
    /** @inheritDoc */
    mode = 'edit';

    /**
     * @inheritDoc
     * @type {module:views/record/detail~button[]}
     */
    buttonList = [{
      name: 'save',
      label: 'Save',
      style: 'primary',
      title: 'Ctrl+Enter'
    }, {
      name: 'cancel',
      label: 'Cancel',
      title: 'Esc'
    }];
    /**
     * @inheritDoc
     * @type {Array<module:views/record/detail~dropdownItem>}
     */
    dropdownItemList = [];

    /** @inheritDoc */
    sideView = 'views/record/edit-side';
    /** @inheritDoc */
    bottomView = 'views/record/edit-bottom';
    /** @inheritDoc */
    duplicateAction = false;
    /** @inheritDoc */
    saveAndContinueEditingAction = true;
    /** @inheritDoc */
    saveAndNewAction = true;
    /** @inheritDoc */
    setupHandlerType = 'record/edit';

    /**
     * @param {
     *     module:views/record/detail~options |
     *     {
     *         duplicateSourceId?: string,
     *         focusForCreate?: boolean,
     *     }
     * } options Options.
     */
    constructor(options) {
      super(options);
    }

    /** @inheritDoc */
    actionSave(data) {
      data = data || {};
      const isNew = this.isNew;
      return this.save(data.options).then(() => {
        if (this.options.duplicateSourceId) {
          this.returnUrl = null;
        }
        this.exit(isNew ? 'create' : 'save');
      }).catch(reason => Promise.reject(reason));
    }

    /**
     * A `cancel` action.
     */
    actionCancel() {
      this.cancel();
    }

    /**
     * Cancel.
     */
    cancel() {
      if (this.isChanged) {
        this.resetModelChanges();
      }
      this.setIsNotChanged();
      this.exit('cancel');
    }

    /** @inheritDoc */
    setupBeforeFinal() {
      /** @type {Promise|undefined} */
      let promise = undefined;
      if (this.model.isNew()) {
        promise = this.populateDefaults();
      }
      if (!promise) {
        // Attributes are yet not ready.
        super.setupBeforeFinal();
      }
      if (promise) {
        this.wait(promise);

        // @todo Revise. Possible race condition issues.
        promise.then(() => super.setupBeforeFinal());
      }
      if (this.model.isNew()) {
        this.once('after:render', () => {
          this.model.set(this.fetch(), {
            silent: true
          });
        });
      }
      if (this.options.focusForCreate) {
        this.once('after:render', () => {
          if (this.$el.closest('.modal').length) {
            setTimeout(() => this.focusForCreate(), 50);
            return;
          }
          this.focusForCreate();
        });
      }
      this.setupHighlight();
    }

    /** @inheritDoc */
    setupActionItems() {
      super.setupActionItems();
      if (this.saveAndContinueEditingAction && this.getAcl().checkScope(this.entityType, 'edit')) {
        this.dropdownItemList.push({
          name: 'saveAndContinueEditing',
          label: 'Save & Continue Editing',
          title: 'Ctrl+S',
          groupIndex: 0
        });
      }
      if (this.isNew && this.saveAndNewAction && this.getAcl().checkScope(this.entityType, 'create')) {
        this.dropdownItemList.push({
          name: 'saveAndNew',
          label: 'Save & New',
          title: 'Ctrl+Alt+Enter',
          groupIndex: 0
        });
      }
    }

    /**
     * A `save-and-create-new` action.
     */
    actionSaveAndNew(data) {
      data = data || {};
      const proceedCallback = () => {
        Espo.Ui.success(this.translate('Created'));
        this.getRouter().dispatch(this.scope, 'create', {
          rootUrl: this.options.rootUrl,
          focusForCreate: !!data.focusForCreate
        });
        this.getRouter().navigate('#' + this.scope + '/create', {
          trigger: false
        });
      };
      this.save(data.options).then(proceedCallback).catch(() => {});
      if (this.lastSaveCancelReason === 'notModified') {
        proceedCallback();
      }
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyEscape(e) {
      if (this.buttonsDisabled) {
        return;
      }
      if (this.buttonList.findIndex(item => item.name === 'cancel' && !item.hidden) === -1) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      const focusedFieldView = this.getFocusedFieldView();
      if (focusedFieldView) {
        this.model.set(focusedFieldView.fetch());
      }
      if (this.isChanged) {
        this.confirm(this.translate('confirmLeaveOutMessage', 'messages')).then(() => this.actionCancel());
        return;
      }
      this.actionCancel();
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlAltEnter(e) {
      if (this.buttonsDisabled) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      if (!this.saveAndNewAction) {
        return;
      }
      if (!this.hasAvailableActionItem('saveAndNew')) {
        return;
      }
      this.actionSaveAndNew({
        focusForCreate: true
      });
    }

    /** @private */
    setupHighlight() {
      if (!this.options.highlightFieldList) {
        return;
      }
      this.on('after:render', () => {
        const fieldList = /** @type {string[]} */this.options.highlightFieldList;
        fieldList.map(it => this.getFieldView(it)).filter(view => view).forEach(view => view.highlight());
      });
    }
  }
  var _default = _exports.default = EditRecordView;
});

define("views/fields/varchar", ["exports", "views/fields/base", "helpers/reg-exp-pattern", "ui/autocomplete", "ui/multi-select"], function (_exports, _base, _regExpPattern, _autocomplete, _multiSelect) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _regExpPattern = _interopRequireDefault(_regExpPattern);
  _autocomplete = _interopRequireDefault(_autocomplete);
  _multiSelect = _interopRequireDefault(_multiSelect);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/varchar */

  /**
   * A varchar field.
   *
   * @extends BaseFieldView<module:views/fields/varchar~params>
   */
  class VarcharFieldView extends _base.default {
    /**
     * @typedef {Object} module:views/fields/varchar~options
     * @property {
     *     module:views/fields/varchar~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/varchar~params
     * @property {number} [maxLength] A max length.
     * @property {string[]} [options] Select options.
     * @property {boolean} [required] Required.
     * @property {string} [optionsPath] An options metadata path.
     * @property {boolean} [noSpellCheck] Disable spell check.
     * @property {string} [pattern] A validation pattern. If starts with `$`, then a predefined pattern is used.
     * @property {boolean} [copyToClipboard] To display a Copy-to-clipboard button.
     */

    /**
     * @param {
     *     module:views/fields/varchar~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'varchar';
    listTemplate = 'fields/varchar/list';
    detailTemplate = 'fields/varchar/detail';
    searchTemplate = 'fields/varchar/search';
    searchTypeList = ['startsWith', 'contains', 'equals', 'endsWith', 'like', 'notContains', 'notEquals', 'notLike', 'anyOf', 'noneOf', 'isEmpty', 'isNotEmpty'];

    /**
     * @inheritDoc
     * @type {Array<(function (): boolean)|string>}
     */
    validations = ['required', 'pattern'];

    /**
     * Use an autocomplete requesting data from the backend.
     *
     * @protected
     * @type {boolean}
     */
    useAutocompleteUrl = false;

    /**
     * No spell-check.
     *
     * @protected
     * @type {boolean}
     */
    noSpellCheck = false;

    /**
     * @private
     * @type {HTMLInputElement}
     */
    searchMultiSelectInputElement;
    setup() {
      this.setupOptions();
      this.noSpellCheck = this.noSpellCheck || this.params.noSpellCheck;
      if (this.params.optionsPath) {
        this.params.options = Espo.Utils.clone(this.getMetadata().get(this.params.optionsPath) || []);
      }
      if (this.options.customOptionList) {
        this.setOptionList(this.options.customOptionList);
      }
      if (this.mode === this.MODE_DETAIL) {
        if (this.params.copyToClipboard) {
          this.events['click [data-action="copyToClipboard"]'] = () => this.copyToClipboard();
        }
      }
      this.on('remove', () => {
        this.searchMultiSelectInputElement = undefined;
      });
    }

    /**
     * Set up options.
     */
    setupOptions() {}

    /**
     * Set options.
     *
     * @param {string[]} optionList Options.
     */
    setOptionList(optionList) {
      if (!this.originalOptionList) {
        this.originalOptionList = this.params.options || [];
      }
      this.params.options = Espo.Utils.clone(optionList);
      if (this.isEditMode()) {
        if (this.isRendered()) {
          this.reRender();
        }
      }
    }

    /**
     * Reset options.
     */
    resetOptionList() {
      if (this.originalOptionList) {
        this.params.options = Espo.Utils.clone(this.originalOptionList);
      }
      if (this.isEditMode()) {
        if (this.isRendered()) {
          this.reRender();
        }
      }
    }

    /**
     * @protected
     */
    copyToClipboard() {
      const value = this.model.get(this.name);
      navigator.clipboard.writeText(value).then(() => {
        Espo.Ui.success(this.translate('Copied to clipboard'));
      });
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * Compose an autocomplete URL.
     *
     * @param {string} q A query.
     * @return {string}
     */
    getAutocompleteUrl(q) {
      return '';
    }

    /**
     * @return {module:ui/autocomplete~item[]}
     */
    transformAutocompleteResult(response) {
      const responseParsed = typeof response === 'string' ? JSON.parse(response) : response;
      const list = [];
      responseParsed.list.forEach(item => {
        list.push({
          value: item.name || item.id,
          attributes: item
        });
      });
      return list;
    }
    setupSearch() {
      this.addHandler('change', 'select.search-type', (e, /** HTMLSelectElement */target) => {
        this.handleSearchType(target.value);
      });
    }
    data() {
      const data = super.data();
      if (this.model.get(this.name) !== null && this.model.get(this.name) !== '' && this.model.has(this.name)) {
        data.isNotEmpty = true;
      }
      data.valueIsSet = this.model.has(this.name);
      if (this.isSearchMode()) {
        if (typeof this.searchParams.value === 'string') {
          this.searchData.value = this.searchParams.value;
        }
        if (this.searchParams.data && typeof this.searchParams.data.value === 'string') {
          this.searchData.value = this.searchParams.data.value;
        }
        if (!this.searchParams.value && !this.searchParams.data) {
          this.searchData.value = null;
        }
      }
      data.noSpellCheck = this.noSpellCheck;
      data.copyToClipboard = this.params.copyToClipboard;
      data.textClass = null;
      return data;
    }

    /**
     * @protected
     * @param {string} type
     */
    handleSearchType(type) {
      const mainElement = this.element.querySelector('input.main-element');
      const multiSelectContainer = this.element.querySelector('div[data-role="multi-select-container"]');
      if (['isEmpty', 'isNotEmpty', 'anyOf', 'noneOf'].includes(type)) {
        mainElement.classList.add('hidden');
      } else {
        mainElement.classList.remove('hidden');
      }
      if (multiSelectContainer) {
        if (['anyOf', 'noneOf'].includes(type)) {
          multiSelectContainer.classList.remove('hidden');
        } else {
          multiSelectContainer.classList.add('hidden');
        }
      }
    }
    afterRender() {
      super.afterRender();
      if (this.isSearchMode()) {
        const type = this.$el.find('select.search-type').val();
        this.handleSearchType(type);
        this.initSearchMultiSelect();
      }
      if ((this.isEditMode() || this.isSearchMode()) && (this.params.options && this.params.options.length || this.useAutocompleteUrl)) {
        let lookupFunction = this.getAutocompleteLookupFunction();
        if (this.useAutocompleteUrl) {
          lookupFunction = query => {
            return Espo.Ajax.getRequest(this.getAutocompleteUrl(query)).then(response => this.transformAutocompleteResult(response));
          };
        }
        const autocomplete = new _autocomplete.default(this.$element.get(0), {
          name: this.name,
          triggerSelectOnValidInput: true,
          autoSelectFirst: true,
          handleFocusMode: 1,
          focusOnSelect: true,
          onSelect: () => this.trigger('change'),
          lookup: this.params.options,
          lookupFunction: lookupFunction
        });
        this.once('render remove', () => autocomplete.dispose());
      }
      if (this.isSearchMode()) {
        this.$el.find('select.search-type').on('change', () => {
          this.trigger('change');
        });
        this.$element.on('input', () => {
          this.trigger('change');
        });
      }
    }

    // noinspection JSUnusedGlobalSymbols
    validatePattern() {
      const pattern = this.params.pattern;
      return this.fieldValidatePattern(this.name, pattern);
    }

    /**
     * Used by other field views.
     *
     * @param {string} name
     * @param {string} [pattern]
     */
    fieldValidatePattern(name, pattern) {
      pattern = pattern || this.model.getFieldParam(name, 'pattern');
      /** @var {string|null} value */
      const value = this.model.get(name);
      if (!pattern) {
        return false;
      }
      const helper = new _regExpPattern.default();
      const result = helper.validate(pattern, value, name, this.entityType);
      if (!result) {
        return false;
      }
      const message = result.message.replace('{field}', this.getLanguage().translate(this.getLabelText()));
      this.showValidationMessage(message, '[data-name="' + name + '"]');
      return true;
    }

    /** @inheritDoc */
    fetch() {
      const data = {};
      const value = this.$element.val().trim();
      data[this.name] = value || null;
      return data;
    }

    /** @inheritDoc */
    fetchSearch() {
      const type = this.fetchSearchType() || 'startsWith';
      if (['isEmpty', 'isNotEmpty'].includes(type)) {
        if (type === 'isEmpty') {
          return {
            type: 'or',
            value: [{
              type: 'isNull',
              attribute: this.name
            }, {
              type: 'equals',
              attribute: this.name,
              value: ''
            }],
            data: {
              type: type
            }
          };
        }

        /** @type {Record[]} */
        const value = [{
          type: 'isNotNull',
          attribute: this.name,
          value: null
        }];
        if (!this.model.getFieldParam(this.name, 'notStorable')) {
          value.push({
            type: 'notEquals',
            attribute: this.name,
            value: ''
          });
        }
        return {
          type: 'and',
          value: value,
          data: {
            type: type
          }
        };
      }
      if (type === 'anyOf' || type === 'noneOf') {
        let list = this.searchMultiSelectInputElement ? this.searchMultiSelectInputElement.value.split(_multiSelect.default.defaultDelimiter) : [];
        if (list.length === 1 && list[0] === '') {
          list = [];
        }
        if (type === 'anyOf') {
          if (list.length === 0) {
            return {
              type: 'any',
              data: {
                type: 'anyOf',
                valueList: list
              }
            };
          }
          return {
            type: 'in',
            value: list,
            data: {
              type: 'anyOf',
              valueList: list
            }
          };
        }
        if (list.length === 0) {
          return {
            type: 'any',
            data: {
              type: 'noneOf',
              valueList: list
            }
          };
        }
        return {
          type: 'or',
          value: [
          // Don't change order.
          {
            type: 'notIn',
            value: list,
            attribute: this.name
          }, {
            type: 'isNull',
            attribute: this.name
          }],
          data: {
            type: 'noneOf',
            valueList: list
          }
        };
      }
      const value = this.$element.val().toString().trim();
      if (!value) {
        return null;
      }
      return {
        value: value,
        type: type,
        data: {
          type: type
        }
      };
    }
    getSearchType() {
      return this.getSearchParamsData().type || this.searchParams.typeFront || this.searchParams.type;
    }

    /**
     * Get an autocomplete lookup function.
     *
     * @protected
     * @return {function (string): Promise<Array<module:ui/autocomplete~item & Record>>|undefined}
     */
    getAutocompleteLookupFunction() {
      return undefined;
    }

    /**
     * @private
     */
    initSearchMultiSelect() {
      this.searchMultiSelectInputElement = this.element.querySelector('input[data-role="multi-select-input"]');
      _multiSelect.default.init(this.searchMultiSelectInputElement, {
        items: (this.params.options || []).map(it => ({
          value: it,
          text: it
        })),
        allowCustomOptions: true,
        create: input => {
          return {
            value: input,
            text: input
          };
        },
        values: this.getSearchParamsData().valueList || []
      });
      this.element.querySelector('.selectize-dropdown-content').classList.add('small');
    }
  }
  var _default = _exports.default = VarcharFieldView;
});

define("views/record/edit-for-modal", ["exports", "views/record/edit"], function (_exports, _edit) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/edit-for-modal */

  /**
   * An edit-record view to used for custom forms.
   */
  class EditForModalRecordView extends _edit.default {
    bottomView = null;
    sideView = null;
    buttonsDisabled = true;
    isWide = true;
    accessControlDisabled = true;
    confirmLeaveDisabled = true;
  }
  var _default = _exports.default = EditForModalRecordView;
});

define("views/fields/enum", ["exports", "views/fields/base", "ui/multi-select", "ui/select"], function (_exports, _base, _multiSelect, _select) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _multiSelect = _interopRequireDefault(_multiSelect);
  _select = _interopRequireDefault(_select);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/enumeration */

  /**
   * An enum field (select-box).
   *
   * @extends BaseFieldView<module:views/fields/enumeration~params>
   */
  class EnumFieldView extends _base.default {
    /**
     * @typedef {Object} module:views/fields/enumeration~options
     * @property {
     *     module:views/fields/enumeration~params &
     *     module:views/fields/base~params &
     *     Object.<string, *>
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/enumeration~params
     * @property {string[]} [options] Select options.
     * @property {boolean} [required] Required.
     * @property {string} [translation] A translation string. E.g. `Global.scopeNames`.
     * @property {boolean} [displayAsLabel] Display as label.
     * @property {string|'state'} [labelType] A label type.
     * @property {'regular'|'state'} [labelType] A label type.
     * @property {string} [optionsReference] A reference to options. E.g. `Account.industry`.
     * @property {string} [optionsPath] An options metadata path.
     * @property {boolean} [isSorted] To sort options.
     * @property {Object.<string, 'warning'|'danger'|'success'|'info'|'primary'>} [style] A style map.
     * @property {Object.<string, string>} [translatedOptions] Option translations.
     */

    /**
     * @param {
     *     module:views/fields/enumeration~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'enum';
    listTemplate = 'fields/enum/list';
    listLinkTemplate = 'fields/enum/list-link';
    detailTemplate = 'fields/enum/detail';
    editTemplate = 'fields/enum/edit';
    searchTemplate = 'fields/enum/search';
    translatedOptions = null;

    /**
     * @todo Remove? Always treat as true.
     */
    fetchEmptyValueAsNull = true;
    searchTypeList = ['anyOf', 'noneOf', 'isEmpty', 'isNotEmpty'];
    validationElementSelector = '.selectize-control';

    /**
     * @protected
     * @type {boolean}
     */
    nativeSelect = false;

    // noinspection JSCheckFunctionSignatures
    /** @inheritDoc */
    data() {
      const data = super.data();
      data.translatedOptions = this.translatedOptions;
      const value = this.model.get(this.name);
      if (this.isReadMode() && this.styleMap) {
        data.style = this.styleMap[value || ''] || 'default';
      }
      data.styleMap = this.styleMap;
      if (this.isReadMode()) {
        if (!this.params.displayAsLabel) {
          data.class = 'text';
        } else {
          if (this.params.labelType === 'state') {
            data.class = 'label label-md label-state label';
          } else {
            data.class = data.style && data.style !== 'default' ? 'label label-md label' : 'text';
          }
        }
      }
      const translationKey = value || '';
      if (typeof value !== 'undefined' && value !== null && value !== '' || translationKey === '' && translationKey in (this.translatedOptions || {}) && (this.translatedOptions || {})[translationKey] !== '') {
        data.isNotEmpty = true;
      }
      data.valueIsSet = this.model.has(this.name);
      if (data.isNotEmpty) {
        data.valueTranslated = this.translatedOptions ? this.translatedOptions[translationKey] || value : this.getLanguage().translateOption(translationKey, this.name, this.entityType);
      }
      if (this.isEditMode()) {
        data.nativeSelect = this.nativeSelect;
      }

      // noinspection JSValidateTypes
      return data;
    }
    setup() {
      if (!this.params.options) {
        const methodName = 'get' + Espo.Utils.upperCaseFirst(this.name) + 'Options';
        if (typeof this.model[methodName] === 'function') {
          this.params.options = this.model[methodName].call(this.model);
        }
      }
      this.styleMap = this.params.style || this.model.getFieldParam(this.name, 'style') || {};
      let optionsPath = this.params.optionsPath;
      /** @type {?string} */
      const optionsReference = this.params.optionsReference;
      if (!optionsPath && optionsReference) {
        const [refEntityType, refField] = optionsReference.split('.');
        optionsPath = `entityDefs.${refEntityType}.fields.${refField}.options`;
        if (Object.keys(this.styleMap).length === 0) {
          this.styleMap = this.getMetadata().get(`entityDefs.${refEntityType}.fields.${refField}.style`) || {};
        }
      }
      if (optionsPath) {
        this.params.options = Espo.Utils.clone(this.getMetadata().get(optionsPath)) || [];
      }
      this.setupOptions();
      if ('translatedOptions' in this.options) {
        this.translatedOptions = this.options.translatedOptions;
      }
      if ('translatedOptions' in this.params) {
        this.translatedOptions = this.params.translatedOptions;
      }
      this.setupTranslation();
      if (this.translatedOptions === null) {
        this.translatedOptions = this.getLanguage().translate(this.name, 'options', this.model.name) || {};
        if (this.translatedOptions === this.name) {
          this.translatedOptions = null;
        }
      }
      if (this.params.isSorted && this.translatedOptions) {
        this.params.options = Espo.Utils.clone(this.params.options) || [];
        this.params.options = this.params.options.sort((v1, v2) => {
          return (this.translatedOptions[v1] || v1).localeCompare(this.translatedOptions[v2] || v2);
        });
      }
      if (this.options.customOptionList) {
        this.setOptionList(this.options.customOptionList);
      }
    }
    setupTranslation() {
      let translation = this.params.translation;
      /** @type {?string} */
      const optionsReference = this.params.optionsReference;
      if (!translation && optionsReference) {
        const [refEntityType, refField] = optionsReference.split('.');
        translation = `${refEntityType}.options.${refField}`;
      }
      if (!translation) {
        return;
      }
      this.translatedOptions = null;
      if (!this.params.options) {
        return;
      }
      const obj = this.getLanguage().translatePath(translation);
      const map = {};
      this.params.options.forEach(item => {
        if (typeof obj === 'object' && item in obj) {
          map[item] = obj[item];
          return;
        }
        if (Array.isArray(obj) && typeof item === 'number' && typeof obj[item] !== 'undefined') {
          map[item.toString()] = obj[item];
          return;
        }
        map[item] = item;
      });
      const value = this.model.get(this.name);
      if ((value || value === '') && !(value in map)) {
        if (typeof obj === 'object' && value in obj) {
          map[value] = obj[value];
        }
      }
      this.translatedOptions = map;
    }

    /**
     * Set up options.
     */
    setupOptions() {}

    /**
     * Set translated options.
     *
     * @param {Record} translatedOptions
     * @since 8.4.0
     */
    setTranslatedOptions(translatedOptions) {
      this.translatedOptions = translatedOptions;
    }

    /**
     * Set an option list.
     *
     * @param {string[]} optionList An option list.
     * @return {Promise}
     */
    setOptionList(optionList) {
      const previousOptions = this.params.options;
      if (!this.originalOptionList) {
        this.originalOptionList = this.params.options;
      }
      const newOptions = Espo.Utils.clone(optionList) || [];
      this.params.options = newOptions;
      const isChanged = !_(previousOptions).isEqual(optionList);
      if (!this.isEditMode() || !isChanged) {
        return Promise.resolve();
      }
      let triggerChange = false;
      const currentValue = this.model.get(this.name);
      if (!newOptions.includes(currentValue) && this.isReady) {
        this.model.set(this.name, newOptions[0] ?? null, {
          silent: true
        });
        triggerChange = true;
      }
      return this.reRender().then(() => {
        if (triggerChange) {
          this.trigger('change');
        }
      });
    }

    /**
     * Reset a previously set option list.
     *
     * @return {Promise}
     */
    resetOptionList() {
      if (!this.originalOptionList) {
        return Promise.resolve();
      }
      const previousOptions = this.params.options;
      this.params.options = Espo.Utils.clone(this.originalOptionList);
      const isChanged = !_(previousOptions).isEqual(this.originalOptionList);
      if (!this.isEditMode() || !isChanged) {
        return Promise.resolve();
      }
      if (this.isRendered()) {
        return this.reRender();
      }
      return Promise.resolve();
    }
    setupSearch() {
      this.events = _.extend({
        'change select.search-type': e => {
          this.handleSearchType($(e.currentTarget).val());
        }
      }, this.events || {});
    }
    handleSearchType(type) {
      const $inputContainer = this.$el.find('div.input-container');
      if (~['anyOf', 'noneOf'].indexOf(type)) {
        $inputContainer.removeClass('hidden');
      } else {
        $inputContainer.addClass('hidden');
      }
    }
    afterRender() {
      super.afterRender();
      if (this.isSearchMode()) {
        this.$element = this.$el.find('.main-element');
        const type = this.$el.find('select.search-type').val();
        this.handleSearchType(type);
        const valueList = this.getSearchParamsData().valueList || this.searchParams.value || [];
        this.$element.val(valueList.join(':,:'));
        const items = [];
        (this.params.options || []).forEach(value => {
          let label = this.getLanguage().translateOption(value, this.name, this.scope);
          if (this.translatedOptions) {
            if (value in this.translatedOptions) {
              label = this.translatedOptions[value];
            }
          }
          if (label === '') {
            return;
          }
          items.push({
            value: value,
            text: label
          });
        });

        /** @type {module:ui/multi-select~Options} */
        const multiSelectOptions = {
          items: items,
          delimiter: ':,:',
          matchAnyWord: true
        };
        _multiSelect.default.init(this.$element, multiSelectOptions);
        this.$el.find('.selectize-dropdown-content').addClass('small');
        this.$el.find('select.search-type').on('change', () => this.trigger('change'));
        this.$element.on('change', () => this.trigger('change'));
      }
      if ((this.isEditMode() || this.isSearchMode()) && !this.nativeSelect) {
        _select.default.init(this.$element, {
          matchAnyWord: true
        });
      }
    }
    focusOnInlineEdit() {
      _select.default.focus(this.$element);
    }
    validateRequired() {
      if (this.isRequired()) {
        if (!this.model.get(this.name)) {
          const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
          this.showValidationMessage(msg);
          return true;
        }
      }
    }
    fetch() {
      let value = this.$element.val();
      if (this.fetchEmptyValueAsNull && !value) {
        value = null;
      }
      const data = {};
      data[this.name] = value;
      return data;
    }
    parseItemForSearch(item) {
      return item;
    }
    fetchSearch() {
      const type = this.fetchSearchType();
      let list = this.$element.val().split(':,:');
      if (list.length === 1 && list[0] === '') {
        list = [];
      }
      list.forEach((item, i) => {
        list[i] = this.parseItemForSearch(item);
      });
      if (type === 'anyOf') {
        if (list.length === 0) {
          return {
            type: 'any',
            data: {
              type: 'anyOf',
              valueList: list
            }
          };
        }
        return {
          type: 'in',
          value: list,
          data: {
            type: 'anyOf',
            valueList: list
          }
        };
      }
      if (type === 'noneOf') {
        if (list.length === 0) {
          return {
            type: 'any',
            data: {
              type: 'noneOf',
              valueList: list
            }
          };
        }
        return {
          type: 'or',
          value: [
          // Don't change order.
          {
            type: 'notIn',
            value: list,
            attribute: this.name
          }, {
            type: 'isNull',
            attribute: this.name
          }],
          data: {
            type: 'noneOf',
            valueList: list
          }
        };
      }
      if (type === 'isEmpty') {
        return {
          type: 'or',
          value: [{
            type: 'isNull',
            attribute: this.name
          }, {
            type: 'equals',
            value: '',
            attribute: this.name
          }],
          data: {
            type: 'isEmpty'
          }
        };
      }
      if (type === 'isNotEmpty') {
        const value = [{
          type: 'isNotNull',
          attribute: this.name
        }];
        if (!this.model.getFieldParam(this.name, 'notStorable')) {
          value.push({
            type: 'notEquals',
            value: '',
            attribute: this.name
          });
        }
        return {
          type: 'and',
          value: value,
          data: {
            type: 'isNotEmpty'
          }
        };
      }
      return null;
    }
    getSearchType() {
      return this.getSearchParamsData().type || 'anyOf';
    }
  }
  var _default = _exports.default = EnumFieldView;
});

define("views/fields/colorpicker", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ColorpickerFieldView extends _varchar.default {
    type = 'varchar';
    detailTemplate = 'fields/colorpicker/detail';
    listTemplate = 'fields/colorpicker/detail';
    editTemplate = 'fields/colorpicker/edit';
    setup() {
      super.setup();
      this.params.maxLength = 7;
      this.wait(Espo.loader.requirePromise('lib!bootstrap-colorpicker'));
    }
    afterRender() {
      super.afterRender();
      if (this.isEditMode()) {
        const isModal = !!this.$el.closest('.modal').length;

        // noinspection JSUnresolvedReference
        this.$element.parent().colorpicker({
          format: 'hex',
          container: isModal ? this.$el : false,
          sliders: {
            saturation: {
              maxLeft: 200,
              maxTop: 200
            },
            hue: {
              maxTop: 200
            },
            alpha: {
              maxTop: 200
            }
          }
        });
        if (isModal) {
          this.$el.find('.colorpicker').css('position', 'relative').addClass('pull-right');
        }
        this.$element.on('change', () => {
          if (this.$element.val() === '') {
            this.$el.find('.input-group-addon > i').css('background-color', 'transparent');
          }
        });
      }
    }
  }
  var _default = _exports.default = ColorpickerFieldView;
});

define("views/wysiwyg/modals/edit-table", ["exports", "views/modal", "views/record/edit-for-modal", "model", "views/fields/enum", "views/fields/varchar", "views/fields/colorpicker"], function (_exports, _modal, _editForModal, _model, _enum, _varchar, _colorpicker) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _editForModal = _interopRequireDefault(_editForModal);
  _model = _interopRequireDefault(_model);
  _enum = _interopRequireDefault(_enum);
  _varchar = _interopRequireDefault(_varchar);
  _colorpicker = _interopRequireDefault(_colorpicker);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EditTableModalView extends _modal.default {
    templateContent = `
        <div class="record no-side-margin">{{{record}}}</div>
    `;
    /**
     * @param {{
     *     params: {
     *         align: null|'left'|'center'|'right',
     *         width: null|string,
     *         height: null|string,
     *         borderWidth: null|string,
     *         borderColor: null|string,
     *         cellPadding: null|string,
     *         backgroundColor: null|string,
     *    },
     *    onApply: function({
     *         align: null|'left'|'center'|'right',
     *         width: null|string,
     *         height: null|string,
     *         borderWidth: null|string,
     *         borderColor: null|string,
     *         cellPadding: null|string,
     *         backgroundColor: null|string,
     *    }),
     * }} options
     */
    constructor(options) {
      super(options);
      this.params = options.params;
      this.onApply = options.onApply;
    }
    setup() {
      this.addButton({
        name: 'apply',
        style: 'primary',
        label: 'Apply',
        onClick: () => this.apply()
      });
      this.addButton({
        name: 'cancel',
        label: 'Cancel',
        onClick: () => this.close()
      });
      this.shortcutKeys = {
        'Control+Enter': () => this.apply()
      };
      this.model = new _model.default({
        align: this.params.align,
        width: this.params.width,
        height: this.params.height,
        borderWidth: this.params.borderWidth,
        borderColor: this.params.borderColor,
        cellPadding: this.params.cellPadding,
        backgroundColor: this.params.backgroundColor
      });
      this.recordView = new _editForModal.default({
        model: this.model,
        detailLayout: [{
          rows: [[{
            view: new _varchar.default({
              name: 'width',
              labelText: this.translate('width', 'wysiwygLabels'),
              params: {
                maxLength: 12
              }
            })
          }, {
            view: new _varchar.default({
              name: 'height',
              labelText: this.translate('height', 'wysiwygLabels'),
              params: {
                maxLength: 12
              }
            })
          }], [{
            view: new _varchar.default({
              name: 'borderWidth',
              labelText: this.translate('borderWidth', 'wysiwygLabels'),
              params: {
                maxLength: 12
              }
            })
          }, {
            view: new _colorpicker.default({
              name: 'borderColor',
              labelText: this.translate('borderColor', 'wysiwygLabels')
            })
          }], [{
            view: new _varchar.default({
              name: 'cellPadding',
              labelText: this.translate('cellPadding', 'wysiwygLabels'),
              params: {
                maxLength: 12
              }
            })
          }, {
            view: new _colorpicker.default({
              name: 'backgroundColor',
              labelText: this.translate('backgroundColor', 'wysiwygLabels')
            })
          }], [{
            view: new _enum.default({
              name: 'align',
              labelText: this.translate('align', 'wysiwygLabels'),
              params: {
                options: ['', 'left', 'center', 'right'],
                translation: 'Global.wysiwygOptions.align'
              }
            })
          }, false]]
        }]
      });
      this.assignView('record', this.recordView, '.record');
    }
    apply() {
      if (this.recordView.validate()) {
        return;
      }
      let borderWidth = this.model.attributes.borderWidth;
      let cellPadding = this.model.attributes.cellPadding;
      let width = this.model.attributes.width;
      let height = this.model.attributes.height;
      if (/^\d+$/.test(borderWidth)) {
        borderWidth += 'px';
      }
      if (/^\d+$/.test(cellPadding)) {
        cellPadding += 'px';
      }
      if (/^\d+$/.test(width)) {
        width += 'px';
      }
      if (/^\d+$/.test(height)) {
        height += 'px';
      }
      this.onApply({
        align: this.model.attributes.align,
        width: width,
        height: height,
        borderWidth: borderWidth,
        borderColor: this.model.attributes.borderColor,
        cellPadding: cellPadding,
        backgroundColor: this.model.attributes.backgroundColor
      });
      this.close();
    }
  }
  var _default = _exports.default = EditTableModalView;
});

define("views/wysiwyg/modals/edit-cell", ["exports", "views/modal", "views/record/edit-for-modal", "model", "views/fields/varchar", "views/fields/colorpicker", "views/fields/enum"], function (_exports, _modal, _editForModal, _model, _varchar, _colorpicker, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _editForModal = _interopRequireDefault(_editForModal);
  _model = _interopRequireDefault(_model);
  _varchar = _interopRequireDefault(_varchar);
  _colorpicker = _interopRequireDefault(_colorpicker);
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EditCellModalView extends _modal.default {
    templateContent = `
        <div class="record no-side-margin">{{{record}}}</div>
    `;
    /**
     * @param {{
     *     params: {
     *         width: null|string,
     *         height: null|height,
     *         backgroundColor: null|string,
     *         verticalAlign: null|'top'|'middle'|'bottom',
     *    },
     *    onApply: function({
     *         width: null|string,
     *         height: null|height,
     *         backgroundColor: null|string,
     *         verticalAlign: null|'top'|'middle'|'bottom',
     *    }),
     * }} options
     */
    constructor(options) {
      super(options);
      this.params = options.params;
      this.onApply = options.onApply;
    }
    setup() {
      this.addButton({
        name: 'apply',
        style: 'primary',
        label: 'Apply',
        onClick: () => this.apply()
      });
      this.addButton({
        name: 'cancel',
        label: 'Cancel',
        onClick: () => this.close()
      });
      this.shortcutKeys = {
        'Control+Enter': () => this.apply()
      };
      this.model = new _model.default({
        width: this.params.width,
        height: this.params.height,
        backgroundColor: this.params.backgroundColor,
        verticalAlign: this.params.verticalAlign
      });
      this.recordView = new _editForModal.default({
        model: this.model,
        detailLayout: [{
          rows: [[{
            view: new _varchar.default({
              name: 'width',
              labelText: this.translate('width', 'wysiwygLabels'),
              params: {
                maxLength: 12
              }
            })
          }, {
            view: new _varchar.default({
              name: 'height',
              labelText: this.translate('height', 'wysiwygLabels'),
              params: {
                maxLength: 12
              }
            })
          }], [{
            view: new _enum.default({
              name: 'verticalAlign',
              labelText: this.translate('verticalAlign', 'wysiwygLabels'),
              params: {
                options: ['', 'top', 'middle', 'bottom'],
                translation: 'Global.wysiwygOptions.verticalAlign'
              }
            })
          }, {
            view: new _colorpicker.default({
              name: 'backgroundColor',
              labelText: this.translate('backgroundColor', 'wysiwygLabels')
            })
          }]]
        }]
      });
      this.assignView('record', this.recordView, '.record');
    }
    apply() {
      if (this.recordView.validate()) {
        return;
      }
      let width = this.model.attributes.width;
      if (/^\d+$/.test(width)) {
        width += 'px';
      }
      let height = this.model.attributes.height;
      if (/^\d+$/.test(height)) {
        height += 'px';
      }
      this.onApply({
        width: width,
        height: height,
        backgroundColor: this.model.attributes.backgroundColor,
        verticalAlign: this.model.attributes.verticalAlign
      });
      this.close();
    }
  }
  var _default = _exports.default = EditCellModalView;
});

define("views/record/list/settings", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RecordListSettingsView extends _view.default {
    // language=Handlebars
    templateContent = `
        {{#if toDisplay}}
        <div class="btn-group">
            <a
                role="button"
                class="btn btn-text dropdown-toggle"
                data-toggle="dropdown"
                tabindex="0"
            ><span class="caret"></span></a>
            <ul class="dropdown-menu pull-right">
                {{#if dataList.length}}
                <li class="dropdown-header">{{fieldsLabel}}</li>
                    {{#each dataList}}
                        <li>
                            <a
                                role="button"
                                tabindex="0"
                                data-action="toggleColumn"
                                data-name="{{name}}"
                            ><span class="check-icon fas fa-check pull-right{{#if hidden}} hidden{{/if}}"></span><div>{{label}}</div></a>
                        </li>
                    {{/each}}
                {{/if}}
            {{#if hasColumnResize}}
                <li class="divider"></li>
                <li>
                    <a
                        role="button"
                        tabindex="0"
                        data-action="toggleColumnResize"
                    >
                        <span class="check-icon fas fa-check pull-right {{#unless columnResize}} hidden {{/unless}}"></span>
                        <div>{{translate 'Column Resize'}}</div></a>
                </li>
            {{/if}}
            {{#if isNotDefault}}
                <li class="divider"></li>
                <li>
                    <a
                        role="button"
                        tabindex="0"
                        data-action="resetToDefault"
                    >{{translate 'Reset'}}</a>
                </li>
            {{/if}}
            </ul>
        </div>
        {{/if}}
    `;
    data() {
      const columnResize = this.helper.getColumnResize();
      const dataList = this.getDataList();
      const hasColumnResize = this.columnResize && (columnResize || this.isColumnResizeApplicable());
      const isNotDefault = dataList.find(item => item.hiddenDefault !== item.hidden) !== undefined || Object.keys(this.helper.getColumnWidthMap()).length > 0;
      return {
        dataList: dataList,
        toDisplay: dataList.length > 0 || columnResize,
        isNotDefault: isNotDefault,
        fieldsLabel: this.translate('Fields'),
        hasColumnResize: hasColumnResize,
        columnResize: columnResize
      };
    }

    /**
     * @typedef {Object} RecordListSettingsView~onChangeOptions
     * @property {'resetToDefault'|'toggleColumn'|'toggleColumnResize'} action An action.
     * @property {string} [column] A column.
     */

    /**
     * @param {{
     *     layoutProvider: function(): {
     *         name: string,
     *         width?: number,
     *         widthPx?: number,
     *         label?: string,
     *         customLabel?: string,
     *         noLabel?: boolean,
     *         hidden?: boolean,
     *     }[],
     *     helper: import('helpers/list/settings').default,
     *     entityType: string,
     *     onChange: function(RecordListSettingsView~onChangeOptions),
     *     columnResize?: boolean,
     * }} options
     */
    constructor(options) {
      super();
      this.layoutProvider = options.layoutProvider;
      this.helper = options.helper;
      this.entityType = options.entityType;
      this.onChange = options.onChange;
      this.columnResize = options.columnResize || false;
    }
    setup() {
      this.addActionHandler('toggleColumn', (e, target) => this.toggleColumn(target.dataset.name));
      this.addActionHandler('toggleColumnResize', () => this.toggleColumnResize());
      this.addActionHandler('resetToDefault', () => this.resetToDefault());

      /** @private */
      this.onColumnWidthChangeBind = this.onColumnWidthChange.bind(this);
      this.helper.subscribeToColumnWidthChange(this.onColumnWidthChangeBind);
      if (window.innerWidth < this.getThemeManager().getParam('screenWidthXs')) {
        this.columnResize = false;
      }
    }
    onRemove() {
      this.helper.unsubscribeFromColumnWidthChange(this.onColumnWidthChangeBind);
    }

    /**
     * @private
     */
    onColumnWidthChange() {
      this.reRender();
    }

    /**
     * @private
     * @return {{
     *     hidden: boolean,
     *     hiddenDefault: boolean,
     *     name: string,
     *     label: string,
     * }[]}
     */
    getDataList() {
      const list = this.layoutProvider() || [];
      const map = this.helper.getHiddenColumnMap() || {};
      return list.filter(item => item.name && !item.link && !item.noLabel && !item.customLabel).map(item => {
        const label = item.label || item.name;
        const hidden = item.name in map ? map[item.name] : !!item.hidden;
        return {
          name: item.name,
          label: this.translate(label, 'fields', this.entityType),
          hidden: hidden,
          hiddenDefault: !!item.hidden
        };
      });
    }

    /**
     * @private
     * @return {boolean}
     */
    isColumnResizeApplicable() {
      const list = this.layoutProvider().filter(it => {
        return !this.helper.isColumnHidden(it.name, it.hidden);
      });
      if (!list || list.length <= 1) {
        return false;
      }
      if (!list.find(it => !it.widthPx && !it.width)) {
        return false;
      }
      return !!list.find(it => it.widthPx || it.width);
    }

    /**
     * @private
     * @param {string} name
     */
    toggleColumn(name) {
      const map = /** @type {Object.<string, boolean>} */this.helper.getHiddenColumnMap() || {};
      const item = this.getDataList().find(item => item.name === name);
      const defaultValue = item ? item.hiddenDefault : false;
      map[name] = !(name in map ? map[name] : defaultValue);
      this.helper.storeHiddenColumnMap(map);
      this.onChange({
        action: 'toggleColumn',
        column: name
      });
    }

    /**
     * @private
     */
    toggleColumnResize() {
      const value = !this.helper.getColumnResize();
      this.helper.storeColumnResize(value);
      this.onChange({
        action: 'toggleColumnResize'
      });
    }

    /**
     * @private
     */
    resetToDefault() {
      this.helper.clearHiddenColumnMap();
      this.helper.clearColumnWidthMap();
      this.onChange({
        action: 'resetToDefault'
      });
    }
  }
  var _default = _exports.default = RecordListSettingsView;
});

define("views/modals/text-preview", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class TextPreviewModalView extends _modal.default {
    // language=Handlebars
    templateContent = `
        <div class="panel panel-default no-side-margin">
            <div class="panel-body">
                <div class="complex-text">{{complexText viewObject.options.text linksInNewTab=true}}</div>
            </div>
        </div>
    `;
    backdrop = true;

    /**
     * @param {{text: string}} options
     */
    constructor(options) {
      super(options);
      this.options = options;
    }
    setup() {
      this.headerText = this.translate('Preview');
    }
  }
  _exports.default = TextPreviewModalView;
});

define("helpers/mass-action", ["exports", "di", "models/settings", "models/user"], function (_exports, _di, _settings, _user) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _settings = _interopRequireDefault(_settings);
  _user = _interopRequireDefault(_user);
  let _init_config, _init_extra_config, _init_user, _init_extra_user;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * A mass-action helper.
   */
  class MassActionHelper {
    static #_ = [_init_config, _init_extra_config, _init_user, _init_extra_user] = _applyDecs(this, [], [[(0, _di.inject)(_settings.default), 0, "config"], [(0, _di.inject)(_user.default), 0, "user"]]).e;
    /**
     * @private
     * @type {Settings}
     */
    config = _init_config(this);

    /**
     * @private
     * @type {User}
     */
    user = (_init_extra_config(this), _init_user(this));

    /**
     * @param {module:view} view A view.
     */
    constructor(view) {
      _init_extra_user(this);
      /**
       * @private
       * @type {module:view}
       */
      this.view = view;
    }

    /**
     * Check whether an action should be run in idle.
     *
     * @param {number} [totalCount] A total record count.
     * @returns {boolean}
     */
    checkIsIdle(totalCount) {
      if (this.user.isPortal()) {
        return false;
      }
      if (typeof totalCount === 'undefined') {
        totalCount = this.view.options.totalCount;
      }
      if (typeof totalCount === 'undefined' && this.view.collection) {
        totalCount = this.view.collection.total;
      }
      return totalCount === -1 || totalCount > this.config.get('massActionIdleCountThreshold');
    }

    /**
     * Process.
     *
     * @param {string} id An ID.
     * @param {string} action An action.
     * @returns {Promise<module:view>} Resolves with a dialog view.
     *   The view emits the 'close:success' event.
     */
    process(id, action) {
      Espo.Ui.notify(false);
      return new Promise(resolve => {
        const entityType = this.view.scope || this.view.entityType;
        this.view.createView('dialog', 'views/modals/mass-action', {
          id: id,
          action: action,
          scope: entityType
        }).then(view => {
          view.render();
          resolve(view);
          this.view.listenToOnce(view, 'success', data => {
            resolve(data);
            this.view.listenToOnce(view, 'close', () => {
              view.trigger('close:success', data);
            });
          });
        });
      });
    }
  }
  var _default = _exports.default = MassActionHelper;
});

define("helpers/export", ["exports", "di", "models/settings", "models/user"], function (_exports, _di, _settings, _user) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _settings = _interopRequireDefault(_settings);
  _user = _interopRequireDefault(_user);
  let _init_config, _init_extra_config, _init_user, _init_extra_user;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * An export helper.
   */
  class ExportHelper {
    static #_ = [_init_config, _init_extra_config, _init_user, _init_extra_user] = _applyDecs(this, [], [[(0, _di.inject)(_settings.default), 0, "config"], [(0, _di.inject)(_user.default), 0, "user"]]).e;
    /**
     * @private
     * @type {Settings}
     */
    config = _init_config(this);

    /**
     * @private
     * @type {User}
     */
    user = (_init_extra_config(this), _init_user(this));

    /**
     * @param {module:view} view A view.
     */
    constructor(view) {
      _init_extra_user(this);
      /**
       * @private
       * @type {module:view}
       */
      this.view = view;
    }

    /**
     * Check whether an export should be run in idle.
     *
     * @param {number} totalCount A total record count.
     * @returns {boolean}
     */
    checkIsIdle(totalCount) {
      if (this.user.isPortal()) {
        return false;
      }
      if (typeof totalCount === 'undefined') {
        totalCount = this.view.options.totalCount;
      }
      return totalCount === -1 || totalCount > this.config.get('exportIdleCountThreshold');
    }

    /**
     * Process export.
     *
     * @param {string} id An ID.
     * @returns {Promise<module:view>} Resolves with a dialog view.
     *   The view emits the 'close:success' event.
     */
    process(id) {
      Espo.Ui.notify(false);
      return new Promise(resolve => {
        this.view.createView('dialog', 'views/export/modals/idle', {
          id: id
        }).then(view => {
          view.render();
          resolve(view);
          this.view.listenToOnce(view, 'success', data => {
            resolve(data);
            this.view.listenToOnce(view, 'close', () => {
              view.trigger('close:success', data);
            });
          });
        });
      });
    }
  }
  var _default = _exports.default = ExportHelper;
});

define("helpers/record/list/column-width-control", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ListColumnWidthControlHelper {
    /**
     * A min width in pixels.
     *
     * @private
     * @type {number}
     */
    minWidth = 30;

    /**
     * @param {{
     *     view?: import('views/record/list').default,
     *     helper: import('helpers/list/settings').default,
     *     layoutProvider?: function(): {
     *     name: string,
     *     width?: number,
     *     widthPx?: number,
     *     hidden?: boolean,
     * }[]
     * }} options
     */
    constructor(options) {
      /** @private */
      this.view = options.view;
      /** @private */
      this.helper = options.helper;
      /** @private */
      this.layoutProvider = options.layoutProvider;
    }

    /**
     * Adjust widths.
     *
     * @param {{
     *     tableWidth?: number,
     *     staticWidth?: number,
     * }} [options]
     * @return {boolean}
     */
    adjust() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      let tableWidthData;
      if (options.tableWidth === undefined || options.staticWidth === undefined) {
        tableWidthData = this.getTableWidths();
      }
      const tableWidth = options.tableWidth === undefined ? tableWidthData.table : options.tableWidth;
      const staticWidth = options.staticWidth === undefined ? tableWidthData.static : options.staticWidth;
      const widthMap = this.helper.getColumnWidthMap();

      /**
       * @type {{
       *     name: string,
       *     width: {
       *         value: number,
       *         unit: 'px'|'%',
       *     }|null,
       *     isCustom: boolean,
       *     widthPx: number|null,
       * }[]}
       */
      const list = this.layoutProvider().filter(it => !this.helper.isColumnHidden(it.name, it.hidden)).map(it => {
        let widthItem = widthMap[it.name];
        const isCustom = !!widthItem;
        if (!widthItem) {
          widthItem = null;
          if (it.width) {
            widthItem = {
              value: it.width,
              unit: '%'
            };
          } else if (it.widthPx) {
            widthItem = {
              value: it.widthPx,
              unit: 'px'
            };
          }
        }
        let widthPx = null;
        if (widthItem) {
          if (widthItem.unit === 'px') {
            widthPx = widthItem.value;
          } else {
            widthPx = tableWidth * (widthItem.value / 100.0);
          }
        }
        return {
          name: it.name,
          width: widthItem,
          isCustom: isCustom,
          widthPx: widthPx
        };
      });
      const flexColumnCount = list.filter(it => !it.width).length;
      const extraWidth = flexColumnCount * this.minWidth;
      let sumWidth = 0;
      list.filter(it => it.widthPx).forEach(it => sumWidth += it.widthPx);
      if (tableWidth - extraWidth - staticWidth - sumWidth >= 0) {
        return true;
      }
      const listSorted = list.filter(it => it.widthPx && it.width).sort((a, b) => b.widthPx - a.widthPx);
      if (!listSorted.length) {
        return true;
      }
      const item = listSorted[0];
      const reduceWidthPx = 10;
      if (item.widthPx < reduceWidthPx) {
        return true;
      }

      /** @type {{value: number, unit: 'px'|'%'}} */
      let newWidth;
      if (item.width.unit === 'px') {
        newWidth = {
          value: item.width.value - reduceWidthPx,
          unit: 'px'
        };
      } else {
        const factor = Math.pow(10, 4);
        const reducePercent = Math.floor(factor * (reduceWidthPx / tableWidth) * 100) / factor;
        newWidth = {
          value: item.width.value - reducePercent,
          unit: '%'
        };
      }
      const map = this.helper.getColumnWidthMap();
      map[item.name] = newWidth;
      this.helper.storeColumnWidthMap(map);
      this.adjust({
        tableWidth,
        staticWidth
      });
      return false;
    }

    /**
     * @private
     * @return {{
     *     table: number,
     *     static: number,
     * }|null}
     */
    getTableWidths() {
      const tableElement = this.view.element.querySelector('.list > table');
      if (!tableElement) {
        return null;
      }
      const tableWidth = tableElement.clientWidth;
      let staticWidth = 0;
      tableElement.querySelectorAll(':scope > thead > tr > th').forEach(th => {
        if (!th.classList.contains('field-header-cell') || th.classList.contains('action-cell')) {
          staticWidth += th.clientWidth;
        }
      });
      return {
        table: tableWidth,
        static: staticWidth
      };
    }
  }
  _exports.default = ListColumnWidthControlHelper;
});

define("helpers/record/list/column-resize", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * @internal
   */
  class ListColumnResizeHelper {
    /**
     * @type {{
     *     startX: number,
     *     startWidth: number,
     *     thElement: HTMLTableCellElement,
     *     name: string,
     *     inPx: boolean,
     *     onRight: boolean,
     *     newWidth: number|null,
     *     thElements: HTMLTableCellElement[],
     * }}
     * @private
     */
    item;

    /**
     * A min width in pixels.
     *
     * @private
     * @type {number}
     */
    minWidth = 30;
    static selector = 'table > thead > tr > th > .column-resizer';

    /**
     * @param {import('views/record/list').default} view
     * @param {import('helpers/list/settings').default} helper
     */
    constructor(view, helper) {
      /** @private */
      this.view = view;
      /** @private */
      this.helper = helper;

      /**
       * @private
       * @type {number}
       */
      this.fontSizeFactor = view.getThemeManager().getFontSizeFactor();
      this.onPointerUpBind = this.onPointerUp.bind(this);
      this.onPointerMoveBind = this.onPointerMove.bind(this);
      view.addHandler('pointerdown', ListColumnResizeHelper.selector, (/** PointerEvent */e, target) => {
        this.onPointerDown(e, target);
      });
    }

    /**
     * @private
     * @param {PointerEvent} event
     * @param {HTMLElement} target
     */
    onPointerDown(event, target) {
      if (!event.isPrimary) {
        return;
      }
      this.startResizeInit(event, target);
      window.addEventListener('pointerup', this.onPointerUpBind);
      window.addEventListener('pointermove', this.onPointerMoveBind);
    }

    /**
     * @private
     * @param {PointerEvent} event
     * @param {HTMLElement} target
     */
    startResizeInit(event, target) {
      const th = /** @type {HTMLTableCellElement} */target.parentNode;
      const thElements = [...th.parentNode.querySelectorAll(':scope > th.field-header-cell')].filter(it => !it.style.width);
      this.item = {
        startX: event.clientX,
        startWidth: th.clientWidth,
        thElement: th,
        name: th.dataset.name,
        inPx: th.style.width && th.style.width.endsWith('px'),
        onRight: target.classList.contains('column-resizer-right'),
        newWidth: null,
        thElements: thElements
      };
      document.body.style.cursor = 'col-resize';
      const trElement = this.item.thElement.closest('tr');
      trElement.classList.add('being-column-resized');
      this.item.thElement.classList.add('being-resized');
    }

    /**
     * @private
     * @param {number} width
     */
    isWidthOk(width) {
      if (width < this.minWidth * this.fontSizeFactor) {
        return false;
      }
      for (const th of this.item.thElements) {
        if (th.style.width) {
          continue;
        }
        if (th.clientWidth < this.minWidth * this.fontSizeFactor) {
          return false;
        }
      }
      return true;
    }

    /**
     * @private
     * @param {PointerEvent} event
     */
    onPointerMove(event) {
      let diff = event.clientX - this.item.startX;
      if (!this.item.onRight) {
        diff *= -1;
      }
      const width = this.item.startWidth + diff;
      if (!this.isWidthOk(width)) {
        return;
      }
      const previousWidth = this.item.newWidth;
      const previousStyleWidth = this.item.thElement.style.width;
      this.item.newWidth = width;
      this.item.thElement.style.width = width.toString() + 'px';
      if (!this.isWidthOk(width)) {
        if (previousWidth) {
          this.item.newWidth = previousWidth;
        }
        this.item.thElement.style.width = previousStyleWidth;
      }
    }

    /**
     * @private
     */
    onPointerUp() {
      window.removeEventListener('pointermove', this.onPointerMoveBind);
      window.removeEventListener('pointerup', this.onPointerUpBind);
      document.body.style.cursor = '';
      const width = this.item.newWidth;
      if (width === null) {
        this.disableResizingState();
        return;
      }
      let unit = 'px';
      let value = width;
      if (!this.item.inPx) {
        const tableElement = this.item.thElement.closest('table');
        const tableWidth = tableElement.clientWidth;
        const factor = Math.pow(10, 4);
        const widthPercents = width / tableWidth;
        const widthPercentsRounded = Math.floor(factor * widthPercents * 100) / factor;
        this.item.thElement.style.width = widthPercentsRounded.toString() + '%';
        unit = '%';
        value = widthPercentsRounded;
      }
      if (this.item.inPx) {
        value = value / this.fontSizeFactor;
      }
      this.helper.storeColumnWidth(this.item.name, {
        value: value,
        unit: unit
      });
      this.disableResizingState();
    }

    /**
     * @private
     */
    disableResizingState() {
      const trElement = this.item.thElement.closest('tr');
      trElement.classList.remove('being-column-resized');
      this.item.thElement.classList.remove('being-resized');
      this.item = undefined;
    }
  }
  _exports.default = ListColumnResizeHelper;
});

define("helpers/misc/mailto", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class MailtoHelper {
    /**
     * @param {import('models/settings').default} config
     * @param {import('models/preferences').default} preferences
     * @param {import('acl-manager').default} acl
     */
    constructor(config, preferences, acl) {
      this.config = config;
      this.preferences = preferences;
      this.acl = acl;
    }

    /**
     * Whether mailto should be used.
     *
     * @return {boolean}
     */
    toUse() {
      return this.config.get('emailForceUseExternalClient') || this.preferences.get('emailUseExternalClient') || !this.acl.checkScope('Email', 'create');
    }

    /**
     * Compose a mailto link.
     *
     * @param {Record} attributes
     * @return {string}
     */
    composeLink(attributes) {
      let link = 'mailto:';
      link += (attributes.to || '').split(';').join(',');
      const params = {};
      if (attributes.cc) {
        params.cc = attributes.cc.split(';').join(',');
      }
      let bcc = this.config.get('outboundEmailBccAddress');
      if (attributes.bcc) {
        if (!bcc) {
          bcc = '';
        } else {
          bcc += ';';
        }
        bcc += attributes.bcc;
      }
      if (bcc) {
        params.bcc = bcc.split(';').join(',');
      }
      if (attributes.name) {
        params.subject = attributes.name;
      }
      if (attributes.body) {
        params.body = /** @type {string} */attributes.body;
        if (attributes.isHtml) {
          params.body = this.htmlToPlain(params.body);
        }
        if (params.body.length > 700) {
          params.body = params.body.substring(0, 700) + '...';
        }
      }
      if (attributes.inReplyTo) {
        params['In-Reply-To'] = attributes.inReplyTo;
      }
      let part = '';
      for (const key in params) {
        if (part !== '') {
          part += '&';
        } else {
          part += '?';
        }
        part += key + '=' + encodeURIComponent(params[key]);
      }
      link += part;
      return link;
    }

    /**
     * @private
     * @param {string} text
     * @returns {string}
     */
    htmlToPlain(text) {
      text = text || '';
      let value = text.replace(/<br\s*\/?>/mg, '\n');
      value = value.replace(/<\/p\s*\/?>/mg, '\n\n');
      const $div = $('<div>').html(value);
      $div.find('style').remove();
      $div.find('link[ref="stylesheet"]').remove();
      value = $div.text();
      return value;
    }
  }
  var _default = _exports.default = MailtoHelper;
});

define("helpers/list/settings", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ListSettingsHelper {
    /**
     * @typedef {Object} ListSettingsHelper~columnWidth
     * @property {number} value A value.
     * @property {'px'|'%'} unit A unit.
     */

    /**
     * @param {string} entityType
     * @param {string} type
     * @param {string} userId
     * @param {module:storage} storage
     */
    constructor(entityType, type, userId, storage) {
      /** @private */
      this.storage = storage;

      /** @private */
      this.layoutColumnsKey = `${type}-${entityType}-${userId}`;

      /**
       * @private
       * @type {Object.<string, boolean>}
       */
      this.hiddenColumnMapCache = undefined;

      /**
       * @private
       * @type {Object.<string, ListSettingsHelper~columnWidth>}
       */
      this.columnWidthMapCache = undefined;

      /**
       * @private
       * @type {boolean|undefined}
       */
      this.columnResize = undefined;

      /**
       * @private
       * @type {function()[]}
       */
      this.columnWidthChangeFunctions = [];
    }

    /**
     * Get a stored hidden column map.
     *
     * @return {Object.<string, boolean>}
     */
    getHiddenColumnMap() {
      if (this.hiddenColumnMapCache) {
        return this.hiddenColumnMapCache;
      }
      this.hiddenColumnMapCache = this.storage.get('listHiddenColumns', this.layoutColumnsKey) || {};
      return this.hiddenColumnMapCache;
    }

    /**
     * Is a column hidden.
     *
     * @param {string} name A name.
     * @param {boolean} [hidden] Is hidden by default.
     * @return {boolean}
     * @since 9.0.0
     */
    isColumnHidden(name, hidden) {
      const hiddenMap = this.getHiddenColumnMap();
      if (hiddenMap[name]) {
        return true;
      }
      if (!hidden) {
        return false;
      }
      if (!(name in hiddenMap)) {
        return true;
      }
      return hiddenMap[name];
    }

    /**
     * Is column resize enabled.
     *
     * @return {boolean}
     * @since 9.0.0
     */
    getColumnResize() {
      if (this.columnResize === undefined) {
        this.columnResize = this.storage.get('listColumnResize', this.layoutColumnsKey) || false;
      }
      return this.columnResize;
    }

    /**
     * Store column width editable.
     *
     * @param {boolean} columnResize
     */
    storeColumnResize(columnResize) {
      this.columnResize = columnResize;
      this.storage.set('listColumnResize', this.layoutColumnsKey, columnResize);
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Clear column width editable.
     */
    clearColumnResize() {
      this.columnResize = undefined;
      this.storage.clear('listColumnResize', this.layoutColumnsKey);
    }

    /**
     * Store a hidden column map.
     *
     * @param {Object.<string, boolean>} map
     */
    storeHiddenColumnMap(map) {
      this.hiddenColumnMapCache = undefined;
      this.storage.set('listHiddenColumns', this.layoutColumnsKey, map);
    }

    /**
     * Clear a hidden column map in the storage.
     */
    clearHiddenColumnMap() {
      this.hiddenColumnMapCache = undefined;
      this.storage.clear('listHiddenColumns', this.layoutColumnsKey);
    }

    /**
     * Get a stored column width map.
     *
     * @return {Object.<string, ListSettingsHelper~columnWidth>}
     */
    getColumnWidthMap() {
      if (this.columnWidthMapCache) {
        return this.columnWidthMapCache;
      }
      this.columnWidthMapCache = this.storage.get('listColumnsWidths', this.layoutColumnsKey) || {};
      return this.columnWidthMapCache;
    }

    /**
     * Store a column width map.
     *
     * @param {Object.<string, ListSettingsHelper~columnWidth>} map
     */
    storeColumnWidthMap(map) {
      this.columnWidthMapCache = undefined;
      this.storage.set('listColumnsWidths', this.layoutColumnsKey, map);
    }

    /**
     * Clear a column width map in the storage.
     */
    clearColumnWidthMap() {
      this.columnWidthMapCache = undefined;
      this.storage.clear('listColumnsWidths', this.layoutColumnsKey);
    }

    /**
     * Set a column width.
     *
     * @param {string} name A column name.
     * @param {ListSettingsHelper~columnWidth} width Width data.
     */
    storeColumnWidth(name, width) {
      if (!this.columnWidthMapCache) {
        this.columnWidthMapCache = {};
      }
      this.columnWidthMapCache[name] = width;
      this.storeColumnWidthMap(this.columnWidthMapCache);
      for (const handler of this.columnWidthChangeFunctions) {
        handler();
      }
    }

    /**
     * Subscribe to a column width change.
     *
     * @param {function()} handler A handler.
     */
    subscribeToColumnWidthChange(handler) {
      this.columnWidthChangeFunctions.push(handler);
    }

    /**
     * Unsubscribe from a column width change.
     *
     * @param {function()} handler A handler.
     */
    unsubscribeFromColumnWidthChange(handler) {
      const index = this.columnWidthChangeFunctions.findIndex(it => handler === it);
      if (!~index) {
        return;
      }
      this.columnWidthChangeFunctions.splice(index, 1);
    }
  }
  var _default = _exports.default = ListSettingsHelper;
});

define("helpers/list/select-provider", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class SelectProvider {
    /**
     * @param {module:layout-manager} layoutManager
     * @param {module:metadata} metadata
     * @param {module:field-manager} fieldManager
     */
    constructor(layoutManager, metadata, fieldManager) {
      this.layoutManager = layoutManager;
      this.metadata = metadata;
      this.fieldManager = fieldManager;
    }

    /**
     * Get select attributes.
     *
     * @param {string} entityType
     * @param {string} [layoutName='list']
     * @return {Promise<string[]>}
     */
    get(entityType, layoutName) {
      return new Promise(resolve => {
        this.layoutManager.get(entityType, layoutName || 'list', layout => {
          const list = this.getFromLayout(entityType, layout);
          resolve(list);
        });
      });
    }

    /**
     * Get select attributes from a layout.
     *
     * @param {string} entityType
     * @param {module:views/record/list~columnDefs[]} listLayout
     * @return {string[]}
     */
    getFromLayout(entityType, listLayout) {
      let list = [];
      listLayout.forEach(item => {
        if (!item.name) {
          return;
        }
        const field = item.name;
        const fieldType = this.metadata.get(['entityDefs', entityType, 'fields', field, 'type']);
        if (!fieldType) {
          return;
        }
        list = [this.fieldManager.getEntityTypeFieldAttributeList(entityType, field), ...list];
      });
      return list;
    }
  }
  var _default = _exports.default = SelectProvider;
});

define("helpers/list/misc/sticky-bar", ["exports", "jquery", "bullbone"], function (_exports, _jquery, _bullbone) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _jquery = _interopRequireDefault(_jquery);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * @internal
   *
   * @mixes Bull.Events
   */
  class StickyBarHelper {
    /** @private */
    $bar;
    /** @private */
    $scrollable;
    /** @private */
    $window;
    /** @private */
    $navbarRight;
    /** @private */
    $middle;
    /** @private */
    _isReady = false;

    /**
     * @param {import('views/record/list').default} view
     * @param {{force?: boolean}} options
     */
    constructor(view) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      this.view = view;

      /**
       * @private
       * @type {import('theme-manager').default}
       */

      this.themeManager = this.view.getThemeManager();
      this.$el = view.$el;

      /** @private */
      this.force = options.force || false;
      this.init();
    }
    init() {
      this.$bar = this.$el.find('.sticked-bar');
      this.$middle = this.$el.find('> .list');
      if (!this.$middle.get(0)) {
        return;
      }
      this.$window = (0, _jquery.default)(window);
      this.$scrollable = this.$window;
      this.$navbarRight = (0, _jquery.default)('#navbar .navbar-right');
      this.isModal = !!this.$el.closest('.modal-body').length;
      this.isSmallWindow = (0, _jquery.default)(window.document).width() < this.themeManager.getParam('screenWidthXs');
      if (this.isModal) {
        this.$scrollable = this.$el.closest('.modal-body');
        this.$navbarRight = this.$scrollable.parent().find('.modal-footer');
      }
      if (!this.force) {
        this.$scrollable.off(`scroll.list-${this.view.cid}`);
        this.$scrollable.on(`scroll.list-${this.view.cid}`, () => this._controlSticking());
        this.$window.off(`resize.list-${this.view.cid}`);
        this.$window.on(`resize.list-${this.view.cid}`, () => this._controlSticking());
      }
      this.listenTo(this.view, 'check', () => {
        if (this.view.getCheckedIds().length === 0 && !this.view.isAllResultChecked()) {
          return;
        }
        this._controlSticking();
      });
      this._isReady = true;
    }
    _getMiddleTop() {
      if (this._middleTop !== undefined && this._middleTop >= 0) {
        return this._middleTop;
      }
      this._middleTop = this._getOffsetTop(this.$middle.get(0));
      return this._middleTop;
    }
    _getButtonsTop() {
      if (this._buttonsTop !== undefined && this._buttonsTop >= 0) {
        return this._buttonsTop;
      }
      this._buttonsTop = this._getOffsetTop(this.$el.find('.list-buttons-container').get(0));
      return this._buttonsTop;
    }

    /**
     * @private
     */
    _controlSticking() {
      if (!this.view.toShowStickyBar()) {
        return;
      }
      if (this.isSmallWindow && (0, _jquery.default)('#navbar .navbar-body').hasClass('in')) {
        return;
      }
      const scrollTop = this.$scrollable.scrollTop();
      const stickTop = !this.force ? this._getButtonsTop() : 0;
      const edge = this._getMiddleTop() + this.$middle.outerHeight(true);
      const hide = () => {
        this.$bar.addClass('hidden');
        this.$navbarRight.removeClass('has-sticked-bar');
      };
      const show = () => {
        this.$bar.removeClass('hidden');
        this.$navbarRight.addClass('has-sticked-bar');
      };
      if (scrollTop >= edge) {
        hide();
        return;
      }
      if (scrollTop > stickTop || this.force) {
        show();
        return;
      }
      hide();
    }

    /**
     * @private
     * @param {HTMLElement} element
     */
    _getOffsetTop(element) {
      if (!element) {
        return 0;
      }
      const navbarHeight = this.themeManager.getParam('navbarHeight') * this.themeManager.getFontSizeFactor();
      const withHeader = !this.isSmallWindow && !this.isModal;
      let offsetTop = 0;
      do {
        if (element.classList.contains('modal-body')) {
          break;
        }
        if (!isNaN(element.offsetTop)) {
          offsetTop += element.offsetTop;
        }
        element = element.offsetParent;
      } while (element);
      if (withHeader) {
        offsetTop -= navbarHeight;
      }
      if (!this.isModal) {
        // padding
        offsetTop -= 5;
      }
      return offsetTop;
    }
    hide() {
      this.$bar.addClass('hidden');
    }
    destroy() {
      this.stopListening(this.view, 'check');
      if (!this._isReady) {
        return;
      }
      this.$window.off(`resize.list-${this.view.cid}`);
      this.$scrollable.off(`scroll.list-${this.view.cid}`);
    }
  }
  Object.assign(StickyBarHelper.prototype, _bullbone.Events);
  var _default = _exports.default = StickyBarHelper;
});

define("model-factory", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 model-factory */

  /**
   * A model factory.
   */
  class ModelFactory {
    /**
     * @param {module:metadata} metadata
     */
    constructor(metadata) {
      this.metadata = metadata;
    }

    /**
     * Used by default value expressions.
     * @public
     * @type {module:date-time|null}
     * @internal
     */
    dateTime = null;

    /**
     * Create a model.
     *
     * @param {string} entityType An entity type.
     * @param {Function} [callback] Deprecated.
     * @param {Object} [context] Deprecated.
     * @returns {Promise<module:model>}
     */
    create(entityType, callback, context) {
      return new Promise(resolve => {
        context = context || this;
        this.getSeed(entityType, Seed => {
          const model = new Seed({}, {
            entityType: entityType,
            defs: this.metadata.get(['entityDefs', entityType]) || {},
            dateTime: this.dateTime
          });
          if (callback) {
            callback.call(context, model);
          }
          resolve(model);
        });
      });
    }

    /**
     * Get a class.
     *
     * @param {string} entityType An entity type.
     * @param {function(module:model): void} callback A callback.
     * @public
     */
    getSeed(entityType, callback) {
      const className = this.metadata.get(['clientDefs', entityType, 'model']) || 'model';
      Espo.loader.require(className, modelClass => callback(modelClass));
    }
  }
  var _default = _exports.default = ModelFactory;
});

define("exceptions", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 exceptions */

  Espo.Exceptions = Espo.Exceptions || {};

  /**
   * An access-denied exception.
   *
   * @param {string} [message] A message.
   * @class
   */
  Espo.Exceptions.AccessDenied = function (message) {
    this.message = message;
    Error.apply(this, arguments);
  };
  Espo.Exceptions.AccessDenied.prototype = new Error();
  Espo.Exceptions.AccessDenied.prototype.name = 'AccessDenied';

  /**
   * A not-found exception.
   *
   * @param {string} [message] A message.
   * @class
   */
  Espo.Exceptions.NotFound = function (message) {
    this.message = message;
    Error.apply(this, arguments);
  };
  Espo.Exceptions.NotFound.prototype = new Error();
  Espo.Exceptions.NotFound.prototype.name = 'NotFound';
  var _default = _exports.default = Espo.Exceptions;
});

define("views/record/list", ["exports", "view", "helpers/mass-action", "helpers/export", "helpers/record-modal", "helpers/list/select-provider", "views/record/list/settings", "helpers/list/settings", "helpers/list/misc/sticky-bar", "helpers/record/list/column-resize", "helpers/record/list/column-width-control"], function (_exports, _view, _massAction, _export, _recordModal, _selectProvider, _settings, _settings2, _stickyBar, _columnResize, _columnWidthControl) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _massAction = _interopRequireDefault(_massAction);
  _export = _interopRequireDefault(_export);
  _recordModal = _interopRequireDefault(_recordModal);
  _selectProvider = _interopRequireDefault(_selectProvider);
  _settings = _interopRequireDefault(_settings);
  _settings2 = _interopRequireDefault(_settings2);
  _stickyBar = _interopRequireDefault(_stickyBar);
  _columnResize = _interopRequireDefault(_columnResize);
  _columnWidthControl = _interopRequireDefault(_columnWidthControl);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/list */

  /**
   * A record-list view. Renders and processes list items, actions.
   */
  class ListRecordView extends _view.default {
    /**
     * A row action.
     *
     * @typedef {Object} module:views/record/list~rowAction
     *
     * @property {string} action An action.
     * @property {string} [label] A label.
     * @property {string} [link] A link.
     * @property {string} [text] A text.
     * @property {Object.<string, string|number|boolean>} [data] Data attributes.
     * @property {number} [groupIndex] A group index.
     */

    /**
     * List view options.
     *
     * @typedef {Record} module:views/record/list~options
     * @property {import('collection').default} collection A collection.
     * @property {module:views/record/list~columnDefs[]} [listLayout] A layout.
     * @property {string|'list'|'listSmall'} [type] A type.
     * @property {string} [layoutName] A layout name.
     * @property {boolean} [checkboxes] To show row checkboxes.
     * @property {boolean} [selectable] Clicking on the record link will trigger the 'select' event.
     * @property {boolean} [skipBuildRows] Do not build rows on initialization. Use when the collection will be fetched
     *    afterward.
     * @property {boolean} [buttonsDisabled] Disable buttons.
     * @property {boolean} [checkAllResultDisabled] Disable select-all-results.
     * @property {boolean} [pagination] To enable the pagination.
     * @property {boolean} [headerDisabled] Disable the header.
     * @property {boolean} [noDataDisabled] Disable the no-data label (when no results).
     * @property {string|null} [rowActionsView] A row actions view.
     * @property {boolean} [rowActionsDisabled] Disable row actions.
     * @property {boolean} [showMore] The show-more button.
     * @property {boolean} [keepCurrentRootUrl] Keep a current root URL.
     * @property {boolean} [stickyBarDisabled] Disable the sticky bar.
     * @property {boolean} [forceStickyBar] To make bar sticky regardless of scrolling.
     * @property {boolean} [massActionsDisabled] Disable mass actions.
     * @property {module:views/record/list~dropdownItem[]} [dropdownItemList] Dropdown items.
     * @property {string[]} [mandatorySelectAttributeList] Mandatory select attributes. Attributes to be selected
     *     regardless being in the layout.
     * @property {boolean} [editDisabled] Disable edit.
     * @property {boolean} [removeDisabled] Disable remove.
     * @property {boolean} [showCount] To show a record count.
     * @property {boolean} [forceDisplayTopBar] Force displaying the top bar even if empty.
     * @property {boolean} [unlinkMassAction] Enable the 'unlink' mass-action.
     * @property {Record} [rowActionsOptions] Row-actions options.
     * @property {string[]} [additionalRowActionList] Additional row-action list.
     * @property {boolean} [settingsEnabled] Enable settings dropdown.
     * @property {import('helpers/list/settings').default} [settingsHelper] A settings helper.
     * @property {boolean} [displayTotalCount] Display total count.
     * @property {Record} [rootData] Root data.
     * @property {boolean} [columnResize] Column resize. Actual only if the settings is enabled.
     * @property {function(import('model').default[])} [onSelect] An on-select callback. Actual if selectable.
     *     As of v9.1.0.
     */

    /**
     * @param {module:views/record/list~options | Record} options Options.
     */
    constructor(options) {
      super(options);
    }

    /** @inheritDoc */
    template = 'record/list';

    /**
     * A type. Can be 'list', 'listSmall'.
     */
    type = 'list';

    /** @inheritDoc */
    name = 'list';

    // noinspection JSUnusedGlobalSymbols
    /**
     * A presentation type.
     */
    presentationType = 'table';

    /**
     * If true checkboxes will be shown. Can be overridden by an option parameter.
     *
     * @protected
     */
    checkboxes = true;

    /**
     * If true clicking on the record link will trigger the 'select' event with model passed.
     * Can be overridden by an option parameter.
     */
    selectable = false;

    /**
     * A row-actions view. A dropdown on the right side.
     *
     * @protected
     * @type {string}
     */
    rowActionsView = 'views/record/row-actions/default';

    /**
     * Disable row-actions. Can be overridden by an option parameter.
     */
    rowActionsDisabled = false;

    /**
     * An entity type. Set automatically.
     *
     * @type {string|null}
     */
    entityType = null;

    /**
     * A scope. Set automatically.
     *
     * @type {?string}
     */
    scope = null;

    /** @protected */
    _internalLayoutType = 'list-row';

    /**
     * A selector to a list container.
     *
     * @protected
     */
    listContainerEl = '.list > table > tbody';

    /**
     * To show number of records. Can be overridden by an option parameter.
     *
     * @protected
     */
    showCount = true;

    /** @protected */
    rowActionsColumnWidth = 25;

    /** @protected */
    checkboxColumnWidth = 40;

    /**
     * A button. Handled by a class method `action{Name}` or a handler.
     *
     * @typedef {Object} module:views/record/list~button
     *
     * @property {string} name A name.
     * @property {string} label A label. To be translated in a current scope.
     * @property {'default'|'danger'|'warning'|'success'} [style] A style.
     * @property {boolean} [hidden] Hidden.
     * @property {function()} [onClick] A click handler.
     */

    /**
     * A button list.
     *
     * @protected
     * @type {module:views/record/list~button[]}
     */
    buttonList = [];

    /**
     * A dropdown item. Handled by a class method `action{Name}` or a handler.
     *
     * @typedef {Object} module:views/record/list~dropdownItem
     *
     * @property {string} name A name.
     * @property {string} [label] A label. To be translated in a current scope.
     * @property {string} [html] An HTML.
     * @property {boolean} [hidden] Hidden.
     * @property {function()} [onClick] A click handler.
     */

    /**
     * A dropdown item list. Can be overridden by an option parameter.
     *
     * @protected
     * @type {module:views/record/list~dropdownItem[]}
     */
    dropdownItemList = [];

    /**
     * Disable the header. Can be overridden by an option parameter.
     *
     * @protected
     */
    headerDisabled = false;

    /**
     * Disable mass actions. Can be overridden by an option parameter.
     *
     * @protected
     */
    massActionsDisabled = false;

    /**
     * Disable a portal layout usage. Can be overridden by an option parameter.
     *
     * @protected
     */
    portalLayoutDisabled = false;

    /**
     * Mandatory select attributes. Can be overridden by an option parameter.
     * Attributes to be selected regardless being in the layout.
     *
     * @protected
     * @type {string[]|null}
     */
    mandatorySelectAttributeList = null;

    /**
     * A layout name. If null, a value from `type` property will be used.
     * Can be overridden by an option parameter.
     *
     * @protected
     * @type {string|null}
     */
    layoutName = null;

    /**
     * A scope name for layout loading. If null, an entity type of collection will be used.
     * Can be overridden by an option parameter.
     *
     * @protected
     * @type {string|null}
     */
    layoutScope = null;

    /**
     * To disable field-level access check for a layout.
     * Can be overridden by an option parameter.
     *
     * @protected
     */
    layoutAclDisabled = false;

    /**
     * A setup-handler type.
     *
     * @protected
     */
    setupHandlerType = 'record/list';

    /**
     * @internal
     * @private
     */
    checkboxesDisabled = false;

    /**
     * Force displaying the top bar even if empty. Can be overridden by an option parameter.
     * @protected
     */
    forceDisplayTopBar = false;

    /**
     * Where to display the pagination. Can be overridden by an option parameter.
     *
     * @protected
     * @type {boolean}
     */
    pagination = false;

    /**
     * To display a table header with column names. Can be overridden by an option parameter.
     *
     * @protected
     * @type {boolean}
     */
    header = true;

    /**
     * A show-more button. Can be overridden by an option parameter.
     *
     * @protected
     */
    showMore = true;

    /**
     * Column resize.
     *
     * @protected
     * @type {boolean}
     * @since 9.0.0
     */
    columnResize = true;

    /**
     * A mass-action list.
     *
     * @protected
     * @type {string[]}
     */
    massActionList = ['remove', 'merge', 'massUpdate', 'export'];

    /**
     * A mass-action list available when selecting all results.
     *
     * @protected
     * @type {string[]}
     */
    checkAllResultMassActionList = ['remove', 'massUpdate', 'export'];

    /**
     * A forced mass-action list.
     *
     * @protected
     * @type {?string[]}
     */
    forcedCheckAllResultMassActionList = null;

    /**
     * Disable quick-detail (viewing a record in modal)
     *
     * @protected
     */
    quickDetailDisabled = false;

    /**
     * Disable quick-edit (editing a record in modal)
     *
     * @protected
     */
    quickEditDisabled = false;

    /**
     * Force settings.
     *
     * @protected
     * @type {boolean}
     */
    forceSettings = false;

    /**
     * Disable settings.
     *
     * @protected
     * @type {boolean}
     */
    settingsDisabled = false;

    /**
     * Column definitions.
     *
     * @typedef module:views/record/list~columnDefs
     * @type {Object}
     * @property {string} name A name (usually a field name).
     * @property {string} [view] An overridden field view name.
     * @property {number} [width] A width in percents.
     * @property {number} [widthPx] A width in pixels.
     * @property {boolean} [link] To use `listLink` mode (link to the detail view).
     * @property {'left'|'right'} [align] An alignment.
     * @property {string} [type] An overridden field type.
     * @property {Object.<string, *>} [params] Overridden field parameters.
     * @property {Object.<string, *>} [options] Field view options.
     * @property {string} [label] A label.
     * @property {boolean} [notSortable] Not sortable.
     * @property {boolean} [hidden] Hidden by default.
     * @property {boolean} [noLabel] No label.
     * @property {string} [customLabel] A custom label.
     */

    /**
     * A list layout. Can be overridden by an option parameter.
     * If null, then will be loaded from the backend (using the `layoutName` value).
     *
     * @protected
     * @type {module:views/record/list~columnDefs[]|null}
     */
    listLayout = null;

    /** @private */
    _internalLayout = null;

    /**
     * A list of record IDs currently selected. Only for reading.
     *
     * @protected
     * @type {string[]|null}
     */
    checkedList = null;

    /**
     * Whether all results currently selected. Only for reading.
     *
     * @protected
     */
    allResultIsChecked = false;

    /**
     * Disable the ability to select all results. Can be overridden by an option parameter.
     *
     * @protected
     */
    checkAllResultDisabled = false;

    /**
     * Disable buttons. Can be overridden by an option parameter.
     *
     * @protected
     */
    buttonsDisabled = false;

    /**
     * Disable edit. Can be overridden by an option parameter.
     *
     * @protected
     */
    editDisabled = false;

    /**
     * Disable remove. Can be overridden by an option parameter.
     *
     * @protected
     */
    removeDisabled = false;

    /**
     * Disable a stick-bar. Can be overridden by an option parameter.
     *
     * @protected
     */
    stickyBarDisabled = false;

    /**
     * To show sticky bar regardless of scrolling.
     *
     * @protected
     */
    forceStickyBar = false;

    /**
     * Disable the follow/unfollow mass action.
     *
     * @protected
     */
    massFollowDisabled = false;

    /**
     * Disable the print-pdf mass action.
     *
     * @protected
     */
    massPrintPdfDisabled = false;

    /**
     * Disable the convert-currency mass action.
     *
     * @protected
     */
    massConvertCurrencyDisabled = false;

    /**
     * Disable mass-update.
     *
     * @protected
     */
    massUpdateDisabled = false;

    /**
     * Disable export.
     *
     * @protected
     */
    exportDisabled = false;

    /**
     * Disable merge.
     *
     * @protected
     */
    mergeDisabled = false;

    /**
     * Disable the no-data label (when no result).
     *
     * @protected
     */
    noDataDisabled = false;

    /**
     * Disable pagination.
     *
     * @protected
     */
    paginationDisabled = false;

    /** @private */
    _$focusedCheckbox = null;

    /**
     * @protected
     * @type {?JQuery}
     */
    $selectAllCheckbox = null;

    /** @private */
    _disabledCheckboxes = false;

    /**
     * Mass-action definitions.
     *
     * @typedef {Object} module:views/record/list~massActionItem
     * @property {string} [name] A name.
     * @property {number} [groupIndex] A group index.
     * @property {string} [handler] A handler.
     * @property {string} [initFunction] An init function.
     * @property {string} [actionFunction] An action function.
     * @property {string} [configCheck] A config check.
     * @property {string} [aclScope] An ACL scope to check.
     * @property {string} [acl] An access action to check.
     * @property {string} [url]
     * @property {boolean} [bypassConfirmation] To skip confirmation.
     * @property {string} [confirmationMessage] A confirmation message.
     * @property {string} [waitMessage] A wait message.
     * @property {string} [successMessage] A success message.
     * @property {boolean} [hidden] Is hidden.
     */

    /**
     * @private
     * @type {Object.<string, module:views/record/list~massActionItem>}
     */
    massActionDefs;

    /**
     * Data to pass to record views.
     *
     * @protected
     * @type {Object.<string, *>}
     * @since 9.0.0
     */
    rootData;

    /** @private */
    _additionalRowActionList;

    /**
     * @private
     * @type {import('helpers/record/list/column-resize').default}
     */
    _columnResizeHelper;

    /**
     * @protected
     * @type {string[]}
     * @since 9.1.1
     */
    collectionEventSyncList;

    /** @inheritDoc */
    events = {
      /**
       * @param {JQueryMouseEventObject} e
       * @this ListRecordView
       */
      'auxclick a.link': function (e) {
        const isCombination = e.button === 1 && (e.ctrlKey || e.metaKey);
        if (!isCombination) {
          return;
        }
        const $target = $(e.currentTarget);
        const id = $target.attr('data-id');
        if (!id) {
          return;
        }
        if (this.quickDetailDisabled) {
          return;
        }
        const $menu = $target.parent().closest(`[data-id="${id}"]`).find(`ul.list-row-dropdown-menu[data-id="${id}"]`);
        const $quickView = $menu.find(`a[data-action="quickView"]`);
        if ($menu.length && !$quickView.length) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        this.actionQuickView({
          id: id
        });
      },
      /** @this ListRecordView */
      'click [data-action="showMore"]': async function () {
        await this.showMoreRecords();
        this.focusOnList();
      },
      'mousedown a.sort': function (e) {
        e.preventDefault();
      },
      /**
       * @param {JQueryKeyEventObject} e
       * @this module:views/record/list
       */
      'click a.sort': function (e) {
        const field = $(e.currentTarget).data('name');
        this.toggleSort(field);
      },
      /**
       * @param {JQueryKeyEventObject} e
       * @this ListRecordView
       */
      'click .pagination a[data-page]': function (e) {
        const page = $(e.currentTarget).data('page');
        if ($(e.currentTarget).parent().hasClass('disabled')) {
          return;
        }
        this.goToPage(page);
      },
      /** @this ListRecordView */
      'mousedown input.record-checkbox': function () {
        const $focused = $(document.activeElement);
        this._$focusedCheckbox = null;
        if ($focused.length && $focused.get(0).tagName === 'INPUT' && $focused.hasClass('record-checkbox')) {
          this._$focusedCheckbox = $focused;
        }
      },
      /**
       * @param {JQueryKeyEventObject} e
       * @this ListRecordView
       */
      'click input.record-checkbox': function (e) {
        if (this._disabledCheckboxes) {
          return;
        }
        const $target = $(e.currentTarget);
        const $from = this._$focusedCheckbox;
        if (e.shiftKey && $from) {
          const $checkboxes = this.$el.find('input.record-checkbox');
          const start = $checkboxes.index($target);
          const end = $checkboxes.index($from);
          const checked = $from.prop('checked');
          $checkboxes.slice(Math.min(start, end), Math.max(start, end) + 1).each((i, el) => {
            const $el = $(el);
            $el.prop('checked', checked);
            this.checkboxClick($el, checked);
          });
          return;
        }
        this.checkboxClick($target, $target.is(':checked'));
      },
      /**
       * @param {JQueryKeyEventObject} e
       * @this module:views/record/list
       */
      'click .select-all': function (e) {
        if (this._disabledCheckboxes) {
          return;
        }

        // noinspection JSUnresolvedReference
        this.selectAllHandler(e.currentTarget.checked);
      },
      /** @this ListRecordView */
      'click .action': function (e) {
        Espo.Utils.handleAction(this, e.originalEvent, e.currentTarget, {
          actionItems: [...this.buttonList, ...this.dropdownItemList],
          className: 'list-action-item'
        });
      },
      /** @this ListRecordView */
      'click .checkbox-dropdown [data-action="selectAllResult"]': function () {
        if (this._disabledCheckboxes) {
          return;
        }
        this.selectAllResult();
      },
      /**
       * @param {JQueryKeyEventObject} e
       * @this ListRecordView
       */
      'click .actions-menu a.mass-action': function (e) {
        const $el = $(e.currentTarget);
        const action = $el.data('action');
        const method = 'massAction' + Espo.Utils.upperCaseFirst(action);
        e.preventDefault();
        e.stopPropagation();
        const $parent = $el.closest('.dropdown-menu').parent();

        // noinspection JSUnresolvedReference
        $parent.find('.actions-button[data-toggle="dropdown"]').dropdown('toggle').focus();
        if (method in this) {
          this[method]();
          return;
        }
        this.massAction(action);
      },
      /** @this ListRecordView */
      'click a.reset-custom-order': function () {
        this.resetCustomOrder();
      }
    };
    focusOnList() {
      const element = /** @type {HTMLElement} */this.$el.find('.list').get(0);
      if (!element) {
        return;
      }
      element.focus({
        preventScroll: true
      });
    }

    /**
     * @private
     * @param {'first'|'last'|'next'|'previous'|'current'} page
     */
    goToPage(page) {
      Espo.Ui.notifyWait();
      const onSync = () => {
        Espo.Ui.notify(false);
        this.trigger('after:paginate');
        this.focusOnList();
      };
      if (page === 'current') {
        this.collection.fetch().then(() => onSync());
        this.deactivate();
        return;
      }
      if (page === 'next') {
        this.collection.nextPage().then(() => onSync());
      } else if (page === 'previous') {
        this.collection.previousPage().then(() => onSync());
      } else if (page === 'last') {
        this.collection.lastPage().then(() => onSync());
      } else if (page === 'first') {
        this.collection.firstPage().then(() => onSync());
      }
      this.trigger('paginate');
      this.deactivate();
    }

    /**
     * @param {JQuery} $checkbox
     * @param {boolean} checked
     * @private
     */
    checkboxClick($checkbox, checked) {
      const id = $checkbox.attr('data-id');
      if (checked) {
        this.checkRecord(id, $checkbox);
        return;
      }
      this.uncheckRecord(id, $checkbox);
    }
    resetCustomOrder() {
      this.collection.offset = 0;
      this.collection.resetOrderToDefault();
      this.collection.trigger('order-changed');
      this.collection.fetch().then(() => {
        this.trigger('sort', {
          orderBy: this.collection.orderBy,
          order: this.collection.order
        });
      });
    }

    /**
     * @param {string} orderBy
     * @protected
     */
    toggleSort(orderBy) {
      let asc = true;
      if (orderBy === this.collection.orderBy && this.collection.order === 'asc') {
        asc = false;
      }
      const order = asc ? 'asc' : 'desc';
      Espo.Ui.notifyWait();
      const maxSizeLimit = this.getConfig().get('recordListMaxSizeLimit') || 200;
      while (this.collection.length > maxSizeLimit) {
        this.collection.pop();
      }
      this.collection.offset = 0;
      this.collection.sort(orderBy, order).then(() => {
        Espo.Ui.notify(false);
        this.trigger('sort', {
          orderBy: orderBy,
          order: order
        });
      });
      this.collection.trigger('order-changed');
      this.deactivate();
    }

    /**
     * @return {boolean}
     */
    toShowStickyBar() {
      return this.getCheckedIds().length > 0 || this.isAllResultChecked() || this.pagination;
    }

    /** @private */
    initStickyBar() {
      this._stickyBarHelper = new _stickyBar.default(this, {
        force: this.forceStickyBar
      });
    }

    /** @protected */
    showActions() {
      this.$el.find('.actions-button').removeClass('hidden');
      if (!this.options.stickyBarDisabled && !this.stickyBarDisabled && this.massActionList.length) {
        if (!this._stickyBarHelper) {
          this.initStickyBar();
        }
      }
    }

    /** @protected */
    hideActions() {
      this.$el.find('.actions-button').addClass('hidden');
      if (this._stickyBarHelper && (!this.pagination || this.forceStickyBar)) {
        this._stickyBarHelper.hide();
      }
    }

    /** @protected */
    selectAllHandler(isChecked) {
      this.checkedList = [];
      if (isChecked) {
        this.$el.find('input.record-checkbox').prop('checked', true);
        this.showActions();
        this.collection.models.forEach(model => {
          this.checkedList.push(model.id);
        });
        this.$el.find('.list > table tbody tr').addClass('active');
      } else {
        if (this.allResultIsChecked) {
          this.unselectAllResult();
        }
        this.$el.find('input.record-checkbox').prop('checked', false);
        this.hideActions();
        this.$el.find('.list > table tbody tr').removeClass('active');
      }
      this.trigger('check');
    }

    /** @inheritDoc */
    data() {
      const moreCount = this.collection.total - this.collection.length - this.collection.offset;
      let checkAllResultDisabled = this.checkAllResultDisabled;
      if (!this.massActionsDisabled) {
        if (!this.checkAllResultMassActionList.length) {
          checkAllResultDisabled = true;
        }
      }
      const displayTotalCount = this.displayTotalCount && this.collection.total > 0 && !this.pagination;
      let topBar = this.forceDisplayTopBar || this.collection.length && (this.pagination || this.checkboxes || this.buttonList.length && !this.buttonsDisabled || this.dropdownItemList.length && !this.buttonsDisabled || displayTotalCount);
      if (!topBar && this.pagination && !this.collection.length && this.collection.offset > 0) {
        topBar = true;
      }
      if (this.forceStickyBar) {
        topBar = false;
      }
      const checkboxes = this.checkboxes && this.massActionList.length;
      const displayActionsButtonGroup = checkboxes || this.buttonList.length || this.dropdownItemList.length;
      const hasStickyBar = this.forceStickyBar || displayActionsButtonGroup || this.pagination;
      const noDataDisabled = this.noDataDisabled || this._renderEmpty;
      const rowDataList = this.rowList ? this.rowList.map(id => {
        return {
          id: id,
          isStarred: this.hasStars && this.collection.get(id) ? this.collection.get(id).attributes.isStarred : false
        };
      }) : [];
      const checkboxColumnWidth = (this.checkboxColumnWidth * this._fontSizeFactor).toString() + 'px';
      return {
        scope: this.scope,
        collectionLength: this.collection.models.length,
        entityType: this.entityType,
        header: this.header,
        hasColumnResize: this._hasColumnResize(),
        headerDefs: this._getHeaderDefs(),
        hasPagination: this.hasPagination(),
        showMoreActive: this.collection.hasMore(),
        showMoreEnabled: this.showMore,
        showCount: this.showCount && this.collection.total > 0,
        moreCount: moreCount,
        checkboxes: this.checkboxes,
        massActionDataList: this.getMassActionDataList(),
        rowList: this.rowList,
        // For bc.
        rowDataList: rowDataList,
        topBar: topBar,
        checkAllResultDisabled: checkAllResultDisabled,
        buttonList: this.buttonList,
        dropdownItemList: this.dropdownItemList,
        displayTotalCount: displayTotalCount,
        displayActionsButtonGroup: displayActionsButtonGroup,
        totalCountFormatted: this.getNumberUtil().formatInt(this.collection.total),
        moreCountFormatted: this.getNumberUtil().formatInt(moreCount),
        checkboxColumnWidth: checkboxColumnWidth,
        noDataDisabled: noDataDisabled,
        hasStickyBar: hasStickyBar
      };
    }

    /** @inheritDoc */
    init() {
      this.type = this.options.type || this.type;
      this.listLayout = this.options.listLayout || this.listLayout;
      this.layoutName = this.options.layoutName || this.layoutName || this.type;
      this.layoutScope = this.options.layoutScope || this.layoutScope;
      this.layoutAclDisabled = this.options.layoutAclDisabled || this.layoutAclDisabled;
      this.headerDisabled = this.options.headerDisabled || this.headerDisabled;
      this.noDataDisabled = this.options.noDataDisabled || this.noDataDisabled;
      if (!this.collectionEventSyncList) {
        this.collectionEventSyncList = [];
      } else {
        this.collectionEventSyncList = [...this.collectionEventSyncList];
      }
      if (!this.headerDisabled) {
        this.header = _.isUndefined(this.options.header) ? this.header : this.options.header;
      } else {
        this.header = false;
      }
      this.pagination = this.options.pagination == null ? this.pagination : this.options.pagination;
      if (this.paginationDisabled) {
        this.pagination = false;
      }
      if (this.options.columnResize !== undefined) {
        this.columnResize = this.options.columnResize;
      }
      this.checkboxes = _.isUndefined(this.options.checkboxes) ? this.checkboxes : this.options.checkboxes;
      this.selectable = _.isUndefined(this.options.selectable) ? this.selectable : this.options.selectable;
      this.checkboxesDisabled = this.options.checkboxes === false;
      this.rowActionsView = _.isUndefined(this.options.rowActionsView) ? this.rowActionsView : this.options.rowActionsView;
      this.showMore = _.isUndefined(this.options.showMore) ? this.showMore : this.options.showMore;
      this.massActionsDisabled = this.options.massActionsDisabled || this.massActionsDisabled;
      this.portalLayoutDisabled = this.options.portalLayoutDisabled || this.portalLayoutDisabled;
      if (this.massActionsDisabled && !this.selectable) {
        this.checkboxes = false;
      }
      this.rowActionsDisabled = this.options.rowActionsDisabled || this.rowActionsDisabled;
      this.dropdownItemList = Espo.Utils.cloneDeep(this.options.dropdownItemList || this.dropdownItemList);
      if ('buttonsDisabled' in this.options) {
        this.buttonsDisabled = this.options.buttonsDisabled;
      }
      if ('checkAllResultDisabled' in this.options) {
        this.checkAllResultDisabled = this.options.checkAllResultDisabled;
      }
      this.rootData = this.options.rootData || {};
      this._fontSizeFactor = this.getThemeManager().getFontSizeFactor();
    }

    /**
     * Get a record entity type (scope).
     *
     * @param {string} id A record ID.
     * @return {string}
     */
    getModelScope(id) {
      return this.scope;
    }

    /**
     * Select all results.
     */
    selectAllResult() {
      this.allResultIsChecked = true;
      this.hideActions();
      this.$el.find('input.record-checkbox').prop('checked', true).attr('disabled', 'disabled');
      this.$selectAllCheckbox.prop('checked', true);
      this.massActionList.forEach(item => {
        if (!this.checkAllResultMassActionList.includes(item)) {
          this.$el.find(`div.list-buttons-container .actions-menu li a.mass-action[data-action="${item}"]`).parent().addClass('hidden');
        }
      });
      if (this.checkAllResultMassActionList.length) {
        this.showActions();
      }
      this.$el.find('.list > table tbody tr').removeClass('active');
      this.trigger('select-all-results');
    }

    /**
     * Unselect all results.
     */
    unselectAllResult() {
      this.allResultIsChecked = false;
      this.$el.find('input.record-checkbox').prop('checked', false).removeAttr('disabled');
      this.$selectAllCheckbox.prop('checked', false);
      this.massActionList.forEach(item => {
        if (!this.checkAllResultMassActionList.includes(item) && !(this.massActionDefs[item] || {}).hidden) {
          this.$el.find(`div.list-buttons-container .actions-menu li a.mass-action[data-action="${item}"]`).parent().removeClass('hidden');
        }
      });
    }

    /** @protected */
    deactivate() {
      if (this.$el) {
        this.$el.find(".pagination a").addClass('disabled');
        this.$el.find("a.sort").addClass('disabled');
      }
    }

    /**
     * Process export.
     *
     * @param {Object<string,*>} [data]
     * @param {string} [url='Export'] An API URL.
     * @param {string[]} [fieldList] A field list.
     */
    export(data, url, fieldList) {
      if (!data) {
        data = {
          entityType: this.entityType
        };
        if (this.allResultIsChecked) {
          data.where = this.collection.getWhere();
          data.searchParams = this.collection.data || null;
          data.searchData = this.collection.data || {}; // for bc;
        } else {
          data.ids = this.checkedList;
        }
      }
      url = url || 'Export';
      const o = {
        scope: this.entityType
      };
      if (fieldList) {
        o.fieldList = fieldList;
      } else {
        const layoutFieldList = [];
        (this.listLayout || []).forEach(item => {
          if (item.name) {
            layoutFieldList.push(item.name);
          }
        });
        o.fieldList = layoutFieldList;
      }
      const helper = new _export.default(this);
      const idle = this.allResultIsChecked && helper.checkIsIdle(this.collection.total);
      const proceedDownload = attachmentId => {
        window.location = `${this.getBasePath()}?entryPoint=download&id=${attachmentId}`;
      };
      this.createView('dialogExport', 'views/export/modals/export', o, view => {
        view.render();
        this.listenToOnce(view, 'proceed', dialogData => {
          if (!dialogData.exportAllFields) {
            data.attributeList = dialogData.attributeList;
            data.fieldList = dialogData.fieldList;
          }
          data.idle = idle;
          data.format = dialogData.format;
          data.params = dialogData.params;
          Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
          Espo.Ajax.postRequest(url, data, {
            timeout: 0
          }).then(/** Object.<string, *> */response => {
            Espo.Ui.notify(false);
            if (response.exportId) {
              helper.process(response.exportId).then(view => {
                this.listenToOnce(view, 'download', id => proceedDownload(id));
              });
              return;
            }
            if (!response.id) {
              throw new Error("No attachment-id.");
            }
            proceedDownload(response.id);
          });
        });
      });
    }

    /**
     * Process a mass-action.
     *
     * @param {string} name An action.
     */
    massAction(name) {
      const defs = this.massActionDefs[name] || {};
      const handler = defs.handler;
      if (handler) {
        const method = defs.actionFunction || 'action' + Espo.Utils.upperCaseFirst(name);
        const data = {
          entityType: this.entityType,
          action: name,
          params: this.getMassActionSelectionPostData()
        };
        Espo.loader.require(handler, Handler => {
          const handler = new Handler(this);
          handler[method].call(handler, data);
        });
        return;
      }
      const bypassConfirmation = defs.bypassConfirmation || false;
      const confirmationMsg = defs.confirmationMessage || 'confirmation';
      const acl = defs.acl;
      const aclScope = defs.aclScope;
      const proceed = () => {
        if (acl || aclScope) {
          if (!this.getAcl().check(aclScope || this.scope, acl)) {
            Espo.Ui.error(this.translate('Access denied'));
            return;
          }
        }
        const idList = [];
        const data = {};
        if (this.allResultIsChecked) {
          data.where = this.collection.getWhere();
          data.searchParams = this.collection.data || {};
          data.selectData = data.searchData; // for bc;
          data.byWhere = true; // for bc
        } else {
          data.idList = idList; // for bc
          data.ids = idList;
        }
        for (const i in this.checkedList) {
          idList.push(this.checkedList[i]);
        }
        data.entityType = this.entityType;
        const waitMessage = defs.waitMessage || 'pleaseWait';
        Espo.Ui.notify(this.translate(waitMessage, 'messages', this.scope));
        const url = defs.url;
        Espo.Ajax.postRequest(url, data).then(/** Object.<string, *> */result => {
          const successMessage = result.successMessage || defs.successMessage || 'done';
          this.collection.fetch().then(() => {
            let message = this.translate(successMessage, 'messages', this.scope);
            if ('count' in result) {
              message = message.replace('{count}', result.count);
            }
            Espo.Ui.success(message);
          });
        });
      };
      if (!bypassConfirmation) {
        this.confirm(this.translate(confirmationMsg, 'messages', this.scope), proceed, this);
      } else {
        proceed.call(this);
      }
    }
    getMassActionSelectionPostData() {
      const data = {};
      if (this.allResultIsChecked) {
        data.where = this.collection.getWhere();
        data.searchParams = this.collection.data || {};
        data.selectData = this.collection.data || {}; // for bc;
        data.byWhere = true; // for bc;
      } else {
        data.ids = [];
        for (const i in this.checkedList) {
          data.ids.push(this.checkedList[i]);
        }
      }
      return data;
    }

    // noinspection JSUnusedGlobalSymbols
    massActionRecalculateFormula() {
      let ids = false;
      const allResultIsChecked = this.allResultIsChecked;
      if (!allResultIsChecked) {
        ids = this.checkedList;
      }
      this.confirm({
        message: this.translate('recalculateFormulaConfirmation', 'messages'),
        confirmText: this.translate('Yes')
      }, () => {
        Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
        const params = this.getMassActionSelectionPostData();
        const helper = new _massAction.default(this);
        const idle = !!params.searchParams && helper.checkIsIdle(this.collection.total);
        Espo.Ajax.postRequest('MassAction', {
          entityType: this.entityType,
          action: 'recalculateFormula',
          params: params,
          idle: idle
        }).then(result => {
          result = result || {};
          const final = () => {
            this.collection.fetch().then(() => {
              Espo.Ui.success(this.translate('Done'));
              if (allResultIsChecked) {
                this.selectAllResult();
                return;
              }
              ids.forEach(id => {
                this.checkRecord(id);
              });
            });
          };
          if (result.id) {
            helper.process(result.id, 'recalculateFormula').then(view => {
              this.listenToOnce(view, 'close:success', () => final());
            });
            return;
          }
          final();
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    massActionRemove() {
      if (!this.getAcl().check(this.entityType, 'delete')) {
        Espo.Ui.error(this.translate('Access denied'));
        return false;
      }
      this.confirm({
        message: this.translate('removeSelectedRecordsConfirmation', 'messages', this.scope),
        confirmText: this.translate('Remove')
      }, () => {
        Espo.Ui.notifyWait();
        const helper = new _massAction.default(this);
        const params = this.getMassActionSelectionPostData();
        const idle = !!params.searchParams && helper.checkIsIdle(this.collection.total);
        Espo.Ajax.postRequest('MassAction', {
          entityType: this.entityType,
          action: 'delete',
          params: params,
          idle: idle
        }).then(result => {
          result = result || {};
          const afterAllResult = count => {
            if (!count) {
              Espo.Ui.warning(this.translate('noRecordsRemoved', 'messages'));
              return;
            }
            this.unselectAllResult();
            this.collection.fetch().then(() => {
              const msg = count === 1 ? 'massRemoveResultSingle' : 'massRemoveResult';
              Espo.Ui.success(this.translate(msg, 'messages').replace('{count}', count));
            });
            this.collection.trigger('after:mass-remove');
            Espo.Ui.notify(false);
          };
          if (result.id) {
            helper.process(result.id, 'delete').then(view => {
              this.listenToOnce(view, 'close:success', result => afterAllResult(result.count));
            });
            return;
          }
          const count = result.count;
          if (this.allResultIsChecked) {
            afterAllResult(count);
            return;
          }
          const idsRemoved = result.ids || [];
          if (!count) {
            Espo.Ui.warning(this.translate('noRecordsRemoved', 'messages'));
            return;
          }
          idsRemoved.forEach(id => {
            Espo.Ui.notify(false);
            this.collection.trigger('model-removing', id);
            this.removeRecordFromList(id);
            this.uncheckRecord(id, null, true);
          });
          if (this.$selectAllCheckbox.prop('checked')) {
            this.$selectAllCheckbox.prop('checked', false);
            if (this.collection.hasMore()) {
              this.showMoreRecords({
                skipNotify: true
              });
            }
          }
          this.collection.trigger('after:mass-remove');
          const showSuccess = () => {
            const msgKey = count === 1 ? 'massRemoveResultSingle' : 'massRemoveResult';
            const msg = this.translate(msgKey, 'messages').replace('{count}', count);
            Espo.Ui.success(msg);
          };
          showSuccess();
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    massActionPrintPdf() {
      const maxCount = this.getConfig().get('massPrintPdfMaxCount');
      if (maxCount) {
        if (this.checkedList.length > maxCount) {
          const msg = this.translate('massPrintPdfMaxCountError', 'messages').replace('{maxCount}', maxCount.toString());
          Espo.Ui.error(msg);
          return;
        }
      }
      const idList = [];
      for (const i in this.checkedList) {
        idList.push(this.checkedList[i]);
      }
      this.createView('pdfTemplate', 'views/modals/select-template', {
        entityType: this.entityType
      }, view => {
        view.render();
        this.listenToOnce(view, 'select', templateModel => {
          this.clearView('pdfTemplate');
          Espo.Ui.notifyWait();
          Espo.Ajax.postRequest('Pdf/action/massPrint', {
            idList: idList,
            entityType: this.entityType,
            templateId: templateModel.id
          }, {
            timeout: 0
          }).then(result => {
            Espo.Ui.notify(false);
            window.open('?entryPoint=download&id=' + result.id, '_blank');
          });
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    massActionFollow() {
      const count = this.checkedList.length;
      const confirmMsg = this.translate('confirmMassFollow', 'messages').replace('{count}', count.toString());
      this.confirm({
        message: confirmMsg,
        confirmText: this.translate('Follow')
      }, () => {
        Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
        Espo.Ajax.postRequest('MassAction', {
          action: 'follow',
          entityType: this.entityType,
          params: this.getMassActionSelectionPostData()
        }).then(result => {
          const resultCount = result.count || 0;
          let msg = 'massFollowResult';
          if (resultCount) {
            if (resultCount === 1) {
              msg += 'Single';
            }
            Espo.Ui.success(this.translate(msg, 'messages').replace('{count}', resultCount.toString()));
            return;
          }
          Espo.Ui.warning(this.translate('massFollowZeroResult', 'messages'));
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    massActionUnfollow() {
      const count = this.checkedList.length;
      const confirmMsg = this.translate('confirmMassUnfollow', 'messages').replace('{count}', count.toString());
      this.confirm({
        message: confirmMsg,
        confirmText: this.translate('Yes')
      }, () => {
        Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
        const params = this.getMassActionSelectionPostData();
        const helper = new _massAction.default(this);
        const idle = !!params.searchParams && helper.checkIsIdle(this.collection.total);
        Espo.Ajax.postRequest('MassAction', {
          action: 'unfollow',
          entityType: this.entityType,
          params: params,
          idle: idle
        }).then(result => {
          const final = count => {
            let msg = 'massUnfollowResult';
            if (!count) {
              Espo.Ui.warning(this.translate('massUnfollowZeroResult', 'messages'));
            }
            if (count === 1) {
              msg += 'Single';
            }
            Espo.Ui.success(this.translate(msg, 'messages').replace('{count}', count));
          };
          if (result.id) {
            helper.process(result.id, 'unfollow').then(view => {
              this.listenToOnce(view, 'close:success', result => final(result.count));
            });
            return;
          }
          final(result.count || 0);
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    massActionMerge() {
      if (!this.getAcl().check(this.entityType, 'edit')) {
        Espo.Ui.error(this.translate('Access denied'));
        return false;
      }
      if (this.checkedList.length < 2) {
        Espo.Ui.error(this.translate('select2OrMoreRecords', 'messages'));
        return;
      }
      if (this.checkedList.length > 4) {
        const msg = this.translate('selectNotMoreThanNumberRecords', 'messages').replace('{number}', '4');
        Espo.Ui.error(msg);
        return;
      }
      this.checkedList.sort();
      const url = '#' + this.entityType + '/merge/ids=' + this.checkedList.join(',');
      this.getRouter().navigate(url, {
        trigger: false
      });
      this.getRouter().dispatch(this.entityType, 'merge', {
        ids: this.checkedList.join(','),
        collection: this.collection
      });
    }

    // noinspection JSUnusedGlobalSymbols
    async massActionMassUpdate() {
      if (!this.getAcl().check(this.entityType, 'edit')) {
        Espo.Ui.error(this.translate('Access denied'));
        return false;
      }
      Espo.Ui.notifyWait();
      let ids = false;
      const allResultIsChecked = this.allResultIsChecked;
      if (!allResultIsChecked) {
        ids = this.checkedList;
      }
      const viewName = this.getMetadata().get(['clientDefs', this.entityType, 'modalViews', 'massUpdate']) || 'views/modals/mass-update';

      // noinspection JSValidateTypes
      /** @type {import('views/modals/mass-update').default} view */
      const view = await this.createView('massUpdate', viewName, {
        scope: this.scope,
        entityType: this.entityType,
        ids: ids,
        where: this.collection.getWhere(),
        searchParams: this.collection.data,
        byWhere: this.allResultIsChecked,
        totalCount: this.collection.total
      });
      this.listenToOnce(view, 'after:update', async o => {
        if (o.idle) {
          this.listenToOnce(view, 'close', async () => {
            await this.collection.fetch();
            if (allResultIsChecked) {
              this.selectAllResult();
              return;
            }
            ids.forEach(id => this.checkRecord(id));
          });
          return;
        }
        view.close();
        const count = o.count;
        await this.collection.fetch();
        if (count) {
          const msgKey = count === 1 ? 'massUpdateResultSingle' : 'massUpdateResult';
          const message = this.translate(msgKey, 'messages').replace('{count}', count);
          Espo.Ui.success(message);
        } else {
          Espo.Ui.warning(this.translate('noRecordsUpdated', 'messages'));
        }
        if (allResultIsChecked) {
          this.selectAllResult();
          return;
        }
        ids.forEach(id => this.checkRecord(id));
      });
      Espo.Ui.notify();
      await view.render();
    }

    // noinspection JSUnusedGlobalSymbols
    massActionExport() {
      if (this.getConfig().get('exportDisabled') && !this.getUser().isAdmin()) {
        return;
      }
      this.export();
    }

    // noinspection JSUnusedGlobalSymbols
    massActionUnlink() {
      this.confirm({
        message: this.translate('unlinkSelectedRecordsConfirmation', 'messages'),
        confirmText: this.translate('Unlink')
      }, () => {
        Espo.Ui.notifyWait();
        Espo.Ajax.deleteRequest(this.collection.url, {
          ids: this.checkedList
        }).then(() => {
          Espo.Ui.success(this.translate('Unlinked'));
          this.collection.fetch();
          this.model.trigger('after:unrelate');
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    massActionConvertCurrency() {
      let ids = false;
      const allResultIsChecked = this.allResultIsChecked;
      if (!allResultIsChecked) {
        ids = this.checkedList;
      }
      this.createView('modalConvertCurrency', 'views/modals/mass-convert-currency', {
        entityType: this.entityType,
        ids: ids,
        where: this.collection.getWhere(),
        searchParams: this.collection.data,
        byWhere: this.allResultIsChecked,
        totalCount: this.collection.total
      }, view => {
        view.render();
        this.listenToOnce(view, 'after:update', o => {
          if (o.idle) {
            this.listenToOnce(view, 'close', () => {
              this.collection.fetch().then(() => {
                if (allResultIsChecked) {
                  this.selectAllResult();
                  return;
                }
                ids.forEach(id => {
                  this.checkRecord(id);
                });
              });
            });
            return;
          }
          const count = o.count;
          this.collection.fetch().then(() => {
            if (count) {
              let msg = 'massUpdateResult';
              if (count === 1) {
                msg = 'massUpdateResultSingle';
              }
              Espo.Ui.success(this.translate(msg, 'messages').replace('{count}', count));
            } else {
              Espo.Ui.warning(this.translate('noRecordsUpdated', 'messages'));
            }
            if (allResultIsChecked) {
              this.selectAllResult();
              return;
            }
            ids.forEach(id => {
              this.checkRecord(id);
            });
          });
        });
      });
    }

    /**
     * Add a mass action.
     *
     * @protected
     * @param {string|module:views/record/list~massActionItem} item An action.
     * @param {boolean} [allResult] To make available for all-result.
     * @param {boolean} [toBeginning] Add to the beginning of the list.
     */
    addMassAction(item, allResult, toBeginning) {
      if (typeof item !== 'string') {
        const name = item.name;
        this.massActionDefs[name] = {
          ...this.massActionDefs[name],
          ...item
        };
        item = name;
      }
      toBeginning ? this.massActionList.unshift(item) : this.massActionList.push(item);
      if (allResult && this.collection.url === this.entityType) {
        toBeginning ? this.checkAllResultMassActionList.unshift(item) : this.checkAllResultMassActionList.push(item);
      }
      if (!this.checkboxesDisabled) {
        this.checkboxes = true;
      }
    }

    /**
     * Remove a mass action.
     *
     * @param {string} item An action.
     */
    removeMassAction(item) {
      let index = this.massActionList.indexOf(item);
      if (~index) {
        this.massActionList.splice(index, 1);
      }
      index = this.checkAllResultMassActionList.indexOf(item);
      if (~index) {
        this.checkAllResultMassActionList.splice(index, 1);
      }
    }

    /**
     * Remove an all-result mass action.
     *
     * @param {string} item An action.
     */
    removeAllResultMassAction(item) {
      const index = this.checkAllResultMassActionList.indexOf(item);
      if (~index) {
        this.checkAllResultMassActionList.splice(index, 1);
      }
    }

    /** @inheritDoc */
    setup() {
      this.addHandler('click', 'a.link', (/** KeyboardEvent */e, target) => {
        if (e.ctrlKey || e.metaKey || e.shiftKey) {
          return;
        }
        e.stopPropagation();
        if (!this.scope || this.selectable) {
          return;
        }
        e.preventDefault();
        this.processLinkClick(target.dataset.id);
      });
      if (typeof this.collection === 'undefined') {
        throw new Error('Collection has not been injected into views/record/list view.');
      }
      this.layoutLoadCallbackList = [];
      this.entityType = this.collection.entityType || null;
      this.scope = this.options.scope || this.entityType;
      this.massActionList = Espo.Utils.clone(this.massActionList);
      this.checkAllResultMassActionList = Espo.Utils.clone(this.checkAllResultMassActionList);
      this.buttonList = Espo.Utils.clone(this.buttonList);
      this.mandatorySelectAttributeList = Espo.Utils.clone(this.options.mandatorySelectAttributeList || this.mandatorySelectAttributeList || []);
      this.forceStickyBar = this.options.forceStickyBar || this.forceStickyBar;
      this.editDisabled = this.options.editDisabled || this.editDisabled || this.getMetadata().get(['clientDefs', this.scope, 'editDisabled']);
      this.removeDisabled = this.options.removeDisabled || this.removeDisabled || this.getMetadata().get(['clientDefs', this.scope, 'removeDisabled']);
      this.setupMassActions();
      if (this.selectable) {
        this.events['click .list a.link'] = e => {
          e.preventDefault();
          const id = $(e.currentTarget).attr('data-id');
          if (id) {
            this.selectModel(id);
          }
          e.stopPropagation();
        };
      }
      if ('showCount' in this.options) {
        this.showCount = this.options.showCount;
      }
      this.displayTotalCount = this.showCount && this.getConfig().get('displayListViewRecordCount');
      if ('displayTotalCount' in this.options) {
        this.displayTotalCount = this.options.displayTotalCount;
      }
      this.forceDisplayTopBar = this.options.forceDisplayTopBar || this.forceDisplayTopBar;
      if (!this.massActionList.length && !this.selectable) {
        this.checkboxes = false;
      }

      /**
       * @private
       * @type {boolean}
       */
      this.hasStars = this.getMetadata().get(`scopes.${this.entityType}.stars`) || false;
      if (this.getUser().isPortal() && !this.portalLayoutDisabled && this.getMetadata().get(['clientDefs', this.scope, 'additionalLayouts', this.layoutName + 'Portal'])) {
        this.layoutName += 'Portal';
      }
      this.setupRowActionDefs();
      this.setupSettings();
      this.wait(this.getHelper().processSetupHandlers(this, this.setupHandlerType));
      this.listenTo(this.collection, 'sync', (c, r, options) => {
        this._renderEmpty = false;
        options = options || {};
        if (options.previousDataList) {
          const currentDataList = this.collection.models.map(model => {
            return Espo.Utils.cloneDeep(model.attributes);
          });
          if (_.isEqual(currentDataList, options.previousDataList) && options.previousTotal === this.collection.total) {
            return;
          }
        }
        if (this.noRebuild) {
          this.noRebuild = null;
          return;
        }
        if (options.noRebuild) {
          this.noRebuild = null;
          return;
        }
        this.checkedList = [];
        this.allResultIsChecked = false;
        this.buildRowsAndRender();
      });
      this.checkedList = [];
      if (!this.options.skipBuildRows) {
        this.buildRows();
      }
      if (this.pagination) {
        this.createView('pagination', 'views/record/list-pagination', {
          collection: this.collection,
          displayTotalCount: this.displayTotalCount,
          recordView: this
        });
        this.createView('paginationSticky', 'views/record/list-pagination', {
          collection: this.collection,
          displayTotalCount: this.displayTotalCount,
          recordView: this
        });
        this.on('request-page', /** string */page => {
          if (this.collection.isBeingFetched()) {
            return;
          }
          if (page === 'next' && !this.collection.hasNextPage()) {
            return;
          }
          if (page === 'previous' && !this.collection.hasPreviousPage()) {
            return;
          }
          this.goToPage(page);
        });
      }
      this._renderEmpty = this.options.skipBuildRows;
      if (this.columnResize && this._listSettingsHelper) {
        this._columnResizeHelper = new _columnResize.default(this, this._listSettingsHelper);
      }
    }

    /**
     * @protected
     * @since 9.1.0
     */
    buildRowsAndRender() {
      let modalView;
      const modalKey = 'modal';
      if (this.hasView(modalKey) && this.getView(modalKey).isRendered()) {
        modalView = this.getView(modalKey);
        this.unchainView(modalKey);
      }
      this.buildRows(async () => {
        await this.reRender({
          force: true
        });
        if (modalView) {
          this.setView(modalKey, modalView);
        }
      });
    }

    /**
     * @private
     * @param {string} id
     */
    processLinkClick(id) {
      var _this = this;
      const scope = this.getModelScope(id);
      const collection = this.collection.clone({
        withModels: true
      });
      const options = {
        id: id,
        model: collection.get(id)
      };
      if (this.collectionEventSyncList) {
        this.listenTo(collection, 'all', function (event) {
          if (_this.collectionEventSyncList.includes(event)) {
            for (var _len = arguments.length, parameters = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
              parameters[_key - 1] = arguments[_key];
            }
            _this.collection.trigger(event, ...parameters);
          }
        });
      }
      this.listenTo(collection, 'model-sync', (/** Model */m, /** Record */o) => {
        if (o.action === 'destroy') {
          this.removeRecordFromList(m.id);
        }
        const model = this.collection.get(m.id);
        if (!model) {
          return;
        }
        if (o.action === 'set' || o.action === 'fetch' || o.action === 'save') {
          model.setMultiple(m.getClonedAttributes(), o);
        }
      });
      this.listenTo(collection, 'sync', (c, r, /** Record */o) => {
        if (!o.more) {
          return;
        }
        const moreModels = collection.models.slice(this.collection.length);
        this.collection.add(moreModels);
        this.collection.total = collection.total;
        this.collection.lengthCorrection = collection.lengthCorrection;
        this.collection.trigger('sync', c, r, o);
      });
      if (this.options.keepCurrentRootUrl) {
        options.rootUrl = this.getRouter().getCurrentUrl();
      }
      options.rootData = this.rootData;
      this.getRouter().navigate(`#${scope}/view/${id}`, {
        trigger: false
      });
      this.getRouter().dispatch(scope, 'view', options);
    }

    /**
     * @private
     * @param {string} id
     */
    selectModel(id) {
      const model = this.collection.get(id);
      if (this.checkboxes) {
        this.trigger('select', [model]);
        return;
      }
      this.trigger('select', model);
      if (this.options.onSelect) {
        this.options.onSelect([model]);
      }
    }
    onRemove() {
      this.destroyStickyBar();
    }
    afterRender() {
      this.destroyStickyBar();
      this.$selectAllCheckbox = this.$el.find('input.select-all');
      if (this.allResultIsChecked) {
        this.selectAllResult();
      } else if (this.checkedList.length) {
        this.checkedList.forEach(id => {
          this.checkRecord(id);
        });
      }
      if (this.pagination && this.$el.find('.list-buttons-container').length) {
        this.initStickyBar();
      }
      if (this._disabledCheckboxes) {
        this.disableCheckboxes();
      }
    }

    /** @private */
    destroyStickyBar() {
      if (this._stickyBarHelper) {
        this._stickyBarHelper.destroy();
      }
      this._stickyBarHelper = null;
    }

    /** @private */
    setupMassActions() {
      if (this.massActionsDisabled) {
        this.massActionList = [];
        this.checkAllResultMassActionList = [];
        this.massActionDefs = {};
        return;
      }
      if (!this.getAcl().checkScope(this.entityType, 'delete')) {
        this.removeMassAction('remove');
        this.removeMassAction('merge');
      }
      if (this.removeDisabled || this.getMetadata().get(['clientDefs', this.scope, 'massRemoveDisabled'])) {
        this.removeMassAction('remove');
      }
      if (!this.getAcl().checkScope(this.entityType, 'edit')) {
        this.removeMassAction('massUpdate');
        this.removeMassAction('merge');
      }
      if (this.getMetadata().get(['clientDefs', this.scope, 'mergeDisabled']) || this.mergeDisabled) {
        this.removeMassAction('merge');
      }
      this.massActionDefs = {
        remove: {
          groupIndex: 0
        },
        merge: {
          groupIndex: 0
        },
        massUpdate: {
          groupIndex: 0
        },
        export: {
          groupIndex: 2
        },
        follow: {
          groupIndex: 4
        },
        unfollow: {
          groupIndex: 4
        },
        convertCurrency: {
          groupIndex: 6
        },
        printToPdf: {
          groupIndex: 8
        },
        ...(this.getMetadata().get(['clientDefs', 'Global', 'massActionDefs']) || {}),
        ...(this.getMetadata().get(['clientDefs', this.scope, 'massActionDefs']) || {})
      };
      const metadataMassActionList = [...(this.getMetadata().get(['clientDefs', 'Global', 'massActionList']) || []), ...(this.getMetadata().get(['clientDefs', this.scope, 'massActionList']) || [])];
      const metadataCheckAllMassActionList = [...(this.getMetadata().get(['clientDefs', 'Global', 'checkAllResultMassActionList']) || []), ...(this.getMetadata().get(['clientDefs', this.scope, 'checkAllResultMassActionList']) || [])];
      metadataMassActionList.forEach(item => {
        const defs = /** @type {Espo.Utils~ActionAccessDefs & Espo.Utils~ActionAvailabilityDefs} */
        this.massActionDefs[item] || {};
        if (!Espo.Utils.checkActionAvailability(this.getHelper(), defs) || !Espo.Utils.checkActionAccess(this.getAcl(), this.entityType, defs)) {
          return;
        }
        this.massActionList.push(item);
      });
      this.checkAllResultMassActionList = this.checkAllResultMassActionList.filter(item => this.massActionList.includes(item));
      metadataCheckAllMassActionList.forEach(item => {
        if (this.collection.url !== this.entityType) {
          return;
        }
        if (~this.massActionList.indexOf(item)) {
          const defs = /** @type {Espo.Utils~ActionAccessDefs & Espo.Utils~ActionAvailabilityDefs} */
          this.massActionDefs[item] || {};
          if (!Espo.Utils.checkActionAvailability(this.getHelper(), defs) || !Espo.Utils.checkActionAccess(this.getAcl(), this.entityType, defs)) {
            return;
          }
          this.checkAllResultMassActionList.push(item);
        }
      });
      metadataMassActionList.concat(metadataCheckAllMassActionList).forEach(action => {
        const defs = this.massActionDefs[action] || {};
        if (!defs.initFunction || !defs.handler) {
          return;
        }
        const viewObject = this;
        this.wait(new Promise(resolve => {
          Espo.loader.require(defs.handler, Handler => {
            const handler = new Handler(viewObject);
            handler[defs.initFunction].call(handler);
            resolve();
          });
        }));
      });
      if (this.getConfig().get('exportDisabled') && !this.getUser().isAdmin() || this.getAcl().getPermissionLevel('exportPermission') === 'no' || this.getMetadata().get(['clientDefs', this.scope, 'exportDisabled']) || this.exportDisabled) {
        this.removeMassAction('export');
      }
      if (this.getAcl().getPermissionLevel('massUpdatePermission') !== 'yes' || this.editDisabled || this.massUpdateDisabled || this.getMetadata().get(['clientDefs', this.scope, 'massUpdateDisabled'])) {
        this.removeMassAction('massUpdate');
      }
      if (!this.massFollowDisabled && this.getMetadata().get(['scopes', this.entityType, 'stream']) && this.getAcl().check(this.entityType, 'stream') || this.getMetadata().get(['clientDefs', this.scope, 'massFollowDisabled'])) {
        this.addMassAction('follow');
        this.addMassAction('unfollow', true);
      }
      if (!this.massPrintPdfDisabled && (this.getHelper().getAppParam('templateEntityTypeList') || []).includes(this.entityType)) {
        this.addMassAction('printPdf');
      }
      if (this.options.unlinkMassAction && this.collection) {
        this.addMassAction('unlink', false, true);
      }
      if (!this.massConvertCurrencyDisabled && !this.getMetadata().get(['clientDefs', this.scope, 'convertCurrencyDisabled']) && this.getConfig().get('currencyList').length > 1 && this.getAcl().checkScope(this.scope, 'edit') && this.getAcl().getPermissionLevel('massUpdatePermission') === 'yes') {
        const currencyFieldList = this.getFieldManager().getEntityTypeFieldList(this.entityType, {
          type: 'currency',
          acl: 'edit'
        });
        if (currencyFieldList.length) {
          this.addMassAction('convertCurrency', true);
        }
      }
      this.setupMassActionItems();
      if (this.getUser().isAdmin()) {
        if (this.getMetadata().get(['formula', this.entityType, 'beforeSaveCustomScript'])) {
          this.addMassAction('recalculateFormula', true);
        }
      }
      if (this.collection.url !== this.entityType) {
        Espo.Utils.clone(this.checkAllResultMassActionList).forEach(item => {
          this.removeAllResultMassAction(item);
        });
      }
      if (this.forcedCheckAllResultMassActionList) {
        this.checkAllResultMassActionList = Espo.Utils.clone(this.forcedCheckAllResultMassActionList);
      }
      if (this.getAcl().getPermissionLevel('massUpdatePermission') !== 'yes') {
        this.removeAllResultMassAction('remove');
      }
      Espo.Utils.clone(this.massActionList).forEach(item => {
        const propName = 'massAction' + Espo.Utils.upperCaseFirst(item) + 'Disabled';
        if (this[propName] || this.options[propName]) {
          this.removeMassAction(item);
        }
      });
    }

    /** @protected */
    setupMassActionItems() {}

    /**
     * @param {module:views/record/list~columnDefs[]} listLayout
     * @return {module:views/record/list~columnDefs[]}
     * @protected
     */
    filterListLayout(listLayout) {
      if (this._cachedFilteredListLayout) {
        return this._cachedFilteredListLayout;
      }
      let forbiddenFieldList = this._cachedScopeForbiddenFieldList = this._cachedScopeForbiddenFieldList || this.getAcl().getScopeForbiddenFieldList(this.entityType, 'read');
      if (this.layoutAclDisabled) {
        forbiddenFieldList = [];
      }

      /*if (!forbiddenFieldList.length) {
          this._cachedFilteredListLayout = listLayout;
            return this._cachedFilteredListLayout;
      }*/

      const filteredListLayout = Espo.Utils.cloneDeep(listLayout);
      const deleteIndexes = [];
      for (const [i, item] of listLayout.entries()) {
        if (item.name && forbiddenFieldList.includes(item.name)) {
          item.customLabel = '';
          item.notSortable = true;
          deleteIndexes.push(i);
        }
      }
      deleteIndexes.reverse().forEach(index => filteredListLayout.splice(index, 1));

      /** @type {Record<Record>} */
      const fieldDefs = this.getMetadata().get(`entityDefs.${this.entityType}.fields`) || {};
      filteredListLayout.forEach(item => {
        if (!item.name || !fieldDefs[item.name]) {
          return;
        }
        if (fieldDefs[item.name].orderDisabled) {
          item.notSortable = true;
        }
      });
      this._cachedFilteredListLayout = filteredListLayout;
      return this._cachedFilteredListLayout;
    }

    /**
     * @protected
     * @param {function(module:views/record/list~columnDefs[]): void} callback A callback.
     */
    _loadListLayout(callback) {
      this.layoutLoadCallbackList.push(callback);
      if (this.layoutIsBeingLoaded) {
        return;
      }
      this.layoutIsBeingLoaded = true;
      const layoutName = this.layoutName;
      const layoutScope = this.layoutScope || this.collection.entityType;
      this.getHelper().layoutManager.get(layoutScope, layoutName, listLayout => {
        const filteredListLayout = this.filterListLayout(listLayout);
        this.layoutLoadCallbackList.forEach(callbackItem => {
          callbackItem(filteredListLayout);
          this.layoutLoadCallbackList = [];
          this.layoutIsBeingLoaded = false;
        });
      });
    }

    /**
     * Get a select-attribute list.
     *
     * @param {function(string[]):void} callback A callback.
     *
     * @todo Return promise. But support callback for bc.
     */
    getSelectAttributeList(callback) {
      if (this.scope === null) {
        callback(null);
        return;
      }
      if (this.listLayout) {
        const attributeList = this.fetchAttributeListFromLayout();
        callback(attributeList);
        return;
      }
      this._loadListLayout(listLayout => {
        this.listLayout = listLayout;
        let attributeList = this.fetchAttributeListFromLayout();
        if (this.mandatorySelectAttributeList) {
          attributeList = attributeList.concat(this.mandatorySelectAttributeList);
        }
        callback(attributeList);
      });
    }

    /**
     * @protected
     * @return {string[]}
     */
    fetchAttributeListFromLayout() {
      const selectProvider = new _selectProvider.default(this.getHelper().layoutManager, this.getHelper().metadata, this.getHelper().fieldManager);
      return selectProvider.getFromLayout(this.entityType, this.listLayout);
    }

    /**
     * @private
     */
    _hasColumnResize() {
      return this._listSettingsHelper ? this._listSettingsHelper.getColumnResize() : false;
    }

    /**
     * @private
     */
    _getHeaderDefs() {
      const resize = this._hasColumnResize();
      const widthMap = this._listSettingsHelper ? this._listSettingsHelper.getColumnWidthMap() : {};

      // noinspection JSIncompatibleTypesComparison
      if (!this.listLayout || !Array.isArray(this.listLayout)) {
        return [];
      }
      let emptyWidthMet = false;
      const visibleColumns = this.listLayout.filter(it => {
        if (!this._listSettingsHelper && it.hidden) {
          return false;
        }
        if (!this._listSettingsHelper) {
          return true;
        }
        if (it.name && this._listSettingsHelper.isColumnHidden(it.name, it.hidden)) {
          return false;
        }
        return true;
      }).map(it => ({
        ...it
      }));

      /**
       * @type {({
       *     widthPercent?: number|null,
       *     width?: string|false,
       *     isResized: boolean,
       * } & Record)[]}
       */
      const defs = [];
      for (const col of visibleColumns) {
        let width = false;
        let widthPercent = null;
        let isResized = false;
        const itemName = col.name;
        if (itemName && itemName in widthMap) {
          const widthItem = widthMap[itemName];
          width = widthItem.value + widthItem.unit;
          if (widthItem.unit === '%') {
            widthPercent = widthItem.value;
          }
          isResized = true;
        } else if ('width' in col && col.width !== null) {
          width = col.width + '%';
          widthPercent = col.width;
        } else if ('widthPx' in col) {
          width = (col.widthPx * this._fontSizeFactor).toString() + 'px';
        } else {
          emptyWidthMet = true;
        }
        const label = col.label || itemName;
        const item = {
          name: itemName,
          isSortable: !(col.notSortable || false),
          width: width,
          align: 'align' in col ? col.align : false,
          resizable: resize && width && visibleColumns.length > 1,
          resizeOnRight: resize && width && !emptyWidthMet,
          widthPercent: widthPercent,
          isResized: isResized
        };
        if ('customLabel' in col) {
          item.customLabel = col.customLabel;
          item.hasCustomLabel = true;
          item.label = item.customLabel;
        } else {
          item.label = this.translate(label, 'fields', this.collection.entityType);
        }
        if (col.noLabel) {
          item.label = null;
        }
        if (item.isSortable) {
          item.isSorted = this.collection.orderBy === itemName;
          if (item.isSorted) {
            item.isDesc = this.collection.order === 'desc';
          }
        }
        defs.push(item);
      }
      {
        const emptyWidth = 3.0;
        let sum = 0.0;
        let sumResized = 0.0;
        let countEmpty = 0;
        for (const item of defs) {
          if (item.widthPercent === null) {
            sum += emptyWidth;
            countEmpty++;
            continue;
          }
          sum += item.widthPercent;
          if (item.isResized) {
            sumResized += item.widthPercent;
          }
        }
        if (emptyWidthMet && sum > 100) {
          const space = 5;
          const factor = (100 - countEmpty * emptyWidth - space - sumResized) / sum;
          for (const item of defs) {
            if (item.widthPercent === null || item.isResized) {
              continue;
            }
            item.widthPercent = item.widthPercent * factor;
            item.width = item.widthPercent.toString() + '%';
          }
        }
      }
      const isCustomSorted = this.collection.orderBy !== this.collection.defaultOrderBy || this.collection.order !== this.collection.defaultOrder;
      if (this.rowActionsView && !this.rowActionsDisabled || isCustomSorted) {
        let html = null;
        if (isCustomSorted) {
          html = $('<a>').attr('role', 'button').attr('tabindex', '0').addClass('reset-custom-order').attr('title', this.translate('Reset')).append($('<span>').addClass('fas fa-times fa-sm')).get(0).outerHTML;
        }
        const width = (this._fontSizeFactor * this.rowActionsColumnWidth).toString() + 'px';
        defs.push({
          width: width,
          html: html,
          className: 'action-cell'
        });
      }
      return defs;
    }

    /** @protected */
    _convertLayout(listLayout, model) {
      model = model || this.collection.prepareModel();
      const layout = [];
      if (this.checkboxes) {
        layout.push({
          name: 'r-checkboxField',
          columnName: 'r-checkbox',
          template: 'record/list-checkbox'
        });
      }
      for (const col of listLayout) {
        const type = col.type || model.getFieldType(col.name) || 'base';
        if (!col.name) {
          continue;
        }
        const item = {
          columnName: col.name,
          name: col.name + 'Field',
          view: col.view || model.getFieldParam(col.name, 'view') || this.getFieldManager().getViewName(type),
          options: {
            defs: {
              name: col.name,
              params: col.params || {}
            },
            mode: 'list'
          }
        };
        if (col.width) {
          item.options.defs.width = col.width;
        }
        if (col.widthPx) {
          item.options.defs.widthPx = col.widthPx;
        }
        if (col.link) {
          item.options.mode = 'listLink';
        }
        if (col.align) {
          item.options.defs.align = col.align;
        }
        if (col.options) {
          for (const optionName in col.options) {
            if (typeof item.options[optionName] !== 'undefined') {
              continue;
            }
            item.options[optionName] = col.options[optionName];
          }
        }
        if (col.name && this._listSettingsHelper) {
          if (this._listSettingsHelper.isColumnHidden(col.name, col.hidden)) {
            continue;
          }
        }
        if (!this._listSettingsHelper && col.hidden) {
          continue;
        }
        layout.push(item);
      }
      if (this.rowActionsView && !this.rowActionsDisabled) {
        layout.push(this.getRowActionsDefs());
      }
      return layout;
    }

    /**
     * Select a record.
     *
     * @param {string} id An ID.
     * @param {JQuery} [$target]
     * @param {boolean} [isSilent] Do not trigger the `check` event.
     */
    checkRecord(id, $target, isSilent) {
      if (this._disabledCheckboxes) {
        return;
      }
      if (!this.collection.get(id)) {
        return;
      }
      $target = $target || this.$el.find('.record-checkbox[data-id="' + id + '"]');
      if ($target.length) {
        $target.get(0).checked = true;
        $target.closest('tr').addClass('active');
      }
      const index = this.checkedList.indexOf(id);
      if (index === -1) {
        this.checkedList.push(id);
      }
      this.handleAfterCheck(isSilent);
    }

    /**
     * Unselect a record.
     *
     * @param {string} id An ID.
     * @param {JQuery} [$target]
     * @param {boolean} [isSilent] Do not trigger the `check` event.
     */
    uncheckRecord(id, $target, isSilent) {
      $target = $target || this.$el.find('.record-checkbox[data-id="' + id + '"]');
      if ($target.get(0)) {
        $target.get(0).checked = false;
        $target.closest('tr').removeClass('active');
      }
      const index = this.checkedList.indexOf(id);
      if (index !== -1) {
        this.checkedList.splice(index, 1);
      }
      this.handleAfterCheck(isSilent);
    }

    /**
     * @protected
     * @param {boolean} [isSilent]
     */
    handleAfterCheck(isSilent) {
      if (this.checkedList.length) {
        this.showActions();
      } else {
        this.hideActions();
      }
      if (this.checkedList.length === this.collection.models.length) {
        this.$el.find('.select-all').prop('checked', true);
      } else {
        this.$el.find('.select-all').prop('checked', false);
      }
      if (!isSilent) {
        this.trigger('check');
      }
    }

    /**
     * Get row-actions defs.
     *
     * @return {Object}
     */
    getRowActionsDefs() {
      const options = {
        defs: {
          params: {}
        },
        additionalActionList: this._additionalRowActionList || [],
        scope: this.scope
      };
      if (this.options.rowActionsOptions) {
        for (const item in this.options.rowActionsOptions) {
          options[item] = this.options.rowActionsOptions[item];
        }
      }
      return {
        columnName: 'buttons',
        name: 'buttonsField',
        view: this.rowActionsView,
        options: options
      };
    }

    /**
     * Is all-result is checked.
     *
     * @return {boolean}
     */
    isAllResultChecked() {
      return this.allResultIsChecked;
    }

    /**
     * Get checked record IDs.
     *
     * @return {string[]}
     */
    getCheckedIds() {
      return Espo.Utils.clone(this.checkedList);
    }

    /**
     * Get selected models.
     *
     * @return {module:model[]}
     */
    getSelected() {
      const list = [];
      this.$el.find('input.record-checkbox:checked').each((i, el) => {
        const id = $(el).attr('data-id');
        const model = this.collection.get(id);
        list.push(model);
      });
      return list;
    }

    /** @protected */
    getInternalLayoutForModel(callback, model) {
      const scope = model.entityType;
      if (this._internalLayout === null) {
        this._internalLayout = {};
      }
      if (!(scope in this._internalLayout)) {
        this._internalLayout[scope] = this._convertLayout(this.listLayout[scope], model);
      }
      callback(this._internalLayout[scope]);
    }

    /** @protected */
    getInternalLayout(callback, model) {
      if (this.scope === null && !Array.isArray(this.listLayout)) {
        if (!model) {
          callback(null);
          return;
        }
        this.getInternalLayoutForModel(callback, model);
        return;
      }
      if (this._internalLayout !== null) {
        callback(this._internalLayout);
        return;
      }
      if (this.listLayout !== null) {
        this._internalLayout = this._convertLayout(this.listLayout);
        callback(this._internalLayout);
        return;
      }
      this._loadListLayout(listLayout => {
        this.listLayout = listLayout;
        this._internalLayout = this._convertLayout(listLayout);
        callback(this._internalLayout);
      });
    }

    /**
     * Compose a cell selector for a layout item.
     *
     * @param {import('model').default} model A model.
     * @param {Record} item An item.
     * @return {string}
     */
    getCellSelector(model, item) {
      return `${this.getSelector()} ${this.getRowSelector(model.id)} .cell[data-name="${item.columnName}"]`;
    }

    /**
     * @protected
     * @param {Record.<string, *>[]} internalLayout
     * @param {import('model').default} model
     */
    prepareInternalLayout(internalLayout, model) {
      internalLayout.forEach(item => {
        item.fullSelector = this.getCellSelector(model, item);
        if (this.header && item.options && item.options.defs) {
          item.options.defs.width = undefined;
          item.options.defs.widthPx = undefined;
        }
      });
    }

    /**
     * Build a row.
     *
     * @param {number} i An index.
     * @param {module:model} model A model.
     * @param {function(module:view):void} [callback] A callback.
     */
    buildRow(i, model, callback) {
      const key = model.id;
      this.rowList.push(key);
      this.getInternalLayout(internalLayout => {
        internalLayout = Espo.Utils.cloneDeep(internalLayout);
        this.prepareInternalLayout(internalLayout, model);
        const acl = {
          edit: this.getAcl().checkModel(model, 'edit') && !this.editDisabled,
          delete: this.getAcl().checkModel(model, 'delete') && !this.removeDisabled
        };
        this.createView(key, 'views/base', {
          model: model,
          acl: acl,
          rowActionHandlers: this._rowActionHandlers || {},
          selector: this.getRowSelector(key),
          optionsToPass: ['acl', 'rowActionHandlers'],
          layoutDefs: {
            type: this._internalLayoutType,
            layout: internalLayout
          },
          setViewBeforeCallback: this.options.skipBuildRows && !this.isRendered()
        }, callback);
      }, model);
    }

    /**
     * Build rows.
     *
     * @param {function():void} [callback] A callback.
     */
    buildRows(callback) {
      this.checkedList = [];

      /**
       * @internal
       * @type {string[]}
       * @private
       */
      this.rowList = [];
      if (this.collection.length <= 0) {
        if (typeof callback === 'function') {
          callback();
          this.trigger('after:build-rows');
        }
        return;
      }
      this.wait(true);
      const modelList = this.collection.models;
      let counter = 0;
      modelList.forEach((model, i) => {
        this.buildRow(i, model, () => {
          counter++;
          if (counter !== modelList.length) {
            return;
          }
          if (typeof callback === 'function') {
            callback();
          }
          this.wait(false);
          this.trigger('after:build-rows');
        });
      });
    }

    /**
     * Show more records.
     *
     * @param {{skipNotify?: boolean}} [options]
     * @param {module:collection} [collection]
     * @param {JQuery} [$list]
     * @param {JQuery} [$showMore]
     * @param {function(): void} [callback] A callback.
     */
    showMoreRecords(options, collection, $list, $showMore, callback) {
      collection = collection || this.collection;
      $showMore = $showMore || this.$el.find('.show-more');
      $list = $list || this.$el.find(this.listContainerEl);
      options = options || {};
      const $container = this.$el.find('.list');
      $showMore.children('a').addClass('disabled');
      if (!options.skipNotify) {
        Espo.Ui.notifyWait();
      }
      const lengthBefore = collection.length;
      const final = () => {
        $showMore.parent().append($showMore);
        if (collection.hasMore()) {
          const moreCount = collection.total - collection.offset - collection.length - collection.lengthCorrection;
          this.$el.find('.more-count').text(this.getNumberUtil().formatInt(moreCount));
          $showMore.removeClass('hidden');
          $container.addClass('has-show-more');
        } else {
          $showMore.remove();
          $container.removeClass('has-show-more');
        }
        $showMore.children('a').removeClass('disabled');
        if (this.allResultIsChecked) {
          this.$el.find('input.record-checkbox').attr('disabled', 'disabled').prop('checked', true);
        }
        if (!options.skipNotify) {
          Espo.Ui.notify(false);
        }
        if (callback) {
          callback.call(this);
        }
        this.trigger('after:show-more', lengthBefore);
      };
      const initialCount = collection.length;
      const success = () => {
        if (!options.skipNotify) {
          Espo.Ui.notify(false);
        }
        $showMore.addClass('hidden');
        $container.removeClass('has-show-more');
        const rowCount = collection.length - initialCount;
        let rowsReady = 0;
        if (collection.length <= initialCount) {
          final();
        }
        for (let i = initialCount; i < collection.length; i++) {
          const model = collection.at(i);
          this.buildRow(i, model, view => {
            const model = view.model;
            const $existingRow = this.getDomRowItem(model.id);
            if ($existingRow && $existingRow.length) {
              $existingRow.remove();
            }
            $list.append(this.getRowContainerHtml(model.id));
            view.render().then(() => {
              rowsReady++;
              if (rowsReady === rowCount) {
                final();
              }
            });
          });
        }
        this.noRebuild = true;
      };
      const onUpdate = (c, /** Record */o) => {
        if (o.changes.merged.length) {
          collection.lengthCorrection += o.changes.merged.length;
        }
      };
      this.listenToOnce(collection, 'update', onUpdate);

      // If using promise callback, then need to pass `noRebuild: true`.
      collection.fetch({
        success: success,
        remove: false,
        more: true
      }).catch(() => this.stopListening(collection, 'update', onUpdate));
    }
    getDomRowItem(id) {
      return null;
    }

    /**
     * Compose a row-container HTML.
     *
     * @param {string} id A record ID.
     * @return {string} HTML.
     */
    getRowContainerHtml(id) {
      return $('<tr>').attr('data-id', id).addClass('list-row').get(0).outerHTML;
    }

    /**
     * @protected
     * @param {{id?: string, scope?: string}} [data]
     */
    async actionQuickView(data) {
      data = data || {};
      const id = data.id;
      if (!id) {
        console.error("No id.");
        return;
      }
      let model = null;
      if (this.collection) {
        model = this.collection.get(id);
      }
      let scope = data.scope;
      if (!scope && model) {
        scope = model.entityType;
      }
      if (!scope) {
        scope = this.scope;
      }
      if (!scope) {
        console.error("No scope.");
        return;
      }
      if (this.quickDetailDisabled) {
        this.getRouter().navigate(`#${scope}/view/${id}`, {
          trigger: true
        });
        return;
      }
      const rootUrl = this.options.keepCurrentRootUrl ? this.getRouter().getCurrentUrl() : undefined;
      const helper = new _recordModal.default();
      await helper.showDetail(this, {
        id: id,
        entityType: scope,
        model: model,
        rootUrl: rootUrl,
        editDisabled: this.quickEditDisabled,
        afterSave: m => {
          if (!model) {
            return;
          }
          this.trigger('after:save', m);
        },
        afterDestroy: m => {
          if (!model) {
            return;
          }
          this.removeRecordFromList(m.id);
        }
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @param {Object.<string, *>} data
     */
    async actionQuickEdit(data) {
      data = data || {};
      const id = data.id;
      if (!id) {
        console.error("No id.");
        return;
      }
      let model = null;
      if (this.collection) {
        model = this.collection.get(id);
      }
      let scope = data.scope;
      if (!scope && model) {
        scope = model.entityType;
      }
      if (!scope) {
        scope = this.scope;
      }
      if (!scope) {
        console.error("No scope.");
        return;
      }
      if (!this.quickEditDisabled) {
        const helper = new _recordModal.default();
        const rootUrl = this.options.keepCurrentRootUrl ? this.getRouter().getCurrentUrl() : undefined;
        await helper.showEdit(this, {
          entityType: scope,
          id: id,
          model: model,
          fullFormDisabled: data.noFullForm,
          rootUrl: rootUrl,
          afterSave: m => {
            const model = this.collection.get(m.id);
            if (model) {
              model.set(m.getClonedAttributes(), {
                sync: true
              });
            }
            this.trigger('after:save', m);
          },
          returnDispatchParams: {
            controller: scope,
            action: null,
            options: {
              isReturn: true
            }
          }
        });
        return;
      }
      const options = {
        id: id,
        model: this.collection.get(id),
        returnUrl: this.getRouter().getCurrentUrl(),
        returnDispatchParams: {
          controller: scope,
          action: null,
          options: {
            isReturn: true
          }
        }
      };
      if (this.options.keepCurrentRootUrl) {
        options.rootUrl = this.getRouter().getCurrentUrl();
      }
      this.getRouter().navigate(`#${scope}/edit/${id}`, {
        trigger: false
      });
      this.getRouter().dispatch(scope, 'edit', options);
    }

    /**
     * Compose a row selector.
     *
     * @param {string} id A record ID.
     * @return {string}
     */
    getRowSelector(id) {
      return `tr.list-row[data-id="${id}"]`;
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @protected
     * @param {{id?: string}} [data]
     * @return {Promise<void>}
     */
    async actionQuickRemove(data) {
      data = data || {};
      const id = data.id;
      if (!id) {
        return;
      }
      const model = this.collection.get(id);
      const index = this.collection.indexOf(model);
      if (!model) {
        throw new Error("No model.");
      }
      if (!this.getAcl().checkModel(model, 'delete')) {
        Espo.Ui.error(this.translate('Access denied'));
        return;
      }
      await this.confirm({
        message: this.translate('removeRecordConfirmation', 'messages', this.scope),
        confirmText: this.translate('Remove')
      });
      this.collection.trigger('model-removing', id);
      this.collection.remove(model);
      Espo.Ui.notifyWait();
      try {
        await model.destroy({
          wait: true,
          fromList: true
        });
      } catch (e) {
        if (!this.collection.models.includes(model)) {
          this.collection.add(model, {
            at: index
          });
        }
        return;
      }
      Espo.Ui.success(this.translate('Removed'));
      this.trigger('after:delete', model);
      this.removeRecordFromList(id);
    }

    /**
     * @protected
     * @param {string} id An ID.
     */
    removeRecordFromList(id) {
      if (this.collection.total > 0) {
        this.collection.total--;
        this.collection.trigger('update-total');
      }
      this.collection.remove(id);
      this.$el.find('.total-count-span').text(this.collection.total.toString());
      let index = this.checkedList.indexOf(id);
      if (index !== -1) {
        this.checkedList.splice(index, 1);
      }
      const key = id;
      this.clearView(key);
      index = this.rowList.indexOf(key);
      if (~index) {
        this.rowList.splice(index, 1);
      }
      this.removeRowHtml(id);
    }

    /**
     * @protected
     * @param {string} id An ID.
     */
    removeRowHtml(id) {
      this.$el.find(this.getRowSelector(id)).remove();
      if (this.collection.length === 0 && (this.collection.total === 0 || this.collection.total === -2)) {
        this.reRender();
      }
    }

    /**
     * @public
     * @param {string} id An ID.
     * @return {boolean}
     */
    isIdChecked(id) {
      return this.checkedList.indexOf(id) !== -1;
    }
    setupRowActionDefs() {
      this._rowActionHandlers = {};
      const list = this.options.additionalRowActionList;
      if (!list) {
        return;
      }
      this._additionalRowActionList = list;
      const defs = this.getMetadata().get(`clientDefs.${this.scope}.rowActionDefs`) || {};
      const promiseList = list.map(action => {
        /** @type {{handler: string, label?: string, labelTranslation?: string, groupIndex?: number}} */
        const itemDefs = defs[action] || {};
        if (!itemDefs.handler) {
          return Promise.resolve();
        }
        return Espo.loader.requirePromise(itemDefs.handler).then(Handler => {
          this._rowActionHandlers[action] = new Handler(this);
          return true;
        });
      });
      this.wait(Promise.all(promiseList));
    }

    // noinspection JSUnusedGlobalSymbols
    actionRowAction(data) {
      const action = data.actualAction;
      const id = data.id;
      if (!action) {
        return;
      }

      /** @type {{process: function(module:model, string)}} */
      const handler = (this._rowActionHandlers || {})[action];
      if (!handler) {
        console.warn(`No handler for action ${action}.`);
        return;
      }
      const model = this.collection.get(id);
      if (!model) {
        return;
      }
      handler.process(model, action);
    }

    /** @protected */
    setupSettings() {
      if (!this.options.settingsEnabled || !this.collection.entityType || !this.layoutName) {
        return;
      }
      if (!this.forceSettings && !this.getMetadata().get(`scopes.${this.entityType}.object`) || this.getConfig().get('listViewSettingsDisabled')) {
        return;
      }
      if (this.settingsDisabled) {
        return;
      }
      this._listSettingsHelper = this.options.settingsHelper || new _settings2.default(this.entityType, this.layoutName, this.getUser().id, this.getStorage());
      const view = new _settings.default({
        layoutProvider: () => this.listLayout,
        helper: this._listSettingsHelper,
        entityType: this.entityType,
        columnResize: this.columnResize,
        onChange: options => this.afterSettingsChange(options)
      });
      this.assignView('settings', view, '.settings-container');
    }

    /**
     * @protected
     * @param {RecordListSettingsView~onChangeOptions} options
     */
    async afterSettingsChange(options) {
      if (options.action === 'toggleColumnResize') {
        await this.reRender();
        return;
      }
      if (options.action === 'toggleColumn' && !this._listSettingsHelper.getHiddenColumnMap()[options.column] && this._columnResizeHelper) {
        const helper = new _columnWidthControl.default({
          view: this,
          helper: this._listSettingsHelper,
          layoutProvider: () => this.listLayout
        });
        helper.adjust();
      }
      this._internalLayout = null;
      Espo.Ui.notifyWait();
      await this.collection.fetch();
      Espo.Ui.notify(false);
    }

    /**
     * Whether the pagination is enabled.
     *
     * @return {boolean}
     */
    hasPagination() {
      return this.pagination;
    }

    /**
     * Hide a mass action. Requires re-render.
     *
     * @protected
     * @param {string} name An action name.
     * @since 8.4.0
     */
    hideMassAction(name) {
      if (!this.massActionDefs[name]) {
        this.massActionDefs[name] = {};
      }
      this.massActionDefs[name].hidden = true;
    }

    /**
     * Show a mass action. Requires re-render.
     *
     * @protected
     * @param {string} name An action name.
     * @since 8.4.0
     */
    showMassAction(name) {
      if (!this.massActionDefs[name]) {
        this.massActionDefs[name] = {};
      }
      this.massActionDefs[name].hidden = false;
    }

    /**
     * @private
     * @return {Array<{name: string, hidden: boolean}|false>}
     */
    getMassActionDataList() {
      /** @type {string[][]} */
      const groups = [];
      this.massActionList.forEach(action => {
        const item = this.massActionDefs[action];

        // For bc.
        if (item === false) {
          return;
        }
        const index = (!item || item.groupIndex === undefined ? 9999 : item.groupIndex) + 100;
        if (groups[index] === undefined) {
          groups[index] = [];
        }
        groups[index].push(action);
      });
      const list = [];
      groups.forEach(subList => {
        subList.forEach(it => list.push(it));
        list.push(false);
      });
      return list.map(name => {
        if (name === false) {
          return false;
        }
        return {
          name,
          hidden: (this.massActionDefs[name] || {}).hidden
        };
      });
    }

    /**
     * Uncheck all.
     *
     * @since 8.4.0
     */
    uncheckAll() {
      if (this.allResultIsChecked) {
        this.unselectAllResult();
      }
      this.checkedList.forEach(id => this.uncheckRecord(id));
    }

    /**
     * To temporarily disable checkboxes.
     *
     * @since 8.4.0
     */
    disableCheckboxes() {
      if (!this.checkboxes) {
        return;
      }
      this._disabledCheckboxes = true;
      this.uncheckAll();
      this.$el.find('input.record-checkbox').attr('disabled', 'disabled');
      if (this.$selectAllCheckbox) {
        this.$selectAllCheckbox.attr('disabled', 'disabled');
      }
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * To enabled temporarily disabled checkboxes.
     *
     * @since 8.4.0
     */
    enableCheckboxes() {
      if (!this.checkboxes) {
        return;
      }
      this._disabledCheckboxes = false;
      this.$el.find('input.record-checkbox').removeAttr('disabled');
      if (this.$selectAllCheckbox) {
        this.$selectAllCheckbox.removeAttr('disabled');
      }
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Checkboxes are disabled.
     *
     * @since 9.0.1
     * @return {boolean}
     */
    checkboxesAreDisabled() {
      return this._disabledCheckboxes || !this.checkboxes;
    }

    /**
     * Rebuild the internal layout.
     *
     * @return {Promise}
     * @since 8.4.0
     */
    rebuild() {
      return new Promise(resolve => {
        this._internalLayout = null;
        this.buildRows(() => resolve());
      });
    }
  }
  var _default = _exports.default = ListRecordView;
});

define("views/fields/text", ["exports", "views/fields/base", "helpers/misc/mailto", "views/modals/text-preview"], function (_exports, _base, _mailto, _textPreview) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _mailto = _interopRequireDefault(_mailto);
  _textPreview = _interopRequireDefault(_textPreview);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/text */

  /**
   * A text field.
   *
   * @extends BaseFieldView<module:views/fields/text~params>
   */
  class TextFieldView extends _base.default {
    /**
     * @typedef {Object} module:views/fields/text~options
     * @property {
     *     module:views/fields/text~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/text~params
     * @property {boolean} [required] Required.
     * @property {number} [maxLength] A max length.
     * @property {number} [rows] A number of rows.
     * @property {number} [rowsMin] A min number of rows.
     * @property {boolean} [noResize] No resize.
     * @property {boolean} [seeMoreDisabled] Disable 'See-more'.
     * @property {boolean} [autoHeightDisabled] Disable auto-height.
     * @property {number} [cutHeight] A height of cut in pixels.
     * @property {boolean} [displayRawText] Display raw text.
     * @property {boolean} [preview] Display the preview button.
     * @property {string} [attachmentField] An attachment-multiple field to connect with.
     */

    /**
     * @param {
     *     module:views/fields/text~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'text';
    listTemplate = 'fields/text/list';
    detailTemplate = 'fields/text/detail';
    editTemplate = 'fields/text/edit';
    searchTemplate = 'fields/text/search';

    /**
     * Show-more is applied.
     * @type {boolean}
     */
    seeMoreText = false;
    rowsDefault = 50000;
    rowsMin = 2;
    seeMoreDisabled = false;
    cutHeight = 200;
    noResize = false;
    changeInterval = 5;
    shrinkThreshold = 10;
    searchTypeList = ['contains', 'startsWith', 'equals', 'endsWith', 'like', 'notContains', 'notLike', 'isEmpty', 'isNotEmpty'];

    /** @private */
    _lastLength;

    /** @private */
    maxRows;

    /**
     * @private
     * @type {HTMLElement}
     */
    previewButtonElement;

    /**
     * @private
     * @type {HTMLTextAreaElement}
     */
    textAreaElement;
    setup() {
      super.setup();
      this.addActionHandler('seeMoreText', () => this.seeMore());
      this.addActionHandler('mailTo', (e, target) => this.mailTo(target.dataset.emailAddress));
      this.maxRows = this.params.rows || this.rowsDefault;
      this.noResize = this.options.noResize || this.params.noResize || this.noResize;
      this.seeMoreDisabled = this.seeMoreDisabled || this.params.seeMoreDisabled;
      this.autoHeightDisabled = this.options.autoHeightDisabled || this.params.autoHeightDisabled || this.autoHeightDisabled;
      if (this.params.cutHeight) {
        this.cutHeight = this.params.cutHeight;
      }
      this.rowsMin = this.options.rowsMin || this.params.rowsMin || this.rowsMin;
      if (this.maxRows < this.rowsMin) {
        this.rowsMin = this.maxRows;
      }
      this.on('remove', () => {
        $(window).off('resize.see-more-' + this.cid);
        if (this.textAreaElement) {
          this.textAreaElement.removeEventListener('keydown', this.onKeyDownMarkdownBind);
        }
      });
      if (this.params.preview) {
        this.addHandler('input', 'textarea', (e, /** HTMLTextAreaElement */target) => {
          const text = target.value;
          if (!this.previewButtonElement) {
            return;
          }
          if (text) {
            this.previewButtonElement.classList.remove('hidden');
          } else {
            this.previewButtonElement.classList.add('hidden');
          }
        });
        this.addActionHandler('previewText', () => this.preview());
      }
      this.listenTo(this.model, `change:${this.name}`, (m, v, /** Record*/o) => {
        if (o.ui === true && this.mode === this.MODE_EDIT) {
          // After changing the field content it's reasonable to show all text
          // when returning to the detail mode.
          this.seeMoreText = true;
        }
      });

      /** @private */
      this.controlSeeMoreBind = this.controlSeeMore.bind(this);
      /** @private */
      this.onPasteBind = this.onPaste.bind(this);
      /** @private */
      this.onKeyDownMarkdownBind = this.onKeyDownMarkdown.bind(this);
    }
    setupSearch() {
      this.events['change select.search-type'] = e => {
        const type = $(e.currentTarget).val();
        this.handleSearchType(type);
      };
    }

    // noinspection JSCheckFunctionSignatures
    data() {
      const data = super.data();
      if (this.model.get(this.name) !== null && this.model.get(this.name) !== '' && this.model.has(this.name)) {
        data.isNotEmpty = true;
      }
      if (this.mode === this.MODE_SEARCH) {
        if (typeof this.searchParams.value === 'string') {
          this.searchData.value = this.searchParams.value;
        }
      }
      if (this.mode === this.MODE_EDIT) {
        data.rows = this.autoHeightDisabled ? this.maxRows : this.rowsMin;
      }
      data.valueIsSet = this.model.has(this.name);
      if (this.isReadMode()) {
        data.isCut = this.isCut();
        if (data.isCut) {
          data.cutHeight = this.cutHeight;
        }
        data.displayRawText = this.params.displayRawText;
      }
      data.htmlValue = undefined;
      data.noResize = this.noResize || !this.autoHeightDisabled && !this.params.rows;
      data.preview = this.params.preview && !this.params.displayRawText;

      // noinspection JSValidateTypes
      return data;
    }
    handleSearchType(type) {
      if (~['isEmpty', 'isNotEmpty'].indexOf(type)) {
        this.$el.find('input.main-element').addClass('hidden');
      } else {
        this.$el.find('input.main-element').removeClass('hidden');
      }
    }
    getValueForDisplay() {
      const text = this.model.get(this.name);
      return text || '';
    }

    /**
     * @public
     * @param {Number} [lastHeight]
     */
    controlTextareaHeight(lastHeight) {
      const scrollHeight = this.$element.prop('scrollHeight');
      const clientHeight = this.$element.prop('clientHeight');
      if (typeof lastHeight === 'undefined' && clientHeight === 0) {
        setTimeout(this.controlTextareaHeight.bind(this), 10);
        return;
      }

      /** @type {HTMLTextAreaElement} */
      const element = this.$element.get(0);
      if (!element || element.value === undefined) {
        return;
      }
      const length = element.value.length;
      if (this._lastLength === undefined) {
        this._lastLength = length;
      }
      if (length > this._lastLength) {
        this._lastLength = length;
      }
      if (clientHeight === lastHeight) {
        // @todo Revise.
        return;
      }
      if (scrollHeight > clientHeight + 1) {
        const rows = element.rows;
        if (this.maxRows && rows >= this.maxRows) {
          return;
        }
        element.rows++;
        this.controlTextareaHeight(clientHeight);
        return;
      }
      if (this.$element.val().length === 0) {
        element.rows = this.rowsMin;
        return;
      }
      const tryShrink = () => {
        const rows = element.rows;
        if (this.rowsMin && rows - 1 <= this.rowsMin) {
          return;
        }
        element.rows--;
        if (element.scrollHeight > element.clientHeight + 1) {
          this.controlTextareaHeight();
          return;
        }
        tryShrink();
      };
      if (length < this._lastLength - this.shrinkThreshold) {
        this._lastLength = length;
        tryShrink();
      }
    }
    isCut() {
      return !this.seeMoreText && !this.seeMoreDisabled;
    }
    controlSeeMore() {
      if (!this.isCut()) {
        return;
      }
      if (this.$text.height() > this.cutHeight) {
        this.$seeMoreContainer.removeClass('hidden');
        this.$textContainer.addClass('cut');
      } else {
        this.$seeMoreContainer.addClass('hidden');
        this.$textContainer.removeClass('cut');
      }
    }
    afterRender() {
      super.afterRender();
      if (this.isReadMode()) {
        $(window).off('resize.see-more-' + this.cid);
        this.$textContainer = this.$el.find('> .complex-text-container');
        this.$text = this.$textContainer.find('> .complex-text');
        this.$seeMoreContainer = this.$el.find('> .see-more-container');
        if (this.isCut()) {
          this.controlSeeMore();
          if (this.model.get(this.name) && this.$text.height() === 0) {
            this.$textContainer.addClass('cut');
            setTimeout(this.controlSeeMore.bind(this), 50);
          }
          this.listenTo(this.recordHelper, 'panel-show', () => this.controlSeeMore());
          this.on('panel-show-propagated', () => this.controlSeeMore());
          $(window).on('resize.see-more-' + this.cid, () => {
            this.controlSeeMore();
          });

          // @todo Revise stream post with empty text.

          this.element.querySelectorAll('img').forEach(image => {
            image.addEventListener('load', this.controlSeeMoreBind);
          });
        }
      }
      if (this.mode === this.MODE_EDIT) {
        const text = this.getValueForDisplay();
        if (text) {
          this.$element.val(text);
        }
        this.previewButtonElement = this.element ? this.element.querySelector('a[data-action="previewText"]') : undefined;
        this.textAreaElement = this.element ? this.element.querySelector('textarea') : undefined;
        const textAreaElement = this.textAreaElement;
        if (this.params.attachmentField && textAreaElement) {
          textAreaElement.removeEventListener('paste', this.onPasteBind);
          textAreaElement.addEventListener('paste', this.onPasteBind);
        }
        if (!this.params.displayRawText) {
          this.initTextareaMarkdownHelper();
        }
      }
      if (this.mode === this.MODE_SEARCH) {
        const type = this.$el.find('select.search-type').val();
        this.handleSearchType(type);
        this.$el.find('select.search-type').on('change', () => {
          this.trigger('change');
        });
        this.$element.on('input', () => {
          this.trigger('change');
        });
      }
      if (this.mode === this.MODE_EDIT && !this.autoHeightDisabled) {
        if (!this.autoHeightDisabled) {
          this.controlTextareaHeight();
          this.$element.on('input', () => this.controlTextareaHeight());
        }
        let lastChangeKeydown = new Date();
        const changeKeydownInterval = this.changeInterval * 1000;
        this.$element.on('keydown', () => {
          if (Date.now() - lastChangeKeydown > changeKeydownInterval) {
            this.trigger('change');
            lastChangeKeydown = Date.now();
          }
        });
      }
    }
    fetch() {
      const data = {};
      let value = this.$element.val() || null;
      if (value && value.trim() === '') {
        value = '';
      }
      data[this.name] = value;
      return data;
    }
    fetchSearch() {
      const type = this.fetchSearchType() || 'startsWith';
      if (type === 'isEmpty') {
        return {
          type: 'or',
          value: [{
            type: 'isNull',
            field: this.name
          }, {
            type: 'equals',
            field: this.name,
            value: ''
          }],
          data: {
            type: type
          }
        };
      }
      if (type === 'isNotEmpty') {
        return {
          type: 'and',
          value: [{
            type: 'notEquals',
            field: this.name,
            value: ''
          }, {
            type: 'isNotNull',
            field: this.name,
            value: null
          }],
          data: {
            type: type
          }
        };
      }
      const value = this.$element.val().toString().trim();
      if (value) {
        return {
          value: value,
          type: type
        };
      }
      return false;
    }
    getSearchType() {
      return this.getSearchParamsData().type || this.searchParams.typeFront || this.searchParams.type;
    }
    mailTo(emailAddress) {
      const attributes = {
        status: 'Draft',
        to: emailAddress
      };
      const helper = new _mailto.default(this.getConfig(), this.getPreferences(), this.getAcl());
      if (helper.toUse()) {
        document.location.href = helper.composeLink(attributes);
        return;
      }
      const viewName = this.getMetadata().get('clientDefs.' + this.scope + '.modalViews.compose') || 'views/modals/compose-email';
      Espo.Ui.notifyWait();
      this.createView('quickCreate', viewName, {
        attributes: attributes
      }, view => {
        view.render();
        Espo.Ui.notify(false);
      });
    }

    /**
     * Show the preview modal.
     *
     * @since 9.0.0
     * @return {Promise<void>}
     */
    async preview() {
      const text = this.model.attributes[this.name] || '';
      const view = new _textPreview.default({
        text: text
      });
      await this.assignView('modal', view);
      await view.render();
    }

    /**
     * @protected
     * @param {ClipboardEvent} event
     */
    onPaste(event) {
      const items = event.clipboardData.items;
      if (!items) {
        return;
      }
      for (let i = 0; i < items.length; i++) {
        if (!items[i].type.startsWith('image')) {
          continue;
        }
        const blob = items[i].getAsFile();
        this.recordHelper.trigger('upload-files:' + this.params.attachmentField, [blob]);
      }
    }

    /**
     * @return {Promise}
     * @since 9.0.0
     */
    async seeMore() {
      this.seeMoreText = true;
      await this.reRender();
    }

    /**
     * @private
     */
    initTextareaMarkdownHelper() {
      if (!this.textAreaElement) {
        return;
      }
      this.textAreaElement.addEventListener('keydown', this.onKeyDownMarkdownBind);
    }

    /**
     * @type {boolean}
     * @private
     */
    _lastEnteredKeyIsEnter = false;

    /**
     * @private
     * @param {KeyboardEvent} event
     */
    onKeyDownMarkdown(event) {
      if (Espo.Utils.getKeyFromKeyEvent(event) !== 'Enter') {
        this._lastEnteredKeyIsEnter = false;
        return;
      }
      const lastEnteredKeyIsEnter = this._lastEnteredKeyIsEnter;
      this._lastEnteredKeyIsEnter = true;
      const target = event.target;
      if (!(target instanceof HTMLTextAreaElement)) {
        return;
      }
      const {
        selectionStart,
        selectionEnd,
        value
      } = target;
      const before = value.substring(0, selectionStart);
      const after = value.substring(selectionEnd);

      // Last line, a list item syntax.
      const match = before.match(/(^|\n)( *[-*]|\d+\.) ([^*\-\n]*)$/);
      if (!match) {
        // Prevent unwanted scroll applied by the browser on enter.
        const previousWindowScroll = window.scrollY;
        setTimeout(() => window.scrollTo({
          top: previousWindowScroll
        }), 0);
        this.controlTextareaHeight();
        return;
      }
      event.preventDefault();
      if (match[3].trim() === '' && lastEnteredKeyIsEnter) {
        target.value = before.substring(0, match.index) + '\n' + after;
        target.selectionStart = target.selectionEnd = target.value.length - after.length;
        this.controlTextareaHeight();
        return;
      }
      let itemPart = match[2];
      const matchPart = itemPart.match(/( *)(\d+)/);
      if (matchPart) {
        const number = parseInt(matchPart[2]);
        if (!isNaN(number)) {
          itemPart = matchPart[1] + (number + 1).toString() + '.';
        }
      }
      const newLine = "\n" + itemPart + " ";
      target.value = before + newLine + after;
      target.selectionStart = target.selectionEnd = target.value.length - after.length;
      this.controlTextareaHeight();
    }
  }
  var _default = _exports.default = TextFieldView;
});

define("views/fields/int", ["exports", "views/fields/base", "autonumeric"], function (_exports, _base, _autonumeric) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _autonumeric = _interopRequireDefault(_autonumeric);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/int */

  /**
   * An integer field.
   *
   * @extends BaseFieldView<module:views/fields/int~params>
   */
  class IntFieldView extends _base.default {
    /**
     * @typedef {Object} module:views/fields/int~options
     * @property {
     *     module:views/fields/int~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/int~params
     * @property {number} [min] A max value.
     * @property {number} [max] A max value.
     * @property {boolean} [required] Required.
     * @property {boolean} [disableFormatting] Disable formatting.
     */

    /**
     * @param {
     *     module:views/fields/int~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'int';
    listTemplate = 'fields/int/list';
    detailTemplate = 'fields/int/detail';
    editTemplate = 'fields/int/edit';
    searchTemplate = 'fields/int/search';

    /**
     * @inheritDoc
     * @type {Array<(function (): boolean)|string>}
     */
    validations = ['required', 'int', 'range'];
    thousandSeparator = ',';
    searchTypeList = ['isNotEmpty', 'isEmpty', 'equals', 'notEquals', 'greaterThan', 'lessThan', 'greaterThanOrEquals', 'lessThanOrEquals', 'between'];

    /**
     * @type {import('autonumeric').Options}
     * @protected
     */
    autoNumericOptions;

    /**
     * @type {?AutoNumeric}
     * @protected
     */
    autoNumericInstance = null;
    setup() {
      super.setup();
      this.setupMaxLength();
      if (this.getPreferences().has('thousandSeparator')) {
        this.thousandSeparator = this.getPreferences().get('thousandSeparator');
      } else if (this.getConfig().has('thousandSeparator')) {
        this.thousandSeparator = this.getConfig().get('thousandSeparator');
      }
      if (this.params.disableFormatting) {
        this.disableFormatting = true;
      }
    }
    setupFinal() {
      super.setupFinal();
      this.setupAutoNumericOptions();
    }

    /**
     * @protected
     */
    setupAutoNumericOptions() {
      const separator = (!this.disableFormatting ? this.thousandSeparator : null) || '';
      let decimalCharacter = '.';
      if (separator === '.') {
        decimalCharacter = ',';
      }

      // noinspection JSValidateTypes
      this.autoNumericOptions = {
        digitGroupSeparator: separator,
        decimalCharacter: decimalCharacter,
        modifyValueOnWheel: false,
        decimalPlaces: 0,
        selectOnFocus: false,
        formulaMode: true
      };
      if (this.params.max != null && this.params.max > Math.pow(10, 6)) {
        this.autoNumericOptions.maximumValue = this.params.max.toString();
      }
      if (this.params.min != null && this.params.min < -Math.pow(10, 6)) {
        this.autoNumericOptions.minimumValue = this.params.min.toString();
      }
    }
    afterRender() {
      super.afterRender();
      if (this.mode === this.MODE_EDIT) {
        if (this.autoNumericOptions) {
          /** @type {HTMLInputElement} */
          const element = this.$element.get(0);
          this.autoNumericInstance = new _autonumeric.default(element, this.autoNumericOptions);
        }
      }
      if (this.mode === this.MODE_SEARCH) {
        const $searchType = this.$el.find('select.search-type');
        this.handleSearchType($searchType.val());
        this.$el.find('select.search-type').on('change', () => {
          this.trigger('change');
        });
        this.$element.on('input', () => {
          this.trigger('change');
        });
        const $inputAdditional = this.$el.find('input.additional');
        $inputAdditional.on('input', () => {
          this.trigger('change');
        });
        if (this.autoNumericOptions) {
          /** @type {HTMLInputElement} */
          const element1 = this.$element.get(0);
          /** @type {HTMLInputElement} */
          const element2 = $inputAdditional.get(0);
          new _autonumeric.default(element1, this.autoNumericOptions);
          new _autonumeric.default(element2, this.autoNumericOptions);
        }
      }
    }

    // noinspection JSCheckFunctionSignatures
    data() {
      const data = super.data();
      if (this.model.get(this.name) !== null && typeof this.model.get(this.name) !== 'undefined') {
        data.isNotEmpty = true;
      }
      data.valueIsSet = this.model.has(this.name);
      if (this.isSearchMode()) {
        data.value = this.searchParams.value;
        if (this.getSearchType() === 'between') {
          data.value = this.getSearchParamsData().value1 || this.searchParams.value1;
          data.value2 = this.getSearchParamsData().value2 || this.searchParams.value2;
        }
      }
      if (this.isEditMode()) {
        data.value = this.model.get(this.name);
      }

      // noinspection JSValidateTypes
      return data;
    }
    getValueForDisplay() {
      const value = isNaN(this.model.get(this.name)) ? null : this.model.get(this.name);
      return this.formatNumber(value);
    }
    formatNumber(value) {
      if (this.disableFormatting) {
        return value;
      }
      return this.formatNumberDetail(value);
    }
    formatNumberDetail(value) {
      if (value === null) {
        return '';
      }
      let stringValue = value.toString();
      stringValue = stringValue.replace(/\B(?=(\d{3})+(?!\d))/g, this.thousandSeparator);
      return stringValue;
    }
    setupSearch() {
      this.events['change select.search-type'] = e => {
        // noinspection JSUnresolvedReference
        this.handleSearchType($(e.currentTarget).val());
      };
    }
    handleSearchType(type) {
      const $additionalInput = this.$el.find('input.additional');
      const $input = this.$el.find('input[data-name="' + this.name + '"]');
      if (type === 'between') {
        $additionalInput.removeClass('hidden');
        $input.removeClass('hidden');
      } else if (~['isEmpty', 'isNotEmpty'].indexOf(type)) {
        $additionalInput.addClass('hidden');
        $input.addClass('hidden');
      } else {
        $additionalInput.addClass('hidden');
        $input.removeClass('hidden');
      }
    }
    getMaxValue() {
      let maxValue = this.model.getFieldParam(this.name, 'max') || null;
      if (!maxValue && maxValue !== 0) {
        maxValue = null;
      }
      if ('max' in this.params) {
        maxValue = this.params.max;
      }
      return maxValue;
    }
    getMinValue() {
      let minValue = this.model.getFieldParam(this.name, 'min');
      if (!minValue && minValue !== 0) {
        minValue = null;
      }
      if ('min' in this.params) {
        minValue = this.params.min;
      }
      return minValue;
    }
    setupMaxLength() {
      let maxValue = this.getMaxValue();
      if (typeof max !== 'undefined' && max !== null) {
        maxValue = this.formatNumber(maxValue);
        this.params.maxLength = maxValue.toString().length;
      }
    }

    // noinspection JSUnusedGlobalSymbols
    validateInt() {
      const value = this.model.get(this.name);
      if (isNaN(value)) {
        const msg = this.translate('fieldShouldBeInt', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg);
        return true;
      }
    }

    // noinspection JSUnusedGlobalSymbols
    validateRange() {
      const value = this.model.get(this.name);
      if (value === null) {
        return false;
      }
      const minValue = this.getMinValue();
      const maxValue = this.getMaxValue();
      if (minValue !== null && maxValue !== null) {
        if (value < minValue || value > maxValue) {
          const msg = this.translate('fieldShouldBeBetween', 'messages').replace('{field}', this.getLabelText()).replace('{min}', minValue).replace('{max}', maxValue);
          this.showValidationMessage(msg);
          return true;
        }
      } else {
        if (minValue !== null) {
          if (value < minValue) {
            const msg = this.translate('fieldShouldBeGreater', 'messages').replace('{field}', this.getLabelText()).replace('{value}', minValue);
            this.showValidationMessage(msg);
            return true;
          }
        } else if (maxValue !== null) {
          if (value > maxValue) {
            const msg = this.translate('fieldShouldBeLess', 'messages').replace('{field}', this.getLabelText()).replace('{value}', maxValue);
            this.showValidationMessage(msg);
            return true;
          }
        }
      }
    }
    validateRequired() {
      if (this.isRequired()) {
        const value = this.model.get(this.name);
        if (value === null || value === false) {
          const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
          this.showValidationMessage(msg);
          return true;
        }
      }
    }
    parse(value) {
      value = value !== '' ? value : null;
      if (value === null) {
        return null;
      }
      value = value.split(this.thousandSeparator).join('');
      if (value.indexOf('.') !== -1 || value.indexOf(',') !== -1) {
        return NaN;
      }
      return parseInt(value);
    }
    fetch() {
      let value = this.$element.val();
      value = this.parse(value);
      const data = {};
      data[this.name] = value;
      return data;
    }
    fetchSearch() {
      const value = this.parse(this.$element.val());
      const type = this.fetchSearchType();
      let data;
      if (isNaN(value)) {
        return false;
      }
      if (type === 'between') {
        const valueTo = this.parse(this.$el.find('input.additional').val());
        if (isNaN(valueTo)) {
          return false;
        }
        data = {
          type: type,
          value: [value, valueTo],
          data: {
            value1: value,
            value2: valueTo
          }
        };
      } else if (type === 'isEmpty') {
        data = {
          type: 'isNull',
          typeFront: 'isEmpty'
        };
      } else if (type === 'isNotEmpty') {
        data = {
          type: 'isNotNull',
          typeFront: 'isNotEmpty'
        };
      } else {
        data = {
          type: type,
          value: value,
          data: {
            value1: value
          }
        };
      }
      return data;
    }
    getSearchType() {
      return this.searchParams.typeFront || this.searchParams.type;
    }
  }
  var _default = _exports.default = IntFieldView;
});

define("ui/datepicker", ["exports", "jquery", "language", "models/settings", "di", "moment"], function (_exports, _jquery, _language, _settings, _di, _moment) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _jquery = _interopRequireDefault(_jquery);
  _language = _interopRequireDefault(_language);
  _settings = _interopRequireDefault(_settings);
  _moment = _interopRequireDefault(_moment);
  let _init_language, _init_extra_language, _init_config, _init_extra_config;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * A datepicker.
   *
   * @since 9.0.0
   */
  class Datepicker {
    static #_ = [_init_language, _init_extra_language, _init_config, _init_extra_config] = _applyDecs(this, [], [[(0, _di.inject)(_language.default), 0, "language"], [(0, _di.inject)(_settings.default), 0, "config"]]).e;
    /**
     * @private
     * @type {Language}
     */
    language = _init_language(this);

    /**
     * @private
     * @type {Settings}
     */
    config = (_init_extra_language(this), _init_config(this));

    /**
     * @param {HTMLElement} element
     * @param {{
     *     format: string,
     *     weekStart: number,
     *     todayButton?: boolean,
     *     date?: string,
     *     startDate?: string|undefined,
     *     onChange?: function(),
     *     hasDay?: function(string): boolean,
     *     hasMonth?: function(string): boolean,
     *     hasYear?: function(string): boolean,
     *     onChangeDate?: function(),
     *     onChangeMonth?: function(string),
     *     defaultViewDate?: string,
     * }} options
     */
    constructor(element, options) {
      _init_extra_config(this);
      /**
       * @private
       */
      this.$element = (0, _jquery.default)(element);

      /**
       * @private
       * @type {string}
       */
      this.format = options.format;
      if (element instanceof HTMLInputElement) {
        if (options.date) {
          element.value = options.date;
        }
        let wait = false;
        this.$element.on('change', /** Record */e => {
          if (!wait) {
            if (options.onChange) {
              options.onChange();
            }
            wait = true;
            setTimeout(() => wait = false, 100);
          }
          if (e.isTrigger && document.activeElement !== this.$element.get(0)) {
            this.$element.focus();
          }
        });
        this.$element.on('click', () => this.show());
      } else {
        if (options.date) {
          element.dataset.date = options.date;
        }
      }
      const modalBodyElement = element.closest('.modal-body');
      const language = this.config.get('language');
      const format = options.format;
      const datepickerOptions = {
        autoclose: true,
        todayHighlight: true,
        keyboardNavigation: true,
        assumeNearbyYear: true,
        format: format.toLowerCase(),
        weekStart: options.weekStart,
        todayBtn: options.todayButton || false,
        startDate: options.startDate,
        orientation: 'bottom auto',
        templates: {
          leftArrow: '<span class="fas fa-chevron-left fa-sm"></span>',
          rightArrow: '<span class="fas fa-chevron-right fa-sm"></span>'
        },
        container: modalBodyElement ? (0, _jquery.default)(modalBodyElement) : 'body',
        language: language,
        maxViewMode: 2,
        defaultViewDate: options.defaultViewDate
      };
      if (options.hasDay) {
        datepickerOptions.beforeShowDay = (/** Date */date) => {
          const stringDate = (0, _moment.default)(date).format(this.format);
          return {
            enabled: options.hasDay(stringDate)
          };
        };
      }
      if (options.hasMonth) {
        datepickerOptions.beforeShowMonth = (/** Date */date) => {
          const stringDate = (0, _moment.default)(date).format(this.format);
          return {
            enabled: options.hasMonth(stringDate)
          };
        };
      }
      if (options.hasYear) {
        datepickerOptions.beforeShowYear = (/** Date */date) => {
          const stringDate = (0, _moment.default)(date).format(this.format);
          return {
            enabled: options.hasYear(stringDate)
          };
        };
      }

      // noinspection JSUnresolvedReference
      if (!(language in _jquery.default.fn.datepicker.dates)) {
        // noinspection JSUnresolvedReference
        _jquery.default.fn.datepicker.dates[language] = {
          days: this.language.get('Global', 'lists', 'dayNames'),
          daysShort: this.language.get('Global', 'lists', 'dayNamesShort'),
          daysMin: this.language.get('Global', 'lists', 'dayNamesMin'),
          months: this.language.get('Global', 'lists', 'monthNames'),
          monthsShort: this.language.get('Global', 'lists', 'monthNamesShort'),
          today: this.language.translate('Today'),
          clear: this.language.translate('Clear')
        };
      }
      this.$element.datepicker(datepickerOptions).on('changeDate', () => {
        if (options.onChangeDate) {
          options.onChangeDate();
        }
      }).on('changeMonth', (/** {date: Date} */event) => {
        if (options.onChangeMonth) {
          const dateString = (0, _moment.default)(event.date).startOf('month').format(options.format);
          options.onChangeMonth(dateString);
        }
      });
      if (element.classList.contains('input-group') && !(element instanceof HTMLInputElement)) {
        element.querySelectorAll('input').forEach(input => {
          (0, _jquery.default)(input).on('click', () => (0, _jquery.default)(input).datepicker('show'));
        });
      }
    }

    /**
     * Set a start date.
     *
     * @param {string|undefined} startDate
     */
    setStartDate(startDate) {
      this.$element.datepicker('setStartDate', startDate);
    }

    /**
     * Show.
     */
    show() {
      this.$element.datepicker('show');
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Get the value.
     *
     * @return {string|null}
     */
    getDate() {
      const date = this.$element.datepicker('getDate');
      if (!date) {
        return null;
      }
      return (0, _moment.default)(date).format(this.format);
    }

    /**
     * Refresh.
     */
    refresh() {
      const picker = this.$element.data('datepicker');
      if (!picker) {
        return;
      }
      picker.fill();
    }
  }
  var _default = _exports.default = Datepicker;
});

define("helpers/misc/summernote-custom", ["exports", "jquery", "views/wysiwyg/modals/edit-table", "views/wysiwyg/modals/edit-cell", "handlebars"], function (_exports, _jquery, _editTable, _editCell, _handlebars) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.init = init;
  _jquery = _interopRequireDefault(_jquery);
  _editTable = _interopRequireDefault(_editTable);
  _editCell = _interopRequireDefault(_editCell);
  _handlebars = _interopRequireDefault(_handlebars);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * @type {{
   *     edit: import('ace-builds').edit,
   *     require: import('ace-builds').require,
   * }}
   */
  let ace;
  function init(langSets) {
    _jquery.default.extend(_jquery.default.summernote.options, {
      espoImage: {
        icon: '<i class="note-icon-picture"/>',
        tooltip: langSets.image.image
      },
      espoLink: {
        icon: '<i class="note-icon-link"/>',
        tooltip: langSets.link.link
      },
      espoTable: {
        icon: '<i class="note-icon-table"/>',
        tooltip: langSets.table.table
      },
      popover: {
        table: [['custom', ['tableParams', 'cellParams']], ['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']], ['delete', ['deleteRow', 'deleteCol', 'deleteTable']]]
      }
    });
    _jquery.default.extend(_jquery.default.summernote.plugins, {
      'cellParams': function (/** Record */context) {
        const ui = _jquery.default.summernote.ui;
        const options = context.options;
        const view = /** @type {import('view').default} */options.espoView;
        if (!view) {
          return;
        }
        context.memo('button.cellParams', () => {
          return ui.button({
            className: '',
            contents: '<i class="far fa-square fa-sm"/>',
            tooltip: view.translate('cell', 'wysiwygLabels'),
            click: () => {
              context.invoke('cellParams.show');
            }
          }).render();
        });
        this.show = function () {
          const range = context.invoke('editor.getLastRange');
          const $td = (0, _jquery.default)(range.ec).closest('td,th');
          const td = /** @type {HTMLTableCellElement} */$td[0];
          const width = td.style.width;
          const height = td.style.height;
          const backgroundColor = td.style.backgroundColor;
          const verticalAlign = td.style.verticalAlign;
          const params = {
            width,
            height,
            backgroundColor,
            verticalAlign
          };

          //const prevParams = params;

          const modalView = new _editCell.default({
            params,
            headerText: view.translate('cell', 'wysiwygLabels'),
            onApply: params => applyParams(params)
          });
          view.assignView('dialog', modalView).then(() => {
            modalView.render();
          });

          /**
           * @param {{
           *     width: string|null,
           *     height: string|null,
           *     backgroundColor: string|null,
           *     verticalAlign: string|null,
           * }} params
           */
          const applyParams = params => {
            let backgroundColor = td.style.backgroundColor;
            if (backgroundColor === 'transparent') {
              backgroundColor = null;
            }
            td.style.backgroundColor = params.backgroundColor;
            td.style.width = params.width;
            td.style.height = params.height;
            td.style.verticalAlign = params.verticalAlign;
          };
        };
        this.destroy = function () {
          if (view) {
            view.clearView('dialog');
          }
        };
      },
      'tableParams': function (/** Record */context) {
        const ui = _jquery.default.summernote.ui;
        const options = context.options;
        const view = /** @type {import('view').default} */options.espoView;
        if (!view) {
          // Prevents an issue with a collapsed modal.
          // @todo Revise.
          return;
        }
        context.memo('button.tableParams', () => {
          return ui.button({
            className: '',
            contents: '<i class="note-icon-table"/>',
            tooltip: langSets.table.table,
            click: () => {
              context.invoke('tableParams.show');
            }
          }).render();
        });
        this.show = function () {
          const range = context.invoke('editor.getLastRange');
          const $table = (0, _jquery.default)(range.ec).closest('table');
          const table = /** @type {HTMLTableElement} */$table[0];
          let borderWidth = table.style.borderWidth;
          if (borderWidth !== null) {
            // @todo
            for (const /** HTMLTableCellElement */cell of table.querySelectorAll('td, th')) {
              if (cell.style.borderWidth !== borderWidth) {
                borderWidth = null;
                break;
              }
            }
          }
          let backgroundColor = table.style.backgroundColor;
          if (backgroundColor === 'transparent') {
            backgroundColor = null;
          }
          let borderColor = table.style.borderColor;
          if (borderColor !== null) {
            // @todo
            for (const /** HTMLTableCellElement */cell of table.querySelectorAll('td, th')) {
              if (cell.style.borderColor !== borderColor) {
                borderColor = null;
                break;
              }
            }
          }
          let cellPadding = null;
          for (const /** HTMLTableCellElement */cell of table.querySelectorAll('td, th')) {
            if (cellPadding !== null && cell.style.padding !== cellPadding) {
              cellPadding = null;
              break;
            }
            cellPadding = cell.style.padding;
          }
          let align = null;
          if (table.style.marginLeft === 'auto' && table.style.marginRight === '0px') {
            align = 'right';
          } else if (table.style.marginLeft === 'auto' && table.style.marginRight === 'auto') {
            align = 'center';
          } else if (table.style.marginLeft === '0px' && table.style.marginRight === 'auto') {
            align = 'left';
          }
          const width = table.style.width;
          const height = table.style.height;
          const params = {
            align,
            width,
            height,
            borderWidth,
            borderColor,
            cellPadding,
            backgroundColor
          };
          const prevParams = params;
          const modalView = new _editTable.default({
            params,
            headerText: langSets.table.table,
            onApply: params => applyParams(params)
          });
          view.assignView('dialog', modalView).then(() => {
            modalView.render();
          });

          /**
           * @param {{
           *     align: string|null,
           *     width: string|null,
           *     height: string|null,
           *     borderWidth: string|null,
           *     borderColor: string|null,
           *     cellPadding: string|null,
           *     backgroundColor: string|null,
           * }} params
           */
          const applyParams = params => {
            if (params.align === 'left') {
              table.style.marginLeft = '0';
              table.style.marginRight = 'auto';
            } else if (params.align === 'right') {
              table.style.marginLeft = 'auto';
              table.style.marginRight = '0';
            } else if (params.align === 'center') {
              table.style.marginLeft = 'auto';
              table.style.marginRight = 'auto';
            } else if (params.align === null && prevParams.align !== null) {
              table.style.marginLeft = null;
              table.style.marginRight = null;
            }
            table.style.backgroundColor = params.backgroundColor;
            table.style.width = params.width;
            table.style.height = params.height;
            if (params.borderWidth !== null || prevParams.borderWidth !== null) {
              table.style.borderWidth = params.borderWidth;
              table.style.borderStyle = params.borderWidth !== null ? 'solid' : null;
              for (const /** HTMLTableCellElement */cell of table.querySelectorAll('td, th')) {
                cell.style.borderWidth = params.borderWidth;
                cell.style.borderStyle = params.borderWidth !== null ? 'solid' : null;
              }
            }
            if (params.borderColor !== null || prevParams.borderColor !== null) {
              table.style.borderColor = params.borderColor;
              for (const /** HTMLTableCellElement */cell of table.querySelectorAll('td, th')) {
                cell.style.borderColor = params.borderColor;
              }
            }
            if (params.cellPadding !== null || prevParams.padding !== null) {
              for (const /** HTMLTableCellElement */cell of table.querySelectorAll('td, th')) {
                cell.style.padding = params.cellPadding;
              }
            }
          };
        };
        this.destroy = function () {
          if (view) {
            view.clearView('dialog');
          }
        };
      },
      'aceCodeview': function (/** Record */context) {
        const ui = _jquery.default.summernote.ui;
        const options = context.options;
        const lang = options.langInfo;
        /** @type {JQuery} */
        const $editor = context.layoutInfo.editor;
        /** @type {JQuery} */
        const $editable = context.layoutInfo.editable;
        const view = /** @type {module:view} */options.espoView;
        if (!view) {
          return;
        }

        // noinspection SpellCheckingInspection
        let $codable;
        /** @type {import('ace-builds').Ace.Editor} */
        let aceEditor;
        let isActivated = false;
        let isReplaced = false;
        let isBeingActivated = false;
        const EMPTY = '<p>&nbsp;</p>';
        context.destroyAceCodeview = function () {
          if (isActivated) {
            deactivate();
          }
        };
        context.code = function (html) {
          if (html === undefined) {
            let value;
            if (isActivated) {
              value = prepareValue(aceEditor.getValue()) || EMPTY;
              $editable.html(value);
            }
            return isActivated ? value : $editable.html();
          }
          if (isActivated) {
            aceEditor.setValue(html);
          }
          $editable.html(html);
          this.$note.val(html);
          context.triggerEvent('change', html, $editable);
        };
        const id = 'editor-' + Math.random().toString(36).substring(2, 17);
        const prepareValue = input => {
          return input;
        };
        const prepareHtml = (/** string */input) => {
          return prepareValue(input);
        };
        const toggle = () => {
          isActivated ? deactivate() : activate();
          context.triggerEvent('codeview.toggled');
        };
        const deactivate = () => {
          if (!isActivated) {
            return;
          }
          $codable.addClass('hidden');
          let value = prepareValue(aceEditor.getValue()) || EMPTY;
          value = context.invoke('codeview.purify', value);
          const isChange = $editable.html() !== value;
          $editable.html(value);
          $editor.removeClass('codeview');
          if (isChange) {
            context.triggerEvent('change', $editable.html(), $editable);
          }
          $editable.focus();
          context.invoke('toolbar.updateCodeview', false);
          context.invoke('airPopover.updateCodeview', false);
          if (aceEditor) {
            aceEditor.destroy();
            aceEditor = null;
          }
          context.aceEditor = undefined;
          isActivated = false;
        };
        const activate = () => {
          if (isBeingActivated) {
            return;
          }
          isBeingActivated = true;
          context.invoke('toolbar.updateCodeview', true);
          context.invoke('airPopover.updateCodeview', true);
          $editor.addClass('codeview');
          if (!isReplaced) {
            $codable = (0, _jquery.default)('<div>').attr('id', id).css('minHeight', 40 + 'px');
            (0, _jquery.default)(context.layoutInfo.codable).replaceWith($codable);
            context.$aceCodable = $codable;
            isReplaced = true;
          }
          $codable.removeClass('hidden');
          if ($editor.hasClass('fullscreen')) {
            $codable.css('height', $editable.css('height'));
          }
          requireAce().then(() => {
            const html = prepareHtml($editable.html());
            aceEditor = ace.edit(id);
            aceEditor.setValue(html);
            aceEditor.setOptions({
              maxLines: !$editor.hasClass('fullscreen') ? 100000 : null,
              enableLiveAutocompletion: true,
              tabSize: 2,
              useSoftTabs: true
            });
            aceEditor.setOptions({
              fontFamily: 'var(--font-family-monospace)'
            });
            aceEditor.setFontSize('var(--font-size-small)');
            aceEditor.container.style.lineHeight = 'var(--line-height-small)';
            aceEditor.renderer.updateFontSize();
            if (options.isDark) {
              aceEditor.setOptions({
                theme: 'ace/theme/tomorrow_night'
              });
            }
            aceEditor.getSession().setUseWrapMode(true);
            aceEditor.setShowPrintMargin(false);
            aceEditor.getSession().setUseWorker(false);
            aceEditor.commands.removeCommand('find');
            aceEditor.setHighlightActiveLine(false);
            aceEditor.focus();
            try {
              aceEditor.gotoLine(0, 0, false);
            } catch (e) {}
            aceEditor.on('blur', e => {
              context.triggerEvent('blur.codeview', aceEditor.getValue(), e);
            });
            aceEditor.on('change', () => {
              context.triggerEvent('change.codeview', aceEditor.getValue());
            });

            // noinspection JSValidateTypes
            context.aceEditor = aceEditor;
            const modeToRequired = options.handlebars ? 'ace/mode/handlebars' : 'ace/mode/html';
            const Mode = ace.require(modeToRequired).Mode;
            aceEditor.session.setMode(new Mode());
            isActivated = true;
            isBeingActivated = false;
          });
        };
        context.memo('button.aceCodeview', () => {
          return ui.button({
            className: 'btn-codeview note-codeview-keep',
            contents: '<i class="note-icon-code"/>',
            tooltip: lang.options.codeview,
            click: () => toggle()
          }).render();
        });

        /**
         * @return Promise
         */
        const requireAce = function () {
          return Espo.loader.requirePromise('lib!ace').then(lib => {
            ace = /** window.ace */lib;
            const list = [Espo.loader.requirePromise('lib!ace-ext-language_tools')];
            list.push(options.handlebars ? Espo.loader.requirePromise('lib!ace-mode-handlebars') : Espo.loader.requirePromise('lib!ace-mode-html'));
            if (options.isDark) {
              list.push(Espo.loader.requirePromise('lib!ace-theme-tomorrow_night'));
            }
            return Promise.all(list);
          });
        };
      },
      'espoTable': function (context) {
        const ui = _jquery.default.summernote.ui;
        const options = context.options;
        const self = options.espoView;
        const lang = options.langInfo;
        if (!self) {
          return;
        }
        context.memo('button.espoTable', () => {
          return ui.buttonGroup([ui.button({
            className: 'dropdown-toggle',
            contents: ui.dropdownButtonContents(ui.icon(options.icons.table), options),
            tooltip: options.espoTable.tooltip,
            data: {
              toggle: 'dropdown'
            }
          }), ui.dropdown({
            title: lang.table.table,
            className: 'note-table',
            items: ['<div class="note-dimension-picker">', '<div class="note-dimension-picker-mousecatcher" data-event="insertTable" data-value="1x1"></div>', '<div class="note-dimension-picker-highlighted"></div>', '<div class="note-dimension-picker-unhighlighted"></div>', '</div>', '<div class="note-dimension-display">1 x 1</div>'].join('')
          })], {
            callback: $node => {
              const $catcher = $node.find('.note-dimension-picker-mousecatcher');
              const createTable = (colCount, rowCount, options) => {
                const tds = [];
                let tdHTML;
                for (let idxCol = 0; idxCol < colCount; idxCol++) {
                  tds.push('<td>&nbsp;</td>');
                }
                tdHTML = tds.join('\n');
                const trs = [];
                let trHTML;
                for (let idxRow = 0; idxRow < rowCount; idxRow++) {
                  trs.push('<tr>' + tdHTML + '</tr>');
                }
                trHTML = trs.join('\n');
                const $table = (0, _jquery.default)('<table>\n' + trHTML + '</table>');

                /*if (options.tableBorderWidth !== undefined) {
                    $table.attr('border', options.tableBorderWidth);
                }
                  if (options.tableCellPadding !== undefined) {
                    $table.attr('cellpadding', options.tableCellPadding);
                }*/

                $table.css({
                  width: '100%',
                  borderCollapse: 'collapse'
                  //borderSpacing: 0,
                });
                if (options && options.tableClassName) {
                  $table.addClass(options.tableClassName);
                }
                return $table[0];
              };
              $catcher.css({
                width: options.insertTableMaxSize.col + 'em',
                height: options.insertTableMaxSize.row + 'em'
              }).mousedown(() => {
                const $note = context.$note;
                const dims = $catcher.data('value').split('x');
                const range = $note.summernote('editor.getLastRange').deleteContents();
                createTable(dims[0], dims[1], options);
                range.insertNode(createTable(dims[0], dims[1], options));
              }).on('mousemove', event => {
                const PX_PER_EM = 18;
                const $picker = (0, _jquery.default)(event.target.parentNode);
                const $dimensionDisplay = $picker.next();
                const $catcher = $picker.find('.note-dimension-picker-mousecatcher');
                const $highlighted = $picker.find('.note-dimension-picker-highlighted');
                const $unhighlighted = $picker.find('.note-dimension-picker-unhighlighted');
                let posOffset;
                if (event.offsetX === undefined) {
                  const posCatcher = (0, _jquery.default)(event.target).offset();
                  posOffset = {
                    x: event.pageX - posCatcher.left,
                    y: event.pageY - posCatcher.top
                  };
                } else {
                  posOffset = {
                    x: event.offsetX,
                    y: event.offsetY
                  };
                }
                const dim = {
                  c: Math.ceil(posOffset.x / PX_PER_EM) || 1,
                  r: Math.ceil(posOffset.y / PX_PER_EM) || 1
                };
                $highlighted.css({
                  width: dim.c + 'em',
                  height: dim.r + 'em'
                });
                $catcher.data('value', dim.c + 'x' + dim.r);
                if (dim.c > 3 && dim.c < options.insertTableMaxSize.col) {
                  $unhighlighted.css({
                    width: dim.c + 1 + 'em'
                  });
                }
                if (dim.r > 3 && dim.r < options.insertTableMaxSize.row) {
                  $unhighlighted.css({
                    height: dim.r + 1 + 'em'
                  });
                }
                $dimensionDisplay.html(dim.c + ' x ' + dim.r);
              });
            }
          }).render();
        });
      },
      'espoImage': function (context) {
        const ui = _jquery.default.summernote.ui;
        const options = context.options;
        const self = options.espoView;
        const lang = options.langInfo;
        if (!self) {
          return;
        }
        context.memo('button.espoImage', () => {
          const button = ui.button({
            contents: options.espoImage.icon,
            tooltip: options.espoImage.tooltip,
            click() {
              context.invoke('espoImage.show');
            }
          });
          return button.render();
        });
        this.initialize = function () {};
        this.destroy = function () {
          if (!self) {
            return;
          }
          self.clearView('insertImageDialog');
        };
        this.show = function () {
          self.createView('insertImageDialog', 'views/wysiwyg/modals/insert-image', {
            labels: {
              insert: lang.image.insert,
              url: lang.image.url,
              selectFromFiles: lang.image.selectFromFiles
            }
          }, view => {
            view.render();
            self.listenToOnce(view, 'upload', target => {
              self.$summernote.summernote('insertImagesOrCallback', target);
            });
            self.listenToOnce(view, 'insert', target => {
              self.$summernote.summernote('insertImage', target);
            });
            self.listenToOnce(view, 'close', () => {
              self.clearView('insertImageDialog');
              self.fixPopovers();
            });
          });
        };
      },
      // Not used?
      'linkDialog': function (context) {
        const options = context.options;
        const self = options.espoView;
        const lang = options.langInfo;
        if (!self) {
          return;
        }
        this.show = function () {
          const linkInfo = context.invoke('editor.getLinkInfo');
          self.createView('dialogInsertLink', 'views/wysiwyg/modals/insert-link', {
            labels: {
              insert: lang.link.insert,
              openInNewWindow: lang.link.openInNewWindow,
              url: lang.link.url,
              textToDisplay: lang.link.textToDisplay
            },
            linkInfo: linkInfo
          }, view => {
            view.render();
            self.listenToOnce(view, 'insert', data => {
              data.text = _handlebars.default.Utils.escapeExpression(data.text);
              self.$summernote.summernote('createLink', data);
            });
            self.listenToOnce(view, 'close', () => {
              self.clearView('dialogInsertLink');
              self.fixPopovers();
            });
          });
        };
      },
      'espoLink': function (context) {
        const ui = _jquery.default.summernote.ui;
        const options = context.options;
        const self = options.espoView;
        const lang = options.langInfo;
        if (!self) {
          return;
        }
        const isMacLike = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);
        context.memo('button.espoLink', function () {
          const button = ui.button({
            contents: options.espoLink.icon,
            tooltip: options.espoLink.tooltip + ' (' + (isMacLike ? 'CMD+K' : 'CTRL+K') + ')',
            click() {
              context.invoke('espoLink.show');
            }
          });
          return button.render();
        });
        this.initialize = function () {
          this.$modalBody = self.$el.closest('.modal-body');
          this.isInModal = this.$modalBody.length > 0;
        };
        this.destroy = function () {
          if (!self) {
            return;
          }
          self.clearView('dialogInsertLink');
        };
        this.show = function () {
          const linkInfo = context.invoke('editor.getLinkInfo');
          const container = this.isInModal ? this.$modalBody.get(0) : window;
          self.createView('dialogInsertLink', 'views/wysiwyg/modals/insert-link', {
            labels: {
              insert: lang.link.insert,
              openInNewWindow: lang.link.openInNewWindow,
              url: lang.link.url,
              textToDisplay: lang.link.textToDisplay
            },
            linkInfo: linkInfo
          }, view => {
            view.render();
            self.listenToOnce(view, 'insert', data => {
              const scrollY = 'scrollY' in container ? container.scrollY : container.scrollTop;
              data.text = _handlebars.default.Utils.escapeExpression(data.text);
              self.$summernote.summernote('createLink', data);
              setTimeout(() => container.scroll(0, scrollY), 20);
            });
            self.listenToOnce(view, 'close', () => {
              self.clearView('dialogInsertLink');
              self.fixPopovers();
            });
          });
        };
      },
      'fullscreen': function (context) {
        const options = context.options;
        const self = options.espoView;
        if (!self) {
          return;
        }
        this.$window = (0, _jquery.default)(window);
        this.$scrollbar = (0, _jquery.default)('html, body');
        this.initialize = function () {
          this.$editor = context.layoutInfo.editor;
          this.$toolbar = context.layoutInfo.toolbar;
          this.$editable = context.layoutInfo.editable;
          this.$modal = self.$el.closest('.modal');
          this.isInModal = this.$modal.length > 0;
        };
        this.resizeTo = function (size) {
          this.$editable.css('height', size.h);
          if (context.$aceCodable) {
            context.$aceCodable.css('height', size.h);
          }
        };
        this.onResize = function () {
          this.resizeTo({
            h: this.$window.height() - this.$toolbar.outerHeight()
          });
        };
        this.isFullscreen = function () {
          return this.$editor.hasClass('fullscreen');
        };
        this.destroy = function () {
          this.$window.off('resize.summernote' + self.cid);
          if (this.isInModal) {
            this.$modal.css('overflow-y', '');
          } else {
            this.$scrollbar.css('overflow', '');
          }
        };
        let maxLines;
        this.toggle = function () {
          this.$editor.toggleClass('fullscreen');
          const aceEditor = /** @type {import('ace-builds').Ace.Editor} */context.aceEditor;
          if (this.isFullscreen()) {
            this.$editable.data('orgHeight', this.$editable.css('height'));
            this.$editable.data('orgMaxHeight', this.$editable.css('maxHeight'));
            this.$editable.css('maxHeight', '');
            this.$window.on('resize.summernote' + self.cid, this.onResize.bind(this)).trigger('resize');
            if (this.isInModal) {
              this.$modal.css('overflow-y', 'hidden');
            } else {
              this.$scrollbar.css('overflow', 'hidden');
            }

            // noinspection JSUnusedGlobalSymbols
            this._isFullscreen = true;
            if (aceEditor) {
              maxLines = aceEditor.getOption('maxLines');
              aceEditor.setOptions({
                maxLines: null
              });
              aceEditor.resize();
            }
          } else {
            this.$window.off('resize.summernote' + self.cid);
            this.resizeTo({
              h: this.$editable.data('orgHeight')
            });
            if (context.$aceCodable) {
              context.$aceCodable.css('height', '');
            }
            this.$editable.css('maxHeight', this.$editable.css('orgMaxHeight'));
            this.$editable.css('height', '');
            if (this.isInModal) {
              this.$modal.css('overflow-y', '');
            } else {
              this.$scrollbar.css('overflow', '');
            }

            // noinspection JSUnusedGlobalSymbols
            this._isFullscreen = false;
            if (aceEditor) {
              aceEditor.setOptions({
                maxLines: 100000
              });
              aceEditor.resize();
            }
          }
          context.invoke('toolbar.updateFullscreen', this.isFullscreen());
        };
      }
    });
  }
});

define("controller", ["exports", "exceptions", "bullbone", "di", "helpers/site/modal-bar-provider"], function (_exports, _exceptions, _bullbone, _di, _modalBarProvider) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _exceptions = _interopRequireDefault(_exceptions);
  _modalBarProvider = _interopRequireDefault(_modalBarProvider);
  let _init_modalBarProvider, _init_extra_modalBarProvider;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 controller */
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * @callback module:controller~viewCallback
   * @param {module:view} view A view.
   */

  /**
   * @callback module:controller~masterViewCallback
   * @param {module:views/site/master} view A master view.
   */

  /**
   * A controller. To be extended.
   *
   * @mixes Bull.Events
   */
  class Controller {
    static #_ = [_init_modalBarProvider, _init_extra_modalBarProvider] = _applyDecs(this, [], [[(0, _di.inject)(_modalBarProvider.default), 0, "modalBarProvider"]]).e;
    /**
     * @internal
     * @param {Object.<string, *>} params
     * @param {Object} injections
     */
    constructor(params, injections) {
      _init_extra_modalBarProvider(this);
      this.params = params || {};

      /** @type {module:controllers/base} */
      this.baseController = injections.baseController;
      /** @type {Bull.Factory} */
      this.viewFactory = injections.viewFactory;
      /** @type {module:model} */
      this.modelFactory = injections.modelFactory;
      /** @type {module:collection-factory} */
      this.collectionFactory = injections.collectionFactory;
      this._settings = injections.settings || null;
      this._user = injections.user || null;
      this._preferences = injections.preferences || null;
      this._acl = injections.acl || null;
      this._cache = injections.cache || null;
      this._router = injections.router || null;
      this._storage = injections.storage || null;
      this._metadata = injections.metadata || null;
      this._dateTime = injections.dateTime || null;
      this._broadcastChannel = injections.broadcastChannel || null;
      this.setMasterRendered(false);
    }

    /**
     * A default action.
     *
     * @type {string}
     */
    defaultAction = 'index';

    /**
     * A name.
     *
     * @type {string|null}
     */
    name = null;

    /**
     * Params.
     *
     * @type {Object}
     * @private
     */
    params = null;

    /**
     * A view factory.
     *
     * @type {Bull.Factory}
     * @protected
     */
    viewFactory = null;

    /**
     * A model factory.
     *
     * @type {module:model-factory}
     * @protected
     */
    modelFactory = null;

    /**
     * A body view.
     *
     * @public
     * @type {string|null}
     */
    masterView = null;

    /**
     * @private
     * @type {ModalBarProvider}
     */
    modalBarProvider = _init_modalBarProvider(this);

    /**
     * Set the router.
     *
     * @internal
     * @param {module:router} router
     */
    setRouter(router) {
      this._router = router;
      this.trigger('router-set', router);
    }

    /**
     * @protected
     * @returns {module:models/settings}
     */
    getConfig() {
      return this._settings;
    }

    /**
     * @protected
     * @returns {module:models/user}
     */
    getUser() {
      return this._user;
    }

    /**
     * @protected
     * @returns {module:models/preferences}
     */
    getPreferences() {
      return this._preferences;
    }

    /**
     * @protected
     * @returns {module:acl-manager}
     */
    getAcl() {
      return this._acl;
    }

    /**
     * @protected
     * @returns {module:cache}
     */
    getCache() {
      return this._cache;
    }

    /**
     * @protected
     * @returns {module:router}
     */
    getRouter() {
      return this._router;
    }

    /**
     * @protected
     * @returns {module:storage}
     */
    getStorage() {
      return this._storage;
    }

    /**
     * @protected
     * @returns {module:metadata}
     */
    getMetadata() {
      return this._metadata;
    }

    /**
     * @protected
     * @returns {module:date-time}
     */
    getDateTime() {
      return this._dateTime;
    }

    /**
     * Get a parameter of all controllers.
     *
     * @param {string} key A key.
     * @return {*} Null if a key doesn't exist.
     */
    get(key) {
      if (key in this.params) {
        return this.params[key];
      }
      return null;
    }

    /**
     * Set a parameter for all controllers.
     *
     * @param {string} key A name of a view.
     * @param {*} value
     */
    set(key, value) {
      this.params[key] = value;
    }

    /**
     * Unset a parameter.
     *
     * @param {string} key A key.
     */
    unset(key) {
      delete this.params[key];
    }

    /**
     * Has a parameter.
     *
     * @param {string} key A key.
     * @returns {boolean}
     */
    has(key) {
      return key in this.params;
    }

    /**
     * @param {string} key
     * @param {string} [name]
     * @return {string}
     * @private
     */
    _composeScrollKey(key, name) {
      name = name || this.name;
      return `scrollTop-${name}-${key}`;
    }

    /**
     * @param {string} key
     * @return {string}
     * @private
     */
    _composeMainViewKey(key) {
      return `mainView-${this.name}-${key}`;
    }

    /**
     * Get a stored main view.
     *
     * @param {string} key A key.
     * @returns {module:view|null}
     */
    getStoredMainView(key) {
      return this.get(this._composeMainViewKey(key));
    }

    /**
     * Has a stored main view.
     * @param {string} key
     * @returns {boolean}
     */
    hasStoredMainView(key) {
      return this.has(this._composeMainViewKey(key));
    }

    /**
     * Clear a stored main view.
     * @param {string} key
     */
    clearStoredMainView(key) {
      const view = this.getStoredMainView(key);
      if (view) {
        view.remove(true);
      }
      this.unset(this._composeScrollKey(key));
      this.unset(this._composeMainViewKey(key));
    }

    /**
     * Store a main view.
     *
     * @param {string} key A key.
     * @param {module:view} view A view.
     */
    storeMainView(key, view) {
      this.set(this._composeMainViewKey(key), view);
      this.listenTo(view, 'remove', o => {
        o = o || {};
        if (o.ignoreCleaning) {
          return;
        }
        this.stopListening(view, 'remove');
        this.clearStoredMainView(key);
      });
    }

    /**
     * Check access to an action.
     *
     * @param {string} action An action.
     * @returns {boolean}
     */
    checkAccess(action) {
      return true;
    }

    /**
     * Process access check to the controller.
     */
    handleAccessGlobal() {
      if (!this.checkAccessGlobal()) {
        throw new _exceptions.default.AccessDenied("Denied access to '" + this.name + "'");
      }
    }

    /**
     * Check access to the controller.
     *
     * @returns {boolean}
     */
    checkAccessGlobal() {
      return true;
    }

    /**
     * Check access to an action. Throwing an exception.
     *
     * @param {string} action An action.
     */
    handleCheckAccess(action) {
      if (this.checkAccess(action)) {
        return;
      }
      const msg = action ? "Denied access to action '" + this.name + "#" + action + "'" : "Denied access to scope '" + this.name + "'";
      throw new _exceptions.default.AccessDenied(msg);
    }

    /**
     * Process an action.
     *
     * @param {string} action
     * @param {Object} options
     */
    doAction(action, options) {
      this.handleAccessGlobal();
      action = action || this.defaultAction;
      const method = 'action' + Espo.Utils.upperCaseFirst(action);
      if (!(method in this)) {
        throw new _exceptions.default.NotFound("Action '" + this.name + "#" + action + "' is not found");
      }
      const preMethod = 'before' + Espo.Utils.upperCaseFirst(action);
      const postMethod = 'after' + Espo.Utils.upperCaseFirst(action);
      if (preMethod in this) {
        this[preMethod].call(this, options || {});
      }
      this[method].call(this, options || {});
      if (postMethod in this) {
        this[postMethod].call(this, options || {});
      }
    }

    /**
     * Serve a master view. Render if not already rendered.
     *
     * @param {module:controller~masterViewCallback} callback A callback with a created master view.
     * @private
     */
    master(callback) {
      const entireView = this.getEntireView();
      if (entireView) {
        entireView.remove();
        this.setEntireView(null);
      }
      const masterView = this.getMasterView();
      if (masterView) {
        callback.call(this, masterView);
        return;
      }
      const viewName = this.masterView || 'views/site/master';
      this.viewFactory.create(viewName, {
        fullSelector: 'body'
      }, async (/** import('views/site/master').default */masterView) => {
        this.setMasterView(masterView);
        if (this.isMasterRendered()) {
          callback.call(this, masterView);
          return;
        }
        this.modalBarProvider.set(masterView.collapsedModalBarView || null);
        await masterView.render();
        this.setMasterRendered(true);
        callback.call(this, masterView);
      });
    }

    /**
     * @private
     * @return {import('view').default|null}
     */
    getEntireView() {
      return this.get('entire');
    }

    /**
     * @private
     * @param {import('view').default|null} view
     */
    setEntireView(view) {
      this.set('entire', view);
    }

    /**
     * @private
     * @return {import('view').default|null}
     */
    getMasterView() {
      return this.get('master');
    }

    /**
     * @private
     * @param {import('view').default|null} view
     */
    setMasterView(view) {
      if (!view) {
        this.modalBarProvider.set(null);
      }
      this.set('master', view);
    }

    /**
     * @private
     * @param {boolean} value
     */
    setMasterRendered(value) {
      this.set('masterRendered', value);
    }

    /**
     * @private
     * @return {boolean}
     */
    isMasterRendered() {
      return !!this.get('masterRendered');
    }

    /**
     * @param {import('views/site/master').default} masterView
     * @private
     */
    _unchainMainView(masterView) {
      if (!masterView.currentViewKey /*||
                                     !this.hasStoredMainView(masterView.currentViewKey)*/) {
        return;
      }
      const currentMainView = masterView.getMainView();
      if (!currentMainView) {
        return;
      }
      currentMainView.propagateEvent('remove', {
        ignoreCleaning: true
      });
      masterView.unchainView('main');
    }

    /**
     * @typedef {Object} module:controller~mainParams
     * @property {boolean} [useStored] Use a stored view if available.
     * @property {string} [key] A stored view key.
     */

    /**
     * Create a main view in the master container and render it.
     *
     * @param {string|module:view} [view] A view name or view instance.
     * @param {Object.<string, *>} [options] Options for a view.
     * @param {module:controller~viewCallback} [callback] A callback with a created view.
     * @param {module:controller~mainParams} [params] Parameters.
     */
    main(view, options, callback) {
      let params = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
      const dto = {
        isCanceled: false,
        key: params.key,
        useStored: params.useStored,
        callback: callback
      };
      const selector = '#main';
      const useStored = params.useStored || false;
      const key = params.key;
      this.listenToOnce(this.baseController, 'action', () => dto.isCanceled = true);
      const mainView = view && typeof view === 'object' ? view : undefined;
      const viewName = !mainView ? view || 'views/base' : undefined;
      this.master(async masterView => {
        if (dto.isCanceled) {
          return;
        }
        options = options || {};
        options.fullSelector = selector;
        if (useStored && this.hasStoredMainView(key)) {
          const mainView = this.getStoredMainView(key);
          let isActual = true;
          if (mainView && 'isActualForReuse' in mainView && typeof mainView.isActualForReuse === 'function') {
            isActual = mainView.isActualForReuse();
          }
          const lastUrl = mainView && 'lastUrl' in mainView ? mainView.lastUrl : null;
          if (isActual && (!lastUrl || lastUrl === this.getRouter().getCurrentUrl())) {
            this._processMain(mainView, masterView, dto);
            if ('setupReuse' in mainView && typeof mainView.setupReuse === 'function') {
              mainView.setupReuse(options.params || {});
            }
            return;
          }
          this.clearStoredMainView(key);
        }
        if (mainView) {
          this._unchainMainView(masterView);
          await masterView.assignView('main', mainView, selector);
          dto.isSet = true;
          this._processMain(view, masterView, dto);
          return;
        }
        this.viewFactory.create(viewName, options, view => {
          this._processMain(view, masterView, dto);
        });
      });
    }

    /**
     * @param {import('view').default} mainView
     * @param {import('views/site/master').default} masterView
     * @param {{
     *     isCanceled: boolean,
     *     key?: string,
     *     useStored?: boolean,
     *     callback?: module:controller~viewCallback,
     *     isSet?: boolean,
     * }} dto Data.
     * @private
     */
    _processMain(mainView, masterView, dto) {
      if (dto.isCanceled) {
        return;
      }
      const key = dto.key;
      if (key) {
        this.storeMainView(key, mainView);
      }
      const onAction = () => {
        mainView.cancelRender();
        dto.isCanceled = true;
      };
      mainView.listenToOnce(this.baseController, 'action', onAction);
      if (masterView.currentViewKey) {
        const scrollKey = this._composeScrollKey(masterView.currentViewKey, masterView.currentName);
        this.set(scrollKey, window.scrollY);
        if (!dto.isSet) {
          this._unchainMainView(masterView);
        }
      }
      masterView.currentViewKey = key;
      masterView.currentName = this.name;
      if (!dto.isSet) {
        masterView.setView('main', mainView);
      }
      const afterRender = () => {
        setTimeout(() => mainView.stopListening(this.baseController, 'action', onAction), 500);
        mainView.updatePageTitle();
        const scrollKey = this._composeScrollKey(key);
        if (dto.useStored && this.has(scrollKey)) {
          window.scrollTo({
            top: this.get(scrollKey)
          });
          return;
        }
        window.scrollTo({
          top: 0
        });
      };
      if (dto.callback) {
        this.listenToOnce(mainView, 'after:render', afterRender);
        dto.callback.call(this, mainView);
        return;
      }
      mainView.render().then(afterRender);
    }

    /**
     * Show a loading notify-message.
     */
    showLoadingNotification() {
      const master = this.getMasterView();
      if (!master) {
        return;
      }
      master.showLoadingNotification();
    }

    /**
     * Hide a loading notify-message.
     */
    hideLoadingNotification() {
      const master = this.getMasterView();
      if (!master) {
        return;
      }
      master.hideLoadingNotification();
    }

    /**
     * Create a view in the BODY element. Use for rendering separate pages without the default navbar and footer.
     * If a callback is not passed, the view will be automatically rendered.
     *
     * @param {string|module:view} view A view name or view instance.
     * @param {Object.<string, *>} [options] Options for a view.
     * @param {module:controller~viewCallback} [callback] A callback with a created view.
     */
    entire(view, options, callback) {
      const masterView = this.getMasterView();
      if (masterView) {
        masterView.remove();
      }
      this.setMasterView(null);
      this.setMasterRendered(false);
      if (typeof view === 'object') {
        view.setElement('body');
        this.viewFactory.prepare(view, () => {
          if (!callback) {
            view.render();
            return;
          }
          callback(view);
        });
        return;
      }
      options = options || {};
      options.fullSelector = 'body';
      this.viewFactory.create(view, options, view => {
        this.setEntireView(view);
        if (!callback) {
          view.render();
          return;
        }
        callback(view);
      });
    }
  }
  Object.assign(Controller.prototype, _bullbone.Events);

  /** For backward compatibility. */
  Controller.extend = _bullbone.View.extend;
  var _default = _exports.default = Controller;
});

define("views/main", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/main */

  /**
   * A base main view. The detail, edit, list views to be extended from.
   */
  class MainView extends _view.default {
    /**
     * A scope name.
     *
     * @type {string} scope
     */
    scope = '';

    /**
     * A name.
     *
     * @type {string} name
     */
    name = '';

    /**
     * A top-right menu item (button or dropdown action).
     * Handled by a class method `action{Action}`, a click handler or a handler class.
     *
     * @typedef {Object} module:views/main~MenuItem
     *
     * @property {string} [name] A name.
     * @property {string} [action] An action.
     * @property {string} [link] A link.
     * @property {string} [label] A translatable label.
     * @property {string} [labelTranslation] A label translation path.
     * @property {'default'|'danger'|'success'|'warning'} [style] A style. Only for buttons.
     * @property {boolean} [hidden] Hidden.
     * @property {boolean} [disabled] Disabled.
     * @property {Object.<string,string|number|boolean>} [data] Data attribute values.
     * @property {string} [title] A title.
     * @property {string} [iconHtml] An icon HTML.
     * @property {string} [iconClass] An icon class.
     * @property {string} [html] An HTML.
     * @property {string} [text] A text.
     * @property {string} [className] An additional class name. Only for buttons.
     * @property {'create'|'read'|'edit'|'stream'|'delete'} [acl] Access to a record (or a scope if `aclScope` specified)
     *   required for a menu item.
     * @property {string} [aclScope] A scope to check access to with the `acl` parameter.
     * @property {string} [configCheck] A config parameter defining a menu item availability.
     *   If starts with `!`, then the result is negated.
     * @property {module:utils~AccessDefs[]} [accessDataList] Access definitions.
     * @property {string} [handler] A handler.
     * @property {string} [initFunction] An init method in the handler.
     * @property {string} [actionFunction] An action method in the handler.
     * @property {string} [checkVisibilityFunction] A method in the handler that determine whether an item is available.
     * @property {function()} [onClick] A click handler.
     */

    /**
     * Top-right menu definitions.
     *
     * @type {{
     *     buttons: module:views/main~MenuItem[],
     *     dropdown: module:views/main~MenuItem[],
     *     actions: module:views/main~MenuItem[],
     * }} menu
     * @private
     * @internal
     */
    menu = {};

    /**
     * @private
     * @type {JQuery|null}
     */
    $headerActionsContainer = null;

    /**
     * A shortcut-key => action map.
     *
     * @protected
     * @type {?Object.<string, string|function (KeyboardEvent): void>}
     */
    shortcutKeys = null;

    /** @inheritDoc */
    events = {
      /** @this MainView */
      'click .action': function (e) {
        Espo.Utils.handleAction(this, e.originalEvent, e.currentTarget, {
          actionItems: [...this.menu.buttons, ...this.menu.dropdown],
          className: 'main-header-manu-action'
        });
      }
    };
    lastUrl;

    /** @inheritDoc */
    init() {
      this.scope = this.options.scope || this.scope;
      this.menu = {};
      this.options.params = this.options.params || {};
      if (this.name && this.scope) {
        const key = this.name.charAt(0).toLowerCase() + this.name.slice(1);
        this.menu = this.getMetadata().get(['clientDefs', this.scope, 'menu', key]) || {};
      }

      /**
       * @private
       * @type {string[]}
       */
      this.headerActionItemTypeList = ['buttons', 'dropdown', 'actions'];
      this.menu = Espo.Utils.cloneDeep(this.menu);
      let globalMenu = {};
      if (this.name) {
        globalMenu = Espo.Utils.cloneDeep(this.getMetadata().get(['clientDefs', 'Global', 'menu', this.name.charAt(0).toLowerCase() + this.name.slice(1)]) || {});
      }
      this._reRenderHeaderOnSync = false;
      this._menuHandlers = {};
      this.headerActionItemTypeList.forEach(type => {
        this.menu[type] = this.menu[type] || [];
        this.menu[type] = this.menu[type].concat(globalMenu[type] || []);
        const itemList = this.menu[type];
        itemList.forEach(item => {
          const viewObject = this;

          // @todo Set _reRenderHeaderOnSync to true if `acl` is set `ascScope` is not set?
          //     Set _reRenderHeaderOnSync in `addMenuItem` method.

          if ((item.initFunction || item.checkVisibilityFunction) && (item.handler || item.data && item.data.handler)) {
            this.wait(new Promise(resolve => {
              const handler = item.handler || item.data.handler;
              Espo.loader.require(handler, Handler => {
                const handler = new Handler(viewObject);
                const name = item.name || item.action;
                if (name) {
                  this._menuHandlers[name] = handler;
                }
                if (item.initFunction) {
                  handler[item.initFunction].call(handler);
                }
                if (item.checkVisibilityFunction && this.model) {
                  this._reRenderHeaderOnSync = true;
                }
                resolve();
              });
            }));
          }
        });
      });
      if (this.model) {
        this.whenReady().then(() => {
          if (!this._reRenderHeaderOnSync) {
            return;
          }
          this.listenTo(this.model, 'sync', () => {
            if (!this.getHeaderView()) {
              return;
            }
            this.getHeaderView().reRender();
          });
        });
      }
      this.updateLastUrl();
      this.on('after:render-internal', () => {
        this.$headerActionsContainer = this.$el.find('.page-header .header-buttons');
      });
      this.on('header-rendered', () => {
        this.$headerActionsContainer = this.$el.find('.page-header .header-buttons');
        this.adjustButtons();
      });
      this.on('after:render', () => this.adjustButtons());
      if (this.shortcutKeys) {
        this.shortcutKeys = Espo.Utils.cloneDeep(this.shortcutKeys);
      }
    }
    setupFinal() {
      if (this.shortcutKeys) {
        this.events['keydown.main'] = e => {
          const key = Espo.Utils.getKeyFromKeyEvent(e);
          if (typeof this.shortcutKeys[key] === 'function') {
            this.shortcutKeys[key].call(this, e.originalEvent);
            return;
          }
          const actionName = this.shortcutKeys[key];
          if (!actionName) {
            return;
          }
          e.preventDefault();
          e.stopPropagation();
          const methodName = 'action' + Espo.Utils.upperCaseFirst(actionName);
          if (typeof this[methodName] === 'function') {
            this[methodName]();
            return;
          }
          this[actionName]();
        };
      }
    }

    /**
     * Update a last history URL.
     */
    updateLastUrl() {
      this.lastUrl = this.getRouter().getCurrentUrl();
    }

    /**
     * @internal
     * @returns {{
     *     buttons?: module:views/main~MenuItem[],
     *     dropdown?: module:views/main~MenuItem[],
     *     actions?: module:views/main~MenuItem[],
     * }}
     */
    getMenu() {
      if (this.menuDisabled || !this.menu) {
        return {};
      }
      const menu = {};
      this.headerActionItemTypeList.forEach(type => {
        (this.menu[type] || []).forEach(item => {
          if (item === false) {
            menu[type].push(false);
            return;
          }
          item = Espo.Utils.clone(item);
          menu[type] = menu[type] || [];
          if (!Espo.Utils.checkActionAvailability(this.getHelper(), item)) {
            return;
          }
          if (!Espo.Utils.checkActionAccess(this.getAcl(), this.model || this.scope, item)) {
            return;
          }
          if (item.accessDataList) {
            if (!Espo.Utils.checkAccessDataList(item.accessDataList, this.getAcl(), this.getUser())) {
              return;
            }
          }
          item.name = item.name || item.action;
          item.action = item.action || null;
          if (this._menuHandlers[item.name] && item.checkVisibilityFunction) {
            const handler = this._menuHandlers[item.name];
            if (!handler[item.checkVisibilityFunction](item.name)) {
              return;
            }
          }
          if (item.labelTranslation) {
            item.html = this.getHelper().escapeString(this.getLanguage().translatePath(item.labelTranslation));
          }
          menu[type].push(item);
        });
      });
      return menu;
    }

    /**
     * Get a header HTML. To be overridden.
     *
     * @returns {string} HTML.
     */
    getHeader() {
      return '';
    }

    /**
     * Build a header HTML. To be called from the #getHeader method.
     * Beware of XSS.
     *
     * @param {(string|Element|JQuery)[]} itemList A breadcrumb path. Like: Account > Name > edit.
     * @returns {string} HTML
     */
    buildHeaderHtml(itemList) {
      const $itemList = itemList.map(item => {
        return $('<div>').addClass('breadcrumb-item').append(item);
      });
      const $div = $('<div>').addClass('header-breadcrumbs');
      $itemList.forEach(($item, i) => {
        $div.append($item);
        if (i === $itemList.length - 1) {
          return;
        }
        $div.append($('<div>').addClass('breadcrumb-separator').append($('<span>')));
      });
      return $div.get(0).outerHTML;
    }

    /**
     * Get an icon HTML.
     *
     * @returns {string} HTML
     */
    getHeaderIconHtml() {
      return this.getHelper().getScopeColorIconHtml(this.scope);
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Action 'showModal'.
     *
     * @todo Revise. To be removed?
     *
     * @param {Object} data
     */
    actionShowModal(data) {
      const view = data.view;
      if (!view) {
        return;
      }
      this.createView('modal', view, {
        model: this.model,
        collection: this.collection
      }, view => {
        view.render();
        this.listenTo(view, 'after:save', () => {
          if (this.model) {
            this.model.fetch();
          }
          if (this.collection) {
            this.collection.fetch();
          }
        });
      });
    }

    /**
     * Update a menu item.
     *
     * @param {string} name An item name.
     * @param {module:views/main~MenuItem} item New item definitions to write.
     * @param {boolean} [doNotReRender=false] Skip re-render.
     *
     * @since 8.2.0
     */
    updateMenuItem(name, item, doNotReRender) {
      const actionItem = this._getHeaderActionItem(name);
      if (!actionItem) {
        return;
      }
      for (const key in item) {
        actionItem[key] = item[key];
      }
      if (doNotReRender) {
        return;
      }
      if (this.isRendered()) {
        this.getHeaderView().reRender();
        return;
      }
      if (this.isBeingRendered()) {
        this.whenRendered().then(() => {
          this.getHeaderView().reRender();
        });
      }
    }

    /**
     * Add a menu item.
     *
     * @param {'buttons'|'dropdown'} type A type.
     * @param {module:views/main~MenuItem|false} item Item definitions.
     * @param {boolean} [toBeginning=false] To beginning.
     * @param {boolean} [doNotReRender=false] Skip re-render.
     */
    addMenuItem(type, item, toBeginning, doNotReRender) {
      if (item) {
        item.name = item.name || item.action || Espo.Utils.generateId();
        const name = item.name;
        let index = -1;
        this.menu[type].forEach((data, i) => {
          data = data || {};
          if (data.name === name) {
            index = i;
          }
        });
        if (~index) {
          this.menu[type].splice(index, 1);
        }
      }
      let method = 'push';
      if (toBeginning) {
        method = 'unshift';
      }
      this.menu[type][method](item);
      if (!doNotReRender && this.isRendered()) {
        this.getHeaderView().reRender();
        return;
      }
      if (!doNotReRender && this.isBeingRendered()) {
        this.once('after:render', () => {
          this.getHeaderView().reRender();
        });
      }
    }

    /**
     * Remove a menu item.
     *
     * @param {string} name An item name.
     * @param {boolean} [doNotReRender] Skip re-render.
     */
    removeMenuItem(name, doNotReRender) {
      let index = -1;
      let type = false;
      this.headerActionItemTypeList.forEach(t => {
        (this.menu[t] || []).forEach((item, i) => {
          item = item || {};
          if (item.name === name) {
            index = i;
            type = t;
          }
        });
      });
      if (~index && type) {
        this.menu[type].splice(index, 1);
      }
      if (!doNotReRender && this.isRendered()) {
        this.getHeaderView().reRender();
        return;
      }
      if (!doNotReRender && this.isBeingRendered()) {
        this.once('after:render', () => {
          this.getHeaderView().reRender();
        });
        return;
      }
      if (doNotReRender && this.isRendered()) {
        this.$headerActionsContainer.find('[data-name="' + name + '"]').remove();
      }
    }

    /**
     * Disable a menu item.
     *
     * @param {string} name A name.
     */
    disableMenuItem(name) {
      const item = this._getHeaderActionItem(name);
      if (item) {
        item.disabled = true;
      }
      const process = () => {
        this.$headerActionsContainer.find(`[data-name="${name}"]`).addClass('disabled').attr('disabled');
      };
      if (this.isBeingRendered()) {
        this.whenRendered().then(() => process());
        return;
      }
      if (!this.isRendered()) {
        return;
      }
      process();
    }

    /**
     * Enable a menu item.
     *
     * @param {string} name A name.
     */
    enableMenuItem(name) {
      const item = this._getHeaderActionItem(name);
      if (item) {
        item.disabled = false;
      }
      const process = () => {
        this.$headerActionsContainer.find(`[data-name="${name}"]`).removeClass('disabled').removeAttr('disabled');
      };
      if (this.isBeingRendered()) {
        this.whenRendered().then(() => process());
        return;
      }
      if (!this.isRendered()) {
        return;
      }
      process();
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Action 'navigateToRoot'.
     *
     * @param {Object} data
     * @param {MouseEvent} event
     */
    actionNavigateToRoot(data, event) {
      event.stopPropagation();
      this.getRouter().checkConfirmLeaveOut(() => {
        const rootUrl = this.options.rootUrl || this.options.params.rootUrl || '#' + this.scope;
        this.getRouter().navigate(rootUrl, {
          trigger: true,
          isReturn: true
        });
      });
    }

    /**
     * @private
     * @param {string} name
     * @return {module:views/main~MenuItem|undefined}
     */
    _getHeaderActionItem(name) {
      for (const type of this.headerActionItemTypeList) {
        if (!this.menu[type]) {
          continue;
        }
        for (const item of this.menu[type]) {
          if (item && item.name === name) {
            return item;
          }
        }
      }
      return undefined;
    }

    /**
     * Hide a menu item.
     *
     * @param {string} name A name.
     */
    hideHeaderActionItem(name) {
      const item = this._getHeaderActionItem(name);
      if (item) {
        item.hidden = true;
      }
      if (!this.isRendered()) {
        return;
      }
      this.$headerActionsContainer.find(`li > .action[data-name="${name}"]`).parent().addClass('hidden');
      this.$headerActionsContainer.find(`a.action[data-name="${name}"]`).addClass('hidden');
      this.controlMenuDropdownVisibility();
      this.adjustButtons();
      if (this.getHeaderView()) {
        this.getHeaderView().trigger('action-item-update');
      }
    }

    /**
     * Show a hidden menu item.
     *
     * @param {string} name A name.
     */
    showHeaderActionItem(name) {
      const item = this._getHeaderActionItem(name);
      if (item) {
        item.hidden = false;
      }
      const processUi = () => {
        const $dropdownItem = this.$headerActionsContainer.find(`li > .action[data-name="${name}"]`).parent();
        const $button = this.$headerActionsContainer.find(`a.action[data-name="${name}"]`);

        // Item can be available but not rendered as it was skipped by access check in getMenu.
        if (item && !$dropdownItem.length && !$button.length) {
          if (this.getHeaderView()) {
            this.getHeaderView().reRender();
          }
          return;
        }
        $dropdownItem.removeClass('hidden');
        $button.removeClass('hidden');
        this.controlMenuDropdownVisibility();
        this.adjustButtons();
        if (this.getHeaderView()) {
          this.getHeaderView().trigger('action-item-update');
        }
      };
      if (!this.isRendered()) {
        if (this.isBeingRendered()) {
          this.whenRendered().then(() => processUi());
        }
        return;
      }
      processUi();
    }

    /**
     * Whether a menu has any non-hidden dropdown items.
     *
     * @private
     * @returns {boolean}
     */
    hasMenuVisibleDropdownItems() {
      let hasItems = false;
      (this.menu.dropdown || []).forEach(item => {
        if (!item.hidden) {
          hasItems = true;
        }
      });
      return hasItems;
    }

    /**
     * @private
     */
    controlMenuDropdownVisibility() {
      const $group = this.$headerActionsContainer.find('.dropdown-group');
      if (this.hasMenuVisibleDropdownItems()) {
        $group.removeClass('hidden');
        $group.find('> button').removeClass('hidden');
        return;
      }
      $group.addClass('hidden');
      $group.find('> button').addClass('hidden');
    }

    /**
     * @protected
     * @return {module:views/header}
     */
    getHeaderView() {
      return this.getView('header');
    }

    /**
     * @private
     */
    adjustButtons() {
      const $buttons = this.$headerActionsContainer.find('.btn');
      $buttons.removeClass('radius-left').removeClass('radius-right');
      const $buttonsVisible = $buttons.filter(':not(.hidden)');
      $buttonsVisible.first().addClass('radius-left');
      $buttonsVisible.last().addClass('radius-right');
    }

    /**
     * Called when a stored view is reused (by the controller).
     *
     * @public
     * @param {Object.<string, *>} params Routing params.
     */
    setupReuse(params) {}
  }
  var _default = _exports.default = MainView;
});

define("views/collapsed-modal", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class CollapsedModalView extends _view.default {
    templateContent = `
        <div class="title-container">
            <a role="button" data-action="expand" class="title">{{title}}</a>
        </div>
        <div class="close-container">
            <a role="button" data-action="close"><span class="fas fa-times"></span></a>
        </div>
    `;
    events = {
      /** @this CollapsedModalView */
      'click [data-action="expand"]': function () {
        this.expand();
      },
      /** @this CollapsedModalView */
      'click [data-action="close"]': function () {
        this.close();
      }
    };

    /**
     * @private
     */
    title;

    /**
     * @param {{
     *     modalView: import('views/modal').default,
     *     onClose: function(),
     *     onExpand: function(),
     *     duplicateNumber?: number|null,
     *     title?: string,
     * }} options
     */
    constructor(options) {
      super(options);
      this.options = options;
      this.modalView = options.modalView;
    }
    data() {
      let title = this.title;
      if (this.options.duplicateNumber) {
        title = `${this.title} ${this.options.duplicateNumber}`;
      }
      return {
        title: title
      };
    }
    setup() {
      this.title = this.options.title || 'no-title';
    }
    expand() {
      this.options.onExpand();
    }
    close() {
      this.options.onClose();
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = CollapsedModalView;
});

define("views/base", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/base */

  class BaseView extends _view.default {
    /**
     * @typedef {Object} module:views/base~options
     * @property {string} [template] A template.
     */

    /**
     * @param {module:views/base~options & Object.<string, *>} [options] Options.
     */
    constructor(options) {
      super(options);
    }
  }
  var _default = _exports.default = BaseView;
});

define("views/record/panels-container", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/panels-container */

  /**
   * A panel container view. For bottom and side views.
   */
  class PanelsContainerRecordView extends _view.default {
    /** @private */
    panelSoftLockedTypeList = ['default', 'acl', 'delimiter', 'dynamicLogic'];

    /**
     * A panel.
     *
     * @typedef {Object} module:views/record/panels-container~panel
     *
     * @property {string} name A name.
     * @property {boolean} [hidden] Hidden.
     * @property {string} [label] A label.
     * @property {'default'|'success'|'danger'|'warning'} [style] A style.
     * @property {string} [titleHtml] A title HTML.
     * @property {boolean} [notRefreshable] Not refreshable.
     * @property {boolean} [isForm] If for a form.
     * @property {module:views/record/panels-container~button[]} [buttonList] Buttons.
     * @property {module:views/record/panels-container~action[]} [actionList] Dropdown actions.
     * @property {string} [view] A view name.
     * @property {Object.<string, *>} [Options] A view options.
     * @property {boolean} [sticked] To stick to an upper panel.
     * @property {number} [tabNumber] A tab number.
     * @property {string} [aclScope] A scope to check access to.
     * @property {Espo.Utils~AccessDefs[]} [accessDataList] Access control defs.
     * @property {Record} [dynamicLogicVisible] Visibility rules.
     * @property {Record} [dynamicLogicStyled] Style rules.
     * @property {Record} [options] Options.
     * @property {boolean} [disabled] Disabled.
     */

    /**
     * A button. Handled by an `action{Action}` method or a click handler.
     *
     * @typedef {Object} module:views/record/panels-container~button
     *
     * @property {string} [action] An action.
     * @property {string} [name] A name. Required if a handler is used.
     * @property {boolean} [hidden] Hidden.
     * @property {string} [label] A label. Translatable.
     * @property {string} [html] A HTML.
     * @property {string} [text] A text.
     * @property {string} [title] A title (on hover). Translatable.
     * @property {Object.<string, (string|number|boolean)>} [data] Data attributes.
     * @property {string} [handler] A handler.
     * @property {string} [actionFunction] An action function.
     * @property {function()} [onClick] A click event.
     */

    /**
     * An action. Handled by an `action{Action}` method or a click handler.
     *
     * @typedef {Object} module:views/record/panels-container~action
     *
     * @property {string} [action] An action.
     * @property {string} [name] A name. Required if a handler is used.
     * @property {string} [link] A link URL.
     * @property {boolean} [hidden] Hidden.
     * @property {string} [label] A label. Translatable.
     * @property {string} [html] A HTML.
     * @property {string} [text] A text.
     * @property {Object.<string, (string|number|boolean)>} [data] Data attributes.
     * @property {string} [handler] A handler.
     * @property {string} [actionFunction] An action function.
     * @property {function()} [onClick] A click event.
     */

    /**
     * A panel list.
     *
     * @protected
     * @type {module:views/record/panels-container~panel[]}
     */
    panelList = null;

    /** @private */
    hasTabs = false;

    /**
     * @private
     * @type {Object.<string,*>[]|null}
     */
    tabDataList = null;

    /**
     * @protected
     */
    currentTab = 0;

    /**
     * @protected
     * @type {string}
     */
    scope = '';

    /**
     * @protected
     * @type {string}
     */
    entityType = '';

    /**
     * @protected
     * @type {string}
     */
    name = '';

    /**
     * A mode.
     *
     * @type 'detail'|'edit'
     */
    mode = 'detail';
    data() {
      const tabDataList = this.hasTabs ? this.getTabDataList() : [];
      return {
        panelList: this.panelList,
        scope: this.scope,
        entityType: this.entityType,
        tabDataList: tabDataList
      };
    }
    events = {
      'click .action': function (e) {
        const $target = $(e.currentTarget);
        const panel = $target.data('panel');
        if (!panel) {
          return;
        }
        const panelView = this.getView(panel);
        if (!panelView) {
          return;
        }
        let actionItems;
        if (typeof panelView.getButtonList === 'function' && typeof panelView.getActionList === 'function') {
          actionItems = [...panelView.getButtonList(), ...panelView.getActionList()];
        }
        Espo.Utils.handleAction(panelView, e.originalEvent, e.currentTarget, {
          actionItems: actionItems,
          className: 'panel-action'
        });

        // @todo Check data. Maybe pass cloned data with unset params.

        /*
        let action = $target.data('action');
        let data = $target.data();
          if (action && panel) {
            let method = 'action' + Espo.Utils.upperCaseFirst(action);
            let d = _.clone(data);
              delete d['action'];
            delete d['panel'];
              let view = this.getView(panel);
              if (view && typeof view[method] == 'function') {
                view[method].call(view, d, e);
            }
        }*/
      },
      'click .panels-show-more-delimiter [data-action="showMorePanels"]': 'actionShowMorePanels',
      /** @this module:views/record/panels-container */
      'click .tabs > button': function (e) {
        const tab = parseInt($(e.currentTarget).attr('data-tab'));
        this.selectTab(tab);
      }
    };
    afterRender() {
      this.adjustPanels();
    }
    adjustPanels() {
      if (!this.isRendered()) {
        return;
      }
      const $panels = this.$el.find('> .panel');
      $panels.removeClass('first').removeClass('last').removeClass('in-middle');
      const $visiblePanels = $panels.filter(`:not(.tab-hidden):not(.hidden)`);
      const groups = [];
      let currentGroup = [];
      let inTab = false;
      $visiblePanels.each((i, el) => {
        const $el = $(el);
        let breakGroup = false;
        if (!breakGroup && this.hasTabs && !inTab && $el.attr('data-tab') !== '-1') {
          inTab = true;
          breakGroup = true;
        }
        if (!breakGroup && !$el.hasClass('sticked')) {
          breakGroup = true;
        }
        if (breakGroup) {
          if (i !== 0) {
            groups.push(currentGroup);
          }
          currentGroup = [];
        }
        currentGroup.push($el);
        if (i === $visiblePanels.length - 1) {
          groups.push(currentGroup);
        }
      });
      groups.forEach(group => {
        group.forEach(($el, i) => {
          if (i === group.length - 1) {
            if (i === 0) {
              return;
            }
            $el.addClass('last');
            return;
          }
          if (i === 0 && group.length) {
            $el.addClass('first');
            return;
          }
          $el.addClass('in-middle');
        });
      });
    }

    /**
     * Set read-only.
     */
    setReadOnly() {
      this.readOnly = true;
    }

    /**
     * Set not read-only.
     */
    setNotReadOnly(onlyNotSetAsReadOnly) {
      this.readOnly = false;
      if (!onlyNotSetAsReadOnly) {
        return;
      }
      this.panelList.forEach(item => {
        this.applyAccessToActions(item.buttonList);
        this.applyAccessToActions(item.actionList);
        this.whenRendered().then(() => {
          if (this.getPanelActionsView(item.name)) {
            this.getPanelActionsView(item.name).reRender();
          }
        });
      });
    }

    /**
     * @private
     * @param {Object[]} actionList
     */
    applyAccessToActions(actionList) {
      if (!actionList) {
        return;
      }
      actionList.forEach(item => {
        if (!Espo.Utils.checkActionAvailability(this.getHelper(), item)) {
          item.hidden = true;
          return;
        }
        const access = Espo.Utils.checkActionAccess(this.getAcl(), this.model, item, true);
        if (access) {
          if (item.isHiddenByAcl) {
            item.isHiddenByAcl = false;
            item.hidden = false;
            delete item.hiddenByAclSoft;
          }
        } else if (!item.hidden) {
          item.isHiddenByAcl = true;
          item.hidden = true;
          delete item.hiddenByAclSoft;
          if (access === null) {
            item.hiddenByAclSoft = true;
          }
        }
      });
    }

    /**
     * Set up panel views.
     *
     * @protected
     */
    setupPanelViews() {
      this.panelList.forEach(p => {
        const name = p.name;
        let options = {
          model: this.model,
          panelName: name,
          selector: `.panel[data-name="${name}"] > .panel-body`,
          defs: p,
          mode: this.mode,
          recordHelper: this.recordHelper,
          inlineEditDisabled: this.inlineEditDisabled,
          readOnly: this.readOnly,
          disabled: p.hidden || false,
          recordViewObject: this.recordViewObject,
          dataObject: this.options.dataObject
        };
        options = _.extend(options, p.options);
        this.createView(name, p.view, options, view => {
          let hasSoftHidden = false;
          if ('getActionList' in view) {
            p.actionList = view.getActionList();
            this.applyAccessToActions(p.actionList);

            // noinspection JSUnresolvedReference
            if (p.actionList.find(it => it.hiddenByAclSoft)) {
              hasSoftHidden = true;
            }
          }
          if ('getButtonList' in view) {
            p.buttonList = view.getButtonList();
            this.applyAccessToActions(p.buttonList);

            // noinspection JSUnresolvedReference
            if (p.buttonList.find(it => it.hiddenByAclSoft)) {
              hasSoftHidden = true;
            }
          }
          if (hasSoftHidden) {
            this.listenToOnce(this.model, 'sync', () => {
              this.applyAccessToActions(p.actionList);
              this.applyAccessToActions(p.buttonList);
              view.whenRendered().then(() => {
                if (this.getPanelActionsView(name)) {
                  this.getPanelActionsView(name).reRender();
                }
              });
            });
          }
          if (view.titleHtml) {
            p.titleHtml = view.titleHtml;
          } else {
            if (p.label) {
              p.title = this.translate(p.label, 'labels', this.scope);
            } else {
              p.title = view.title;
            }
          }

          // @todo Use name_Actions.
          this.createView(name + 'Actions', 'views/record/panel-actions', {
            selector: `.panel[data-name="${p.name}"] > .panel-heading > .panel-actions-container`,
            model: this.model,
            defs: p,
            scope: this.scope,
            entityType: this.entityType
          });
        });
      });
    }

    /**
     * @param {string} name
     * @return {import('views/record/panel-actions')}
     */
    getPanelActionsView(name) {
      return this.getView(name + 'Actions');
    }

    /**
     * Set up panels.
     *
     * @protected
     */
    setupPanels() {}

    /**
     * Get field views.
     *
     * @param {boolean} [withHidden] With hidden.
     * @return {Object.<string, module:views/fields/base>}
     */
    getFieldViews(withHidden) {
      let fields = {};
      this.panelList.forEach(p => {
        const panelView = this.getPanelView(p.name);
        if ((!panelView.disabled || withHidden) && 'getFieldViews' in panelView) {
          fields = _.extend(fields, panelView.getFieldViews());
        }
      });
      return fields;
    }

    /**
     * Fetch.
     *
     * @return {Object.<string, *>}
     */
    fetch() {
      let data = {};
      this.panelList.forEach(p => {
        const panelView = this.getPanelView(p.name);
        if (!panelView.disabled && 'fetch' in panelView) {
          data = _.extend(data, panelView.fetch());
        }
      });
      return data;
    }

    /**
     * @private
     * @param name
     * @return {import('views/record/panels/side').default|import('views/record/panels/side').default|null}
     */
    getPanelView(name) {
      return this.getView(name);
    }

    /**
     * @param {string} name
     * @return {boolean}
     */
    hasPanel(name) {
      return !!this.panelList.find(item => item.name === name);
    }

    /**
     * @internal
     *
     * @param {string} name
     * @param [callback] Not to be used.
     * @param {boolean} [wasShown]
     */
    processShowPanel(name, callback) {
      let wasShown = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
      if (this.recordHelper.getPanelStateParam(name, 'hidden')) {
        return;
      }
      if (!this.hasPanel(name)) {
        return;
      }
      this.panelList.filter(item => item.name === name).forEach(item => {
        item.hidden = false;
        if (typeof item.tabNumber !== 'undefined') {
          this.controlTabVisibilityShow(item.tabNumber);
        }
      });
      this.showPanelFinalize(name, callback, wasShown);
    }

    /**
     * @internal
     *
     * @param {string} name
     * @param [callback] Not to be used.
     */
    processHidePanel(name, callback) {
      if (!this.recordHelper.getPanelStateParam(name, 'hidden')) {
        return;
      }
      if (!this.hasPanel(name)) {
        return;
      }
      this.panelList.filter(item => item.name === name).forEach(item => {
        item.hidden = true;
        if (typeof item.tabNumber !== 'undefined') {
          this.controlTabVisibilityHide(item.tabNumber);
        }
      });
      this.hidePanelFinalize(name, callback);
    }

    /**
     * @private
     *
     * @param {string} name
     * @param [callback] Not to be used.
     * @param {boolean} [wasShown]
     */
    async showPanelFinalize(name, callback, wasShown) {
      const process = wasRendered => {
        const view = this.getPanelView(name);
        if (view) {
          if (view.element) {
            const panelElement = view.element.closest('.panel');
            if (panelElement) {
              panelElement.classList.remove('hidden');
            }
          }
          view.disabled = false;
          view.trigger('show');
          view.trigger('panel-show-propagated');
          if (wasRendered && !wasShown && view.getFieldViews) {
            const fields = view.getFieldViews();
            if (fields) {
              for (const i in fields) {
                fields[i].reRender();
              }
            }
          }
        }
        if (typeof callback === 'function') {
          callback.call(this);
        }
      };
      if (this.isRendered()) {
        process(true);
        this.adjustPanels();
        return;
      }
      await this.whenRendered();
      process();
    }

    /**
     * @private
     *
     * @param {string} name
     * @param [callback] Not to be used.
     */
    async hidePanelFinalize(name, callback) {
      if (this.isRendered()) {
        const view = this.getPanelView(name);
        if (view) {
          if (view.element) {
            const panelElement = view.element.closest('.panel');
            if (panelElement) {
              panelElement.classList.add('hidden');
            }
          }
          view.disabled = true;
          view.trigger('hide');
        }
        if (typeof callback === 'function') {
          callback();
        }
        this.adjustPanels();
        return;
      }
      if (typeof callback === 'function') {
        await this.whenRendered();
        callback();
      }
    }

    /**
     * @param {string} name
     * @param {string} [softLockedType]
     * @param [callback] Not to be used.
     */
    showPanel(name, softLockedType, callback) {
      if (this.recordHelper.getPanelStateParam(name, 'hiddenLocked')) {
        return;
      }
      if (softLockedType) {
        const param = 'hidden' + Espo.Utils.upperCaseFirst(softLockedType) + 'Locked';
        this.recordHelper.setPanelStateParam(name, param, false);
        for (let i = 0; i < this.panelSoftLockedTypeList.length; i++) {
          const iType = this.panelSoftLockedTypeList[i];
          if (iType === softLockedType) {
            continue;
          }
          const iParam = 'hidden' + Espo.Utils.upperCaseFirst(iType) + 'Locked';
          if (this.recordHelper.getPanelStateParam(name, iParam)) {
            return;
          }
        }
      }
      const wasShown = this.recordHelper.getPanelStateParam(name, 'hidden') === false;
      this.recordHelper.setPanelStateParam(name, 'hidden', false);
      this.processShowPanel(name, callback, wasShown);
    }

    /**
     * @param {string} name
     * @param {boolean} [locked=false]
     * @param {string} [softLockedType]
     * @param [callback] Not to be used.
     */
    hidePanel(name, locked, softLockedType, callback) {
      this.recordHelper.setPanelStateParam(name, 'hidden', true);
      if (locked) {
        this.recordHelper.setPanelStateParam(name, 'hiddenLocked', true);
      }
      if (softLockedType) {
        const param = 'hidden' + Espo.Utils.upperCaseFirst(softLockedType) + 'Locked';
        this.recordHelper.setPanelStateParam(name, param, true);
      }
      this.processHidePanel(name, callback);
    }

    /**
     * @internal
     * @protected
     * @param {Object} layoutData
     */
    alterPanels(layoutData) {
      layoutData = layoutData || this.layoutData || {};
      const tabBreakIndexList = [];
      const tabDataList = [];
      for (const name in layoutData) {
        const item = layoutData[name];
        if (name === '_delimiter_') {
          this.panelList.push({
            name: name
          });
        }
        if (item.tabBreak) {
          tabBreakIndexList.push(item.index);
          tabDataList.push({
            index: item.index,
            label: item.tabLabel
          });
        }
      }

      /**
       * @private
       * @type {Object.<string, *>[]}
       */
      this.tabDataList = tabDataList.sort((v1, v2) => v1.index - v2.index);
      this.panelList = this.panelList.filter(item => {
        return !this.recordHelper.getPanelStateParam(item.name, 'hiddenLocked');
      });
      const newList = [];
      this.panelList.forEach((item, i) => {
        item.index = 'index' in item ? item.index : i;
        let allowedInLayout = false;
        if (item.name) {
          const itemData = layoutData[item.name] || {};
          if (itemData.disabled) {
            return;
          }
          if (layoutData[item.name]) {
            allowedInLayout = true;
          }
          for (const i in itemData) {
            item[i] = itemData[i];
          }
        }
        if (item.disabled && !allowedInLayout) {
          return;
        }
        item.tabNumber = tabBreakIndexList.length - tabBreakIndexList.slice().reverse().findIndex(index => item.index > index) - 1;
        if (item.tabNumber === tabBreakIndexList.length) {
          item.tabNumber = -1;
        }
        newList.push(item);
      });
      newList.sort((v1, v2) => v1.index - v2.index);
      const firstTabIndex = newList.findIndex(item => item.tabNumber !== -1);

      /*let firstNonHiddenTabIndex = newList.findIndex(item => item.tabNumber !== -1 && !item.hidden);
        if (firstNonHiddenTabIndex < 0) {
          firstNonHiddenTabIndex = firstTabIndex;
      }*/

      if (firstTabIndex !== -1) {
        newList[firstTabIndex].isTabsBeginning = true;
        this.hasTabs = true;
        this.currentTab = newList[firstTabIndex].tabNumber;
        this.panelList.filter(item => item.tabNumber !== -1 && item.tabNumber !== this.currentTab).forEach(item => {
          item.tabHidden = true;
        });
        this.panelList.forEach((item, i) => {
          if (item.tabNumber !== -1 && (i === 0 || this.panelList[i - 1].tabNumber !== item.tabNumber)) {
            item.sticked = false;
          }
        });
      }
      this.panelList = newList;
      if (this.recordViewObject && this.recordViewObject.dynamicLogic) {
        const dynamicLogic = this.recordViewObject.dynamicLogic;
        this.panelList.forEach(item => {
          if (item.dynamicLogicVisible) {
            dynamicLogic.addPanelVisibleCondition(item.name, item.dynamicLogicVisible);
            if (this.recordHelper.getPanelStateParam(item.name, 'hidden')) {
              item.hidden = true;
            }
          }
          if (item.style && item.style !== 'default' && item.dynamicLogicStyled) {
            dynamicLogic.addPanelStyledCondition(item.name, item.dynamicLogicStyled);
          }
        });
      }
      if (this.hasTabs && this.options.isReturn && this.isStoredTabForThisRecord()) {
        this.selectStoredTab();
      }
    }

    /**
     * @protected
     */
    setupPanelsFinal() {
      let afterDelimiter = false;
      let rightAfterDelimiter = false;
      let index = -1;
      this.panelList.forEach((p, i) => {
        if (p.name === '_delimiter_') {
          afterDelimiter = true;
          rightAfterDelimiter = true;
          index = i;
          return;
        }
        if (afterDelimiter) {
          p.hidden = true;
          p.hiddenAfterDelimiter = true;
          this.recordHelper.setPanelStateParam(p.name, 'hidden', true);
          this.recordHelper.setPanelStateParam(p.name, 'hiddenDelimiterLocked', true);
        }
        if (rightAfterDelimiter) {
          p.isRightAfterDelimiter = true;
          rightAfterDelimiter = false;
        }
      });
      if (~index) {
        this.panelList.splice(index, 1);
      }
      this.panelsAreSet = true;
      this.trigger('panels-set');
    }
    actionShowMorePanels() {
      this.panelList.forEach(/** module:views/record/panels-container~panel & Record*/p => {
        if (!p.hiddenAfterDelimiter) {
          return;
        }
        delete p.isRightAfterDelimiter;
        this.showPanel(p.name, 'delimiter');
      });
      this.$el.find('.panels-show-more-delimiter').remove();
    }

    /**
     * @internal
     * @protected
     * @param {function} callback
     */
    onPanelsReady(callback) {
      Promise.race([new Promise(resolve => {
        if (this.panelsAreSet) {
          resolve();
        }
      }), new Promise(resolve => {
        this.once('panels-set', resolve);
      })]).then(() => {
        callback.call(this);
      });
    }

    /**
     * @private
     * @return {{
     *     label: string,
     *     isActive: boolean,
     *     hidden: boolean,
     * }[]}
     */
    getTabDataList() {
      return this.tabDataList.map((item, i) => {
        let label = item.label;
        if (!label) {
          label = (i + 1).toString();
        } else if (label[0] === '$') {
          label = this.translate(label.substring(1), 'tabs', this.scope);
        }
        const hidden = this.panelList.filter(panel => panel.tabNumber === i).findIndex(panel => !this.recordHelper.getPanelStateParam(panel.name, 'hidden')) === -1;
        return {
          label: label,
          isActive: i === this.currentTab,
          hidden: hidden
        };
      });
    }

    /**
     * @private
     * @param {number} tab
     * @param {boolean} [doNotStore]
     */
    selectTab(tab) {
      let doNotStore = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
      this.currentTab = tab;
      if (this.isRendered()) {
        $('body > .popover').remove();
        this.$el.find('.tabs > button').removeClass('active');
        this.$el.find(`.tabs > button[data-tab="${tab}"]`).addClass('active');
        this.$el.find('.panel[data-tab]:not([data-tab="-1"])').addClass('tab-hidden');
        this.$el.find(`.panel[data-tab="${tab}"]`).removeClass('tab-hidden');
      }
      this.adjustPanels();
      this.panelList.filter(item => item.tabNumber === tab && item.name).forEach(item => {
        const view = this.getPanelView(item.name);
        if (view) {
          view.trigger('tab-show');
          view.propagateEvent('panel-show-propagated');
        }
        item.tabHidden = false;
      });
      this.panelList.filter(item => item.tabNumber !== tab && item.name).forEach(item => {
        const view = this.getPanelView(item.name);
        if (view) {
          view.trigger('tab-hide');
        }
        if (item.tabNumber > -1) {
          item.tabHidden = true;
        }
      });
      if (doNotStore) {
        return;
      }
      this.storeTab();
    }

    /** @private */
    storeTab() {
      const key = `tab_${this.name}`;
      const keyRecord = `tab_${this.name}_record`;
      this.getSessionStorage().set(key, this.currentTab);
      this.getSessionStorage().set(keyRecord, `${this.entityType}_${this.model.id}`);
    }

    /** @private */
    isStoredTabForThisRecord() {
      const keyRecord = `tab_${this.name}_record`;
      return this.getSessionStorage().get(keyRecord) === `${this.entityType}_${this.model.id}`;
    }

    /** @private */
    selectStoredTab() {
      const key = `tab_${this.name}`;
      const tab = this.getSessionStorage().get(key);
      if (tab > 0) {
        this.selectTab(tab);
      }
    }

    /**
     * @private
     * @param {number} tab
     */
    async controlTabVisibilityShow(tab) {
      if (!this.hasTabs) {
        return;
      }
      await this.whenRendered();
      if (this.element) {
        const tabElement = this.element.querySelector(`.tabs > [data-tab="${tab.toString()}"]`);
        if (tabElement) {
          tabElement.classList.remove('hidden');
        }
      }
    }

    /**
     * @private
     * @param {number} tab
     */
    async controlTabVisibilityHide(tab) {
      if (!this.hasTabs) {
        return;
      }
      await this.whenRendered();
      const panelList = this.panelList.filter(panel => panel.tabNumber === tab);
      const allHidden = panelList.findIndex(panel => !this.recordHelper.getPanelStateParam(panel.name, 'hidden')) === -1;
      if (!allHidden) {
        return;
      }
      if (this.element) {
        const tabElement = this.element.querySelector(`.tabs > [data-tab="${tab.toString()}"]`);
        if (tabElement) {
          tabElement.classList.add('hidden');
        }
      }
      if (this.currentTab === tab) {
        const firstVisiblePanel = this.panelList.find(panel => panel.tabNumber > -1 && !panel.hidden);
        const firstVisibleTab = firstVisiblePanel ? firstVisiblePanel.tabNumber : 0;
        this.selectTab(firstVisibleTab, true);
      }
    }

    /**
     * @protected
     */
    setupInitial() {
      // Handles the situation when the first tab is hidden.
      this.listenToOnce(this.model, 'sync', async (m, r, /** Record */o) => {
        if (o.action !== 'fetch') {
          return;
        }
        setTimeout(async () => {
          await this.whenRendered();
          if (!this.hasTabs) {
            return;
          }
          const firstVisiblePanel = this.panelList.find(p => !p.hidden && p.tabNumber > -1);
          if (!firstVisiblePanel) {
            return;
          }
          if (firstVisiblePanel.tabNumber > this.currentTab) {
            this.selectTab(firstVisiblePanel.tabNumber, true);
          }
        }, 1);
      });
    }
  }
  var _default = _exports.default = PanelsContainerRecordView;
});

define("views/record/list-expanded", ["exports", "views/record/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 module:views/record/list-expanded */

  class ListExpandedRecordView extends _list.default {
    template = 'record/list-expanded';
    checkboxes = false;
    selectable = false;
    rowActionsView = false;
    _internalLayoutType = 'list-row-expanded';
    presentationType = 'expanded';
    paginationDisabled = true;
    header = false;
    _internalLayout = null;
    checkedList = null;
    listContainerEl = '.list > ul';
    columnResize = false;
    init() {
      if (this.options.forcePagination) {
        this.paginationDisabled = false;
      }
      super.init();
    }
    setup() {
      super.setup();
      this.on('after:save', model => {
        const view = this.getView(model.id);
        if (!view) {
          return;
        }
        view.reRender();
      });

      // Prevents displaying an empty buttons container.
      this.displayTotalCount = false;
    }
    _loadListLayout(callback) {
      const type = this.type + 'Expanded';
      this.layoutLoadCallbackList.push(callback);
      if (this.layoutIsBeingLoaded) {
        return;
      }
      this.layoutIsBeingLoaded = true;
      this._helper.layoutManager.get(this.collection.entityType, type, listLayout => {
        this.layoutLoadCallbackList.forEach(c => {
          c(listLayout);
          this.layoutLoadCallbackList = [];
          this.layoutIsBeingLoaded = false;
        });
      });
    }
    _convertLayout(listLayout, model) {
      model = model || this.collection.prepareModel();
      const layout = {
        rows: [],
        right: false
      };
      for (const i in listLayout.rows) {
        const row = listLayout.rows[i];
        const layoutRow = [];
        for (const j in row) {
          const rowItem = row[j];
          const type = rowItem.type || model.getFieldType(rowItem.name) || 'base';
          const item = {
            name: rowItem.name + 'Field',
            field: rowItem.name,
            view: rowItem.view || model.getFieldParam(rowItem.name, 'view') || this.getFieldManager().getViewName(type),
            options: {
              defs: {
                name: rowItem.name,
                params: rowItem.params || {}
              },
              mode: 'list'
            },
            align: rowItem.align,
            small: rowItem.small,
            soft: rowItem.soft
          };
          if (rowItem.options) {
            for (const optionName in rowItem.options) {
              if (typeof item.options[optionName] !== 'undefined') {
                continue;
              }
              item.options[optionName] = rowItem.options[optionName];
            }
          }
          if (rowItem.link) {
            item.options.mode = 'listLink';
          }
          layoutRow.push(item);
        }
        layout.rows.push(layoutRow);
      }
      if ('right' in listLayout) {
        if (listLayout.right) {
          const name = listLayout.right.name || 'right';
          layout.right = {
            field: name,
            name: name,
            view: listLayout.right.view,
            options: {
              defs: {
                params: {
                  width: listLayout.right.width || '7%'
                }
              }
            }
          };
        }
      } else if (this.rowActionsView) {
        layout.right = this.getRowActionsDefs();
      }
      return layout;
    }
    getRowSelector(id) {
      return 'li[data-id="' + id + '"]';
    }
    getCellSelector(model, item) {
      const name = item.field || item.columnName;
      return `${this.getSelector()} ${this.getRowSelector(model.id)} .cell[data-name="${name}"]`;
    }
    getRowContainerHtml(id) {
      return $('<li>').attr('data-id', id).addClass('list-group-item list-row').get(0).outerHTML;
    }
    prepareInternalLayout(internalLayout, model) {
      const rows = internalLayout.rows || [];
      rows.forEach(row => {
        row.forEach(col => {
          col.fullSelector = this.getCellSelector(model, col);
        });
      });
      if (internalLayout.right) {
        internalLayout.right.fullSelector = this.getCellSelector(model, internalLayout.right);
      }
    }
    fetchAttributeListFromLayout() {
      const list = [];
      if (this.listLayout.rows) {
        this.listLayout.rows.forEach(row => {
          row.forEach(item => {
            if (!item.name) {
              return;
            }
            const field = item.name;
            const fieldType = this.getMetadata().get(['entityDefs', this.scope, 'fields', field, 'type']);
            if (!fieldType) {
              return;
            }
            this.getFieldManager().getEntityTypeFieldAttributeList(this.scope, field).forEach(attribute => {
              list.push(attribute);
            });
          });
        });
      }
      return list;
    }
  }
  var _default = _exports.default = ListExpandedRecordView;
});

define("views/record/panels/bottom", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/panels/bottom */

  /**
   * @typedef {Object} module:views/record/panels/bottom~defs
   * @property [buttonList]
   * @property [actionList]
   * @property [tabNumber] For internal purposes.
   */

  /**
   * A bottom panel.
   */
  class BottomPanelView extends _view.default {
    template = 'record/panels/side';

    /**
     * A field list.
     *
     * @protected
     * @type {module:views/record/panels/side~field[]}
     */
    fieldList;

    /**
     * @protected
     * @type {Array<module:views/record/panels-container~action|false>}
     */
    actionList;

    /**
     * @protected
     * @type {module:views/record/panels-container~button[]}
     */
    buttonList;

    /**
     * @protected
     * @type {module:views/record/panels/bottom~defs|Object.<string, *>}
     */
    defs;

    /**
     * A mode.
     *
     * @protected
     * @type {'list'|'detail'|'edit'}
     */
    mode = 'detail';

    /**
     * Disable.
     *
     * @protected
     */
    disabled = false;
    events = {
      /** @this BottomPanelView */
      'click .action': function (e) {
        Espo.Utils.handleAction(this, e.originalEvent, e.currentTarget, {
          actionItems: [...this.buttonList, ...this.actionList],
          className: 'panel-action'
        });
      }
    };
    data() {
      return {
        scope: this.scope,
        entityType: this.entityType,
        name: this.panelName,
        hiddenFields: this.recordHelper.getHiddenFields(),
        fieldList: this.getFieldList()
      };
    }
    init() {
      this.panelName = this.options.panelName;
      this.defs = this.options.defs || {};
      this.recordHelper = this.options.recordHelper;
      if ('disabled' in this.options) {
        this.disabled = this.options.disabled;
      }
      this.mode = this.options.mode || this.mode;
      this.readOnlyLocked = this.options.readOnlyLocked || this.readOnly;
      this.readOnly = this.readOnly || this.options.readOnly;
      this.inlineEditDisabled = this.inlineEditDisabled || this.options.inlineEditDisabled;
      this.buttonList = Espo.Utils.clone(this.defs.buttonList || this.buttonList || []);
      this.actionList = Espo.Utils.clone(this.defs.actionList || this.actionList || []);
      this.actionList.forEach(it => {
        if (it.name) {
          it.action = it.name;
        }
      });
      this.fieldList = this.options.fieldList || this.fieldList || [];
      this.recordViewObject = this.options.recordViewObject;
    }
    setup() {
      this.setupFields();
      this.fieldList = this.fieldList.map(d => {
        let item = d;
        if (typeof item !== 'object') {
          item = {
            name: item,
            viewKey: item + 'Field'
          };
        }
        item = Espo.Utils.clone(item);
        item.viewKey = item.name + 'Field';
        item.label = item.label || item.name;
        if (this.recordHelper.getFieldStateParam(item.name, 'hidden') !== null) {
          item.hidden = this.recordHelper.getFieldStateParam(item.name, 'hidden');
        } else {
          this.recordHelper.setFieldStateParam(item.name, 'hidden', item.hidden || false);
        }
        return item;
      });
      this.fieldList = this.fieldList.filter(item => {
        if (!item.name) {
          return;
        }
        if (!(item.name in ((this.model.defs || {}).fields || {}))) {
          return;
        }
        return true;
      });
      this.createFields();
    }

    /**
     * Set up fields.
     *
     * @protected
     */
    setupFields() {}

    /**
     * @return {module:views/record/panels-container~button[]}
     */
    getButtonList() {
      return this.buttonList || [];
    }

    /**
     * @return {module:views/record/panels-container~action[]}
     */
    getActionList() {
      return this.actionList || [];
    }

    /**
     * Get field views.
     *
     * @return {Object.<string,module:views/fields/base>}
     */
    getFieldViews() {
      const fields = {};
      this.getFieldList().forEach(item => {
        if (this.hasView(item.viewKey)) {
          fields[item.name] = this.getView(item.viewKey);
        }
      });
      return fields;
    }

    /**
     * @deprecated Use `getFieldViews`.
     * @todo Remove in v10.0.
     */
    getFields() {
      return this.getFieldViews();
    }

    /**
     * Get a field list.
     *
     * @return {module:views/record/panels/side~field[]}
     */
    getFieldList() {
      return this.fieldList.map(item => {
        if (typeof item !== 'object') {
          return {
            name: item
          };
        }
        return item;
      });
    }

    /**
     * @private
     */
    createFields() {
      this.getFieldList().forEach(item => {
        let view = null;
        let field;
        let readOnly = null;
        if (typeof item === 'object') {
          field = item.name;
          view = item.view;
          if ('readOnly' in item) {
            readOnly = item.readOnly;
          }
        } else {
          field = item;
        }
        if (!(field in this.model.defs.fields)) {
          return;
        }
        this.createField(field, view, null, null, readOnly);
      });
    }

    /**
     * Create a field view.
     *
     * @protected
     * @param {string} field A field name.
     * @param {string|null} [viewName] A view name/path.
     * @param {Object<string,*>} [params] Field params.
     * @param {'detail'|'edit'|'list'|null} [mode='edit'] A mode.
     * @param {boolean} [readOnly] Read-only.
     * @param {Object<string,*>} [options] View options.
     */
    createField(field, viewName, params, mode, readOnly, options) {
      const type = this.model.getFieldType(field) || 'base';
      viewName = viewName || this.model.getFieldParam(field, 'view') || this.getFieldManager().getViewName(type);
      const o = {
        model: this.model,
        selector: '.field[data-name="' + field + '"]',
        defs: {
          name: field,
          params: params || {}
        },
        mode: mode || this.mode,
        dataObject: this.options.dataObject
      };
      if (options) {
        for (const param in options) {
          o[param] = options[param];
        }
      }
      let readOnlyLocked = this.readOnlyLocked;
      if (this.readOnly) {
        o.readOnly = true;
      } else {
        if (readOnly !== null) {
          o.readOnly = readOnly;
        }
      }
      if (readOnly) {
        readOnlyLocked = true;
      }
      if (this.inlineEditDisabled) {
        o.inlineEditDisabled = true;
      }
      if (this.recordHelper.getFieldStateParam(field, 'hidden')) {
        o.disabled = true;
      }
      if (this.recordHelper.getFieldStateParam(field, 'hiddenLocked')) {
        o.disabledLocked = true;
      }
      if (this.recordHelper.getFieldStateParam(field, 'readOnly')) {
        o.readOnly = true;
      }
      if (this.recordHelper.getFieldStateParam(field, 'required') !== null) {
        o.defs.params.required = this.recordHelper.getFieldStateParam(field, 'required');
      }
      if (!readOnlyLocked && this.recordHelper.getFieldStateParam(field, 'readOnlyLocked')) {
        readOnlyLocked = true;
      }
      if (readOnlyLocked) {
        o.readOnlyLocked = readOnlyLocked;
      }
      if (this.recordHelper.hasFieldOptionList(field)) {
        o.customOptionList = this.recordHelper.getFieldOptionList(field);
      }
      if (this.recordViewObject) {
        o.validateCallback = () => this.recordViewObject.validateField(field);
      }
      const viewKey = field + 'Field';
      this.createView(viewKey, viewName, o);
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Is tab-hidden.
     *
     * @return {boolean}
     */
    isTabHidden() {
      if (this.defs.tabNumber === -1 || typeof this.defs.tabNumber === 'undefined') {
        return false;
      }
      const parentView = this.getParentView();
      if (!parentView) {
        return this.defs.tabNumber > 0;
      }

      // noinspection JSUnresolvedReference
      if (parentView && parentView.hasTabs) {
        return parentView.currentTab !== this.defs.tabNumber;
      }
      return false;
    }
  }
  var _default = _exports.default = BottomPanelView;
});

define("views/fields/wysiwyg", ["exports", "views/fields/text", "helpers/misc/summernote-custom"], function (_exports, _text, _summernoteCustom) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _text = _interopRequireDefault(_text);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/wysiwyg */

  /**
   * A wysiwyg field.
   *
   * @extends TextFieldView<module:views/fields/wysiwyg~params>
   */
  class WysiwygFieldView extends _text.default {
    /**
     * @typedef {Object} module:views/fields/wysiwyg~options
     * @property {
     *     module:views/fields/wysiwyg~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/wysiwyg~params
     * @property {boolean} [required] Required.
     * @property {number} [maxLength] A max length.
     * @property {number} [height] A height in pixels.
     * @property {number} [minHeight] A min height in pixels.
     * @property {boolean} [useIframe] Use iframe.
     * @property {Array} [toolbar] A custom toolbar.
     * @property {string} [attachmentField] An attachment field name.
     */

    /**
     * @param {
     *     module:views/fields/wysiwyg~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'wysiwyg';
    listTemplate = 'fields/wysiwyg/detail';
    detailTemplate = 'fields/wysiwyg/detail';
    editTemplate = 'fields/wysiwyg/edit';
    height = 250;
    rowsDefault = 10000;
    fallbackBodySideMargin = 5;
    fallbackBodyTopMargin = 4;
    seeMoreDisabled = true;
    fetchEmptyValueAsNull = true;
    validationElementSelector = '.note-editor';
    htmlPurificationDisabled = false;
    htmlPurificationForEditDisabled = false;
    tableClassName = 'table table-bordered';
    noStylesheet = false;
    useIframe = false;
    handlebars = false;

    /** @protected */
    toolbar;
    /** @protected */
    hasBodyPlainField = false;
    events = {
      /** @this WysiwygFieldView */
      'click .note-editable': function () {
        this.fixPopovers();
      },
      /** @this WysiwygFieldView */
      'focus .note-editable': function () {
        this.$noteEditor.addClass('in-focus');
      },
      /** @this WysiwygFieldView */
      'blur .note-editable': function () {
        this.$noteEditor.removeClass('in-focus');
      }
    };
    setup() {
      super.setup();
      this.loadSummernote();
      if ('height' in this.params) {
        this.height = this.params.height;
      }
      if ('minHeight' in this.params) {
        this.minHeight = this.params.minHeight;
      }
      this.useIframe = this.params.useIframe || this.useIframe;
      this.setupToolbar();
      this.setupIsHtml();
      this.once('remove', () => this.destroySummernote());
      this.on('inline-edit-off', () => this.destroySummernote());
      this.on('render', () => this.destroySummernote());
      this.once('remove', () => {
        $(window).off(`resize.${this.cid}`);
        if (this.$scrollable) {
          this.$scrollable.off(`scroll.${this.cid}-edit`);
        }
      });
    }

    /** @private */
    loadSummernote() {
      this.wait(Espo.loader.requirePromise('lib!summernote').then(() => {
        if (!$.summernote.options || 'espoImage' in $.summernote.options) {
          return;
        }
        this.initEspoPlugin();
      }));
    }

    /** @protected */
    setupIsHtml() {
      if (!this.hasBodyPlainField) {
        return;
      }
      this.listenTo(this.model, 'change:isHtml', (model, value, o) => {
        if (o.ui && this.isEditMode()) {
          if (!this.isRendered()) {
            return;
          }
          if (this.isHtml()) {
            let value = this.plainToHtml(this.model.get(this.name));
            if (this.lastHtmlValue && this.model.get(this.name) === this.htmlToPlain(this.lastHtmlValue)) {
              value = this.lastHtmlValue;
            }
            this.model.set(this.name, value, {
              skipReRender: true
            });
            this.enableWysiwygMode();
            return;
          }
          this.lastHtmlValue = this.model.get(this.name);
          const value = this.htmlToPlain(this.model.get(this.name));
          this.disableWysiwygMode();
          this.model.set(this.name, value);
          return;
        }
        if (this.isDetailMode() && this.isRendered()) {
          this.reRender();
        }
      });
    }
    data() {
      const data = super.data();
      data.useIframe = this.useIframe;
      data.isPlain = !this.isHtml();
      data.isNone = !data.isNotEmpty && data.valueIsSet && this.isDetailMode();

      // noinspection JSValidateTypes
      return data;
    }
    setupToolbar() {
      this.buttons = {};
      const codeviewName = this.getConfig().get('wysiwygCodeEditorDisabled') ? 'codeview' : 'aceCodeview';
      this.toolbar = this.params.toolbar || this.toolbar || [['style', ['style']], ['style', ['bold', 'italic', 'underline', 'clear']], ['fontsize', ['fontsize']], ['color', ['color']], ['para', ['ul', 'ol', 'paragraph']], ['height', ['height']], ['table', ['espoTable', 'espoLink', 'espoImage', 'hr']], ['misc', [codeviewName, 'fullscreen']]];
      if (this.params.toolbar) {
        return;
      }
      if (!this.params.attachmentField) {
        return;
      }
      this.toolbar.push(['attachment', ['attachment']]);
      this.buttons['attachment'] = () => {
        const ui = $.summernote.ui;
        const button = ui.button({
          contents: '<i class="fas fa-paperclip"></i>',
          tooltip: this.translate('Attach File'),
          click: () => {
            this.attachFile();
          }
        });
        return button.render();
      };
    }

    /**
     * @protected
     * @return {boolean}
     */
    isHtml() {
      if (!this.hasBodyPlainField) {
        return true;
      }
      return !this.model.has('isHtml') || this.model.get('isHtml');
    }
    fixPopovers() {
      $('body > .note-popover').removeClass('hidden');
    }
    getValueForDisplay() {
      if (!this.isReadMode() && this.isHtml()) {
        return undefined;
      }
      const value = super.getValueForDisplay();
      if (!this.isHtml()) {
        return value;
      }
      return this.sanitizeHtml(value);
    }

    /**
     * @protected
     * @param {string} value
     * @return {string}
     */
    sanitizeHtml(value) {
      if (!value) {
        return '';
      }
      if (this.htmlPurificationDisabled) {
        return this.sanitizeHtmlLight(value);
      }
      value = this.getHelper().sanitizeHtml(value);
      if (this.isEditMode()) {
        // Trick to handle the issue that attributes are re-ordered.
        value = this.getHelper().sanitizeHtml(value);
      }
      return value;
    }
    sanitizeHtmlLight(value) {
      return this.getHelper().moderateSanitizeHtml(value);
    }
    getValueForEdit() {
      const value = this.model.get(this.name) || '';
      if (this.htmlPurificationForEditDisabled) {
        return this.sanitizeHtmlLight(value);
      }
      return this.sanitizeHtml(value);
    }
    afterRender() {
      super.afterRender();
      if (this.isEditMode()) {
        this.$summernote = this.$el.find('.summernote');
      }
      const language = this.getConfig().get('language');
      if (!(language in $.summernote.lang)) {
        $.summernote.lang[language] = this.getLanguage().translate('summernote', 'sets');
      }
      if (this.isEditMode()) {
        if (this.isHtml()) {
          this.enableWysiwygMode();
        } else {
          this.$element.removeClass('hidden');
        }
        if (this.params.attachmentField && this.isInlineEditMode()) {
          this.$el.find('.note-attachment').addClass('hidden');
        }
      }
      if (this.isReadMode()) {
        this.renderDetail();
      }
    }
    renderDetail() {
      if (!this.isHtml()) {
        this.$el.find('.plain').removeClass('hidden');
        return;
      }
      if (!this.useIframe) {
        this.$element = this.$el.find('.html-container');
        return;
      }
      this.$el.find('iframe').removeClass('hidden');
      const $iframe = this.$el.find('iframe');

      /** @type {HTMLIFrameElement} */
      const iframeElement = this.iframe = $iframe.get(0);
      iframeElement.setAttribute('sandbox', '');
      if (!iframeElement || !iframeElement.contentWindow) {
        return;
      }
      $iframe.on('load', () => {
        $iframe.contents().find('a').attr('target', '_blank');
      });
      const documentElement = iframeElement.contentWindow.document;
      let bodyHtml = this.getValueForIframe();
      const useFallbackStylesheet = this.getThemeManager().getParam('isDark') && this.htmlHasColors(bodyHtml);
      const addFallbackClass = this.getThemeManager().getParam('isDark') && (this.htmlHasColors(bodyHtml) || this.noStylesheet);
      const $iframeContainer = $iframe.parent();
      addFallbackClass ? $iframeContainer.addClass('fallback') : $iframeContainer.removeClass('fallback');
      if (!this.noStylesheet) {
        const linkElement = iframeElement.contentWindow.document.createElement('link');
        linkElement.type = 'text/css';
        linkElement.rel = 'stylesheet';
        linkElement.href = this.getBasePath() + (useFallbackStylesheet ? this.getThemeManager().getIframeFallbackStylesheet() : this.getThemeManager().getIframeStylesheet());
        bodyHtml = linkElement.outerHTML + bodyHtml;
      }
      let headHtml = '';
      if (this.noStylesheet) {
        const styleElement = documentElement.createElement('style');
        styleElement.textContent = `\ntable.bordered, table.bordered td, table.bordered th {border: 1px solid;}\n`;
        headHtml = styleElement.outerHTML;
      }

      // noinspection HtmlRequiredTitleElement
      const documentHtml = `<head>${headHtml}</head><body>${bodyHtml}</body>`;
      documentElement.write(documentHtml);
      documentElement.close();
      const $body = $iframe.contents().find('html body');
      $body.find('img').each((i, img) => {
        const $img = $(img);
        if ($img.css('max-width') !== 'none') {
          return;
        }
        $img.css('max-width', '100%');
      });
      const $document = $(documentElement);

      // Make dropdowns closed.
      $document.on('click', () => {
        const event = new MouseEvent('click', {
          bubbles: true
        });
        $iframe[0].dispatchEvent(event);
      });

      // Make notifications & global-search popup closed.
      $document.on('mouseup', () => {
        const event = new MouseEvent('mouseup', {
          bubbles: true
        });
        $iframe[0].dispatchEvent(event);
      });

      // Make shortcuts working.
      $document.on('keydown', e => {
        const originalEvent = /** @type {KeyboardEvent} */e.originalEvent;
        const event = new KeyboardEvent('keydown', {
          bubbles: true,
          code: originalEvent.code,
          ctrlKey: originalEvent.ctrlKey,
          metaKey: originalEvent.metaKey,
          altKey: originalEvent.altKey
        });
        $iframe[0].dispatchEvent(event);
      });
      const processWidth = function () {
        const bodyElement = $body.get(0);
        if (bodyElement) {
          if (bodyElement.clientWidth !== iframeElement.scrollWidth) {
            iframeElement.style.height = iframeElement.scrollHeight + 20 + 'px';
          }
        }
      };
      if (useFallbackStylesheet) {
        $iframeContainer.css({
          paddingLeft: this.fallbackBodySideMargin + 'px',
          paddingRight: this.fallbackBodySideMargin + 'px',
          paddingTop: this.fallbackBodyTopMargin + 'px'
        });
      }
      const increaseHeightStep = 10;
      const processIncreaseHeight = function (iteration, previousDiff) {
        $body.css('height', '');
        iteration = iteration || 0;
        if (iteration > 200) {
          return;
        }
        iteration++;
        const diff = $document.height() - iframeElement.scrollHeight;
        if (typeof previousDiff !== 'undefined') {
          if (diff === previousDiff) {
            $body.css('height', iframeElement.clientHeight - increaseHeightStep + 'px');
            processWidth();
            return;
          }
        }
        if (diff) {
          const height = iframeElement.scrollHeight + increaseHeightStep;
          iframeElement.style.height = height + 'px';
          processIncreaseHeight(iteration, diff);
        } else {
          processWidth();
        }
      };
      const processBg = () => {
        const color = iframeElement.contentWindow.getComputedStyle($body.get(0)).backgroundColor;
        $iframeContainer.css({
          backgroundColor: color
        });
      };
      const processHeight = function (isOnLoad) {
        if (!isOnLoad) {
          $iframe.css({
            overflowY: 'hidden',
            overflowX: 'hidden'
          });
          iframeElement.style.height = '0px';
        } else {
          if (iframeElement.scrollHeight >= $document.height()) {
            return;
          }
        }
        const $body = $iframe.contents().find('html body');
        let height = $body.height();
        if (height === 0) {
          height = $body.children().height() + 100;
        }
        iframeElement.style.height = height + 'px';
        processIncreaseHeight();
        if (!isOnLoad) {
          $iframe.css({
            overflowY: 'hidden',
            overflowX: 'scroll'
          });
        }
      };
      $iframe.css({
        visibility: 'hidden'
      });
      setTimeout(() => {
        processHeight();
        $iframe.css({
          visibility: 'visible'
        });
        $iframe.on('load', () => {
          processHeight(true);
          if (useFallbackStylesheet && !this.noStylesheet) {
            processBg();
          }
        });
      }, 40);
      if (!this.model.get(this.name)) {
        $iframe.addClass('hidden');
      }
      let windowWidth = $(window).width();
      $(window).off('resize.' + this.cid);
      $(window).on('resize.' + this.cid, () => {
        if ($(window).width() !== windowWidth) {
          processHeight();
          windowWidth = $(window).width();
        }
      });
    }

    /**
     * @protected
     * @return {string}
     */
    getValueForIframe() {
      return this.sanitizeHtml(this.model.get(this.name) || '');
    }
    enableWysiwygMode() {
      if (!this.$element) {
        return;
      }
      this.$element.addClass('hidden');
      this.$summernote.removeClass('hidden');
      const contents = this.getValueForEdit();
      this.$summernote.html(contents);

      // The same sanitizing in the email body field.
      this.$summernote.find('style').remove();
      this.$summernote.find('link[ref="stylesheet"]').remove();
      const keyMap = Espo.Utils.cloneDeep($.summernote.options.keyMap);
      keyMap.pc['CTRL+K'] = 'espoLink.show';
      keyMap.mac['CMD+K'] = 'espoLink.show';
      keyMap.pc['CTRL+DELETE'] = 'removeFormat';
      keyMap.mac['CMD+DELETE'] = 'removeFormat';
      delete keyMap.pc['CTRL+ENTER'];
      delete keyMap.mac['CMD+ENTER'];
      delete keyMap.pc['CTRL+BACKSLASH'];
      delete keyMap.mac['CMD+BACKSLASH'];
      const toolbar = this.toolbar;
      let lastChangeKeydown = new Date();
      const changeKeydownInterval = this.changeInterval * 1000;

      // noinspection JSUnusedGlobalSymbols
      const options = {
        handlebars: this.handlebars,
        prettifyHtml: false,
        // should not be true
        disableResizeEditor: true,
        isDark: this.getThemeManager().getParam('isDark'),
        espoView: this,
        lang: this.getConfig().get('language'),
        keyMap: keyMap,
        callbacks: {
          onImageUpload: files => {
            const file = files[0];
            Espo.Ui.notify(this.translate('Uploading...'));
            this.uploadInlineAttachment(file).then(attachment => {
              const url = '?entryPoint=attachment&id=' + attachment.id;
              this.$summernote.summernote('insertImage', url);
              Espo.Ui.notify(false);
            });
          },
          onBlur: () => {
            this.trigger('change');
          },
          onKeydown: () => {
            if (Date.now() - lastChangeKeydown > changeKeydownInterval) {
              this.trigger('change');
              lastChangeKeydown = Date.now();
            }
          }
        },
        onCreateLink(link) {
          return link;
        },
        toolbar: toolbar,
        buttons: this.buttons,
        dialogsInBody: this.$el,
        codeviewFilter: true,
        tableClassName: this.tableClassName,
        // Dnd has issues.
        disableDragAndDrop: true,
        colorButton: {
          foreColor: '#000000',
          backColor: '#FFFFFF'
        }
      };
      if (this.height) {
        options.height = this.height;
      } else {
        let $scrollable = this.$el.closest('.modal-body');
        if (!$scrollable.length) {
          $scrollable = $(window);
        }
        this.$scrollable = $scrollable;
        $scrollable.off(`scroll.${this.cid}-edit`);
        $scrollable.on(`scroll.${this.cid}-edit`, e => this.onScrollEdit(e));
      }
      if (this.minHeight) {
        options.minHeight = this.minHeight;
      }
      this.destroySummernote();
      this.$summernote.summernote(options);
      this.summernoteIsInitialized = true;
      this.$toolbar = this.$el.find('.note-toolbar');
      this.$area = this.$el.find('.note-editing-area');
      this.$noteEditor = this.$el.find('> .note-editor');
    }
    focusOnInlineEdit() {
      if (this.$noteEditor) {
        this.$summernote.summernote('focus');
        return;
      }
      super.focusOnInlineEdit();
    }
    uploadInlineAttachment(file) {
      return new Promise((resolve, reject) => {
        this.getModelFactory().create('Attachment', attachment => {
          const fileReader = new FileReader();
          fileReader.onload = e => {
            attachment.set('name', file.name);
            attachment.set('type', file.type);
            attachment.set('role', 'Inline Attachment');
            attachment.set('global', true);
            attachment.set('size', file.size);
            if (this.model.id) {
              attachment.set('relatedId', this.model.id);
            }
            attachment.set('relatedType', this.model.entityType);
            attachment.set('file', e.target.result);
            attachment.set('field', this.name);
            attachment.save().then(() => resolve(attachment)).catch(() => reject());
          };
          fileReader.readAsDataURL(file);
        });
      });
    }
    destroySummernote() {
      if (this.summernoteIsInitialized && this.$summernote) {
        this.$summernote.summernote('destroyAceCodeview');
        this.$summernote.summernote('destroy');
        this.summernoteIsInitialized = false;
      }
    }
    plainToHtml(html) {
      html = html || '';
      return html.replace(/\n/g, '<br>');
    }

    /**
     * @protected
     * @param {string} html
     * @return {string}
     */
    htmlToPlain(html) {
      const div = document.createElement('div');
      div.innerHTML = html;

      /**
       * @param {Node|HTMLElement} node
       * @return {string}
       */
      function processNode(node) {
        if (node.nodeType === Node.TEXT_NODE) {
          return node.nodeValue;
        }
        if (node.nodeType === Node.ELEMENT_NODE) {
          if (node instanceof HTMLAnchorElement) {
            if (node.textContent === node.href) {
              return node.href;
            }
            return `${node.textContent} (${node.href})`;
          }
          if (node instanceof HTMLQuoteElement) {
            return `> ${node.textContent.trim()}`;
          }
          switch (node.tagName.toLowerCase()) {
            case 'br':
            case 'p':
            case 'div':
              return `\n${Array.from(node.childNodes).map(processNode).join('')}\n`;
          }
          return Array.from(node.childNodes).map(processNode).join('');
        }
        return '';
      }
      return processNode(div).replace(/\n{2,}/g, '\n\n').trim();
    }
    disableWysiwygMode() {
      this.destroySummernote();
      this.$noteEditor = null;
      if (this.$summernote) {
        this.$summernote.addClass('hidden');
      }
      this.$element.removeClass('hidden');
      if (this.$scrollable) {
        this.$scrollable.off('scroll.' + this.cid + '-edit');
      }
    }
    fetch() {
      const data = {};
      if (this.isHtml()) {
        let code = this.$summernote.summernote('code');
        if (code === '<p><br></p>') {
          code = '';
        }
        const imageTagString = `<img src="${window.location.origin}${window.location.pathname}?entryPoint=attachment`;
        code = code.replace(new RegExp(imageTagString.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"), 'g'), '<img src="?entryPoint=attachment');
        data[this.name] = code;
      } else {
        data[this.name] = this.$element.val();
      }
      if (this.fetchEmptyValueAsNull && !data[this.name]) {
        data[this.name] = null;
      }
      return data;
    }
    onScrollEdit(e) {
      const $target = $(e.target);
      const toolbarHeight = this.$toolbar.height();
      const toolbarWidth = this.$toolbar.parent().width();
      let edgeTop, edgeTopAbsolute;

      // noinspection JSIncompatibleTypesComparison
      if ($target.get(0) === window.document) {
        const $buttonContainer = $target.find('.detail-button-container:not(.hidden)');
        const offset = $buttonContainer.offset();
        if (offset) {
          edgeTop = offset.top + $buttonContainer.outerHeight();
          edgeTopAbsolute = edgeTop - $(window).scrollTop();
        }
      } else {
        const offset = $target.offset();
        if (offset) {
          edgeTop = offset.top;
          edgeTopAbsolute = edgeTop - $(window).scrollTop();
        }
      }
      const top = this.$el.offset().top;
      const bottom = top + this.$el.height() - toolbarHeight;
      let toStick = false;
      if (edgeTop > top && bottom > edgeTop) {
        toStick = true;
      }
      if (toStick) {
        this.$toolbar.css({
          top: edgeTopAbsolute + 'px',
          width: toolbarWidth + 'px'
        });
        this.$toolbar.addClass('sticked');
        this.$area.css({
          marginTop: toolbarHeight + 'px',
          backgroundColor: ''
        });
        return;
      }
      this.$toolbar.css({
        top: '',
        width: ''
      });
      this.$toolbar.removeClass('sticked');
      this.$area.css({
        marginTop: ''
      });
    }
    attachFile() {
      const $form = this.$el.closest('.record');
      $form.find(`.field[data-name="${this.params.attachmentField}"] input.file`).click();
      this.stopListening(this.model, 'attachment-uploaded:attachments');
      this.listenToOnce(this.model, 'attachment-uploaded:attachments', /** module:model[] */attachments => {
        if (this.isEditMode()) {
          const msg = this.translate('Attached') + '\n' + attachments.map(m => m.attributes.name).join('\n');
          Espo.Ui.notify(msg, 'success', 3000);
        }
      });
    }
    initEspoPlugin() {
      const langSets = this.getLanguage().get('Global', 'sets', 'summernote') || {
        image: {},
        link: {},
        video: {}
      };
      (0, _summernoteCustom.init)(langSets);
    }
    htmlHasColors(string) {
      if (~string.indexOf('background-color:')) {
        return true;
      }
      if (~string.indexOf('color:')) {
        return true;
      }
      if (~string.indexOf('<font color="')) {
        return true;
      }
      return false;
    }

    /**
     * @param {string} text
     * @since 8.4.0
     */
    insertText(text) {
      if (this.isHtml()) {
        this.$summernote.summernote('insertText', text);
      }
    }
    toSkipReRenderOnChange() {
      if (!this.element || !this.element.contains(document.activeElement)) {
        return false;
      }
      if (!this.model.hasChanged(this.name)) {
        return true;
      }
      return false;
    }
  }
  var _default = _exports.default = WysiwygFieldView;
});

define("views/fields/link", ["exports", "views/fields/base", "helpers/record-modal", "ui/autocomplete"], function (_exports, _base, _recordModal, _autocomplete) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _recordModal = _interopRequireDefault(_recordModal);
  _autocomplete = _interopRequireDefault(_autocomplete);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/link */

  /**
   * A link field (belongs-to relation).
   *
   * @extends BaseFieldView<module:views/fields/link~params>
   */
  class LinkFieldView extends _base.default {
    /**
     * @typedef {Object} module:views/fields/link~options
     * @property {
     *     module:views/fields/link~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     * @property {boolean} [createDisabled] Disable create button in the select modal.
     */

    /**
     * @typedef {Object} module:views/fields/link~params
     * @property {boolean} [required] Required.
     * @property {boolean} [autocompleteOnEmpty] Autocomplete on empty input.
     * @property {boolean} [createButton] Show 'Create' button.
     * @property {string} [entity] An entity type. As of 9.1.0.
     */

    /**
     * @param {
     *     module:views/fields/link~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }

    /** @inheritDoc */
    type = 'link';

    /** @inheritDoc */
    listTemplate = 'fields/link/list';
    /** @inheritDoc */
    detailTemplate = 'fields/link/detail';
    /** @inheritDoc */
    editTemplate = 'fields/link/edit';
    /** @inheritDoc */
    searchTemplate = 'fields/link/search';

    /**
     * A name attribute name.
     *
     * @type {string}
     */
    nameName;

    /**
     * An ID attribute name.
     *
     * @type {string}
     */
    idName;

    /**
     * A foreign entity type.
     *
     * @type {string|null}
     */
    foreignScope = null;

    /**
     * A select-record view.
     *
     * @protected
     * @type {string}
     */
    selectRecordsView = 'views/modals/select-records';

    /**
     * Autocomplete disabled.
     *
     * @protected
     * @type {boolean}
     */
    autocompleteDisabled = false;

    /**
     * Create disabled.
     *
     * @protected
     * @type {boolean}
     */
    createDisabled = false;

    /**
     * To display the create button.
     *
     * @protected
     * @type {boolean}
     */
    createButton = false;

    /**
     * Force create button even is disabled in clientDefs > relationshipPanels.
     *
     * @protected
     * @type {boolean}
     */
    forceCreateButton = false;

    /**
     * A search type list.
     *
     * @protected
     * @type {string[]}
     */
    searchTypeList = ['is', 'isEmpty', 'isNotEmpty', 'isNot', 'isOneOf', 'isNotOneOf'];

    /**
     * A primary filter list that will be available when selecting a record.
     *
     * @protected
     * @type {string[]|null}
     */
    selectFilterList = null;

    /**
     * A select primary filter.
     *
     * @protected
     * @type {string|null}
     */
    selectPrimaryFilterName = null;

    /**
     * A select bool filter list.
     *
     * @protected
     * @type {string[]|null}
     */
    selectBoolFilterList = null;

    /**
     * An autocomplete max record number.
     *
     * @protected
     * @type {number|null}
     */
    autocompleteMaxCount = null;

    /**
     * Select all attributes.
     *
     * @protected
     * @type {boolean}
     */
    forceSelectAllAttributes = false;

    /**
     * @protected
     * @type {string[]|null}
     */
    mandatorySelectAttributeList = null;

    /**
     * Trigger autocomplete on empty input.
     *
     * @protected
     * @type {boolean}
     */
    autocompleteOnEmpty = false;

    /**
     * A link element class name. Applicable in the detail mode.
     *
     * @protected
     * @since 9.1.6
     */
    linkClass;

    /** @inheritDoc */
    events = {
      /** @this LinkFieldView */
      'auxclick a[href]:not([role="button"])': function (e) {
        if (!this.isReadMode()) {
          return;
        }
        const isCombination = e.button === 1 && (e.ctrlKey || e.metaKey);
        if (!isCombination) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        this.quickView();
      }
    };

    // noinspection JSCheckFunctionSignatures
    /** @inheritDoc */
    data() {
      let nameValue = this.model.has(this.nameName) ? this.model.get(this.nameName) : this.model.get(this.idName);
      if (nameValue === null) {
        nameValue = this.model.get(this.idName);
      }
      if (this.isReadMode() && !nameValue && this.model.get(this.idName)) {
        nameValue = this.translate(this.foreignScope, 'scopeNames');
      }
      let iconHtml = null;
      if (this.isDetailMode() || this.isListMode()) {
        iconHtml = this.getHelper().getScopeColorIconHtml(this.foreignScope);
      }
      const createButton = this.createButton && (!this.createDisabled || this.forceCreateButton);

      // noinspection JSValidateTypes
      return {
        ...super.data(),
        idName: this.idName,
        nameName: this.nameName,
        idValue: this.model.get(this.idName),
        nameValue: nameValue,
        foreignScope: this.foreignScope,
        valueIsSet: this.model.has(this.idName),
        iconHtml: iconHtml,
        url: this.getUrl(),
        createButton: createButton,
        linkClass: this.linkClass
      };
    }

    /**
     * @protected
     * @return {?string}
     */
    getUrl() {
      const id = this.model.get(this.idName);
      if (!id) {
        return null;
      }
      return '#' + this.foreignScope + '/view/' + id;
    }

    /**
     * Get advanced filters (field filters) to be applied when select a record.
     * Can be extended.
     *
     * @protected
     * @return {Object.<string,module:search-manager~advancedFilter>|null}
     */
    getSelectFilters() {
      return null;
    }

    /**
     * Get a select bool filter list. Applied when select a record.
     * Can be extended.
     *
     * @protected
     * @return {string[]|null}
     */
    getSelectBoolFilterList() {
      return this.selectBoolFilterList;
    }

    /**
     * Get a select primary filter. Applied when select a record.
     * Can be extended.
     *
     * @protected
     * @return {string|null}
     */
    getSelectPrimaryFilterName() {
      return this.selectPrimaryFilterName;
    }

    /**
     * Get a primary filter list that will be available when selecting a record.
     * Can be extended.
     *
     * @return {string[]|null}
     */
    getSelectFilterList() {
      return this.selectFilterList;
    }

    /**
     * Attributes to pass to a model when creating a new record.
     * Can be extended.
     *
     * @return {Object.<string,*>|null}
     */
    getCreateAttributes() {
      const attributeMap = this.getMetadata().get(['clientDefs', this.entityType, 'relationshipPanels', this.name, 'createAttributeMap']) || {};
      const attributes = {};
      Object.keys(attributeMap).forEach(attr => attributes[attributeMap[attr]] = this.model.get(attr));
      return attributes;
    }

    /** @inheritDoc */
    setup() {
      this.nameName = this.name + 'Name';
      this.idName = this.name + 'Id';
      this.foreignScope = this.options.foreignScope || this.foreignScope;
      this.foreignScope = this.foreignScope || this.params.entity || this.model.getFieldParam(this.name, 'entity') || this.model.getLinkParam(this.name, 'entity');
      if ('createDisabled' in this.options) {
        this.createDisabled = this.options.createDisabled;
      }
      if (!this.isListMode()) {
        this.addActionHandler('selectLink', () => this.actionSelect());
        this.addActionHandler('clearLink', () => this.clearLink());
      }
      if (this.isSearchMode()) {
        this.addActionHandler('selectLinkOneOf', () => this.actionSelectOneOf());
        this.events['click a[data-action="clearLinkOneOf"]'] = e => {
          const id = $(e.currentTarget).data('id').toString();
          this.deleteLinkOneOf(id);
        };
      }
      this.autocompleteOnEmpty = this.params.autocompleteOnEmpty || this.autocompleteOnEmpty;
      this.createButton = this.params.createButton || this.createButton;
      if (this.createButton && !this.getAcl().checkScope(this.foreignScope, 'create')) {
        this.createButton = false;
      }
      if (this.createButton) {
        this.addActionHandler('createLink', () => this.actionCreateLink());
      }

      /** @type {Object.<string, *>} */
      this.panelDefs = this.getMetadata().get(['clientDefs', this.entityType, 'relationshipPanels', this.name]) || {};
      if (this.panelDefs.createDisabled) {
        this.createDisabled = true;
      }
    }

    /**
     * Select.
     *
     * @param {module:model} model A model.
     * @protected
     */
    select(model) {
      this.$elementName.val(model.get('name') || model.id);
      this.$elementId.val(model.get('id'));
      if (this.mode === this.MODE_SEARCH) {
        this.searchData.idValue = model.get('id');
        this.searchData.nameValue = model.get('name') || model.id;
      }
      this.trigger('change');
      this.controlCreateButtonVisibility();
      const attributes = {};
      for (const [foreign, field] of Object.entries(this.getDependantForeignMap())) {
        attributes[field] = model.get(foreign);
      }
      this.getSelectFieldHandler().then(async handler => {
        this.model.set({
          ...attributes,
          ...(await handler.getAttributes(model))
        }, {
          fromField: this.name
        });
      });
    }

    /**
     * Clear.
     */
    clearLink() {
      this.$elementName.val('');
      this.$elementId.val('');
      this.trigger('change');
      this.controlCreateButtonVisibility();
      for (const [, field] of Object.entries(this.getDependantForeignMap())) {
        this.model.unset(field, {
          fromField: this.name
        });
      }
      this.getSelectFieldHandler().then(handler => {
        handler.getClearAttributes().then(attributes => {
          this.model.set(attributes, {
            fromField: this.name
          });
        });
      });
    }

    /** @private */
    controlCreateButtonVisibility() {
      if (!this.createButton) {
        return;
      }
      const $btn = this.$el.find('[data-action="createLink"]');
      this.model.get(this.idName) ? $btn.addClass('hidden') : $btn.removeClass('hidden');
    }

    /**
     * @private
     * @return {Promise<{
     *     getAttributes: function (module:model): Promise<Object.<string, *>>,
     *     getClearAttributes: function(): Promise<Object.<string, *>>,
     * }>}
     */
    getSelectFieldHandler() {
      if (!this.panelDefs.selectFieldHandler) {
        return Promise.resolve({
          getClearAttributes: () => Promise.resolve({}),
          getAttributes: () => Promise.resolve({})
        });
      }
      return new Promise(resolve => {
        Espo.loader.requirePromise(this.panelDefs.selectFieldHandler).then(Handler => {
          // Model is passed as of v8.2.
          const handler = new Handler(this.getHelper(), this.model);
          resolve(handler);
        });
      });
    }

    /** @inheritDoc */
    setupSearch() {
      this.searchData.oneOfIdList = this.getSearchParamsData().oneOfIdList || this.searchParams.oneOfIdList || [];
      this.searchData.oneOfNameHash = this.getSearchParamsData().oneOfNameHash || this.searchParams.oneOfNameHash || {};
      if (~['is', 'isNot', 'equals'].indexOf(this.getSearchType())) {
        this.searchData.idValue = this.getSearchParamsData().idValue || this.searchParams.idValue || this.searchParams.value;
        this.searchData.nameValue = this.getSearchParamsData().nameValue || this.searchParams.nameValue || this.searchParams.valueName;
      }
      this.events['change select.search-type'] = e => {
        const type = $(e.currentTarget).val();
        this.handleSearchType(type);
      };
    }

    /**
     * Handle a search type.
     *
     * @protected
     * @param {string} type A type.
     */
    handleSearchType(type) {
      if (~['is', 'isNot', 'isNotAndIsNotEmpty'].indexOf(type)) {
        this.$el.find('div.primary').removeClass('hidden');
      } else {
        this.$el.find('div.primary').addClass('hidden');
      }
      if (~['isOneOf', 'isNotOneOf', 'isNotOneOfAndIsNotEmpty'].indexOf(type)) {
        this.$el.find('div.one-of-container').removeClass('hidden');
      } else {
        this.$el.find('div.one-of-container').addClass('hidden');
      }
    }

    /**
     * Get an autocomplete max record number. Can be extended.
     *
     * @protected
     * @return {number}
     */
    getAutocompleteMaxCount() {
      if (this.autocompleteMaxCount) {
        return this.autocompleteMaxCount;
      }
      return this.getConfig().get('recordsPerPage');
    }

    /**
     * @private
     * @return {string[]}
     */
    getMandatorySelectAttributeList() {
      const list = this.mandatorySelectAttributeList || this.panelDefs.selectMandatoryAttributeList || [];
      const map = this.getDependantForeignMap();
      return [...list, ...Object.keys(map)];
    }

    /**
     * @private
     * @return {Record<string>}
     */
    getDependantForeignMap() {
      if (this._dependantForeignMap) {
        return this._dependantForeignMap;
      }
      const map = {};
      this.model.getFieldList().filter(it => {
        return this.model.getFieldType(it) === 'foreign' && this.model.getFieldParam(it, 'link') === this.name;
      }).forEach(field => {
        const foreign = this.model.getFieldParam(field, 'field');
        if (!foreign) {
          return;
        }
        map[foreign] = field;
      });
      this._dependantForeignMap = map;
      return map;
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * Compose an autocomplete URL. Can be extended.
     *
     * @protected
     * @param {string} [q] A query.
     * @return {string|Promise<string>}
     */
    getAutocompleteUrl(q) {
      let url = this.foreignScope + '?maxSize=' + this.getAutocompleteMaxCount();
      if (!this.forceSelectAllAttributes) {
        const mandatorySelectAttributeList = this.getMandatorySelectAttributeList();
        let select = ['id', 'name'];
        if (mandatorySelectAttributeList) {
          select = select.concat(mandatorySelectAttributeList);
        }
        url += '&select=' + select.join(',');
      }
      if (this.panelDefs.selectHandler) {
        return new Promise(resolve => {
          this._getSelectFilters().then(filters => {
            if (filters.bool) {
              url += '&' + $.param({
                'boolFilterList': filters.bool
              });
            }
            if (filters.primary) {
              url += '&' + $.param({
                'primaryFilter': filters.primary
              });
            }
            if (filters.advanced && Object.keys(filters.advanced).length) {
              url += '&' + $.param({
                'where': filters.advanced
              });
            }
            const orderBy = filters.orderBy || this.panelDefs.selectOrderBy;
            const orderDirection = filters.orderBy ? filters.order : this.panelDefs.selectOrderDirection;
            if (orderBy) {
              url += '&' + $.param({
                orderBy: orderBy,
                order: orderDirection || 'asc'
              });
            }
            resolve(url);
          });
        });
      }
      const boolList = [...(this.getSelectBoolFilterList() || []), ...(this.panelDefs.selectBoolFilterList || [])];
      const primary = this.getSelectPrimaryFilterName() || this.panelDefs.selectPrimaryFilterName;
      if (boolList.length) {
        url += '&' + $.param({
          'boolFilterList': boolList
        });
      }
      if (primary) {
        url += '&' + $.param({
          'primaryFilter': primary
        });
      }
      if (this.panelDefs.selectOrderBy) {
        const direction = this.panelDefs.selectOrderDirection || 'asc';
        url += '&' + $.param({
          orderBy: this.panelDefs.selectOrderBy,
          order: direction
        });
      }
      return url;
    }

    /** @inheritDoc */
    afterRender() {
      if (this.isEditMode() || this.isSearchMode()) {
        this.$elementId = this.$el.find('input[data-name="' + this.idName + '"]');
        this.$elementName = this.$el.find('input[data-name="' + this.nameName + '"]');
        this.$elementName.on('change', () => {
          if (this.$elementName.val() === '') {
            this.clearLink();
          }
        });
        this.$elementName.on('blur', e => {
          setTimeout(() => {
            if (this.mode === this.MODE_EDIT && this.model.has(this.nameName)) {
              e.currentTarget.value = this.model.get(this.nameName) || this.model.get(this.idName);
            }
          }, 100);
        });
        const $elementName = this.$elementName;
        if (!this.autocompleteDisabled) {
          /** @type {module:ajax.AjaxPromise & Promise<any>} */
          let lastAjaxPromise;
          const autocomplete = new _autocomplete.default(this.$elementName.get(0), {
            name: this.name,
            handleFocusMode: 2,
            autoSelectFirst: true,
            forceHide: true,
            triggerSelectOnValidInput: false,
            onSelect: item => {
              this.getModelFactory().create(this.foreignScope, model => {
                model.set(item.attributes);
                this.select(model);
                this.$elementName.focus();
              });
            },
            lookupFunction: query => {
              if (!this.autocompleteOnEmpty && query.length === 0) {
                const onEmptyPromise = this.getOnEmptyAutocomplete();
                if (onEmptyPromise) {
                  return onEmptyPromise.then(list => this._transformAutocompleteResult({
                    list: list
                  }));
                }
                return Promise.resolve([]);
              }
              return Promise.resolve(this.getAutocompleteUrl(query)).then(url => {
                if (lastAjaxPromise && lastAjaxPromise.getReadyState() < 4) {
                  lastAjaxPromise.abort();
                }
                lastAjaxPromise = Espo.Ajax.getRequest(url, {
                  q: query
                });
                return lastAjaxPromise;
              }).then(response => this._transformAutocompleteResult(response));
            }
          });
          this.once('render remove', () => autocomplete.dispose());
          if (this.isSearchMode()) {
            const $elementOneOf = this.$el.find('input.element-one-of');

            /** @type {module:ajax.AjaxPromise & Promise<any>} */
            let lastAjaxPromise;
            const autocomplete = new _autocomplete.default($elementOneOf.get(0), {
              minChars: 1,
              focusOnSelect: true,
              handleFocusMode: 3,
              autoSelectFirst: true,
              triggerSelectOnValidInput: false,
              forceHide: true,
              onSelect: item => {
                this.getModelFactory().create(this.foreignScope, model => {
                  model.set(item.attributes);
                  this.selectOneOf([model]);
                  $elementOneOf.val('');
                  setTimeout(() => $elementOneOf.focus(), 50);
                });
              },
              lookupFunction: query => {
                return Promise.resolve(this.getAutocompleteUrl(query)).then(url => {
                  if (lastAjaxPromise && lastAjaxPromise.getReadyState() < 4) {
                    lastAjaxPromise.abort();
                  }
                  lastAjaxPromise = Espo.Ajax.getRequest(url, {
                    q: query
                  });
                  return lastAjaxPromise;
                }).then(/** {list: Record[]} */response => {
                  return response.list.map(item => ({
                    value: item.name,
                    attributes: item
                  }));
                });
              }
            });
            this.once('render remove', () => autocomplete.dispose());
            this.$el.find('select.search-type').on('change', () => {
              this.trigger('change');
            });
          }
        }
        $elementName.on('change', () => {
          if (!this.isSearchMode() && !this.model.get(this.idName)) {
            $elementName.val(this.model.get(this.nameName));
          }
        });
      }
      if (this.isSearchMode()) {
        const type = this.$el.find('select.search-type').val();
        this.handleSearchType(type);
        if (~['isOneOf', 'isNotOneOf', 'isNotOneOfAndIsNotEmpty'].indexOf(type)) {
          this.searchData.oneOfIdList.forEach(id => {
            this.addLinkOneOfHtml(id, this.searchData.oneOfNameHash[id]);
          });
        }
      }
    }

    /**
     * @private
     */
    _transformAutocompleteResult(response) {
      const list = [];
      response.list.forEach(item => {
        list.push({
          id: item.id,
          name: item.name || item.id,
          data: item.id,
          value: item.name || item.id,
          attributes: item
        });
      });
      return list;
    }

    /** @inheritDoc */
    getValueForDisplay() {
      return this.model.get(this.nameName);
    }

    /** @inheritDoc */
    validateRequired() {
      if (this.isRequired()) {
        if (this.model.get(this.idName) == null) {
          const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
          this.showValidationMessage(msg);
          return true;
        }
      }
    }

    /**
     * Delete a one-of item. For search mode.
     *
     * @param {string} id An ID.
     */
    deleteLinkOneOf(id) {
      this.deleteLinkOneOfHtml(id);
      const index = this.searchData.oneOfIdList.indexOf(id);
      if (index > -1) {
        this.searchData.oneOfIdList.splice(index, 1);
      }
      delete this.searchData.oneOfNameHash[id];
      this.trigger('change');
    }

    /**
     * Add a one-of item. For search mode.
     *
     * @param {string} id An ID.
     * @param {string} name A name.
     */
    addLinkOneOf(id, name) {
      if (!~this.searchData.oneOfIdList.indexOf(id)) {
        this.searchData.oneOfIdList.push(id);
        this.searchData.oneOfNameHash[id] = name;
        this.addLinkOneOfHtml(id, name);
        this.trigger('change');
      }
    }

    /**
     * @protected
     * @param {string} id An ID.
     */
    deleteLinkOneOfHtml(id) {
      this.$el.find('.link-one-of-container .link-' + id).remove();
    }

    /**
     * @protected
     * @param {string} id An ID.
     * @param {string} name A name.
     * @return {JQuery}
     */
    addLinkOneOfHtml(id, name) {
      const $container = this.$el.find('.link-one-of-container');
      const $el = $('<div>').addClass('link-' + id).addClass('list-group-item');
      $el.append($('<a>').attr('role', 'button').addClass('pull-right').attr('data-id', id).attr('data-action', 'clearLinkOneOf').append($('<span>').addClass('fas fa-times')), $('<span>').text(name), ' ');
      $container.append($el);
      return $el;
    }

    /** @inheritDoc */
    fetch() {
      const data = {};
      data[this.nameName] = this.$el.find('[data-name="' + this.nameName + '"]').val() || null;
      data[this.idName] = this.$el.find('[data-name="' + this.idName + '"]').val() || null;
      return data;
    }

    /** @inheritDoc */
    fetchSearch() {
      const type = this.$el.find('select.search-type').val();
      const value = this.$el.find('[data-name="' + this.idName + '"]').val();
      if (~['isOneOf', 'isNotOneOf'].indexOf(type) && !this.searchData.oneOfIdList.length) {
        return {
          type: 'isNotNull',
          attribute: 'id',
          data: {
            type: type
          }
        };
      }
      if (type === 'isEmpty') {
        return {
          type: 'isNull',
          attribute: this.idName,
          data: {
            type: type
          }
        };
      }
      if (type === 'isNotEmpty') {
        return {
          type: 'isNotNull',
          attribute: this.idName,
          data: {
            type: type
          }
        };
      }
      if (type === 'isOneOf') {
        return {
          type: 'in',
          attribute: this.idName,
          value: this.searchData.oneOfIdList,
          data: {
            type: type,
            oneOfIdList: this.searchData.oneOfIdList,
            oneOfNameHash: this.searchData.oneOfNameHash
          }
        };
      }
      if (type === 'isNotOneOf') {
        return {
          type: 'or',
          value: [{
            type: 'notIn',
            attribute: this.idName,
            value: this.searchData.oneOfIdList
          }, {
            type: 'isNull',
            attribute: this.idName
          }],
          data: {
            type: type,
            oneOfIdList: this.searchData.oneOfIdList,
            oneOfNameHash: this.searchData.oneOfNameHash
          }
        };
      }
      if (type === 'isNotOneOfAndIsNotEmpty') {
        return {
          type: 'notIn',
          attribute: this.idName,
          value: this.searchData.oneOfIdList,
          data: {
            type: type,
            oneOfIdList: this.searchData.oneOfIdList,
            oneOfNameHash: this.searchData.oneOfNameHash
          }
        };
      }
      if (type === 'isNot') {
        if (!value) {
          return false;
        }
        const nameValue = this.$el.find('[data-name="' + this.nameName + '"]').val();
        return {
          type: 'or',
          value: [{
            type: 'notEquals',
            attribute: this.idName,
            value: value
          }, {
            type: 'isNull',
            attribute: this.idName
          }],
          data: {
            type: type,
            idValue: value,
            nameValue: nameValue
          }
        };
      }
      if (type === 'isNotAndIsNotEmpty') {
        if (!value) {
          return false;
        }
        const nameValue = this.$el.find('[data-name="' + this.nameName + '"]').val();
        return {
          type: 'notEquals',
          attribute: this.idName,
          value: value,
          data: {
            type: type,
            idValue: value,
            nameValue: nameValue
          }
        };
      }
      if (!value) {
        return false;
      }
      const nameValue = this.$el.find('[data-name="' + this.nameName + '"]').val();
      return {
        type: 'equals',
        attribute: this.idName,
        value: value,
        data: {
          type: type,
          idValue: value,
          nameValue: nameValue
        }
      };
    }

    /** @inheritDoc */
    getSearchType() {
      return this.getSearchParamsData().type || this.searchParams.typeFront || this.searchParams.type;
    }

    /**
     * @protected
     */
    quickView() {
      const id = this.model.get(this.idName);
      if (!id) {
        return;
      }
      const entityType = this.foreignScope;
      const helper = new _recordModal.default();
      helper.showDetail(this, {
        id: id,
        entityType: entityType
      });
    }

    /**
     * @protected
     * @return {function(): Promise<Object.<string, *>>}
     */
    getCreateAttributesProvider() {
      return () => {
        const attributes = this.getCreateAttributes() || {};
        if (!this.panelDefs.createHandler) {
          return Promise.resolve(attributes);
        }
        return new Promise(resolve => {
          Espo.loader.requirePromise(this.panelDefs.createHandler).then(Handler => new Handler(this.getHelper())).then(handler => {
            handler.getAttributes(this.model).then(additionalAttributes => {
              resolve({
                ...attributes,
                ...additionalAttributes
              });
            });
          });
        });
      };
    }

    /**
     * @protected
     */
    async actionSelect() {
      const viewName = this.panelDefs.selectModalView || this.getMetadata().get(`clientDefs.${this.foreignScope}.modalViews.select`) || this.selectRecordsView;
      const mandatorySelectAttributeList = this.getMandatorySelectAttributeList();
      const createButton = this.isEditMode() && (!this.createDisabled || this.forceCreateButton);
      const createAttributesProvider = createButton ? this.getCreateAttributesProvider() : null;
      Espo.Ui.notifyWait();
      const filters = await this._getSelectFilters();
      const orderBy = filters.orderBy || this.panelDefs.selectOrderBy;
      const orderDirection = filters.orderBy ? filters.order : this.panelDefs.selectOrderDirection;

      /** @type {module:views/modals/select-records~Options} */
      const options = {
        entityType: this.foreignScope,
        createButton: createButton,
        filters: filters.advanced,
        boolFilterList: filters.bool,
        primaryFilterName: filters.primary,
        mandatorySelectAttributeList: mandatorySelectAttributeList,
        forceSelectAllAttributes: this.forceSelectAllAttributes,
        filterList: this.getSelectFilterList(),
        createAttributesProvider: createAttributesProvider,
        layoutName: this.panelDefs.selectLayout,
        orderBy: orderBy,
        orderDirection: orderDirection,
        onSelect: models => {
          this.select(models[0]);
        }
      };
      const view = await this.createView('modal', viewName, options);
      await view.render();
      Espo.Ui.notify();
    }

    /**
     * @param {Object} advanced
     * @private
     */
    _applyAdditionalFilter(advanced) {
      const foreignLink = this.model.getLinkParam(this.name, 'foreign');
      if (!foreignLink) {
        return;
      }
      if (advanced[foreignLink]) {
        return;
      }
      const linkType = this.model.getLinkParam(this.name, 'type');
      const foreignLinkType = this.getMetadata().get(['entityDefs', this.foreignScope, 'links', foreignLink, 'type']);
      const foreignFieldType = this.getMetadata().get(['entityDefs', this.foreignScope, 'fields', foreignLink, 'type']);
      if (!foreignFieldType) {
        return;
      }
      const isOneToOne = (linkType === 'hasOne' || foreignLinkType === 'hasOne') && ['link', 'linkOne'].includes(foreignFieldType);
      if (!isOneToOne) {
        return;
      }
      advanced[foreignLink] = {
        type: 'isNull',
        attribute: foreignLink + 'Id',
        data: {
          type: 'isEmpty'
        }
      };
    }

    /**
     * @private
     * @return {Promise<{
     *     bool?: string[],
     *     advanced?: Object,
     *     primary?: string,
     *     orderBy?: string,
     *     order?: 'asc'|'desc',
     * }>}
     */
    _getSelectFilters() {
      const handler = this.panelDefs.selectHandler;
      const localBoolFilterList = this.getSelectBoolFilterList();
      if (!handler || this.isSearchMode()) {
        const boolFilterList = localBoolFilterList || this.panelDefs.selectBoolFilterList ? [...(localBoolFilterList || []), ...(this.panelDefs.selectBoolFilterList || [])] : undefined;
        const advanced = this.getSelectFilters() || {};
        this._applyAdditionalFilter(advanced);
        return Promise.resolve({
          primary: this.getSelectPrimaryFilterName() || this.panelDefs.selectPrimaryFilterName,
          bool: boolFilterList,
          advanced: advanced
        });
      }
      return new Promise(resolve => {
        Espo.loader.requirePromise(handler).then(Handler => new Handler(this.getHelper())).then(/** module:handlers/select-related */handler => {
          return handler.getFilters(this.model);
        }).then(filters => {
          const advanced = {
            ...(this.getSelectFilters() || {}),
            ...(filters.advanced || {})
          };
          const primaryFilter = this.getSelectPrimaryFilterName() || filters.primary || this.panelDefs.selectPrimaryFilterName;
          const boolFilterList = localBoolFilterList || filters.bool || this.panelDefs.selectBoolFilterList ? [...(localBoolFilterList || []), ...(filters.bool || []), ...(this.panelDefs.selectBoolFilterList || [])] : undefined;
          this._applyAdditionalFilter(advanced);
          const orderBy = filters.orderBy;
          const order = orderBy ? filters.order : undefined;
          resolve({
            bool: boolFilterList,
            primary: primaryFilter,
            advanced: advanced,
            orderBy: orderBy,
            order: order
          });
        });
      });
    }
    actionSelectOneOf() {
      Espo.Ui.notifyWait();
      const viewName = this.getMetadata().get(['clientDefs', this.foreignScope, 'modalViews', 'select']) || this.selectRecordsView;
      this.createView('dialog', viewName, {
        scope: this.foreignScope,
        createButton: false,
        filters: this.getSelectFilters(),
        boolFilterList: this.getSelectBoolFilterList(),
        primaryFilterName: this.getSelectPrimaryFilterName(),
        multiple: true,
        layoutName: this.panelDefs.selectLayout
      }, view => {
        view.render();
        Espo.Ui.notify(false);
        this.listenToOnce(view, 'select', models => {
          this.clearView('dialog');
          if (Object.prototype.toString.call(models) !== '[object Array]') {
            models = [models];
          }
          this.selectOneOf(models);
        });
      });
    }

    /**
     * Get an empty autocomplete result.
     *
     * @protected
     * @return {Promise<[{name: ?string, id: string} & Record]>}
     */
    getOnEmptyAutocomplete() {
      return undefined;
    }

    /**
     * @protected
     */
    async actionCreateLink() {
      const helper = new _recordModal.default();
      const attributes = await this.getCreateAttributesProvider()();
      await helper.showCreate(this, {
        entityType: this.foreignScope,
        fullFormDisabled: true,
        attributes: attributes,
        afterSave: model => this.select(model)
      });
    }

    /**
     * @protected
     * @param {module:model[]} models
     * @since 8.0.4
     */
    selectOneOf(models) {
      models.forEach(model => {
        this.addLinkOneOf(model.id, model.get('name'));
      });
    }
  }
  var _default = _exports.default = LinkFieldView;
});

define("views/fields/float", ["exports", "views/fields/int"], function (_exports, _int) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _int = _interopRequireDefault(_int);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/float */

  /**
   * A float field.
   *
   * @extends IntFieldView<module:views/fields/float~params>
   */
  class FloatFieldView extends _int.default {
    /**
     * @typedef {Object} module:views/fields/float~options
     * @property {
     *     module:views/fields/float~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/float~params
     * @property {number} [min] A max value.
     * @property {number} [max] A max value.
     * @property {boolean} [required] Required.
     * @property {boolean} [disableFormatting] Disable formatting.
     * @property {number|null} [decimalPlaces] A number of decimal places.
     */

    /**
     * @param {
     *     module:views/fields/float~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'float';
    editTemplate = 'fields/float/edit';
    decimalMark = '.';
    decimalPlacesRawValue = 10;

    /**
     * @inheritDoc
     * @type {Array<(function (): boolean)|string>}
     */
    validations = ['required', 'float', 'range'];

    /** @inheritDoc */
    setup() {
      super.setup();
      if (this.getPreferences().has('decimalMark')) {
        this.decimalMark = this.getPreferences().get('decimalMark');
      } else if (this.getConfig().has('decimalMark')) {
        this.decimalMark = this.getConfig().get('decimalMark');
      }
      if (!this.decimalMark) {
        this.decimalMark = '.';
      }
      if (this.decimalMark === this.thousandSeparator) {
        this.thousandSeparator = '';
      }
    }

    /** @inheritDoc */
    setupAutoNumericOptions() {
      // noinspection JSValidateTypes
      this.autoNumericOptions = {
        digitGroupSeparator: this.thousandSeparator || '',
        decimalCharacter: this.decimalMark,
        modifyValueOnWheel: false,
        selectOnFocus: false,
        decimalPlaces: this.decimalPlacesRawValue,
        decimalPlacesRawValue: this.decimalPlacesRawValue,
        allowDecimalPadding: false,
        showWarnings: false,
        formulaMode: true
      };
    }
    getValueForDisplay() {
      const value = isNaN(this.model.get(this.name)) ? null : this.model.get(this.name);
      return this.formatNumber(value);
    }
    formatNumber(value) {
      if (this.disableFormatting) {
        return value;
      }
      return this.formatNumberDetail(value);
    }
    formatNumberDetail(value) {
      if (value === null) {
        return '';
      }
      const decimalPlaces = this.params.decimalPlaces;
      if (decimalPlaces === 0) {
        value = Math.round(value);
      } else if (decimalPlaces) {
        value = Math.round(value * Math.pow(10, decimalPlaces)) / Math.pow(10, decimalPlaces);
      }
      const parts = value.toString().split(".");
      parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, this.thousandSeparator);
      if (decimalPlaces === 0) {
        return parts[0];
      } else if (decimalPlaces) {
        let decimalPartLength = 0;
        if (parts.length > 1) {
          decimalPartLength = parts[1].length;
        } else {
          parts[1] = '';
        }
        if (decimalPlaces && decimalPartLength < decimalPlaces) {
          const limit = decimalPlaces - decimalPartLength;
          for (let i = 0; i < limit; i++) {
            parts[1] += '0';
          }
        }
      }
      return parts.join(this.decimalMark);
    }
    setupMaxLength() {}
    validateFloat() {
      const value = this.model.get(this.name);
      if (isNaN(value)) {
        const msg = this.translate('fieldShouldBeFloat', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg);
        return true;
      }
    }
    parse(value) {
      value = value !== '' ? value : null;
      if (value === null) {
        return null;
      }
      value = value.split(this.thousandSeparator).join('').split(this.decimalMark).join('.');
      return parseFloat(value);
    }
    fetch() {
      let value = this.$element.val();
      value = this.parse(value);
      const data = {};
      data[this.name] = value;
      return data;
    }
  }
  var _default = _exports.default = FloatFieldView;
});

define("views/fields/date", ["exports", "views/fields/base", "moment", "ui/datepicker"], function (_exports, _base, _moment, _datepicker) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _moment = _interopRequireDefault(_moment);
  _datepicker = _interopRequireDefault(_datepicker);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/date */

  /**
   * A date field.
   *
   * @extends BaseFieldView<module:views/fields/date~params>
   */
  class DateFieldView extends _base.default {
    /**
     * @typedef {Object} module:views/fields/date~options
     * @property {
     *     module:views/fields/date~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     * @property {string} [otherFieldLabelText] A label text of other field. Used in before/after validations.
     */

    /**
     * @typedef {Object} module:views/fields/date~params
     * @property {boolean} [required] Required.
     * @property {boolean} [useNumericFormat] Use numeric format.
     * @property {string} [after] Validate to be after another date field.
     * @property {string} [before] Validate to be before another date field.
     * @property {boolean} [afterOrEqual] Allow an equal date for 'after' validation.
     */

    /**
     * @param {
     *     module:views/fields/date~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'date';
    listTemplate = 'fields/date/list';
    listLinkTemplate = 'fields/date/list-link';
    detailTemplate = 'fields/date/detail';
    editTemplate = 'fields/date/edit';
    searchTemplate = 'fields/date/search';

    /**
     * @inheritDoc
     * @type {Array<(function (): boolean)|string>}
     */
    validations = ['required', 'date', 'after', 'before'];
    searchTypeList = ['lastSevenDays', 'ever', 'isEmpty', 'currentMonth', 'lastMonth', 'nextMonth', 'currentQuarter', 'lastQuarter', 'currentYear', 'lastYear', 'today', 'past', 'future', 'lastXDays', 'nextXDays', 'olderThanXDays', 'afterXDays', 'on', 'after', 'before', 'between'];
    initialSearchIsNotIdle = true;

    /**
     * @private
     * @type {import('ui/datepicker').default}
     */
    datepicker;
    setup() {
      super.setup();
      if (this.getConfig().get('fiscalYearShift')) {
        this.searchTypeList = Espo.Utils.clone(this.searchTypeList);
        if (this.getConfig().get('fiscalYearShift') % 3 !== 0) {
          this.searchTypeList.push('currentFiscalQuarter');
          this.searchTypeList.push('lastFiscalQuarter');
        }
        this.searchTypeList.push('currentFiscalYear');
        this.searchTypeList.push('lastFiscalYear');
      }
      if (this.params.after) {
        this.listenTo(this.model, `change:${this.params.after}`, async () => {
          if (!this.isEditMode()) {
            return;
          }
          await this.whenRendered();

          // Timeout prevents the picker popping one when the duration field adjusts the date end.
          setTimeout(() => {
            this.datepicker.setStartDate(this.getStartDateForDatePicker());
          }, 100);
        });
      }

      /** @protected */
      this.useNumericFormat = this.getConfig().get('readableDateFormatDisabled') || this.params.useNumericFormat;
    }

    // noinspection JSCheckFunctionSignatures
    data() {
      const data = super.data();
      data.dateValue = this.getDateStringValue();
      data.isNone = data.dateValue === null;
      if (data.dateValue === -1) {
        data.dateValue = null;
        data.isLoading = true;
      }
      if (this.isSearchMode()) {
        const value = this.getSearchParamsData().value || this.searchParams.dateValue;
        const valueTo = this.getSearchParamsData().valueTo || this.searchParams.dateValueTo;
        data.dateValue = this.getDateTime().toDisplayDate(value);
        data.dateValueTo = this.getDateTime().toDisplayDate(valueTo);
        if (['lastXDays', 'nextXDays', 'olderThanXDays', 'afterXDays'].includes(this.getSearchType())) {
          data.number = this.searchParams.value;
        }
      }
      if (this.isListMode()) {
        data.titleDateValue = data.dateValue;
      }
      if (this.useNumericFormat) {
        data.useNumericFormat = true;
      }

      // noinspection JSValidateTypes
      return data;
    }
    setupSearch() {
      this.addHandler('change', 'select.search-type', (e, /** HTMLSelectElement */target) => {
        this.handleSearchType(target.value);
      });
    }
    stringifyDateValue(value) {
      if (!value) {
        if (this.mode === this.MODE_EDIT || this.mode === this.MODE_SEARCH || this.mode === this.MODE_LIST || this.mode === this.MODE_LIST_LINK) {
          return '';
        }
        return null;
      }
      if (this.mode === this.MODE_LIST || this.mode === this.MODE_DETAIL || this.mode === this.MODE_LIST_LINK) {
        return this.convertDateValueForDetail(value);
      }
      return this.getDateTime().toDisplayDate(value);
    }
    convertDateValueForDetail(value) {
      if (this.useNumericFormat) {
        return this.getDateTime().toDisplayDate(value);
      }
      const timezone = this.getDateTime().getTimeZone();
      const internalDateTimeFormat = this.getDateTime().internalDateTimeFormat;
      const readableFormat = this.getDateTime().getReadableDateFormat();
      const valueWithTime = value + ' 00:00:00';
      const today = _moment.default.tz(timezone).startOf('day');
      let dateTime = _moment.default.tz(valueWithTime, internalDateTimeFormat, timezone);
      const temp = today.clone();
      const ranges = {
        'today': [temp.unix(), temp.add(1, 'days').unix()],
        'tomorrow': [temp.unix(), temp.add(1, 'days').unix()],
        'yesterday': [temp.add(-3, 'days').unix(), temp.add(1, 'days').unix()]
      };
      if (dateTime.unix() >= ranges['today'][0] && dateTime.unix() < ranges['today'][1]) {
        return this.translate('Today');
      }
      if (dateTime.unix() >= ranges['tomorrow'][0] && dateTime.unix() < ranges['tomorrow'][1]) {
        return this.translate('Tomorrow');
      }
      if (dateTime.unix() >= ranges['yesterday'][0] && dateTime.unix() < ranges['yesterday'][1]) {
        return this.translate('Yesterday');
      }

      // Need to use UTC, otherwise there's a DST issue with old dates.
      dateTime = _moment.default.utc(valueWithTime, internalDateTimeFormat);
      if (dateTime.format('YYYY') === today.format('YYYY')) {
        return dateTime.format(readableFormat);
      }
      return dateTime.format(readableFormat + ', YYYY');
    }
    getDateStringValue() {
      if (this.mode === this.MODE_DETAIL && !this.model.has(this.name)) {
        return -1;
      }
      const value = this.model.get(this.name);
      return this.stringifyDateValue(value);
    }

    /**
     * @protected
     * @return {string|undefined}
     */
    getStartDateForDatePicker() {
      if (!this.isEditMode() || !this.params.after) {
        return undefined;
      }

      /** @type {string} */
      let date = this.model.attributes[this.params.after];
      if (date == null) {
        return undefined;
      }
      if (date.length > 10) {
        date = this.getDateTime().toDisplay(date);
        [date] = date.split(' ');
        return date;
      }
      return this.getDateTime().toDisplayDate(date);
    }
    afterRender() {
      if (this.mode === this.MODE_EDIT || this.mode === this.MODE_SEARCH) {
        this.$element = this.$el.find(`[data-name="${this.name}"]`);

        /** @type {HTMLElement} */
        const element = this.$element.get(0);
        const options = {
          format: this.getDateTime().dateFormat,
          weekStart: this.getDateTime().weekStart,
          startDate: this.getStartDateForDatePicker(),
          todayButton: this.getConfig().get('datepickerTodayButton') || false
        };
        this.datepicker = undefined;
        if (element) {
          this.datepicker = new _datepicker.default(element, {
            ...options,
            onChange: () => this.trigger('change')
          });
        }
        if (this.mode === this.MODE_SEARCH) {
          this.$el.find('select.search-type').on('change', () => this.trigger('change'));
          this.$el.find('input.number').on('change', () => this.trigger('change'));
          const element = this.$el.find('.input-group.additional').get(0);
          if (element) {
            new _datepicker.default(element, options);
            this.initDatePickerEventHandlers('input.filter-from');
            this.initDatePickerEventHandlers('input.filter-to');
          }
        }
        this.$element.parent().find('button.date-picker-btn').on('click', () => {
          this.datepicker.show();
        });
        if (this.mode === this.MODE_SEARCH) {
          const $searchType = this.$el.find('select.search-type');
          this.handleSearchType($searchType.val());
        }
      }
    }

    /**
     * @private
     * @param {string} selector
     */
    initDatePickerEventHandlers(selector) {
      const $input = this.$el.find(selector);
      $input.on('change', /** Record */e => {
        this.trigger('change');
        if (e.isTrigger) {
          if (document.activeElement !== $input.get(0)) {
            $input.focus();
          }
        }
      });
    }
    handleSearchType(type) {
      this.$el.find('div.primary').addClass('hidden');
      this.$el.find('div.additional').addClass('hidden');
      this.$el.find('div.additional-number').addClass('hidden');
      if (['on', 'notOn', 'after', 'before'].includes(type)) {
        this.$el.find('div.primary').removeClass('hidden');
      } else if (['lastXDays', 'nextXDays', 'olderThanXDays', 'afterXDays'].includes(type)) {
        this.$el.find('div.additional-number').removeClass('hidden');
      } else if (type === 'between') {
        this.$el.find('div.primary').addClass('hidden');
        this.$el.find('div.additional').removeClass('hidden');
      }
    }
    parseDate(string) {
      return this.getDateTime().fromDisplayDate(string);
    }

    /**
     * @param {string} string
     * @return {string|-1|null}
     */
    parse(string) {
      if (!string) {
        return null;
      }
      return this.parseDate(string);
    }

    /** @inheritDoc */
    fetch() {
      const data = {};
      data[this.name] = this.parse(this.$element.val());
      return data;
    }

    /** @inheritDoc */
    fetchSearch() {
      const type = this.fetchSearchType();
      if (type === 'between') {
        const valueFrom = this.parseDate(this.$el.find('input.filter-from').val());
        const valueTo = this.parseDate(this.$el.find('input.filter-to').val());
        if (!valueFrom || !valueTo) {
          return null;
        }
        return {
          type: type,
          value: [valueFrom, valueTo],
          data: {
            value: valueFrom,
            valueTo: valueTo
          }
        };
      }
      let data;
      const value = this.parseDate(this.$element.val());
      if (['lastXDays', 'nextXDays', 'olderThanXDays', 'afterXDays'].includes(type)) {
        const number = this.$el.find('input.number').val();
        data = {
          type: type,
          value: number,
          date: true
        };
      } else if (['on', 'notOn', 'after', 'before'].includes(type)) {
        if (!value) {
          return null;
        }
        data = {
          type: type,
          value: value,
          data: {
            value: value
          }
        };
      } else if (type === 'isEmpty') {
        data = {
          type: 'isNull',
          data: {
            type: type
          }
        };
      } else {
        data = {
          type: type,
          date: true
        };
      }
      return data;
    }
    getSearchType() {
      return this.getSearchParamsData().type || this.searchParams.typeFront || this.searchParams.type;
    }
    validateRequired() {
      if (!this.isRequired()) {
        return;
      }
      if (this.model.get(this.name) === null) {
        const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg);
        return true;
      }
    }

    // noinspection JSUnusedGlobalSymbols
    validateDate() {
      if (this.model.get(this.name) === -1) {
        const msg = this.translate('fieldShouldBeDate', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg);
        return true;
      }
    }

    // noinspection JSUnusedGlobalSymbols
    validateAfter() {
      const field = this.params.after;
      if (!field) {
        return false;
      }
      const value = this.model.get(this.name);
      const otherValue = this.model.get(field);
      if (!(value && otherValue)) {
        return false;
      }
      const unix = (0, _moment.default)(value).unix();
      const otherUnix = (0, _moment.default)(otherValue).unix();
      if (this.params.afterOrEqual && unix === otherUnix) {
        return false;
      }
      if (unix <= otherUnix) {
        const otherFieldLabelText = this.options.otherFieldLabelText || this.translate(field, 'fields', this.entityType);
        const msg = this.translate('fieldShouldAfter', 'messages').replace('{field}', this.getLabelText()).replace('{otherField}', otherFieldLabelText);
        this.showValidationMessage(msg);
        return true;
      }
      return false;
    }

    // noinspection JSUnusedGlobalSymbols
    validateBefore() {
      const field = this.params.before;
      if (!field) {
        return false;
      }
      const value = this.model.get(this.name);
      const otherValue = this.model.get(field);
      if (!(value && otherValue)) {
        return;
      }
      if ((0, _moment.default)(value).unix() >= (0, _moment.default)(otherValue).unix()) {
        const msg = this.translate('fieldShouldBefore', 'messages').replace('{field}', this.getLabelText()).replace('{otherField}', this.translate(field, 'fields', this.entityType));
        this.showValidationMessage(msg);
        return true;
      }
    }
  }
  var _default = _exports.default = DateFieldView;
});

define("views/detail/modes", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class DetailModesView extends _view.default {
    // language=Handlebars
    templateContent = `
        <div class="button-container clearfix">
            <div class="btn-group">
                {{#each modeDataList}}
                    <button
                        class="btn btn-text btn-xs-wide{{#if active}} active{{/if}}"
                        data-action="switchMode"
                        data-value="{{name}}"
                        {{#if ../disabled}}disabled="disabled"{{/if}}
                    >{{label}}</button>
                {{/each}}
            </div>
        </div>
    `;

    /** @private */
    disabled = false;

    /**
     * @param {{
     *     modeList: string[],
     *     mode: string,
     *     scope: string.
     * }} options
     */
    constructor(options) {
      super(options);

      /** @private */
      this.modeList = options.modeList;
      /** @private */
      this.mode = options.mode;
      /** @private */
      this.scope = options.scope;

      /**
       * @private
       * @type {Object.<string, boolean>}
       */
      this.hiddenMap = {};
    }
    data() {
      return {
        disabled: this.disabled,
        modeDataList: this.modeList.filter(mode => !this.hiddenMap[mode] || mode === this.mode).map(mode => ({
          name: mode,
          active: mode === this.mode,
          label: this.translate(mode, 'detailViewModes', this.scope)
        }))
      };
    }

    /**
     * Change mode.
     *
     * @param {string} mode
     * @return {Promise}
     */
    changeMode(mode) {
      this.mode = mode;
      return this.reRender();
    }

    /**
     * Hide a mode.
     *
     * @param {string} mode
     */
    async hideMode(mode) {
      this.hiddenMap[mode] = true;
      await this.reRender();
    }

    /**
     * Show a mode.
     *
     * @param {string} mode
     */
    async showMode(mode) {
      delete this.hiddenMap[mode];
      await this.reRender();
    }

    /**
     * Disable.
     *
     * @return {Promise}
     */
    disable() {
      this.disabled = true;
      return this.reRender();
    }

    /**
     * Enable.
     *
     * @return {Promise}
     */
    enable() {
      this.disabled = false;
      return this.reRender();
    }
  }
  var _default = _exports.default = DetailModesView;
});

define("helpers/file-upload", ["exports", "di", "models/settings"], function (_exports, _di, _settings) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _settings = _interopRequireDefault(_settings);
  let _init_config, _init_extra_config;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 helpers/file-upload */
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * A file-upload helper.
   */
  class FileUploadExport {
    static #_ = [_init_config, _init_extra_config] = _applyDecs(this, [], [[(0, _di.inject)(_settings.default), 0, "config"]]).e;
    constructor() {
      _init_extra_config(this);
    }
    /**
     * @private
     * @type {Settings}
     */
    config = _init_config(this);

    /**
     * @typedef {Object} module:helpers/file-upload~Options
     *
     * @property {function(number):void} [afterChunkUpload] After every chunk is uploaded.
     * @property {function(module:model):void} [afterAttachmentSave] After an attachment is saved.
     * @property {{isCanceled?: boolean}} [mediator] A mediator.
     */

    /**
     * Upload.
     *
     * @param {File} file A file.
     * @param {module:model} attachment An attachment model.
     * @param {module:helpers/file-upload~Options} [options] Options.
     * @returns {Promise}
     */
    upload(file, attachment, options) {
      options = options || {};
      options.afterChunkUpload = options.afterChunkUpload || (() => {});
      options.afterAttachmentSave = options.afterAttachmentSave || (() => {});
      options.mediator = options.mediator || {};
      attachment.set('name', file.name);
      attachment.set('type', file.type || 'text/plain');
      attachment.set('size', file.size);
      if (this._useChunks(file)) {
        return this._uploadByChunks(file, attachment, options);
      }
      return new Promise((resolve, reject) => {
        const fileReader = new FileReader();
        fileReader.onload = e => {
          attachment.set('file', e.target.result);
          attachment.save({}, {
            timeout: 0
          }).then(() => resolve()).catch(() => reject());
        };
        fileReader.readAsDataURL(file);
      });
    }

    /**
     * @private
     */
    _uploadByChunks(file, attachment, options) {
      return new Promise((resolve, reject) => {
        attachment.set('isBeingUploaded', true);
        attachment.save().then(() => {
          options.afterAttachmentSave(attachment);
          return this._uploadChunks(file, attachment, resolve, reject, options);
        }).catch(() => reject());
      });
    }

    /**
     * @private
     */
    _uploadChunks(file, attachment, resolve, reject, options, start) {
      start = start || 0;
      let end = start + this._getChunkSize() + 1;
      if (end > file.size) {
        end = file.size;
      }
      if (options.mediator.isCanceled) {
        reject();
        return;
      }
      const blob = file.slice(start, end);
      const fileReader = new FileReader();
      fileReader.onloadend = e => {
        if (e.target.readyState !== FileReader.DONE) {
          return;
        }
        Espo.Ajax.postRequest('Attachment/chunk/' + attachment.id, e.target.result, {
          headers: {
            contentType: 'multipart/form-data'
          }
        }).then(() => {
          options.afterChunkUpload(end);
          if (end === file.size) {
            resolve();
            return;
          }
          this._uploadChunks(file, attachment, resolve, reject, options, end);
        }).catch(() => reject());
      };
      fileReader.readAsDataURL(blob);
    }

    /**
     * @private
     */
    _useChunks(file) {
      const chunkSize = this._getChunkSize();
      if (!chunkSize) {
        return false;
      }
      if (file.size > chunkSize) {
        return true;
      }
      return false;
    }

    /**
     * @private
     */
    _getChunkSize() {
      return (this.config.get('attachmentUploadChunkSize') || 0) * 1024 * 1024;
    }
  }
  var _default = _exports.default = FileUploadExport;
});

define("helpers/record/select-related", ["exports", "di", "metadata"], function (_exports, _di, _metadata) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _metadata = _interopRequireDefault(_metadata);
  let _init_metadata, _init_extra_metadata;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * @internal
   */
  class SelectRelatedHelper {
    static #_ = [_init_metadata, _init_extra_metadata] = _applyDecs(this, [], [[(0, _di.inject)(_metadata.default), 0, "metadata"]]).e;
    /**
     * @private
     * @type {Metadata}
     */
    metadata = _init_metadata(this);

    /**
     * @param {import('view').default} view
     */
    constructor(view) {
      _init_extra_metadata(this);
      /** @private */
      this.view = view;
    }

    /**
     * @param {import('model').default} model
     * @param {string} link
     * @param {{
     *     foreignEntityType?: string,
     *     massSelect?: boolean,
     *     primaryFilterName?: string,
     *     boolFilterList?: string[]|string,
     *     viewKey?: string,
     *     hasCreate?: boolean,
     *     onCreate?: function(): void,
     * }} options
     */
    process(model, link) {
      let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
      if (!options.foreignEntityType && !model.defs['links'][link]) {
        throw new Error(`Link ${link} does not exist.`);
      }
      const scope = options.foreignEntityType || model.defs['links'][link].entity;

      /** @var {Object.<string, *>} */
      const panelDefs = this.metadata.get(['clientDefs', model.entityType, 'relationshipPanels', link]) || {};
      const massRelateEnabled = options.massSelect || panelDefs.massSelect;
      let advanced = {};
      const foreignLink = model.getLinkParam(link, 'foreign');
      if (foreignLink && scope) {
        // Select only records not related with any.
        const foreignLinkType = this.metadata.get(['entityDefs', scope, 'links', foreignLink, 'type']);
        const foreignLinkFieldType = this.metadata.get(['entityDefs', scope, 'fields', foreignLink, 'type']);
        if (['belongsTo', 'belongsToParent'].includes(foreignLinkType) && foreignLinkFieldType && !advanced[foreignLink] && ['link', 'linkParent'].includes(foreignLinkFieldType)) {
          advanced[foreignLink] = {
            type: 'isNull',
            attribute: foreignLink + 'Id',
            data: {
              type: 'isEmpty'
            }
          };
        }
      }
      let primaryFilterName = options.primaryFilterName || null;
      if (typeof primaryFilterName === 'function') {
        primaryFilterName = primaryFilterName.call(this);
      }
      let dataBoolFilterList = options.boolFilterList;
      if (typeof options.boolFilterList === 'string') {
        dataBoolFilterList = options.boolFilterList.split(',');
      }
      let boolFilterList = dataBoolFilterList || panelDefs.selectBoolFilterList;
      if (typeof boolFilterList === 'function') {
        boolFilterList = boolFilterList.call(this);
      }
      boolFilterList = Espo.Utils.clone(boolFilterList);
      primaryFilterName = primaryFilterName || panelDefs.selectPrimaryFilterName || null;
      const viewKey = options.viewKey || 'select';
      const viewName = panelDefs.selectModalView || this.metadata.get(['clientDefs', scope, 'modalViews', viewKey]) || 'views/modals/select-records';
      Espo.Ui.notifyWait();
      const handler = panelDefs.selectHandler || null;
      new Promise(resolve => {
        if (!handler) {
          resolve({});
          return;
        }
        Espo.loader.requirePromise(handler).then(Handler => new Handler(this.view.getHelper())).then(/** module:handlers/select-related */handler => {
          handler.getFilters(model).then(filters => resolve(filters));
        });
      }).then(filters => {
        advanced = {
          ...advanced,
          ...(filters.advanced || {})
        };
        if (boolFilterList || filters.bool) {
          boolFilterList = [...(boolFilterList || []), ...(filters.bool || [])];
        }
        if (filters.primary && !primaryFilterName) {
          primaryFilterName = filters.primary;
        }
        const orderBy = filters.orderBy || panelDefs.selectOrderBy;
        const orderDirection = filters.orderBy ? filters.order : panelDefs.selectOrderDirection;
        const createButton = options.hasCreate === true && options.onCreate !== undefined;

        /** @type {import('views/modals/select-records').default} */
        let modalView;
        this.view.createView('dialogSelectRelated', viewName, {
          scope: scope,
          multiple: true,
          filters: advanced,
          massRelateEnabled: massRelateEnabled,
          primaryFilterName: primaryFilterName,
          boolFilterList: boolFilterList,
          mandatorySelectAttributeList: panelDefs.selectMandatoryAttributeList,
          layoutName: panelDefs.selectLayout,
          orderBy: orderBy,
          orderDirection: orderDirection,
          createButton: createButton,
          onCreate: () => {
            modalView.close();
            if (options.onCreate) {
              options.onCreate();
            }
          }
        }, view => {
          modalView = view;
          view.render();
          Espo.Ui.notify(false);
          this.view.listenToOnce(view, 'select', selectObj => {
            const data = {};
            if (Object.prototype.toString.call(selectObj) === '[object Array]') {
              const ids = [];
              selectObj.forEach(model => ids.push(model.id));
              data.ids = ids;
            } else if (selectObj.massRelate) {
              data.massRelate = true;
              data.where = selectObj.where;
              data.searchParams = selectObj.searchParams;
            } else {
              data.id = selectObj.id;
            }
            const url = `${model.entityType}/${model.id}/${link}`;
            Espo.Ajax.postRequest(url, data).then(() => {
              Espo.Ui.success(this.view.translate('Linked'));
              model.trigger(`update-related:${link}`);
              model.trigger('after:relate');
              model.trigger(`after:relate:${link}`);
            });
          });
        });
      });
    }
  }
  var _default = _exports.default = SelectRelatedHelper;
});

define("helpers/record/create-related", ["exports", "di", "metadata", "helpers/record-modal"], function (_exports, _di, _metadata, _recordModal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _metadata = _interopRequireDefault(_metadata);
  _recordModal = _interopRequireDefault(_recordModal);
  let _init_metadata, _init_extra_metadata;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * @internal
   */
  class CreateRelatedHelper {
    static #_ = [_init_metadata, _init_extra_metadata] = _applyDecs(this, [], [[(0, _di.inject)(_metadata.default), 0, "metadata"]]).e;
    /**
     * @private
     * @type {Metadata}
     */
    metadata = _init_metadata(this);

    /**
     * @param {import('view').default} view
     */
    constructor(view) {
      _init_extra_metadata(this);
      /** @private */
      this.view = view;
    }

    /**
     * @param {import('model').default} model
     * @param {string} link
     * @param {{
     *     afterSave: function(import('model').default),
     * }} [options]
     */
    async process(model, link) {
      let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
      const scope = model.defs['links'][link].entity;
      const foreignLink = model.defs['links'][link].foreign;

      /** @type {Record} */
      const panelDefs = this.metadata.get(`clientDefs.${model.entityType}.relationshipPanels.${link}`) || {};
      const attributeMap = panelDefs.createAttributeMap || {};
      const handler = panelDefs.createHandler;
      let attributes = {};
      Object.keys(attributeMap).forEach(attr => attributes[attributeMap[attr]] = model.get(attr));
      if (handler) {
        const Handler = await Espo.loader.requirePromise(handler);
        /** @type {{getAttributes: function(import('model').default): Promise<Record>}} */
        const handlerObj = new Handler(this.view.getHelper());
        const additionalAttributes = await handlerObj.getAttributes(model);
        attributes = {
          ...attributes,
          ...additionalAttributes
        };
      }
      const helper = new _recordModal.default();
      await helper.showCreate(this.view, {
        entityType: scope,
        relate: {
          model: model,
          link: foreignLink
        },
        attributes: attributes,
        afterSave: m => {
          if (options.afterSave) {
            options.afterSave(m);
          }
          model.trigger(`update-related:${link}`);
          model.trigger('after:relate');
          model.trigger(`after:relate:${link}`);
        }
      });
    }
  }
  var _default = _exports.default = CreateRelatedHelper;
});

define("helpers/misc/stored-text-search", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 helpers/misc/stored-text-search */

  class _default {
    /**
     * @param {module:storage} storage
     * @param {string} scope
     * @param {Number} [maxCount]
     */
    constructor(scope, storage, maxCount) {
      this.scope = scope;
      this.storage = storage;
      this.key = 'textSearches';
      this.maxCount = maxCount || 100;
      /** @type {string[]|null} */
      this.list = null;
    }

    /**
     * Match.
     *
     * @param {string} text
     * @param {Number} [limit]
     * @return {string[]}
     */
    match(text, limit) {
      text = text.toLowerCase().trim();
      const list = this.get();
      const matchedList = [];
      for (const item of list) {
        if (item.toLowerCase().startsWith(text)) {
          matchedList.push(item);
        }
        if (limit !== undefined && matchedList.length === limit) {
          break;
        }
      }
      return matchedList;
    }

    /**
     * Get stored text filters.
     *
     * @private
     * @return {string[]}
     */
    get() {
      if (this.list === null) {
        this.list = this.getFromStorage();
      }
      return this.list;
    }

    /**
     * @private
     * @return {string[]}
     */
    getFromStorage() {
      /** @var {string[]} */
      return this.storage.get(this.key, this.scope) || [];
    }

    /**
     * Store a text filter.
     *
     * @param {string} text
     */
    store(text) {
      text = text.trim();
      let list = this.getFromStorage();
      const index = list.indexOf(text);
      if (index !== -1) {
        list.splice(index, 1);
      }
      list.unshift(text);
      if (list.length > this.maxCount) {
        list = list.slice(0, this.maxCount);
      }
      this.list = list;
      this.storage.set(this.key, this.scope, list);
    }

    /**
     * Remove a text filter.
     *
     * @param {string} text
     */
    remove(text) {
      text = text.trim();
      const list = this.getFromStorage();
      const index = list.indexOf(text);
      if (index === -1) {
        return;
      }
      list.splice(index, 1);
      this.list = list;
      this.storage.set(this.key, this.scope, list);
    }
  }
  _exports.default = _default;
});

define("helpers/misc/attachment-insert-from-source", ["exports", "di", "metadata", "model-factory"], function (_exports, _di, _metadata, _modelFactory) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _metadata = _interopRequireDefault(_metadata);
  _modelFactory = _interopRequireDefault(_modelFactory);
  let _init_metadata, _init_extra_metadata, _init_modelFactory, _init_extra_modelFactory;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * @internal
   */
  class AttachmentInsertSourceFromHelper {
    static #_ = [_init_metadata, _init_extra_metadata, _init_modelFactory, _init_extra_modelFactory] = _applyDecs(this, [], [[(0, _di.inject)(_metadata.default), 0, "metadata"], [(0, _di.inject)(_modelFactory.default), 0, "modelFactory"]]).e;
    /**
     * @param {import('views/fields/attachment-multiple').default|import('views/fields/file').default} view
     */
    constructor(view) {
      _init_extra_modelFactory(this);
      /** @private */
      this.view = view;

      /** @private */
      this.model = view.model;
    }

    /**
     * @type {Metadata}
     * @private
     */
    metadata = _init_metadata(this);

    /**
     * @type {ModelFactory}
     * @private
     */
    modelFactory = (_init_extra_metadata(this), _init_modelFactory(this));

    /**
     * @param {{
     *     source: string,
     *     onInsert: function(import('model').default[]),
     * }} params
     */
    insert(params) {
      const source = params.source;
      const viewName = this.metadata.get(['clientDefs', 'Attachment', 'sourceDefs', source, 'insertModalView']) || this.metadata.get(['clientDefs', source, 'modalViews', 'select']) || 'views/modals/select-records';
      let filters = {};
      if ('getSelectFilters' + source in this.view) {
        filters = this.view['getSelectFilters' + source]() || {};
      }
      if (this.model.attributes.parentId && this.model.attributes.parentType === 'Account') {
        if (this.metadata.get(`entityDefs.${source}.fields.account.type`) === 'link' && this.metadata.get(`entityDefs.${source}.links.account.entity`) === 'Account') {
          filters = {
            account: {
              type: 'equals',
              attribute: 'accountId',
              value: this.model.attributes.parentId,
              data: {
                type: 'is',
                idValue: this.model.attributes.parentId,
                nameValue: this.model.attributes.parentType
              }
            },
            ...filters
          };
        }
      }
      let boolFilterList = this.metadata.get(`clientDefs.Attachment.sourceDefs.${source}.boolFilterList`);
      if ('getSelectBoolFilterList' + source in this.view) {
        boolFilterList = this.view['getSelectBoolFilterList' + source]();
      }
      let primaryFilterName = this.metadata.get(`clientDefs.Attachment.sourceDefs.${source}.primaryFilter`);
      if ('getSelectPrimaryFilterName' + source in this.view) {
        primaryFilterName = this.view['getSelectPrimaryFilterName' + source]();
      }

      /** @type {module:views/modals/select-records~Options} */
      const options = {
        entityType: source,
        createButton: false,
        filters: filters,
        boolFilterList: boolFilterList,
        primaryFilterName: primaryFilterName,
        multiple: true,
        onSelect: models => {
          models.forEach(async model => {
            if (model.entityType === 'Attachment') {
              params.onInsert([model]);
              return;
            }

            /** @type {Record[]} */
            const attachmentDataList = await Espo.Ajax.postRequest(`${source}/action/getAttachmentList`, {
              id: model.id,
              field: this.view.name,
              parentType: this.view.entityType
            });
            const attachmentSeed = await this.modelFactory.create('Attachment');
            for (const item of attachmentDataList) {
              const attachment = attachmentSeed.clone();
              attachment.set(item);
              params.onInsert([attachment]);
            }
          });
        }
      };
      Espo.Ui.notifyWait();
      this.view.createView('modal', viewName, options, view => {
        view.render();
        Espo.Ui.notify();
      });
    }
  }
  _exports.default = AttachmentInsertSourceFromHelper;
});

define("web-socket-manager", ["exports", "js-base64"], function (_exports, _jsBase) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _jsBase = _interopRequireDefault(_jsBase);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 web-socket-manager */

  /**
   * A web-socket manager.
   */
  class WebSocketManager {
    /**
     * @private
     * @type {number}
     */
    pingInterval = 60;

    /**
     * @private
     * @type {number}
     */
    reconnectInterval = 3;

    /**
     * @private
     */
    pingTimeout;

    /**
     * @param {module:models/settings} config A config.
     */
    constructor(config) {
      /**
       * @private
       * @type {module:models/settings}
       */
      this.config = config;

      /**
       * @private
       * @type {Function[]}
       */
      this.subscribeToReconnectQueue = [];

      /**
       * @private
       * @type {{category: string, callback: Function}[]}
       */
      this.subscribeQueue = [];

      /**
       * @private
       * @type {{category: string, callback: Function}[]}
       */
      this.subscriptions = [];

      /**
       * @private
       * @type {boolean}
       */
      this.isConnected = false;

      /**
       * @private
       */
      this.connection = null;

      /**
       * @private
       * @type {string}
       */
      this.url = '';

      /**
       * @private
       * @type {string}
       */
      this.protocolPart = '';
      const url = this.config.get('webSocketUrl');
      if (url) {
        if (url.indexOf('wss://') === 0) {
          this.url = url.substring(6);
          this.protocolPart = 'wss://';
        } else {
          this.url = url.substring(5);
          this.protocolPart = 'ws://';
        }
      } else {
        const siteUrl = this.config.get('siteUrl') || '';
        if (siteUrl.indexOf('https://') === 0) {
          this.url = siteUrl.substring(8);
          this.protocolPart = 'wss://';
        } else {
          this.url = siteUrl.substring(7);
          this.protocolPart = 'ws://';
        }
        if (~this.url.indexOf('/')) {
          this.url = this.url.replace(/\/$/, '');
        }
        const port = this.protocolPart === 'wss://' ? 443 : 8080;
        const si = this.url.indexOf('/');
        if (~si) {
          this.url = this.url.substring(0, si) + ':' + port;
        } else {
          this.url += ':' + port;
        }
        if (this.protocolPart === 'wss://') {
          this.url += '/wss';
        }
      }
    }

    /**
     * Connect.
     *
     * @param {string} auth An auth string.
     * @param {string} userId A user ID.
     */
    connect(auth, userId) {
      const authArray = _jsBase.default.decode(auth).split(':');
      const authToken = authArray[1];
      const url = `${this.protocolPart + this.url}?authToken=${authToken}&userId=${userId}`;
      try {
        this.connectInternal(auth, userId, url);
      } catch (e) {
        console.error(e.message);
        this.connection = null;
      }
    }

    /**
     * @private
     * @param {string} auth
     * @param {string} userId
     * @param {string} url
     */
    connectInternal(auth, userId, url) {
      let wasConnected = false;
      this.connection = new ab.Session(url, () => {
        this.isConnected = true;
        this.subscribeQueue.forEach(item => {
          this.subscribe(item.category, item.callback);
        });
        this.subscribeQueue = [];
        if (wasConnected) {
          this.subscribeToReconnectQueue.forEach(callback => callback());
        }
        this.schedulePing();
        wasConnected = true;
      }, code => {
        if (code === ab.CONNECTION_CLOSED) {
          this.subscribeQueue = [];
        }
        if (code === ab.CONNECTION_LOST || code === ab.CONNECTION_UNREACHABLE) {
          if (this.isConnected) {
            this.subscribeQueue = this.subscriptions;
            this.subscriptions = [];
          }
          setTimeout(() => this.connect(auth, userId), this.reconnectInterval * 1000);
        }
        this.isConnected = false;
      }, {
        skipSubprotocolCheck: true
      });
    }

    /**
     * Subscribe to reconnecting.
     *
     * @param {function(): void} callback A callback.
     * @since 9.1.1
     */
    subscribeToReconnect(callback) {
      this.subscribeToReconnectQueue.push(callback);
    }

    /**
     * Unsubscribe from reconnecting.
     *
     * @param {function(): void} callback A callback.
     * @since 9.1.1
     */
    unsubscribeFromReconnect(callback) {
      this.subscribeToReconnectQueue = this.subscribeToReconnectQueue.filter(it => it !== callback);
    }

    /**
     * Subscribe to a topic.
     *
     * @param {string} category A topic.
     * @param {function(string, *): void} callback A callback.
     */
    subscribe(category, callback) {
      if (!this.connection) {
        return;
      }
      if (!this.isConnected) {
        this.subscribeQueue.push({
          category: category,
          callback: callback
        });
        return;
      }
      try {
        this.connection.subscribe(category, callback);
        this.subscriptions.push({
          category: category,
          callback: callback
        });
      } catch (e) {
        if (e.message) {
          console.error(e.message);
        } else {
          console.error("WebSocket: Could not subscribe to " + category + ".");
        }
      }
    }

    /**
     * Unsubscribe.
     *
     * @param {string} category A topic.
     * @param {Function} [callback] A callback.
     */
    unsubscribe(category, callback) {
      if (!this.connection) {
        return;
      }
      this.subscribeQueue = this.subscribeQueue.filter(item => {
        if (callback === undefined) {
          return item.category !== category;
        }
        return item.category !== category || item.callback !== callback;
      });
      this.subscriptions = this.subscriptions.filter(item => {
        if (callback === undefined) {
          return item.category !== category;
        }
        return item.category !== category || item.callback !== callback;
      });
      try {
        this.connection.unsubscribe(category, callback);
      } catch (e) {
        if (e.message) {
          console.error(e.message);
        } else {
          console.error("WebSocket: Could not unsubscribe from " + category + ".");
        }
      }
    }

    /**
     * Close a connection.
     */
    close() {
      this.stopPing();
      if (!this.connection) {
        return;
      }
      this.subscribeQueue = [];
      this.subscriptions = [];
      try {
        this.connection.close();
      } catch (e) {
        console.error(e.message);
      }
      this.isConnected = false;
    }

    /**
     * @private
     */
    stopPing() {
      this.pingTimeout = undefined;
    }

    /**
     * @private
     */
    schedulePing() {
      //ab._debugws = true;

      if (!this.connection) {
        this.stopPing();
        return;
      }
      this.pingTimeout = setTimeout(() => {
        if (!this.connection) {
          return;
        }
        this.connection.publish('', '');
        this.schedulePing();
      }, this.pingInterval * 1000);
    }
  }
  var _default = _exports.default = WebSocketManager;
});

define("ui", ["exports", "marked", "dompurify", "jquery"], function (_exports, _marked, _dompurify, _jquery) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _dompurify = _interopRequireDefault(_dompurify);
  _jquery = _interopRequireDefault(_jquery);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 */

  /**
   * 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<module:ui.Dialog~Button|false>} [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<module:ui.Dialog~Button|false>}
       */
      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 = (0, _jquery.default)('<div>').addClass('modal-body body').html(this.body);
      const $content = (0, _jquery.default)('<div>').addClass('modal-content');
      if ($header) {
        $content.append($header);
      }
      if ($footer && this.options.footerAtTheTop) {
        $content.append($footer);
      }
      $content.append($body);
      if ($footer && !this.options.footerAtTheTop) {
        $content.append($footer);
      }
      const $dialog = (0, _jquery.default)('<div>').addClass('modal-dialog').append($content);
      const $container = (0, _jquery.default)(this.container);
      (0, _jquery.default)('<div>').attr('id', this.id).attr('class', this.className + ' modal').attr('role', 'dialog').attr('tabindex', '-1').append($dialog).appendTo($container);

      /**
       * An element.
       *
       * @type {JQuery}
       */
      this.$el = (0, _jquery.default)('#' + this.id);

      /**
       * @private
       * @type {Element}
       */
      this.el = this.$el.get(0);
      this.$el.find('header a.close').on('click', () => {
        //this.close();
      });
      this.initButtonEvents();
      if (this.draggable) {
        this.$el.find('header').css('cursor', 'pointer');

        // noinspection JSUnresolvedReference
        this.$el.draggable({
          handle: 'header'
        });
      }
      const modalContentEl = this.$el.find('.modal-content');
      if (this.width) {
        modalContentEl.css('width', this.width);
        modalContentEl.css('margin-left', '-' + parseInt(this.width.replace('px', '')) / 5 + 'px');
      }
      if (this.removeOnClose) {
        this.$el.on('hidden.bs.modal', e => {
          if (this.$el.get(0) === e.target) {
            if (!this.onCloseIsCalled) {
              this.close();
            }
            if (this.skipRemove) {
              return;
            }
            this.remove();
          }
        });
      }
      const $window = (0, _jquery.default)(window);
      this.$el.on('shown.bs.modal', () => {
        (0, _jquery.default)('.modal-backdrop').not('.stacked').addClass('stacked');
        const headerHeight = this.$el.find('.modal-header').outerHeight() || 0;
        const footerHeight = this.$el.find('.modal-footer').outerHeight() || 0;
        let diffHeight = headerHeight + footerHeight;
        if (!options.fullHeight) {
          diffHeight = diffHeight + options.bodyDiffHeight;
        }
        if (this.fitHeight || options.fullHeight) {
          const processResize = () => {
            const windowHeight = window.innerHeight;
            const windowWidth = $window.width();
            if (!options.fullHeight && windowHeight < 512) {
              this.$el.find('div.modal-body').css({
                maxHeight: 'none',
                overflow: 'auto',
                height: 'none'
              });
              return;
            }
            const cssParams = {
              overflow: 'auto'
            };
            if (options.fullHeight) {
              cssParams.height = windowHeight - diffHeight + 'px';
              this.$el.css('paddingRight', 0);
            } else {
              if (windowWidth <= options.screenWidthXs) {
                cssParams.maxHeight = 'none';
              } else {
                cssParams.maxHeight = windowHeight - diffHeight + 'px';
              }
            }
            this.$el.find('div.modal-body').css(cssParams);
          };
          $window.off('resize.modal-height');
          $window.on('resize.modal-height', processResize);
          processResize();
        }
      });
      const $documentBody = (0, _jquery.default)(document.body);
      this.$el.on('hidden.bs.modal', () => {
        if ((0, _jquery.default)('.modal:visible').length > 0) {
          $documentBody.addClass('modal-open');
        }
      });
    }

    /**
     * Get a general container element.
     *
     * @return {HTMLDivElement}
     * @since 9.1.0
     */
    getElement() {
      return this.el;
    }

    /**
     * Update the header text.
     *
     * @param {string} text
     * @since 9.1.0
     */
    setHeaderText(text) {
      const element = this.el.querySelector('.modal-header .modal-title');
      if (!element) {
        return;
      }
      element.textContent = text;
    }

    /** @private */
    callOnClose() {
      if (this.onClose) {
        this.onClose();
      }
    }

    /** @private */
    callOnBackdropClick() {
      if (this.onBackdropClick) {
        this.onBackdropClick();
      }
    }

    /** @private */
    callOnRemove() {
      if (this.onRemove) {
        this.onRemove();
      }
    }

    /**
     * Set action items.
     *
     * @param {module:ui.Dialog~Button[]} buttonList
     * @param {Array<module:ui.Dialog~Button|false>} dropdownItemList
     */
    setActionItems(buttonList, dropdownItemList) {
      this.buttonList = buttonList;
      this.dropdownItemList = dropdownItemList;
    }

    /**
     * Init button events.
     */
    initButtonEvents() {
      this.buttonList.forEach(o => {
        if (typeof o.onClick === 'function') {
          const $button = (0, _jquery.default)('#' + this.id + ' .modal-footer button[data-name="' + o.name + '"]');
          $button.on('click', e => o.onClick(this, e));
        }
      });
      this.dropdownItemList.forEach(o => {
        if (o === false) {
          return;
        }
        if (typeof o.onClick === 'function') {
          const $button = (0, _jquery.default)('#' + this.id + ' .modal-footer a[data-name="' + o.name + '"]');
          $button.on('click', e => o.onClick(this, e));
        }
      });
    }

    /**
     * @private
     * @return {JQuery|null}
     */
    getHeader() {
      if (!this.header) {
        return null;
      }
      const $header = (0, _jquery.default)('<header />').addClass('modal-header').addClass(this.options.fixedHeaderHeight ? 'fixed-height' : '').append((0, _jquery.default)('<h4 />').addClass('modal-title').append((0, _jquery.default)('<span />').addClass('modal-title-text').html(this.header)));
      if (this.collapseButton) {
        $header.prepend((0, _jquery.default)('<a>').addClass('collapse-button').attr('role', 'button').attr('tabindex', '-1').attr('data-action', 'collapseModal').append((0, _jquery.default)('<span />').addClass('fas fa-minus')));
      }
      if (this.maximizeButton) {
        {
          const a = document.createElement('a');
          a.classList.add('maximize-button');
          a.role = 'button';
          a.tabIndex = -1;
          a.setAttribute('data-action', 'maximizeModal');
          const icon = document.createElement('span');
          icon.classList.add('far', 'fa-window-maximize');
          a.append(icon);
          $header.prepend(a);
          this.maximizeButtonElement = a;
          a.addEventListener('click', () => {
            this.maximizeButtonElement.classList.add('hidden');
            this.minimizeButtonElement.classList.remove('hidden');
            this.el.querySelector('.modal-dialog').classList.add('maximized');
            if (this.onMaximize) {
              this.onMaximize();
            }
            this.getElement().focus();
          });
        }
        {
          const a = document.createElement('a');
          a.classList.add('minimize-button', 'hidden');
          a.role = 'button';
          a.tabIndex = -1;
          a.setAttribute('data-action', 'minimizeModal');
          const icon = document.createElement('span');
          icon.classList.add('far', 'fa-window-minimize');
          a.append(icon);
          $header.prepend(a);
          this.minimizeButtonElement = a;
          a.addEventListener('click', () => {
            this.minimizeButtonElement.classList.add('hidden');
            this.maximizeButtonElement.classList.remove('hidden');
            this.el.querySelector('.modal-dialog').classList.remove('maximized');
            if (this.onMinimize) {
              this.onMinimize();
            }
            this.getElement().focus();
          });
        }
      }
      if (this.closeButton) {
        $header.prepend((0, _jquery.default)('<a>').addClass('close').attr('data-dismiss', 'modal').attr('role', 'button').attr('tabindex', '-1').append((0, _jquery.default)('<span />').attr('aria-hidden', 'true').html('&times;')));
      }
      return $header;
    }

    /**
     * Get a footer.
     *
     * @return {JQuery|null}
     */
    getFooter() {
      if (!this.buttonList.length && !this.dropdownItemList.length) {
        return null;
      }
      const $footer = (0, _jquery.default)('<footer>').addClass('modal-footer');
      const $main = (0, _jquery.default)('<div>').addClass('btn-group').addClass('main-btn-group');
      const $additional = (0, _jquery.default)('<div>').addClass('btn-group').addClass('additional-btn-group');
      this.buttonList.forEach(/** module:ui.Dialog~Button */o => {
        const style = o.style || 'default';
        const $button = (0, _jquery.default)('<button>').attr('type', 'button').attr('data-name', o.name).addClass('btn').addClass('btn-' + style).addClass(o.className || 'btn-xs-wide');
        if (o.disabled) {
          $button.attr('disabled', 'disabled');
          $button.addClass('disabled');
        }
        if (o.hidden) {
          $button.addClass('hidden');
        }
        if (o.title) {
          $button.attr('title', o.title);
        }
        if (o.text) {
          $button.text(o.text);
        }
        if (o.html) {
          $button.html(o.html);
        }
        if (o.pullLeft || o.position === 'right') {
          $additional.append($button);
          return;
        }
        $main.append($button);
      });
      const allDdItemsHidden = this.dropdownItemList.filter(o => o && !o.hidden).length === 0;
      const $dropdown = (0, _jquery.default)('<div>').addClass('btn-group').addClass(allDdItemsHidden ? 'hidden' : '').append((0, _jquery.default)('<button>').attr('type', 'button').addClass('btn btn-default dropdown-toggle').addClass(allDdItemsHidden ? 'hidden' : '').attr('data-toggle', 'dropdown').append((0, _jquery.default)('<span>').addClass('fas fa-ellipsis-h')));
      const $ul = (0, _jquery.default)('<ul>').addClass('dropdown-menu pull-right');
      $dropdown.append($ul);
      this.dropdownItemList.forEach((o, i) => {
        if (o === false) {
          if (i === this.dropdownItemList.length - 1) {
            return;
          }
          $ul.append(`<li class="divider"></li>`);
          return;
        }
        const $a = (0, _jquery.default)('<a>').attr('role', 'button').attr('tabindex', '0').attr('data-name', o.name);
        if (o.text) {
          $a.text(o.text);
        }
        if (o.title) {
          $a.attr('title', o.title);
        }
        if (o.html) {
          $a.html(o.html);
        }
        const $li = (0, _jquery.default)('<li>').addClass(o.hidden ? ' hidden' : '').append($a);
        $ul.append($li);
      });
      if ($ul.children().length) {
        $main.append($dropdown);
      }
      if ($additional.children().length) {
        $footer.append($additional);
      }
      $footer.append($main);
      return $footer;
    }

    /**
     * @internal
     * @type {HTMLElement}
     */
    _backdropElement;

    /**
     * Show.
     */
    show() {
      shownDialogList.push(this);

      // noinspection JSUnresolvedReference,JSUnusedGlobalSymbols
      this.$el.modal({
        backdrop: this.backdrop,
        keyboard: this.keyboard,
        onBackdropCreate: element => {
          this._backdropElement = element;
          if (this.backdropClassName) {
            this._backdropElement.classList.add(this.backdropClassName);
          }
        }
      });
      this.$el.find('.modal-content').removeClass('hidden');
      for (const [i, dialog] of shownDialogList.entries()) {
        if (i < shownDialogList.length - 1 && dialog.getElement() && dialog.getElement().parentElement) {
          if (dialog._backdropElement) {
            dialog._backdropElement.classList.add('hidden');
          }
          dialog.getElement().parentElement.classList.add('overlaid');
        }
      }
      this.$el.off('click.dismiss.bs.modal');
      this.$el.on('click.dismiss.bs.modal', '> div.modal-dialog > div.modal-content > header [data-dismiss="modal"]', () => this.close());
      this.$el.on('mousedown', e => {
        this.$mouseDownTarget = (0, _jquery.default)(e.target);
      });
      this.$el.on('click.dismiss.bs.modal', e => {
        if (e.target !== e.currentTarget) {
          return;
        }
        if (this.$mouseDownTarget && this.$mouseDownTarget.closest('.modal-content').length) {
          return;
        }
        this.callOnBackdropClick();
        if (this.backdrop === 'static') {
          return;
        }
        this.close();
      });
      (0, _jquery.default)('body > .popover').addClass('hidden');
    }

    /**
     * Hide.
     *
     * @todo Check usage. Deprecate?
     */
    hide() {
      this.$el.find('.modal-content').addClass('hidden');
    }

    /**
     * Hide with a backdrop.
     */
    hideWithBackdrop() {
      if (this._backdropElement) {
        this._backdropElement.classList.add('hidden');
      }
      this._hideInternal();
      this.skipRemove = true;
      setTimeout(() => this.skipRemove = false, 50);

      // noinspection JSUnresolvedReference
      this.$el.modal('hide');
      this.$el.find('.modal-content').addClass('hidden');
    }

    /**
     * @private
     */
    _hideInternal() {
      const findIndex = shownDialogList.findIndex(it => it === this);
      if (findIndex >= 0) {
        shownDialogList.splice(findIndex, 1);
      }
      const last = shownDialogList[shownDialogList.length - 1];
      if (last && last._backdropElement) {
        last._backdropElement.classList.remove('hidden');
      }
      if (last && last.getElement() && last.getElement().parentElement) {
        last.getElement().parentElement.classList.remove('overlaid');
      }
    }

    /**
     * @private
     */
    _close() {
      this._hideInternal();
    }

    /**
     * @private
     * @param {Element} element
     * @return {Element|null}
     */
    _findClosestFocusableElement(element) {
      // noinspection JSUnresolvedReference
      const isVisible = !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
      if (isVisible) {
        // noinspection JSUnresolvedReference
        element.focus({
          preventScroll: true
        });
        return element;
      }
      const $element = (0, _jquery.default)(element);
      if ($element.closest('.dropdown-menu').length) {
        const $button = $element.closest('.btn-group').find(`[data-toggle="dropdown"]`);
        if ($button.length) {
          // noinspection JSUnresolvedReference
          $button.get(0).focus({
            preventScroll: true
          });
          return $button.get(0);
        }
      }
      return null;
    }

    /**
     * Close.
     */
    close() {
      if (!this.onCloseIsCalled) {
        this.callOnClose();
        this.onCloseIsCalled = true;
        if (this.activeElement) {
          setTimeout(() => {
            const element = this._findClosestFocusableElement(this.activeElement);
            if (element) {
              // noinspection JSUnresolvedReference
              element.focus({
                preventScroll: true
              });
            }
          }, 50);
        }
      }
      this._close();
      // noinspection JSUnresolvedReference
      this.$el.modal('hide');
      (0, _jquery.default)(this).trigger('dialog:close');
    }

    /**
     * Remove.
     */
    remove() {
      this.callOnRemove();

      // Hack allowing multiple backdrops.
      // `close` function may be called twice.
      this._close();
      this.$el.remove();
      (0, _jquery.default)(this).off();
      (0, _jquery.default)(window).off('resize.modal-height');
    }
  }

  /**
   * UI utils.
   */
  Espo.Ui = {
    Dialog: Dialog,
    /**
     * @typedef {Object} Espo.Ui~ConfirmOptions
     *
     * @property {string} confirmText A confirm-button text.
     * @property {string} cancelText A cancel-button text.
     * @property {'danger'|'success'|'warning'|'default'} [confirmStyle='danger']
     *   A confirm-button style.
     * @property {'static'|boolean} [backdrop=false] A backdrop.
     * @property {function():void} [cancelCallback] A cancel-callback.
     * @property {boolean} [isHtml=false] Whether the message is HTML.
     */

    /**
     * Show a confirmation dialog.
     *
     * @param {string} message A message.
     * @param {Espo.Ui~ConfirmOptions|{}} o Options.
     * @param {function} [callback] Deprecated. Use a promise.
     * @param {Object} [context] Deprecated.
     * @returns {Promise} Resolves if confirmed.
     */
    confirm: function (message, o, callback, context) {
      o = o || {};
      const confirmText = o.confirmText;
      const cancelText = o.cancelText;
      const confirmStyle = o.confirmStyle || 'danger';
      let backdrop = o.backdrop;
      if (typeof backdrop === 'undefined') {
        backdrop = false;
      }
      let isResolved = false;
      const processCancel = () => {
        if (!o.cancelCallback) {
          return;
        }
        if (context) {
          o.cancelCallback.call(context);
          return;
        }
        o.cancelCallback();
      };
      if (!o.isHtml) {
        message = Handlebars.Utils.escapeExpression(message);
      }
      return new Promise(resolve => {
        const dialog = new Dialog({
          backdrop: backdrop,
          header: null,
          className: 'dialog-confirm',
          backdropClassName: 'backdrop-confirm',
          body: '<span class="confirm-message">' + message + '</a>',
          buttonList: [{
            text: ' ' + confirmText + ' ',
            name: 'confirm',
            className: 'btn-s-wide',
            onClick: () => {
              isResolved = true;
              if (callback) {
                if (context) {
                  callback.call(context);
                } else {
                  callback();
                }
              }
              resolve();
              dialog.close();
            },
            style: confirmStyle,
            position: 'right'
          }, {
            text: cancelText,
            name: 'cancel',
            className: 'btn-s-wide',
            onClick: () => {
              isResolved = true;
              dialog.close();
              processCancel();
            },
            position: 'left'
          }],
          onClose: () => {
            if (isResolved) {
              return;
            }
            processCancel();
          }
        });
        dialog.show();
        dialog.$el.find('button[data-name="confirm"]').focus();
      });
    },
    /**
     * Create a dialog.
     *
     * @param {module:ui.Dialog~Params} options Options.
     * @returns {Dialog}
     */
    dialog: function (options) {
      return new Dialog(options);
    },
    /**
     * Popover options.
     *
     * @typedef {Object} Espo.Ui~PopoverOptions
     *
     * @property {'bottom'|'top'|'left'|'right'} [placement='bottom'] A placement.
     * @property {string|JQuery} [container] A container selector.
     * @property {string} [content] An HTML content.
     * @property {string} [text] A text.
     * @property {'manual'|'click'|'hover'|'focus'} [trigger='manual'] A trigger type.
     * @property {boolean} [noToggleInit=false] Skip init toggle on click.
     * @property {boolean} [preventDestroyOnRender=false] Don't destroy on re-render.
     * @property {boolean} [noHideOnOutsideClick=false] Don't hide on clicking outside.
     * @property {function(): void} [onShow] On-show callback.
     * @property {function(): void} [onHide] On-hide callback.
     * @property {string|function(): string} [title] A title text.
     * @property {boolean} [keepElementTitle] Keep an original element's title.
     */

    /**
     * Init a popover.
     *
     * @param {Element|JQuery} element An element.
     * @param {Espo.Ui~PopoverOptions} o Options.
     * @param {module:view} [view] A view.
     * @return {{
     *     hide: function(),
     *     destroy: function(),
     *     show: function(): string,
     *     detach: function(),
     * }}
     */
    popover: function (element, o, view) {
      const $el = (0, _jquery.default)(element);
      const $body = (0, _jquery.default)('body');
      const content = o.content || Handlebars.Utils.escapeExpression(o.text || '');
      let isShown = false;
      let container = o.container;
      if (!container) {
        const $modalBody = $el.closest('.modal-body');
        container = $modalBody.length ? $modalBody : 'body';
      }

      // noinspection JSUnresolvedReference
      $el.popover({
        placement: o.placement || 'bottom',
        container: container,
        viewport: container,
        html: true,
        content: content,
        trigger: o.trigger || 'manual',
        title: o.title,
        keepElementTitle: o.keepElementTitle
      }).on('shown.bs.popover', () => {
        isShown = true;
        if (!view) {
          return;
        }
        if (view && !o.noHideOnOutsideClick) {
          $body.off(`click.popover-${view.cid}`);
          $body.on(`click.popover-${view.cid}`, e => {
            if ((0, _jquery.default)(e.target).closest('.popover').get(0)) {
              return;
            }
            if (_jquery.default.contains($el.get(0), e.target)) {
              return;
            }
            if ($el.get(0) === e.target) {
              return;
            }
            $body.off(`click.popover-${view.cid}`);
            // noinspection JSUnresolvedReference
            $el.popover('hide');
          });
        }
        if (o.onShow) {
          o.onShow();
        }
      }).on('hidden.bs.popover', () => {
        isShown = false;
        if (o.onHide) {
          o.onHide();
        }
      });
      if (!o.noToggleInit) {
        $el.on('click', () => {
          // noinspection JSUnresolvedReference
          $el.popover('toggle');
        });
      }
      let isDetached = false;
      const detach = () => {
        if (view) {
          $body.off(`click.popover-${view.cid}`);
          view.off('remove', destroy);
          view.off('render', destroy);
          view.off('render', hide);
        }
        isDetached = true;
      };
      const destroy = () => {
        if (isDetached) {
          return;
        }

        // noinspection JSUnresolvedReference
        $el.popover('destroy');
        detach();
      };
      const hide = () => {
        if (!isShown) {
          return;
        }

        // noinspection JSUnresolvedReference
        $el.popover('hide');
      };
      const show = () => {
        // noinspection JSUnresolvedReference
        $el.popover('show');
        return $el.attr('aria-describedby');
      };
      if (view) {
        view.once('remove', destroy);
        if (!o.preventDestroyOnRender) {
          view.once('render', destroy);
        }
        if (o.preventDestroyOnRender) {
          view.on('render', hide);
        }
      }
      return {
        hide: () => hide(),
        destroy: () => destroy(),
        show: () => show(),
        detach: () => detach()
      };
    },
    /**
     * Notify options.
     *
     * @typedef {Object} Espo.Ui~NotifyOptions
     * @property {boolean} [closeButton] A close button.
     * @property {boolean} [suppress] Suppress other warning alerts while this is displayed.
     */

    /**
     * Show the spinner.
     *
     * @since 9.1.0
     */
    notifyWait: function () {
      Espo.Ui.notify(' ... ');
    },
    /**
     * Show a notify-message.
     *
     * @param {string|false} [message=false] A message. False removes an already displayed message.
     * @param {'warning'|'danger'|'success'|'info'} [type='warning'] A type.
     * @param {number} [timeout] Microseconds. If empty, then won't be hidden.
     *   Should be hidden manually or by displaying another message.
     * @param {Espo.Ui~NotifyOptions} [options] Options.
     */
    notify: function (message, type, timeout, options) {
      type = type || 'warning';
      options = {
        ...options
      };
      if (type === 'warning' && notifySuppressed) {
        return;
      }
      (0, _jquery.default)('#notification').remove();
      if (!message) {
        return;
      }
      if (options.suppress && timeout) {
        notifySuppressed = true;
        setTimeout(() => notifySuppressed = false, timeout);
      }
      const parsedMessage = message.indexOf('\n') !== -1 ? _marked.marked.parse(message) : _marked.marked.parseInline(message);
      let sanitizedMessage = _dompurify.default.sanitize(parsedMessage, {}).toString();
      const closeButton = options.closeButton || false;
      if (type === 'error') {
        // For bc.
        type = 'danger';
      }
      if (sanitizedMessage === ' ... ') {
        sanitizedMessage = ' <span class="fas fa-spinner fa-spin"> ';
      }
      const additionalClassName = closeButton ? ' alert-closable' : '';
      const $el = (0, _jquery.default)('<div>').addClass('alert alert-' + type + additionalClassName + ' fade in').attr('id', 'notification').css({
        'position': 'fixed',
        'top': '0',
        'left': '50vw',
        'transform': 'translate(-50%, 0)',
        'z-index': 2000
      }).append((0, _jquery.default)('<div>').addClass('message').html(sanitizedMessage));
      if (closeButton) {
        const $close = (0, _jquery.default)('<button>').attr('type', 'button').attr('data-dismiss', 'modal').attr('aria-hidden', 'true').addClass('close').html(`<span class="fas fa-times"></span>`);
        $el.append((0, _jquery.default)('<div>').addClass('close-container').append($close));
        $close.on('click', () => $el.alert('close'));
      }
      if (timeout) {
        setTimeout(() => $el.alert('close'), timeout);
      }
      $el.appendTo('body');
    },
    /**
     * Show a warning message.
     *
     * @param {string} message A message.
     * @param {Espo.Ui~NotifyOptions} [options] Options.
     */
    warning: function (message, options) {
      Espo.Ui.notify(message, 'warning', 2000, options);
    },
    /**
     * Show a success message.
     *
     * @param {string} message A message.
     * @param {Espo.Ui~NotifyOptions} [options] Options.
     */
    success: function (message, options) {
      Espo.Ui.notify(message, 'success', 2000, options);
    },
    /**
     * Show an error message.
     *
     * @param {string} message A message.
     * @param {Espo.Ui~NotifyOptions|true} [options] Options. If true, then only closeButton option will be applied.
     */
    error: function (message, options) {
      options = typeof options === 'boolean' ? {
        closeButton: options
      } : {
        ...options
      };
      const timeout = options.closeButton ? 0 : 4000;
      Espo.Ui.notify(message, 'danger', timeout, options);
    },
    /**
     * Show an info message.
     *
     * @param {string} message A message.
     * @param {Espo.Ui~NotifyOptions} [options] Options.
     */
    info: function (message, options) {
      Espo.Ui.notify(message, 'info', 2000, options);
    }
  };
  let notifySuppressed = false;

  /**
   * @deprecated Use `Espo.Ui`.
   */
  Espo.ui = Espo.Ui;
  var _default = _exports.default = Espo.Ui;
});

define("theme-manager", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 theme-manager */

  /**
   * A theme manager.
   */
  class ThemeManager {
    /**
     * @param {module:models/settings} config A config.
     * @param {module:models/preferences} preferences Preferences.
     * @param {module:metadata} metadata Metadata.
     * @param {string|null} [name] A name. If not set, then will be obtained from config and preferences.
     */
    constructor(config, preferences, metadata, name) {
      /**
       * @private
       * @type {module:models/settings}
       */
      this.config = config;

      /**
       * @private
       * @type {module:models/preferences}
       */
      this.preferences = preferences;

      /**
       * @private
       * @type {module:metadata}
       */
      this.metadata = metadata;

      /**
       * @private
       * @type {boolean}
       */
      this.useConfig = !name;

      /**
       * @private
       * @type {string|null}
       */
      this.name = name || null;
    }

    /**
     * @private
     */
    defaultParams = {
      screenWidthXs: 768,
      dashboardCellHeight: 40,
      dashboardCellMargin: 16
    };

    /**
     * Get a theme name for the current user.
     *
     * @returns {string}
     */
    getName() {
      if (this.name) {
        return this.name;
      }
      if (!this.config.get('userThemesDisabled')) {
        const name = this.preferences.get('theme');
        if (name && name !== '') {
          return name;
        }
      }
      return this.config.get('theme');
    }

    /**
     * Get a theme name currently applied to the DOM.
     *
     * @returns {string|null} Null if not applied.
     */
    getAppliedName() {
      const name = window.getComputedStyle(document.body).getPropertyValue('--theme-name');
      if (!name) {
        return null;
      }
      return name.trim();
    }

    /**
     * Whether a current theme is applied to the DOM.
     *
     * @returns {boolean}
     */
    isApplied() {
      const appliedName = this.getAppliedName();
      if (!appliedName) {
        return true;
      }
      return this.getName() === appliedName;
    }

    /**
     * Get a stylesheet path for a current theme.
     *
     * @returns {string}
     */
    getStylesheet() {
      let link = this.getParam('stylesheet') || 'client/css/espo/espo.css';
      if (this.config.get('cacheTimestamp')) {
        link += '?r=' + this.config.get('cacheTimestamp').toString();
      }
      return link;
    }

    /**
     * Get an iframe stylesheet path for a current theme.
     *
     * @returns {string}
     */
    getIframeStylesheet() {
      let link = this.getParam('stylesheetIframe') || 'client/css/espo/espo-iframe.css';
      if (this.config.get('cacheTimestamp')) {
        link += '?r=' + this.config.get('cacheTimestamp').toString();
      }
      return link;
    }

    /**
     * Get an iframe-fallback stylesheet path for a current theme.
     *
     * @returns {string}
     */
    getIframeFallbackStylesheet() {
      let link = this.getParam('stylesheetIframeFallback') || 'client/css/espo/espo-iframe.css';
      if (this.config.get('cacheTimestamp')) {
        link += '?r=' + this.config.get('cacheTimestamp').toString();
      }
      return link;
    }

    /**
     * Get a theme parameter.
     *
     * @param {string} name A parameter name.
     * @returns {*} Null if not set.
     */
    getParam(name) {
      if (name !== 'params' && name !== 'mappedParams') {
        const varValue = this.getVarParam(name);
        if (varValue !== null) {
          return varValue;
        }
        const mappedValue = this.getMappedParam(name);
        if (mappedValue !== null) {
          return mappedValue;
        }
      }
      let value = this.metadata.get(['themes', this.getName(), name]);
      if (value !== null) {
        return value;
      }
      value = this.metadata.get(['themes', this.getParentName(), name]);
      if (value !== null) {
        return value;
      }
      return this.defaultParams[name] || null;
    }

    /**
     * @private
     * @param {string} name
     * @returns {*}
     */
    getVarParam(name) {
      const params = this.getParam('params') || {};
      if (!(name in params)) {
        return null;
      }
      let values = null;
      if (this.useConfig && !this.config.get('userThemesDisabled') && this.preferences.get('theme')) {
        values = this.preferences.get('themeParams');
      }
      if (!values && this.useConfig) {
        values = this.config.get('themeParams');
      }
      if (values && name in values) {
        return values[name];
      }
      if ('default' in params[name]) {
        return params[name].default;
      }
      return null;
    }

    /**
     * @private
     * @param {string} name
     * @returns {*}
     */
    getMappedParam(name) {
      const mappedParams = this.getParam('mappedParams') || {};
      if (!(name in mappedParams)) {
        return null;
      }
      const mapped = mappedParams[name].param;
      const valueMap = mappedParams[name].valueMap;
      if (mapped && valueMap) {
        const key = this.getParam(mapped);
        return valueMap[key];
      }
      return null;
    }

    /**
     * @private
     * @returns {string}
     */
    getParentName() {
      return this.metadata.get(['themes', this.getName(), 'parent']) || 'Espo';
    }

    /**
     * Whether a current theme is different from a system default theme.
     *
     * @returns {boolean}
     */
    isUserTheme() {
      if (this.config.get('userThemesDisabled')) {
        return false;
      }
      const name = this.preferences.get('theme');
      if (!name || name === '') {
        return false;
      }
      return name !== this.config.get('theme');
    }

    /**
     * Get a font-size factor. To adjust px sizes based on font-size.
     *
     * @return {number}
     * @since 9.0.0
     * @internal Experimental.
     */
    getFontSizeFactor() {
      const paramFontSize = this.getParam('fontSize') || 14;
      const fontSize = parseInt(getComputedStyle(document.body).fontSize);
      return Math.round(fontSize / paramFontSize * 10000) / 10000;
    }
  }
  var _default = _exports.default = ThemeManager;
});

define("session-storage", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 session-storage */

  /**
   * A session storage. Cleared when a page session ends.
   */
  class SessionStorage {
    /** @private */
    storageObject = sessionStorage;

    /**
     * Get a value.
     *
     * @param {string} name A name.
     * @returns {*} Null if not set.
     */
    get(name) {
      let stored;
      try {
        stored = this.storageObject.getItem(name);
      } catch (error) {
        console.error(error);
        return null;
      }
      if (stored) {
        let result = stored;
        if (stored.length > 9 && stored.substring(0, 9) === '__JSON__:') {
          const jsonString = stored.slice(9);
          try {
            result = JSON.parse(jsonString);
          } catch (error) {
            result = stored;
          }
        }
        return result;
      }
      return null;
    }

    /**
     * Set (store) a value.
     *
     * @param {string} name A name.
     * @param {*} value A value.
     */
    set(name, value) {
      if (value === null) {
        this.clear(name);
        return;
      }
      if (value instanceof Object || Array.isArray(value) || value === true || value === false || typeof value === 'number') {
        value = '__JSON__:' + JSON.stringify(value);
      }
      try {
        this.storageObject.setItem(name, value);
      } catch (error) {
        console.error(error);
      }
    }

    /**
     * Has a value.
     *
     * @param {string} name A name.
     * @returns {boolean}
     */
    has(name) {
      return this.storageObject.getItem(name) !== null;
    }

    /**
     * Clear a value.
     *
     * @param {string} name A name.
     */
    clear(name) {
      for (const i in this.storageObject) {
        if (i === name) {
          delete this.storageObject[i];
        }
      }
    }
  }
  var _default = _exports.default = SessionStorage;
});

define("page-title", ["exports", "jquery"], function (_exports, _jquery) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _jquery = _interopRequireDefault(_jquery);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 page-title */

  /**
   * A page-title util.
   */
  class PageTitle {
    /**
     * @class
     * @param {module:models/settings} config A config.
     */
    constructor(config) {
      /**
       * @private
       * @type {boolean}
       */
      this.displayNotificationNumber = config.get('newNotificationCountInTitle') || false;

      /**
       * @private
       * @type {string}
       */
      this.title = (0, _jquery.default)('head title').text() || '';

      /**
       * @private
       * @type {number}
       */
      this.notificationNumber = 0;
    }

    /**
     * Set a title.
     *
     * @param {string} title A title.
     */
    setTitle(title) {
      this.title = title;
      this.update();
    }

    /**
     * Set a notification number.
     *
     * @param {number} notificationNumber A number.
     */
    setNotificationNumber(notificationNumber) {
      this.notificationNumber = notificationNumber;
      if (this.displayNotificationNumber) {
        this.update();
      }
    }

    /**
     * Update a page title.
     */
    update() {
      let value = '';
      if (this.displayNotificationNumber && this.notificationNumber) {
        value = '(' + this.notificationNumber.toString() + ')';
        if (this.title) {
          value += ' ';
        }
      }
      value += this.title;
      (0, _jquery.default)('head title').text(value);
    }
  }
  var _default = _exports.default = PageTitle;
});

define("number-util", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 num-util */

  /**
   * A number util.
   */
  class NumberUtil {
    /**
     * @param {module:models/settings} config A config.
     * @param {module:models/preferences} preferences Preferences.
     */
    constructor(config, preferences) {
      /**
       * @private
       * @type {module:models/settings}
       */
      this.config = config;

      /**
       * @private
       * @type {module:models/preferences}
       */
      this.preferences = preferences;

      /**
       * A thousand separator.
       *
       * @private
       * @type {string|null}
       */
      this.thousandSeparator = null;

      /**
       * A decimal mark.
       *
       * @private
       * @type {string|null}
       */
      this.decimalMark = null;
      this.config.on('change', () => {
        this.thousandSeparator = null;
        this.decimalMark = null;
      });
      this.preferences.on('change', () => {
        this.thousandSeparator = null;
        this.decimalMark = null;
      });

      /**
       * A max decimal places.
       *
       * @private
       * @type {number}
       */
      this.maxDecimalPlaces = 10;
    }

    /**
     * Format an integer number.
     *
     * @param {number} value A value.
     * @returns {string}
     */
    formatInt(value) {
      if (value === null || value === undefined) {
        return '';
      }
      let stringValue = value.toString();
      stringValue = stringValue.replace(/\B(?=(\d{3})+(?!\d))/g, this.getThousandSeparator());
      return stringValue;
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Format a float number.
     *
     * @param {number} value A value.
     * @param {number} [decimalPlaces] Decimal places.
     * @returns {string}
     */
    formatFloat(value, decimalPlaces) {
      if (value === null || value === undefined) {
        return '';
      }
      if (decimalPlaces === 0) {
        value = Math.round(value);
      } else if (decimalPlaces) {
        value = Math.round(value * Math.pow(10, decimalPlaces)) / Math.pow(10, decimalPlaces);
      } else {
        value = Math.round(value * Math.pow(10, this.maxDecimalPlaces)) / Math.pow(10, this.maxDecimalPlaces);
      }
      const parts = value.toString().split('.');
      parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, this.getThousandSeparator());
      if (decimalPlaces === 0) {
        return parts[0];
      }
      if (decimalPlaces) {
        let decimalPartLength = 0;
        if (parts.length > 1) {
          decimalPartLength = parts[1].length;
        } else {
          parts[1] = '';
        }
        if (decimalPlaces && decimalPartLength < decimalPlaces) {
          const limit = decimalPlaces - decimalPartLength;
          for (let i = 0; i < limit; i++) {
            parts[1] += '0';
          }
        }
      }
      return parts.join(this.getDecimalMark());
    }

    /**
     * @private
     * @returns {string}
     */
    getThousandSeparator() {
      if (this.thousandSeparator !== null) {
        return this.thousandSeparator;
      }
      let thousandSeparator = '.';
      if (this.preferences.has('thousandSeparator')) {
        thousandSeparator = this.preferences.get('thousandSeparator');
      } else if (this.config.has('thousandSeparator')) {
        thousandSeparator = this.config.get('thousandSeparator');
      }

      /**
       * A thousand separator.
       *
       * @private
       * @type {string|null}
       */
      this.thousandSeparator = thousandSeparator;
      return thousandSeparator;
    }

    /**
     * @private
     * @returns {string}
     */
    getDecimalMark() {
      if (this.decimalMark !== null) {
        return this.decimalMark;
      }
      let decimalMark = '.';
      if (this.preferences.has('decimalMark')) {
        decimalMark = this.preferences.get('decimalMark');
      } else {
        if (this.config.has('decimalMark')) {
          decimalMark = this.config.get('decimalMark');
        }
      }

      /**
       * A decimal mark.
       *
       * @private
       * @type {string|null}
       */
      this.decimalMark = decimalMark;
      return decimalMark;
    }
  }
  var _default = _exports.default = NumberUtil;
});

define("layout-manager", ["exports", "bullbone"], function (_exports, _bullbone) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 layout-manager */

  /**
   * A layout manager.
   *
   * @mixes Bull.Events
   */
  class LayoutManager {
    /**
     * @param {module:cache|null} [cache] A cache.
     * @param {string} [applicationId] An application ID.
     * @param {string} [userId] A user ID.
     */
    constructor(cache, applicationId, userId) {
      /**
       * @private
       * @type {module:cache|null}
       */
      this.cache = cache || null;

      /**
       * @private
       * @type {string}
       */
      this.applicationId = applicationId || 'espocrm';

      /**
       * @private
       * @type {string|null}
       */
      this.userId = userId || null;

      /**
       * @private
       * @type {Object}
       */
      this.data = {};

      /** @private */
      this.ajax = Espo.Ajax;

      /**
       * @private
       * @type {Object.<string, module:ajax~AjaxPromise>}
       */
      this.fetchPromises = {};
    }

    /**
     * Set a user ID. To be used for the cache purpose.
     *
     * @param {string} userId A user ID.
     * @internal
     * @todo Throw an exception if already set.
     */
    setUserId(userId) {
      this.userId = userId;
    }

    /**
     * @private
     * @param {string} scope
     * @param {string} type
     * @returns {string}
     */
    getKey(scope, type) {
      if (this.userId) {
        return `${this.applicationId}-${this.userId}-${scope}-${type}`;
      }
      return `${this.applicationId}-${scope}-${type}`;
    }

    /**
     * @private
     * @param {string} scope
     * @param {string} type
     * @param {string} [setId]
     * @returns {string}
     */
    getUrl(scope, type, setId) {
      let url = `${scope}/layout/${type}`;
      if (setId) {
        url += `/${setId}`;
      }
      return url;
    }

    /**
     * @callback module:layout-manager~getCallback
     *
     * @param {*} layout A layout.
     */

    /**
     * Get a layout.
     *
     * @param {string} scope A scope (entity type).
     * @param {string} type A layout type (name).
     * @param {module:layout-manager~getCallback} callback
     * @param {boolean} [cache=true] Use cache.
     */
    get(scope, type, callback, cache) {
      if (typeof cache === 'undefined') {
        cache = true;
      }
      if (!callback) {
        callback = () => {};
      }
      const key = this.getKey(scope, type);
      if (cache && key in this.data) {
        callback(this.data[key]);
        return;
      }
      if (this.cache && cache) {
        const cached = this.cache.get('app-layout', key);
        if (cached) {
          callback(cached);
          this.data[key] = cached;
          return;
        }
      }
      if (key in this.fetchPromises) {
        this.fetchPromises[key].then(layout => callback(layout));
        return;
      }
      this.fetchPromises[key] = this.ajax.getRequest(this.getUrl(scope, type)).then(layout => {
        callback(layout);
        this.data[key] = layout;
        if (this.cache) {
          this.cache.set('app-layout', key, layout);
        }
        return layout;
      }).finally(() => delete this.fetchPromises[key]);
    }

    /**
     * Get an original layout.
     *
     * @param {string} scope A scope (entity type).
     * @param {string} type A layout type (name).
     * @param {string} [setId]
     * @param {module:layout-manager~getCallback} callback
     */
    getOriginal(scope, type, setId, callback) {
      let url = 'Layout/action/getOriginal?scope=' + scope + '&name=' + type;
      if (setId) {
        url += '&setId=' + setId;
      }
      Espo.Ajax.getRequest(url).then(layout => {
        if (typeof callback === 'function') {
          callback(layout);
        }
      });
    }

    /**
     * Store and set a layout.
     *
     * @param {string} scope A scope (entity type).
     * @param {string} type A type (name).
     * @param {*} layout A layout.
     * @param {Function} callback A callback.
     * @param {string} [setId] A set ID.
     * @returns {Promise}
     */
    set(scope, type, layout, callback, setId) {
      return Espo.Ajax.putRequest(this.getUrl(scope, type, setId), layout).then(() => {
        const key = this.getKey(scope, type);
        if (this.cache && key) {
          this.cache.clear('app-layout', key);
        }
        delete this.data[key];
        this.trigger('sync');
        if (typeof callback === 'function') {
          callback();
        }
      });
    }

    /**
     * Reset a layout to default.
     *
     * @param {string} scope A scope (entity type).
     * @param {string} type A type (name).
     * @param {Function} callback A callback.
     * @param {string} [setId] A set ID.
     */
    resetToDefault(scope, type, callback, setId) {
      Espo.Ajax.postRequest('Layout/action/resetToDefault', {
        scope: scope,
        name: type,
        setId: setId
      }).then(() => {
        const key = this.getKey(scope, type);
        if (this.cache) {
          this.cache.clear('app-layout', key);
        }
        delete this.data[key];
        this.trigger('sync');
        if (typeof callback === 'function') {
          callback();
        }
      });
    }

    /**
     * Clear loaded data.
     */
    clearLoadedData() {
      this.data = {};
    }
  }
  Object.assign(LayoutManager.prototype, _bullbone.Events);
  var _default = _exports.default = LayoutManager;
});

define("field-manager", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 field-manager */

  /**
   * Utility for getting field related meta information.
   */
  class FieldManager {
    /**
     * Utility for getting field related meta information.
     *
     * @param {Object} [defs] Field type definitions (metadata > fields).
     * @param {module:metadata} [metadata] Metadata.
     * @param {module:acl-manager} [acl] An ACL.
     */
    constructor(defs, metadata, acl) {
      /**
       * @typedef {Object} FieldManager~defs
       * @property {string[]} [actualFields]
       * @property {string[]} [notActualFields]
       * @property {'suffix'|'prefix'} [naming]
       * @property {Object.<string, Object.<string, *>>} [params]
       * @property {boolean} [filter]
       * @property {boolean} [notMergeable]
       * @property {string} [view]
       */

      /**
       * @public
       * @internal
       * @type {FieldManager~defs}
       */
      this.defs = defs || /** @type {FieldManager~defs} */{};

      /**
       * @public
       * @internal
       * @type {module:metadata}
       */
      this.metadata = metadata;

      /**
       * @public
       * @internal
       * @type {module:acl-manager}
       */
      this.acl = acl;
    }

    /**
     * Get a list of parameters for a specific field type.
     *
     * @param {string} fieldType A field type.
     * @returns {Object.<string, *>[]}
     */
    getParamList(fieldType) {
      if (fieldType in this.defs) {
        return this.defs[fieldType].params || [];
      }
      return [];
    }

    /**
     * Whether search filters are allowed for a field type.
     *
     * @param {string} fieldType A field type.
     * @returns {boolean}
     */
    checkFilter(fieldType) {
      if (fieldType in this.defs) {
        if ('filter' in this.defs[fieldType]) {
          return this.defs[fieldType].filter;
        }
        return false;
      }
      return false;
    }

    /**
     * Whether a merge operation is allowed for a field type.
     *
     * @param {string} fieldType A field type.
     * @returns {boolean}
     */
    isMergeable(fieldType) {
      if (fieldType in this.defs) {
        return !this.defs[fieldType].notMergeable;
      }
      return false;
    }

    /**
     * Get a list of attributes of an entity type.
     *
     * @param {string} entityType An entity type.
     * @param {module:field-manager~FieldFilters} [options] Filters.
     * @returns {string[]}
     */
    getEntityTypeAttributeList(entityType, options) {
      const list = [];
      const defs = this.metadata.get(`entityDefs.${entityType}.fields`) || {};
      this.getEntityTypeFieldList(entityType, options).forEach(field => {
        const fieldDefs = /** @type {Record} */defs[field] || {};
        this.getAttributeList(fieldDefs.type, field).forEach(attr => {
          if (!list.includes(attr)) {
            list.push(attr);
          }
        });
      });
      return list;
    }

    /**
     * Get a list of actual attributes by a given field type and field name.
     * Non-actual attributes contains data that for a representation-only purpose.
     * E.g. `accountId` is actual, `accountName` is non-actual.
     *
     * @param {string} fieldType A field type.
     * @param {string} fieldName A field name.
     * @returns {string[]}
     */
    getActualAttributeList(fieldType, fieldName) {
      const fieldNames = [];
      if (fieldType in this.defs) {
        if ('actualFields' in this.defs[fieldType]) {
          const actualFields = this.defs[fieldType].actualFields;
          let naming = 'suffix';
          if ('naming' in this.defs[fieldType]) {
            naming = this.defs[fieldType].naming;
          }
          if (naming === 'prefix') {
            actualFields.forEach(f => {
              fieldNames.push(f + Espo.Utils.upperCaseFirst(fieldName));
            });
          } else {
            actualFields.forEach(f => {
              fieldNames.push(fieldName + Espo.Utils.upperCaseFirst(f));
            });
          }
        } else {
          fieldNames.push(fieldName);
        }
      }
      return fieldNames;
    }

    /**
     * Get a list of non-actual attributes by a given field type and field name.
     * Non-actual attributes contains data that for a representation-only purpose.
     * E.g. `accountId` is actual, `accountName` is non-actual.
     *
     * @param {string} fieldType A field type.
     * @param {string} fieldName A field name.
     * @returns {string[]}
     */
    getNotActualAttributeList(fieldType, fieldName) {
      const fieldNames = [];
      if (fieldType in this.defs) {
        if ('notActualFields' in this.defs[fieldType]) {
          const notActualFields = this.defs[fieldType].notActualFields;
          let naming = 'suffix';
          if ('naming' in this.defs[fieldType]) {
            naming = this.defs[fieldType].naming;
          }
          if (naming === 'prefix') {
            notActualFields.forEach(f => {
              if (f === '') {
                fieldNames.push(fieldName);
              } else {
                fieldNames.push(f + Espo.Utils.upperCaseFirst(fieldName));
              }
            });
          } else {
            notActualFields.forEach(f => {
              fieldNames.push(fieldName + Espo.Utils.upperCaseFirst(f));
            });
          }
        }
      }
      return fieldNames;
    }

    /**
     * Get an attribute list of a specific field.
     *
     * @param {string} entityType An entity type.
     * @param {string} field A field.
     * @returns {string[]}
     */
    getEntityTypeFieldAttributeList(entityType, field) {
      const type = this.metadata.get(['entityDefs', entityType, 'fields', field, 'type']);
      if (!type) {
        return [];
      }
      return _.union(this.getAttributeList(type, field), this._getEntityTypeFieldAdditionalAttributeList(entityType, field));
    }

    /**
     * Get an actual attribute list of a specific field.
     *
     * @param {string} entityType An entity type.
     * @param {string} field A field.
     * @returns {string[]}
     */
    getEntityTypeFieldActualAttributeList(entityType, field) {
      const type = this.metadata.get(['entityDefs', entityType, 'fields', field, 'type']);
      if (!type) {
        return [];
      }
      return _.union(this.getActualAttributeList(type, field), this._getEntityTypeFieldAdditionalAttributeList(entityType, field));
    }

    /**
     * @private
     */
    _getEntityTypeFieldAdditionalAttributeList(entityType, field) {
      const type = this.metadata.get(['entityDefs', entityType, 'fields', field, 'type']);
      if (!type) {
        return [];
      }
      const partList = this.metadata.get(['entityDefs', entityType, 'fields', field, 'additionalAttributeList']) || [];
      if (partList.length === 0) {
        return [];
      }
      const isPrefix = (this.defs[type] || {}).naming === 'prefix';
      const list = [];
      partList.forEach(item => {
        if (isPrefix) {
          list.push(item + Espo.Utils.upperCaseFirst(field));
          return;
        }
        list.push(field + Espo.Utils.upperCaseFirst(item));
      });
      return list;
    }

    /**
     * Get a list of attributes by a given field type and field name.
     *
     * @param {string} fieldType A field type.
     * @param {string} fieldName A field name.
     * @returns {string[]}
     */
    getAttributeList(fieldType, fieldName) {
      return _.union(this.getActualAttributeList(fieldType, fieldName), this.getNotActualAttributeList(fieldType, fieldName));
    }

    /**
     * @typedef {Object} module:field-manager~FieldFilters
     *
     * @property {string} [type] Only of a specific field type.
     * @property {string[]} [typeList] Only of a specific field types.
     * @property {string[]} [ignoreTypeList] Ignore field types.
     * @property {boolean} [onlyAvailable] To exclude disabled, admin-only, internal, forbidden fields.
     * @property {'read'|'edit'} [acl] To exclude fields not accessible for a current user over
     *   a specified access level.
     */

    /**
     * Get a list of fields of a specific entity type.
     *
     * @param {string} entityType An entity type.
     * @param {module:field-manager~FieldFilters} [options] Filters.
     * @returns {string[]}
     */
    getEntityTypeFieldList(entityType, options) {
      /** @type {Record} */
      const fieldDefs = this.metadata.get(['entityDefs', entityType, 'fields']) || {};
      let list = Object.keys(fieldDefs);
      options = options || {};
      let typeList = options.typeList;
      if (!typeList && options.type) {
        typeList = [options.type];
      }
      if (typeList) {
        list = list.filter(item => {
          const type = this.metadata.get(['entityDefs', entityType, 'fields', item, 'type']);
          return typeList.includes(type);
        });
      }
      if (options.ignoreTypeList) {
        list = list.filter(field => {
          const type = (fieldDefs[field] || {}).type;
          return !options.ignoreTypeList.includes(type);
        });
      }
      if (options.onlyAvailable || options.acl) {
        list = list.filter(item => {
          return this.isEntityTypeFieldAvailable(entityType, item);
        });
      }
      if (options.acl) {
        const level = options.acl || 'read';
        const forbiddenEditFieldList = this.acl.getScopeForbiddenFieldList(entityType, level);
        list = list.filter(item => {
          return !forbiddenEditFieldList.includes(item);
        });
      }
      return list;
    }

    /**
     * @deprecated Since v5.7.
     * @todo Remove.
     */
    getScopeFieldList(entityType) {
      return this.getEntityTypeFieldList(entityType);
    }

    /**
     * Get a field parameter value.
     *
     * @param {string} entityType An entity type.
     * @param {string} field A field name.
     * @param {string} param A parameter name.
     * @returns {*}
     */
    getEntityTypeFieldParam(entityType, field, param) {
      return this.metadata.get(['entityDefs', entityType, 'fields', field, param]);
    }

    /**
     * Get a view name/path for a specific field type.
     *
     * @param {string} fieldType A field type.
     * @returns {string}
     */
    getViewName(fieldType) {
      if (fieldType in this.defs) {
        if ('view' in this.defs[fieldType]) {
          return this.defs[fieldType].view;
        }
      }
      return 'views/fields/' + Espo.Utils.camelCaseToHyphen(fieldType);
    }

    /**
     * @deprecated Use `getParamList`.
     * @todo Remove.
     */
    getParams(fieldType) {
      return this.getParamList(fieldType);
    }

    /**
     * @deprecated Use `getAttributeList`.
     * @todo Remove.
     */
    getAttributes(fieldType, fieldName) {
      return this.getAttributeList(fieldType, fieldName);
    }

    /**
     * @deprecated Use `getActualAttributeList`.
     * @todo Remove.
     */
    getActualAttributes(fieldType, fieldName) {
      return this.getActualAttributeList(fieldType, fieldName);
    }

    /**
     * @deprecated Use `getNotActualAttributeList`.
     */
    getNotActualAttributes(fieldType, fieldName) {
      return this.getNotActualAttributeList(fieldType, fieldName);
    }

    /**
     * Check whether a field is not disabled, not utility, not only-admin, not forbidden and not internal.
     *
     * @param {string} entityType An entity type.
     * @param {string} field A field name.
     * @returns {boolean}
     */
    isEntityTypeFieldAvailable(entityType, field) {
      /** @type {Record} */
      const defs = this.metadata.get(['entityDefs', entityType, 'fields', field]) || {};
      if (defs.disabled || defs.utility) {
        return false;
      }

      /** @type {Record} */
      const aclDefs = this.metadata.get(['entityAcl', entityType, 'fields', field]) || {};
      if (aclDefs.onlyAdmin || aclDefs.forbidden || aclDefs.internal) {
        return false;
      }
      return true;
    }

    /**
     * @deprecated Use `isEntityTypeFieldAvailable`.
     * @todo Remove.
     */
    isScopeFieldAvailable(entityType, field) {
      return this.isEntityTypeFieldAvailable(entityType, field);
    }
  }
  var _default = _exports.default = FieldManager;
});

define("email-helper", ["exports", "di", "language", "models/user", "date-time", "acl-manager"], function (_exports, _di, _language, _user, _dateTime, _aclManager) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _language = _interopRequireDefault(_language);
  _user = _interopRequireDefault(_user);
  _dateTime = _interopRequireDefault(_dateTime);
  _aclManager = _interopRequireDefault(_aclManager);
  let _init_language, _init_extra_language, _init_user, _init_extra_user, _init_dateTime, _init_extra_dateTime, _init_acl, _init_extra_acl;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 email-helper */
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * An email helper.
   */
  class EmailHelper {
    static #_ = [_init_language, _init_extra_language, _init_user, _init_extra_user, _init_dateTime, _init_extra_dateTime, _init_acl, _init_extra_acl] = _applyDecs(this, [], [[(0, _di.inject)(_language.default), 0, "language"], [(0, _di.inject)(_user.default), 0, "user"], [(0, _di.inject)(_dateTime.default), 0, "dateTime"], [(0, _di.inject)(_aclManager.default), 0, "acl"]]).e;
    /**
     * @private
     * @type {Language}
     */
    language = _init_language(this);

    /**
     * @private
     * @type {User}
     */
    user = (_init_extra_language(this), _init_user(this));

    /**
     * @private
     * @type {DateTime}
     */
    dateTime = (_init_extra_user(this), _init_dateTime(this));

    /**
     * @private
     * @type {AclManager}
     */
    acl = (_init_extra_dateTime(this), _init_acl(this));
    constructor() {
      _init_extra_acl(this);
      /** @private */
      this.erasedPlaceholder = 'ERASED:';
    }

    /**
     * Get reply email attributes.
     *
     * @param {module:model} model An email model.
     * @param {Object|null} [data=null] Action data. Unused.
     * @param {boolean} [cc=false] To include CC (reply-all).
     * @returns {Object.<string, *>}
     */
    getReplyAttributes(model, data, cc) {
      const attributes = {
        status: 'Draft',
        isHtml: model.attributes.isHtml
      };
      const subject = model.attributes.name || '';
      attributes['name'] = subject.toUpperCase().indexOf('RE:') !== 0 ? 'Re: ' + subject : subject;
      let to = '';
      let isReplyOnSent = false;
      const nameHash = model.attributes.nameHash || {};
      const replyToAddressString = model.attributes.replyTo || null;
      const replyToString = model.attributes.replyToString || null;
      const userEmailAddressList = this.user.attributes.emailAddressList || [];
      const idHash = model.attributes.idHash || {};
      const typeHash = model.attributes.typeHash || {};
      if (replyToAddressString) {
        const replyToAddressList = replyToAddressString.split(';');
        to = replyToAddressList.join(';');
      } else if (replyToString) {
        const a = [];
        replyToString.split(';').forEach(item => {
          const part = item.trim();
          const address = this.parseAddressFromStringAddress(item);
          if (address) {
            a.push(address);
            const name = this.parseNameFromStringAddress(part);
            if (name && name !== address) {
              nameHash[address] = name;
            }
          }
        });
        to = a.join(';');
      }
      if ((!to || !to.includes('@')) && model.attributes.from) {
        if (!userEmailAddressList.includes(model.attributes.from)) {
          to = model.attributes.from;
          if (!nameHash[to]) {
            const fromString = model.attributes.fromString || model.attributes.fromName;
            if (fromString) {
              const name = this.parseNameFromStringAddress(fromString);
              if (name !== to) {
                nameHash[to] = name;
              }
            }
          }
        } else {
          isReplyOnSent = true;
        }
      }
      attributes.to = to;
      if (cc) {
        attributes.cc = model.attributes.cc || '';

        /** @type {string[]} */
        const excludeFromReplyEmailAddressList = this.user.get('excludeFromReplyEmailAddressList') || [];
        (model.get('to') || '').split(';').forEach(item => {
          item = item.trim();
          if (item === this.user.get('emailAddress')) {
            return;
          }
          if (excludeFromReplyEmailAddressList.includes(item)) {
            return;
          }
          if (isReplyOnSent) {
            if (attributes.to) {
              attributes.to += ';';
            }
            attributes.to += item;
            return;
          }
          if (attributes.cc) {
            attributes.cc += ';';
          }
          attributes.cc += item;
        });
        attributes.cc = attributes.cc.replace(/^(; )/, "");
      }
      if (attributes.to) {
        let toList = attributes.to.split(';');
        toList = toList.filter(item => {
          if (item.indexOf(this.erasedPlaceholder) === 0) {
            return false;
          }
          return true;
        });
        attributes.to = toList.join(';');
      }

      /** @type {string[]} */
      const personalAddresses = this.user.get('userEmailAddressList') || [];
      const lcPersonalAddresses = personalAddresses.map(it => it.toLowerCase());
      if (attributes.cc) {
        const ccList = attributes.cc.split(';').filter(item => {
          if (lcPersonalAddresses.includes(item.toLowerCase())) {
            return false;
          }
          if (item.indexOf(this.erasedPlaceholder) === 0) {
            return false;
          }
          return true;
        });
        attributes.cc = ccList.join(';');
      }
      if (model.get('parentId')) {
        attributes['parentId'] = model.get('parentId');
        attributes['parentName'] = model.get('parentName');
        attributes['parentType'] = model.get('parentType');
      }
      if (model.get('teamsIds') && model.get('teamsIds').length) {
        attributes.teamsIds = Espo.Utils.clone(model.get('teamsIds'));
        attributes.teamsNames = Espo.Utils.clone(model.get('teamsNames') || {});
        const defaultTeamId = this.user.get('defaultTeamId');
        if (defaultTeamId && !~attributes.teamsIds.indexOf(defaultTeamId)) {
          attributes.teamsIds.push(this.user.get('defaultTeamId'));
          attributes.teamsNames[this.user.get('defaultTeamId')] = this.user.get('defaultTeamName');
        }
        attributes.teamsIds = attributes.teamsIds.filter(teamId => this.acl.checkTeamAssignmentPermission(teamId));
      }
      attributes.nameHash = nameHash;
      attributes.typeHash = typeHash;
      attributes.idHash = idHash;
      attributes.repliedId = model.id;
      attributes.inReplyTo = model.get('messageId');

      /** @type {string[]} */
      const lcToAddresses = (model.attributes.to || '').split(';').map(it => it.toLowerCase());
      for (const address of personalAddresses) {
        if (lcToAddresses.includes(address.toLowerCase())) {
          attributes.from = address;
          break;
        }
      }
      this.addReplyBodyAttributes(model, attributes);
      return attributes;
    }

    /**
     * Get forward email attributes.
     *
     * @param {module:model} model An email model.
     * @returns {Object}
     */
    getForwardAttributes(model) {
      const attributes = {
        status: 'Draft',
        isHtml: model.get('isHtml')
      };
      const subject = model.get('name');
      if (~!subject.toUpperCase().indexOf('FWD:') && ~!subject.toUpperCase().indexOf('FW:')) {
        attributes['name'] = 'Fwd: ' + subject;
      } else {
        attributes['name'] = subject;
      }
      if (model.get('parentId')) {
        attributes['parentId'] = model.get('parentId');
        attributes['parentName'] = model.get('parentName');
        attributes['parentType'] = model.get('parentType');
      }
      this.addForwardBodyAttributes(model, attributes);
      return attributes;
    }

    /**
     * Add body attributes for a forward email.
     *
     * @param {module:model} model An email model.
     * @param {Object} attributes
     */
    addForwardBodyAttributes(model, attributes) {
      let prepending = '';
      if (model.get('isHtml')) {
        prepending = '<br>' + '------' + this.language.translate('Forwarded message', 'labels', 'Email') + '------';
      } else {
        prepending = '\n\n' + '------' + this.language.translate('Forwarded message', 'labels', 'Email') + '------';
      }
      const list = [];
      if (model.get('from')) {
        const from = model.get('from');
        let line = this.language.translate('from', 'fields', 'Email') + ': ';
        const nameHash = model.get('nameHash') || {};
        if (from in nameHash) {
          line += nameHash[from] + ' ';
        }
        if (model.get('isHtml')) {
          line += '&lt;' + from + '&gt;';
        } else {
          line += '<' + from + '>';
        }
        list.push(line);
      }
      if (model.get('dateSent')) {
        let line = this.language.translate('dateSent', 'fields', 'Email') + ': ';
        line += this.dateTime.toDisplay(model.get('dateSent'));
        list.push(line);
      }
      if (model.get('name')) {
        let line = this.language.translate('subject', 'fields', 'Email') + ': ';
        line += model.get('name');
        list.push(line);
      }
      if (model.get('to')) {
        let line = this.language.translate('to', 'fields', 'Email') + ': ';
        const partList = [];
        model.get('to').split(';').forEach(to => {
          const nameHash = model.get('nameHash') || {};
          let line = '';
          if (to in nameHash) {
            line += nameHash[to] + ' ';
          }
          if (model.get('isHtml')) {
            line += '&lt;' + to + '&gt;';
          } else {
            line += '<' + to + '>';
          }
          partList.push(line);
        });
        line += partList.join(';');
        list.push(line);
      }
      list.forEach(line => {
        if (model.get('isHtml')) {
          prepending += '<br>' + line;
        } else {
          prepending += '\n' + line;
        }
      });
      if (model.get('isHtml')) {
        const body = model.get('body');
        attributes['body'] = prepending + '<br><br>' + body;
      } else {
        const bodyPlain = model.get('body') || model.get('bodyPlain') || '';
        attributes['bodyPlain'] = attributes['body'] = prepending + '\n\n' + bodyPlain;
      }
    }

    /**
     * Parse a name from a string address.
     *
     * @param {string} value A string address. E.g. `Test Name <address@domain>`.
     * @returns {string|null}
     */
    parseNameFromStringAddress(value) {
      if (!value.includes('<')) {
        return null;
      }
      let name = value.replace(/<(.*)>/, '').trim();
      if (name.charAt(0) === '"' && name.charAt(name.length - 1) === '"') {
        name = name.slice(1, name.length - 2);
      }
      return name;
    }

    /**
     * Parse an address from a string address.
     *
     * @param {string} value A string address. E.g. `Test Name <address@domain>`.
     * @returns {string|null}
     */
    parseAddressFromStringAddress(value) {
      const r = value.match(/<(.*)>/);
      let address;
      if (r && r.length > 1) {
        address = r[1];
      } else {
        address = value.trim();
      }
      return address;
    }

    /**
     * Add body attributes for a reply email.
     *
     * @param {module:model} model An email model.
     * @param {Object.<string, *>} attributes
     */
    addReplyBodyAttributes(model, attributes) {
      const format = this.dateTime.getReadableShortDateTimeFormat();
      const dateSent = model.get('dateSent');
      let dateSentString = null;
      if (dateSent) {
        const dateSentMoment = this.dateTime.toMoment(dateSent);
        dateSentString = dateSentMoment.format(format);
        if (dateSentMoment.year() !== this.dateTime.getNowMoment().year()) {
          dateSentString += ', ' + dateSentMoment.year();
        }
      }
      let replyHeadString = dateSentString || this.language.translate('Original message', 'labels', 'Email');
      let fromName = model.get('fromName');
      if (!fromName && model.get('from')) {
        fromName = (model.get('nameHash') || {})[model.get('from')];
        if (fromName) {
          replyHeadString += ', ' + fromName;
        }
      }
      replyHeadString += ':';
      if (model.get('isHtml')) {
        const body = model.get('body');
        attributes['body'] = `<p data-quote-start="true"><br></p>` + `<p>${replyHeadString}</p><blockquote>${body}</blockquote>`;
        return;
      }
      let bodyPlain = model.get('body') || model.get('bodyPlain') || '';
      let b = '\n\n';
      b += replyHeadString + '\n';
      bodyPlain.split('\n').forEach(line => {
        b += '> ' + line + '\n';
      });
      bodyPlain = b;
      attributes['body'] = bodyPlain;
      attributes['bodyPlain'] = bodyPlain;
    }
  }
  var _default = _exports.default = EmailHelper;
});

define("collection-factory", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 collection-factory */

  /**
   * A collection factory.
   */
  class CollectionFactory {
    /**
     * @param {module:model-factory} modelFactory
     * @param {module:models/settings} config
     * @param {module:metadata} metadata
     */
    constructor(modelFactory, config, metadata) {
      /** @private */
      this.modelFactory = modelFactory;
      /** @private */
      this.metadata = metadata;
      /** @private */
      this.recordListMaxSizeLimit = config.get('recordListMaxSizeLimit') || 200;
    }

    /**
     * Create a collection.
     *
     * @param {string} entityType An entity type.
     * @param {Function} [callback] Deprecated.
     * @param {Object} [context] Deprecated.
     * @returns {Promise<module:collection>}
     */
    create(entityType, callback, context) {
      return new Promise(resolve => {
        context = context || this;
        this.modelFactory.getSeed(entityType, Model => {
          const orderBy = this.modelFactory.metadata.get(['entityDefs', entityType, 'collection', 'orderBy']);
          const order = this.modelFactory.metadata.get(['entityDefs', entityType, 'collection', 'order']);
          const className = this.modelFactory.metadata.get(['clientDefs', entityType, 'collection']) || 'collection';
          const defs = this.metadata.get(['entityDefs', entityType]) || {};
          Espo.loader.require(className, Collection => {
            const collection = new Collection(null, {
              entityType: entityType,
              orderBy: orderBy,
              order: order,
              defs: defs
            });
            collection.model = Model;
            collection.entityType = entityType;
            collection.maxMaxSize = this.recordListMaxSizeLimit;
            if (callback) {
              callback.call(context, collection);
            }
            resolve(collection);
          });
        });
      });
    }
  }
  var _default = _exports.default = CollectionFactory;
});

define("cache", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 cache */

  /**
   * Cache for source and resource files.
   */
  class Cache {
    /**
     * @param {Number} [cacheTimestamp] A cache timestamp.
     */
    constructor(cacheTimestamp) {
      this.basePrefix = this.prefix;
      if (cacheTimestamp) {
        this.prefix = this.basePrefix + '-' + cacheTimestamp;
      }
      if (!this.get('app', 'timestamp')) {
        this.storeTimestamp();
      }
    }

    /** @private */
    prefix = 'cache';

    /**
     * Handle actuality. Clears cache if not actual.
     *
     * @param {Number} cacheTimestamp A cache timestamp.
     */
    handleActuality(cacheTimestamp) {
      const storedTimestamp = this.getCacheTimestamp();
      if (storedTimestamp) {
        if (storedTimestamp !== cacheTimestamp) {
          this.clear();
          this.set('app', 'cacheTimestamp', cacheTimestamp);
          this.storeTimestamp();
        }
        return;
      }
      this.clear();
      this.set('app', 'cacheTimestamp', cacheTimestamp);
      this.storeTimestamp();
    }

    /**
     * Get a cache timestamp.
     *
     * @returns {number}
     */
    getCacheTimestamp() {
      return parseInt(this.get('app', 'cacheTimestamp') || 0);
    }

    /**
     * @todo Revise whether is needed.
     */
    storeTimestamp() {
      const frontendCacheTimestamp = Date.now();
      this.set('app', 'timestamp', frontendCacheTimestamp);
    }

    /**
     * @private
     * @param {string} type
     * @returns {string}
     */
    composeFullPrefix(type) {
      return this.prefix + '-' + type;
    }

    /**
     * @private
     * @param {string} type
     * @param {string} name
     * @returns {string}
     */
    composeKey(type, name) {
      return this.composeFullPrefix(type) + '-' + name;
    }

    /**
     * @private
     * @param {string} type
     */
    checkType(type) {
      if (typeof type === 'undefined' && toString.call(type) !== '[object String]') {
        throw new TypeError("Bad type \"" + type + "\" passed to Cache().");
      }
    }

    /**
     * Get a stored value.
     *
     * @param {string} type A type/category.
     * @param {string} name A name.
     * @returns {string|null} Null if no stored value.
     */
    get(type, name) {
      this.checkType(type);
      const key = this.composeKey(type, name);
      let stored;
      try {
        stored = localStorage.getItem(key);
      } catch (error) {
        console.error(error);
        return null;
      }
      if (stored) {
        let result = stored;
        if (stored.length > 9 && stored.substring(0, 9) === '__JSON__:') {
          const jsonString = stored.slice(9);
          try {
            result = JSON.parse(jsonString);
          } catch (error) {
            result = stored;
          }
        }
        return result;
      }
      return null;
    }

    /**
     * Store a value.
     *
     * @param {string} type A type/category.
     * @param {string} name A name.
     * @param {any} value A value.
     */
    set(type, name, value) {
      this.checkType(type);
      const key = this.composeKey(type, name);
      if (value instanceof Object || Array.isArray(value)) {
        value = '__JSON__:' + JSON.stringify(value);
      }
      try {
        localStorage.setItem(key, value);
      } catch (error) {
        console.log('Local storage limit exceeded.');
      }
    }

    /**
     * Clear a stored value.
     *
     * @param {string} [type] A type/category.
     * @param {string} [name] A name.
     */
    clear(type, name) {
      let reText;
      if (typeof type !== 'undefined') {
        if (typeof name === 'undefined') {
          reText = '^' + this.composeFullPrefix(type);
        } else {
          reText = '^' + this.composeKey(type, name);
        }
      } else {
        reText = '^' + this.basePrefix + '-';
      }
      const re = new RegExp(reText);
      for (const i in localStorage) {
        if (re.test(i)) {
          delete localStorage[i];
        }
      }
    }
  }
  var _default = _exports.default = Cache;
});

define("broadcast-channel", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 broadcast-channel */

  class BroadcastChannel {
    constructor() {
      this.object = null;
      if (window.BroadcastChannel) {
        this.object = new window.BroadcastChannel('app');
      }
    }

    /**
     * Post a message.
     *
     * @param {string} message A message.
     */
    postMessage(message) {
      if (!this.object) {
        return;
      }
      this.object.postMessage(message);
    }

    /**
     * @callback module:broadcast-channel~callback
     *
     * @param {MessageEvent} event An event. A message can be obtained from the `data` property.
     */

    /**
     * Subscribe to a message.
     *
     * @param {module:broadcast-channel~callback} callback A callback.
     */
    subscribe(callback) {
      if (!this.object) {
        return;
      }
      this.object.addEventListener('message', callback);
    }
  }
  var _default = _exports.default = BroadcastChannel;
});

define("app-params", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * Application parameters.
   *
   * @since 9.0.0
   */
  class AppParams {
    /**
     * @param {Record} params
     */
    constructor() {
      let params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      /** @private */
      this.params = params;
    }

    /**
     * Get a parameter.
     *
     * @param {string} name A parameter.
     * @return {*}
     */
    get(name) {
      return this.params[name];
    }

    /**
     * Set all parameters.
     *
     * @internal
     * @param {Record} params
     */
    setAll(params) {
      this.params = params;
    }

    /**
     * Reload params from the backend.
     */
    async load() {
      /** @type {module:app~UserData} */
      const data = await Espo.Ajax.getRequest('App/user');
      this.params = data.appParams;
    }
  }
  _exports.default = AppParams;
});

define("ajax", ["exports", "jquery", "utils"], function (_exports, _jquery, _utils) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _jquery = _interopRequireDefault(_jquery);
  _utils = _interopRequireDefault(_utils);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 ajax */

  let isConfigured = false;
  /** @type {number} */
  let defaultTimeout;
  /** @type {string} */
  let apiUrl;
  /** @type {Espo.Ajax~Handler} */
  let beforeSend;
  /** @type {Espo.Ajax~Handler} */
  let onSuccess;
  /** @type {Espo.Ajax~Handler} */
  let onError;
  /** @type {Espo.Ajax~Handler} */
  let onTimeout;
  /** @type {function()} */
  let onOffline;

  /**
   * @callback Espo.Ajax~Handler
   * @param {XMLHttpRequest} [xhr]
   * @param {Object.<string, *>} [options]
   */

  /**
   * Options.
   *
   * @typedef {Object} Espo.Ajax~Options
   *
   * @property {Number} [timeout] A timeout.
   * @property {Object.<string, string>} [headers] A request headers.
   * @property {'json'|'text'} [dataType] A data type.
   * @property {string} [contentType] A content type.
   * @property {boolean} [resolveWithXhr] To resolve with `XMLHttpRequest`.
   */

  const baseUrl = _utils.default.obtainBaseUrl();

  // noinspection JSUnusedGlobalSymbols
  /**
   * Functions for API HTTP requests.
   */
  const Ajax = Espo.Ajax = {
    /**
     * Request.
     *
     * @param {string} url An URL.
     * @param {'GET'|'POST'|'PUT'|'DELETE'|'PATCH'|'OPTIONS'} method An HTTP method.
     * @param {*} [data] Data.
     * @param {Espo.Ajax~Options & Object.<string, *>} [options] Options.
     * @returns {AjaxPromise<any>}
     */
    request: function (url, method, data, options) {
      options = options || {};
      const timeout = 'timeout' in options ? options.timeout : defaultTimeout;
      const contentType = options.contentType || 'application/json';
      let body;
      if (options.data && !data) {
        data = options.data;
      }
      if (apiUrl) {
        url = Espo.Utils.trimSlash(apiUrl) + '/' + url;
      }
      if (!['GET', 'OPTIONS'].includes(method) && data) {
        body = data;
        if (contentType === 'application/json' && typeof data !== 'string') {
          body = JSON.stringify(data);
        }
      }
      if (method === 'GET' && data) {
        const part = _jquery.default.param(data);
        url.includes('?') ? url += '&' : url += '?';
        url += part;
      }
      const urlObj = new URL(baseUrl + url);
      const xhr = new Xhr();
      xhr.timeout = timeout;
      xhr.open(method, urlObj);
      xhr.setRequestHeader('Content-Type', contentType);
      if (options.headers) {
        for (const key in options.headers) {
          xhr.setRequestHeader(key, options.headers[key]);
        }
      }
      if (beforeSend) {
        beforeSend(xhr, options);
      }
      const promiseWrapper = {};
      const promise = new AjaxPromise((resolve, reject) => {
        const onErrorGeneral = isTimeout => {
          if (options.error) {
            options.error(xhr, options);
          }
          reject(xhr, options);
          if (isTimeout) {
            if (onTimeout) {
              onTimeout(xhr, options);
            }
            return;
          }
          if (xhr.status === 0 && !navigator.onLine && onOffline) {
            onOffline();
            return;
          }
          if (onError) {
            onError(xhr, options);
          }
        };
        xhr.ontimeout = () => onErrorGeneral(true);
        xhr.onerror = () => onErrorGeneral();
        xhr.onload = () => {
          if (xhr.status >= 400) {
            onErrorGeneral();
            return;
          }
          let response = xhr.responseText;
          if ((options.dataType || 'json') === 'json') {
            try {
              response = JSON.parse(xhr.responseText);
            } catch (e) {
              console.error('Could not parse API response.');
              onErrorGeneral();
            }
          }
          if (options.success) {
            options.success(response);
          }
          onSuccess(xhr, options);
          if (options.resolveWithXhr) {
            response = xhr;
          }
          resolve(response);
        };
        xhr.send(body);
        if (promiseWrapper.promise) {
          promiseWrapper.promise.xhr = xhr;
          return;
        }
        promiseWrapper.xhr = xhr;
      });
      promiseWrapper.promise = promise;
      promise.xhr = promise.xhr || promiseWrapper.xhr;
      return promise;
    },
    /**
     * POST request.
     *
     * @param {string} url An URL.
     * @param {*} [data] Data.
     * @param {Espo.Ajax~Options & Object.<string, *>} [options] Options.
     * @returns {Promise<any> & AjaxPromise}
     */
    postRequest: function (url, data, options) {
      if (data) {
        data = JSON.stringify(data);
      }
      return /** @type {Promise<any> & AjaxPromise} */Ajax.request(url, 'POST', data, options);
    },
    /**
     * PATCH request.
     *
     * @param {string} url An URL.
     * @param {*} [data] Data.
     * @param {Espo.Ajax~Options & Object.<string, *>} [options] Options.
     * @returns {Promise<any> & AjaxPromise}
     */
    patchRequest: function (url, data, options) {
      if (data) {
        data = JSON.stringify(data);
      }
      return /** @type {Promise<any> & AjaxPromise} */Ajax.request(url, 'PATCH', data, options);
    },
    /**
     * PUT request.
     *
     * @param {string} url An URL.
     * @param {*} [data] Data.
     * @param {Espo.Ajax~Options & Object.<string, *>} [options] Options.
     * @returns {Promise<any> & AjaxPromise}
     */
    putRequest: function (url, data, options) {
      if (data) {
        data = JSON.stringify(data);
      }
      return /** @type {Promise<any> & AjaxPromise} */Ajax.request(url, 'PUT', data, options);
    },
    /**
     * DELETE request.
     *
     * @param {string} url An URL.
     * @param {*} [data] Data.
     * @param {Espo.Ajax~Options & Object.<string, *>} [options] Options.
     * @returns {Promise<any> & AjaxPromise}
     */
    deleteRequest: function (url, data, options) {
      if (data) {
        data = JSON.stringify(data);
      }
      return /** @type {Promise<any> & AjaxPromise} */Ajax.request(url, 'DELETE', data, options);
    },
    /**
     * GET request.
     *
     * @param {string} url An URL.
     * @param {*} [data] Data.
     * @param {Espo.Ajax~Options & Object.<string, *>} [options] Options.
     * @returns {Promise<any> & AjaxPromise}
     */
    getRequest: function (url, data, options) {
      return /** @type {Promise<any> & AjaxPromise} */Ajax.request(url, 'GET', data, options);
    },
    /**
     * @internal
     * @param {{
     *     apiUrl: string,
     *     timeout: number,
     *     beforeSend: Espo.Ajax~Handler,
     *     onSuccess: Espo.Ajax~Handler,
     *     onError: Espo.Ajax~Handler,
     *     onTimeout: Espo.Ajax~Handler,
     *     onOffline?: function(),
     * }} options Options.
     */
    configure: function (options) {
      if (isConfigured) {
        throw new Error("Ajax is already configured.");
      }
      apiUrl = options.apiUrl;
      defaultTimeout = options.timeout;
      beforeSend = options.beforeSend;
      onSuccess = options.onSuccess;
      onError = options.onError;
      onTimeout = options.onTimeout;
      onOffline = options.onOffline;
      isConfigured = true;
    }
  };

  /**
   * @memberOf module:ajax
   */
  class AjaxPromise extends Promise {
    /**
     * @type {XMLHttpRequest|null}
     * @internal
     */
    xhr = null;
    isAborted = false;

    /**
     * Abort the request.
     */
    abort() {
      this.isAborted = true;
      if (this.xhr) {
        this.xhr.abort();
      }
    }

    /**
     * Get a ready state.
     *
     * @return {Number}
     */
    getReadyState() {
      if (!this.xhr) {
        return 0;
      }
      return this.xhr.readyState || 0;
    }

    /**
     * Get a status code
     *
     * @return {Number}
     */
    getStatus() {
      if (!this.xhr) {
        return 0;
      }
      return this.xhr.status;
    }
  }

  /**
   * @name module:ajax.Xhr
   */
  class Xhr extends XMLHttpRequest {
    /**
     * To be set in an error handler to bypass default handling.
     */
    errorIsHandled = false;
  }
  var _default = _exports.default = Ajax;
});

define("acl-portal", ["exports", "acl", "di", "metadata"], function (_exports, _acl, _di, _metadata) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _acl = _interopRequireDefault(_acl);
  _metadata = _interopRequireDefault(_metadata);
  let _init_metadata, _init_extra_metadata;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 acl-portal */
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * Internal class for portal access checking. Can be extended to customize access checking
   * for a specific scope.
   */
  class AclPortal extends _acl.default {
    static #_ = [_init_metadata, _init_extra_metadata] = _applyDecs(this, [], [[(0, _di.inject)(_metadata.default), 0, "metadata"]], 0, void 0, _acl.default).e;
    constructor() {
      super(...arguments);
      _init_extra_metadata(this);
    }
    /**
     * @private
     * @type {Metadata}
     */
    metadata = _init_metadata(this);

    /** @inheritDoc */
    checkScope(data, action, precise, entityAccessData) {
      entityAccessData = entityAccessData || {};
      const inAccount = entityAccessData.inAccount;
      const isOwnContact = entityAccessData.isOwnContact;
      const isOwner = entityAccessData.isOwner;
      if (this.getUser().isAdmin()) {
        return true;
      }
      if (data === false) {
        return false;
      }
      if (data === true) {
        return true;
      }
      if (typeof data === 'string') {
        return true;
      }
      if (data === null) {
        return false;
      }
      action = action || null;
      if (action === null) {
        return true;
      }
      if (!(action in data)) {
        return false;
      }
      const value = data[action];
      if (value === 'all') {
        return true;
      }
      if (value === 'yes') {
        return true;
      }
      if (value === 'no') {
        return false;
      }
      if (typeof isOwner === 'undefined') {
        return true;
      }
      if (isOwner) {
        if (value === 'own' || value === 'account' || value === 'contact') {
          return true;
        }
      }
      let result = false;
      if (value === 'account') {
        result = inAccount;
        if (inAccount === null) {
          if (precise) {
            result = null;
          } else {
            return true;
          }
        } else if (inAccount) {
          return true;
        }
      }
      if (value === 'contact') {
        result = isOwnContact;
        if (isOwnContact === null) {
          if (precise) {
            result = null;
          } else {
            return true;
          }
        } else if (isOwnContact) {
          return true;
        }
      }
      if (isOwner === null) {
        if (precise) {
          result = null;
        } else {
          return true;
        }
      }
      return result;
    }

    /** @inheritDoc */
    checkModel(model, data, action, precise) {
      if (this.getUser().isAdmin()) {
        return true;
      }
      const entityAccessData = {
        isOwner: this.checkIsOwner(model),
        inAccount: this.checkInAccount(model),
        isOwnContact: this.checkIsOwnContact(model)
      };
      return this.checkScope(data, action, precise, entityAccessData);
    }

    /** @inheritDoc */
    checkIsOwner(model) {
      if (model.hasField('createdBy') && this.getUser().id === model.get('createdById')) {
        return true;
      }
      return false;
    }

    /**
     * Check if a user in an account of a model.
     *
     * @param {import('model').default} model A model.
     * @returns {boolean|null} True if in an account, null if not clear.
     */
    checkInAccount(model) {
      const accountsIds = this.getUser().getLinkMultipleIdList('accounts');
      if (!accountsIds.length) {
        return false;
      }
      const link = this.metadata.get(`aclDefs.${model.entityType}.accountLink`);
      if (link) {
        const linkType = model.getLinkType(link);
        if (linkType === 'belongsTo' || linkType === 'hasOne') {
          const idAttribute = link + 'Id';
          if (!model.has(idAttribute)) {
            return null;
          }
          const id = model.get(idAttribute);
          if (!id) {
            return false;
          }
          return accountsIds.includes(id);
        }
        if (linkType === 'belongsToParent') {
          const idAttribute = link + 'Id';
          const typeAttribute = link + 'Type';
          if (!model.has(idAttribute) || !model.has(typeAttribute)) {
            return null;
          }
          const id = model.get(idAttribute);
          if (model.get(typeAttribute) !== 'Account' || !id) {
            return false;
          }
          return accountsIds.includes(id);
        }
        if (linkType === 'hasMany') {
          if (!model.hasField(link) || model.getFieldType(link) !== 'linkMultiple') {
            return true;
          }
          if (!model.has(link + 'Ids')) {
            return null;
          }
          const ids = model.getLinkMultipleIdList(link);
          for (const id of ids) {
            if (accountsIds.includes(id)) {
              return true;
            }
          }
          return false;
        }
        return false;
      }
      if (model.hasField('account') && model.get('accountId') && accountsIds.includes(model.get('accountId'))) {
        return true;
      }
      let result = false;
      if (model.hasField('accounts') && model.hasLink('accounts')) {
        if (!model.has('accountsIds')) {
          result = null;
        }
        model.getLinkMultipleIdList('accounts').forEach(id => {
          if (accountsIds.includes(id)) {
            result = true;
          }
        });
      }
      if (model.hasField('parent') && model.hasLink('parent') && model.get('parentType') === 'Account' && accountsIds.includes(model.get('parentId'))) {
        return true;
      }
      if (result === false) {
        if (!model.hasField('accounts') && model.hasLink('accounts')) {
          return true;
        }
      }
      return result;
    }

    /**
     * Check if a user is a contact-owner to a model.
     *
     * @param {module:model} model A model.
     * @returns {boolean|null} True if in a contact-owner, null if not clear.
     */
    checkIsOwnContact(model) {
      const contactId = this.getUser().get('contactId');
      if (!contactId) {
        return false;
      }
      const link = this.metadata.get(`aclDefs.${model.entityType}.contactLink`);
      if (link) {
        const linkType = model.getLinkType(link);
        if (linkType === 'belongsTo' || linkType === 'hasOne') {
          const idAttribute = link + 'Id';
          if (!model.has(idAttribute)) {
            return null;
          }
          return model.get(idAttribute) === contactId;
        }
        if (linkType === 'belongsToParent') {
          const idAttribute = link + 'Id';
          const typeAttribute = link + 'Type';
          if (!model.has(idAttribute) || !model.has(typeAttribute)) {
            return null;
          }
          if (model.get(typeAttribute) !== 'Contact') {
            return false;
          }
          return model.get(idAttribute) === contactId;
        }
        if (linkType === 'hasMany') {
          if (!model.hasField(link) || model.getFieldType(link) !== 'linkMultiple') {
            return true;
          }
          if (!model.has(link + 'Ids')) {
            return null;
          }
          const ids = model.getLinkMultipleIdList(link);
          return ids.includes(contactId);
        }
        return false;
      }
      if (model.hasField('contact')) {
        if (model.get('contactId')) {
          if (contactId === model.get('contactId')) {
            return true;
          }
        }
      }
      let result = false;
      if (model.hasField('contacts') && model.hasLink('contacts')) {
        if (!model.has('contactsIds')) {
          result = null;
        }
        model.getLinkMultipleIdList('contacts').forEach(id => {
          if (contactId === id) {
            result = true;
          }
        });
      }
      if (model.hasField('parent') && model.hasLink('parent')) {
        if (model.get('parentType') === 'Contact' && model.get('parentId') === contactId) {
          return true;
        }
      }
      if (result === false) {
        if (!model.hasField('contacts') && model.hasLink('contacts')) {
          return true;
        }
      }
      return result;
    }
  }
  var _default = _exports.default = AclPortal;
});

define("views/list", ["exports", "views/main", "search-manager", "helpers/record-modal"], function (_exports, _main, _searchManager, _recordModal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _main = _interopRequireDefault(_main);
  _searchManager = _interopRequireDefault(_searchManager);
  _recordModal = _interopRequireDefault(_recordModal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 module:views/list */

  /**
   * A list view.
   */
  class ListView extends _main.default {
    /** @inheritDoc */
    template = 'list';

    /** @inheritDoc */
    name = 'List';

    /** @inheritDoc */
    optionsToPass = [];

    /**
     * A header view name.
     *
     * @type {string}
     * @protected
     */
    headerView = 'views/header';

    /**
     * A search view name.
     *
     * @type {string}
     * @protected
     */
    searchView = 'views/record/search';

    /**
     * A record/list view name.
     *
     * @type {string}
     * @protected
     */
    recordView = 'views/record/list';

    /**
     * A record/kanban view name.
     *
     * @type {string}
     * @protected
     */
    recordKanbanView = 'views/record/kanban';

    /**
     * Has a search panel.
     *
     * @type {boolean}
     * @protected
     */
    searchPanel = true;

    /**
     * @type {module:search-manager}
     * @protected
     */
    searchManager = null;

    /**
     * Has a create button.
     *
     * @type {boolean}
     * @protected
     */
    createButton = true;

    /**
     * To use a modal dialog when creating a record.
     *
     * @type {boolean}
     * @protected
     */
    quickCreate = false;

    /**
     * After create a view will be stored, so it can be re-used after.
     * Useful to avoid re-rendering when come back the list view.
     *
     * @type {boolean}
     */
    storeViewAfterCreate = false;

    /**
     * After update a view will be stored, so it can be re-used after.
     * Useful to avoid re-rendering when come back the list view.
     *
     * @type {boolean}
     */
    storeViewAfterUpdate = true;

    /**
     * Use a current URL as a root URL when open a record. To be able to return to the same URL.
     */
    keepCurrentRootUrl = false;

    /**
     * A view mode. 'list', 'kanban'.
     *
     * @type {string}
     */
    viewMode = '';

    /**
     * An available view mode list.
     *
     * @type {string[]|null}
     */
    viewModeList = null;

    /**
     * A default view mode.
     *
     * @type {string}
     */
    defaultViewMode = 'list';

    /** @const */
    MODE_LIST = 'list';
    /** @const */
    MODE_KANBAN = 'kanban';

    /**
     * Root data. To be passed to the detail record view when following to a record.
     *
     * @protected
     * @type {Object.<string, *>}
     * @since 9.0.0
     */
    rootData;

    /** @inheritDoc */
    shortcutKeys = {
      /** @this ListView */
      'Control+Space': function (e) {
        this.handleShortcutKeyCtrlSpace(e);
      },
      /** @this ListView */
      'Control+Slash': function (e) {
        this.handleShortcutKeyCtrlSlash(e);
      },
      /** @this ListView */
      'Control+Comma': function (e) {
        this.handleShortcutKeyCtrlComma(e);
      },
      /** @this ListView */
      'Control+Period': function (e) {
        this.handleShortcutKeyCtrlPeriod(e);
      },
      /** @this ListView */
      'Control+ArrowLeft': function (e) {
        this.handleShortcutKeyControlArrowLeft(e);
      },
      /** @this ListView */
      'Control+ArrowRight': function (e) {
        this.handleShortcutKeyControlArrowRight(e);
      }
    };

    /** @inheritDoc */
    setup() {
      this.collection.maxSize = this.getConfig().get('recordsPerPage') || this.collection.maxSize;
      this.collectionUrl = this.collection.url;
      this.collectionMaxSize = this.collection.maxSize;
      this.rootData = {};

      /**
       * @type {string}
       * @protected
       */
      this._primaryFilter = this.options.params.primaryFilter;
      this._fromAdmin = this.options.params.fromAdmin;
      this.setupModes();
      this.setViewMode(this.viewMode);
      if (this.getMetadata().get(['clientDefs', this.scope, 'searchPanelDisabled'])) {
        this.searchPanel = false;
      }
      if (this.getUser().isPortal()) {
        if (this.getMetadata().get(['clientDefs', this.scope, 'searchPanelInPortalDisabled'])) {
          this.searchPanel = false;
        }
      }
      if (this.getMetadata().get(['clientDefs', this.scope, 'createDisabled'])) {
        this.createButton = false;
      }
      this.entityType = this.collection.entityType;
      this.headerView = this.options.headerView || this.headerView;
      this.recordView = this.options.recordView || this.recordView;
      this.searchView = this.options.searchView || this.searchView;
      this.setupHeader();
      this.defaultOrderBy = this.defaultOrderBy || this.collection.orderBy;
      this.defaultOrder = this.defaultOrder || this.collection.order;
      this.collection.setOrder(this.defaultOrderBy, this.defaultOrder, true);
      if (this.searchPanel) {
        this.setupSearchManager();
      }
      this.setupSorting();
      if (this.searchPanel) {
        this.setupSearchPanel();
      }
      if (this.createButton) {
        this.setupCreateButton();
      }
      if (this._fromAdmin || this._primaryFilter) {
        this.keepCurrentRootUrl = true;
      }
      this.addActionHandler('fullRefresh', () => this.actionFullRefresh());
    }
    setupFinal() {
      super.setupFinal();
      this.wait(this.getHelper().processSetupHandlers(this, 'list'));
    }

    /**
     * Set up modes.
     */
    setupModes() {
      this.defaultViewMode = this.options.defaultViewMode || this.getMetadata().get(['clientDefs', this.scope, 'listDefaultViewMode']) || this.defaultViewMode;
      this.viewMode = this.viewMode || this.defaultViewMode;
      const viewModeList = this.options.viewModeList || this.viewModeList || this.getMetadata().get(['clientDefs', this.scope, 'listViewModeList']);
      if (viewModeList) {
        this.viewModeList = viewModeList;
      } else {
        this.viewModeList = [this.MODE_LIST];
        if (this.getMetadata().get(['clientDefs', this.scope, 'kanbanViewMode'])) {
          if (!~this.viewModeList.indexOf(this.MODE_KANBAN)) {
            this.viewModeList.push(this.MODE_KANBAN);
          }
        }
      }
      if (this.viewModeList.length > 1) {
        let viewMode = null;
        const modeKey = 'listViewMode' + this.scope;
        if (this.getStorage().has('state', modeKey)) {
          const storedViewMode = this.getStorage().get('state', modeKey);
          if (storedViewMode && this.viewModeList.includes(storedViewMode)) {
            viewMode = storedViewMode;
          }
        }
        if (!viewMode) {
          viewMode = this.defaultViewMode;
        }
        this.viewMode = /** @type {string} */viewMode;
      }
    }

    /**
     * Set up a header.
     */
    setupHeader() {
      this.createView('header', this.headerView, {
        collection: this.collection,
        fullSelector: '#main > .page-header',
        scope: this.scope,
        isXsSingleRow: true
      });
    }

    /**
     * Set up a create button.
     */
    setupCreateButton() {
      if (this.quickCreate) {
        this.menu.buttons.unshift({
          action: 'quickCreate',
          iconHtml: '<span class="fas fa-plus fa-sm"></span>',
          text: this.translate('Create ' + this.scope, 'labels', this.scope),
          style: 'default',
          acl: 'create',
          aclScope: this.entityType || this.scope,
          title: 'Ctrl+Space'
        });
        return;
      }
      this.menu.buttons.unshift({
        link: '#' + this.scope + '/create',
        action: 'create',
        iconHtml: '<span class="fas fa-plus fa-sm"></span>',
        text: this.translate('Create ' + this.scope, 'labels', this.scope),
        style: 'default',
        acl: 'create',
        aclScope: this.entityType || this.scope,
        title: 'Ctrl+Space'
      });
    }

    /**
     * Set up a search panel.
     *
     * @protected
     */
    setupSearchPanel() {
      this.createSearchView();
    }

    /**
     * Create a search view.
     *
     * @return {Promise<module:view>}
     * @protected
     */
    createSearchView() {
      // noinspection JSValidateTypes
      return this.createView('search', this.searchView, {
        collection: this.collection,
        fullSelector: '#main > .search-container',
        searchManager: this.searchManager,
        scope: this.scope,
        viewMode: this.viewMode,
        viewModeList: this.viewModeList,
        isWide: true,
        disableSavePreset: !!this._primaryFilter,
        primaryFiltersDisabled: !!this._primaryFilter
      }, view => {
        this.listenTo(view, 'reset', () => this.resetSorting());
        if (this.viewModeList.length > 1) {
          this.listenTo(view, 'change-view-mode', mode => this.switchViewMode(mode));
        }
      });
    }

    /**
     * Switch a view mode.
     *
     * @param {string} mode
     */
    switchViewMode(mode) {
      this.clearView('list');
      this.collection.isFetched = false;
      this.collection.reset();
      this.applyStoredSorting();
      this.setViewMode(mode, true);
      this.loadList();
    }

    /**
     * Set a view mode.
     *
     * @param {string} mode A mode.
     * @param {boolean} [toStore=false] To preserve a mode being set.
     */
    setViewMode(mode, toStore) {
      this.viewMode = mode;
      this.collection.url = this.collectionUrl;
      this.collection.maxSize = this.collectionMaxSize;
      if (toStore) {
        const modeKey = 'listViewMode' + this.scope;
        this.getStorage().set('state', modeKey, mode);
      }
      if (this.searchView && this.getView('search')) {
        this.getSearchView().setViewMode(mode);
      }
      if (this.viewMode === this.MODE_KANBAN) {
        this.setViewModeKanban();
        return;
      }
      const methodName = 'setViewMode' + Espo.Utils.upperCaseFirst(this.viewMode);
      if (this[methodName]) {
        this[methodName]();
      }
    }

    /**
     * Called when the kanban mode is set.
     */
    setViewModeKanban() {
      this.collection.url = 'Kanban/' + this.scope;
      this.collection.maxSize = this.getConfig().get('recordsPerPageKanban');
      this.collection.resetOrderToDefault();
    }

    /**
     * Reset sorting in a storage.
     */
    resetSorting() {
      this.getStorage().clear('listSorting', this.collection.entityType);
    }

    /**
     * Get default search data.
     *
     * @returns {Object}
     */
    getSearchDefaultData() {
      return this.getMetadata().get(`clientDefs.${this.scope}.defaultFilterData`);
    }

    /**
     * Set up a search manager.
     */
    setupSearchManager() {
      const collection = this.collection;
      let key = 'list';
      if (this._primaryFilter) {
        key += 'Filter' + Espo.Utils.upperCaseFirst(this._primaryFilter);
      }
      const searchManager = new _searchManager.default(collection, {
        storageKey: key,
        defaultData: this.getSearchDefaultData()
      });
      searchManager.scope = this.scope;
      searchManager.loadStored();
      if (this._primaryFilter) {
        searchManager.clearPreset();
        searchManager.setPrimary(this._primaryFilter);
      }
      collection.where = searchManager.getWhere();
      this.searchManager = searchManager;
    }

    /**
     * Set up sorting.
     */
    setupSorting() {
      if (!this.searchPanel) {
        return;
      }
      this.applyStoredSorting();
    }

    /**
     * Apply stored sorting.
     */
    applyStoredSorting() {
      const sortingParams = this.getStorage().get('listSorting', this.collection.entityType) || {};
      if ('orderBy' in sortingParams) {
        this.collection.orderBy = sortingParams.orderBy;
      }
      if ('order' in sortingParams) {
        this.collection.order = sortingParams.order;
      }
    }

    /**
     * @protected
     * @return {module:views/record/search}
     */
    getSearchView() {
      return this.getView('search');
    }

    /**
     * @protected
     * @return {module:view}
     */
    getRecordView() {
      return this.getView('list');
    }

    /**
     * Get a record view name.
     *
     * @returns {string}
     */
    getRecordViewName() {
      let viewName = this.getMetadata().get(['clientDefs', this.scope, 'recordViews', this.viewMode]);
      if (viewName) {
        return viewName;
      }
      if (this.viewMode === this.MODE_LIST) {
        return this.recordView;
      }
      if (this.viewMode === this.MODE_KANBAN) {
        return this.recordKanbanView;
      }
      const propertyName = 'record' + Espo.Utils.upperCaseFirst(this.viewMode) + 'View';
      viewName = this[propertyName];
      if (!viewName) {
        throw new Error("No record view.");
      }
      return viewName;
    }

    /** @inheritDoc */
    cancelRender() {
      if (this.hasView('list')) {
        this.getRecordView();
        if (this.getRecordView().isBeingRendered()) {
          this.getRecordView().cancelRender();
        }
      }
      super.cancelRender();
    }

    /**
     * @inheritDoc
     */
    afterRender() {
      Espo.Ui.notify(false);
      if (!this.hasView('list')) {
        this.loadList();
      }

      // noinspection JSUnresolvedReference
      this.$el.get(0).focus({
        preventScroll: true
      });
    }

    /**
     * Load a record list view.
     */
    loadList() {
      if ('isFetched' in this.collection && this.collection.isFetched) {
        this.createListRecordView(false);
        return;
      }
      Espo.Ui.notifyWait();
      this.createListRecordView(true);
    }

    /**
     * Prepare record view options. Options can be modified in an extended method.
     *
     * @protected
     * @param {Object} options Options
     */
    prepareRecordViewOptions(options) {}

    /**
     * Create a record list view.
     *
     * @param {boolean} [fetch=false] To fetch after creation.
     * @return {Promise<module:views/record/list>}
     */
    createListRecordView(fetch) {
      /** @type {module:views/record/list~options | Bull.View~Options} */
      const o = {
        collection: this.collection,
        selector: '.list-container',
        scope: this.scope,
        skipBuildRows: true,
        shortcutKeysEnabled: true,
        forceDisplayTopBar: true,
        additionalRowActionList: this.getMetadata().get(`clientDefs.${this.scope}.rowActionList`),
        settingsEnabled: true
      };
      if (this.getHelper().isXsScreen()) {
        o.type = 'listSmall';
      }
      this.optionsToPass.forEach(option => {
        o[option] = this.options[option];
      });
      if (this.keepCurrentRootUrl) {
        o.keepCurrentRootUrl = true;
      }
      if (this.getConfig().get('listPagination') || this.getMetadata().get(['clientDefs', this.scope, 'listPagination'])) {
        o.pagination = true;
      }
      o.rootData = this.rootData;
      this.prepareRecordViewOptions(o);
      const listViewName = this.getRecordViewName();

      // noinspection JSValidateTypes
      return this.createView('list', listViewName, o, /** import('views/record/list').default */view => {
        if (!this.hasParentView()) {
          view.undelegateEvents();
          return;
        }
        this.listenTo(view, 'after:paginate', () => window.scrollTo({
          top: 0
        }));
        this.listenTo(view, 'sort', () => window.scrollTo({
          top: 0
        }));
        this.listenToOnce(view, 'after:render', () => {
          if (!this.hasParentView()) {
            view.undelegateEvents();
            this.clearView('list');
          }
        });
        if (!fetch) {
          Espo.Ui.notify(false);
        }
        if (this.searchPanel) {
          this.listenTo(view, 'sort', obj => {
            this.getStorage().set('listSorting', this.collection.entityType, obj);
          });
        }
        if (!fetch) {
          view.render();
          return;
        }
        view.getSelectAttributeList(selectAttributeList => {
          if (this.options.mediator && this.options.mediator.abort) {
            return;
          }
          if (selectAttributeList) {
            this.collection.data.select = selectAttributeList.join(',');
          }
          Espo.Ui.notifyWait();
          this.collection.fetch({
            main: true
          }).then(() => Espo.Ui.notify(false));
        });
      });
    }

    /**
     * @inheritDoc
     */
    getHeader() {
      if (this._fromAdmin) {
        const root = document.createElement('a');
        root.href = '#Admin';
        root.textContent = this.translate('Administration', 'labels', 'Admin');
        root.style.userSelect = 'none';
        const scope = document.createElement('span');
        scope.textContent = this.getLanguage().translate(this.scope, 'scopeNamesPlural');
        scope.dataset.action = 'fullRefresh';
        scope.style.cursor = 'pointer';
        scope.style.userSelect = 'none';
        return this.buildHeaderHtml([root, scope]);
      }
      const root = document.createElement('span');
      root.textContent = this.getLanguage().translate(this.scope, 'scopeNamesPlural');
      root.title = this.translate('clickToRefresh', 'messages');
      root.dataset.action = 'fullRefresh';
      root.style.cursor = 'pointer';
      root.style.userSelect = 'none';
      const iconHtml = this.getHeaderIconHtml();
      if (iconHtml) {
        root.insertAdjacentHTML('afterbegin', iconHtml);
      }
      if (this._primaryFilter) {
        const label = this.translate(this._primaryFilter, 'presetFilters', this.entityType);
        root.insertAdjacentHTML('beforeend', ' · ' + label);
      }
      return this.buildHeaderHtml([root]);
    }

    /**
     * @inheritDoc
     */
    updatePageTitle() {
      this.setPageTitle(this.getLanguage().translate(this.scope, 'scopeNamesPlural'));
    }

    /**
     * Create attributes for an entity being created.
     *
     * @return {Object}
     */
    getCreateAttributes() {}

    /**
     * Prepare return dispatch parameters to pass to a view when creating a record.
     * To pass some data to restore when returning to the list view.
     *
     * Example:
     * ```
     * params.options.categoryId = this.currentCategoryId;
     * params.options.categoryName = this.currentCategoryName;
     * ```
     *
     * @param {Object} params Parameters to be modified.
     */
    prepareCreateReturnDispatchParams(params) {}

    /**
     * Action `quickCreate`.
     *
     * @param {Object.<string,*>} [data]
     * @returns {Promise<module:views/modals/edit>}
     */
    actionQuickCreate(data) {
      data = data || {};
      const attributes = this.getCreateAttributes() || {};
      const returnDispatchParams = {
        controller: this.scope,
        action: null,
        options: {
          isReturn: true
        }
      };
      this.prepareCreateReturnDispatchParams(returnDispatchParams);
      const helper = new _recordModal.default();
      return helper.showCreate(this, {
        entityType: this.scope,
        attributes: attributes,
        rootUrl: this.keepCurrentRootUrl ? this.getRouter().getCurrentUrl() : undefined,
        focusForCreate: data.focusForCreate,
        returnUrl: this.getRouter().getCurrentUrl(),
        returnDispatchParams: returnDispatchParams,
        afterSave: () => {
          this.collection.fetch();
        }
      });
    }

    /**
     * Action 'create'.
     *
     * @param {Object.<string,*>} [data]
     */
    actionCreate(data) {
      data = data || {};
      const router = this.getRouter();
      const url = '#' + this.scope + '/create';
      const attributes = this.getCreateAttributes() || {};
      let options = {
        attributes: attributes
      };
      if (this.keepCurrentRootUrl) {
        options.rootUrl = this.getRouter().getCurrentUrl();
      }
      if (data.focusForCreate) {
        options.focusForCreate = true;
      }
      const returnDispatchParams = {
        controller: this.scope,
        action: null,
        options: {
          isReturn: true
        }
      };
      this.prepareCreateReturnDispatchParams(returnDispatchParams);
      options = {
        ...options,
        returnUrl: this.getRouter().getCurrentUrl(),
        returnDispatchParams: returnDispatchParams
      };
      router.navigate(url, {
        trigger: false
      });
      router.dispatch(this.scope, 'create', options);
    }

    /**
     * Whether the view is actual to be reused.
     *
     * @returns {boolean}
     */
    isActualForReuse() {
      return 'isFetched' in this.collection && this.collection.isFetched;
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlSpace(e) {
      if (!this.createButton) {
        return;
      }

      /*if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT') {
          return;
      }*/

      if (!this.getAcl().checkScope(this.scope, 'create')) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      if (this.quickCreate) {
        this.actionQuickCreate({
          focusForCreate: true
        });
        return;
      }
      this.actionCreate({
        focusForCreate: true
      });
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlSlash(e) {
      if (!this.searchPanel) {
        return;
      }
      const $search = this.$el.find('input.text-filter').first();
      if (!$search.length) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      $search.focus();
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlComma(e) {
      if (!this.getSearchView()) {
        return;
      }
      this.getSearchView().selectPreviousPreset();
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlPeriod(e) {
      if (!this.getSearchView()) {
        return;
      }
      this.getSearchView().selectNextPreset();
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyControlArrowLeft(e) {
      this.getRecordView().trigger('request-page', 'previous');
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyControlArrowRight(e) {
      this.getRecordView().trigger('request-page', 'next');
    }

    /**
     * @protected
     */
    async actionFullRefresh() {
      Espo.Ui.notifyWait();
      await this.collection.fetch();
      Espo.Ui.notify();
    }
  }
  var _default = _exports.default = ListView;
});

define("views/detail", ["exports", "views/main", "views/detail/modes"], function (_exports, _main, _modes) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _main = _interopRequireDefault(_main);
  _modes = _interopRequireDefault(_modes);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 module:views/detail */

  /**
   * A detail view.
   */
  class DetailView extends _main.default {
    /** @inheritDoc */
    template = 'detail';
    /** @inheritDoc */
    name = 'Detail';

    /** @inheritDoc */
    optionsToPass = ['attributes', 'returnUrl', 'returnDispatchParams', 'rootUrl'];

    /**
     * A header view name.
     *
     * @type {string}
     */
    headerView = 'views/header';

    /**
     * A record view name.
     *
     * @type {string}
     */
    recordView = 'views/record/detail';

    /**
     * A root breadcrumb item not to be a link.
     *
     * @type {boolean}
     */
    rootLinkDisabled = false;

    /**
     * A root URL.
     *
     * @type {string}
     */
    rootUrl = '';

    /**
     * Is return.
     *
     * @protected
     */
    isReturn = false;

    /** @inheritDoc */
    shortcutKeys = {};

    /**
     * An entity type.
     *
     * @type {string}
     */
    entityType;

    /**
     * A default view mode.
     *
     * @protected
     */
    defaultViewMode = 'detail';

    /**
     * A view mode.
     *
     * @protected
     * @type {string}
     */
    viewMode;

    /**
     * @private
     * @type {string}
     */
    viewModeIsStorable;

    /**
     * @private
     * @type {boolean}
     */
    hasMultipleModes = false;

    /**
     * @private
     * @type {import('views/detail/modes').default}
     */
    modesView;

    /**
     * A 'detail' view mode.
     * @const
     */
    MODE_DETAIL = 'detail';

    /**
     * @private
     * @type {string}
     */
    nameAttribute;

    /** @inheritDoc */
    setup() {
      super.setup();
      this.entityType = this.model.entityType || this.model.name;
      this.headerView = this.options.headerView || this.headerView;
      this.recordView = this.options.recordView || this.recordView;
      this.rootUrl = this.options.rootUrl || this.options.params.rootUrl || '#' + this.scope;
      this.isReturn = this.options.isReturn || this.options.params.isReturn || false;
      this.nameAttribute = this.getMetadata().get(`clientDefs.${this.entityType}.nameAttribute`) || 'name';
      this.setupModes();
      this.setupHeader();
      this.setupRecord();
      this.setupPageTitle();
      this.initFollowButtons();
      this.initStarButtons();
      this.initRedirect();
      this.addActionHandler('fullRefresh', () => this.actionFullRefresh());
    }

    /** @inheritDoc */
    setupFinal() {
      super.setupFinal();
      this.wait(this.getHelper().processSetupHandlers(this, 'detail'));
    }

    /** @private */
    initRedirect() {
      if (!this.options.params.isAfterCreate) {
        return;
      }
      const redirect = () => {
        Espo.Ui.success(this.translate('Created'));
        setTimeout(() => {
          this.getRouter().navigate(this.rootUrl, {
            trigger: true
          });
        }, 1000);
      };
      if (this.model.lastSyncPromise && this.model.lastSyncPromise.getStatus() === 403) {
        redirect();
        return;
      }
      this.listenToOnce(this.model, 'fetch-forbidden', () => redirect());
    }

    /**
     * Set up a page title.
     */
    setupPageTitle() {
      this.listenTo(this.model, 'after:save', () => {
        this.updatePageTitle();
      });
      this.listenTo(this.model, 'sync', model => {
        if (model && model.hasChanged(this.nameAttribute)) {
          this.updatePageTitle();
        }
      });
    }

    /**
     * Set up a header.
     */
    setupHeader() {
      this.createView('header', this.headerView, {
        model: this.model,
        fullSelector: '#main > .header',
        scope: this.scope,
        fontSizeFlexible: true
      });
      this.listenTo(this.model, 'sync', model => {
        if (model && model.hasChanged(this.nameAttribute)) {
          if (this.getView('header')) {
            this.getView('header').reRender();
          }
        }
      });
    }

    /**
     * Set up modes.
     */
    setupModes() {
      this.defaultViewMode = this.options.defaultViewMode || this.getMetadata().get(`clientDefs.${this.scope}.detailDefaultViewMode`) || this.defaultViewMode;
      this.viewMode = this.viewMode || this.defaultViewMode;
      const viewModeList = this.options.viewModeList || this.viewModeList || this.getMetadata().get(`clientDefs.${this.scope}.detailViewModeList`);
      this.viewModeList = viewModeList ? viewModeList : [this.MODE_DETAIL];
      this.viewModeIsStorable = this.viewModeIsStorable !== undefined ? this.viewModeIsStorable : this.getMetadata().get(`clientDefs.${this.scope}.detailViewModeIsStorable`);
      this.hasMultipleModes = this.viewModeList.length > 1;
      if (this.viewModeIsStorable && this.hasMultipleModes) {
        let viewMode = null;
        const modeKey = this.getViewModeKey();
        if (this.getStorage().has('state', modeKey)) {
          const storedViewMode = this.getStorage().get('state', modeKey);
          if (storedViewMode && this.viewModeList.includes(storedViewMode)) {
            viewMode = storedViewMode;
          }
        }
        if (!viewMode) {
          viewMode = this.defaultViewMode;
        }
        this.viewMode = /** @type {string} */viewMode;
      }
      if (this.hasMultipleModes) {
        this.addActionHandler('switchMode', (e, target) => this.switchViewMode(target.dataset.value));
        this.modesView = new _modes.default({
          mode: this.viewMode,
          modeList: this.viewModeList,
          scope: this.scope
        });
        this.assignView('modes', this.modesView, '.modes');
      }
    }

    /**
     * @private
     * @return {string}
     */
    getViewModeKey() {
      return `detailViewMode-${this.scope}-${this.model.id}}`;
    }

    /**
     * Set up a record.
     *
     * @return {Promise<import('view').default>}
     */
    setupRecord() {
      const o = {
        model: this.model,
        fullSelector: '#main > .record',
        scope: this.scope,
        shortcutKeysEnabled: true,
        isReturn: this.isReturn
      };
      this.optionsToPass.forEach(option => {
        o[option] = this.options[option];
      });
      if (this.options.params && this.options.params.rootUrl) {
        o.rootUrl = this.options.params.rootUrl;
      }
      if (this.options.params && this.options.params.rootData) {
        o.rootData = this.options.params.rootData;
      }
      if (this.model.get('deleted')) {
        o.readOnly = true;
      }

      // noinspection JSValidateTypes
      return this.createView('record', this.getRecordViewName(), o, view => {
        this.listenTo(view, 'after:mode-change', mode => {
          // Mode change should also re-render the header what the methods do.
          mode === 'edit' ? this.hideAllHeaderActionItems() : this.showAllHeaderActionItems();
        });
        if (this.modesView) {
          this.listenTo(view, 'after:set-detail-mode', () => this.modesView.enable());
          this.listenTo(view, 'after:set-edit-mode', () => this.modesView.disable());
        }
      });
    }

    /**
     * Get a record view name.
     *
     * @returns {string}
     */
    getRecordViewName() {
      return this.getMetadata().get(`clientDefs.${this.scope}.recordViews.${this.viewMode}`) || this.recordView;
    }

    /**
     * Switch a view mode.
     *
     * @param {string} mode
     */
    switchViewMode(mode) {
      this.clearView('record');
      this.setViewMode(mode, true);
      Espo.Ui.notifyWait();
      if (this.modesView) {
        this.modesView.changeMode(mode);
      }
      this.setupRecord().then(view => {
        view.render().then(() => Espo.Ui.notify(false));
      });
    }

    /**
     * Set a view mode.
     *
     * @param {string} mode A mode.
     * @param {boolean} [toStore=false] To preserve a mode being set.
     */
    setViewMode(mode, toStore) {
      this.viewMode = mode;
      if (toStore && this.viewModeIsStorable) {
        const modeKey = this.getViewModeKey();
        this.getStorage().set('state', modeKey, mode);
      }
    }

    /** @private */
    initStarButtons() {
      if (!this.getMetadata().get(`scopes.${this.scope}.stars`)) {
        return;
      }
      this.addStarButtons();
      this.listenTo(this.model, 'change:isStarred', () => this.controlStarButtons());
    }

    /** @private */
    addStarButtons() {
      const isStarred = this.model.get('isStarred');
      this.addMenuItem('buttons', {
        name: 'unstar',
        iconHtml: '<span class="fas fa-star fa-sm text-warning"></span>',
        className: 'btn-s-wide',
        text: this.translate('Starred'),
        hidden: !isStarred,
        //title: this.translate('Unstar'),
        onClick: () => this.actionUnstar()
      }, true);
      this.addMenuItem('buttons', {
        name: 'star',
        iconHtml: '<span class="far fa-star fa-sm"></span>',
        className: 'btn-s-wide',
        text: this.translate('Star'),
        //title: this.translate('Star'),
        hidden: isStarred || !this.model.has('isStarred'),
        onClick: () => this.actionStar()
      }, true);
    }

    /** @private */
    controlStarButtons() {
      const isStarred = this.model.get('isStarred');
      if (isStarred) {
        this.hideHeaderActionItem('star');
        this.showHeaderActionItem('unstar');
        return;
      }
      this.hideHeaderActionItem('unstar');
      this.showHeaderActionItem('star');
    }

    /**
     * Action 'star'.
     */
    actionStar() {
      this.disableMenuItem('star');
      Espo.Ajax.putRequest(`${this.entityType}/${this.model.id}/starSubscription`).then(() => {
        this.hideHeaderActionItem('star');
        this.model.set('isStarred', true, {
          sync: true
        });
      }).finally(() => this.enableMenuItem('star'));
    }

    /**
     * Action 'unstar'.
     */
    actionUnstar() {
      this.disableMenuItem('unstar');
      Espo.Ajax.deleteRequest(`${this.entityType}/${this.model.id}/starSubscription`).then(() => {
        this.hideHeaderActionItem('unstar');
        this.model.set('isStarred', false, {
          sync: true
        });
      }).finally(() => this.enableMenuItem('unstar'));
    }

    /** @private */
    initFollowButtons() {
      if (!this.getMetadata().get(['scopes', this.scope, 'stream'])) {
        return;
      }
      this.addFollowButtons();
      this.listenTo(this.model, 'change:isFollowed', () => {
        this.controlFollowButtons();
      });
    }

    /** @private */
    addFollowButtons() {
      const isFollowed = this.model.get('isFollowed');
      this.addMenuItem('buttons', {
        name: 'unfollow',
        label: 'Followed',
        style: 'success',
        action: 'unfollow',
        hidden: !isFollowed
      }, true);
      this.addMenuItem('buttons', {
        name: 'follow',
        label: 'Follow',
        style: 'default',
        iconHtml: '<span class="fas fa-rss fa-sm"></span>',
        text: this.translate('Follow'),
        action: 'follow',
        hidden: isFollowed || !this.model.has('isFollowed') || !this.getAcl().checkModel(this.model, 'stream')
      }, true);
    }

    /** @private */
    controlFollowButtons() {
      const isFollowed = this.model.get('isFollowed');
      if (isFollowed) {
        this.hideHeaderActionItem('follow');
        this.showHeaderActionItem('unfollow');
        return;
      }
      this.hideHeaderActionItem('unfollow');
      if (this.getAcl().checkModel(this.model, 'stream')) {
        this.showHeaderActionItem('follow');
      }
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Action 'follow'.
     */
    actionFollow() {
      this.disableMenuItem('follow');
      Espo.Ajax.putRequest(this.entityType + '/' + this.model.id + '/subscription').then(() => {
        this.hideHeaderActionItem('follow');
        this.model.set('isFollowed', true, {
          sync: true
        });
        this.enableMenuItem('follow');
      }).catch(() => {
        this.enableMenuItem('follow');
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Action 'unfollow'.
     */
    actionUnfollow() {
      this.disableMenuItem('unfollow');
      Espo.Ajax.deleteRequest(this.entityType + '/' + this.model.id + '/subscription').then(() => {
        this.hideHeaderActionItem('unfollow');
        this.model.set('isFollowed', false, {
          sync: true
        });
        this.enableMenuItem('unfollow');
      }).catch(() => {
        this.enableMenuItem('unfollow');
      });
    }

    /**
     * @inheritDoc
     */
    getHeader() {
      const name = this.model.attributes[this.nameAttribute] || this.model.id;
      const title = document.createElement('span');
      title.classList.add('font-size-flexible', 'title');
      title.textContent = name;
      if (this.model.attributes.deleted) {
        title.style.textDecoration = 'line-through';
      }
      if (this.getRecordMode() === 'detail') {
        title.title = this.translate('clickToRefresh', 'messages');
        title.dataset.action = 'fullRefresh';
        title.style.cursor = 'pointer';
      }
      const scopeLabel = this.getLanguage().translate(this.scope, 'scopeNamesPlural');
      let root = document.createElement('span');
      root.text = scopeLabel;
      root.style.userSelect = 'none';
      if (!this.rootLinkDisabled) {
        const a = document.createElement('a');
        a.href = this.rootUrl;
        a.classList.add('action');
        a.dataset.action = 'navigateToRoot';
        a.text = scopeLabel;
        root = document.createElement('span');
        root.style.userSelect = 'none';
        root.append(a);
      }
      const iconHtml = this.getHeaderIconHtml();
      if (iconHtml) {
        root.insertAdjacentHTML('afterbegin', iconHtml);
      }
      return this.buildHeaderHtml([root, title]);
    }

    /**
     * @inheritDoc
     */
    updatePageTitle() {
      if (this.model.has(this.nameAttribute)) {
        this.setPageTitle(this.model.attributes[this.nameAttribute] || this.model.id);
        return;
      }
      super.updatePageTitle();
    }

    /**
     * @return {module:views/record/detail}
     */
    getRecordView() {
      return this.getView('record');
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @param {string} name A relationship name.
     * @deprecated As of v8.4.
     */
    updateRelationshipPanel(name) {
      this.model.trigger(`update-related:${name}`);
      console.warn('updateRelationshipPanel method is deprecated.');
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Action 'duplicate'.
     */
    actionDuplicate() {
      Espo.Ui.notifyWait();
      Espo.Ajax.postRequest(this.scope + '/action/getDuplicateAttributes', {
        id: this.model.id
      }).then(attributes => {
        Espo.Ui.notify(false);
        const url = '#' + this.scope + '/create';
        this.getRouter().dispatch(this.scope, 'create', {
          attributes: attributes,
          returnUrl: this.getRouter().getCurrentUrl(),
          options: {
            duplicateSourceId: this.model.id
          }
        });
        this.getRouter().navigate(url, {
          trigger: false
        });
      });
    }

    /**
     * @protected
     */
    hideAllHeaderActionItems() {
      if (!this.getHeaderView()) {
        return;
      }
      this.getHeaderView().hideAllMenuItems();
    }

    /**
     * @protected
     */
    showAllHeaderActionItems() {
      if (!this.getHeaderView()) {
        return;
      }
      this.getHeaderView().showAllActionItems();
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Hide a view mode.
     *
     * @param {string} mode
     * @since 8.4.0
     */
    hideViewMode(mode) {
      if (!this.modesView) {
        return;
      }
      this.modesView.hideMode(mode);
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Show a view mode.
     *
     * @param {string} mode
     * @since 8.4.0
     */
    showViewMode(mode) {
      if (!this.modesView) {
        return;
      }
      this.modesView.showMode(mode);
    }

    /**
     * @protected
     */
    async actionFullRefresh() {
      if (this.getRecordMode() === 'edit') {
        return;
      }
      Espo.Ui.notifyWait();
      await this.model.fetch();
      this.model.trigger('update-all');
      Espo.Ui.notify();
    }

    /**
     * @private
     * @return {'detail'|'edit'}
     */
    getRecordMode() {
      if (this.getRecordView().getMode) {
        return this.getRecordView().getMode();
      }
      return 'detail';
    }
  }
  var _default = _exports.default = DetailView;
});

define("views/collapsed-modal-bar", ["exports", "view", "views/collapsed-modal"], function (_exports, _view, _collapsedModal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _collapsedModal = _interopRequireDefault(_collapsedModal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class CollapsedModalBarView extends _view.default {
    // language=Handlebars
    templateContent = `
        {{#each dataList}}
            <div class="collapsed-modal" data-number="{{number}}">{{var key ../this}}</div>
        {{/each}}
    `;

    /**
     * @private
     * @type {number}
     */
    maxNumberToDisplay = 3;

    /**
     * @private
     * @type {number[]}
     */
    numberList;

    /**
     * @private
     * @type {number}
     */
    lastNumber;
    data() {
      return {
        dataList: this.getDataList()
      };
    }
    init() {
      this.on('render', () => {
        if (document.querySelector('.collapsed-modal-bar')) {
          return;
        }
        const div = document.createElement('div');
        div.classList.add('collapsed-modal-bar');
        document.body.append(div);
      });
    }
    setup() {
      this.lastNumber = 0;
      this.numberList = [];
    }

    /**
     * @private
     * @return {Record[]}
     */
    getDataList() {
      const list = [];
      let numberList = [...this.numberList];
      if (this.numberList.length > this.maxNumberToDisplay) {
        numberList = numberList.slice(this.numberList.length - this.maxNumberToDisplay);
      }
      numberList.reverse().forEach((number, i) => {
        list.push({
          number: number.toString(),
          key: this.composeKey(number),
          index: i
        });
      });
      return list;
    }

    /**
     * @private
     * @param {string} title
     * @return {number|null}
     */
    calculateDuplicateNumber(title) {
      let duplicateNumber = 0;
      for (const number of this.numberList) {
        const view = this.getCollapsedModalViewByNumber(number);
        if (!view) {
          continue;
        }
        if (view.title === title) {
          duplicateNumber++;
        }
      }
      if (duplicateNumber === 0) {
        return null;
      }
      return duplicateNumber;
    }

    /**
     * @param {number} number
     * @return {import('views/collapsed-modal').default|null}
     */
    getCollapsedModalViewByNumber(number) {
      const key = this.composeKey(number);
      return this.getView(key);
    }

    /**
     * @type {import('views/modal').default[]}
     */
    getModalViewList() {
      return this.numberList.map(number => this.getCollapsedModalViewByNumber(number)).filter(it => it).map(it => it.modalView);
    }

    /**
     * @param {import('views/modal').default} modalView
     * @param {{title: string}} options
     */
    async addModalView(modalView, options) {
      const number = this.lastNumber;
      this.numberList.push(this.lastNumber);
      const key = this.composeKey(number);
      this.lastNumber++;
      const view = new _collapsedModal.default({
        modalView: modalView,
        title: options.title,
        duplicateNumber: this.calculateDuplicateNumber(options.title),
        onClose: () => this.removeModalView(number),
        onExpand: () => {
          this.removeModalView(number, true);

          // Use timeout to prevent DOM being updated after modal is re-rendered.
          setTimeout(async () => {
            const key = `dialog-${number}`;
            this.setView(key, modalView);
            modalView.setSelector(modalView.containerSelector);
            await this.getView(key).render();
            modalView.trigger('after:expand');
          }, 5);
        }
      });
      await this.assignView(key, view, `[data-number="${number}"]`);
      await this.reRender(true);
    }

    /**
     * @param {number} number
     * @param {boolean} [noReRender]
     */
    removeModalView(number) {
      let noReRender = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
      const key = this.composeKey(number);
      const index = this.numberList.indexOf(number);
      if (~index) {
        this.numberList.splice(index, 1);
      }
      if (this.isRendered()) {
        const element = this.element.querySelector(`.collapsed-modal[data-number="${number}"]`);
        if (element) {
          element.remove();
        }
      }
      if (!noReRender) {
        this.reRender();
      }
      this.clearView(key);
    }

    /**
     * @private
     * @param {number} number
     * @return {string}
     */
    composeKey(number) {
      return `key-${number}`;
    }
  }
  var _default = _exports.default = CollapsedModalBarView;
});

define("views/stream/note", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NoteStreamView extends _view.default {
    /**
     * @protected
     * @type {string|null}
     */
    messageName = null;

    /**
     * @protected
     * @type {string|null}
     */
    messageTemplate = null;

    /**
     * Data to pass to a message template.
     *
     * @protected
     * @type {Object.<string,JQuery|Element|string>|null}
     */
    messageData = null;

    /** @protected */
    isEditable = false;
    /** @protected */
    isRemovable = false;
    /** @protected */
    isSystemAvatar = false;
    rowActionsView = 'views/stream/record/row-actions/default';
    data() {
      return {
        isUserStream: this.isUserStream,
        noEdit: this.options.noEdit,
        acl: this.options.acl,
        onlyContent: this.options.onlyContent,
        avatar: this.getAvatarHtml()
      };
    }
    init() {
      this.createField('createdAt', null, null, 'views/fields/datetime-short');

      /** @type {string} */
      this.listType = this.options.listType;
      this.isUserStream = this.options.isUserStream;
      this.isThis = !this.isUserStream;
      this.parentModel = this.options.parentModel;
      if (!this.isUserStream) {
        if (this.parentModel) {
          if (this.parentModel.entityType !== this.model.get('parentType') || this.parentModel.id !== this.model.get('parentId')) {
            this.isThis = false;
          }
        }
      }
      if (this.getUser().isAdmin()) {
        this.isRemovable = true;
      }
      if (this.messageName && this.isThis) {
        this.messageName += 'This';
      }
      if (!this.isThis) {
        this.createField('parent');
      }
      const translatedEntityType = this.translateEntityType(this.model.get('parentType'));
      this.messageData = {
        'user': 'field:createdBy',
        'entity': 'field:parent',
        'entityType': translatedEntityType
      };
      const rowActionsView = this.options.rowActionsView || this.rowActionsView;
      if (!this.options.noEdit && (this.isEditable || this.isRemovable)) {
        this.createView('right', rowActionsView, {
          selector: '.right-container',
          acl: this.options.acl,
          model: this.model,
          isEditable: this.isEditable,
          isRemovable: this.isRemovable,
          listType: this.listType,
          isThis: this.isThis,
          parentModel: this.parentModel,
          isNotification: this.options.isNotification
        });
      }
    }
    translateEntityType(entityType, isPlural) {
      let string = isPlural ? this.translate(entityType, 'scopeNamesPlural') || '' : this.translate(entityType, 'scopeNames') || '';
      if (!this.isToUpperCaseStringItems()) {
        string = string.toLowerCase();
      }
      return string;
    }
    isToUpperCaseStringItems() {
      const language = this.getPreferences().get('language') || this.getConfig().get('language');
      return ['de_DE', 'nl_NL'].includes(language);
    }
    createField(name, type, params, view, options) {
      type = type || this.model.getFieldType(name) || 'base';
      const o = {
        model: this.model,
        defs: {
          name: name,
          params: params || {}
        },
        selector: '.cell-' + name,
        mode: 'list'
      };
      if (options) {
        for (const i in options) {
          o[i] = options[i];
        }
      }
      this.createView(name, view || this.getFieldManager().getViewName(type), o);
    }
    isMale() {
      return this.model.get('createdByGender') === 'Male';
    }
    isFemale() {
      return this.model.get('createdByGender') === 'Female';
    }
    createMessage() {
      if (!this.messageTemplate) {
        let isTranslated = false;
        const parentType = this.model.get('parentType') || null;
        if (this.isMale()) {
          this.messageTemplate = this.translate(this.messageName, 'streamMessagesMale', parentType) || '';
          if (this.messageTemplate !== this.messageName) {
            isTranslated = true;
          }
        } else if (this.isFemale()) {
          this.messageTemplate = this.translate(this.messageName, 'streamMessagesFemale', parentType) || '';
          if (this.messageTemplate !== this.messageName) {
            isTranslated = true;
          }
        }
        if (!isTranslated) {
          this.messageTemplate = this.translate(this.messageName, 'streamMessages', parentType) || '';
        }
      }
      if (this.messageTemplate.indexOf('{entityType}') === 0 && typeof this.messageData.entityType === 'string') {
        this.messageData.entityTypeUcFirst = Espo.Utils.upperCaseFirst(this.messageData.entityType);
        this.messageTemplate = this.messageTemplate.replace('{entityType}', '{entityTypeUcFirst}');
      }
      this.createView('message', 'views/stream/message', {
        messageTemplate: this.messageTemplate,
        selector: '.message',
        model: this.model,
        messageData: this.messageData
      });
    }
    getAvatarHtml() {
      let id = this.model.get('createdById');
      if (this.isSystemAvatar) {
        id = this.getHelper().getAppParam('systemUserId');
      }
      return this.getHelper().getAvatarHtml(id, 'small', 20);
    }

    /**
     *
     * @param [scope]
     * @param [id]
     * @return {string|null}
     */
    getIconHtml(scope, id) {
      if (!scope) {
        if (!this.model.attributes.parentType) {
          return null;
        }
        scope = this.model.attributes.parentType;
        id = this.model.attributes.parentId;
      }
      if (this.isThis && this.parentModel && scope === this.parentModel.entityType) {
        return null;
      }
      const iconClass = this.getMetadata().get(`clientDefs.${scope}.iconClass`);
      const color = this.getMetadata().get(`clientDefs.${scope}.color`);
      if (!iconClass) {
        return null;
      }
      return $('<span>').addClass(iconClass).addClass('action text-muted icon').css('cursor', 'pointer').css('color', color ? color : '').attr('title', this.translate('View')).attr('data-action', 'quickView').attr('data-id', id).attr('data-scope', scope).get(0).outerHTML;
    }
  }
  var _default = _exports.default = NoteStreamView;
});

define("views/stream/record/list", ["exports", "views/record/list-expanded"], function (_exports, _listExpanded) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _listExpanded = _interopRequireDefault(_listExpanded);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/stream/record/list */

  class ListStreamRecordView extends _listExpanded.default {
    type = 'listStream';
    massActionsDisabled = true;

    /**
     * @private
     * @type {boolean}
     */
    isUserStream;
    setup() {
      this.isUserStream = this.options.isUserStream || false;
      this.itemViews = this.getMetadata().get('clientDefs.Note.itemViews') || {};
      super.setup();
      this.isRenderingNew = false;
      this.listenTo(this.collection, 'update-sync', () => {
        this.buildRows(() => this.reRender());
      });
      if (this.isUserStream || this.model.entityType === 'User') {
        const collection = /** @type {import('collections/note').default} */this.collection;
        collection.reactionsCheckMaxSize = this.getConfig().get('streamReactionsCheckMaxSize') || 0;
      }
      this.listenTo(this.collection, 'sync', (c, r, options) => {
        if (!options.fetchNew) {
          return;
        }
        if (this.isRenderingNew) {
          // Prevent race condition.
          return;
        }
        const lengthBeforeFetch = options.lengthBeforeFetch || 0;
        if (lengthBeforeFetch === 0) {
          this.buildRows(() => this.reRender());
          return;
        }
        const $list = this.$el.find(this.listContainerEl);
        const rowCount = this.collection.length - lengthBeforeFetch;
        if (rowCount === 0) {
          return;
        }
        this.isRenderingNew = true;
        for (let i = rowCount - 1; i >= 0; i--) {
          const model = this.collection.at(i);
          this.buildRow(i, model, view => {
            if (i === 0) {
              this.isRenderingNew = false;
            }
            let $row = $(this.getRowContainerHtml(model.id));

            // Prevent a race condition issue.
            const $existingRow = this.$el.find(`[data-id="${model.id}"]`);
            if ($existingRow.length) {
              $row = $existingRow;
            }
            if (!$existingRow.length) {
              $list.prepend($row);
            }
            view.render();
          });
        }
      });
      this.events['auxclick a[href][data-scope][data-id]'] = e => {
        const isCombination = e.button === 1 && (e.ctrlKey || e.metaKey);
        if (!isCombination) {
          return;
        }
        const $target = $(e.currentTarget);
        const id = $target.attr('data-id');
        const scope = $target.attr('data-scope');
        e.preventDefault();
        e.stopPropagation();
        this.actionQuickView({
          id: id,
          scope: scope
        });
      };
    }
    buildRow(i, model, callback) {
      const key = model.id;
      this.rowList.push(key);
      const type = model.get('type');
      const viewName = this.itemViews[type] || 'views/stream/notes/' + Espo.Utils.camelCaseToHyphen(type);
      this.createView(key, viewName, {
        model: model,
        parentModel: this.model,
        acl: {
          edit: this.getAcl().checkModel(model, 'edit')
        },
        isUserStream: this.options.isUserStream,
        noEdit: this.options.noEdit,
        optionsToPass: ['acl'],
        name: this.type + '-' + model.entityType,
        selector: 'li[data-id="' + model.id + '"]',
        setViewBeforeCallback: this.options.skipBuildRows && !this.isRendered(),
        listType: this.type,
        rowActionsView: this.options.rowActionsView
      }, callback);
    }
    buildRows(callback) {
      this.checkedList = [];
      this.rowList = [];
      if (this.collection.length > 0) {
        this.wait(true);
        const count = this.collection.models.length;
        let built = 0;
        for (const i in this.collection.models) {
          const model = this.collection.models[i];
          this.buildRow(i, model, () => {
            built++;
            if (built === count) {
              if (typeof callback === 'function') {
                callback();
              }
              this.wait(false);
              this.trigger('after:build-rows');
            }
          });
        }
        return;
      }
      if (typeof callback === 'function') {
        callback();
        this.trigger('after:build-rows');
      }
    }

    /**
     * Load new records.
     *
     * @return {Promise}
     */
    showNewRecords() {
      const collection = /** @type {import('collections/note').default} */
      this.collection;
      return collection.fetchNew();
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @private
     * @param {{id: string}} data
     */
    actionPin(data) {
      const collection = /** @type {import('collections/note').default} */this.collection;
      Espo.Ui.notifyWait();
      Espo.Ajax.postRequest(`Note/${data.id}/pin`).then(() => {
        Espo.Ui.notify(false);
        const model = collection.get(data.id);
        if (model) {
          model.set('isPinned', true);
        }
        if (collection.pinnedList) {
          collection.fetchNew();
        }
        collection.trigger('pin', model.id);
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @private
     * @param {{id: string}} data
     */
    actionUnpin(data) {
      const collection = /** @type {import('collections/note').default} */this.collection;
      Espo.Ui.notifyWait();
      Espo.Ajax.deleteRequest(`Note/${data.id}/pin`).then(() => {
        Espo.Ui.notify(false);
        const model = collection.get(data.id);
        if (model) {
          model.set('isPinned', false);
        }
        if (collection.pinnedList) {
          collection.fetchNew();
        }
        collection.trigger('unpin', model.id);
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @private
     * @param {{id: string}} data
     */
    actionQuoteReply(data) {
      const rowView = this.getView(data.id);
      const selection = window.getSelection();
      if (selection && selection.anchorNode && selection.focusNode) {
        const postContainer = rowView.element.querySelector('.complex-text');
        if (postContainer.contains(selection.anchorNode) && postContainer.contains(selection.focusNode)) {
          let contents = '';
          for (let i = 0; i < selection.rangeCount; i++) {
            const range = selection.getRangeAt(i);
            const div = document.createElement('div');
            div.appendChild(range.cloneContents());
            contents += div.innerHTML;
          }
          if (contents) {
            Espo.loader.requirePromise('turndown').then(/** typeof import('turndown').default */TurndownService => {
              const turndownService = new TurndownService();

              // noinspection JSValidateTypes
              const code = turndownService.turndown(contents);
              this.trigger('quote-reply', code);
            });
            return;
          }
        }
      }
      const model = this.collection.get(data.id);
      if (!model) {
        return;
      }
      const code = model.attributes.post;
      if (!code) {
        return;
      }
      this.trigger('quote-reply', code);
    }
  }
  var _default = _exports.default = ListStreamRecordView;
});

define("views/stream/record/row-actions/reactions/reactions", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ReactionsRowActionView extends _view.default {
    // language=Handlebars
    templateContent = `
        <div class="item-icon-grid">
            {{#each reactions}}
                <a
                    role="button"
                    {{#if isReacted}}
                        data-action="unReact"
                    {{else}}
                        data-action="react"
                    {{/if}}
                    data-type="{{type}}"
                    title="{{label}}"
                    class=" {{#if isReacted}} text-primary {{else}} text-soft {{/if}}"
                ><span class="{{iconClass}}"></span></a>
            {{/each}}
        </div>
    `;
    /**
     * @param {{
     *     reactions: {
     *         type: string,
     *         iconClass: string|null,
     *         label: string,
     *         isReacted: boolean,
     *     }[]
     * }} options
     */
    constructor(options) {
      super(options);
      this.reactions = options.reactions;
    }
    data() {
      return {
        reactions: this.reactions
      };
    }
  }
  _exports.default = ReactionsRowActionView;
});

define("views/record/search", ["exports", "view", "helpers/misc/stored-text-search", "ui/autocomplete"], function (_exports, _view, _storedTextSearch, _autocomplete) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _storedTextSearch = _interopRequireDefault(_storedTextSearch);
  _autocomplete = _interopRequireDefault(_autocomplete);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/search */

  /**
   * @typedef {Object} module:views/record/search~boolFilterDefs
   * @property {boolean} [inPortalDisabled]
   * @property {boolean} [isPortalOnly]
   * @property {boolean} [aux]
   * @property {module:utils~AccessDefs[]} [accessDataList]
   */

  /**
   * A search panel view.
   */
  class SearchView extends _view.default {
    template = 'record/search';
    scope = '';
    entityType = '';
    /** @type {module:search-manager} */
    searchManager;
    fieldFilterList = null;
    /** @type {Object.<string, string>|null}*/
    fieldFilterTranslations = null;
    textFilter = '';
    /**
     * @type {string|null}
     */
    primary = null;
    presetFilterList = null;
    /** @type {{string: module:search-manager~advancedFilter}} */
    advanced;
    bool = null;
    filtersLayoutName = 'filters';
    disableSavePreset = false;
    textFilterDisabled = false;
    toShowApplyFiltersButton = false;
    toShowResetFiltersText = false;
    isSearchedWithAdvancedFilter = false;
    primaryFiltersDisabled = false;
    viewModeIconClassMap = {
      list: 'fas fa-align-justify',
      kanban: 'fas fa-align-left fa-rotate-90'
    };
    FIELD_QUICK_SEARCH_COUNT_THRESHOLD = 4;
    autocompleteLimit = 7;
    data() {
      return {
        scope: this.scope,
        entityType: this.entityType,
        textFilter: this.textFilter,
        bool: this.bool || {},
        boolFilterList: this.boolFilterList,
        hasFieldQuickSearch: this.fieldFilterList.length >= this.FIELD_QUICK_SEARCH_COUNT_THRESHOLD,
        filterFieldDataList: this.getFilterFieldDataList(),
        filterDataList: this.getFilterDataList(),
        presetName: this.presetName,
        presetFilterList: this.getPresetFilterList(),
        leftDropdown: this.hasLeftDropdown(),
        textFilterDisabled: this.textFilterDisabled,
        viewMode: this.viewMode,
        viewModeDataList: this.viewModeDataList || [],
        hasViewModeSwitcher: this.viewModeList && this.viewModeList.length > 1,
        isWide: this.options.isWide,
        toShowApplyFiltersButton: this.toShowApplyFiltersButton,
        toShowResetFiltersText: this.toShowResetFiltersText,
        primaryFiltersDisabled: this.primaryFiltersDisabled
      };
    }
    setup() {
      this.entityType = this.collection.entityType;
      this.scope = this.options.scope || this.entityType;
      this.filtersLayoutName = this.options.filtersLayoutName || this.filtersLayoutName;
      this.primaryFiltersDisabled = this.options.primaryFiltersDisabled || this.primaryFiltersDisabled;
      this.viewModeIconClassMap = {
        ...this.viewModeIconClassMap,
        ...this.getMetadata().get(`clientDefs.${this.scope}.viewModeIconClassMap`)
      };

      /** @type {module:search-manager} */
      this.searchManager = this.options.searchManager;

      /** @private */
      this.storedTextSearchHelper = new _storedTextSearch.default(this.scope, this.getHelper().storage);
      this.textSearchStoringDisabled = this.getPreferences().get('textSearchStoringDisabled');
      this.textFilterDisabled = this.options.textFilterDisabled || this.textFilterDisabled || this.getMetadata().get(['clientDefs', this.scope, 'textFilterDisabled']);
      if ('disableSavePreset' in this.options) {
        this.disableSavePreset = this.options.disableSavePreset;
      }
      this.viewMode = this.options.viewMode;
      this.viewModeList = this.options.viewModeList;
      this.addReadyCondition(() => {
        return this.fieldFilterList !== null;
      });
      this.boolFilterList = Espo.Utils.clone(this.getMetadata().get(['clientDefs', this.scope, 'boolFilterList']) || []).filter(/** module:views/record/search~boolFilterDefs|string */item => {
        if (typeof item === 'string') {
          return true;
        }
        item = item || {};
        if (item.aux) {
          return false;
        }
        if (item.inPortalDisabled && this.getUser().isPortal()) {
          return false;
        }
        if (item.isPortalOnly && !this.getUser().isPortal()) {
          return false;
        }
        if (item.accessDataList) {
          if (!Espo.Utils.checkAccessDataList(item.accessDataList, this.getAcl(), this.getUser())) {
            return false;
          }
        }
        return true;
      }).map(item => {
        if (typeof item === 'string') {
          return item;
        }
        item = item || {};
        return item.name;
      });
      this.fieldFilterTranslations = {};
      const forbiddenFieldList = this.getAcl().getScopeForbiddenFieldList(this.entityType) || [];
      this.wait(new Promise(resolve => {
        this.getHelper().layoutManager.get(this.entityType, this.filtersLayoutName, list => {
          this.fieldFilterList = [];
          (list || []).forEach(field => {
            if (~forbiddenFieldList.indexOf(field)) {
              return;
            }
            this.fieldFilterList.push(field);
            this.fieldFilterTranslations[field] = this.translate(field, 'fields', this.entityType);
          });
          resolve();
        });
      }));
      this.setupPresetFilters();
      if (this.getMetadata().get(['scopes', this.entityType, 'stream'])) {
        this.boolFilterList.push('followed');
      }
      if (this.getMetadata().get(`scopes.${this.entityType}.collaborators`) && !this.getUser().isPortal()) {
        this.boolFilterList.push('shared');
      }
      this.loadSearchData();
      if (this.hasAdvancedFilter()) {
        this.isSearchedWithAdvancedFilter = true;
      }
      if (this.presetName) {
        let hasPresetListed = false;
        for (const i in this.presetFilterList) {
          const item = this.presetFilterList[i] || {};
          const name = typeof item === 'string' ? item : item.name;
          if (name === this.presetName) {
            hasPresetListed = true;
            break;
          }
        }
        if (!hasPresetListed) {
          this.presetFilterList.push(this.presetName);
        }
      }
      this.model = this.collection.prepareModel();
      this.model.clear();
      this.createFilters();
      this.setupViewModeDataList();
      this.listenTo(this.collection, 'order-changed', () => {
        this.controlResetButtonVisibility();
      });
      this.wait(this.getHelper().processSetupHandlers(this, 'record/search'));
    }
    setupPresetFilters() {
      if (this.primaryFiltersDisabled) {
        this.presetFilterList = [];
        return;
      }
      const filterList = this.options.filterList || this.getMetadata().get(['clientDefs', this.scope, 'filterList']) || [];
      this.presetFilterList = filterList.filter(item => {
        if (typeof item === 'string') {
          return true;
        }
        item = item || {};
        if (item.aux) {
          return false;
        }
        if (item.inPortalDisabled && this.getUser().isPortal()) {
          return false;
        }
        if (item.isPortalOnly && !this.getUser().isPortal()) {
          return false;
        }
        if (item.accessDataList) {
          if (!Espo.Utils.checkAccessDataList(item.accessDataList, this.getAcl(), this.getUser())) {
            return false;
          }
        }
        return true;
      });
      if (this.getMetadata().get(`scopes.${this.scope}.stars`)) {
        this.presetFilterList.unshift({
          name: 'starred'
        });
      }
      ((this.getPreferences().get('presetFilters') || {})[this.scope] || []).forEach(item => {
        this.presetFilterList.push(item);
      });
    }
    setupViewModeDataList() {
      if (!this.viewModeList) {
        return [];
      }
      const list = [];
      this.viewModeList.forEach(item => {
        const o = {
          name: item,
          title: this.translate(item, 'listViewModes'),
          iconClass: this.viewModeIconClassMap[item]
        };
        list.push(o);
      });
      this.viewModeDataList = list;
    }
    setViewMode(mode, preventLoop, toTriggerEvent) {
      this.viewMode = mode;
      if (this.isRendered()) {
        this.$el.find('[data-action="switchViewMode"]').removeClass('active');
        this.$el.find('[data-action="switchViewMode"][data-name="' + mode + '"]').addClass('active');
      } else {
        if (this.isBeingRendered() && !preventLoop) {
          this.once('after:render', () => {
            this.setViewMode(mode, true);
          });
        }
      }
      this.collection.offset = 0;
      if (toTriggerEvent) {
        this.trigger('change-view-mode', mode);
      }
    }
    hasLeftDropdown() {
      if (this.primaryFiltersDisabled && !this.boolFilterList.length) {
        return false;
      }
      return this.presetFilterList.length || this.boolFilterList.length || Object.keys(this.advanced || {}).length;
    }
    handleLeftDropdownVisibility() {
      if (this.hasLeftDropdown()) {
        this.$leftDropdown.removeClass('hidden');
      } else {
        this.$leftDropdown.addClass('hidden');
      }
    }
    createFilters(callback) {
      let i = 0;
      const count = Object.keys(this.advanced || {}).length;
      if (count === 0) {
        if (typeof callback === 'function') {
          callback();
        }
      }
      for (const field in this.advanced) {
        this.createFilter(field, this.advanced[field], () => {
          i++;
          if (i === count) {
            if (typeof callback === 'function') {
              callback();
            }
          }
        });
      }
    }
    events = {
      /** @this SearchView */
      'keydown input[data-name="textFilter"]': function (e) {
        const key = Espo.Utils.getKeyFromKeyEvent(e);
        if (e.key === 'Enter' || key === 'Enter' || key === 'Control+Enter') {
          this.search();
          this.hideApplyFiltersButton();
        }
      },
      /** @this SearchView */
      'focus input[data-name="textFilter"]': function (e) {
        e.currentTarget.select();
      },
      /** @this SearchView */
      'click .advanced-filters-apply-container a[data-action="applyFilters"]': function () {
        this.search();
        this.hideApplyFiltersButton();
        this.$el.find('button.search').focus();
      },
      /** @this SearchView */
      'click button[data-action="search"]': function () {
        this.search();
        this.hideApplyFiltersButton();
      },
      /** @this SearchView */
      'click a[data-action="addFilter"]': function (e) {
        const $target = $(e.currentTarget);
        const name = $target.data('name');
        $target.closest('li').addClass('hidden');
        this.addFilter(name);
      },
      /** @this SearchView */
      'click .advanced-filters a.remove-filter': function (e) {
        const $target = $(e.currentTarget);
        const name = $target.data('name');
        this.removeFilter(name);
      },
      /** @this SearchView */
      'click button[data-action="reset"]': function () {
        this.resetFilters();
      },
      /** @this SearchView */
      'click button[data-action="refresh"]': function () {
        this.refresh();
      },
      /** @this SearchView */
      'click a[data-action="selectPreset"]': function (e) {
        const $target = $(e.currentTarget);
        const presetName = $target.data('name') || null;
        this.selectPreset(presetName);
      },
      /** @this SearchView */
      'click .dropdown-menu a[data-action="savePreset"]': function () {
        this.createView('savePreset', 'views/modals/save-filters', {}, view => {
          view.render();
          this.listenToOnce(view, 'save', name => {
            this.savePreset(name);
            view.close();
            this.removeFilters();
            this.createFilters(() => {
              this.render();
            });
          });
        });
      },
      /** @this SearchView */
      'click .dropdown-menu a[data-action="removePreset"]': function () {
        const id = this.presetName;
        this.confirm(this.translate('confirmation', 'messages'), () => {
          this.removePreset(id);
        });
      },
      /** @this SearchView */
      'change .search-row ul.filter-menu input[data-role="boolFilterCheckbox"]': function (e) {
        e.stopPropagation();
        this.search();
        this.manageLabels();
      },
      /** @this SearchView */
      'click [data-action="switchViewMode"]': function (e) {
        const mode = $(e.currentTarget).data('name');
        if (mode === this.viewMode) {
          return;
        }
        this.setViewMode(mode, false, true);
      },
      /** @this SearchView */
      'keyup input.field-filter-quick-search-input': function (e) {
        this.processFieldFilterQuickSearch(e.currentTarget.value);
      },
      /** @this SearchView */
      'keydown input.field-filter-quick-search-input': function (e) {
        if (e.code === 'Enter') {
          this.addFirstFieldFilter();
          return;
        }
        if (e.code === 'Escape') {
          this.closeAddFieldDropdown();
        }
      }
    };
    removeFilter(name) {
      this.$el.find('ul.filter-list li[data-name="' + name + '"]').removeClass('hidden');
      const container = this.getView('filter-' + name).$el.closest('div.filter');
      this.clearView('filter-' + name);
      container.remove();
      delete this.advanced[name];
      this.presetName = this.primary;
      this.updateAddFilterButton();
      this.fetch();
      this.updateSearch();
      this.manageLabels();
      this.handleLeftDropdownVisibility();
      this.controlResetButtonVisibility();
      if (this.isSearchedWithAdvancedFilter) {
        this.hasAdvancedFilter() ? this.showApplyFiltersButton() : this.showResetFiltersButton();
        this.$applyFilters.focus();
        return;
      }
      if (!this.hasAdvancedFilter()) {
        this.hideApplyFiltersButton();
      }
    }
    addFilter(name) {
      this.advanced[name] = {};
      this.presetName = this.primary;
      this.createFilter(name, {}, view => {
        view.populateDefaults();
        this.fetch();
        this.updateSearch();
        if (view.getFieldView().initialSearchIsNotIdle) {
          this.showApplyFiltersButton();
        }
      });
      this.updateAddFilterButton();
      this.handleLeftDropdownVisibility();
      this.manageLabels();
      this.controlResetButtonVisibility();
    }
    refresh() {
      Espo.Ui.notifyWait();
      this.collection.abortLastFetch();
      this.collection.reset();
      this.collection.fetch().then(() => {
        Espo.Ui.notify(false);
      });
    }
    selectPreset(presetName, forceClearAdvancedFilters) {
      const wasPreset = !(this.primary === this.presetName);
      this.presetName = presetName;
      const advanced = this.getPresetData();
      this.primary = this.getPrimaryFilterName();
      const isPreset = !(this.primary === this.presetName);
      if (forceClearAdvancedFilters || wasPreset || isPreset || Object.keys(advanced).length) {
        this.removeFilters();
        this.advanced = advanced;
      }
      this.updateSearch();
      this.manageLabels();
      this.createFilters(() => {
        this.reRender().then(() => {
          // noinspection JSUnresolvedReference
          this.$el.find('.filters-button').get(0).focus({
            preventScroll: true
          });
        });
      });
      this.updateCollection();
    }
    removeFilters() {
      this.$advancedFiltersPanel.empty();
      for (const name in this.advanced) {
        this.clearView('filter-' + name);
      }
    }
    resetFilters() {
      this.trigger('reset');
      this.collection.resetOrderToDefault();
      this.textFilter = '';
      this.selectPreset(this.presetName, true);
      this.hideApplyFiltersButton();
      this.trigger('update-ui');
    }
    savePreset(name) {
      const id = 'f' + Math.floor(Math.random() * 1000001).toString();
      this.fetch();
      this.updateSearch();
      const presetFilters = this.getPreferences().get('presetFilters') || {};
      if (!(this.scope in presetFilters)) {
        presetFilters[this.scope] = [];
      }
      const data = {
        id: id,
        name: id,
        label: name,
        data: this.advanced,
        primary: this.primary
      };
      presetFilters[this.scope].push(data);
      this.presetFilterList.push(data);
      this.getPreferences().once('sync', () => {
        this.getPreferences().trigger('update');
        this.updateSearch();
      });
      this.getPreferences().save({
        'presetFilters': presetFilters
      }, {
        patch: true
      });
      this.presetName = id;
    }
    removePreset(id) {
      const presetFilters = this.getPreferences().get('presetFilters') || {};
      if (!(this.scope in presetFilters)) {
        presetFilters[this.scope] = [];
      }
      let list;
      list = presetFilters[this.scope];
      list.forEach((item, i) => {
        if (item.id === id) {
          list.splice(i, 1);
        }
      });
      list = this.presetFilterList;
      list.forEach((item, i) => {
        if (item.id === id) {
          list.splice(i, 1);
        }
      });
      this.getPreferences().set('presetFilters', presetFilters);
      this.getPreferences().save({
        patch: true
      });
      this.getPreferences().trigger('update');
      this.presetName = this.primary;
      this.advanced = {};
      this.removeFilters();
      this.render();
      this.updateSearch();
      this.updateCollection();
    }
    updateAddFilterButton() {
      const $ul = this.$el.find('ul.filter-list');
      if ($ul.children().not('.hidden').not('.dropdown-header').not('.quick-search-list-item').length === 0) {
        this.$addFilterButton.addClass('disabled');
      } else {
        this.$addFilterButton.removeClass('disabled');
      }
      this.trigger('update-ui');
    }
    afterRender() {
      this.$filtersLabel = this.$el.find('.search-row span.filters-label');
      this.$filtersButton = this.$el.find('.search-row button.filters-button');
      this.$leftDropdown = this.$el.find('div.search-row div.left-dropdown');
      this.$resetButton = this.$el.find('[data-action="reset"]');
      this.$applyFiltersContainer = this.$el.find('.advanced-filters-apply-container');
      this.$applyFilters = this.$applyFiltersContainer.find('[data-action="applyFilters"]');
      /** @type {JQuery} */
      this.$filterList = this.$el.find('ul.filter-list');
      /** @type {JQuery} */
      this.$fieldQuickSearch = this.$filterList.find('input.field-filter-quick-search-input');
      /** @type {JQuery} */
      this.$addFilterButton = this.$el.find('button.add-filter-button');
      /** @type {JQuery} */
      this.$textFilter = this.$el.find('input.text-filter');
      this.updateAddFilterButton();
      this.$advancedFiltersPanel = this.$el.find('.advanced-filters');
      this.manageLabels();
      this.controlResetButtonVisibility();
      this.initQuickSearchUi();
      this.initTextSearchAutocomplete();
    }
    initTextSearchAutocomplete() {
      if (this.textSearchStoringDisabled) {
        return;
      }
      const autocomplete = new _autocomplete.default(this.$textFilter.get(0), {
        triggerSelectOnValidInput: false,
        focusOnSelect: true,
        onSelect: () => {
          setTimeout(() => autocomplete.hide(), 1);
        },
        lookupFunction: query => {
          return Promise.resolve(this.storedTextSearchHelper.match(query, this.autocompleteLimit).map(item => ({
            value: item
          })));
        },
        formatResult: item => {
          return $('<span>').append($('<a>').attr('data-action', 'clearStoredTextSearch').attr('role', 'button').attr('data-value', item.value).attr('title', this.translate('Remove')).html('<span class="fas fa-times fa-sm"></span>').addClass('pull-right text-soft'), $('<span>').text(item.value)).get(0).innerHTML;
        },
        beforeRender: container => {
          const $container = $(container);
          $container.addClass('text-search-suggestions');
          $container.find('a[data-action="clearStoredTextSearch"]').on('click', e => {
            e.stopPropagation();
            e.preventDefault();
            const text = e.currentTarget.getAttribute('data-value');
            this.storedTextSearchHelper.remove(text);
            autocomplete.hide();
            // 200 is hardcoded in autocomplete lib.
            setTimeout(() => this.$textFilter.focus(), 201);
          });
        }
      });
      this.once('render remove', () => autocomplete.dispose());
    }
    initQuickSearchUi() {
      this.$addFilterButton.parent().on('show.bs.dropdown', () => {
        setTimeout(() => {
          this.$fieldQuickSearch.focus();
          const width = this.$fieldQuickSearch.outerWidth();
          this.$fieldQuickSearch.css('minWidth', width);
        }, 1);
      });
      this.$addFilterButton.parent().on('hide.bs.dropdown', () => {
        this.resetFieldFilterQuickSearch();
        this.$fieldQuickSearch.css('minWidth', '');
      });
    }
    manageLabels() {
      this.$el.find('ul.dropdown-menu > li.preset-control').addClass('hidden');
      this.currentFilterLabelList = [];
      this.managePresetFilters();
      this.manageBoolFilters();
      this.$filtersLabel.html(this.currentFilterLabelList.join(' &middot; '));
    }

    /**
     * @private
     * @return {boolean}
     */
    toShowResetButton() {
      if (this.textFilter) {
        return true;
      }
      const presetName = this.presetName || null;
      const primary = this.primary;
      if (!presetName || presetName === primary) {
        if (Object.keys(this.advanced).length) {
          return true;
        }
      }
      if (this.collection.orderBy !== this.collection.defaultOrderBy || this.collection.order !== this.collection.defaultOrder) {
        return true;
      }
      return false;
    }
    controlResetButtonVisibility() {
      if (this.toShowResetButton()) {
        this.$resetButton.css('visibility', 'visible');
        return;
      }
      this.$resetButton.css('visibility', 'hidden');
    }
    managePresetFilters() {
      let presetName = this.presetName || null;
      const primary = this.primary;
      this.$el.find('ul.filter-menu a.preset span').remove();
      let filterLabel = this.translate('all', 'presetFilters', this.entityType);
      let filterStyle = 'default';
      if (!presetName && primary) {
        presetName = primary;
      }
      if (presetName && presetName !== primary) {
        this.$advancedFiltersPanel.addClass('hidden');
        let label = null;
        let style = 'default';
        let id = null;
        this.presetFilterList.forEach(item => {
          if (item.name === presetName) {
            label = item.label || false;
            style = item.style || 'default';
            id = item.id;
          }
        });
        label = label || this.translate(this.presetName, 'presetFilters', this.entityType);
        filterLabel = label;
        filterStyle = style;
        if (id) {
          this.$el.find('ul.dropdown-menu > li.divider.preset-control').removeClass('hidden');
          this.$el.find('ul.dropdown-menu > li.preset-control.remove-preset').removeClass('hidden');
        }
      } else {
        this.$advancedFiltersPanel.removeClass('hidden');
        if (Object.keys(this.advanced).length !== 0) {
          if (!this.disableSavePreset) {
            this.$el.find('ul.dropdown-menu > li.divider.preset-control').removeClass('hidden');
            this.$el.find('ul.dropdown-menu > li.preset-control.save-preset').removeClass('hidden');
            this.$el.find('ul.dropdown-menu > li.preset-control.remove-preset').addClass('hidden');
          }
        }
        if (primary) {
          const label = this.translate(primary, 'presetFilters', this.entityType);
          const style = this.getPrimaryFilterStyle();
          filterLabel = label;
          filterStyle = style;
        }
      }
      this.currentFilterLabelList.push(filterLabel);
      this.$filtersButton.removeClass('btn-default').removeClass('btn-primary').removeClass('btn-danger').removeClass('btn-success').removeClass('btn-info');
      this.$filtersButton.addClass('btn-' + filterStyle);
      presetName = presetName || '';
      this.$el.find('ul.filter-menu a.preset[data-name="' + presetName + '"]').prepend('<span class="fas fa-check pull-right"></span>');
    }
    manageBoolFilters() {
      (this.boolFilterList || []).forEach(item => {
        if (this.bool[item]) {
          const label = this.translate(item, 'boolFilters', this.entityType);
          this.currentFilterLabelList.push(label);
        }
      });
    }
    search() {
      this.fetch();
      this.updateSearch();
      this.updateCollection();
      this.controlResetButtonVisibility();
      this.storeTextSearch();
      this.isSearchedWithAdvancedFilter = this.hasAdvancedFilter();
    }
    hasAdvancedFilter() {
      return Object.keys(this.advanced).length > 0;
    }
    getFilterDataList() {
      const list = [];
      for (const field in this.advanced) {
        list.push({
          key: 'filter-' + field,
          name: field
        });
      }
      return list;
    }
    updateCollection() {
      this.collection.abortLastFetch();
      this.collection.reset();
      this.collection.where = this.searchManager.getWhere();
      this.collection.offset = 0;
      Espo.Ui.notifyWait();
      this.collection.fetch().then(() => {
        Espo.Ui.notify(false);
      });
    }
    getPresetFilterList() {
      const arr = [];
      this.presetFilterList.forEach(item => {
        if (typeof item == 'string') {
          item = {
            name: item
          };
        }
        arr.push(item);
      });
      return arr;
    }
    getPresetData() {
      let data = {};
      this.getPresetFilterList().forEach(item => {
        if (item.name === this.presetName) {
          data = Espo.Utils.clone(item.data || {});
        }
      });
      return data;
    }
    getPrimaryFilterName() {
      let primaryFilterName = null;
      this.getPresetFilterList().forEach(item => {
        if (item.name === this.presetName) {
          if (!('data' in item)) {
            primaryFilterName = item.name;
          } else if (item.primary) {
            primaryFilterName = item.primary;
          }
        }
      });
      return primaryFilterName;
    }
    getPrimaryFilterStyle() {
      let style = null;
      this.getPresetFilterList().forEach(item => {
        if (item.name === this.primary) {
          style = item.style || 'default';
        }
      });
      return style;
    }
    loadSearchData() {
      const searchData = this.searchManager.get();
      this.textFilter = searchData.textFilter;
      if ('presetName' in searchData) {
        this.presetName = searchData.presetName;
      }
      let primaryIsSet = false;
      if ('primary' in searchData) {
        this.primary = searchData.primary;
        if (!this.presetName) {
          this.presetName = this.primary;
        }
        primaryIsSet = true;
      }
      if (this.presetName) {
        this.advanced = _.extend(Espo.Utils.clone(this.getPresetData()), searchData.advanced);
        if (!primaryIsSet) {
          this.primary = this.getPrimaryFilterName();
        }
      } else {
        this.advanced = Espo.Utils.clone(searchData.advanced);
      }
      this.bool = searchData.bool;
    }

    /**
     * @callback SearchView~createFilterCallback
     * @param {module:views/search/filter} view
     */

    /**
     * @param {string} name
     * @param {Object.<string, *>} params
     * @param {SearchView~createFilterCallback} callback
     * @param {boolean} [noRender]
     */
    createFilter(name, params, callback, noRender) {
      params = params || {};
      let rendered = false;
      if (this.isRendered()) {
        rendered = true;
        this.$advancedFiltersPanel.append('<div data-name="' + name + '" class="filter filter-' + name + '" />');
      }
      this.createView('filter-' + name, 'views/search/filter', {
        name: name,
        model: this.model,
        params: params,
        selector: '.filter[data-name="' + name + '"]'
      }, view => {
        if (typeof callback === 'function') {
          view.once('after:render', () => {
            callback(view);
          });
        }
        if (rendered && !noRender) {
          view.render();
        }
        this.listenTo(view, 'change', () => {
          let toShowApply = this.isSearchedWithAdvancedFilter;
          if (!toShowApply) {
            const data = view.getView('field').fetchSearch();
            if (data) {
              toShowApply = true;
            }
          }
          if (!toShowApply) {
            return;
          }
          this.showApplyFiltersButton();
        });
        this.listenTo(view, 'search', () => {
          this.search();
          this.hideApplyFiltersButton();
        });
      });
    }
    fetch() {
      this.textFilter = (this.$el.find('input[data-name="textFilter"]').val() || '').trim();
      this.bool = {};
      this.boolFilterList.forEach(name => {
        this.bool[name] = this.$el.find('input[data-name="' + name + '"][data-role="boolFilterCheckbox"]').prop('checked');
      });
      for (const field in this.advanced) {
        const view = /** @type {module:views/fields/base} */
        this.getView('filter-' + field).getView('field');
        this.advanced[field] = view.fetchSearch();
        view.searchParams = Espo.Utils.clone(this.advanced[field] || {});
      }
    }
    updateSearch() {
      this.searchManager.set({
        textFilter: this.textFilter,
        advanced: this.advanced,
        bool: this.bool,
        presetName: this.presetName,
        primary: this.primary
      });
    }
    getFilterFieldDataList() {
      const defs = [];
      for (const i in this.fieldFilterList) {
        const field = this.fieldFilterList[i];
        const o = {
          name: field,
          checked: field in this.advanced,
          label: this.fieldFilterTranslations[field] || field
        };
        defs.push(o);
      }
      return defs;
    }
    showResetFiltersButton() {
      this.toShowApplyFiltersButton = true;
      this.toShowResetFiltersText = true;
      this.$applyFiltersContainer.removeClass('hidden');
      this.$applyFiltersContainer.find('.text-apply').addClass('hidden');
      this.$applyFiltersContainer.find('.text-reset').removeClass('hidden');
    }
    showApplyFiltersButton() {
      this.toShowApplyFiltersButton = true;
      this.toShowResetFiltersText = false;
      this.$applyFiltersContainer.removeClass('hidden');
      this.$applyFiltersContainer.find('.text-reset').addClass('hidden');
      this.$applyFiltersContainer.find('.text-apply').removeClass('hidden');
    }
    hideApplyFiltersButton() {
      this.toShowApplyFiltersButton = false;
      this.toShowResetFiltersText = false;
      this.$applyFiltersContainer.addClass('hidden');
    }
    selectPreviousPreset() {
      const list = Espo.Utils.clone(this.getPresetFilterList());
      list.unshift({
        name: null
      });
      if (list.length === 1) {
        return;
      }
      const index = list.findIndex(item => item.name === this.presetName) - 1;
      if (index < 0) {
        return;
      }
      const preset = list[index];
      this.selectPreset(preset.name);
    }
    selectNextPreset() {
      const list = Espo.Utils.clone(this.getPresetFilterList());
      list.unshift({
        name: null
      });
      if (list.length === 1) {
        return;
      }
      const index = list.findIndex(item => item.name === this.presetName) + 1;
      if (index >= list.length) {
        return;
      }
      const preset = list[index];
      this.selectPreset(preset.name);
    }

    /**
     * @private
     * @param {string} text
     */
    processFieldFilterQuickSearch(text) {
      text = text.trim();
      text = text.toLowerCase();

      /** @type {JQuery} */
      const $li = this.$filterList.find('li.filter-item');
      if (text === '') {
        $li.removeClass('search-hidden');
        return;
      }
      $li.addClass('search-hidden');
      this.fieldFilterList.forEach(field => {
        let label = this.fieldFilterTranslations[field] || field;
        label = label.toLowerCase();
        const wordList = label.split(' ');
        let matched = label.indexOf(text) === 0;
        if (!matched) {
          matched = wordList.filter(word => word.length > 3 && word.indexOf(text) === 0).length > 0;
        }
        if (matched) {
          $li.filter(`[data-name="${field}"]`).removeClass('search-hidden');
        }
      });
    }
    resetFieldFilterQuickSearch() {
      this.$fieldQuickSearch.val('');
      this.$filterList.find('li.filter-item').removeClass('search-hidden');
    }
    addFirstFieldFilter() {
      const $first = this.$filterList.find('li.filter-item:not(.hidden):not(.search-hidden)').first();
      if (!$first.length) {
        return;
      }
      const name = $first.attr('data-name');
      $first.addClass('hidden');
      this.closeAddFieldDropdown();
      this.addFilter(name);
      this.resetFieldFilterQuickSearch();
    }
    closeAddFieldDropdown() {
      // noinspection JSUnresolvedReference
      this.$addFilterButton.parent().find('[data-toggle="dropdown"]').dropdown('toggle');
    }
    storeTextSearch() {
      if (!this.textFilter) {
        return;
      }
      if (this.textSearchStoringDisabled) {
        return;
      }
      this.storedTextSearchHelper.store(this.textFilter);
    }
  }
  var _default = _exports.default = SearchView;
});

define("views/record/detail-side", ["exports", "views/record/panels-container"], function (_exports, _panelsContainer) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _panelsContainer = _interopRequireDefault(_panelsContainer);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/detail-side */

  /**
   * A detail-side record view.
   */
  class DetailSideRecordView extends _panelsContainer.default {
    /** @inheritDoc */
    template = 'record/side';

    /** @inheritDoc */
    mode = 'detail';
    readOnly = false;
    inlineEditDisabled = false;
    name = 'side';
    defaultPanel = true;

    /**
     * A panel list.
     *
     * @protected
     * @type {module:views/record/panels-container~panel[]}
     */
    panelList = [];

    /**
     * A default panel.
     *
     * @type {module:views/record/panels-container~panel}
     */
    defaultPanelDefs = {
      name: 'default',
      label: false,
      view: 'views/record/panels/default-side',
      isForm: true,
      options: {
        fieldList: [{
          name: ':assignedUser'
        }, {
          name: 'teams'
        }]
      }
    };
    init() {
      this.panelList = this.options.panelList || this.panelList;
      this.scope = this.entityType = this.options.model.entityType;
      this.recordHelper = this.options.recordHelper;
      this.panelList = Espo.Utils.clone(this.panelList);
      this.readOnlyLocked = this.options.readOnlyLocked || this.readOnly;
      this.readOnly = this.options.readOnly || this.readOnly;
      this.inlineEditDisabled = this.options.inlineEditDisabled || this.inlineEditDisabled;
      this.recordViewObject = this.options.recordViewObject;
    }

    /** @inheritDoc */
    setupPanels() {}
    setup() {
      this.type = this.mode;
      if ('type' in this.options) {
        this.type = this.options.type;
      }
      this.setupInitial();
      this.setupPanels();
      if (!this.additionalPanelsDisabled) {
        const additionalPanels = this.getMetadata().get(['clientDefs', this.scope, 'sidePanels', this.type]) || [];
        additionalPanels.forEach(panel => {
          this.panelList.push(panel);
        });
      }
      this.panelList = this.panelList.map(item => {
        if ('reference' in item && item.reference) {
          // noinspection UnnecessaryLocalVariableJS
          /** @type {module:views/record/panels-container~panel} */
          const newItem = {
            ...this.getMetadata().get(`app.clientRecord.panels.${item.reference}`),
            ...item
          };
          return newItem;
        }
        return item;
      });
      this.panelList = this.panelList.filter(p => {
        if (p.aclScope) {
          if (!this.getAcl().checkScope(p.aclScope)) {
            return;
          }
        }
        if (p.accessDataList) {
          if (!Espo.Utils.checkAccessDataList(p.accessDataList, this.getAcl(), this.getUser())) {
            return false;
          }
        }
        return true;
      });
      this.panelList = this.panelList.map(p => {
        const item = Espo.Utils.clone(p);
        if (this.recordHelper.getPanelStateParam(p.name, 'hidden') !== null) {
          item.hidden = this.recordHelper.getPanelStateParam(p.name, 'hidden');
        } else {
          this.recordHelper.setPanelStateParam(p.name, 'hidden', item.hidden || false);
        }
        return item;
      });
      this.panelList.forEach(item => {
        item.actionsViewKey = item.name + 'Actions';
      });
      this.wait(Promise.all([new Promise(resolve => {
        this.getHelper().layoutManager.get(this.scope, 'sidePanels' + Espo.Utils.upperCaseFirst(this.type), layoutData => {
          this.layoutData = layoutData;
          resolve();
        });
      }), new Promise(resolve => {
        if (!this.defaultPanel || this.getMetadata().get(['clientDefs', this.scope, 'defaultSidePanelDisabled']) || this.getMetadata().get(['clientDefs', this.scope, 'defaultSidePanel', this.type]) || this.getMetadata().get(['clientDefs', this.scope, 'defaultSidePanelFieldLists', this.type]) || this.getMetadata().get(['clientDefs', this.scope, 'defaultSidePanelFieldList'])) {
          resolve();
          return;
        }
        this.getHelper().layoutManager.get(this.scope, 'defaultSidePanel', layoutData => {
          this.defaultSidePanelLayoutData = layoutData;
          resolve();
        });
      })]).then(() => {
        if (this.defaultPanel) {
          this.setupDefaultPanel();
        }
        this.alterPanels();
        this.setupPanelsFinal();
        this.setupPanelViews();
      }));
    }

    /**
     * Set up a default panel.
     *
     * @protected
     */
    setupDefaultPanel() {
      let met = false;
      this.panelList.forEach(item => {
        if (item.name === 'default') {
          met = true;
        }
      });
      if (met) {
        return;
      }
      let defaultPanelDefs = this.getMetadata().get(['clientDefs', this.scope, 'defaultSidePanel', this.type]);
      if (defaultPanelDefs === false) {
        return;
      }
      if (this.getMetadata().get(['clientDefs', this.scope, 'defaultSidePanelDisabled'])) {
        return;
      }
      defaultPanelDefs = defaultPanelDefs || this.defaultPanelDefs;
      if (!defaultPanelDefs) {
        return;
      }
      defaultPanelDefs = Espo.Utils.cloneDeep(defaultPanelDefs);
      defaultPanelDefs.view = this.getMetadata().get(['clientDefs', this.scope, 'defaultSidePanelView']) || defaultPanelDefs.view;
      let fieldList = this.getMetadata().get(['clientDefs', this.scope, 'defaultSidePanelFieldLists', this.type]);
      if (!fieldList) {
        fieldList = this.getMetadata().get(['clientDefs', this.scope, 'defaultSidePanelFieldList']);
      }
      if (!fieldList && this.defaultSidePanelLayoutData) {
        fieldList = this.defaultSidePanelLayoutData;
      }
      if (fieldList) {
        defaultPanelDefs.options = defaultPanelDefs.options || {};
        defaultPanelDefs.options.fieldList = fieldList;
      }
      fieldList = defaultPanelDefs.options.fieldList;
      if (fieldList && fieldList.length) {
        fieldList.forEach((item, i) => {
          if (typeof item !== 'object') {
            item = {
              name: item
            };
            fieldList[i] = item;
          }
          if (item.name === ':assignedUser') {
            if (this.model.hasField('assignedUsers')) {
              item.name = 'assignedUsers';
              if (!this.model.getFieldParam('assignedUsers', 'view')) {
                item.view = 'views/fields/assigned-users';
              }
            } else if (this.model.hasField('assignedUser')) {
              item.name = 'assignedUser';
            } else {
              defaultPanelDefs.options.fieldList[i] = {};
            }
          }
        });
        const fieldDefs = this.getMetadata().get(['entityDefs', this.entityType, 'fields']) || {};
        defaultPanelDefs.options.fieldList = fieldList.filter(item => {
          const defs = fieldDefs[item.name] || {};
          return !defs.disabled;
        });
      }
      this.panelList.unshift(defaultPanelDefs);
    }
  }
  var _default = _exports.default = DetailSideRecordView;
});

define("views/record/row-actions/default", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/row-actions/actions
   */

  /**
   * @typedef {{
   *     label?: string,
   *     labelTranslation?: string,
   *     acl?: string,
   *     groupIndex?: number,
   *     name?: string,
   *     text?: string,
   *     html?: string,
   *     viewKey?: string,
   * }} module:views/record/row-actions/actions~item
   */

  /**
   * Row actions.
   */
  class DefaultRowActionsView extends _view.default {
    template = 'record/row-actions/default';

    /**
     * @private
     * @type {boolean}
     */
    menuIsShown = false;

    /**
     * @private
     * @type {module:views/record/row-actions/actions~item[]}
     */
    lastActionList;

    /**
     * @private
     * @type {Object.<string, {isAvailable: function(module:model, string)}>}
     */
    handlers;

    /**
     * @param {{
     *    acl?: {
     *        edit?: boolean,
     *        detete?: boolean,
     *    },
     *    model: import('model').default,
     *    rowActionHandlers?: Object.<string, {isAvailable: function(module:model, string)}>,
     *    additionalActionList?: string[],
     *    scope?: string,
     * } & Record} options
     */
    constructor(options) {
      super(options);
      this.options = options;
    }
    setup() {
      this.options.acl = this.options.acl || {};
      this.scope = this.options.scope || this.model.entityType;

      // noinspection JSValidateTypes
      this.handlers = this.options.rowActionHandlers || {};

      /** @type {module:views/record/row-actions/actions~item[]} */
      this.additionalActionDataList = [];
      this.setupAdditionalActions();
      const handleReRender = (/** Record */o) => {
        if (o.keepRowActions) {
          return;
        }
        if (this.menuIsShown) {
          this.once('menu-hidden', () => this.reRender());
          return;
        }
        this.reRender();
      };
      this.listenTo(this.model, 'change', (m, /** Record */o) => handleReRender(o));
      if (this.model.collection && this.model.collection.parentModel) {
        // Access to actions can be defined by a parent model.
        this.listenTo(this.model.collection.parentModel, 'sync', (m, r, /** Record */o) => {
          if (!this.lastActionList) {
            return;
          }
          setTimeout(() => {
            if (Espo.Utils.areEqual(this.lastActionList, this.getActionList())) {
              return true;
            }
            handleReRender(o);
          }, 0);
        });
      }
    }
    afterRender() {
      this.menuIsShown = false;
      const $dd = this.$el.find('button[data-toggle="dropdown"]').parent();
      let isChecked = false;
      $dd.on('show.bs.dropdown', () => {
        const $el = this.$el.closest('.list-row');
        isChecked = false;
        if ($el.hasClass('active')) {
          isChecked = true;
        }
        $el.addClass('active');
        this.menuIsShown = true;
      });
      $dd.on('hide.bs.dropdown', () => {
        if (!isChecked) {
          this.$el.closest('.list-row').removeClass('active');
        }
        this.menuIsShown = false;
        this.trigger('menu-hidden');
      });
    }

    /**
     * Get an action list.
     *
     * @return {module:views/record/list~rowAction[]}
     */
    getActionList() {
      /** @type {module:views/record/list~rowAction[]} */
      const list = [{
        action: 'quickView',
        label: 'View',
        data: {
          id: this.model.id
        },
        link: `#${this.model.entityType}/view/${this.model.id}`,
        groupIndex: 0
      }];
      if (this.checkAccess('edit')) {
        list.push({
          action: 'quickEdit',
          label: 'Edit',
          data: {
            id: this.model.id
          },
          link: `#${this.model.entityType}/edit/${this.model.id}`,
          groupIndex: 0
        });
      }
      this.getAdditionalActionList().forEach(item => list.push(item));
      if (this.checkAccess('delete')) {
        list.push({
          action: 'quickRemove',
          label: 'Remove',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        });
      }
      return list;
    }

    /**
     * Not to be overridden.
     *
     * @protected
     * @return {module:views/record/list~rowAction[]}
     */
    getAdditionalActionList() {
      const list = [];
      this.additionalActionDataList.forEach(item => {
        const handler = this.handlers[item.name];
        if (handler && !handler.isAvailable(this.model, item.name)) {
          return;
        }
        if (item.acl && item.acl !== 'read') {
          if (!this.getAcl().checkModel(this.model, item.acl)) {
            return;
          }
        }
        list.push({
          action: 'rowAction',
          text: item.text,
          data: {
            id: this.model.id,
            actualAction: item.name
          },
          groupIndex: item.groupIndex
        });
      });
      return list;
    }
    data() {
      /** @type {Array<module:views/record/row-actions/actions~item[]>} */
      const dropdownGroups = [];
      const actionList = this.getActionList();
      this.lastActionList = actionList;
      actionList.forEach(item => {
        // For bc.
        if (item === false) {
          return;
        }
        const index = (item.groupIndex === undefined ? 9999 : item.groupIndex) + 100;
        if (dropdownGroups[index] === undefined) {
          dropdownGroups[index] = [];
        }
        dropdownGroups[index].push(item);
      });
      const dropdownItemList = [];
      dropdownGroups.forEach(list => {
        list.forEach(it => dropdownItemList.push(it));
        dropdownItemList.push(false);
      });
      return {
        acl: this.options.acl,
        actionList: dropdownItemList,
        scope: this.model.entityType
      };
    }
    setupAdditionalActions() {
      const list = this.options.additionalActionList;
      if (!list) {
        return;
      }
      const defs = this.getMetadata().get(`clientDefs.${this.scope}.rowActionDefs`) || {};
      list.forEach(action => {
        /** @type {{label?: string, labelTranslation?: string, acl?: string, groupIndex?: number}} */
        const itemDefs = defs[action] || {};
        const text = itemDefs.labelTranslation ? this.getLanguage().translatePath(itemDefs.labelTranslation) : this.getLanguage().translate(itemDefs.label, 'labels', this.model.entityType);
        this.additionalActionDataList.push({
          name: action,
          acl: itemDefs.acl,
          text: text,
          groupIndex: itemDefs.groupIndex
        });
      });
    }

    /**
     * @protected
     * @param {string} action
     * @retyrn {boolean}
     * @since 9.0.0
     */
    checkAccess(action) {
      if (typeof this.options.acl[action] === 'boolean') {
        return this.options.acl[action];
      }
      return this.getAcl().checkModel(this.model, action);
    }
  }
  var _default = _exports.default = DefaultRowActionsView;
});

define("views/record/panels/side", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/panels/side */

  /**
   * @typedef {Object} module:views/record/panels/side~defs
   * @property [buttonList]
   * @property [actionList]
   * @property [fieldList]
   * @property [tabNumber] For internal purposes.
   * @property [isForm] Is form.
   */

  /**
   * A side panel.
   */
  class SidePanelView extends _view.default {
    template = 'record/panels/side';

    /**
     * A field defs.
     *
     * @typedef module:views/record/panels/side~field
     *
     * @property {string} name
     * @property {string} [labelText] A translated label text.
     * @property {string} [view] A view name.
     * @property {boolean} [isAdditional]
     * @property {boolean} [readOnly]
     * @property {Object.<string,*>} [options] Options.
     * @property {string} [viewKey] Not to be set. For internal purposes.
     */

    /**
     * A field list.
     *
     * @protected
     * @type {module:views/record/panels/side~field[]}
     */
    fieldList = null;

    /**
     * A mode.
     *
     * @protected
     * @type {'list'|'detail'|'edit'}
     */
    mode = 'detail';

    /**
     * @protected
     * @type {module:views/record/panels-container~action[]}
     */
    actionList;

    /**
     * @protected
     * @type {Array<module:views/record/panels-container~action|false>}
     */
    buttonList;

    /**
     * Read-only.
     *
     * @protected
     */
    readOnly = false;

    /**
     * Disable inline edit.
     *
     * @protected
     */
    inlineEditDisabled = false;

    /**
     * Disabled.
     *
     * @internal
     */
    disabled = false;

    /**
     * @protected
     * @type {module:views/record/panels/side~defs}
     */
    defs;
    events = {
      /** @this SidePanelView */
      'click .action': function (e) {
        Espo.Utils.handleAction(this, e.originalEvent, e.currentTarget, {
          actionItems: [...this.buttonList, ...this.actionList],
          className: 'panel-action'
        });
      }
    };
    data() {
      return {
        fieldList: this.getFieldList(),
        hiddenFields: this.recordHelper.getHiddenFields()
      };
    }
    init() {
      this.panelName = this.options.panelName;
      this.defs = this.options.defs || {};
      this.recordHelper = this.options.recordHelper;
      if ('disabled' in this.options) {
        this.disabled = this.options.disabled;
      }
      this.buttonList = Espo.Utils.clone(this.defs.buttonList || this.buttonList || []);
      this.actionList = Espo.Utils.clone(this.defs.actionList || this.actionList || []);
      this.fieldList = this.options.fieldList || this.fieldList || this.defs.fieldList || [];
      this.mode = this.options.mode || this.mode;
      this.readOnlyLocked = this.options.readOnlyLocked || this.readOnly;
      this.readOnly = this.readOnly || this.options.readOnly;
      this.inlineEditDisabled = this.inlineEditDisabled || this.options.inlineEditDisabled;

      /** @type {import('views/record/base').default} */
      this.recordViewObject = this.options.recordViewObject;
    }
    setup() {
      this.setupFields();
      this.fieldList = this.fieldList.map(d => {
        let item = d;
        if (typeof item !== 'object') {
          item = {
            name: item,
            viewKey: item + 'Field'
          };
        }
        item = Espo.Utils.clone(item);
        item.viewKey = item.name + 'Field';
        item.label = item.label || item.name;
        if (this.recordHelper.getFieldStateParam(item.name, 'hidden') !== null) {
          item.hidden = this.recordHelper.getFieldStateParam(item.name, 'hidden');
        } else {
          this.recordHelper.setFieldStateParam(item.name, 'hidden', item.hidden || false);
        }
        return item;
      });
      this.fieldList = this.fieldList.filter(item => {
        if (!item.name) {
          return;
        }
        if (!item.isAdditional) {
          if (!(item.name in ((this.model.defs || {}).fields || {}))) return;
        }
        return true;
      });
      this.createFields();
    }
    afterRender() {
      if (this.$el.children().length === 0 && this.defs.isForm) {
        this.$el.parent().addClass('hidden');
      }
    }

    /**
     * Set up fields.
     *
     * @protected
     */
    setupFields() {}

    /**
     * Create a field view.
     *
     * @protected
     * @param {string} field A field name.
     * @param {string|null} [viewName] A view name/path.
     * @param {Object<string,*>} [params] Field params.
     * @param {'detail'|'edit'|'list'|null} [mode='edit'] A mode.
     * @param {boolean} [readOnly] Read-only.
     * @param {Object<string,*>} [options] View options.
     */
    createField(field, viewName, params, mode, readOnly, options) {
      const type = this.model.getFieldType(field) || 'base';
      viewName = viewName || this.model.getFieldParam(field, 'view') || this.getFieldManager().getViewName(type);
      const o = {
        model: this.model,
        selector: '.field[data-name="' + field + '"]',
        defs: {
          name: field,
          params: params || {}
        },
        mode: mode || this.mode,
        dataObject: this.options.dataObject
      };
      if (options) {
        for (const param in options) {
          o[param] = options[param];
        }
      }
      let readOnlyLocked = this.readOnlyLocked;
      if (this.readOnly) {
        o.readOnly = true;
      } else {
        if (readOnly !== null) {
          o.readOnly = readOnly;
        }
      }
      if (readOnly) {
        readOnlyLocked = true;
      }
      if (this.inlineEditDisabled) {
        o.inlineEditDisabled = true;
      }
      if (this.recordHelper.getFieldStateParam(field, 'hidden')) {
        o.disabled = true;
      }
      if (this.recordHelper.getFieldStateParam(field, 'hiddenLocked')) {
        o.disabledLocked = true;
      }
      if (this.recordHelper.getFieldStateParam(field, 'readOnly')) {
        o.readOnly = true;
      }
      if (this.recordHelper.getFieldStateParam(field, 'required') !== null) {
        o.defs.params.required = this.recordHelper.getFieldStateParam(field, 'required');
      }
      if (!readOnlyLocked && this.recordHelper.getFieldStateParam(field, 'readOnlyLocked')) {
        readOnlyLocked = true;
      }
      if (readOnlyLocked) {
        o.readOnlyLocked = readOnlyLocked;
      }
      if (this.recordHelper.hasFieldOptionList(field)) {
        o.customOptionList = this.recordHelper.getFieldOptionList(field);
      }
      if (this.recordViewObject) {
        o.validateCallback = () => this.recordViewObject.validateField(field);
      }
      o.recordHelper = this.recordHelper;
      const viewKey = field + 'Field';
      this.createView(viewKey, viewName, o);
    }

    /**
     * @private
     */
    createFields() {
      this.getFieldList().forEach(item => {
        let view = null;
        let field;
        let readOnly = null;
        if (typeof item === 'object') {
          field = item.name;
          view = item.view;
          if ('readOnly' in item) {
            readOnly = item.readOnly;
          }
        } else {
          field = item;
        }
        if (!item.isAdditional && !(field in this.model.defs.fields)) {
          return;
        }
        readOnly = readOnly || false;
        this.createField(field, view, null, null, readOnly, item.options);
      });
    }

    /**
     * Get field views.
     *
     * @return {Object.<string, module:views/fields/base>}
     */
    getFieldViews() {
      const fields = {};
      this.getFieldList().forEach(item => {
        if (this.hasView(item.viewKey)) {
          fields[item.name] = this.getView(item.viewKey);
        }
      });
      return fields;
    }

    /**
     * Get a field list.
     *
     * @return {module:views/record/panels/side~field[]}
     */
    getFieldList() {
      return this.fieldList.map(item => {
        if (typeof item !== 'object') {
          return {
            name: item
          };
        }
        return item;
      });
    }

    /**
     * @return {module:views/record/panels-container~action[]}
     */
    getActionList() {
      return this.actionList || [];
    }

    /**
     * @return {module:views/record/panels-container~button[]}
     */
    getButtonList() {
      return this.buttonList || [];
    }

    /**
     * A `refresh` action.
     */
    async actionRefresh() {
      Espo.Ui.notifyWait();
      await this.model.fetch();
      Espo.Ui.notify();
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Is tab-hidden.
     *
     * @return {boolean}
     */
    isTabHidden() {
      if (this.defs.tabNumber === -1 || typeof this.defs.tabNumber === 'undefined') {
        return false;
      }
      const parentView = this.getParentView();
      if (!parentView) {
        return this.defs.tabNumber > 0;
      }

      // noinspection JSUnresolvedReference
      if (parentView && parentView.hasTabs) {
        return parentView.currentTab !== this.defs.tabNumber;
      }
      return false;
    }
  }
  var _default = _exports.default = SidePanelView;
});

define("views/record/panels/relationship", ["exports", "views/record/panels/bottom", "search-manager", "helpers/record-modal", "helpers/record/create-related", "helpers/record/select-related"], function (_exports, _bottom, _searchManager, _recordModal, _createRelated, _selectRelated) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _bottom = _interopRequireDefault(_bottom);
  _searchManager = _interopRequireDefault(_searchManager);
  _recordModal = _interopRequireDefault(_recordModal);
  _createRelated = _interopRequireDefault(_createRelated);
  _selectRelated = _interopRequireDefault(_selectRelated);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2024 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/panels/relationship */

  /**
   * A relationship panel.
   *
   * @property {Object} defs
   */
  class RelationshipPanelView extends _bottom.default {
    /** @inheritDoc */
    template = 'record/panels/relationship';

    /**
     * A row-actions view.
     *
     * @protected
     */
    rowActionsView = 'views/record/row-actions/relationship';

    /**
     * An API URL.
     *
     * @protected
     * @type {string|null}
     */
    url = null;

    /**
     * @type {string}
     * @deprecated Use `entityType`.
     */
    scope;

    /**
     * An entity type.
     * @type {string}
     */
    entityType;

    /**
     * Read-only.
     */
    readOnly = false;

    /**
     * Fetch a collection on a model 'after:relate' event.
     *
     * @protected
     */
    fetchOnModelAfterRelate = false;

    /**
     * @protected
     */
    noCreateScopeList = ['User', 'Team', 'Role', 'Portal'];

    /**
     * @private
     */
    recordsPerPage = null;

    /**
     * @protected
     */
    viewModalView = null;

    /**
     * @protected
     */
    listLayoutName;
    setup() {
      super.setup();
      this.link = this.link || this.defs.link || this.panelName;
      if (!this.link) {
        throw new Error(`No link or panelName.`);
      }

      // noinspection JSDeprecatedSymbols
      if (!this.scope && !this.entityType) {
        if (!this.model) {
          throw new Error(`No model passed.`);
        }
        if (!(this.link in this.model.defs.links)) {
          throw new Error(`Link '${this.link}' is not defined in model '${this.model.entityType}'.`);
        }
      }

      // noinspection JSDeprecatedSymbols
      if (this.scope && !this.entityType) {
        // For backward compatibility.
        // noinspection JSDeprecatedSymbols
        this.entityType = this.scope;
      }
      this.entityType = this.entityType || this.model.defs.links[this.link].entity;

      // For backward compatibility.
      // noinspection JSDeprecatedSymbols
      this.scope = this.entityType;

      /** @type {Record} */
      const linkDefs = this.getMetadata().get(`entityDefs.${this.model.entityType}.links.${this.link}`) || {};
      const url = this.url = this.url || `${this.model.entityType}/${this.model.id}/${this.link}`;
      if (!('create' in this.defs)) {
        this.defs.create = true;
      }
      if (!('select' in this.defs)) {
        this.defs.select = true;
      }
      if (!('view' in this.defs)) {
        this.defs.view = true;
      }
      if (linkDefs.readOnly) {
        let hasCreate = false;
        if (this.entityType && linkDefs.foreign) {
          const foreign = linkDefs.foreign;

          /** @type {Record} */
          const foreignLinkDefs = this.getMetadata().get(`entityDefs.${this.entityType}.links.${foreign}`) || {};
          if (foreignLinkDefs.type === 'belongsTo') {
            hasCreate = true;
          } else if (foreignLinkDefs.type === 'hasMany' && this.getMetadata().get(`entityDefs.${this.entityType}.fields.${foreign}.type`) === 'linkMultiple') {
            hasCreate = true;
          }
        }
        if (!hasCreate) {
          this.defs.create = false;
        }
        this.defs.select = false;
      }
      this.filterList = this.defs.filterList || this.filterList || null;
      if (this.filterList && this.filterList.length) {
        this.filter = this.getStoredFilter() || this.filterList[0];
        if (this.filter === 'all') {
          this.filter = null;
        }
      }
      this.setupCreateAvailability();
      this.setupTitle();
      if (this.defs.createDisabled) {
        this.defs.create = false;
      }
      if (this.defs.selectDisabled) {
        this.defs.select = false;
      }
      if (this.defs.viewDisabled) {
        this.defs.view = false;
      }
      let hasCreate = false;
      if (this.defs.create) {
        if (this.getAcl().check(this.entityType, 'create') && !~this.noCreateScopeList.indexOf(this.entityType)) {
          this.buttonList.push({
            title: 'Create',
            action: this.defs.createAction || 'createRelated',
            link: this.link,
            html: '<span class="fas fa-plus"></span>',
            data: {
              link: this.link
            },
            acl: this.defs.createRequiredAccess || null
          });
          hasCreate = true;
        }
      }
      if (this.defs.select) {
        const data = {
          link: this.link
        };
        if (this.defs.selectPrimaryFilterName) {
          data.primaryFilterName = this.defs.selectPrimaryFilterName;
        }
        if (this.defs.selectBoolFilterList) {
          data.boolFilterList = this.defs.selectBoolFilterList;
        }
        data.massSelect = this.defs.massSelect;
        data.createButton = hasCreate;
        this.actionList.unshift({
          label: 'Select',
          action: this.defs.selectAction || 'selectRelated',
          data: data,
          acl: this.defs.selectRequiredAccess || 'edit'
        });
      }
      if (this.defs.view) {
        this.actionList.unshift({
          label: 'View List',
          action: this.defs.viewAction || 'viewRelatedList'
        });
      }
      this.setupActions();
      let layoutName = 'listSmall';
      this.setupListLayout();
      if (this.listLayoutName) {
        layoutName = this.listLayoutName;
      }
      let listLayout = null;
      const layout = this.defs.layout || null;
      if (layout) {
        if (typeof layout === 'string') {
          layoutName = layout;
        } else {
          layoutName = 'listRelationshipCustom';
          listLayout = layout;
        }
      }
      this.listLayout = listLayout;
      this.layoutName = layoutName;
      this.setupSorting();
      this.wait(true);
      this.getCollectionFactory().create(this.entityType, collection => {
        collection.maxSize = this.recordsPerPage || this.getConfig().get('recordsPerPageSmall') || 5;
        if (this.defs.filters) {
          const searchManager = new _searchManager.default(collection);
          searchManager.setAdvanced(this.defs.filters);
          collection.where = searchManager.getWhere();
        }
        if (this.defs.primaryFilter) {
          this.filter = this.defs.primaryFilter;
        }
        collection.url = collection.urlRoot = url;
        if (this.defaultOrderBy) {
          collection.setOrder(this.defaultOrderBy, this.defaultOrder || false, true);
        }
        this.collection = collection;
        collection.parentModel = this.model;
        this.setFilter(this.filter);
        if (this.fetchOnModelAfterRelate) {
          this.listenTo(this.model, 'after:relate', () => collection.fetch());
        }
        this.listenTo(this.model, `update-related:${this.link} update-all`, () => collection.fetch());
        this.listenTo(this.collection, 'change', () => {
          this.model.trigger(`after:related-change:${this.link}`);
        });
        if (this.defs.syncWithModel) {
          this.listenTo(this.model, 'sync', (m, a, o) => {
            if (!o.patch && !o.highlight) {
              // Skip if not save and not web-socket update.
              return;
            }
            if (this.collection.lastSyncPromise && this.collection.lastSyncPromise.getReadyState() < 4) {
              return;
            }
            this.collection.fetch();
          });
        }
        const viewName = this.defs.recordListView || this.getMetadata().get(['clientDefs', this.entityType, 'recordViews', 'listRelated']) || this.getMetadata().get(['clientDefs', this.entityType, 'recordViews', 'list']) || 'views/record/list';
        this.listViewName = viewName;
        this.rowActionsView = this.defs.readOnly ? false : this.defs.rowActionsView || this.rowActionsView;
        this.once('after:render', () => {
          this.createView('list', viewName, {
            collection: collection,
            layoutName: layoutName,
            listLayout: listLayout,
            checkboxes: false,
            rowActionsView: this.rowActionsView,
            buttonsDisabled: true,
            selector: '.list-container',
            skipBuildRows: true,
            rowActionsOptions: {
              unlinkDisabled: this.defs.unlinkDisabled,
              editDisabled: this.defs.editDisabled,
              removeDisabled: this.defs.removeDisabled
            },
            displayTotalCount: false,
            additionalRowActionList: this.defs.rowActionList
          }, view => {
            view.getSelectAttributeList(selectAttributeList => {
              if (selectAttributeList) {
                if (this.defs.mandatoryAttributeList) {
                  selectAttributeList = [...selectAttributeList, ...this.defs.mandatoryAttributeList];
                  selectAttributeList = selectAttributeList.filter((it, i) => selectAttributeList.indexOf(it) === i);
                }
                collection.data.select = selectAttributeList.join(',');
              }
              if (!this.defs.hidden) {
                collection.fetch();
                return;
              }
              this.once('show', () => collection.fetch());
            });
            if (this.defs.syncBackWithModel) {
              this.listenTo(view, 'after:save after:delete', () => this.processSyncBack());
            }
          });
        });
        this.wait(false);
      });
      this.setupFilterActions();
      this.setupLast();
    }

    /**
     * Set up lastly.
     *
     * @protected
     */
    setupLast() {}

    /**
     * Set up title.
     *
     * @protected
     */
    setupTitle() {
      this.title = this.title || this.translate(this.link, 'links', this.model.entityType);
      let iconHtml = '';
      if (!this.getConfig().get('scopeColorsDisabled')) {
        iconHtml = this.getHelper().getScopeColorIconHtml(this.entityType);
      }
      this.titleHtml = this.title;
      if (this.defs.label) {
        this.titleHtml = iconHtml + this.translate(this.defs.label, 'labels', this.entityType);
      } else {
        this.titleHtml = iconHtml + this.title;
      }
      if (this.filter && this.filter !== 'all') {
        this.titleHtml += ' &middot; ' + this.translateFilter(this.filter);
      }
    }

    /**
     * Set up sorting.
     *
     * @protected
     */
    setupSorting() {
      let orderBy = this.defs.orderBy || this.defs.sortBy || this.orderBy;
      let order = this.defs.orderDirection || this.orderDirection || this.order;
      if ('asc' in this.defs) {
        // @todo Remove.
        order = this.defs.asc ? 'asc' : 'desc';
      }
      if (!orderBy) {
        orderBy = this.getMetadata().get(['entityDefs', this.entityType, 'collection', 'orderBy']);
        order = this.getMetadata().get(['entityDefs', this.entityType, 'collection', 'order']);
      }
      if (orderBy && !order) {
        order = 'asc';
      }
      this.defaultOrderBy = orderBy;
      this.defaultOrder = order;
    }

    /**
     * Set up a list layout.
     *
     * @protected
     */
    setupListLayout() {}

    /**
     * Set up actions.
     *
     * @protected
     */
    setupActions() {}

    /**
     * Set up filter actions.
     *
     * @protected
     */
    setupFilterActions() {
      if (!(this.filterList && this.filterList.length)) {
        return;
      }
      this.actionList.push(false);
      this.filterList.slice(0).forEach(item => {
        let selected;
        selected = item === 'all' ? !this.filter : item === this.filter;
        const label = this.translateFilter(item);
        const $item = $('<div>').append($('<span>').addClass('check-icon fas fa-check pull-right').addClass(!selected ? 'hidden' : '')).append($('<div>').text(label));
        this.actionList.push({
          action: 'selectFilter',
          html: $item.get(0).innerHTML,
          data: {
            name: item
          }
        });
      });
    }

    /**
     * Translate a filter.
     *
     * @param {string} name A name.
     * @return {string}
     */
    translateFilter(name) {
      return this.translate(name, 'presetFilters', this.entityType);
    }

    /**
     * @protected
     */
    getStoredFilter() {
      const key = 'panelFilter' + this.model.entityType + '-' + (this.panelName || this.name);
      return this.getStorage().get('state', key) || null;
    }

    /**
     * @private
     */
    storeFilter(filter) {
      const key = 'panelFilter' + this.model.entityType + '-' + (this.panelName || this.name);
      if (filter) {
        this.getStorage().set('state', key, filter);
      } else {
        this.getStorage().clear('state', key);
      }
    }

    /**
     * Set a filter.
     *
     * @param {string} filter A filter.
     */
    setFilter(filter) {
      this.filter = filter;
      this.collection.data.primaryFilter = null;
      if (filter && filter !== 'all') {
        this.collection.data.primaryFilter = filter;
      }
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * A `select-filter` action.
     *
     * @protected
     */
    actionSelectFilter(data) {
      const filter = data.name;
      let filterInternal = filter;
      if (filter === 'all') {
        filterInternal = false;
      }
      this.storeFilter(filterInternal);
      this.setFilter(filterInternal);
      this.filterList.forEach(item => {
        const $el = this.$el.closest('.panel').find('[data-name="' + item + '"] span');
        if (item === filter) {
          $el.removeClass('hidden');
        } else {
          $el.addClass('hidden');
        }
      });
      this.collection.abortLastFetch();
      this.collection.reset();
      const listView = this.getView('list');
      if (listView && listView.$el) {
        const height = listView.$el.parent().get(0).clientHeight;
        listView.$el.empty();
        if (height) {
          listView.$el.parent().css('height', height + 'px');
        }
      }
      this.collection.fetch().then(() => {
        listView.$el.parent().css('height', '');
      });
      this.setupTitle();
      if (this.isRendered()) {
        this.$el.closest('.panel').find('> .panel-heading > .panel-title > span').html(this.titleHtml);
      }
    }

    /**
     * A `refresh` action.
     *
     * @protected
     */
    async actionRefresh() {
      Espo.Ui.notifyWait();
      await this.collection.fetch();
      Espo.Ui.notify();
    }

    /**
     * A `view-related-list` action.
     *
     * @protected
     * @param {{
     *     scope?: string,
     *     entityType: string,
     *     title?: string,
     *     url?: string,
     *     viewOptions?: Record,
     * }} data
     */
    actionViewRelatedList(data) {
      const entityType = data.scope || data.entityType || this.entityType;
      const viewName = this.getMetadata().get(`clientDefs.${this.model.entityType}.relationshipPanels.${this.name}.viewModalView`) || this.getMetadata().get(`clientDefs.${entityType}.modalViews.relatedList`) || this.viewModalView || 'views/modals/related-list';
      let filter = this.filter;
      if (this.relatedListFiltersDisabled) {
        filter = null;
      }
      const options = {
        model: this.model,
        panelName: this.panelName,
        link: this.link,
        entityType: entityType,
        defs: this.defs,
        title: data.title || this.title,
        filterList: this.filterList,
        filter: filter,
        layoutName: this.layoutName,
        defaultOrder: this.defaultOrder,
        defaultOrderBy: this.defaultOrderBy,
        url: data.url || this.url,
        listViewName: this.listViewName,
        createDisabled: !this.isCreateAvailable(entityType),
        selectDisabled: !this.isSelectAvailable(entityType),
        rowActionsView: this.rowActionsView,
        panelCollection: this.collection,
        filtersDisabled: this.relatedListFiltersDisabled
      };
      if (data.viewOptions) {
        for (const item in data.viewOptions) {
          options[item] = data.viewOptions[item];
        }
      }
      Espo.Ui.notifyWait();
      this.createView('modalRelatedList', viewName, options, view => {
        Espo.Ui.notify(false);
        view.render();
        this.listenTo(view, 'action', (event, element) => {
          Espo.Utils.handleAction(this, event, element);
        });
        this.listenToOnce(view, 'close', () => {
          this.clearView('modalRelatedList');
        });
      });
    }

    /**
     * Is create available.
     *
     * @protected
     * @param {string} scope A scope (entity type).
     * @return {boolean};
     */
    isCreateAvailable(scope) {
      return !!this.defs.create;
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * Is select available.
     *
     * @protected
     * @param {string} scope A scope (entity type).
     * @return {boolean};
     */
    isSelectAvailable(scope) {
      return !!this.defs.select;
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * A `view-related` action.
     *
     * @protected
     */
    actionViewRelated(data) {
      const id = data.id;
      const model = this.collection.get(id);
      if (!model) {
        return;
      }
      const scope = model.entityType;
      const helper = new _recordModal.default();
      helper.showDetail(this, {
        entityType: scope,
        id: id,
        model: model
      }).then(view => {
        // @todo Move to afterSave?
        this.listenTo(view, 'after:save', () => {
          this.collection.fetch();
          this.processSyncBack();
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * An `edit-related` action.
     *
     * @protected
     */
    actionEditRelated(data) {
      const id = data.id;
      const entityType = this.collection.get(id).entityType;
      const helper = new _recordModal.default();
      helper.showEdit(this, {
        entityType: entityType,
        id: id,
        afterSave: () => {
          this.collection.fetch();
          this.processSyncBack();
        }
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * An `unlink-related` action.
     *
     * @protected
     */
    actionUnlinkRelated(data) {
      const id = data.id;
      this.confirm({
        message: this.translate('unlinkRecordConfirmation', 'messages'),
        confirmText: this.translate('Unlink')
      }, () => {
        Espo.Ui.notifyWait();
        Espo.Ajax.deleteRequest(this.collection.url, {
          id: id
        }).then(() => {
          Espo.Ui.success(this.translate('Unlinked'));
          this.collection.fetch();
          this.model.trigger('after:unrelate');
          this.model.trigger('after:unrelate:' + this.link);
          this.processSyncBack();
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * A `remove-related` action.
     *
     * @protected
     * @param {{id?: string}} [data]
     * @return {Promise<void>}
     */
    async actionRemoveRelated(data) {
      const id = data.id;
      const model = this.collection.get(id);
      const index = this.collection.indexOf(model);
      if (!model) {
        throw new Error("No model.");
      }
      await this.confirm({
        message: this.translate('removeRecordConfirmation', 'messages'),
        confirmText: this.translate('Remove')
      });
      Espo.Ui.notifyWait();
      try {
        await model.destroy({
          wait: true
        });
      } catch (e) {
        if (!this.collection.models.includes(model)) {
          this.collection.add(model, {
            at: index
          });
        }
        return;
      }
      Espo.Ui.success(this.translate('Removed'));
      this.collection.fetch().then(() => {});
      this.model.trigger('after:unrelate');
      this.model.trigger(`after:unrelate:${this.link}`);
      this.processSyncBack();
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * An `unlink-all-related` action.
     *
     * @protected
     */
    actionUnlinkAllRelated(data) {
      this.confirm(this.translate('unlinkAllConfirmation', 'messages'), () => {
        Espo.Ui.notifyWait();
        Espo.Ajax.postRequest(this.model.entityType + '/action/unlinkAll', {
          link: data.link,
          id: this.model.id
        }).then(() => {
          Espo.Ui.success(this.translate('Unlinked'));
          this.collection.fetch();
          this.model.trigger('after:unrelate');
          this.model.trigger('after:unrelate:' + this.link);
          this.processSyncBack();
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @protected
     * @since 8.4.0
     */
    actionCreateRelated() {
      const helper = new _createRelated.default(this);
      helper.process(this.model, this.link, {
        afterSave: () => {
          this.processSyncBack();
        }
      });
    }

    /**
     * @protected
     */
    processSyncBack() {
      if (!this.defs.syncBackWithModel || this.getHelper().webSocketManager) {
        return;
      }
      this.model.fetch({
        highlight: true
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @protected
     * @since 8.4.0
     */
    actionSelectRelated() {
      const helper = new _selectRelated.default(this);
      helper.process(this.model, this.link, {
        hasCreate: this.defs.create,
        onCreate: () => this.actionCreateRelated()
      });
    }

    /**
     * @private
     */
    setupCreateAvailability() {
      if (!this.link || !this.entityType || !this.model) {
        return;
      }

      /** @type {module:model} */
      const model = this.model;
      const entityType = model.getLinkParam(this.link, 'entity');
      const foreignLink = model.getLinkParam(this.link, 'foreign');
      if (!entityType || !foreignLink) {
        return;
      }
      const readOnly = this.getMetadata().get(`entityDefs.${entityType}.fields.${foreignLink}.readOnly`);
      if (!readOnly) {
        return;
      }
      this.defs.create = false;
    }
  }
  var _default = _exports.default = RelationshipPanelView;
});

define("views/note/fields/post", ["exports", "views/fields/text", "lib!jquery-textcomplete"], function (_exports, _text, _libJqueryTextcomplete) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _text = _interopRequireDefault(_text);
  _libJqueryTextcomplete = _interopRequireDefault(_libJqueryTextcomplete);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection ES6UnusedImports

  class NotePostFieldView extends _text.default {
    setup() {
      super.setup();
      this.insertedImagesData = {};
      this.addHandler('paste', 'textarea', (/** ClipboardEvent */event) => this.handlePaste(event));
    }

    /**
     * @return {HTMLTextAreaElement}
     */
    getTextAreaElement() {
      return this.$textarea.get(0);
    }

    /**
     * @private
     * @param {ClipboardEvent} event
     */
    handlePaste(event) {
      if (!event.clipboardData) {
        return;
      }
      let text = event.clipboardData.getData('text/plain');
      if (!text) {
        return;
      }
      text = text.trim();
      if (!text) {
        return;
      }
      this.handlePastedText(text);
    }
    afterRenderEdit() {
      const placeholderText = this.options.placeholderText || this.translate('writeMessage', 'messages', 'Note');
      this.$element.attr('placeholder', placeholderText);
      this.$textarea = this.$element;
      const $textarea = this.$textarea;
      $textarea.off('drop');
      $textarea.off('dragover');
      $textarea.off('dragleave');
      this.$textarea.on('drop', e => {
        e.preventDefault();
        e.stopPropagation();
        e = e.originalEvent;
        if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length) {
          this.trigger('add-files', e.dataTransfer.files);
        }
        this.$textarea.attr('placeholder', originalPlaceholderText);
      });
      const originalPlaceholderText = this.$textarea.attr('placeholder');
      this.$textarea.on('dragover', e => {
        e.preventDefault();
        this.$textarea.attr('placeholder', this.translate('dropToAttach', 'messages'));
      });
      this.$textarea.on('dragleave', e => {
        e.preventDefault();
        this.$textarea.attr('placeholder', originalPlaceholderText);
      });
      this.initMentions();
    }
    initMentions() {
      const mentionPermissionLevel = this.getAcl().getPermissionLevel('mention');
      if (mentionPermissionLevel === 'no' /*|| this.model.isNew()*/) {
        return;
      }
      const maxSize = this.getConfig().get('recordsPerPage');
      const buildUserListUrl = term => {
        let url = `User?` + `${$.param({
          q: term
        })}` + `&${$.param({
          primaryFilter: 'active'
        })}` + `&orderBy=name` + `&maxSize=${maxSize}` + `&select=id,name,userName`;
        if (mentionPermissionLevel === 'team') {
          url += '&' + $.param({
            boolFilterList: ['onlyMyTeam']
          });
        }
        return url;
      };

      // noinspection JSUnresolvedReference
      this.$element.textcomplete([{
        match: /(^|\s)@(\w[\w@.-]*)$/,
        index: 2,
        // @todo Revise.
        search: (term, callback) => {
          if (term.length === 0) {
            callback([]);
            return;
          }
          Espo.Ajax.getRequest(buildUserListUrl(term)).then(data => callback(data.list));
        },
        template: mention => {
          return this.getHelper().getAvatarHtml(mention.id, 'medium', 16, 'avatar-link') + this.getHelper().escapeString(mention.name) + ' <span class="text-muted">@' + this.getHelper().escapeString(mention.userName) + '</span>';
        },
        replace: o => {
          return '$1@' + o.userName + '';
        }
      }], {
        zIndex: 1100
      });
      this.on('remove', () => {
        if (this.$element.length) {
          this.$element.textcomplete('destroy');
        }
      });
    }
    validateRequired() {
      if (this.isRequired()) {
        if ((this.model.get('attachmentsIds') || []).length) {
          return false;
        }
      }
      return super.validateRequired();
    }

    /**
     * @private
     * @param {string} text
     */
    handlePastedText(text) {
      // noinspection RegExpRedundantEscape,RegExpSimplifiable
      if (!/^http(s){0,1}\:\/\//.test(text)) {
        return;
      }
      const field = 'attachments';
      const imageExtensionList = ['jpg', 'jpeg', 'png', 'gif'];
      const regExpString = '.+\\.(' + imageExtensionList.join('|') + ')(/?.*){0,1}$';
      const regExp = new RegExp(regExpString, 'i');
      let url = text;
      const siteUrl = this.getConfig().get('siteUrl').replace(/\/$/, '');
      const setIds = /** @type {string[]} */this.model.attributes[`${field}Ids`] || [];
      if (regExp.test(text)) {
        const insertedId = this.insertedImagesData[url];
        if (insertedId && setIds.includes(insertedId)) {
          return;
        }
        Espo.Ajax.postRequest('Attachment/fromImageUrl', {
          url: url,
          parentType: this.model.entityType,
          field: field
        }).then(/** {id: string, name: string, type: string} */result => {
          const ids = [...(this.model.attributes[`${field}Ids`] || [])];
          const names = {
            ...this.model.attributes[`${field}Names`]
          };
          const types = {
            ...this.model.attributes[`${field}Types`]
          };
          ids.push(result.id);
          names[result.id] = result.name;
          types[result.id] = result.type;
          this.insertedImagesData[url] = result.id;
          this.model.set({
            [`${field}Ids`]: ids,
            [`${field}Names`]: names,
            [`${field}Types`]: types
          });
        }).catch(xhr => {
          xhr.errorIsHandled = true;
        });
        return;
      }

      // noinspection RegExpRedundantEscape
      if (/\?entryPoint\=image\&/.test(text) && text.indexOf(siteUrl) === 0) {
        // noinspection RegExpRedundantEscape,RegExpSimplifiable
        url = text.replace(/[\&]{0,1}size\=[a-z\-]*/, '');

        // noinspection RegExpRedundantEscape,RegExpSimplifiable
        const match = /\&{0,1}id\=([a-z0-9A-Z]*)/g.exec(text);
        if (match.length !== 2) {
          return;
        }
        const id = match[1];
        if (setIds.includes(id)) {
          return;
        }
        const insertedId = this.insertedImagesData[id];
        if (insertedId && setIds.includes(insertedId)) {
          return;
        }
        Espo.Ajax.postRequest(`Attachment/copy/${id}`, {
          parentType: this.model.entityType,
          field: field
        }).then(/** {id: string, name: string, type: string} */result => {
          const ids = [...(this.model.attributes[`${field}Ids`] || [])];
          const names = {
            ...this.model.attributes[`${field}Names`]
          };
          const types = {
            ...this.model.attributes[`${field}Types`]
          };
          ids.push(result.id);
          names[result.id] = result.name;
          types[result.id] = result.type;
          this.insertedImagesData[id] = result.id;
          this.model.set({
            [`${field}Ids`]: ids,
            [`${field}Names`]: names,
            [`${field}Types`]: types
          });
        }).catch(xhr => {
          xhr.errorIsHandled = true;
        });
      }
    }
  }
  var _default = _exports.default = NotePostFieldView;
});

define("views/fields/user", ["exports", "views/fields/link", "ui/autocomplete"], function (_exports, _link, _autocomplete) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _link = _interopRequireDefault(_link);
  _autocomplete = _interopRequireDefault(_autocomplete);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserFieldView extends _link.default {
    searchTemplate = 'fields/user/search';
    linkClass = 'text-default';
    setupSearch() {
      super.setupSearch();
      this.searchTypeList = Espo.Utils.clone(this.searchTypeList);
      this.searchTypeList.push('isFromTeams');
      this.searchData.teamIdList = this.getSearchParamsData().teamIdList || this.searchParams.teamIdList || [];
      this.searchData.teamNameHash = this.getSearchParamsData().teamNameHash || this.searchParams.teamNameHash || {};
      this.events['click a[data-action="clearLinkTeams"]'] = e => {
        const id = $(e.currentTarget).data('id').toString();
        this.deleteLinkTeams(id);
      };
      this.addActionHandler('selectLinkTeams', () => this.actionSelectLinkTeams());
      this.events['click a[data-action="clearLinkTeams"]'] = e => {
        const id = $(e.currentTarget).data('id').toString();
        this.deleteLinkTeams(id);
      };
    }
    getSelectPrimaryFilterName() {
      return 'active';
    }

    /**
     * @protected
     */
    async actionSelectLinkTeams() {
      const viewName = this.getMetadata().get('clientDefs.Team.modalViews.select') || 'views/modals/select-records';

      /** @type {module:views/modals/select-records~Options} */
      const options = {
        entityType: 'Team',
        createButton: false,
        multiple: true,
        onSelect: models => {
          models.forEach(model => this.addLinkTeams(model.id, model.attributes.name));
        }
      };
      Espo.Ui.notifyWait();
      const view = await this.createView('modal', viewName, options);
      await view.render();
    }
    handleSearchType(type) {
      super.handleSearchType(type);
      if (type === 'isFromTeams') {
        this.$el.find('div.teams-container').removeClass('hidden');
      } else {
        this.$el.find('div.teams-container').addClass('hidden');
      }
    }
    afterRender() {
      super.afterRender();
      if (this.mode === this.MODE_SEARCH) {
        const $elementTeams = this.$el.find('input.element-teams');

        /** @type {module:ajax.AjaxPromise & Promise<any>} */
        let lastAjaxPromise;
        const autocomplete = new _autocomplete.default($elementTeams.get(0), {
          minChars: 1,
          focusOnSelect: true,
          handleFocusMode: 3,
          autoSelectFirst: true,
          forceHide: true,
          onSelect: item => {
            this.addLinkTeams(item.id, item.name);
            $elementTeams.val('');
          },
          lookupFunction: query => {
            if (lastAjaxPromise && lastAjaxPromise.getReadyState() < 4) {
              lastAjaxPromise.abort();
            }
            lastAjaxPromise = Espo.Ajax.getRequest('Team', {
              maxSize: this.getAutocompleteMaxCount(),
              select: 'id,name',
              q: query
            });
            return lastAjaxPromise.then(/** {list: Record[]} */response => {
              return response.list.map(item => ({
                id: item.id,
                name: item.name,
                data: item.id,
                value: item.name
              }));
            });
          }
        });
        this.once('render remove', () => autocomplete.dispose());
        const type = this.$el.find('select.search-type').val();
        if (type === 'isFromTeams') {
          this.searchData.teamIdList.forEach(id => {
            this.addLinkTeamsHtml(id, this.searchData.teamNameHash[id]);
          });
        }
      }
    }
    deleteLinkTeams(id) {
      this.deleteLinkTeamsHtml(id);
      const index = this.searchData.teamIdList.indexOf(id);
      if (index > -1) {
        this.searchData.teamIdList.splice(index, 1);
      }
      delete this.searchData.teamNameHash[id];
      this.trigger('change');
    }
    addLinkTeams(id, name) {
      this.searchData.teamIdList = this.searchData.teamIdList || [];
      if (!~this.searchData.teamIdList.indexOf(id)) {
        this.searchData.teamIdList.push(id);
        this.searchData.teamNameHash[id] = name;
        this.addLinkTeamsHtml(id, name);
        this.trigger('change');
      }
    }
    deleteLinkTeamsHtml(id) {
      this.$el.find('.link-teams-container .link-' + id).remove();
    }
    addLinkTeamsHtml(id, name) {
      id = this.getHelper().escapeString(id);
      name = this.getHelper().escapeString(name);
      const $container = this.$el.find('.link-teams-container');
      const $el = $('<div />').addClass('link-' + id).addClass('list-group-item');
      $el.html(name + '&nbsp');
      $el.prepend('<a role="button" class="pull-right" data-id="' + id + '" ' + 'data-action="clearLinkTeams"><span class="fas fa-times"></a>');
      $container.append($el);
      return $el;
    }
    fetchSearch() {
      const type = this.$el.find('select.search-type').val();
      if (type === 'isFromTeams') {
        return {
          type: 'isUserFromTeams',
          field: this.name,
          value: this.searchData.teamIdList,
          data: {
            type: type,
            teamIdList: this.searchData.teamIdList,
            teamNameHash: this.searchData.teamNameHash
          }
        };
      }
      return super.fetchSearch();
    }
  }
  var _default = _exports.default = UserFieldView;
});

define("views/fields/range-int", ["exports", "views/fields/base", "views/fields/int", "autonumeric"], function (_exports, _base, _int, _autonumeric) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _int = _interopRequireDefault(_int);
  _autonumeric = _interopRequireDefault(_autonumeric);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RangeIntFieldView extends _base.default {
    type = 'rangeInt';
    listTemplate = 'fields/range-int/detail';
    detailTemplate = 'fields/range-int/detail';
    editTemplate = 'fields/range-int/edit';
    validations = ['required', 'int', 'range', 'order'];

    // noinspection JSCheckFunctionSignatures
    data() {
      const data = super.data();
      data.ucName = Espo.Utils.upperCaseFirst(this.name);
      data.fromValue = this.model.get(this.fromField);
      data.toValue = this.model.get(this.toField);

      // noinspection JSValidateTypes
      return data;
    }
    init() {
      const ucName = Espo.Utils.upperCaseFirst(this.options.defs.name);
      this.fromField = 'from' + ucName;
      this.toField = 'to' + ucName;
      super.init();
    }
    getValueForDisplay() {
      let fromValue = this.model.get(this.fromField);
      let toValue = this.model.get(this.toField);
      fromValue = isNaN(fromValue) ? null : fromValue;
      toValue = isNaN(toValue) ? null : toValue;
      if (fromValue !== null && toValue !== null) {
        return this.formatNumber(fromValue) + ' &#8211 ' + this.formatNumber(toValue);
      }
      if (fromValue) {
        return '&#62;&#61; ' + this.formatNumber(fromValue);
      }
      if (toValue) {
        return '&#60;&#61; ' + this.formatNumber(toValue);
      }
      return this.translate('None');
    }
    setup() {
      if (this.getPreferences().has('decimalMark')) {
        this.decimalMark = this.getPreferences().get('decimalMark');
      } else if (this.getConfig().has('decimalMark')) {
        this.decimalMark = this.getConfig().get('decimalMark');
      }
      if (this.getPreferences().has('thousandSeparator')) {
        this.thousandSeparator = this.getPreferences().get('thousandSeparator');
      } else if (this.getConfig().has('thousandSeparator')) {
        this.thousandSeparator = this.getConfig().get('thousandSeparator');
      }
    }
    setupFinal() {
      super.setupFinal();
      this.setupAutoNumericOptions();
    }

    /**
     * @protected
     */
    setupAutoNumericOptions() {
      const separator = (!this.disableFormatting ? this.thousandSeparator : null) || '';
      let decimalCharacter = '.';
      if (separator === '.') {
        decimalCharacter = ',';
      }
      this.autoNumericOptions = {
        digitGroupSeparator: separator,
        decimalCharacter: decimalCharacter,
        modifyValueOnWheel: false,
        decimalPlaces: 0,
        selectOnFocus: false,
        formulaMode: true
      };
    }
    afterRender() {
      super.afterRender();
      if (this.mode === this.MODE_EDIT) {
        this.$from = this.$el.find('[data-name="' + this.fromField + '"]');
        this.$to = this.$el.find('[data-name="' + this.toField + '"]');
        this.$from.on('change', () => {
          this.trigger('change');
        });
        this.$to.on('change', () => {
          this.trigger('change');
        });
        if (this.autoNumericOptions) {
          // noinspection JSUnusedGlobalSymbols
          this.autoNumericInstance1 = new _autonumeric.default(this.$from.get(0), this.autoNumericOptions);
          // noinspection JSUnusedGlobalSymbols
          this.autoNumericInstance2 = new _autonumeric.default(this.$to.get(0), this.autoNumericOptions);
        }
      }
    }
    validateRequired() {
      const validate = name => {
        if (this.model.isRequired(name) && this.model.get(name) === null) {
          const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
          this.showValidationMessage(msg, '[data-name="' + name + '"]');
          return true;
        }
      };
      let result = false;
      result = validate(this.fromField) || result;
      result = validate(this.toField) || result;
      return result;
    }

    // noinspection JSUnusedGlobalSymbols
    validateInt() {
      const validate = name => {
        if (isNaN(this.model.get(name))) {
          const msg = this.translate('fieldShouldBeInt', 'messages').replace('{field}', this.getLabelText());
          this.showValidationMessage(msg, '[data-name="' + name + '"]');
          return true;
        }
      };
      let result = false;
      result = validate(this.fromField) || result;
      result = validate(this.toField) || result;
      return result;
    }

    // noinspection JSUnusedGlobalSymbols
    validateRange() {
      const validate = name => {
        const value = this.model.get(name);
        if (value === null) {
          return false;
        }
        const minValue = this.model.getFieldParam(name, 'min');
        const maxValue = this.model.getFieldParam(name, 'max');
        if (minValue !== null && maxValue !== null) {
          if (value < minValue || value > maxValue) {
            const msg = this.translate('fieldShouldBeBetween', 'messages').replace('{field}', this.translate(name, 'fields', this.entityType)).replace('{min}', minValue).replace('{max}', maxValue);
            this.showValidationMessage(msg, '[data-name="' + name + '"]');
            return true;
          }
        } else if (minValue !== null) {
          if (value < minValue) {
            const msg = this.translate('fieldShouldBeLess', 'messages').replace('{field}', this.translate(name, 'fields', this.entityType)).replace('{value}', minValue);
            this.showValidationMessage(msg, '[data-name="' + name + '"]');
            return true;
          }
        } else if (maxValue !== null) {
          if (value > maxValue) {
            const msg = this.translate('fieldShouldBeGreater', 'messages').replace('{field}', this.translate(name, 'fields', this.entityType)).replace('{value}', maxValue);
            this.showValidationMessage(msg, '[data-name="' + name + '"]');
            return true;
          }
        }
      };
      let result = false;
      result = validate(this.fromField) || result;
      result = validate(this.toField) || result;
      return result;
    }

    // noinspection JSUnusedGlobalSymbols
    validateOrder() {
      const fromValue = this.model.get(this.fromField);
      const toValue = this.model.get(this.toField);
      if (fromValue !== null && toValue !== null && fromValue > toValue) {
        const msg = this.translate('fieldShouldBeGreater', 'messages').replace('{field}', this.translate(this.toField, 'fields', this.entityType)).replace('{value}', this.translate(this.fromField, 'fields', this.entityType));
        this.showValidationMessage(msg, '[data-name="' + this.fromField + '"]');
        return true;
      }
    }
    isRequired() {
      return this.model.getFieldParam(this.fromField, 'required') || this.model.getFieldParam(this.toField, 'required');
    }
    parse(value) {
      return _int.default.prototype.parse.call(this, value);
    }
    formatNumber(value) {
      if (this.params.disableFormatting) {
        return value.toString();
      }
      return _int.default.prototype.formatNumberDetail.call(this, value);
    }
    fetch() {
      const data = {};
      data[this.fromField] = this.parse(this.$from.val().trim());
      data[this.toField] = this.parse(this.$to.val().trim());
      return data;
    }
  }
  var _default = _exports.default = RangeIntFieldView;
});

define("views/fields/link-multiple", ["exports", "views/fields/base", "helpers/record-modal", "ui/autocomplete"], function (_exports, _base, _recordModal, _autocomplete) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _recordModal = _interopRequireDefault(_recordModal);
  _autocomplete = _interopRequireDefault(_autocomplete);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/link-multiple */

  /**
   * A link-multiple field (for has-many relations).
   *
   * @extends BaseFieldView<module:views/fields/link-multiple~params>
   */
  class LinkMultipleFieldView extends _base.default {
    /**
     * @typedef {Object} module:views/fields/link-multiple~options
     * @property {
     *     module:views/fields/link-multiple~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     * @property {boolean} [createDisabled] Disable create button in the select modal.
     */

    /**
     * @typedef {Object} module:views/fields/link-multiple~params
     * @property {boolean} [required] Required.
     * @property {boolean} [autocompleteOnEmpty] Autocomplete on empty input.
     * @property {boolean} [sortable] Sortable.
     * @property {boolean} [createButton] Show 'Create' button.
     * @property {number} [maxCount] A max number of items.
     * @property {string} [entity] An entity type. As of 9.1.0.
     */

    /**
     * @param {
     *     module:views/fields/link-multiple~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'linkMultiple';
    listTemplate = 'fields/link-multiple/list';
    detailTemplate = 'fields/link-multiple/detail';
    editTemplate = 'fields/link-multiple/edit';
    searchTemplate = 'fields/link-multiple/search';

    // noinspection JSUnusedGlobalSymbols
    listLinkTemplateContent = `
        {{#if value}}
            <a
                href="#{{scope}}/view/{{model.id}}"
                class="link"
                data-id="{{model.id}}"
            >{{{value}}}</a>
        {{/if}}
    `;

    /**
     * @inheritDoc
     * @type {Array<(function (): boolean)|string>}
     */
    validations = ['required', 'maxCount'];

    /**
     * A name-hash attribute name.
     *
     * @protected
     * @type {string}
     */
    nameHashName;

    /**
     * A IDs attribute name.
     *
     * @protected
     * @type {string}
     */
    idsName;

    /**
     * @protected
     * @type {Object.<string,string>|null}
     */
    nameHash = null;

    /**
     * @protected
     * @type {string[]|null}
     */
    ids = null;

    /**
     * A foreign entity type.
     *
     * @protected
     * @type {string}
     */
    foreignScope;

    /**
     * Autocomplete disabled.
     *
     * @protected
     * @type {boolean}
     */
    autocompleteDisabled = false;

    /**
     * A select-record view.
     *
     * @protected
     * @type {string}
     */
    selectRecordsView = 'views/modals/select-records';

    /**
     * Create disabled.
     *
     * @protected
     * @type {boolean}
     */
    createDisabled = false;

    /**
     * Force create button even is disabled in clientDefs > relationshipPanels.
     *
     * @protected
     * @type {boolean}
     */
    forceCreateButton = false;

    /**
     * To display the create button.
     *
     * @protected
     * @type {boolean}
     */
    createButton = false;

    /**
     * @protected
     * @type {boolean}
     */
    sortable = false;

    /**
     * A search type list.
     *
     * @protected
     * @type {string[]}
     */
    searchTypeList = ['anyOf', 'isEmpty', 'isNotEmpty', 'noneOf', 'allOf'];

    /**
     * A primary filter list that will be available when selecting a record.
     *
     * @protected
     * @type {string[]|null}
     */
    selectFilterList = null;

    /**
     * A select bool filter list.
     *
     * @protected
     * @type {string[]|null}
     */
    selectBoolFilterList = null;

    /**
     * A select primary filter.
     *
     * @protected
     * @type {string|null}
     */
    selectPrimaryFilterName = null;

    /**
     * An autocomplete max record number.
     *
     * @protected
     * @type {number|null}
     */
    autocompleteMaxCount = null;

    /**
     * Trigger autocomplete on empty input.
     *
     * @protected
     * @type {boolean}
     */
    autocompleteOnEmpty = false;

    /**
     * Select all attributes.
     *
     * @protected
     * @type {boolean}
     */
    forceSelectAllAttributes = false;

    /**
     * @protected
     * @type {string}
     */
    iconHtml = '';

    /**
     * A link element class name. Applicable in the detail mode.
     *
     * @protected
     * @since 9.1.6
     */
    linkClass;

    /** @inheritDoc */
    events = {
      /** @this LinkMultipleFieldView */
      'auxclick a[href]:not([role="button"])': function (e) {
        if (!this.isReadMode()) {
          return;
        }
        const isCombination = e.button === 1 && (e.ctrlKey || e.metaKey);
        if (!isCombination) {
          return;
        }
        const id = $(e.currentTarget).attr('data-id');
        if (!id) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        this.quickView(id);
      }
    };

    // noinspection JSCheckFunctionSignatures
    /** @inheritDoc */
    data() {
      const ids = this.model.get(this.idsName);
      const createButton = this.createButton && (!this.createDisabled || this.forceCreateButton);

      // noinspection JSValidateTypes
      return {
        ...super.data(),
        idValues: this.model.get(this.idsName),
        idValuesString: ids ? ids.join(',') : '',
        nameHash: this.model.get(this.nameHashName),
        foreignScope: this.foreignScope,
        valueIsSet: this.model.has(this.idsName),
        createButton: createButton
      };
    }

    /**
     * Get advanced filters (field filters) to be applied when select a record.
     * Can be extended.
     *
     * @protected
     * @return {Object.<string, module:search-manager~advancedFilter>|null}
     */
    getSelectFilters() {
      return null;
    }

    /**
     * Get a select bool filter list. Applied when select a record.
     * Can be extended.
     *
     * @protected
     * @return {string[]|null}
     */
    getSelectBoolFilterList() {
      return this.selectBoolFilterList;
    }

    /**
     * Get a select primary filter. Applied when select a record.
     * Can be extended.
     *
     * @protected
     * @return {string|null}
     */
    getSelectPrimaryFilterName() {
      return this.selectPrimaryFilterName;
    }

    /**
     * Get a primary filter list that will be available when selecting a record.
     * Can be extended.
     *
     * @return {string[]|null}
     */
    getSelectFilterList() {
      return this.selectFilterList;
    }

    /**
     * Attributes to pass to a model when creating a new record.
     * Can be extended.
     *
     * @return {Object.<string, *>|null}
     */
    getCreateAttributes() {
      const attributeMap = this.getMetadata().get(['clientDefs', this.entityType, 'relationshipPanels', this.name, 'createAttributeMap']) || {};
      const attributes = {};
      Object.keys(attributeMap).forEach(attr => attributes[attributeMap[attr]] = this.model.get(attr));
      return attributes;
    }

    /** @inheritDoc */
    setup() {
      this.nameHashName = this.name + 'Names';
      this.idsName = this.name + 'Ids';
      this.foreignScope = this.options.foreignScope || this.foreignScope || this.params.entity || this.model.getFieldParam(this.name, 'entity') || this.model.getLinkParam(this.name, 'entity');
      if ('createDisabled' in this.options) {
        this.createDisabled = this.options.createDisabled;
      }
      if (this.isSearchMode()) {
        const nameHash = this.getSearchParamsData().nameHash || this.searchParams.nameHash || {};
        const idList = this.getSearchParamsData().idList || this.searchParams.value || [];
        this.nameHash = Espo.Utils.clone(nameHash);
        this.ids = Espo.Utils.clone(idList);
      } else {
        this.copyValuesFromModel();
      }
      this.listenTo(this.model, 'change:' + this.idsName, () => {
        this.copyValuesFromModel();
      });
      this.sortable = this.sortable || this.params.sortable;
      this.iconHtml = this.getHelper().getScopeColorIconHtml(this.foreignScope);
      if (!this.isListMode()) {
        this.addActionHandler('selectLink', () => this.actionSelect());
        this.events['click a[data-action="clearLink"]'] = e => {
          const id = $(e.currentTarget).attr('data-id');
          this.actionDeleteLink(id);
        };
      }
      this.autocompleteOnEmpty = this.params.autocompleteOnEmpty || this.autocompleteOnEmpty;
      this.createButton = this.params.createButton || this.createButton;
      if (this.createButton && !this.getAcl().checkScope(this.foreignScope, 'create')) {
        this.createButton = false;
      }
      if (this.createButton) {
        this.addActionHandler('createLink', () => this.actionCreateLink());
      }

      /** @type {Object.<string, *>} */
      this.panelDefs = this.getMetadata().get(['clientDefs', this.entityType, 'relationshipPanels', this.name]) || {};
    }

    /**
     * @private
     * @param {string} id
     */
    actionDeleteLink(id) {
      this.deleteLink(id);

      // noinspection JSUnresolvedReference
      this.$element.get(0).focus({
        preventScroll: true
      });

      // Timeout prevents autocomplete from disappearing.
      setTimeout(() => {
        // noinspection JSUnresolvedReference
        this.$element.get(0).focus({
          preventScroll: true
        });
      }, 140);
    }

    /**
     * Copy values from a model to view properties.
     */
    copyValuesFromModel() {
      this.ids = Espo.Utils.clone(this.model.get(this.idsName) || []);
      this.nameHash = Espo.Utils.clone(this.model.get(this.nameHashName) || {});
    }

    /**
     * Handle a search type.
     *
     * @protected
     * @param {string} type A type.
     */
    handleSearchType(type) {
      if (~['anyOf', 'noneOf', 'allOf'].indexOf(type)) {
        this.$el.find('div.link-group-container').removeClass('hidden');
      } else {
        this.$el.find('div.link-group-container').addClass('hidden');
      }
    }

    /** @inheritDoc */
    setupSearch() {
      this.events = _.extend({
        'change select.search-type': e => {
          const type = $(e.currentTarget).val();
          this.handleSearchType(type);
        }
      }, this.events || {});
    }

    /**
     * Get an autocomplete max record number. Can be extended.
     *
     * @protected
     * @return {number}
     */
    getAutocompleteMaxCount() {
      if (this.autocompleteMaxCount) {
        return this.autocompleteMaxCount;
      }
      return this.getConfig().get('recordsPerPage');
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * Compose an autocomplete URL. Can be extended.
     *
     * @protected
     * @param {string} [q] A query.
     * @return {string|Promise<string>}
     */
    getAutocompleteUrl(q) {
      let url = this.foreignScope + '?&maxSize=' + this.getAutocompleteMaxCount();
      if (!this.forceSelectAllAttributes) {
        /** @var {Object.<string, *>} */
        const panelDefs = this.getMetadata().get(['clientDefs', this.entityType, 'relationshipPanels', this.name]) || {};
        const mandatorySelectAttributeList = this.mandatorySelectAttributeList || panelDefs.selectMandatoryAttributeList;
        let select = ['id', 'name'];
        if (mandatorySelectAttributeList) {
          select = select.concat(mandatorySelectAttributeList);
        }
        url += '&select=' + select.join(',');
      }
      const notSelectedFilter = this.ids && this.ids.length ? {
        // Prefix to prevent conflict when the 'id' field is in filters.
        _id: {
          type: 'notIn',
          attribute: 'id',
          value: this.ids
        }
      } : {};
      if (this.panelDefs.selectHandler) {
        return new Promise(resolve => {
          this._getSelectFilters().then(filters => {
            if (filters.bool) {
              url += '&' + $.param({
                boolFilterList: filters.bool
              });
            }
            if (filters.primary) {
              url += '&' + $.param({
                primaryFilter: filters.primary
              });
            }
            const advanced = {
              ...notSelectedFilter,
              ...(filters.advanced || {})
            };
            if (Object.keys(advanced).length) {
              url += '&' + $.param({
                where: advanced
              });
            }
            const orderBy = filters.orderBy || this.panelDefs.selectOrderBy;
            const orderDirection = filters.orderBy ? filters.order : this.panelDefs.selectOrderDirection;
            if (orderBy) {
              url += '&' + $.param({
                orderBy: orderBy,
                order: orderDirection || 'asc'
              });
            }
            resolve(url);
          });
        });
      }
      const boolList = [...(this.getSelectBoolFilterList() || []), ...(this.panelDefs.selectBoolFilterList || [])];
      if (boolList.length) {
        url += '&' + $.param({
          'boolFilterList': boolList
        });
      }
      const primary = this.getSelectPrimaryFilterName() || this.panelDefs.selectPrimaryFilterName;
      if (primary) {
        url += '&' + $.param({
          'primaryFilter': primary
        });
      }
      if (Object.keys(notSelectedFilter).length) {
        url += '&' + $.param({
          'where': notSelectedFilter
        });
      }
      if (this.panelDefs.selectOrderBy) {
        const direction = this.panelDefs.selectOrderDirection || 'asc';
        url += '&' + $.param({
          orderBy: this.panelDefs.selectOrderBy,
          order: direction
        });
      }
      return url;
    }

    /** @inheritDoc */
    afterRender() {
      if (this.isEditMode() || this.isSearchMode()) {
        this.$element = this.$el.find('input.main-element');
        if (!this.autocompleteDisabled) {
          /** @type {module:ajax.AjaxPromise & Promise<any>} */
          let lastAjaxPromise;
          const autocomplete = new _autocomplete.default(this.$element.get(0), {
            focusOnSelect: true,
            handleFocusMode: 3,
            autoSelectFirst: true,
            triggerSelectOnValidInput: false,
            forceHide: true,
            onSelect: item => {
              this.getModelFactory().create(this.foreignScope, model => {
                model.set(item.attributes);
                this.select([model]);
                this.$element.val('');
                this.$element.focus();
              });
            },
            lookupFunction: query => {
              if (!this.autocompleteOnEmpty && query.length === 0) {
                const onEmptyPromise = this.getOnEmptyAutocomplete();
                if (onEmptyPromise) {
                  return onEmptyPromise.then(list => this._transformAutocompleteResult({
                    list: list
                  }));
                }
                return Promise.resolve([]);
              }
              return Promise.resolve(this.getAutocompleteUrl(query)).then(url => {
                if (lastAjaxPromise && lastAjaxPromise.getReadyState() < 4) {
                  lastAjaxPromise.abort();
                }
                lastAjaxPromise = Espo.Ajax.getRequest(url, {
                  q: query
                });
                return lastAjaxPromise;
              }).then(/** {list: Record[]} */response => {
                return response.list.map(item => ({
                  value: item.name,
                  attributes: item
                }));
              });
            }
          });
          this.once('render remove', () => autocomplete.dispose());
        }
        this.renderLinks();
        if (this.isEditMode()) {
          if (this.sortable) {
            // noinspection JSUnresolvedReference
            this.$el.find('.link-container').sortable({
              stop: () => {
                this.fetchFromDom();
                this.trigger('change');
              }
            });
          }
        }
        if (this.isSearchMode()) {
          const type = this.$el.find('select.search-type').val();
          this.handleSearchType(type);
          this.$el.find('select.search-type').on('change', () => {
            this.trigger('change');
          });
        }
      }
    }

    /**
     * Render items.
     *
     * @protected
     */
    renderLinks() {
      this.ids.forEach(id => {
        this.addLinkHtml(id, this.nameHash[id]);
      });
    }

    /**
     * Delete an item.
     *
     * @protected
     * @param {string} id An ID.
     */
    deleteLink(id) {
      this.trigger('delete-link', id);
      this.trigger('delete-link:' + id);
      this.deleteLinkHtml(id);
      const index = this.ids.indexOf(id);
      if (index > -1) {
        this.ids.splice(index, 1);
      }
      delete this.nameHash[id];
      this.afterDeleteLink(id);
      this.trigger('change');
    }

    /**
     * Add an item.
     *
     * @protected
     * @param {string} id An ID.
     * @param {string} name A name.
     */
    addLink(id, name) {
      if (!~this.ids.indexOf(id)) {
        this.ids.push(id);
        this.nameHash[id] = name;
        this.addLinkHtml(id, name);
        this.afterAddLink(id);
        this.trigger('add-link', id);
        this.trigger('add-link:' + id);
      }
      this.trigger('change');
    }

    /**
     * @protected
     * @param {string} id An ID.
     */
    afterDeleteLink(id) {}

    /**
     * @protected
     * @param {string} id An ID.
     */
    afterAddLink(id) {}

    /**
     * @protected
     * @param {string} id An ID.
     */
    deleteLinkHtml(id) {
      this.$el.find('.link-' + id).remove();
    }

    /**
     * Add an item for edit mode.
     *
     * @protected
     * @param {string} id An ID.
     * @param {string} name A name.
     * @return {JQuery|null}
     */
    addLinkHtml(id, name) {
      // Beware of XSS.

      name = name || id;
      const $container = this.$el.find('.link-container');
      const itemElement = this.prepareEditItemElement(id, name);
      $container.append(itemElement);
      return $(itemElement);
    }

    /**
     * @protected
     * @param {string} id An ID.
     * @param {string} name A name.
     * @return {HTMLElement}
     */
    prepareEditItemElement(id, name) {
      // Beware of XSS.

      const $el = $('<div>').addClass('link-' + id).addClass('list-group-item').attr('data-id', id);
      $el.text(name).append('&nbsp;');
      $el.prepend($('<a>').addClass('pull-right').attr('role', 'button').attr('tabindex', '0').attr('data-id', id).attr('data-action', 'clearLink').append($('<span>').addClass('fas fa-times')));
      return $el.get(0);
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * @param {string} id An ID.
     * @return {string}
     */
    getIconHtml(id) {
      return this.iconHtml;
    }

    /**
     * Get an item HTML for detail mode.
     *
     * @param {string} id An ID.
     * @param {string} [name] A name.
     * @return {string}
     */
    getDetailLinkHtml(id, name) {
      // Do not use the `html` method to avoid XSS.

      name = name || this.nameHash[id] || id;
      if (!name && id) {
        name = this.translate(this.foreignScope, 'scopeNames');
      }
      const iconHtml = this.isDetailMode() ? this.getIconHtml(id) : '';
      const $a = $('<a>').attr('href', this.getUrl(id)).attr('data-id', id).text(name);
      if (this.mode === this.MODE_LIST) {
        $a.addClass('text-default');
      } else if (this.linkClass) {
        $a.addClass(this.linkClass);
      }
      if (iconHtml) {
        $a.prepend(iconHtml);
      }
      return $a.get(0).outerHTML;
    }

    /**
     * @protected
     * @param {string} id An ID.
     * @return {string}
     */
    getUrl(id) {
      return '#' + this.foreignScope + '/view/' + id;
    }

    /** @inheritDoc */
    getValueForDisplay() {
      if (!this.isDetailMode() && !this.isListMode()) {
        return null;
      }
      if (this.mode === this.MODE_LIST_LINK) {
        const div = document.createElement('div');
        this.ids.forEach(id => {
          const itemDiv = document.createElement('div');
          itemDiv.classList.add('link-multiple-item');
          itemDiv.textContent = this.nameHash[id] || id;
          div.append(itemDiv);
        });
        return div.outerHTML;
      }
      const itemList = [];
      this.ids.forEach(id => {
        itemList.push(this.getDetailLinkHtml(id));
      });
      if (!itemList.length) {
        return null;
      }
      return itemList.map(item => $('<div>').addClass('link-multiple-item').html(item).wrap('<div />').parent().html()).join('');
    }

    /** @inheritDoc */
    validateRequired() {
      if (!this.isRequired()) {
        return false;
      }
      const idList = this.model.get(this.idsName) || [];
      if (idList.length === 0) {
        const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg);
        return true;
      }
      return false;
    }

    // noinspection JSUnusedGlobalSymbols
    validateMaxCount() {
      const maxCount = this.params.maxCount;
      if (!maxCount) {
        return false;
      }
      const idList = this.model.get(this.idsName) || [];
      if (idList.length === 0) {
        return false;
      }
      if (idList.length <= maxCount) {
        return false;
      }
      const msg = this.translate('fieldExceedsMaxCount', 'messages').replace('{field}', this.getLabelText()).replace('{maxCount}', maxCount.toString());
      this.showValidationMessage(msg);
      return true;
    }

    /** @inheritDoc */
    fetch() {
      const data = {};
      data[this.idsName] = Espo.Utils.clone(this.ids);
      data[this.nameHashName] = Espo.Utils.clone(this.nameHash);
      return data;
    }

    /** @inheritDoc */
    fetchFromDom() {
      this.ids = [];
      this.$el.find('.link-container').children().each((i, li) => {
        const id = $(li).attr('data-id');
        if (!id) {
          return;
        }
        this.ids.push(id);
      });
    }

    /** @inheritDoc */
    fetchSearch() {
      const type = this.$el.find('select.search-type').val();
      const idList = this.ids || [];
      if (~['anyOf', 'allOf', 'noneOf'].indexOf(type) && !idList.length) {
        return {
          type: 'isNotNull',
          attribute: 'id',
          data: {
            type: type
          }
        };
      }
      let data;
      if (type === 'anyOf') {
        data = {
          type: 'linkedWith',
          value: idList,
          data: {
            type: type,
            nameHash: this.nameHash
          }
        };
        return data;
      }
      if (type === 'allOf') {
        data = {
          type: 'linkedWithAll',
          value: idList,
          data: {
            type: type,
            nameHash: this.nameHash
          }
        };
        if (!idList.length) {
          data.value = null;
        }
        return data;
      }
      if (type === 'noneOf') {
        data = {
          type: 'notLinkedWith',
          value: idList,
          data: {
            type: type,
            nameHash: this.nameHash
          }
        };
        return data;
      }
      if (type === 'isEmpty') {
        data = {
          type: 'isNotLinked',
          data: {
            type: type
          }
        };
        return data;
      }
      if (type === 'isNotEmpty') {
        data = {
          type: 'isLinked',
          data: {
            type: type
          }
        };
        return data;
      }
    }

    /** @inheritDoc */
    getSearchType() {
      return this.getSearchParamsData().type || this.searchParams.typeFront || this.searchParams.type || 'anyOf';
    }

    /**
     * @protected
     * @param {string} id
     */
    quickView(id) {
      const entityType = this.foreignScope;
      const helper = new _recordModal.default();
      helper.showDetail(this, {
        id: id,
        entityType: entityType
      });
    }

    /**
     * @protected
     */
    async actionSelect() {
      Espo.Ui.notifyWait();
      const viewName = this.panelDefs.selectModalView || this.getMetadata().get(`clientDefs.${this.foreignScope}.modalViews.select`) || this.selectRecordsView;
      const mandatorySelectAttributeList = this.mandatorySelectAttributeList || this.panelDefs.selectMandatoryAttributeList;
      const createButton = this.isEditMode() && (!this.createDisabled && !this.panelDefs.createDisabled || this.forceCreateButton);
      const createAttributesProvider = createButton ? this.getCreateAttributesProvider() : null;
      const filters = await this._getSelectFilters();
      const orderBy = filters.orderBy || this.panelDefs.selectOrderBy;
      const orderDirection = filters.orderBy ? filters.order : this.panelDefs.selectOrderDirection;

      /** @type {module:views/modals/select-records~Options} */
      const options = {
        entityType: this.foreignScope,
        createButton: createButton,
        filters: filters.advanced,
        boolFilterList: filters.bool,
        primaryFilterName: filters.primary,
        filterList: this.getSelectFilterList(),
        multiple: true,
        mandatorySelectAttributeList: mandatorySelectAttributeList,
        forceSelectAllAttributes: this.forceSelectAllAttributes,
        createAttributesProvider: createAttributesProvider,
        layoutName: this.panelDefs.selectLayout,
        orderBy: orderBy,
        orderDirection: orderDirection,
        onSelect: models => {
          this.select(models);
        }
      };
      const view = await this.createView('modal', viewName, options);
      await view.render();
      Espo.Ui.notify();
    }

    /**
     * @protected
     * @return {function(): Promise<Object.<string, *>>}
     */
    getCreateAttributesProvider() {
      return () => {
        const attributes = this.getCreateAttributes() || {};
        if (!this.panelDefs.createHandler) {
          return Promise.resolve(attributes);
        }
        return new Promise(resolve => {
          Espo.loader.requirePromise(this.panelDefs.createHandler).then(Handler => new Handler(this.getHelper())).then(handler => {
            handler.getAttributes(this.model).then(additionalAttributes => {
              resolve({
                ...attributes,
                ...additionalAttributes
              });
            });
          });
        });
      };
    }

    /**
     * On records select.
     *
     * @protected
     * @param {module:model[]} models
     * @since 8.0.4
     */
    select(models) {
      models.forEach(model => {
        this.addLink(model.id, model.get('name'));
      });
    }

    /**
     * @private
     * @return {Promise<{
     *     bool?: string[],
     *     advanced?: Object,
     *     primary?: string,
     *     orderBy?: string,
     *     order?: 'asc'|'desc',
     * }>}
     */
    _getSelectFilters() {
      const handler = this.panelDefs.selectHandler;
      const localBoolFilterList = this.getSelectBoolFilterList();
      if (!handler || this.isSearchMode()) {
        const boolFilterList = localBoolFilterList || this.panelDefs.selectBoolFilterList ? [...(localBoolFilterList || []), ...(this.panelDefs.selectBoolFilterList || [])] : undefined;
        return Promise.resolve({
          primary: this.getSelectPrimaryFilterName() || this.panelDefs.selectPrimaryFilterName,
          bool: boolFilterList,
          advanced: this.getSelectFilters() || undefined
        });
      }
      return new Promise(resolve => {
        Espo.loader.requirePromise(handler).then(Handler => new Handler(this.getHelper())).then(/** module:handlers/select-related */handler => {
          return handler.getFilters(this.model);
        }).then(filters => {
          const advanced = {
            ...(this.getSelectFilters() || {}),
            ...(filters.advanced || {})
          };
          const primaryFilter = this.getSelectPrimaryFilterName() || filters.primary || this.panelDefs.selectPrimaryFilterName;
          const boolFilterList = localBoolFilterList || filters.bool || this.panelDefs.selectBoolFilterList ? [...(localBoolFilterList || []), ...(filters.bool || []), ...(this.panelDefs.selectBoolFilterList || [])] : undefined;
          resolve({
            bool: boolFilterList,
            primary: primaryFilter,
            advanced: advanced
          });
        });
      });
    }

    /**
     * @protected
     */
    async actionCreateLink() {
      const helper = new _recordModal.default();
      const attributes = await this.getCreateAttributesProvider()();
      await helper.showCreate(this, {
        entityType: this.foreignScope,
        fullFormDisabled: true,
        attributes: attributes,
        afterSave: model => this.select([model])
      });
    }

    /** @private */
    _transformAutocompleteResult(response) {
      const list = [];
      response.list.forEach(item => {
        list.push({
          id: item.id,
          name: item.name || item.id,
          data: item.id,
          value: item.name || item.id,
          attributes: item
        });
      });
      return list;
    }

    /**
     * Get an empty autocomplete result.
     *
     * @protected
     * @return {Promise<[{name: ?string, id: string} & Record]>}
     */
    getOnEmptyAutocomplete() {
      return undefined;
    }
  }
  var _default = _exports.default = LinkMultipleFieldView;
});

define("views/fields/file", ["exports", "views/fields/link", "helpers/file-upload", "helpers/misc/attachment-insert-from-source"], function (_exports, _link, _fileUpload, _attachmentInsertFromSource) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _link = _interopRequireDefault(_link);
  _fileUpload = _interopRequireDefault(_fileUpload);
  _attachmentInsertFromSource = _interopRequireDefault(_attachmentInsertFromSource);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/file */

  /**
   * A file field.
   *
   * @extends LinkFieldView<module:views/fields/file~params>
   */
  class FileFieldView extends _link.default {
    /**
     * @typedef {Object} module:views/fields/file~options
     * @property {
     *     module:views/fields/file~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/file~params
     * @property {boolean} [required] Required.
     * @property {boolean} [showPreview] Show preview.
     * @property {'x-small'|'small'|'medium'|'large'} [previewSize] A preview size.
     * @property {'x-small'|'small'|'medium'|'large'} [listPreviewSize] A list preview size.
     * @property {string[]} [sourceList] A source list.
     * @property {string[]} [accept] Formats to accept.
     * @property {number} [maxFileSize] A max file size (in Mb).
     */

    /**
     * @param {
     *     module:views/fields/file~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'file';
    listTemplate = 'fields/file/list';
    listLinkTemplate = 'fields/file/list';
    detailTemplate = 'fields/file/detail';
    editTemplate = 'fields/file/edit';
    showPreview = false;
    accept = false;
    defaultType = false;
    previewSize = 'small';
    validations = ['ready', 'required'];
    searchTypeList = ['isNotEmpty', 'isEmpty'];
    events = {
      /** @this FileFieldView */
      'click a.remove-attachment': function (e) {
        const $div = $(e.currentTarget).parent();
        this.deleteAttachment();
        $div.parent().remove();
        this.$el.find('input.file').val(null);
        setTimeout(() => this.focusOnUploadButton(), 10);
      },
      /** @this FileFieldView */
      'change input.file': function (e) {
        const $file = $(e.currentTarget);
        const files = e.currentTarget.files;
        if (!files.length) {
          return;
        }
        this.uploadFile(files[0]);
        e.target.value = null;
        $file.replaceWith($file.clone(true));
      },
      /** @this FileFieldView */
      'click a[data-action="showImagePreview"]': function (e) {
        e.preventDefault();
        const id = this.model.get(this.idName);
        this.createView('preview', 'views/modals/image-preview', {
          id: id,
          model: this.model,
          name: this.model.get(this.nameName)
        }, view => {
          view.render();
        });
      },
      /** @this FileFieldView */
      'click a.action[data-action="insertFromSource"]': function (e) {
        const name = $(e.currentTarget).data('name');
        this.insertFromSource(name);
      },
      /** @this FileFieldView */
      'keydown label.attach-file-label': function (e) {
        const key = Espo.Utils.getKeyFromKeyEvent(e);
        if (key === 'Enter') {
          const el = /** @type {HTMLInputElement} */this.$el.find('input.file').get(0);
          el.click();
        }
      }
    };

    // noinspection JSCheckFunctionSignatures
    data() {
      const data = {
        ...super.data(),
        id: this.model.get(this.idName),
        acceptAttribute: this.acceptAttribute
      };
      if (this.mode === this.MODE_EDIT) {
        data.sourceList = this.sourceList;
      }
      data.valueIsSet = this.model.has(this.idName);

      // noinspection JSValidateTypes
      return data;
    }
    showValidationMessage(msg, selector, view) {
      const $label = this.$el.find('label');
      const title = $label.attr('title');
      $label.attr('title', '');
      super.showValidationMessage(msg, selector, view);
      $label.attr('title', title);
    }
    validateRequired() {
      if (!this.isRequired()) {
        return;
      }
      if (this.model.get(this.idName) == null) {
        const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
        let $target;
        if (this.isUploading) {
          $target = this.$el.find('.gray-box');
        } else {
          $target = this.$el.find('.attachment-button label');
        }
        this.showValidationMessage(msg, $target);
        return true;
      }
    }

    // noinspection JSUnusedGlobalSymbols
    validateReady() {
      if (this.isUploading) {
        const $target = this.$el.find('.gray-box');
        const msg = this.translate('fieldIsUploading', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg, $target);
        return true;
      }
    }
    setup() {
      this.nameName = this.name + 'Name';
      this.idName = this.name + 'Id';
      this.typeName = this.name + 'Type';
      this.foreignScope = 'Attachment';
      this.previewSize = this.options.previewSize || this.params.previewSize || this.previewSize;
      this.previewTypeList = this.getMetadata().get(['app', 'image', 'previewFileTypeList']) || [];
      this.imageSizes = this.getMetadata().get(['app', 'image', 'sizes']) || {};
      const sourceDefs = this.getMetadata().get(['clientDefs', 'Attachment', 'sourceDefs']) || {};
      this.sourceList = Espo.Utils.clone(this.params.sourceList || []);
      this.sourceList = this.sourceList.concat(this.getMetadata().get(['clientDefs', 'Attachment', 'generalSourceList']) || []).filter((item, i, self) => {
        return self.indexOf(item) === i;
      }).filter(item => {
        const defs = sourceDefs[item] || {};
        if (defs.accessDataList) {
          if (!Espo.Utils.checkAccessDataList(defs.accessDataList, this.getAcl(), this.getUser())) {
            return false;
          }
        }
        if (defs.configCheck) {
          const arr = defs.configCheck.split('.');
          if (!this.getConfig().getByPath(arr)) {
            return false;
          }
        }
        return true;
      });
      if ('showPreview' in this.params) {
        this.showPreview = this.params.showPreview;
      }
      if ('accept' in this.params) {
        this.accept = this.params.accept;
      }
      if (this.accept && this.accept.length) {
        this.acceptAttribute = this.accept.join(', ');
      }
      this.on('remove', () => {
        if (this.resizeIsBeingListened) {
          $(window).off('resize.' + this.cid);
        }
      });
      this.on('inline-edit-off', () => {
        this.isUploading = false;
      });
    }
    afterRender() {
      if (this.mode === this.MODE_EDIT) {
        this.$attachment = this.$el.find('div.attachment');
        const name = this.model.get(this.nameName);
        const type = this.model.get(this.typeName) || this.defaultType;
        const id = this.model.get(this.idName);
        if (id) {
          this.addAttachmentBox(name, type, id);
        }
        this.$el.off('drop');
        this.$el.off('dragover');
        this.$el.off('dragleave');
        this.$el.on('drop', e => {
          e.preventDefault();
          e.stopPropagation();
          const event = /** @type {DragEvent} */e.originalEvent;
          if (event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length) {
            this.uploadFile(event.dataTransfer.files[0]);
          }
        });
        this.$el.on('dragover', e => {
          e.preventDefault();
        });
        this.$el.on('dragleave', e => {
          e.preventDefault();
        });
      }
      if (this.mode === this.MODE_SEARCH) {
        const type = this.$el.find('select.search-type').val();
        this.handleSearchType(type);
      }
      if (this.mode === this.MODE_DETAIL) {
        if (this.previewSize === 'large') {
          this.handleResize();
          this.resizeIsBeingListened = true;
          $(window).on('resize.' + this.cid, () => {
            this.handleResize();
          });
        }
      }
    }
    focusOnInlineEdit() {
      this.focusOnUploadButton();
    }
    focusOnUploadButton() {
      const $element = this.$el.find('.attach-file-label');
      if ($element.length) {
        $element.focus();
      }
    }
    handleResize() {
      const width = this.$el.width();
      this.$el.find('img.image-preview').css('maxWidth', width + 'px');
    }

    /**
     * @return {string}
     */
    getDetailPreview(name, type, id) {
      if (!~this.previewTypeList.indexOf(type)) {
        return name;
      }
      let previewSize = this.previewSize;
      if (this.isListMode()) {
        previewSize = this.params.listPreviewSize || 'small';
      }
      const src = this.getBasePath() + '?entryPoint=image&size=' + previewSize + '&id=' + id;
      let maxHeight = (this.imageSizes[previewSize] || {})[1];
      if (this.isListMode() && !this.params.listPreviewSize) {
        maxHeight = '';
      }

      // noinspection HtmlRequiredAltAttribute,RequiredAttributes
      const $img = $('<img>').attr('src', src).attr('alt', name).addClass('image-preview').css({
        maxWidth: (this.imageSizes[previewSize] || {})[0],
        maxHeight: maxHeight
      });
      if (this.mode === this.MODE_LIST_LINK) {
        const link = '#' + this.model.entityType + '/view/' + this.model.id;
        return $('<a>').attr('href', link).append($img).get(0).outerHTML;
      }
      return $('<a>').attr('data-action', 'showImagePreview').attr('data-id', id).attr('title', name).attr('href', this.getImageUrl(id)).append($img).get(0).outerHTML;
    }
    getEditPreview(name, type, id) {
      if (!~this.previewTypeList.indexOf(type)) {
        return null;
      }

      // noinspection HtmlRequiredAltAttribute,RequiredAttributes
      return $('<img>').attr('src', this.getImageUrl(id, 'small')).attr('title', name).attr('alt', name).attr('draggable', 'false').css({
        maxWidth: (this.imageSizes[this.previewSize] || {})[0],
        maxHeight: (this.imageSizes[this.previewSize] || {})[1]
      }).get(0).outerHTML;
    }
    getValueForDisplay() {
      if (!(this.isDetailMode() || this.isListMode())) {
        return '';
      }
      const name = this.model.get(this.nameName);
      const type = this.model.get(this.typeName) || this.defaultType;
      const id = this.model.get(this.idName);
      if (!id) {
        return false;
      }
      if (this.showPreview && ~this.previewTypeList.indexOf(type)) {
        let className = '';
        if (this.isListMode() && this.params.listPreviewSize) {
          className += 'no-shrink';
        }
        const $item = $('<div>').addClass('attachment-preview').addClass(className).append(this.getDetailPreview(name, type, id));
        let containerClassName = 'attachment-block-container';
        if (this.previewSize === 'large') {
          containerClassName += ' attachment-block-container-large';
        }
        if (this.previewSize === 'small') {
          containerClassName += ' attachment-block-container-small';
        }
        return $('<div>').addClass(containerClassName).append($('<div>').addClass('attachment-block').append($item)).get(0).outerHTML;
      }
      return $('<span>').append($('<span>').addClass('fas fa-paperclip text-soft small'), ' ', $('<a>').attr('href', this.getDownloadUrl(id)).attr('target', '_BLANK').text(name)).get(0).innerHTML;
    }
    getImageUrl(id, size) {
      let url = this.getBasePath() + '?entryPoint=image&id=' + id;
      if (size) {
        url += '&size=' + size;
      }
      if (this.getUser().get('portalId')) {
        url += '&portalId=' + this.getUser().get('portalId');
      }
      return url;
    }
    getDownloadUrl(id) {
      let url = this.getBasePath() + '?entryPoint=download&id=' + id;
      if (this.getUser().get('portalId')) {
        url += '&portalId=' + this.getUser().get('portalId');
      }
      return url;
    }
    deleteAttachment() {
      const id = this.model.get(this.idName);
      const o = {};
      o[this.idName] = null;
      o[this.nameName] = null;
      this.model.set(o);
      this.$attachment.empty();
      if (id) {
        if (this.model.isNew()) {
          this.getModelFactory().create('Attachment', attachment => {
            attachment.id = id;
            attachment.destroy();
          });
        }
      }
    }
    setAttachment(attachment, ui) {
      const o = {};
      o[this.idName] = attachment.id;
      o[this.nameName] = attachment.get('name');
      this.model.set(o, {
        ui: ui
      });
    }
    getMaxFileSize() {
      let maxFileSize = this.params.maxFileSize || 0;
      const noChunk = !this.getConfig().get('attachmentUploadChunkSize');
      const attachmentUploadMaxSize = this.getConfig().get('attachmentUploadMaxSize') || 0;
      const appMaxUploadSize = this.getHelper().getAppParam('maxUploadSize') || 0;
      if (!maxFileSize || maxFileSize > attachmentUploadMaxSize) {
        maxFileSize = attachmentUploadMaxSize;
      }
      if (noChunk && maxFileSize > appMaxUploadSize) {
        maxFileSize = appMaxUploadSize;
      }
      return maxFileSize;
    }

    /**
     * @param {File} file
     */
    uploadFile(file) {
      let isCanceled = false;
      let exceedsMaxFileSize = false;
      const maxFileSize = this.getMaxFileSize();
      if (maxFileSize && file.size > maxFileSize * 1024 * 1024) {
        exceedsMaxFileSize = true;
      }
      if (exceedsMaxFileSize) {
        const msg = this.translate('fieldMaxFileSizeError', 'messages').replace('{field}', this.getLabelText()).replace('{max}', maxFileSize);
        this.showValidationMessage(msg, '.attachment-button label');
        return;
      }
      this.isUploading = true;
      const uploadHelper = new _fileUpload.default();
      this.getModelFactory().create('Attachment', attachment => {
        const $attachmentBox = this.addAttachmentBox(file.name, file.type);
        const $uploadingMsg = $attachmentBox.parent().find('.uploading-message');
        this.$el.find('.attachment-button').addClass('hidden');
        const mediator = {};
        $attachmentBox.find('.remove-attachment').on('click.uploading', () => {
          isCanceled = true;
          this.isUploading = false;
          this.$el.find('.attachment-button').removeClass('hidden');
          this.$el.find('input.file').val(null);
          mediator.isCanceled = true;
        });
        attachment.set('role', 'Attachment');
        attachment.set('relatedType', this.model.entityType);
        attachment.set('field', this.name);
        this.handleUploadingFile(file).then(file => {
          uploadHelper.upload(file, attachment, {
            afterChunkUpload: size => {
              const msg = Math.floor(size / file.size * 100) + '%';
              $uploadingMsg.html(msg);
            },
            afterAttachmentSave: attachment => {
              $attachmentBox.attr('data-id', attachment.id);
            },
            mediator: mediator
          }).then(() => {
            if (isCanceled) {
              return;
            }
            if (!this.isUploading) {
              return;
            }
            this.setAttachment(attachment, true);
            $attachmentBox.trigger('ready');
            this.isUploading = false;
            setTimeout(() => {
              if (document.activeElement && document.activeElement.tagName !== 'BODY') {
                return;
              }
              const $a = this.$el.find('.preview a');
              $a.focus();
            }, 50);
          }).catch(() => {
            if (mediator.isCanceled) {
              return;
            }
            $attachmentBox.remove();
            this.$el.find('.uploading-message').remove();
            this.$el.find('.attachment-button').removeClass('hidden');
            this.isUploading = false;
          });
        });
      });
    }

    /**
     * @protected
     * @param {File} file
     * @return {Promise<unknown>}
     */
    handleUploadingFile(file) {
      return new Promise(resolve => resolve(file));
    }
    getBoxPreviewHtml(name, type, id) {
      const $text = $('<span>').text(name);
      if (!id) {
        return $text.get(0).outerHTML;
      }
      if (this.showPreview) {
        const html = this.getEditPreview(name, type, id);
        if (html) {
          return html;
        }
      }
      const url = this.getBasePath() + '?entryPoint=download&id=' + id;
      return $('<a>').attr('href', url).attr('target', '_BLANK').text(name).get(0).outerHTML;
    }
    addAttachmentBox(name, type, id) {
      this.$attachment.empty();
      const $remove = $('<a>').attr('role', 'button').attr('tabindex', '0').addClass('remove-attachment pull-right').append($('<span>').addClass('fas fa-times'));
      const previewHtml = this.getBoxPreviewHtml(name, type, id);
      const $att = $('<div>').addClass('gray-box').append($remove).append($('<span>').addClass('preview').append($(previewHtml)));
      const $container = $('<div>').append($att);
      this.$attachment.append($container);
      if (id) {
        return $att;
      }
      const $loading = $('<span>').addClass('small uploading-message').text(this.translate('Uploading...'));
      $container.append($loading);
      $att.on('ready', () => {
        const id = this.model.get(this.idName);
        const previewHtml = this.getBoxPreviewHtml(name, type, id);
        $att.find('.preview').html(previewHtml);
        $loading.html(this.translate('Ready'));
        if ($att.find('.preview').find('img').length) {
          $loading.remove();
        }
      });
      return $att;
    }

    /**
     * @private
     * @param {string} source
     */
    insertFromSource(source) {
      const helper = new _attachmentInsertFromSource.default(this);
      helper.insert({
        source: source,
        onInsert: models => {
          models.forEach(model => this.setAttachment(model));
        }
      });
    }
    fetch() {
      const data = {};
      data[this.idName] = this.model.get(this.idName);
      return data;
    }
  }
  var _default = _exports.default = FileFieldView;
});

define("views/fields/datetime", ["exports", "views/fields/date", "moment"], function (_exports, _date, _moment) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _date = _interopRequireDefault(_date);
  _moment = _interopRequireDefault(_moment);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/datetime */

  /**
   * A date-time field.
   *
   * @extends DateFieldView<module:views/fields/datetime~params>
   */
  class DatetimeFieldView extends _date.default {
    /**
     * @typedef {Object} module:views/fields/datetime~options
     * @property {
     *     module:views/fields/varchar~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     * @property {string} [otherFieldLabelText] A label text of other field. Used in before/after validations.
     */

    /**
     * @typedef {Object} module:views/fields/datetime~params
     * @property {boolean} [required] Required.
     * @property {boolean} [useNumericFormat] Use numeric format.
     * @property {boolean} [hasSeconds] Display seconds.
     * @property {number} [minuteStep] A minute step.
     * @property {string} [after] Validate to be after another date field.
     * @property {string} [before] Validate to be before another date field.
     * @property {boolean} [afterOrEqual] Allow an equal date for 'after' validation.
     */

    /**
     * @param {
     *     module:views/fields/datetime~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'datetime';
    editTemplate = 'fields/datetime/edit';

    /**
     * @inheritDoc
     * @type {Array<(function (): boolean)|string>}
     */
    validations = ['required', 'datetime', 'after', 'before'];
    searchTypeList = ['lastSevenDays', 'ever', 'isEmpty', 'currentMonth', 'lastMonth', 'nextMonth', 'currentQuarter', 'lastQuarter', 'currentYear', 'lastYear', 'today', 'past', 'future', 'lastXDays', 'nextXDays', 'olderThanXDays', 'afterXDays', 'on', 'after', 'before', 'between'];
    timeFormatMap = {
      'HH:mm': 'H:i',
      'hh:mm A': 'h:i A',
      'hh:mm a': 'h:i a',
      'hh:mmA': 'h:iA',
      'hh:mma': 'h:ia'
    };
    data() {
      const data = super.data();
      data.date = data.time = '';
      const value = this.getDateTime().toDisplay(this.model.get(this.name));
      if (value) {
        const pair = this.splitDatetime(value);
        data.date = pair[0];
        data.time = pair[1];
      }
      return data;
    }
    getDateStringValue() {
      if (this.mode === this.MODE_DETAIL && !this.model.has(this.name)) {
        return -1;
      }
      const value = this.model.get(this.name);
      if (!value) {
        if (this.mode === this.MODE_EDIT || this.mode === this.MODE_SEARCH || this.mode === this.MODE_LIST || this.mode === this.MODE_LIST_LINK) {
          return '';
        }
        return null;
      }
      if (this.mode === this.MODE_LIST || this.mode === this.MODE_DETAIL || this.mode === this.MODE_LIST_LINK) {
        if (this.getConfig().get('readableDateFormatDisabled') || this.params.useNumericFormat) {
          return this.getDateTime().toDisplay(value);
        }
        let timeFormat = this.getDateTime().timeFormat;
        if (this.params.hasSeconds) {
          timeFormat = timeFormat.replace(/:mm/, ':mm:ss');
        }
        const d = this.getDateTime().toMoment(value);
        const now = (0, _moment.default)().tz(this.getDateTime().timeZone || 'UTC');
        const dt = now.clone().startOf('day');
        const ranges = {
          'today': [dt.unix(), dt.add(1, 'days').unix()],
          'tomorrow': [dt.unix(), dt.add(1, 'days').unix()],
          'yesterday': [dt.add(-3, 'days').unix(), dt.add(1, 'days').unix()]
        };
        if (d.unix() >= ranges['today'][0] && d.unix() < ranges['today'][1]) {
          return this.translate('Today') + ' ' + d.format(timeFormat);
        }
        if (d.unix() > ranges['tomorrow'][0] && d.unix() < ranges['tomorrow'][1]) {
          return this.translate('Tomorrow') + ' ' + d.format(timeFormat);
        }
        if (d.unix() > ranges['yesterday'][0] && d.unix() < ranges['yesterday'][1]) {
          return this.translate('Yesterday') + ' ' + d.format(timeFormat);
        }
        const readableFormat = this.getDateTime().getReadableDateFormat();
        if (d.format('YYYY') === now.format('YYYY')) {
          return d.format(readableFormat) + ' ' + d.format(timeFormat);
        }
        return d.format(readableFormat + ', YYYY') + ' ' + d.format(timeFormat);
      }
      return this.getDateTime().toDisplay(value);
    }
    initTimepicker() {
      const $time = this.$time;
      const modalBodyElement = this.element.closest('.modal-body');
      $time.timepicker({
        step: this.params.minuteStep || 30,
        scrollDefaultNow: true,
        timeFormat: this.timeFormatMap[this.getDateTime().timeFormat],
        appendTo: modalBodyElement ? $(modalBodyElement) : 'body'
      });
      $time.parent().find('button.time-picker-btn').on('click', () => {
        $time.timepicker('show');
      });
    }
    setDefaultTime() {
      const dtString = (0, _moment.default)('2014-01-01 00:00').format(this.getDateTime().getDateTimeFormat()) || '';
      const pair = this.splitDatetime(dtString);
      if (pair.length === 2) {
        this.$time.val(pair[1]);
      }
    }
    splitDatetime(value) {
      const m = (0, _moment.default)(value, this.getDateTime().getDateTimeFormat());
      const dateValue = m.format(this.getDateTime().getDateFormat());
      const timeValue = value.substr(dateValue.length + 1);
      return [dateValue, timeValue];
    }
    setup() {
      super.setup();
      this.on('remove', () => this.destroyTimepicker());
      this.on('mode-changed', () => this.destroyTimepicker());
    }
    destroyTimepicker() {
      if (this.$time && this.$time[0]) {
        this.$time.timepicker('remove');
      }
    }
    afterRender() {
      super.afterRender();
      if (this.mode !== this.MODE_EDIT) {
        return;
      }
      this.$date = this.$element;
      const $time = this.$time = this.$el.find('input.time-part');
      this.initTimepicker();
      this.$element.on('change.datetime', () => {
        if (this.$element.val() && !$time.val()) {
          this.setDefaultTime();
          this.trigger('change');
        }
      });
      let timeout = false;
      let isTimeFormatError = false;
      let previousValue = $time.val();
      $time.on('change', () => {
        if (!timeout) {
          if (isTimeFormatError) {
            $time.val(previousValue);
            return;
          }
          if (this.noneOption && $time.val() === '' && this.$date.val() !== '') {
            $time.val(this.noneOption);
            return;
          }
          this.trigger('change');
          previousValue = $time.val();
        }
        timeout = true;
        setTimeout(() => timeout = false, 100);
      });
      $time.on('timeFormatError', () => {
        isTimeFormatError = true;
        setTimeout(() => isTimeFormatError = false, 50);
      });
    }

    /**
     * @param {string} string
     * @return {string|-1|null}
     */
    parse(string) {
      if (!string) {
        return null;
      }
      return this.getDateTime().fromDisplay(string);
    }
    fetch() {
      const data = {};
      const date = this.$date.val();
      const time = this.$time.val();
      let value = null;
      if (date !== '' && time !== '') {
        value = this.parse(date + ' ' + time);
      }
      data[this.name] = value;
      return data;
    }

    // noinspection JSUnusedGlobalSymbols
    validateDatetime() {
      if (this.model.get(this.name) === -1) {
        const msg = this.translate('fieldShouldBeDatetime', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg);
        return true;
      }
    }

    /** @inheritDoc */
    fetchSearch() {
      const data = super.fetchSearch();
      if (data) {
        data.dateTime = true;
        delete data.date;
      }
      return data;
    }
  }
  var _default = _exports.default = DatetimeFieldView;
});

define("views/fields/currency", ["exports", "views/fields/float", "ui/select"], function (_exports, _float, _select) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _float = _interopRequireDefault(_float);
  _select = _interopRequireDefault(_select);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/currency */

  /**
   * A currency field.
   *
   * @extends IntFieldView<module:views/fields/currency~params>
   */
  class CurrencyFieldView extends _float.default {
    /**
     * @typedef {Object} module:views/fields/currency~options
     * @property {
     *     module:views/fields/currency~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/currency~params
     * @property {number} [min] A max value.
     * @property {number} [max] A max value.
     * @property {boolean} [required] Required.
     * @property {boolean} [disableFormatting] Disable formatting.
     * @property {number|null} [decimalPlaces] A number of decimal places. @todo
     * @property {boolean} [onlyDefaultCurrency] Only the default currency.
     * @property {boolean} [decimal] Stored as decimal.
     * @property {number} [scale] Scale (for decimal).
     */

    /**
     * @param {
     *     module:views/fields/currency~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'currency';
    editTemplate = 'fields/currency/edit';
    detailTemplate = 'fields/currency/detail';
    // noinspection JSUnusedGlobalSymbols
    detailTemplate1 = 'fields/currency/detail-1';
    // noinspection JSUnusedGlobalSymbols
    detailTemplate2 = 'fields/currency/detail-2';
    // noinspection JSUnusedGlobalSymbols
    detailTemplate3 = 'fields/currency/detail-3';
    listTemplate = 'fields/currency/list';
    // noinspection JSUnusedGlobalSymbols
    listTemplate1 = 'fields/currency/list-1';
    // noinspection JSUnusedGlobalSymbols
    listTemplate2 = 'fields/currency/list-2';
    // noinspection JSUnusedGlobalSymbols
    listTemplate3 = 'fields/currency/list-3';
    detailTemplateNoCurrency = 'fields/currency/detail-no-currency';
    maxDecimalPlaces = 3;

    /**
     * @inheritDoc
     * @type {Array<(function (): boolean)|string>}
     */
    validations = ['required', 'number', 'range'];

    /** @inheritDoc */
    data() {
      const currencyValue = this.model.get(this.currencyFieldName) || this.getPreferences().get('defaultCurrency') || this.getConfig().get('defaultCurrency');
      const multipleCurrencies = !this.isSingleCurrency || currencyValue !== this.defaultCurrency;
      return {
        ...super.data(),
        currencyFieldName: this.currencyFieldName,
        currencyValue: currencyValue,
        currencyList: this.currencyList,
        currencySymbol: this.getMetadata().get(['app', 'currency', 'symbolMap', currencyValue]) || '',
        multipleCurrencies: multipleCurrencies,
        defaultCurrency: this.defaultCurrency
      };
    }

    /** @inheritDoc */
    setup() {
      super.setup();
      this.currencyFieldName = this.name + 'Currency';
      this.defaultCurrency = this.getConfig().get('defaultCurrency');
      this.currencyList = this.getConfig().get('currencyList') || [this.defaultCurrency];
      this.decimalPlaces = this.getConfig().get('currencyDecimalPlaces');
      if (this.params.onlyDefaultCurrency) {
        this.currencyList = [this.defaultCurrency];
      }
      this.isSingleCurrency = this.currencyList.length <= 1;
      const currencyValue = this.currencyValue = this.model.get(this.currencyFieldName) || this.defaultCurrency;
      if (!this.currencyList.includes(currencyValue)) {
        this.currencyList = Espo.Utils.clone(this.currencyList);
        this.currencyList.push(currencyValue);
      }
    }

    /** @inheritDoc */
    setupAutoNumericOptions() {
      this.autoNumericOptions = {
        digitGroupSeparator: this.thousandSeparator || '',
        decimalCharacter: this.decimalMark,
        modifyValueOnWheel: false,
        selectOnFocus: false,
        decimalPlaces: this.decimalPlaces,
        allowDecimalPadding: true,
        showWarnings: false,
        formulaMode: true
      };
      if (this.decimalPlaces === null) {
        this.autoNumericOptions.decimalPlaces = this.decimalPlacesRawValue;
        this.autoNumericOptions.decimalPlacesRawValue = this.decimalPlacesRawValue;
        this.autoNumericOptions.allowDecimalPadding = false;
      }
    }
    getCurrencyFormat() {
      return this.getConfig().get('currencyFormat') || 1;
    }
    _getTemplateName() {
      if (this.mode === this.MODE_DETAIL || this.mode === this.MODE_LIST) {
        let prop;
        if (this.mode === this.MODE_LIST) {
          prop = 'listTemplate' + this.getCurrencyFormat().toString();
        } else {
          prop = 'detailTemplate' + this.getCurrencyFormat().toString();
        }
        if (this.options.hideCurrency) {
          prop = 'detailTemplateNoCurrency';
        }
        if (prop in this) {
          return this[prop];
        }
      }
      return super._getTemplateName();
    }
    formatNumber(value) {
      return this.formatNumberDetail(value);
    }
    formatNumberDetail(value) {
      if (value !== null) {
        const currencyDecimalPlaces = this.decimalPlaces;
        if (currencyDecimalPlaces === 0) {
          value = Math.round(value);
        } else if (currencyDecimalPlaces) {
          value = Math.round(value * Math.pow(10, currencyDecimalPlaces)) / Math.pow(10, currencyDecimalPlaces);
        } else {
          value = Math.round(value * Math.pow(10, this.maxDecimalPlaces)) / Math.pow(10, this.maxDecimalPlaces);
        }
        const parts = value.toString().split(".");
        parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, this.thousandSeparator);
        if (currencyDecimalPlaces === 0) {
          return parts[0];
        } else if (currencyDecimalPlaces) {
          let decimalPartLength = 0;
          if (parts.length > 1) {
            decimalPartLength = parts[1].length;
          } else {
            parts[1] = '';
          }
          if (currencyDecimalPlaces && decimalPartLength < currencyDecimalPlaces) {
            const limit = currencyDecimalPlaces - decimalPartLength;
            for (let i = 0; i < limit; i++) {
              parts[1] += '0';
            }
          }
        }
        return parts.join(this.decimalMark);
      }
      return '';
    }
    parse(value) {
      value = value !== '' ? value : null;
      if (value === null) {
        return null;
      }
      value = value.split(this.thousandSeparator).join('');
      value = value.split(this.decimalMark).join('.');
      if (this.params.decimal) {
        // @todo Obtain default scale.
        const scale = this.params.scale || 4;
        const parts = value.split('.');
        const decimalPart = parts[1] || '';
        if (decimalPart.length < scale) {
          value = parts[0] + '.' + decimalPart.padEnd(scale, '0');
        }
      }
      if (!this.params.decimal) {
        value = parseFloat(value);
      }
      return value;
    }
    afterRender() {
      super.afterRender();
      if (this.mode === this.MODE_EDIT) {
        this.$currency = this.$el.find(`[data-name="${this.currencyFieldName}"]`);
        if (this.$currency.length) {
          this.$currency.on('change', () => {
            this.model.set(this.currencyFieldName, this.$currency.val(), {
              ui: true
            });
          });
          _select.default.init(this.$currency);
        }
      }
    }

    // noinspection JSUnusedGlobalSymbols
    validateNumber() {
      if (!this.params.decimal) {
        return this.validateFloat();
      }
      const value = this.model.get(this.name);
      if (Number.isNaN(Number(value))) {
        const msg = this.translate('fieldShouldBeNumber', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg);
        return true;
      }
    }
    fetch() {
      let value = this.$element.val().trim();
      value = this.parse(value);
      const data = {};
      let currencyValue = this.$currency.length ? this.$currency.val() : this.defaultCurrency;
      if (value === null) {
        currencyValue = null;
      }
      data[this.name] = value;
      data[this.currencyFieldName] = currencyValue;
      return data;
    }
  }
  var _default = _exports.default = CurrencyFieldView;
});

define("views/fields/bool", ["exports", "views/fields/base", "ui/select"], function (_exports, _base, _select) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _select = _interopRequireDefault(_select);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/bool */

  /**
   * A boolean field (checkbox).
   *
   * @extends BaseFieldView<module:views/fields/bool~params>
   */
  class BoolFieldView extends _base.default {
    /**
     * @typedef {Object} module:views/fields/bool~options
     * @property {
     *     module:views/fields/bool~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/varchar~params
     */

    /**
     * @param {
     *     module:views/fields/bool~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'bool';
    listTemplate = 'fields/bool/list';
    detailTemplate = 'fields/bool/detail';
    editTemplate = 'fields/bool/edit';
    searchTemplate = 'fields/bool/search';
    validations = [];
    initialSearchIsNotIdle = true;

    // noinspection JSCheckFunctionSignatures
    /** @inheritDoc */
    data() {
      const data = super.data();
      data.valueIsSet = this.model.has(this.name);

      // noinspection JSValidateTypes
      return data;
    }
    afterRender() {
      super.afterRender();
      if (this.mode === this.MODE_SEARCH) {
        this.$element.on('change', () => {
          this.trigger('change');
        });
        _select.default.init(this.$element);
      }
    }
    fetch() {
      // noinspection JSUnresolvedReference
      const value = this.$element.get(0).checked;
      const data = {};
      data[this.name] = value;
      return data;
    }
    fetchSearch() {
      const type = this.$element.val();
      if (!type) {
        return null;
      }
      if (type === 'any') {
        return {
          type: 'or',
          value: [{
            type: 'isTrue',
            attribute: this.name
          }, {
            type: 'isFalse',
            attribute: this.name
          }],
          data: {
            type: type
          }
        };
      }
      return {
        type: type,
        data: {
          type: type
        }
      };
    }
    getSearchType() {
      return this.getSearchParamsData().type || this.searchParams.type || 'isTrue';
    }
  }
  var _default = _exports.default = BoolFieldView;
});

define("views/fields/attachment-multiple", ["exports", "views/fields/base", "helpers/file-upload", "helpers/misc/attachment-insert-from-source"], function (_exports, _base, _fileUpload, _attachmentInsertFromSource) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _fileUpload = _interopRequireDefault(_fileUpload);
  _attachmentInsertFromSource = _interopRequireDefault(_attachmentInsertFromSource);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/attachment-multiple */

  /**
   * An attachment-multiple field.
   *
   * @extends BaseFieldView<module:views/fields/attachment-multiple~params>
   */
  class AttachmentMultipleFieldView extends _base.default {
    /**
     * @typedef {Object} module:views/fields/attachment-multiple~options
     * @property {
     *     module:views/fields/attachment-multiple~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/attachment-multiple~params
     * @property {boolean} [required] Required.
     * @property {boolean} [showPreviews] Show previews.
     * @property {'x-small'|'small'|'medium'|'large'} [previewSize] A preview size.
     * @property {string[]} [sourceList] A source list.
     * @property {string[]} [accept] Formats to accept.
     * @property {number} [maxFileSize] A max file size (in Mb).
     * @property {number} [maxCount] A max number of items.
     */

    /**
     * @param {
     *     module:views/fields/attachment-multiple~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'attachmentMultiple';
    listTemplate = 'fields/attachments-multiple/list';
    detailTemplate = 'fields/attachments-multiple/detail';
    editTemplate = 'fields/attachments-multiple/edit';
    searchTemplate = 'fields/link-multiple/search';
    previewSize = 'medium';
    nameHashName;
    idsName;
    nameHash;
    foreignScope;
    accept = null;
    /** @protected */
    showPreviews = true;
    /** @protected */
    showPreviewsInListMode = false;
    initialSearchIsNotIdle = true;

    /**
     * @inheritDoc
     * @type {Array<(function (): boolean)|string>}
     */
    validations = ['ready', 'required', 'maxCount'];
    searchTypeList = ['isNotEmpty', 'isEmpty'];

    /**
     * @private
     * @type {Object.<string, true>}
     */
    uploadedIdMap;
    events = {
      /** @this AttachmentMultipleFieldView */
      'click a.remove-attachment': function (e) {
        const $div = $(e.currentTarget).parent();
        const id = $div.attr('data-id');
        if (id) {
          this.deleteAttachment(id);
        }
        $div.parent().remove();
        this.$el.find('input.file').val(null);
        setTimeout(() => this.focusOnUploadButton(), 10);
      },
      /** @this AttachmentMultipleFieldView */
      'change input.file': function (e) {
        const $file = $(e.currentTarget);
        const files = e.currentTarget.files;
        this.uploadFiles(files);
        e.target.value = null;
        $file.replaceWith($file.clone(true));
      },
      /** @this AttachmentMultipleFieldView */
      'click a.action[data-action="insertFromSource"]': function (e) {
        const name = $(e.currentTarget).data('name');
        this.insertFromSource(name);
      },
      /** @this AttachmentMultipleFieldView */
      'click a[data-action="showImagePreview"]': function (e) {
        e.preventDefault();
        const id = $(e.currentTarget).data('id');
        const attachmentIdList = this.model.get(this.idsName) || [];
        const typeHash = this.model.get(this.typeHashName) || {};
        const imageIdList = [];
        attachmentIdList.forEach(cId => {
          if (!this.isTypeIsImage(typeHash[cId])) {
            return;
          }
          imageIdList.push(cId);
        });
        const imageList = [];
        imageIdList.forEach(cId => {
          imageList.push({
            id: cId,
            name: this.nameHash[cId]
          });
        });
        this.createView('preview', 'views/modals/image-preview', {
          id: id,
          model: this.model,
          name: this.nameHash[id],
          imageList: imageList
        }, view => {
          view.render();
        });
      },
      /** @this AttachmentMultipleFieldView */
      'keydown label.attach-file-label': function (e) {
        const key = Espo.Utils.getKeyFromKeyEvent(e);
        if (key === 'Enter') {
          const element = /** @type {HTMLInputElement} */this.$el.find('input.file').get(0);
          element.click();
        }
      }
    };

    // noinspection JSCheckFunctionSignatures
    data() {
      const ids = this.model.get(this.idsName);
      const data = {
        ...super.data(),
        idValues: this.model.get(this.idsName),
        idValuesString: ids ? ids.join(',') : '',
        nameHash: this.model.get(this.nameHashName),
        foreignScope: this.foreignScope,
        valueIsSet: this.model.has(this.idsName),
        acceptAttribute: this.acceptAttribute
      };
      if (this.mode === this.MODE_EDIT) {
        data.fileSystem = ~this.sourceList.indexOf('FileSystem');
        data.sourceList = this.sourceList;
      }

      // noinspection JSValidateTypes
      return data;
    }
    setup() {
      this.nameHashName = this.name + 'Names';
      this.typeHashName = this.name + 'Types';
      this.idsName = this.name + 'Ids';
      this.foreignScope = 'Attachment';
      this.previewSize = this.options.previewSize || this.params.previewSize || this.previewSize;
      this.previewTypeList = this.getMetadata().get(['app', 'image', 'previewFileTypeList']) || [];
      this.imageSizes = this.getMetadata().get(['app', 'image', 'sizes']) || {};
      this.nameHash = _.clone(this.model.get(this.nameHashName)) || {};
      if ('showPreviews' in this.params) {
        this.showPreviews = this.params.showPreviews;
      }
      if ('accept' in this.params) {
        this.accept = this.params.accept;
      }
      if (this.accept && this.accept.length) {
        this.acceptAttribute = this.accept.join(', ');
      }
      const sourceDefs = this.getMetadata().get(['clientDefs', 'Attachment', 'sourceDefs']) || {};
      this.sourceList = Espo.Utils.clone(this.params.sourceList || []);
      this.sourceList = this.sourceList.concat(this.getMetadata().get(['clientDefs', 'Attachment', 'generalSourceList']) || []).filter((item, i, self) => {
        return self.indexOf(item) === i;
      }).filter(item => {
        const defs = sourceDefs[item] || {};
        if (defs.accessDataList) {
          if (!Espo.Utils.checkAccessDataList(defs.accessDataList, this.getAcl(), this.getUser())) {
            return false;
          }
        }
        if (defs.configCheck) {
          const arr = defs.configCheck.split('.');
          if (!this.getConfig().getByPath(arr)) {
            return false;
          }
        }
        return true;
      });
      this.listenTo(this.model, 'change:' + this.nameHashName, () => {
        this.nameHash = _.clone(this.model.get(this.nameHashName)) || {};
      });
      this.on('remove', () => {
        if (this.resizeIsBeingListened) {
          $(window).off('resize.' + this.cid);
        }
        this.uploadedIdMap = {};
      });
      this.on('inline-edit-off', () => {
        this.isUploading = false;
      });
      if (this.recordHelper) {
        this.listenTo(this.recordHelper, `upload-files:${this.name}`, /** File[] */files => {
          if (!this.isEditMode()) {
            return;
          }
          this.uploadFiles(files);
        });
      }
      this.uploadedIdMap = {};
    }
    setupSearch() {
      this.addHandler('change', 'select.search-type', (e, /** HTMLSelectElement */target) => {
        this.handleSearchType(target.value);
        this.trigger('change');
      });
    }
    focusOnInlineEdit() {
      this.focusOnUploadButton();
    }
    focusOnUploadButton() {
      this.$el.find('.attach-file-label').focus();
    }

    /**
     * @protected
     */
    empty() {
      this.clearIds();
      this.$attachments.empty();
    }

    /**
     * @private
     */
    handleResize() {
      const width = this.$el.width();
      this.$el.find('img.image-preview').css('maxWidth', width + 'px');
    }

    /**
     * @protected
     * @param {string} id
     */
    deleteAttachment(id) {
      this.removeId(id);
      if (this.model.isNew()) {
        this.getModelFactory().create('Attachment', attachment => {
          attachment.id = id;
          attachment.destroy();
        });
      }
    }

    /**
     * @protected
     * @param {string} id
     * @param {string} [size]
     * @return {string}
     */
    getImageUrl(id, size) {
      let url = `${this.getBasePath()}?entryPoint=image&id=${id}`;
      if (size) {
        url += '&size=' + size;
      }
      if (this.getUser().get('portalId')) {
        url += '&portalId=' + this.getUser().get('portalId');
      }
      return url;
    }

    /**
     * @protected
     * @param {string} id
     * @return {string}
     */
    getDownloadUrl(id) {
      let url = `${this.getBasePath()}?entryPoint=download&id=${id}`;
      if (this.getUser().get('portalId')) {
        url += '&portalId=' + this.getUser().get('portalId');
      }
      return url;
    }

    /**
     * @protected
     * @param {string} id
     */
    removeId(id) {
      const arr = _.clone(this.model.get(this.idsName) || []);
      const i = arr.indexOf(id);
      arr.splice(i, 1);
      this.model.set(this.idsName, arr);
      const nameHash = _.clone(this.model.get(this.nameHashName) || {});
      delete nameHash[id];
      this.model.set(this.nameHashName, nameHash);
      const typeHash = _.clone(this.model.get(this.typeHashName) || {});
      delete typeHash[id];
      this.model.set(this.typeHashName, typeHash);
    }

    /**
     * @protected
     * @param {boolean} [silent]
     */
    clearIds(silent) {
      silent = silent || false;
      this.model.set(this.idsName, [], {
        silent: silent
      });
      this.model.set(this.nameHashName, {}, {
        silent: silent
      });
      this.model.set(this.typeHashName, {}, {
        silent: silent
      });
    }

    /**
     * @protected
     * @param {import('model').default} attachment
     * @param {boolean} [ui]
     */
    pushAttachment(attachment, ui) {
      const arr = _.clone(this.model.get(this.idsName) || []);
      arr.push(attachment.id);
      this.model.set(this.idsName, arr, {
        ui: ui
      });
      const typeHash = _.clone(this.model.get(this.typeHashName) || {});
      typeHash[attachment.id] = attachment.get('type');
      this.model.set(this.typeHashName, typeHash, {
        ui: ui
      });
      const nameHash = _.clone(this.model.get(this.nameHashName) || {});
      nameHash[attachment.id] = attachment.get('name');
      this.model.set(this.nameHashName, nameHash, {
        ui: ui
      });
      this.uploadedIdMap[attachment.id] = true;
    }

    /**
     * @protected
     * @param {string} name
     * @param {string} type
     * @param {string} id
     * @return {string|null}
     */
    getEditPreview(name, type, id) {
      if (!~this.previewTypeList.indexOf(type)) {
        return null;
      }
      const size = id in this.uploadedIdMap ? undefined : 'small';

      // noinspection HtmlRequiredAltAttribute,RequiredAttributes
      return $('<img>').attr('src', this.getImageUrl(id, size)).attr('title', name).attr('alt', name).attr('draggable', 'false').css({
        maxWidth: (this.imageSizes['small'] || {})[0],
        maxHeight: (this.imageSizes['small'] || {})[1]
      }).get(0).outerHTML;
    }
    getBoxPreviewHtml(name, type, id) {
      const $text = $('<span>').text(name);
      if (!id) {
        return $text.get(0).outerHTML;
      }
      if (this.showPreviews) {
        const html = this.getEditPreview(name, type, id);
        if (html) {
          return html;
        }
      }
      const url = this.getBasePath() + '?entryPoint=download&id=' + id;
      return $('<a>').attr('href', url).attr('target', '_BLANK').text(name).get(0).outerHTML;
    }
    addAttachmentBox(name, type, id) {
      const $attachments = this.$attachments;
      const $remove = $('<a>').attr('role', 'button').attr('tabindex', '0').addClass('remove-attachment pull-right').append($('<span>').addClass('fas fa-times'));
      const previewHtml = this.getBoxPreviewHtml(name, type, id);
      const $att = $('<div>').addClass('gray-box').append($remove).append($('<span>').addClass('preview').append($(previewHtml)));
      const $container = $('<div>').append($att);
      $attachments.append($container);
      if (id) {
        $att.attr('data-id', id);
        return $att;
      }
      const $loading = $('<span>').addClass('small uploading-message').text(this.translate('Uploading...'));
      $container.append($loading);
      $att.on('ready', () => {
        $loading.html(this.translate('Ready'));
        const id = $att.attr('data-id');
        const previewHtml = this.getBoxPreviewHtml(name, type, id);
        $att.find('.preview').html(previewHtml);
        if ($att.find('.preview').find('img').length) {
          $loading.remove();
        }
      });
      return $att;
    }
    showValidationMessage(msg, selector, view) {
      const $label = this.$el.find('label');
      const title = $label.attr('title');
      $label.attr('title', '');
      super.showValidationMessage(msg, selector, view);
      $label.attr('title', title);
    }
    getMaxFileSize() {
      let maxFileSize = this.params.maxFileSize || 0;
      const noChunk = !this.getConfig().get('attachmentUploadChunkSize');
      const attachmentUploadMaxSize = this.getConfig().get('attachmentUploadMaxSize') || 0;
      const appMaxUploadSize = this.getHelper().getAppParam('maxUploadSize') || 0;
      if (!maxFileSize || maxFileSize > attachmentUploadMaxSize) {
        maxFileSize = attachmentUploadMaxSize;
      }
      if (noChunk && maxFileSize > appMaxUploadSize) {
        maxFileSize = appMaxUploadSize;
      }
      return maxFileSize;
    }

    /**
     * Upload files.
     *
     * @param {FileList|File[]} files
     */
    uploadFiles(files) {
      let uploadedCount = 0;
      let totalCount = 0;
      let exceedsMaxFileSize = false;
      const maxFileSize = this.getMaxFileSize();
      if (maxFileSize) {
        for (let i = 0; i < files.length; i++) {
          const file = files[i];
          if (file.size > maxFileSize * 1024 * 1024) {
            exceedsMaxFileSize = true;
          }
        }
      }
      if (exceedsMaxFileSize) {
        const msg = this.translate('fieldMaxFileSizeError', 'messages').replace('{field}', this.getLabelText()).replace('{max}', maxFileSize.toString());
        this.showValidationMessage(msg, 'label');
        return;
      }
      this.isUploading = true;
      this.getModelFactory().create('Attachment', model => {
        const canceledList = [];
        const fileList = [];
        const uploadedList = [];
        for (let i = 0; i < files.length; i++) {
          fileList.push(files[i]);
          totalCount++;
        }
        const uploadHelper = new _fileUpload.default();
        fileList.forEach(file => {
          const $attachmentBox = this.addAttachmentBox(file.name, file.type);
          const $uploadingMsg = $attachmentBox.parent().find('.uploading-message');
          const mediator = {};
          $attachmentBox.find('.remove-attachment').on('click.uploading', () => {
            canceledList.push(attachment.cid);
            totalCount--;
            if (uploadedCount === totalCount) {
              this.isUploading = false;
              if (totalCount) {
                this.afterAttachmentsUploaded.call(this);
              }
            }
            mediator.isCanceled = true;
          });
          const attachment = model.clone();
          attachment.set('role', 'Attachment');
          attachment.set('parentType', this.model.entityType);
          attachment.set('field', this.name);
          uploadHelper.upload(file, attachment, {
            afterChunkUpload: size => {
              const msg = Math.floor(size / file.size * 100) + '%';
              $uploadingMsg.html(msg);
            },
            afterAttachmentSave: attachment => {
              $attachmentBox.attr('data-id', attachment.id);
            },
            mediator: mediator
          }).then(() => {
            if (canceledList.indexOf(attachment.cid) !== -1) {
              return;
            }
            this.pushAttachment(attachment, true);
            $attachmentBox.attr('data-id', attachment.id);
            $attachmentBox.trigger('ready');
            uploadedCount++;
            uploadedList.push(attachment);
            if (uploadedCount === totalCount && this.isUploading) {
              this.model.trigger('attachment-uploaded:' + this.name, uploadedList);
              this.afterAttachmentsUploaded.call(this);
              this.isUploading = false;
              setTimeout(() => {
                if (document.activeElement && document.activeElement.tagName !== 'BODY') {
                  return;
                }
                this.focusOnUploadButton();
              }, 50);
            }
          }).catch(() => {
            if (mediator.isCanceled) {
              return;
            }
            $attachmentBox.remove();
            $uploadingMsg.remove();
            totalCount--;
            if (!totalCount) {
              this.isUploading = false;
            }
            if (uploadedCount === totalCount && this.isUploading) {
              this.isUploading = false;
              this.afterAttachmentsUploaded.call(this);
            }
          });
        });
      });
    }
    afterAttachmentsUploaded() {}
    afterRender() {
      if (this.mode === this.MODE_EDIT) {
        this.$attachments = this.$el.find('div.attachments');
        const ids = this.model.get(this.idsName) || [];
        const nameHash = this.model.get(this.nameHashName);
        const typeHash = this.model.get(this.typeHashName) || {};
        ids.forEach(id => {
          if (nameHash) {
            const name = nameHash[id];
            const type = typeHash[id] || null;
            this.addAttachmentBox(name, type, id);
          }
        });
        this.$el.off('drop');
        this.$el.off('dragover');
        this.$el.off('dragleave');
        this.$el.on('drop', e => {
          e.preventDefault();
          e.stopPropagation();
          const event = /** @type {DragEvent} */e.originalEvent;
          if (event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length) {
            this.uploadFiles(event.dataTransfer.files);
          }
        });
        this.$el.get(0).addEventListener('dragover', e => {
          e.preventDefault();
        });
        this.$el.get(0).addEventListener('dragleave', e => {
          e.preventDefault();
        });
      }
      if (this.mode === this.MODE_SEARCH) {
        const type = this.$el.find('select.search-type').val();
        this.handleSearchType(type);
      }
      if (this.mode === this.MODE_DETAIL) {
        if (this.previewSize === 'large') {
          this.handleResize();
          this.resizeIsBeingListened = true;
          $(window).on('resize.' + this.cid, () => {
            this.handleResize();
          });
        }
      }
    }
    isTypeIsImage(type) {
      if (~this.previewTypeList.indexOf(type)) {
        return true;
      }
      return false;
    }

    /**
     * @return {string}
     */
    getDetailPreview(name, type, id) {
      if (!this.isTypeIsImage(type)) {
        return $('<span>').text(name).get(0).outerHTML;
      }

      // noinspection HtmlRequiredAltAttribute,RequiredAttributes
      return $('<a>').attr('data-action', 'showImagePreview').attr('data-id', id).attr('title', name).attr('href', this.getImageUrl(id)).append($('<img>').attr('src', this.getImageUrl(id, this.previewSize)).addClass('image-preview').attr('alt', name).css({
        maxWidth: (this.imageSizes[this.previewSize] || {})[0],
        maxHeight: (this.imageSizes[this.previewSize] || {})[1]
      })).get(0).outerHTML;
    }
    getValueForDisplay() {
      if (this.isDetailMode() || this.isListMode()) {
        const nameHash = this.nameHash;
        const typeHash = this.model.get(this.typeHashName) || {};
        const ids = /** @type {string[]} */this.model.get(this.idsName) || [];
        const previews = [];
        const names = [];
        for (const id of ids) {
          const type = typeHash[id] || false;
          const name = nameHash[id];
          if (this.showPreviews && this.previewTypeList.includes(type) && (this.isDetailMode() || this.isListMode() && this.showPreviewsInListMode)) {
            previews.push($('<div>').addClass('attachment-preview').append(this.getDetailPreview(name, type, id)));
            continue;
          }
          names.push($('<div>').addClass('attachment-block').append($('<span>').addClass('fas fa-paperclip text-soft small'), ' ', $('<a>').attr('href', this.getDownloadUrl(id)).attr('target', '_BLANK').text(name)));
        }
        let containerClassName = null;
        if (this.previewSize === 'large') {
          containerClassName = 'attachment-block-container-large';
        }
        if (this.previewSize === 'small') {
          containerClassName = 'attachment-block-container-small';
        }
        if (names.length === 0 && previews.length === 0) {
          return '';
        }
        const $container = $('<div>').append($('<div>').addClass('attachment-block-container').addClass(containerClassName).append(previews)).append(names);
        return $container.get(0).innerHTML;
      }
    }

    /**
     * @private
     * @param {string} source
     */
    insertFromSource(source) {
      const helper = new _attachmentInsertFromSource.default(this);
      helper.insert({
        source: source,
        onInsert: models => {
          models.forEach(model => this.pushAttachment(model));
        }
      });
    }
    validateRequired() {
      if (this.isRequired()) {
        if ((this.model.get(this.idsName) || []).length === 0) {
          const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
          this.showValidationMessage(msg, 'label');
          return true;
        }
      }
    }

    // noinspection JSUnusedGlobalSymbols
    validateReady() {
      if (this.isUploading) {
        const msg = this.translate('fieldIsUploading', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg, 'label');
        return true;
      }
    }

    // noinspection JSUnusedGlobalSymbols
    validateMaxCount() {
      const maxCount = this.params.maxCount;
      if (!maxCount) {
        return false;
      }
      const idList = this.model.get(this.idsName) || [];
      if (idList.length === 0) {
        return false;
      }
      if (idList.length <= maxCount) {
        return false;
      }
      const msg = this.translate('fieldExceedsMaxCount', 'messages').replace('{field}', this.getLabelText()).replace('{maxCount}', maxCount.toString());
      this.showValidationMessage(msg, 'label');
      return true;
    }
    fetch() {
      const data = {};
      data[this.idsName] = this.model.get(this.idsName) || [];
      return data;
    }

    // noinspection JSUnusedLocalSymbols
    handleSearchType(type) {
      this.$el.find('div.link-group-container').addClass('hidden');
    }
    fetchSearch() {
      const type = this.$el.find('select.search-type').val();
      if (type === 'isEmpty') {
        return {
          type: 'isNotLinked',
          data: {
            type: type
          }
        };
      }
      if (type === 'isNotEmpty') {
        return {
          type: 'isLinked',
          data: {
            type: type
          }
        };
      }
      return null;
    }
  }
  var _default = _exports.default = AttachmentMultipleFieldView;
});

define("views/fields/array", ["exports", "views/fields/base", "helpers/reg-exp-pattern", "ui/multi-select"], function (_exports, _base, _regExpPattern, _multiSelect) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _regExpPattern = _interopRequireDefault(_regExpPattern);
  _multiSelect = _interopRequireDefault(_multiSelect);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/array */

  /**
   * An array field.
   *
   * @extends BaseFieldView<module:views/fields/array~params>
   */
  class ArrayFieldView extends _base.default {
    /**
     * @typedef {Object} module:views/fields/array~options
     * @property {
     *     module:views/fields/array~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/array~params
     * @property {string} [translation] A translation string. E.g. `Global.scopeNames`.
     * @property {string[]} [options] Select options.
     * @property {boolean} [required] Required.
     * @property {boolean} [displayAsList] Display as list (line breaks).
     * @property {boolean} [displayAsLabel] Display as label.
     * @property {string|'state'} [labelType] A label type.
     * @property {boolean} [noEmptyString] No empty string.
     * @property {string} [optionsReference] A reference to options. E.g. `Account.industry`.
     * @property {string} [optionsPath] An options metadata path.
     * @property {boolean} [isSorted] To sort options.
     * @property {Object.<string, string>} [translatedOptions] Option translations.
     * @property {Object.<string, 'warning'|'danger'|'success'|'info'|'primary'>} [style] A style map.
     * @property {number} [maxCount] A max number of items.
     * @property {boolean} [allowCustomOptions] Allow custom options.
     * @property {string} [pattern] A regular expression pattern.
     * @property {boolean} [keepItems] Disable the ability to add or remove items. Reordering is allowed.
     * @property {number} [maxItemLength] Max item length. If not specified, 100 is used.
     */

    /**
     * @param {
     *     module:views/fields/array~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'array';
    listTemplate = 'fields/array/list';
    listLinkTemplate = 'fields/array/list-link';
    detailTemplate = 'fields/array/detail';
    editTemplate = 'fields/array/edit';
    searchTemplate = 'fields/array/search';
    searchTypeList = ['anyOf', 'noneOf', 'allOf', 'isEmpty', 'isNotEmpty'];
    maxItemLength = null;

    /**
     * @inheritDoc
     * @type {Array<(function (): boolean)|string>}
     */
    validations = ['required', 'maxCount'];
    MAX_ITEM_LENGTH = 100;

    /**
     * An add-item model view.
     *
     * @protected
     * @type {string}
     */
    addItemModalView = 'views/modals/array-field-add';
    /**
     * @protected
     * @type {string}
     */
    itemDelimiter = ':,:';
    /**
     * @protected
     * @type {boolean}
     */
    matchAnyWord = true;
    /**
     * @protected
     * @type {Object|null}
     */
    translatedOptions = null;

    // noinspection JSCheckFunctionSignatures
    /** @inheritDoc */
    data() {
      const itemHtmlList = [];
      (this.selected || []).forEach(value => {
        itemHtmlList.push(this.getItemHtml(value || ''));
      });

      // noinspection JSValidateTypes
      return {
        ...super.data(),
        selected: this.selected,
        translatedOptions: this.translatedOptions,
        hasAdd: !!this.params.options && !this.params.keepItems,
        keepItems: this.params.keepItems,
        itemHtmlList: itemHtmlList,
        isEmpty: (this.selected || []).length === 0,
        valueIsSet: this.model.has(this.name),
        maxItemLength: this.maxItemLength || this.MAX_ITEM_LENGTH,
        allowCustomOptions: this.allowCustomOptions
      };
    }

    /** @inheritDoc */
    events = {
      /** @this ArrayFieldView */
      'click [data-action="removeValue"]': function (e) {
        const value = $(e.currentTarget).attr('data-value').toString();
        this.removeValue(value);
        this.focusOnElement();
      },
      /** @this ArrayFieldView */
      'click [data-action="showAddModal"]': function () {
        this.actionAddItem();
      }
    };
    setup() {
      super.setup();
      this.noEmptyString = this.params.noEmptyString;
      if (this.params.maxItemLength != null) {
        this.maxItemLength = this.params.maxItemLength;
      }
      this.listenTo(this.model, 'change:' + this.name, () => {
        this.selected = Espo.Utils.clone(this.model.get(this.name)) || [];
      });
      this.selected = Espo.Utils.clone(this.model.get(this.name) || []);
      if (Object.prototype.toString.call(this.selected) !== '[object Array]') {
        this.selected = [];
      }
      this.styleMap = this.params.style || {};
      let optionsPath = this.params.optionsPath;
      /** @type {?string} */
      const optionsReference = this.params.optionsReference;
      if (!optionsPath && optionsReference) {
        const [refEntityType, refField] = optionsReference.split('.');
        optionsPath = `entityDefs.${refEntityType}.fields.${refField}.options`;
        if (Object.keys(this.styleMap).length === 0) {
          this.styleMap = this.getMetadata().get(`entityDefs.${refEntityType}.fields.${refField}.style`) || {};
        }
      }
      if (optionsPath) {
        this.params.options = Espo.Utils.clone(this.getMetadata().get(optionsPath)) || [];
      }
      this.setupOptions();
      if ('translatedOptions' in this.options) {
        this.translatedOptions = this.options.translatedOptions;
      }
      if ('translatedOptions' in this.params) {
        this.translatedOptions = this.params.translatedOptions;
      }
      if (!this.translatedOptions) {
        this.setupTranslation();
      }
      this.displayAsLabel = this.params.displayAsLabel || this.displayAsLabel;
      this.displayAsList = this.params.displayAsList || this.displayAsList;
      if (this.params.isSorted && this.translatedOptions) {
        this.params.options = Espo.Utils.clone(this.params.options);
        this.params.options = this.params.options.sort((v1, v2) => {
          return (this.translatedOptions[v1] || v1).localeCompare(this.translatedOptions[v2] || v2);
        });
      }
      if (this.options.customOptionList) {
        this.setOptionList(this.options.customOptionList, true);
      }
      if (this.params.allowCustomOptions || !this.params.options) {
        this.allowCustomOptions = true;
      }
      if (this.type === 'array') {
        this.validations.push('noInputValue');
      }
    }
    focusOnElement() {
      const $button = this.$el.find('button[data-action="showAddModal"]');
      if ($button[0]) {
        // noinspection JSUnresolvedReference
        $button[0].focus({
          preventScroll: true
        });
        return;
      }
      const $input = this.$el.find('input.main-element');
      if ($input[0]) {
        // noinspection JSUnresolvedReference
        $input[0].focus({
          preventScroll: true
        });
      }
    }
    setupSearch() {
      this.events['change select.search-type'] = e => {
        this.handleSearchType($(e.currentTarget).val());
      };
    }
    handleSearchType(type) {
      const $inputContainer = this.$el.find('div.input-container');
      if (~['anyOf', 'noneOf', 'allOf'].indexOf(type)) {
        $inputContainer.removeClass('hidden');
      } else {
        $inputContainer.addClass('hidden');
      }
    }
    setupTranslation() {
      let obj = {};
      let translation = this.params.translation;
      /** @type {?string} */
      const optionsReference = this.params.optionsReference;
      if (!translation && optionsReference) {
        const [refEntityType, refField] = optionsReference.split('.');
        translation = `${refEntityType}.options.${refField}`;
      }
      this.translatedOptions = null;
      if (!this.params.options) {
        return;
      }
      obj = translation ? this.getLanguage().translatePath(translation) : this.translate(this.name, 'options', this.model.name);
      const map = {};
      this.params.options.forEach(o => {
        if (typeof obj === 'object' && o in obj) {
          map[o] = obj[o];
          return;
        }
        map[o] = o;
      });
      this.translatedOptions = map;
    }
    setupOptions() {}
    setOptionList(optionList, silent) {
      const previousOptions = this.params.options;
      if (!this.originalOptionList) {
        this.originalOptionList = this.params.options;
      }
      this.params.options = Espo.Utils.clone(optionList);
      const isChanged = !_(previousOptions).isEqual(optionList);
      if (this.isEditMode() && !silent && isChanged) {
        const selectedOptionList = [];
        this.selected.forEach(option => {
          if (~optionList.indexOf(option)) {
            selectedOptionList.push(option);
          }
        });
        this.selected = selectedOptionList;
        if (this.isRendered()) {
          this.reRender();
          this.trigger('change');
        } else {
          this.once('after:render', () => {
            this.trigger('change');
          });
        }
      }
    }
    setTranslatedOptions(translatedOptions) {
      this.translatedOptions = translatedOptions;
    }
    resetOptionList() {
      if (!this.originalOptionList) {
        return;
      }
      const previousOptions = this.params.options;
      this.params.options = Espo.Utils.clone(this.originalOptionList);
      const isChanged = !_(previousOptions).isEqual(this.originalOptionList);
      if (!this.isEditMode() || !isChanged) {
        return;
      }
      if (this.isRendered()) {
        this.reRender();
      }
    }
    controlAddItemButton() {
      const $select = this.$select;
      if (!$select) {
        return;
      }
      if (!$select.get(0)) {
        return;
      }
      const value = $select.val().toString().trim();
      if (!value && this.params.noEmptyString) {
        this.$addButton.addClass('disabled').attr('disabled', 'disabled');
      } else {
        this.$addButton.removeClass('disabled').removeAttr('disabled');
      }
    }
    afterRender() {
      if (this.isEditMode()) {
        this.$list = this.$el.find('.list-group');
        const $select = this.$select = this.$el.find('.select');
        if (this.allowCustomOptions) {
          this.$addButton = this.$el.find('button[data-action="addItem"]');
          this.$addButton.on('click', () => {
            const value = $select.val().toString();
            this.addValueFromUi(value);
            this.focusOnElement();
          });
          $select.on('input', () => this.controlAddItemButton());
          $select.on('keydown', e => {
            const key = Espo.Utils.getKeyFromKeyEvent(e);
            if (key === 'Enter') {
              const value = $select.val().toString();
              this.addValueFromUi(value);
            }
          });
          this.controlAddItemButton();
        }
        this.$list.sortable({
          stop: () => {
            this.fetchFromDom();
            this.trigger('change');
          },
          distance: 5,
          cancel: 'input,textarea,button,select,option,a[role="button"]',
          cursor: 'grabbing'
        });
      }
      if (this.isSearchMode()) {
        this.renderSearch();
      }
    }

    /**
     * @param {string} value
     */
    addValueFromUi(value) {
      value = value.trim();
      if (this.noEmptyString && value === '') {
        return;
      }
      if (this.params.pattern) {
        const helper = new _regExpPattern.default();
        const result = helper.validate(this.params.pattern, value, this.name, this.entityType);
        if (result) {
          setTimeout(() => this.showValidationMessage(result.message, 'input.select'), 10);
          return;
        }
      }
      this.addValue(value);
      this.$select.val('');
      this.controlAddItemButton();
    }
    renderSearch() {
      this.$element = this.$el.find('.main-element');
      const valueList = this.getSearchParamsData().valueList || this.searchParams.valueFront || [];
      this.$element.val(valueList.join(this.itemDelimiter));
      const items = [];
      (this.params.options || []).forEach(value => {
        let label = this.getLanguage().translateOption(value, this.name, this.scope);
        if (this.translatedOptions) {
          if (value in this.translatedOptions) {
            label = this.translatedOptions[value];
          }
        }
        if (label === '') {
          return;
        }
        items.push({
          value: value,
          text: label,
          style: this.styleMap[value] || undefined
        });
      });
      valueList.filter(item => !(this.params.options || []).includes(item)).forEach(item => {
        items.push({
          value: item,
          text: item
        });
      });

      /** @type {module:ui/multi-select~Options} */
      const multiSelectOptions = {
        items: items,
        delimiter: this.itemDelimiter,
        matchAnyWord: this.matchAnyWord,
        allowCustomOptions: this.allowCustomOptions,
        create: input => {
          return {
            value: input,
            text: input
          };
        }
      };
      _multiSelect.default.init(this.$element, multiSelectOptions);
      this.$el.find('.selectize-dropdown-content').addClass('small');
      const type = this.$el.find('select.search-type').val();
      this.handleSearchType(type);
      this.$el.find('select.search-type').on('change', () => {
        this.trigger('change');
      });
      this.$element.on('change', () => {
        this.trigger('change');
      });
    }
    fetchFromDom() {
      const selected = [];
      this.$el.find('.list-group .list-group-item').each((i, el) => {
        const value = $(el).attr('data-value').toString();
        selected.push(value);
      });
      this.selected = selected;
    }
    getValueForDisplay() {
      // Do not use the `html` method to avoid XSS.

      /** @var {string[]} */
      const list = this.selected.map(item => {
        let label = null;
        if (this.translatedOptions !== null) {
          if (item in this.translatedOptions) {
            label = this.translatedOptions[item];
          }
        }
        if (label === null) {
          label = item;
        }
        if (label === '') {
          label = this.translate('None');
        }
        const style = this.styleMap[item] || 'default';
        if (this.displayAsLabel) {
          let className = 'label label-md label-' + style;
          if (this.params.labelType === 'state') {
            className += ' label-state';
          }
          return $('<span>').addClass(className).text(label).get(0).outerHTML;
        }
        if (style && style !== 'default') {
          return $('<span>').addClass('text-' + style).text(label).get(0).outerHTML;
        }
        return $('<span>').text(label).get(0).outerHTML;
      });
      if (this.displayAsList) {
        if (!list.length) {
          return '';
        }
        let itemClassName = 'multi-enum-item-container';
        if (this.displayAsLabel) {
          itemClassName += ' multi-enum-item-label-container';
        }
        return list.map(item => $('<div>').addClass(itemClassName).html(item).get(0).outerHTML).join('');
      }
      if (this.displayAsLabel) {
        return list.join(' ');
      }
      return list.join(', ');
    }

    /**
     * @protected
     * @param {string} value
     * @return {string}
     */
    getItemHtml(value) {
      // Do not use the `html` method to avoid XSS.

      if (this.translatedOptions !== null) {
        for (const item in this.translatedOptions) {
          if (this.translatedOptions[item] === value) {
            value = item;
            break;
          }
        }
      }
      value = value.toString();
      const text = this.translatedOptions && value in this.translatedOptions ? this.translatedOptions[value].toString() : value;
      return $('<div>').addClass('list-group-item').attr('data-value', value).css('cursor', 'default').append(!this.params.keepItems ? $('<a>').attr('role', 'button').attr('tabindex', '0').addClass('pull-right').attr('data-value', value).attr('data-action', 'removeValue').append($('<span>').addClass('fas fa-times')) : undefined).append($('<span>').addClass('text').text(text)).append('').get(0).outerHTML;
    }

    /**
     * @param {string} value
     */
    addValue(value) {
      if (this.selected.indexOf(value) === -1) {
        const html = this.getItemHtml(value);
        this.$list.append(html);
        this.selected.push(value);
        this.trigger('change');
      }
    }

    /**
     * @param {string} value
     */
    removeValue(value) {
      const valueInternal = CSS.escape(value);
      this.$list.children('[data-value="' + valueInternal + '"]').remove();
      const index = this.selected.indexOf(value);
      this.selected.splice(index, 1);
      this.trigger('change');
    }
    fetch() {
      const data = {};
      let list = Espo.Utils.clone(this.selected || []);
      if (this.params.isSorted && this.translatedOptions) {
        list = list.sort((v1, v2) => {
          return (this.translatedOptions[v1] || v1).localeCompare(this.translatedOptions[v2] || v2);
        });
      }
      data[this.name] = list;
      return data;
    }
    fetchSearch() {
      const type = this.$el.find('select.search-type').val() || 'anyOf';
      let valueList;
      if (~['anyOf', 'noneOf', 'allOf'].indexOf(type)) {
        valueList = this.$element.val().split(this.itemDelimiter);
        if (valueList.length === 1 && valueList[0] === '') {
          valueList = [];
        }
        if (valueList.length === 0) {
          if (type === 'anyOf') {
            return {
              type: 'any',
              data: {
                type: type,
                valueList: valueList
              }
            };
          }
          if (type === 'noneOf') {
            return {
              type: 'any',
              data: {
                type: type,
                valueList: valueList
              }
            };
          }
          if (type === 'allOf') {
            return {
              type: 'any',
              data: {
                type: type,
                valueList: valueList
              }
            };
          }
        }
      }
      if (type === 'anyOf') {
        const data = {
          type: 'arrayAnyOf',
          value: valueList,
          data: {
            type: 'anyOf',
            valueList: valueList
          }
        };
        if (!valueList.length) {
          data.value = null;
        }
        return data;
      }
      if (type === 'noneOf') {
        return {
          type: 'arrayNoneOf',
          value: valueList,
          data: {
            type: 'noneOf',
            valueList: valueList
          }
        };
      }
      if (type === 'allOf') {
        const data = {
          type: 'arrayAllOf',
          value: valueList,
          data: {
            type: 'allOf',
            valueList: valueList
          }
        };
        if (!valueList.length) {
          data.value = null;
        }
        return data;
      }
      if (type === 'isEmpty') {
        return {
          type: 'arrayIsEmpty',
          data: {
            type: 'isEmpty'
          }
        };
      }
      if (type === 'isNotEmpty') {
        return {
          type: 'arrayIsNotEmpty',
          data: {
            type: 'isNotEmpty'
          }
        };
      }
      return null;
    }
    validateRequired() {
      if (this.isRequired()) {
        const value = this.model.get(this.name);
        if (!value || value.length === 0) {
          const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
          this.showValidationMessage(msg, '.array-control-container');
          return true;
        }
      }
      return false;
    }

    // noinspection JSUnusedGlobalSymbols
    validateMaxCount() {
      if (this.params.maxCount) {
        const itemList = this.model.get(this.name) || [];
        if (itemList.length > this.params.maxCount) {
          const msg = this.translate('fieldExceedsMaxCount', 'messages').replace('{field}', this.getLabelText()).replace('{maxCount}', this.params.maxCount.toString());
          this.showValidationMessage(msg, '.array-control-container');
          return true;
        }
      }
      return false;
    }
    getSearchType() {
      return this.getSearchParamsData().type || 'anyOf';
    }

    /**
     * @return {{
     *    translatedOptions: Object.<string, *>|null,
     *    options: string[],
     * } | Object.<string, *>}
     */
    getAddItemModalOptions() {
      const options = [];
      this.params.options.forEach(item => {
        if (!~this.selected.indexOf(item)) {
          options.push(item);
        }
      });
      return {
        options: options,
        translatedOptions: this.translatedOptions
      };
    }

    /**
     * @protected
     * @return {Promise<import('views/modals/array-field-add').default>}
     */
    actionAddItem() {
      return this.createView('dialog', this.addItemModalView, this.getAddItemModalOptions(), view => {
        view.render();
        view.once('add', item => {
          this.addValue(item);
          view.close();
        });
        view.once('add-mass', items => {
          items.forEach(item => this.addValue(item));
          view.close();
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @protected
     * @return {boolean}
     */
    validateNoInputValue() {
      if (!this.element) {
        return false;
      }
      const input = this.element.querySelector('input.select');
      if (!(input instanceof HTMLInputElement)) {
        return false;
      }
      if (!input.value) {
        return false;
      }
      const message = this.translate('arrayInputNotEmpty', 'messages');
      this.showValidationMessage(message, 'input.select');
      return true;
    }
  }
  var _default = _exports.default = ArrayFieldView;
});

define("views/email/record/detail", ["exports", "views/record/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/email/record/detail */

  class EmailDetailRecordView extends _detail.default {
    sideView = 'views/email/record/detail-side';
    duplicateAction = false;
    layoutNameConfigure() {
      if (this.model.isNew()) {
        return;
      }
      const status = this.model.get('status');
      if (status === 'Draft') {
        this.layoutName = 'composeSmall';
        return;
      }
      let isRestricted = false;
      if (status === 'Sent') {
        isRestricted = true;
      }
      if (status === 'Archived') {
        if (this.model.get('createdById') === this.getHelper().getAppParam('systemUserId') || !this.model.get('createdById') || this.model.get('isImported')) {
          isRestricted = true;
        }
      }
      if (isRestricted) {
        this.layoutName += 'Restricted';
      }
      this.isRestricted = isRestricted;
    }
    init() {
      super.init();
      this.layoutNameConfigure();
    }
    setup() {
      super.setup();
      if (['Archived', 'Sent'].includes(this.model.get('status'))) {
        this.shortcutKeyCtrlEnterAction = 'save';
      }
      this.addButton({
        name: 'sendFromDetail',
        label: 'Send',
        hidden: true,
        onClick: () => this.actionSendFromDetail()
      });
      this.dropdownEditItemList.push({
        name: 'send',
        label: 'Send',
        onClick: () => this.actionSendFromDetail()
      });
      this.controlSendButton();
      this.listenTo(this.model, 'change:status', () => this.controlSendButton());
      if (this.model.get('status') !== 'Draft' && this.model.has('isRead') && !this.model.get('isRead')) {
        this.model.set('isRead', true, {
          sync: true
        });
      }
      this.listenTo(this.model, 'sync', () => {
        if (!this.model.get('isRead') && this.model.get('status') !== 'Draft') {
          this.model.set('isRead', true, {
            sync: true
          });
        }
      });
      if (!(this.model.get('isHtml') && this.model.get('bodyPlain'))) {
        this.listenToOnce(this.model, 'sync', () => {
          if (this.model.get('isHtml') && this.model.get('bodyPlain')) {
            this.showActionItem('showBodyPlain');
          }
        });
      }
      if (this.model.attributes.isUsers) {
        this.addDropdownItem({
          label: 'Mark as Important',
          name: 'markAsImportant',
          hidden: this.model.get('isImportant'),
          groupIndex: 1
        });
        this.addDropdownItem({
          label: 'Unmark Importance',
          name: 'markAsNotImportant',
          hidden: !this.model.get('isImportant'),
          groupIndex: 1
        });
        this.addDropdownItem({
          label: 'Move to Trash',
          name: 'moveToTrash',
          hidden: this.isInTrash(),
          groupIndex: 2
        });
        this.addDropdownItem({
          label: 'Retrieve from Trash',
          name: 'retrieveFromTrash',
          hidden: !this.isInTrash(),
          groupIndex: 2
        });
        this.addDropdownItem({
          labelTranslation: 'Email.actions.moveToArchive',
          name: 'moveToArchive',
          groupIndex: 2,
          hidden: this.isInArchive(),
          onClick: () => this.actionMoveToArchive()
        });
        this.addDropdownItem({
          label: 'Move to Folder',
          name: 'moveToFolder',
          groupIndex: 2
        });
      } else if (this.model.attributes.groupFolderId) {
        this.addDropdownItem({
          label: 'Move to Trash',
          name: 'moveToTrash',
          hidden: this.isInTrash(),
          groupIndex: 2
        });
        this.addDropdownItem({
          label: 'Retrieve from Trash',
          name: 'retrieveFromTrash',
          hidden: !this.isInTrash(),
          groupIndex: 2
        });
        this.addDropdownItem({
          labelTranslation: 'Email.actions.moveToArchive',
          name: 'moveToArchive',
          groupIndex: 2,
          hidden: this.isInArchive() || this.isInTrash(),
          onClick: () => this.actionMoveToArchive()
        });
        this.addDropdownItem({
          label: 'Move to Folder',
          name: 'moveToFolder',
          groupIndex: 2,
          hidden: this.isInTrash()
        });
      } else {
        this.addDropdownItem({
          label: 'Move to Folder',
          name: 'moveToFolder',
          groupIndex: 2
        });
      }
      this.addDropdownItem({
        label: 'Show Plain Text',
        name: 'showBodyPlain',
        hidden: !(this.model.get('isHtml') && this.model.get('bodyPlain')),
        groupIndex: 7
      });
      this.addDropdownItem({
        label: 'Print',
        name: 'print',
        groupIndex: 7
      });
      this.listenTo(this.model, 'change:isImportant', () => {
        if (this.model.get('isImportant')) {
          this.hideActionItem('markAsImportant');
          this.showActionItem('markAsNotImportant');
        } else {
          this.hideActionItem('markAsNotImportant');
          this.showActionItem('markAsImportant');
        }
      });
      this.listenTo(this.model, 'change:inTrash change:groupStatusFolder', () => {
        if (this.isInTrash()) {
          this.hideActionItem('moveToTrash');
          this.showActionItem('retrieveFromTrash');
        } else {
          this.hideActionItem('retrieveFromTrash');
          this.showActionItem('moveToTrash');
        }
      });
      this.listenTo(this.model, 'change:inArchive change:groupStatusFolder', () => {
        if (this.isInArchive()) {
          this.hideActionItem('moveToArchive');
        } else {
          this.showActionItem('moveToArchive');
        }
      });
      this.handleTasksField();
      this.listenTo(this.model, 'change:tasksIds', () => this.handleTasksField());
      if (this.getAcl().checkScope('User')) {
        this.addDropdownItem({
          label: 'View Users',
          name: 'viewUsers'
        });
      }
      this.setFieldReadOnly('replied');
      if (this.model.get('status') === 'Draft') {
        this.setFieldReadOnly('dateSent');
        this.controlSelectTemplateField();
        this.on('after:mode-change', () => this.controlSelectTemplateField());
      }
      if (this.isRestricted) {
        this.handleAttachmentField();
        this.listenTo(this.model, 'change:attachmentsIds', () => this.handleAttachmentField());
        this.handleCcField();
        this.listenTo(this.model, 'change:cc', () => this.handleCcField());
        this.handleBccField();
        this.listenTo(this.model, 'change:bcc', () => this.handleBccField());
      }
    }
    controlSelectTemplateField() {
      if (this.mode === this.MODE_EDIT) {
        // Not implemented for detail view yet.
        this.hideField('selectTemplate');
        return;
      }
      this.hideField('selectTemplate');
    }
    controlSendButton() {
      const status = this.model.get('status');
      if (status === 'Draft') {
        this.showActionItem('send');
        this.showActionItem('sendFromDetail');
        return;
      }
      this.hideActionItem('sendFromDetail');
      this.hideActionItem('send');
    }

    // noinspection JSUnusedGlobalSymbols
    actionSaveDraft() {
      this.actionSaveAndContinueEditing();
    }
    actionMarkAsImportant() {
      Espo.Ajax.postRequest('Email/inbox/important', {
        id: this.model.id
      });
      this.model.set('isImportant', true, {
        sync: true
      });
    }
    actionMarkAsNotImportant() {
      Espo.Ajax.deleteRequest('Email/inbox/important', {
        id: this.model.id
      });
      this.model.set('isImportant', false, {
        sync: true
      });
    }
    actionMoveToTrash() {
      Espo.Ajax.postRequest('Email/inbox/inTrash', {
        id: this.model.id
      }).then(() => {
        Espo.Ui.warning(this.translate('Moved to Trash', 'labels', 'Email'));
      });
      if (this.model.attributes.groupFolderId) {
        this.model.set('groupStatusFolder', 'Trash', {
          sync: true
        });
      } else {
        this.model.set('inTrash', true, {
          sync: true
        });
      }
      if (this.model.collection) {
        this.model.collection.trigger('moving-to-trash', this.model.id, true);
      }
    }

    // noinspection JSUnusedGlobalSymbols
    actionRetrieveFromTrash() {
      Espo.Ajax.deleteRequest('Email/inbox/inTrash', {
        id: this.model.id
      }).then(() => {
        Espo.Ui.warning(this.translate('Retrieved from Trash', 'labels', 'Email'));
      });
      this.model.set('inTrash', false, {
        sync: true
      });
      if (this.model.attributes.groupFolderId) {
        this.model.set('groupStatusFolder', null, {
          sync: true
        });
      }
      if (this.model.collection) {
        this.model.collection.trigger('retrieving-from-trash', this.model.id, true);
      }
    }
    actionMoveToFolder() {
      let currentFolderId = undefined;
      if (!this.isInArchive() && !this.isInTrash()) {
        if (this.model.attributes.groupFolderId) {
          currentFolderId = 'group:' + this.model.attributes.groupFolderId;
        } else if (this.model.attributes.folderId) {
          currentFolderId = this.model.attributes.folderId;
        }
      } else if (this.isInArchive()) {
        currentFolderId = 'archive';
      }
      this.createView('dialog', 'views/email-folder/modals/select-folder', {
        headerText: this.translate('Move to Folder', 'labels', 'Email'),
        isGroup: !!this.model.attributes.groupFolderId || !this.model.attributes.isUsers,
        noArchive: !this.model.attributes.groupFolderId && !this.model.attributes.isUsers,
        currentFolderId: currentFolderId
      }, view => {
        view.render();
        this.listenToOnce(view, 'select', /** string|null */folderId => {
          this.clearView('dialog');
          Espo.Ajax.postRequest(`Email/inbox/folders/${folderId}`, {
            id: this.model.id
          }).then(() => {
            if (this.model.attributes.groupFolderId) {
              if (folderId === 'archive') {
                this.model.set('groupStatusFolder', 'Archive');
              } else {
                this.model.set('groupStatusFolder', null);
              }
            } else {
              this.model.set('inArchive', folderId === 'archive');
            }
            if (folderId === 'inbox' || folderId === 'archive') {
              folderId = null;
            }
            if (!folderId) {
              this.model.set('groupFolderId', null);
              this.model.set('groupFolderName', null);
            }
            this.model.set('folderId', folderId);
            this.model.fetch();
            Espo.Ui.success(this.translate('Done'));
          });
        });
      });
    }
    actionMoveToArchive() {
      Espo.Ui.notifyWait();
      Espo.Ajax.postRequest(`Email/inbox/folders/archive`, {
        id: this.model.id
      }).then(() => {
        this.model.attributes.groupFolderId ? this.model.set('groupStatusFolder', 'Archive', {
          sync: true
        }) : this.model.set('inArchive', true, {
          sync: true
        });
        Espo.Ui.info(this.translate('Moved to Archive', 'labels', 'Email'));
        if (this.model.collection) {
          this.model.collection.trigger('moving-to-archive', this.model.id, true);
        }
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionShowBodyPlain() {
      this.createView('bodyPlain', 'views/email/modals/body-plain', {
        model: this.model
      }, view => {
        view.render();
      });
    }
    handleAttachmentField() {
      if ((this.model.get('attachmentsIds') || []).length === 0) {
        this.hideField('attachments');
      } else {
        this.showField('attachments');
      }
    }
    handleCcField() {
      if (!this.model.get('cc')) {
        this.hideField('cc');
      } else {
        this.showField('cc');
      }
    }
    handleBccField() {
      if (!this.model.get('bcc')) {
        this.hideField('bcc');
      } else {
        this.showField('bcc');
      }
    }

    /**
     * Send.
     *
     * @return {Promise}
     */
    send() {
      const model = this.model;
      const status = model.get('status');
      model.set('status', 'Sending');
      this.isSending = true;
      const afterSend = () => {
        model.trigger('after:send');
        this.trigger('after:send');
        this.isSending = false;
      };
      this.once('after:save', afterSend, this);
      this.once('cancel:save', () => {
        this.off('after:save', afterSend);
        this.isSending = false;
        model.set('status', status);
      });
      this.once('before:save', () => {
        Espo.Ui.notify(this.translate('Sending...', 'labels', 'Email'));
      });
      return this.save();
    }

    /**
     * @private
     * @return {Promise<void>}
     */
    async actionSendFromDetail() {
      await this.confirm(this.translate('confirmSend', 'messages', 'Email'));
      await this.setEditMode();
      await this.send();
      await this.setDetailMode();
    }

    // noinspection JSUnusedGlobalSymbols
    exitAfterDelete() {
      let folderId = this.rootData.selectedFolderId ? this.rootData.selectedFolderId : null;
      if (folderId === 'inbox') {
        folderId = null;
      }
      const options = {
        isReturn: true,
        isReturnThroughLink: false,
        folder: folderId
      };
      let url = '#' + this.scope;
      let action = null;
      if (folderId) {
        action = 'list';
        url += '/list/folder=' + folderId;
      }
      this.getRouter().dispatch(this.scope, action, options);
      this.getRouter().navigate(url, {
        trigger: false
      });
      return true;
    }

    // noinspection JSUnusedGlobalSymbols
    actionViewUsers(data) {
      const viewName = this.getMetadata().get(`clientDefs.${this.model.entityType}.relationshipPanels.users.viewModalView`) || this.getMetadata().get(`clientDefs.User.modalViews.relatedList`) || 'views/modals/related-list';
      const options = {
        model: this.model,
        link: 'users',
        scope: 'User',
        url: `${this.model.entityType}/${this.model.id}/users`,
        filtersDisabled: true,
        createDisabled: true,
        selectDisabled: !this.getAcl().checkModel(this.model, 'edit') || this.getAcl().getPermissionLevel('assignment') === 'no',
        unlinkDisabled: !this.getUser().isAdmin(),
        removeDisabled: true,
        rowActionsView: 'views/record/row-actions/relationship-view-and-unlink'
      };
      if (data.viewOptions) {
        for (const item in data.viewOptions) {
          options[item] = data.viewOptions[item];
        }
      }
      Espo.Ui.notifyWait();
      this.createView('modalRelatedList', viewName, options, view => {
        Espo.Ui.notify(false);
        view.render();
        this.listenTo(view, 'action', (event, element) => {
          Espo.Utils.handleAction(this, event, element);
        });
        this.listenToOnce(view, 'close', () => {
          this.clearView('modalRelatedList');
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionSend() {
      this.send().then(() => {
        this.model.set('status', 'Sent', {
          sync: true
        });
        if (this.mode !== this.MODE_DETAIL) {
          this.setDetailMode();
          this.setFieldReadOnly('dateSent');
          this.setFieldReadOnly('name');
          this.setFieldReadOnly('attachments');
          this.setFieldReadOnly('isHtml');
          this.setFieldReadOnly('from');
          this.setFieldReadOnly('to');
          this.setFieldReadOnly('cc');
          this.setFieldReadOnly('bcc');
        }
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionPrint() {
      /** @type {module:views/fields/wysiwyg} */
      const bodyView = this.getFieldView('body');
      if (!bodyView) {
        return;
      }
      let iframe = /** @type HTMLIFrameElement */bodyView.$el.find('iframe').get(0);
      if (iframe) {
        iframe.contentWindow.print();
        return;
      }
      const el = bodyView.$el.get(0);
      /** @type {Element} */
      const recordElement = this.$el.get(0);
      iframe = document.createElement('iframe');
      iframe.style.display = 'none';
      recordElement.append(iframe);
      const contentWindow = iframe.contentWindow;
      contentWindow.document.open();
      contentWindow.document.write(el.innerHTML);
      contentWindow.document.close();
      contentWindow.focus();
      contentWindow.print();
      contentWindow.onafterprint = () => {
        recordElement.removeChild(iframe);
      };
    }
    errorHandlerSendingFail(data) {
      if (!this.model.id) {
        this.model.id = data.id;
      }
      let msg = this.translate('sendingFailed', 'strings', 'Email');
      if (data.message) {
        let part = data.message;
        if (this.getLanguage().has(part, 'messages', 'Email')) {
          part = this.translate(part, 'messages', 'Email');
        }
        msg += ': ' + part;
      }
      Espo.Ui.error(msg, true);
      console.error(msg);
    }
    handleTasksField() {
      if ((this.model.get('tasksIds') || []).length === 0) {
        this.hideField('tasks');
        return;
      }
      this.showField('tasks');
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlS(e) {
      if (this.inlineEditModeIsOn || this.buttonsDisabled) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      if (this.mode !== this.MODE_EDIT) {
        return;
      }
      if (!this.saveAndContinueEditingAction) {
        return;
      }
      if (!this.hasAvailableActionItem('saveAndContinueEditing')) {
        return;
      }
      this.actionSaveAndContinueEditing();
    }

    /**
     * @private
     * @return {boolean}
     */
    isInTrash() {
      return this.model.attributes.groupFolderId ? this.model.attributes.groupStatusFolder === 'Trash' : this.model.attributes.inTrash;
    }

    /**
     * @private
     * @return {boolean}
     */
    isInArchive() {
      return this.model.attributes.groupFolderId ? this.model.attributes.groupStatusFolder === 'Archive' : this.model.attributes.inArchive;
    }
  }
  var _default = _exports.default = EmailDetailRecordView;
});

define("views/email/fields/email-address", ["exports", "views/fields/base", "ui/autocomplete"], function (_exports, _base, _autocomplete) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _autocomplete = _interopRequireDefault(_autocomplete);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailEmailAddressFieldView extends _base.default {
    getAutocompleteMaxCount() {
      if (this.autocompleteMaxCount) {
        return this.autocompleteMaxCount;
      }
      return this.getConfig().get('recordsPerPage');
    }
    afterRender() {
      super.afterRender();
      this.$input = this.$el.find('input');
      if (this.mode === this.MODE_SEARCH && this.getAcl().check('Email', 'create')) {
        this.initSearchAutocomplete();
      }
      if (this.mode === this.MODE_SEARCH) {
        this.$input.on('input', () => {
          this.trigger('change');
        });
      }
    }
    initSearchAutocomplete() {
      this.$input = this.$input || this.$el.find('input');

      /** @type {module:ajax.AjaxPromise & Promise<any>} */
      let lastAjaxPromise;
      const autocomplete = new _autocomplete.default(this.$input.get(0), {
        name: this.name,
        autoSelectFirst: true,
        triggerSelectOnValidInput: true,
        focusOnSelect: true,
        minChars: 1,
        forceHide: true,
        handleFocusMode: 2,
        onSelect: item => {
          this.$input.val(item.emailAddress);
        },
        formatResult: item => {
          return this.getHelper().escapeString(item.name) + ' &#60;' + this.getHelper().escapeString(item.id) + '&#62;';
        },
        lookupFunction: query => {
          if (lastAjaxPromise && lastAjaxPromise.getReadyState() < 4) {
            lastAjaxPromise.abort();
          }
          lastAjaxPromise = Espo.Ajax.getRequest('EmailAddress/search', {
            q: query,
            maxSize: this.getAutocompleteMaxCount()
          });
          return lastAjaxPromise.then(/** Record[] */response => {
            let result = response.map(item => {
              return {
                id: item.emailAddress,
                name: item.entityName,
                emailAddress: item.emailAddress,
                entityId: item.entityId,
                entityName: item.entityName,
                entityType: item.entityType,
                data: item.emailAddress,
                value: item.emailAddress
              };
            });
            if (this.skipCurrentInAutocomplete) {
              const current = this.$input.val();
              result = result.filter(item => item.emailAddress !== current);
            }
            return result;
          });
        }
      });
      this.once('render remove', () => autocomplete.dispose());
    }
    fetchSearch() {
      let value = this.$element.val();
      if (typeof value.trim === 'function') {
        value = value.trim();
      }
      if (value) {
        return {
          type: 'equals',
          value: value
        };
      }
      return null;
    }
  }
  var _default = _exports.default = EmailEmailAddressFieldView;
});

define("views/email/fields/body", ["exports", "views/fields/wysiwyg"], function (_exports, _wysiwyg) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _wysiwyg = _interopRequireDefault(_wysiwyg);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailBodyFieldView extends _wysiwyg.default {
    useIframe = true;
    hasBodyPlainField = true;
    toShowQuotePart = false;

    /**
     * @type {string}
     * @private
     */
    replyPart;
    getAttributeList() {
      return ['body', 'bodyPlain'];
    }
    setupToolbar() {
      super.setupToolbar();
      const attachmentItem = this.toolbar.find(it => it[0] === 'attachment');
      if (attachmentItem) {
        attachmentItem[1].push('insert-field');
      } else {
        this.toolbar.push(['insert-field', ['insert-field']]);
      }
      this.buttons['insert-field'] = () => {
        const ui = $.summernote.ui;
        const button = ui.button({
          contents: '<i class="fas fa-plus"></i>',
          tooltip: this.translate('Insert Field', 'labels', 'Email'),
          click: () => {
            this.showInsertFieldModal();
          }
        });
        return button.render();
      };
      this.listenTo(this.model, 'change', m => {
        if (!this.isRendered()) {
          return;
        }
        if (m.hasChanged('parentId') || m.hasChanged('to')) {
          this.controlInsertFieldButton();
        }
      });
    }
    onEditModeSet() {
      this.toShowQuotePart = false;
      return super.onEditModeSet();
    }
    afterRender() {
      super.afterRender();
      this.controlInsertFieldButton();
      if (this.isReadMode() && this.replyPart) {
        this.element.appendChild(this.createShowQuotePartButton());
      }
    }
    controlInsertFieldButton() {
      const $b = this.$el.find('.note-insert-field > button');
      if (this.model.get('to') && this.model.get('to').length || this.model.get('parentId')) {
        $b.removeAttr('disabled').removeClass('disabled');
      } else {
        $b.attr('disabled', 'disabled').addClass('disabled');
      }
    }
    showInsertFieldModal() {
      let to = this.model.get('to');
      if (to) {
        to = to.split(';')[0].trim();
      }
      const parentId = this.model.get('parentId');
      const parentType = this.model.get('parentType');
      Espo.Ui.notifyWait();
      this.createView('insertFieldDialog', 'views/email/modals/insert-field', {
        parentId: parentId,
        parentType: parentType,
        to: to
      }, view => {
        view.render();
        Espo.Ui.notify();
        this.listenToOnce(view, 'insert', /** string */string => {
          if (this.$summernote) {
            if (string.includes('\n')) {
              string = string.replace(/\r\n|\r|\n/g, '<br>');
              const html = '<p>' + string + '</p>';
              this.$summernote.summernote('editor.pasteHTML', html);
            } else {
              this.$summernote.summernote('editor.insertText', string);
            }
          }
          this.clearView('insertFieldDialog');
        });
      });
    }
    getValueForIframe() {
      let contents = super.getValueForIframe();
      if (this.toShowQuotePart) {
        this.replyPart = undefined;
        return contents;
      }
      this.replyPart = undefined;
      contents = this.processQuotePart(contents);
      if (!this.replyPart) {
        return contents;
      }
      return contents;
    }
    getValueForEdit() {
      this.replyPart = undefined;
      const contents = super.getValueForEdit();
      return this.processQuotePart(contents, true);
    }

    /**
     * @private
     * @param {string} contents
     * @param {boolean} [isEdit=false]
     * @return {string}
     */
    processQuotePart(contents, isEdit) {
      const container = document.createElement('div');
      container.innerHTML = contents;
      let selector = `:scope > [data-quote-start="true"]`;
      if (!isEdit) {
        selector += ', :scope > blockquote:last-child' + ', :scope > div:last-child > blockquote:last-child';
      }
      let element = container.querySelector(selector);
      if (!element) {
        return contents;
      }
      container.querySelectorAll('style, link[ref="stylesheet"]').forEach(element => {
        element.parentElement.removeChild(element);
      });
      this.replyPart = '';
      /** @type {HTMLElement[]} */
      const removeList = [];
      while (element) {
        this.replyPart += element.outerHTML;
        removeList.push(element);
        element = element.nextElementSibling;
      }
      removeList.forEach(element => element.parentElement.removeChild(element));
      if (isEdit) {
        this.element.appendChild(this.createShowQuotePartButton());
      }
      return container.innerHTML;
    }

    /**
     * @private
     * @return {HTMLAnchorElement}
     */
    createShowQuotePartButton() {
      const a = this.showQuoteButtonElement = document.createElement('a');
      a.setAttribute('role', 'button');
      a.innerHTML = '...';
      a.classList.add('show-quote-button', 'btn', 'btn-default', 'btn-sm');
      a.addEventListener('click', () => this.showQuotePart());
      return a;
    }

    /** @private */
    showQuotePart() {
      if (this.isReadMode()) {
        this.toShowQuotePart = true;
        this.reRender();
        return;
      }
      const value = this.$summernote.summernote('code') + this.replyPart;
      this.replyPart = undefined;
      this.showQuoteButtonElement.parentElement.removeChild(this.showQuoteButtonElement);
      this.$summernote.summernote('code', value);
    }
    fetch() {
      const data = super.fetch();
      if (this.model.attributes.isHtml && this.replyPart) {
        if (data[this.name] == null) {
          data[this.name] = '';
        }
        data[this.name] += this.replyPart;
      }
      if (this.model.attributes.isHtml) {
        data[this.name + 'Plain'] = this.htmlToPlain(data[this.name]);
      }
      return data;
    }
  }
  var _default = _exports.default = EmailBodyFieldView;
});

define("views/dashlets/abstract/base", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/dashlets/abstract/base */

  /**
   * A base dashlet view. All dashlets should extend it.
   */
  class BaseDashletView extends _view.default {
    /** @type {Object.<string, *>|null}*/
    optionsData = null;
    optionsFields = {
      title: {
        type: 'varchar',
        required: true
      },
      autorefreshInterval: {
        type: 'enumFloat',
        options: [0, 0.5, 1, 2, 5, 10]
      }
    };
    disabledForReadOnlyActionList = ['options', 'remove'];
    disabledForLockedActionList = ['remove'];

    /**
     * @type {boolean}
     */
    noPadding = false;

    /**
     * A button. Handled by an `action{Name}` method or a click handler.
     *
     * @typedef module:views/dashlets/abstract/base~button
     *
     * @property {string} name A name.
     * @property {string} [label] A label.
     * @property {string} [html] An HTML.
     * @property {string} [text] A text.
     * @property {string} [title] A title (not translatable).
     * @property {function()} [onClick] A click handler.
     */

    /**
     * A dropdown action. Handled by an `action{Name}` method or a click handler.
     *
     * @typedef module:views/dashlets/abstract/base~action
     *
     * @property {string} name A name.
     * @property {string} [label] A label.
     * @property {string} [html] An HTML.
     * @property {string} [text] A text.
     * @property {string} [title] A title (not translatable).
     * @property {string} [iconHtml] An icon HTML.
     * @property {string} [url] A link URL.
     * @property {function()} [onClick] A click handler.
     * @property {number} [groupIndex] A group index.
     */

    /**
     * Buttons.
     *
     * @type {Array<module:views/dashlets/abstract/base~button>}
     */
    buttonList = [];

    /**
     * Dropdown actions.
     *
     * @type {Array<module:views/dashlets/abstract/base~action>}
     */
    actionList = [{
      name: 'refresh',
      label: 'Refresh',
      iconHtml: '<span class="fas fa-sync-alt"></span>',
      groupIndex: 10000
    }, {
      name: 'options',
      label: 'Options',
      iconHtml: '<span class="fas fa-pencil-alt"></span>',
      groupIndex: 10000
    }, {
      name: 'remove',
      label: 'Remove',
      iconHtml: '<span class="fas fa-times"></span>',
      groupIndex: 10000
    }];

    /**
     * Refresh.
     */
    actionRefresh() {
      this.render();
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Show options.
     */
    actionOptions() {}
    init() {
      this.name = this.options.name || this.name;
      this.id = this.options.id;
      this.defaultOptions = this.getMetadata().get(['dashlets', this.name, 'options', 'defaults']) || this.defaultOptions || {};
      this.defaultOptions = {
        title: this.getLanguage().translate(this.name, 'dashlets'),
        ...this.defaultOptions
      };
      this.defaultOptions = Espo.Utils.clone(this.defaultOptions);
      this.optionsFields = this.getMetadata().get(['dashlets', this.name, 'options', 'fields']) || this.optionsFields || {};
      this.optionsFields = Espo.Utils.clone(this.optionsFields);
      this.setupDefaultOptions();
      const options = Espo.Utils.cloneDeep(this.defaultOptions);
      for (const key in options) {
        if (typeof options[key] == 'function') {
          options[key] = options[key].call(this);
        }
      }
      let storedOptions;
      if (!this.options.readOnly) {
        storedOptions = this.getPreferences().getDashletOptions(this.id) || {};
      } else {
        const allOptions = this.getConfig().get('forcedDashletsOptions') || this.getConfig().get('dashletsOptions') || {};
        storedOptions = allOptions[this.id] || {};
      }
      this.optionsData = _.extend(options, storedOptions);
      if (this.optionsData.autorefreshInterval) {
        const interval = this.optionsData.autorefreshInterval * 60000;
        let t;
        const process = () => {
          t = setTimeout(() => {
            this.autoRefresh();
            process();
          }, interval);
        };
        process();
        this.once('remove', () => {
          clearTimeout(t);
        });
      }
      this.actionList = Espo.Utils.clone(this.actionList);
      this.buttonList = Espo.Utils.clone(this.buttonList);
      if (this.options.readOnly) {
        this.actionList = this.actionList.filter(item => {
          if (~this.disabledForReadOnlyActionList.indexOf(item.name)) {
            return false;
          }
          return true;
        });
      }
      if (this.options.locked) {
        this.actionList = this.actionList.filter(item => !this.disabledForLockedActionList.includes(item.name));
      }
      this.setupActionList();
      this.setupButtonList();
    }

    /**
     * Called on auto-refresh.
     *
     * @protected
     */
    autoRefresh() {
      this.actionRefresh();
    }

    /**
     * Set up default options.
     */
    setupDefaultOptions() {}

    /**
     * Set up actions.
     */
    setupActionList() {}

    /**
     * Set up buttons.
     */
    setupButtonList() {}

    /**
     * Has an option.
     *
     * @param {string} key
     * @return {boolean}
     */
    hasOption(key) {
      return key in this.optionsData;
    }

    /**
     * Get an option value.
     *
     * @param {string} key
     * @return {*}
     */
    getOption(key) {
      return this.optionsData[key];
    }

    /**
     * Get a title.
     * @return {string|null}
     */
    getTitle() {
      let title = this.getOption('title');
      if (!title) {
        title = null;
      }
      return title;
    }

    /**
     * @return {module:views/dashlet}
     */
    getContainerView() {
      return /** @type module:views/dashlet */this.getParentView();
    }

    /**
     * @internal
     * @param {MouseEvent} event
     * @param {HTMLElement} element
     */
    handleAction(event, element) {
      Espo.Utils.handleAction(this, event, element, {
        actionItems: [...this.buttonList, ...this.actionList],
        className: 'dashlet-action'
      });
    }

    /**
     * @internal
     * @return {Array<module:views/dashlets/abstract/base~action|false>}
     */
    getActionItemDataList() {
      /** @type {Array<module:views/dashlets/abstract/base~action[]>} */
      const groups = [];
      this.actionList.forEach(item => {
        // For bc.
        if (item === false) {
          return;
        }
        const index = (item.groupIndex === undefined ? 9999 : item.groupIndex) + 100;
        if (groups[index] === undefined) {
          groups[index] = [];
        }
        groups[index].push(item);
      });
      const itemList = [];
      groups.forEach(list => {
        list.forEach(it => itemList.push(it));
        itemList.push(false);
      });
      if (itemList.at(itemList.length - 1) === false) {
        itemList.pop();
      }
      return itemList;
    }

    /**
     * @return {string|null}
     * @since 9.0.0
     */
    getColor() {
      return null;
    }
    afterAdding() {}
  }
  var _default = _exports.default = BaseDashletView;
});

define("views/attachment/modals/select-one", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class SelectOneAttachmentModalView extends _modal.default {
    backdrop = true;

    // language=Handlebars
    templateContent = `<ul class="list-group no-side-margin">
            {{#each viewObject.options.dataList}}
                <li
                    class="list-group-item"
                ><a
                    role="button"
                    class="action"
                    data-action="select"
                    data-id="{{id}}"
                >{{name}}</a></li>
            {{/each}}
        </ul>
        `;

    /**
     *
     * @param {{
     *     fieldLabel?: string,
     *     dataList: {id: string, name: string}[],
     *     onSelect: function(string),
     * }} options
     */
    constructor(options) {
      super(options);
      this.options = options;
    }
    setup() {
      this.headerText = this.translate('Select');
      if (this.options.fieldLabel) {
        this.headerText += ' · ' + this.options.fieldLabel;
      }
      this.addActionHandler('select', (e, target) => {
        this.options.onSelect(target.dataset.id);
        this.close();
      });
    }
  }
  _exports.default = SelectOneAttachmentModalView;
});

define("ui/app-init", ["exports", "jquery"], function (_exports, _jquery) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _jquery = _interopRequireDefault(_jquery);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  function uiAppInit() {
    const $document = (0, _jquery.default)(document);
    const topSpaceHeight = 100;
    $document.on('keydown.espo.button', e => {
      if (e.code !== 'Enter' || e.target.tagName !== 'A' || e.target.getAttribute('role') !== 'button' || e.target.getAttribute('href') || e.ctrlKey || e.altKey || e.metaKey) {
        return;
      }
      (0, _jquery.default)(e.target).click();
      e.preventDefault();
    });
    $document.on('hidden.bs.dropdown', e => {
      (0, _jquery.default)(e.target).removeClass('dropup');
    });
    $document.on('show.bs.dropdown', e => {
      let isUp;

      /** @type {HTMLElement} */
      const target = e.target;
      const $dropdown = (0, _jquery.default)(e.target).find('.dropdown-menu');

      /** @type {HTMLElement} */
      const dropdownElement = $dropdown.get(0);
      if (!dropdownElement) {
        return;
      }
      const height = $dropdown.outerHeight();
      const width = $dropdown.outerWidth();
      {
        const $target = (0, _jquery.default)(target);
        const windowHeight = (0, _jquery.default)(window).height();
        const top = e.target.getBoundingClientRect().bottom;
        const spaceBelow = windowHeight - (top + height);
        isUp = spaceBelow < 0 && top - topSpaceHeight > height;
        if ($target.hasClass('more') || $target.hasClass('tab')) {
          return;
        }
        if (isUp) {
          $target.addClass('dropup');
        } else {
          $target.removeClass('dropup');
        }
      }
      if (dropdownElement.classList.contains('pull-right') && target.getBoundingClientRect().left - width < 0) {
        const maxWidth = target.getBoundingClientRect().right - target.getBoundingClientRect().width / 2;
        dropdownElement.style.maxWidth = maxWidth + 'px';
        const $group = (0, _jquery.default)(target);
        $group.one('hidden.bs.dropdown', () => {
          dropdownElement.style.maxWidth = '';
        });
        return;
      }
      const $dashletBody = (0, _jquery.default)(target).closest('.dashlet-body');
      if ($dashletBody.length) {
        const $body = $dashletBody;
        (0, _jquery.default)(target).removeClass('dropup');
        const $group = (0, _jquery.default)(target);
        const rect = target.getBoundingClientRect();
        const $ul = $group.find('.dropdown-menu');
        const isRight = target.classList.contains('pull-right');
        const $toggle = $group.find('.dropdown-toggle');
        $body.on('scroll.dd', () => {
          if ($group.hasClass('open')) {
            // noinspection JSUnresolvedReference
            $toggle.dropdown('toggle');
            $body.off('scroll.dd');
          }
        });
        $group.one('hidden.bs.dropdown', () => {
          $body.off('scroll.dd');
        });
        const left = isRight ? rect.left - $ul.outerWidth() + rect.width : rect.left;
        const top = isUp ? rect.top - height : rect.top + target.getBoundingClientRect().height;
        $ul.css({
          position: 'fixed',
          top: top,
          left: left,
          right: 'auto'
        });
        return;
      }
      if (e.target.parentElement.classList.contains('fix-overflow')) {
        (0, _jquery.default)(target).removeClass('dropup');
        const isRight = e.target.classList.contains('pull-right');
        const $ul = (0, _jquery.default)(e.target.parentElement).find('.dropdown-menu');
        const rect = e.target.getBoundingClientRect();
        const parent = $ul.offsetParent().get(0);
        if (!parent) {
          return;
        }
        const scrollTop = parent === window.document.documentElement ? document.documentElement.scrollTop || document.body.scrollTop : parent.scrollTop;
        const top = isUp ? rect.top + scrollTop - height : rect.top + scrollTop + e.target.getBoundingClientRect().height;
        const left = isRight ? rect.left - $ul.outerWidth() + rect.width : rect.left;
        $ul.css({
          top: top,
          left: left,
          right: 'auto'
        });
      }
    });
  }
  var _default = _exports.default = uiAppInit;
});

define("helpers/site/tabs", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class TabsHelper {
    /**
     * @param {import('models/settings').default} config
     * @param {import('models/preferences').default} preferences
     * @param {import('models/user').default} user
     * @param {import('acl-manager').default} acl
     * @param {import('metadata').default} metadata
     * @param {import('language').default} language
     */
    constructor(config, preferences, user, acl, metadata, language) {
      /** @private */
      this.config = config;
      /** @private */
      this.preferences = preferences;
      /** @private */
      this.user = user;
      /** @private */
      this.acl = acl;
      /** @private */
      this.metadata = metadata;
      /** @private */
      this.language = language;
    }

    /**
     * @typedef {Object} TabsHelper~item
     * @property {string} [url]
     * @property {string} [text]
     * @property {'url'|'divider'} [type]
     * @property {(TabsHelper~item|string)[]} [itemList]
     */

    /**
     * Get the tab list.
     *
     * @return {(TabsHelper~item|string)[]}
     */
    getTabList() {
      let tabList = this.preferences.get('useCustomTabList') && !this.preferences.get('addCustomTabs') ? this.preferences.get('tabList') : this.config.get('tabList');
      if (this.preferences.get('useCustomTabList') && this.preferences.get('addCustomTabs')) {
        tabList = [...tabList, ...(this.preferences.get('tabList') || [])];
      }
      return Espo.Utils.cloneDeep(tabList) || [];
    }

    /**
     * Is a tab a divider.
     *
     * @param {string|{type?: string}} item
     */
    isTabDivider(item) {
      return typeof item === 'object' && item.type === 'divider';
    }

    /**
     * Is a tab more-delimiter.
     *
     * @param {string|{type?: string}} item
     */
    isTabMoreDelimiter(item) {
      return item === '_delimiter_' || item === '_delimiter-ext_';
    }

    /**
     * Is a tab a URL.
     *
     * @param {string|{type?: string}} item
     */
    isTabUrl(item) {
      return typeof item === 'object' && item.type === 'url';
    }

    /**
     * Is a tab a group.
     *
     * @param {string|{type?: string}} item
     */
    isTabGroup(item) {
      if (!this.isTabDivider(item) && !this.isTabUrl(item) && typeof item === 'object') {
        return true;
      }
      return false;
    }

    /**
     * Is a tab a scope.
     *
     * @param {string|{type?: string}} item
     */
    isTabScope(item) {
      if (typeof item === 'object' || this.isTabMoreDelimiter(item) || item === 'Home') {
        return false;
      }
      return true;
    }

    /**
     * Get a translated tab label.
     *
     * @param {{text?: string}|string} item
     */
    getTranslatedTabLabel(item) {
      const translateLabel = label => {
        if (label.indexOf('$') === 0) {
          return this.language.translate(label.slice(1), 'navbarTabs');
        }
        return label;
      };
      if (this.isTabDivider(item) || this.isTabUrl(item) || this.isTabUrl(item) || this.isTabGroup(item)) {
        if (item.text) {
          return translateLabel(item.text);
        }
        return '';
      }
      if (item === 'Home') {
        return this.language.translate('Home');
      }
      if (typeof item === 'object') {
        return '';
      }
      return this.language.translate(item, 'scopeNamesPlural');
    }

    /**
     * Check tab access.
     *
     * @param {Record|string} item
     * @return {boolean}
     */
    checkTabAccess(item) {
      if (this.isTabUrl(item)) {
        if (item.onlyAdmin && !this.user.isAdmin()) {
          return false;
        }
        if (!item.aclScope) {
          return true;
        }
        return this.acl.check(item.aclScope);
      }
      if (item === 'Home' || this.isTabMoreDelimiter(item)) {
        return true;
      }

      /** @type {Record<string, {disabled?: boolean, acl?: boolean, tabAclPermission?: string}>} */
      const scopes = this.metadata.get('scopes') || {};
      if (!scopes[item]) {
        return false;
      }
      const defs = scopes[item] || {};
      if (defs.disabled) {
        return false;
      }
      if (defs.acl) {
        return this.acl.check(item);
      }
      if (defs.tabAclPermission) {
        const level = this.acl.getPermissionLevel(defs.tabAclPermission);
        return level && level !== 'no';
      }
      return true;
    }
  }
  _exports.default = TabsHelper;
});

define("helpers/misc/reactions", ["exports", "di", "models/settings", "metadata"], function (_exports, _di, _settings, _metadata) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _settings = _interopRequireDefault(_settings);
  _metadata = _interopRequireDefault(_metadata);
  let _initClass, _init_config, _init_extra_config, _init_metadata, _init_extra_metadata;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  let _ReactionsHelper;
  class ReactionsHelper {
    static #_ = {
      e: [_init_config, _init_extra_config, _init_metadata, _init_extra_metadata],
      c: [_ReactionsHelper, _initClass]
    } = _applyDecs(this, [(0, _di.register)()], [[(0, _di.inject)(_settings.default), 0, "config"], [(0, _di.inject)(_metadata.default), 0, "metadata"]]);
    /**
     * @type Settings
     */
    config = _init_config(this);

    /**
     * @type Metadata
     */
    metadata = (_init_extra_config(this), _init_metadata(this));

    /**
     * @private
     * @type {{
     *     type: string,
     *     iconClass: string,
     * }[]}
     */
    list = void _init_extra_metadata(this);

    /**
     * @return {{
     *     type: string,
     *     iconClass: string,
     * }[]}
     */
    getDefinitionList() {
      if (!this.list) {
        this.list = this.metadata.get('app.reactions.list') || [];
      }
      return this.list;
    }

    /**
     * @return {string[]}
     */
    getAvailableReactions() {
      return this.config.get('availableReactions') || [];
    }

    /**
     * @param {string|null} type
     * @return {string|null}
     */
    getIconClass(type) {
      const item = this.getDefinitionList().find(it => it.type === type);
      if (!item) {
        return null;
      }
      return item.iconClass;
    }
    static #_2 = _initClass();
  }
  var _default = _exports.default = _ReactionsHelper;
});

define("controllers/base", ["exports", "controller", "views/base"], function (_exports, _controller, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _controller = _interopRequireDefault(_controller);
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 controllers/base */

  /**
   * A base controller.
   */
  class BaseController extends _controller.default {
    constructor(params, injections) {
      super(params, injections);
      this.on('logout', () => this._clearAllStoredMainViews());
    }

    /**
     * @private
     */
    _clearAllStoredMainViews() {
      for (const name in this.params) {
        if (!name.startsWith('mainView-')) {
          continue;
        }
        const [, scope, key] = name.split('-', 3);
        const actualKey = `mainView-${scope}-${key}`;
        const view = /** @type {module:view} */this.get(actualKey);
        if (view) {
          view.remove(true);
        }
        this.unset(actualKey);
      }
    }

    /**
     * Clear a stored main view.
     *
     * @param {string} scope
     */
    clearScopeStoredMainView(scope) {
      for (const key in this.params) {
        if (!key.startsWith(`mainView-${scope}-`)) {
          continue;
        }
        const view = /** @type {module:view} */this.get(key);
        if (view) {
          view.remove(true);
        }
        this.unset(key);
      }
    }

    /**
     * Log in.
     *
     * @param {{
     *     anotherUser?: string,
     *     username?: string,
     * }} [options]
     */
    login(options) {
      const viewName = this.getConfig().get('loginView') || 'views/login';
      const anotherUser = (options || {}).anotherUser;
      const prefilledUsername = (options || {}).username;
      const viewOptions = {
        anotherUser: anotherUser,
        prefilledUsername: prefilledUsername
      };
      this.entire(viewName, viewOptions, loginView => {
        loginView.render();
        loginView.on('login', (userName, data) => {
          this.trigger('login', this.normalizeLoginData(userName, data));
        });
        loginView.once('redirect', (viewName, headers, userName, password, data) => {
          loginView.remove();
          this.entire(viewName, {
            loginData: data,
            userName: userName,
            password: password,
            anotherUser: anotherUser,
            headers: headers
          }, secondStepView => {
            secondStepView.render();
            secondStepView.once('login', (userName, data) => {
              this.trigger('login', this.normalizeLoginData(userName, data));
            });
            secondStepView.once('back', () => {
              secondStepView.remove();
              this.login();
            });
          });
        });
      });
    }

    /** @private */
    normalizeLoginData(userName, data) {
      return {
        auth: {
          userName: userName,
          token: data.token,
          anotherUser: data.anotherUser
        },
        user: data.user,
        preferences: data.preferences,
        acl: data.acl,
        settings: data.settings,
        appParams: data.appParams,
        language: data.language
      };
    }

    /**
     * Log out.
     */
    logout() {
      const title = this.getConfig().get('applicationName') || 'EspoCRM';
      $('head title').text(title);
      this.trigger('logout');
    }

    /**
     * Clear cache.
     */
    clearCache() {
      this.entire('views/clear-cache', {
        cache: this.getCache()
      }, view => {
        view.render();
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionLogin() {
      this.login();
    }

    // noinspection JSUnusedGlobalSymbols
    actionLogout() {
      this.logout();
    }

    // noinspection JSUnusedGlobalSymbols
    actionLogoutWait() {
      this.entire('views/base', {
        template: 'logout-wait'
      }, view => {
        view.render().then(() => Espo.Ui.notifyWait());
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionClearCache() {
      this.clearCache();
    }

    /**
     * Error Not Found.
     */
    error404() {
      const view = new _base.default({
        template: 'errors/404'
      });
      this.entire(view);
    }

    /**
     * Error Forbidden.
     */
    error403() {
      const view = new _base.default({
        template: 'errors/403'
      });
      this.entire(view);
    }

    // noinspection JSUnusedGlobalSymbols
    actionError404() {
      this.error404();
    }

    // noinspection JSUnusedGlobalSymbols
    actionError403() {
      this.error403();
    }
  }
  var _default = _exports.default = BaseController;
});

define("dynamic-handler", ["exports", "bullbone"], function (_exports, _bullbone) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 dynamic-handler */

  /**
   * A dynamic handler. To be extended by a specific handler.
   */
  class DynamicHandler {
    /**
     * @param {module:views/record/detail} recordView A record view.
     */
    constructor(recordView) {
      /**
       * A record view.
       *
       * @protected
       * @type {module:views/record/detail}
       */
      this.recordView = recordView;

      /**
       * A model.
       *
       * @protected
       * @type {module:model}
       */
      this.model = recordView.model;
    }

    /**
     * Initialization logic. To be extended.
     *
     * @protected
     */
    init() {}

    /**
     * Called on model change. To be extended.
     *
     * @protected
     * @param {module:views/record/detail} model A model.
     * @param {Object} o Options.
     */
    onChange(model, o) {}

    /**
     * Get a metadata.
     *
     * @protected
     * @returns {module:metadata}
     */
    getMetadata() {
      return this.recordView.getMetadata();
    }
  }
  DynamicHandler.extend = _bullbone.View.extend;

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = DynamicHandler;
});

define("collection", ["exports", "model", "bullbone", "underscore"], function (_exports, _model, _bullbone, _underscore) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _model = _interopRequireDefault(_model);
  _underscore = _interopRequireDefault(_underscore);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 collection */

  /**
   * On sync with backend.
   *
   * @event Collection#sync
   * @param {Collection} collection A collection.
   * @param {Object} response Response from backend.
   * @param {Object} o Options.
   */

  /**
   * Any number of models have been added, removed or changed.
   *
   * @event Collection#update
   * @param {Collection} collection A collection.
   * @param {Object} o Options.
   */

  /**
   * On reset.
   *
   * @event Collection#reset
   * @param {Collection} collection A collection.
   * @param {Object} o Options.
   */

  /**
   * On model sync.
   *
   * @event Collection#model-sync
   * @param {Model} model A model.
   * @param {Record & {action?: 'fetch'|'save'|'destroy'}} o Options.
   * @since 9.1.0
   */

  /**
   * @typedef {Object} module:collection~Data
   * @property {string|null} [primaryFilter]
   * @property {string[]} [boolFilterList]
   * @property {string} [textFilter]
   * @property {string} [select]
   * @property {string} [q]
   */

  /**
   * A collection.
   *
   * @mixes Bull.Events
   * @copyright Credits to Backbone.js.
   */
  class Collection {
    /**
     * An entity type.
     *
     * @type {string|null}
     */
    entityType = null;

    /**
     * A total number of records.
     *
     * @type {number}
     */
    total = 0;

    /**
     * A current offset (for pagination).
     *
     * @type {number}
     */
    offset = 0;

    /**
     * A max size (for pagination).
     *
     * @type {number}
     */
    maxSize = 20;

    /**
     * An order.
     *
     * @type {boolean|'asc'|'desc'|null}
     */
    order = null;

    /**
     * An order-by field.
     *
     * @type {string|null}
     */
    orderBy = null;

    /**
     * A where clause.
     *
     * @type {module:search-manager~whereItem[]|null}
     */
    where = null;

    /**
     * @deprecated
     */
    whereAdditional = null;

    /**
     * A length correction.
     *
     * @type {number}
     */
    lengthCorrection = 0;

    /**
     * A max max-size.
     *
     * @type {number}
     */
    maxMaxSize = 0;

    /**
     * A where function.
     *
     * @type {function(): Object[]}
     */
    whereFunction;

    /**
     * A last sync request promise.
     *
     * @type {module:ajax.AjaxPromise|null}
     */
    lastSyncPromise = null;

    /**
     * A parent model. To be used for own purposes. E.g. to have access to a parent from related models.
     *
     * @type {import('model').default}
     */
    parentModel;

    /**
     * @param {Model[]|null} [models] Models.
     * @param {{
     *     entityType?: string,
     *     model?: Model.prototype,
     *     defs?: module:model~defs,
     *     order?: 'asc'|'desc'|boolean|null,
     *     orderBy?: string|null,
     *     urlRoot?: string,
     *     url?: string,
     *     maxSize?: number,
     * }} [options] Options.
     */
    constructor(models, options) {
      options = {
        ...options
      };
      if (options.model) {
        this.model = options.model;
      }
      if (options.maxSize !== undefined) {
        this.maxSize = options.maxSize;
      }
      this._reset();
      if (options.entityType) {
        this.entityType = options.entityType;
        /** @deprecated */
        this.name = this.entityType;
      }

      /**
       * A root URL.
       *
       * @public
       * @type {string|null}
       */
      this.urlRoot = options.urlRoot || this.urlRoot || this.entityType;

      /**
       * An URL.
       *
       * @type {string|null}
       */
      this.url = options.url || this.url || this.urlRoot;
      this.orderBy = options.orderBy || this.orderBy;
      this.order = options.order || this.order;
      this.defaultOrder = this.order;
      this.defaultOrderBy = this.orderBy;

      /** @type {module:model~defs} */
      this.defs = options.defs || {};

      /**
       * @type {module:collection~Data | Record<string, *>}
       */
      this.data = {};

      /**
       * @private
       * @type {Model#}
       */
      this.model = options.model || _model.default;
      if (models) {
        this.reset(models, {
          silent: true,
          ...options
        });
      }
    }

    /**
     * Add models or a model.
     *
     * @param {Model[]|Model|Record[]|Record} models Models ar a model.
     * @param {{
     *     merge?: boolean,
     *     at?: number,
     *     silent?: boolean,
     * }} [options] Options. `at` – position; `merge` – merge existing models, otherwise, they are ignored.
     * @return {this}
     * @fires Collection#update
     */
    add(models, options) {
      this.set(models, {
        merge: false,
        ...options,
        ...addOptions
      });
      return this;
    }

    /**
     * Remove models or a model.
     *
     * @param {Model[]|Model|string} models Models, a model or a model ID.
     * @param {{
     *     silent?: boolean,
     * } & Object.<string, *>} [options] Options.
     * @return {this}
     * @fires Collection#update
     */
    remove(models, options) {
      options = {
        ...options
      };
      const singular = !_underscore.default.isArray(models);
      models = singular ? [models] : models.slice();
      const removed = this._removeModels(models, options);
      if (!options.silent && removed.length) {
        options.changes = {
          added: [],
          merged: [],
          removed: removed
        };
        this.trigger('update', this, options);
      }
      return this;
    }

    /**
     * @protected
     * @param {Model[]|Model|Record[]} models Models ar a model.
     * @param {{
     *     silent?: boolean,
     *     at?: number,
     *     prepare?: boolean,
     *     add?: boolean,
     *     merge?: boolean,
     *     remove?: boolean,
     *     index?: number,
     * } & Object.<string, *>} [options]
     * @return {Model[]}
     */
    set(models, options) {
      if (models == null) {
        return [];
      }
      options = {
        ...setOptions,
        ...options
      };
      if (options.prepare && !this._isModel(models)) {
        models = this.prepareAttributes(models, options) || [];
      }
      const singular = !_underscore.default.isArray(models);
      models = singular ? [models] : models.slice();
      let at = options.at;
      if (at != null) {
        at = +at;
      }
      if (at > this.length) {
        at = this.length;
      }
      if (at < 0) {
        at += this.length + 1;
      }
      const set = [];
      const toAdd = [];
      const toMerge = [];
      const toRemove = [];
      const modelMap = {};
      const add = options.add;
      const merge = options.merge;
      const remove = options.remove;
      let model, i;
      for (i = 0; i < models.length; i++) {
        model = models[i];
        const existing = this._get(model);
        if (existing) {
          if (merge && model !== existing) {
            let attributes = this._isModel(model) ? model.attributes : model;
            if (options.prepare) {
              attributes = existing.prepareAttributes(attributes, options);
            }
            existing.set(attributes, options);
            toMerge.push(existing);
          }
          if (!modelMap[existing.cid]) {
            modelMap[existing.cid] = true;
            set.push(existing);
          }
          models[i] = existing;
        } else if (add) {
          model = models[i] = this._prepareModel(model);
          if (model) {
            toAdd.push(model);
            this._addReference(model, options);
            modelMap[model.cid] = true;
            set.push(model);
          }
        }
      }

      // Remove stale models.
      if (remove) {
        for (i = 0; i < this.length; i++) {
          model = this.models[i];
          if (!modelMap[model.cid]) {
            toRemove.push(model);
          }
        }
        if (toRemove.length) {
          this._removeModels(toRemove, options);
        }
      }
      let orderChanged = false;
      const replace = add && remove;
      if (set.length && replace) {
        orderChanged = this.length !== set.length || _underscore.default.some(this.models, (m, index) => {
          return m !== set[index];
        });
        this.models.length = 0;
        splice(this.models, set, 0);
        this.length = this.models.length;
      } else if (toAdd.length) {
        splice(this.models, toAdd, at == null ? this.length : at);
        this.length = this.models.length;
      }
      if (!options.silent) {
        for (i = 0; i < toAdd.length; i++) {
          if (at != null) {
            options.index = at + i;
          }
          model = toAdd[i];
          model.trigger('add', model, this, options);
        }
        if (orderChanged) {
          this.trigger('sort', this, options);
        }
        if (toAdd.length || toRemove.length || toMerge.length) {
          options.changes = {
            added: toAdd,
            removed: toRemove,
            merged: toMerge
          };
          this.trigger('update', this, options);
        }
      }
      return models;
    }

    /**
     * Reset.
     *
     * @param {Model[]|null} [models] Models to replace the collection with.
     * @param {{
     *     silent?: boolean,
     * } & Object.<string, *>} [options]
     * @return {this}
     * @fires Collection#reset
     */
    reset(models, options) {
      this.lengthCorrection = 0;
      options = options ? _underscore.default.clone(options) : {};
      for (let i = 0; i < this.models.length; i++) {
        this._removeReference(this.models[i], options);
      }
      options.previousModels = this.models;
      this._reset();
      if (models) {
        this.add(models, {
          silent: true,
          ...options
        });
      }
      if (!options.silent) {
        this.trigger('reset', this, options);
      }
      return this;
    }

    /**
     * Add a model at the end.
     *
     * @param {Model} model A model.
     * @param {{
     *     silent?: boolean,
     * }} [options] Options
     * @return {this}
     */
    push(model, options) {
      this.add(model, {
        at: this.length,
        ...options
      });
      return this;
    }

    /**
     * Remove and return the last model.
     *
     * @param {{
     *     silent?: boolean,
     * }} [options] Options
     * @return {Model|null}
     */
    pop(options) {
      const model = this.at(this.length - 1);
      if (!model) {
        return null;
      }
      this.remove(model, options);
      return model;
    }

    /**
     * Add a model to the beginning.
     *
     * @param {Model} model A model.
     * @param {{
     *     silent?: boolean,
     * }} [options] Options
     * @return {this}
     */
    unshift(model, options) {
      this.add(model, {
        at: 0,
        ...options
      });
      return this;
    }

    /**
     * Remove and return the first model.
     *
     * @param {{
     *     silent?: boolean,
     * }} [options] Options
     * @return {Model|null}
     */
    shift(options) {
      const model = this.at(0);
      if (!model) {
        return null;
      }
      this.remove(model, options);
      return model;
    }

    /**
     * Get a model by an ID.
     *
     * @todo Usage to _get.
     * @param {string} id An ID.
     * @return {Model|undefined}
     */
    get(id) {
      return this._get(id);
    }

    /**
     * Whether a model in the collection.
     *
     * @todo Usage to _has.
     * @param {string} id An ID.
     * @return {boolean}
     */
    has(id) {
      return this._has(id);
    }

    /**
     * Get a model by index.
     *
     * @param {number} index An index. Can be negative, then counted from the end.
     * @return {Model|undefined}
     */
    at(index) {
      if (index < 0) {
        index += this.length;
      }
      return this.models[index];
    }

    /**
     * Iterates through a collection.
     *
     * @param {function(Model)} callback A function.
     * @param {Object} [context] A context.
     */
    forEach(callback, context) {
      return this.models.forEach(callback, context);
    }

    /**
     * Get an index of a model. Returns -1 if not found.
     *
     * @param {Model} model A model
     * @return {number}
     */
    indexOf(model) {
      return this.models.indexOf(model);
    }

    /**
     * @private
     * @param {string|Object.<string, *>|Model} obj
     * @return {boolean}
     */
    _has(obj) {
      return !!this._get(obj);
    }

    /**
     * @private
     * @param {string|Object.<string, *>|Model} obj
     * @return {Model|undefined}
     */
    _get(obj) {
      if (obj == null) {
        return void 0;
      }
      return this._byId[obj] || this._byId[this.modelId(obj.attributes || obj)] || obj.cid && this._byId[obj.cid];
    }

    /**
     * @protected
     * @param {Object.<string, *>} attributes
     * @return {*}
     */
    modelId(attributes) {
      return attributes['id'];
    }

    /** @private */
    _reset() {
      /**
       * A number of records.
       */
      this.length = 0;

      /**
       * Models.
       *
       * @type {Model[]}
       */
      this.models = [];

      /** @private */
      this._byId = {};
    }

    /**
     * @param {string} orderBy An order field.
     * @param {bool|null|'desc'|'asc'} [order] True for desc.
     * @returns {Promise}
     */
    sort(orderBy, order) {
      this.orderBy = orderBy;
      if (order === true) {
        order = 'desc';
      } else if (order === false) {
        order = 'asc';
      }
      this.order = order || 'asc';
      return this.fetch();
    }

    /**
     * Has previous page.
     *
     * @return {boolean}
     */
    hasPreviousPage() {
      return this.offset > 0;
    }

    /**
     * Has next page.
     *
     * @return {boolean}
     */
    hasNextPage() {
      return this.total - this.offset > this.length || this.total === -1;
    }

    /**
     * Next page.
     *
     * @returns {Promise}
     */
    nextPage() {
      return this.setOffset(this.offset + this.length);
    }

    /**
     * Previous page.
     *
     * @returns {Promise}
     */
    previousPage() {
      return this.setOffset(Math.max(0, this.offset - this.maxSize));
    }

    /**
     * First page.
     *
     * @returns {Promise}
     */
    firstPage() {
      return this.setOffset(0);
    }

    /**
     * Last page.
     *
     * @returns {Promise}
     */
    lastPage() {
      let offset = this.total - this.total % this.maxSize;
      if (offset === this.total) {
        offset = this.total - this.maxSize;
      }
      return this.setOffset(offset);
    }

    /**
     * Set an offset.
     *
     * @param {number} offset Offset.
     * @returns {Promise}
     */
    setOffset(offset) {
      if (offset < 0) {
        throw new RangeError('offset can not be less than 0');
      }
      if (offset > this.total && this.total !== -1 && this.total !== -2 && offset > 0) {
        throw new RangeError('offset can not be larger than total count');
      }
      this.offset = offset;
      return this.fetch({
        maxSize: this.maxSize
      });
    }

    /**
     * Has more.
     *
     * @return {boolean}
     */
    hasMore() {
      return this.total > this.length + this.offset + this.lengthCorrection || this.total === -1;
    }

    /**
     * Prepare attributes.
     *
     * @protected
     * @param {Object.<string, *>|Record[]} response A response from the backend.
     * @param {Object.<string, *>} options Options.
     * @returns {Object.<string, *>[]}
     */
    prepareAttributes(response, options) {
      this.total = response.total;

      // noinspection JSUnusedGlobalSymbols
      /**
       * @deprecated As of v8.4. Use 'sync' event to obtain any additional data from a response.
       */
      this.dataAdditional = response.additionalData || null;
      return response.list;
    }

    /**
     * Fetch from the backend.
     *
     * @param {{
     *     remove?: boolean,
     *     more?: boolean,
     *     offset?: number,
     *     maxSize?: number,
     *     orderBy?: string,
     *     order?: 'asc'|'desc',
     * } & Object.<string, *>} [options] Options.
     * @returns {Promise}
     * @fires Collection#sync Unless `{silent: true}`.
     */
    fetch(options) {
      options = {
        ...options
      };
      options.data = {
        ...options.data,
        ...this.data
      };
      this.offset = options.offset || this.offset;
      this.orderBy = options.orderBy || this.orderBy;
      this.order = options.order || this.order;
      this.where = options.where || this.where;
      const length = this.length + this.lengthCorrection;
      if ('maxSize' in options) {
        options.data.maxSize = options.maxSize;
      } else {
        options.data.maxSize = options.more ? this.maxSize : Math.max(length, this.maxSize);
        if (this.maxMaxSize && options.data.maxSize > this.maxMaxSize) {
          options.data.maxSize = this.maxMaxSize;
        }
      }
      options.data.offset = options.more ? this.offset + length : this.offset;
      options.data.orderBy = this.orderBy;
      options.data.order = this.order;
      options.data.whereGroup = this.getWhere();
      if (options.data.select) {
        options.data.attributeSelect = options.data.select;
        delete options.data.select;
      }
      options = {
        prepare: true,
        ...options
      };
      const success = options.success;
      options.success = response => {
        options.reset ? this.reset(response, options) : this.set(response, options);
        if (success) {
          success.call(options.context, this, response, options);
        }
        this.trigger('sync', this, response, options);
      };
      const error = options.error;
      options.error = response => {
        if (error) {
          error.call(options.context, this, response, options);
        }
        this.trigger('error', this, response, options);
      };
      this.lastSyncPromise = _model.default.prototype.sync.call(this, 'read', this, options);
      return this.lastSyncPromise;
    }

    /**
     * Is being fetched.
     *
     * @return {boolean}
     */
    isBeingFetched() {
      return this.lastSyncPromise && this.lastSyncPromise.getReadyState() < 4;
    }

    /**
     * Abort the last fetch.
     */
    abortLastFetch() {
      if (this.isBeingFetched()) {
        this.lastSyncPromise.abort();
      }
    }

    /**
     * Get a where clause.
     *
     * @returns {Object[]}
     */
    getWhere() {
      let where = (this.where || []).concat(this.whereAdditional || []);
      if (this.whereFunction) {
        where = where.concat(this.whereFunction() || []);
      }
      return where;
    }

    /**
     * Get an entity type.
     *
     * @returns {string}
     */
    getEntityType() {
      return this.entityType || this.name;
    }

    /**
     * Reset the order to default.
     */
    resetOrderToDefault() {
      this.orderBy = this.defaultOrderBy;
      this.order = this.defaultOrder;
    }

    /**
     * Set an order.
     *
     * @param {string|null} orderBy
     * @param {boolean|'asc'|'desc'|null} [order]
     * @param {boolean} [setDefault]
     */
    setOrder(orderBy, order, setDefault) {
      this.orderBy = orderBy;
      this.order = order;
      if (setDefault) {
        this.defaultOrderBy = orderBy;
        this.defaultOrder = order;
      }
    }

    /**
     * Clone.
     *
     * @param {{withModels?: boolean}} [options]
     * @return {Collection}
     */
    clone() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      let models = this.models;
      if (options.withModels) {
        models = this.models.map(m => m.clone());
      }
      const collection = new this.constructor(models, {
        model: this.model,
        entityType: this.entityType,
        defs: this.defs,
        orderBy: this.orderBy,
        order: this.order
      });
      collection.name = this.name;
      collection.urlRoot = this.urlRoot;
      collection.url = this.url;
      collection.defaultOrder = this.defaultOrder;
      collection.defaultOrderBy = this.defaultOrderBy;
      collection.data = Espo.Utils.cloneDeep(this.data);
      collection.where = Espo.Utils.cloneDeep(this.where);
      collection.whereAdditional = Espo.Utils.cloneDeep(this.whereAdditional);
      collection.total = this.total;
      collection.offset = this.offset;
      collection.maxSize = this.maxSize;
      collection.maxMaxSize = this.maxMaxSize;
      collection.whereFunction = this.whereFunction;
      collection.parentModel = this.parentModel;
      return collection;
    }

    /**
     * Prepare an empty model instance.
     *
     * @return {Model}
     */
    prepareModel() {
      return this._prepareModel({});
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Compose a URL for syncing. Called from Model.sync.
     *
     * @protected
     * @return {string}
     */
    composeSyncUrl() {
      return this.url;
    }

    /** @private */
    _isModel(object) {
      return object instanceof _model.default;
    }

    /** @private */
    _removeModels(models, options) {
      const removed = [];
      for (let i = 0; i < models.length; i++) {
        const model = this.get(models[i]);
        if (!model) {
          continue;
        }
        const index = this.models.indexOf(model);
        this.models.splice(index, 1);
        this.length--;
        delete this._byId[model.cid];
        const id = this.modelId(model.attributes);
        if (id != null) {
          delete this._byId[id];
        }
        if (!options.silent) {
          options.index = index;
          model.trigger('remove', model, this, options);
        }
        removed.push(model);
        this._removeReference(model, options);
      }
      return removed;
    }

    /** @private */
    _addReference(model) {
      this._byId[model.cid] = model;
      const id = this.modelId(model.attributes);
      if (id != null) {
        this._byId[id] = model;
      }
      model.on('all', this._onModelEvent, this);
    }

    /** @private */
    _removeReference(model) {
      delete this._byId[model.cid];
      const id = this.modelId(model.attributes);
      if (id != null) {
        delete this._byId[id];
      }
      if (this === model.collection) {
        delete model.collection;
      }
      model.off('all', this._onModelEvent, this);
    }

    /** @private */
    _onModelEvent(event, model, collection, options) {
      // @todo Revise. Never triggerred? Remove?
      if (event === 'sync' && collection !== this) {
        return;
      }
      if (!model) {
        this.trigger.apply(this, arguments);
        return;
      }
      if ((event === 'add' || event === 'remove') && collection !== this) {
        return;
      }
      if (event === 'destroy') {
        this.remove(model, options);
      }
      if (event === 'change') {
        const prevId = this.modelId(model.previousAttributes());
        const id = this.modelId(model.attributes);
        if (prevId !== id) {
          if (prevId != null) {
            delete this._byId[prevId];
          }
          if (id != null) {
            this._byId[id] = model;
          }
        }
      }
      this.trigger.apply(this, arguments);
    }

    // noinspection JSDeprecatedSymbols
    /** @private*/
    _prepareModel(attributes) {
      if (this._isModel(attributes)) {
        if (!attributes.collection) {
          attributes.collection = this;
        }
        return attributes;
      }
      const ModelClass = this.model;

      // noinspection JSValidateTypes
      return new ModelClass(attributes, {
        collection: this,
        entityType: this.entityType || this.name,
        defs: this.defs
      });
    }
  }
  Object.assign(Collection.prototype, _bullbone.Events);
  Collection.extend = _bullbone.View.extend;
  const setOptions = {
    add: true,
    remove: true,
    merge: true
  };
  const addOptions = {
    add: true,
    remove: false
  };
  const splice = (array, insert, at) => {
    at = Math.min(Math.max(at, 0), array.length);
    const tail = Array(array.length - at);
    const length = insert.length;
    let i;
    for (i = 0; i < tail.length; i++) {
      tail[i] = array[i + at];
    }
    for (i = 0; i < length; i++) {
      array[i + at] = insert[i];
    }
    for (i = 0; i < tail.length; i++) {
      array[i + length + at] = tail[i];
    }
  };
  var _default = _exports.default = Collection;
});

define("app", ["exports", "backbone", "bullbone", "js-base64", "ui", "utils", "acl-manager", "cache", "storage", "models/settings", "language", "metadata", "field-manager", "models/user", "models/preferences", "model-factory", "collection-factory", "controllers/base", "router", "date-time", "layout-manager", "theme-manager", "session-storage", "view-helper", "web-socket-manager", "ajax", "number-util", "page-title", "broadcast-channel", "ui/app-init", "app-params", "di"], function (_exports, _backbone, _bullbone, _jsBase, _ui, _utils, _aclManager, _cache, _storage, _settings, _language, _metadata, _fieldManager, _user, _preferences, _modelFactory, _collectionFactory, _base, _router, _dateTime, _layoutManager, _themeManager, _sessionStorage, _viewHelper, _webSocketManager, _ajax, _numberUtil, _pageTitle, _broadcastChannel, _appInit, _appParams, _di) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _backbone = _interopRequireDefault(_backbone);
  _jsBase = _interopRequireDefault(_jsBase);
  _ui = _interopRequireDefault(_ui);
  _utils = _interopRequireDefault(_utils);
  _aclManager = _interopRequireDefault(_aclManager);
  _cache = _interopRequireDefault(_cache);
  _storage = _interopRequireDefault(_storage);
  _settings = _interopRequireDefault(_settings);
  _language = _interopRequireDefault(_language);
  _metadata = _interopRequireDefault(_metadata);
  _fieldManager = _interopRequireDefault(_fieldManager);
  _user = _interopRequireDefault(_user);
  _preferences = _interopRequireDefault(_preferences);
  _modelFactory = _interopRequireDefault(_modelFactory);
  _collectionFactory = _interopRequireDefault(_collectionFactory);
  _base = _interopRequireDefault(_base);
  _router = _interopRequireDefault(_router);
  _dateTime = _interopRequireDefault(_dateTime);
  _layoutManager = _interopRequireDefault(_layoutManager);
  _themeManager = _interopRequireDefault(_themeManager);
  _sessionStorage = _interopRequireDefault(_sessionStorage);
  _viewHelper = _interopRequireDefault(_viewHelper);
  _webSocketManager = _interopRequireDefault(_webSocketManager);
  _ajax = _interopRequireDefault(_ajax);
  _numberUtil = _interopRequireDefault(_numberUtil);
  _pageTitle = _interopRequireDefault(_pageTitle);
  _broadcastChannel = _interopRequireDefault(_broadcastChannel);
  _appInit = _interopRequireDefault(_appInit);
  _appParams = _interopRequireDefault(_appParams);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 app */

  /**
   * A main application class.
   *
   * @mixes Bull.Events
   */
  class App {
    /**
     * @param {module:app~Options} options Options.
     * @param {function(App): void} callback A callback.
     */
    constructor(options, callback) {
      options = options || {};

      /**
       * An application ID.
       *
       * @private
       * @type {string}
       */
      this.id = options.id || 'espocrm';

      /**
       * Use cache.
       *
       * @private
       * @type {boolean}
       */
      this.useCache = options.useCache || this.useCache;
      this.apiUrl = options.apiUrl || this.apiUrl;

      /**
       * A base path.
       *
       * @type {string}
       */
      this.basePath = options.basePath || '';

      /**
       * A default ajax request timeout.
       *
       * @private
       * @type {Number}
       */
      this.ajaxTimeout = options.ajaxTimeout || 0;

      /**
       * A list of internal modules.
       *
       * @private
       * @type {string[]}
       */
      this.internalModuleList = options.internalModuleList || [];

      /**
       * @private
       */
      this.themeName = options.theme || null;

      /**
       * A list of bundled modules.
       *
       * @private
       * @type {string[]}
       */
      this.bundledModuleList = options.bundledModuleList || [];
      this.appTimestamp = options.appTimestamp;
      this.initCache(options).then(async () => {
        await this.init(options);
        callback(this);
      });
      (0, _appInit.default)();
    }

    /**
     * @private
     * @type {boolean}
     */
    useCache = false;

    /**
     * @protected
     * @type {User}
     */
    user = null;

    /**
     * @private
     * @type {Preferences}
     */
    preferences = null;

    /**
     * @protected
     * @type {module:models/settings}
     */
    settings = null;

    /**
     * @private
     * @type {Metadata}
     */
    metadata = null;

    /**
     * @private
     * @type {module:language}
     */
    language = null;

    /**
     * @private
     * @type {module:field-manager}
     */
    fieldManager = null;

    /**
     * @private
     * @type {module:cache|null}
     */
    cache = null;

    /**
     * @private
     * @type {module:storage|null}
     */
    storage = null;

    /**
     * @private
     */
    loader = null;

    /**
     * An API URL.
     *
     * @private
     */
    apiUrl = 'api/v1';

    /**
     * An auth credentials string.
     *
     * @private
     * @type {?string}
     */
    auth = null;

    /**
     * Another user to login as.
     *
     * @private
     * @type {?string}
     */
    anotherUser = null;

    /**
     * A base controller.
     *
     * @private
     * @type {module:controllers/base}
     */
    baseController = null;

    /**
     * @private
     */
    controllers = null;

    /**
     * @private
     * @type {module:router}
     */
    router = null;

    /**
     * @private
     * @type {module:model-factory}
     */
    modelFactory = null;

    /**
     * @private
     * @type {module:collection-factory}
     */
    collectionFactory = null;

    /**
     * A view factory.
     *
     * @private
     * @type {Factory}
     */
    viewFactory = null;

    /**
     * App params.
     *
     * @private
     * @type {import('app-params').default}
     */
    appParams;

    /**
     * @type {function(string, function(View))}
     * @private
     */
    viewLoader = null;

    /**
     * @private
     * @type {module:view-helper}
     */
    viewHelper = null;

    /**
     * A body view.
     *
     * @protected
     * @type {string}
     */
    masterView = 'views/site/master';

    /**
     * @private
     * @type {Cache|null}
     */
    responseCache = null;

    /**
     * @private
     * @type {module:broadcast-channel|null}
     */
    broadcastChannel = null;

    /**
     * @private
     * @type {module:date-time|null}
     */
    dateTime = null;

    /**
     * @private
     * @type {module:num-util|null}
     */
    numberUtil = null;

    /**
     * @private
     * @type {module:web-socket-manager|null}
     */
    webSocketManager = null;

    /**
     * @private
     * @type {AclManager}
     */
    acl;

    /**
     * An application timestamp. Used for asset cache busting and update detection.
     *
     * @private
     * @type {Number|null}
     */
    appTimestamp = null;

    /** @private */
    started = false;

    /** @private */
    aclName = 'acl';

    /**
     * @private
     * @param {module:app~Options} options
     */
    async initCache(options) {
      if (!this.useCache) {
        return;
      }
      const timestamp = options.cacheTimestamp || null;
      this.cache = new _cache.default(timestamp);
      const storedTimestamp = this.cache.getCacheTimestamp();
      timestamp ? this.cache.handleActuality(timestamp) : this.cache.storeTimestamp();
      if (!window.caches) {
        return;
      }
      const deleteCache = !timestamp || !storedTimestamp || timestamp !== storedTimestamp;
      try {
        if (deleteCache) {
          await caches.delete('espo');
        }
        this.responseCache = await caches.open('espo');
      } catch (e) {
        console.error(`Could not open 'espo' cache.`);
      }
    }

    /**
     * @private
     * @param {module:app~Options} options
     */
    async init(options) {
      this.appParams = new _appParams.default();
      this.controllers = {};

      /**
       * @type {Espo.loader}
       * @private
       */
      this.loader = Espo.loader;
      this.loader.setResponseCache(this.responseCache);
      if (this.useCache && !this.loader.getCacheTimestamp() && options.cacheTimestamp) {
        this.loader.setCacheTimestamp(options.cacheTimestamp);
      }
      this.storage = new _storage.default();
      this.sessionStorage = new _sessionStorage.default();
      this.setupAjax();
      this.settings = new _settings.default(null);
      this.language = new _language.default(this.cache);
      this.metadata = new _metadata.default(this.cache);
      this.fieldManager = new _fieldManager.default();
      _di.container.set(_appParams.default, this.appParams);
      _di.container.set(_storage.default, this.storage);
      _di.container.set(_sessionStorage.default, this.sessionStorage);
      _di.container.set(_settings.default, this.settings);
      _di.container.set(_language.default, this.language);
      _di.container.set(_metadata.default, this.metadata);
      _di.container.set(_fieldManager.default, this.fieldManager);
      this.initBroadcastChannel();
      await Promise.all([this.settings.load(), this.language.loadDefault(), this.initTemplateBundles()]);
      this.loader.setIsDeveloperMode(this.settings.get('isDeveloperMode'));
      this.user = new _user.default();
      this.preferences = new _preferences.default();
      this.preferences.setSettings(this.settings);
      this.acl = this.createAclManager();
      this.fieldManager.acl = this.acl;
      this.themeManager = new _themeManager.default(this.settings, this.preferences, this.metadata, this.themeName);
      this.modelFactory = new _modelFactory.default(this.metadata);
      this.collectionFactory = new _collectionFactory.default(this.modelFactory, this.settings, this.metadata);
      if (this.settings.get('useWebSocket')) {
        this.webSocketManager = new _webSocketManager.default(this.settings);
      }
      _di.container.set(_aclManager.default, this.acl);
      _di.container.set(_user.default, this.user);
      _di.container.set(_preferences.default, this.preferences);
      _di.container.set(_themeManager.default, this.themeManager);
      _di.container.set(_modelFactory.default, this.modelFactory);
      _di.container.set(_collectionFactory.default, this.collectionFactory);
      _di.container.set(_webSocketManager.default, this.webSocketManager);
      this.initUtils();
      this.initView();
      this.initBaseController();
    }

    /**
     * Start the application.
     */
    start() {
      this.initAuth();
      this.started = true;
      if (!this.auth) {
        this.baseController.login();
        return;
      }
      this.initUserData(null, () => this.onAuth());
    }

    /**
     * @private
     * @param {boolean} [afterLogin]
     */
    async onAuth() {
      let afterLogin = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
      await this.metadata.load();
      this.fieldManager.defs = this.metadata.get('fields') || {};
      this.fieldManager.metadata = this.metadata;
      this.settings.setDefs(this.metadata.get('entityDefs.Settings') || {});
      this.preferences.setDefs(this.metadata.get('entityDefs.Preferences') || {});
      this.viewHelper.layoutManager.setUserId(this.user.id);
      if (this.themeManager.isUserTheme()) {
        this.loadStylesheet();
      }
      this.applyUserStyle();
      if (this.anotherUser) {
        this.viewHelper.webSocketManager = null;
        this.webSocketManager = null;
      }
      if (this.webSocketManager) {
        this.webSocketManager.connect(this.auth, this.user.id);
      }
      const promiseList = [];
      const aclImplementationClassMap = {};
      const clientDefs = this.metadata.get('clientDefs') || {};
      Object.keys(clientDefs).forEach(scope => {
        const o = clientDefs[scope];
        const implClassName = (o || {})[this.aclName];
        if (!implClassName) {
          return;
        }
        const promise = new Promise(resolve => {
          this.loader.require(implClassName, Class => {
            aclImplementationClassMap[scope] = Class;
            resolve();
          });
        });
        promiseList.push(promise);
      });
      if (!this.themeManager.isApplied() && this.themeManager.isUserTheme()) {
        const promise = new Promise(resolve => {
          const check = i => {
            if (this.themeManager.isApplied() || i === 50) {
              resolve();
              return;
            }
            i = i || 0;
            setTimeout(() => check(i + 1), 10);
          };
          check();
        });
        promiseList.push(promise);
      }
      const promise = Promise.all(promiseList);
      if (afterLogin) {
        this.broadcastChannel.postMessage('logged-in');
      }
      await promise;
      this.acl.implementationClassMap = aclImplementationClassMap;
      this.initRouter();
    }

    /**
     * @private
     */
    initRouter() {
      const routes = this.metadata.get('app.clientRoutes') || {};
      this.router = new _router.default({
        routes: routes
      });
      _di.container.set(_router.default, this.router);
      this.viewHelper.router = this.router;
      this.baseController.setRouter(this.router);
      this.router.confirmLeaveOutMessage = this.language.translate('confirmLeaveOutMessage', 'messages');
      this.router.confirmLeaveOutConfirmText = this.language.translate('Yes');
      this.router.confirmLeaveOutCancelText = this.language.translate('Cancel');
      this.router.on('routed', params => this.doAction(params));
      try {
        _backbone.default.history.start({
          root: window.location.pathname
        });
      } catch (e) {
        _backbone.default.history.loadUrl();
      }
    }

    /**
     * Do an action.
     *
     * @public
     * @param {{
     *   controller?: string,
     *   action: string,
     *   options?: Object.<string,*>,
     *   controllerClassName?: string,
     * }} params
     */
    doAction(params) {
      this.trigger('action', params);
      this.baseController.trigger('action');
      const callback = controller => {
        try {
          controller.doAction(params.action, params.options);
          this.trigger('action:done');
        } catch (e) {
          console.error(e);
          switch (e.name) {
            case 'AccessDenied':
              this.baseController.error403();
              break;
            case 'NotFound':
              this.baseController.error404();
              break;
            default:
              throw e;
          }
        }
      };
      if (params.controllerClassName) {
        this.createController(params.controllerClassName, null, callback);
        return;
      }
      this.getController(params.controller, callback);
    }

    /**
     * @private
     */
    initBaseController() {
      this.baseController = new _base.default({}, this.getControllerInjection());
      this.viewHelper.baseController = this.baseController;
    }

    /**
     * @private
     */
    getControllerInjection() {
      return {
        viewFactory: this.viewFactory,
        modelFactory: this.modelFactory,
        collectionFactory: this.collectionFactory,
        settings: this.settings,
        user: this.user,
        preferences: this.preferences,
        acl: this.acl,
        cache: this.cache,
        router: this.router,
        storage: this.storage,
        metadata: this.metadata,
        dateTime: this.dateTime,
        broadcastChannel: this.broadcastChannel,
        baseController: this.baseController
      };
    }

    /**
     * @param {string} name
     * @param {function(module:controller): void} callback
     * @private
     */
    getController(name, callback) {
      if (!name) {
        callback(this.baseController);
        return;
      }
      if (name in this.controllers) {
        callback(this.controllers[name]);
        return;
      }
      try {
        let className = this.metadata.get(`clientDefs.${name}.controller`);
        if (!className) {
          const module = this.metadata.get(`scopes.${name}.module`);
          className = _utils.default.composeClassName(module, name, 'controllers');
        }
        this.createController(className, name, callback);
      } catch (e) {
        this.baseController.error404();
      }
    }

    /**
     * @private
     * @return {module:controller}
     */
    createController(className, name, callback) {
      Espo.loader.require(className, controllerClass => {
        const injections = this.getControllerInjection();
        const controller = new controllerClass(this.baseController.params, injections);
        controller.name = name;
        controller.masterView = this.masterView;
        this.controllers[name] = controller;
        callback(controller);
      }, () => this.baseController.error404());
    }

    /**
     * @private
     */
    initUtils() {
      this.dateTime = new _dateTime.default();
      this.modelFactory.dateTime = this.dateTime;
      this.dateTime.setSettingsAndPreferences(this.settings, this.preferences);
      this.numberUtil = new _numberUtil.default(this.settings, this.preferences);
      _di.container.set(_dateTime.default, this.dateTime);
      _di.container.set(_numberUtil.default, this.numberUtil);
    }

    /**
     * Create an acl-manager.
     *
     * @protected
     * @return {module:acl-manager}
     */
    createAclManager() {
      return new _aclManager.default(this.user, null, this.settings.get('aclAllowDeleteCreated'));
    }

    /**
     * @private
     */
    initView() {
      const helper = this.viewHelper = new _viewHelper.default();
      helper.layoutManager = new _layoutManager.default(this.cache, this.id);
      helper.settings = this.settings;
      helper.config = this.settings;
      helper.user = this.user;
      helper.preferences = this.preferences;
      helper.acl = this.acl;
      helper.modelFactory = this.modelFactory;
      helper.collectionFactory = this.collectionFactory;
      helper.storage = this.storage;
      helper.sessionStorage = this.sessionStorage;
      helper.dateTime = this.dateTime;
      helper.language = this.language;
      helper.metadata = this.metadata;
      helper.fieldManager = this.fieldManager;
      helper.cache = this.cache;
      helper.themeManager = this.themeManager;
      helper.webSocketManager = this.webSocketManager;
      helper.numberUtil = this.numberUtil;
      helper.pageTitle = new _pageTitle.default(this.settings);
      helper.basePath = this.basePath;
      helper.appParams = this.appParams;
      helper.broadcastChannel = this.broadcastChannel;
      _di.container.set(_viewHelper.default, this.viewHelper);
      _di.container.set(_layoutManager.default, helper.layoutManager);
      _di.container.set(_pageTitle.default, helper.pageTitle);
      this.viewLoader = (viewName, callback) => {
        this.loader.require(_utils.default.composeViewClassName(viewName), callback);
      };
      const internalModuleMap = {};
      const isModuleInternal = module => {
        if (!(module in internalModuleMap)) {
          internalModuleMap[module] = this.internalModuleList.indexOf(module) !== -1;
        }
        return internalModuleMap[module];
      };
      const getResourceInnerPath = (type, name) => {
        let path = null;
        switch (type) {
          case 'template':
            if (~name.indexOf('.')) {
              console.warn(name + ': template name should use slashes for a directory separator.');
            }
            path = 'res/templates/' + name.split('.').join('/') + '.tpl';
            break;
          case 'layoutTemplate':
            path = 'res/layout-types/' + name + '.tpl';
            break;
        }
        return path;
      };
      const getResourcePath = (type, name) => {
        if (!name.includes(':')) {
          return 'client/' + getResourceInnerPath(type, name);
        }
        const [mod, path] = name.split(':');
        if (mod === 'custom') {
          return 'client/custom/' + getResourceInnerPath(type, path);
        }
        if (isModuleInternal(mod)) {
          return 'client/modules/' + mod + '/' + getResourceInnerPath(type, path);
        }
        return 'client/custom/modules/' + mod + '/' + getResourceInnerPath(type, path);
      };
      this.viewFactory = new _bullbone.Factory({
        defaultViewName: 'views/base',
        helper: helper,
        viewLoader: this.viewLoader,
        resources: {
          loaders: {
            template: (name, callback) => {
              const path = getResourcePath('template', name);
              this.loader.require('res!' + path, callback);
            },
            layoutTemplate: (name, callback) => {
              if (Espo.layoutTemplates && name in Espo.layoutTemplates) {
                callback(Espo.layoutTemplates[name]);
                return;
              }
              const path = getResourcePath('layoutTemplate', name);
              this.loader.require('res!' + path, callback);
            }
          }
        },
        preCompiledTemplates: Espo.preCompiledTemplates || {}
      });
    }

    /**
     * @typedef {Record} module:app~LoginData
     * @property {Record} user
     * @property {Record} preferences
     * @property {Record} acl
     * @property {Record} settings
     * @property {Record} appParams
     * @property {string} language
     * @property {{
     *    userName: string,
     *    token: string,
     *    anotherUser?: string,
     * }} auth
     */

    /**
     * @public
     */
    initAuth() {
      this.auth = this.storage.get('user', 'auth') || null;
      this.anotherUser = this.storage.get('user', 'anotherUser') || null;
      this.baseController.on('login', /** module:app~LoginData */data => {
        const userId = data.user.id;
        const userName = data.auth.userName;
        const token = data.auth.token;
        const anotherUser = data.auth.anotherUser || null;
        this.auth = _jsBase.default.encode(userName + ':' + token);
        this.anotherUser = anotherUser;
        const lastUserId = this.storage.get('user', 'lastUserId');
        if (lastUserId !== userId) {
          this.metadata.clearCache();
          this.language.clearCache();
        }
        this.storage.set('user', 'auth', this.auth);
        this.storage.set('user', 'lastUserId', userId);
        this.storage.set('user', 'anotherUser', this.anotherUser);
        this.setCookieAuth(userName, token);
        this.initUserData(data, () => this.onAuth(true));
      });
      this.baseController.on('logout', () => this.logout());
    }

    /**
     * @private
     * @param {boolean} [afterFail]
     * @param {boolean} [silent]
     */
    logout(afterFail, silent) {
      let logoutWait = false;
      if (this.auth && !afterFail) {
        const arr = _jsBase.default.decode(this.auth).split(':');
        if (arr.length > 1) {
          logoutWait = this.appParams.get('logoutWait') || false;
          _ajax.default.postRequest('App/destroyAuthToken', {
            token: arr[1]
          }, {
            resolveWithXhr: true
          }).then(/** XMLHttpRequest */xhr => {
            const redirectUrl = xhr.getResponseHeader('X-Logout-Redirect-Url');
            if (redirectUrl) {
              setTimeout(() => window.location.href = redirectUrl, 50);
              return;
            }
            if (logoutWait) {
              this.doAction({
                action: 'login'
              });
            }
          });
        }
      }
      if (this.webSocketManager) {
        this.webSocketManager.close();
      }
      silent = silent || afterFail && this.auth && this.auth !== this.storage.get('user', 'auth');
      this.auth = null;
      this.anotherUser = null;
      this.user.clear();
      this.preferences.clear();
      this.acl.clear();
      if (!silent) {
        this.storage.clear('user', 'auth');
        this.storage.clear('user', 'anotherUser');
      }
      const action = logoutWait ? 'logoutWait' : 'login';
      this.doAction({
        action: action
      });
      if (!silent) {
        this.unsetCookieAuth();
      }
      if (this.broadcastChannel.object) {
        if (!silent) {
          this.broadcastChannel.postMessage('logged-out');
        }
      }
      if (!silent) {
        this.sendLogoutRequest();
      }
      this.loadStylesheet();
    }

    /**
     * @private
     */
    sendLogoutRequest() {
      const xhr = new XMLHttpRequest();
      xhr.open('GET', this.basePath + this.apiUrl + '/');
      xhr.setRequestHeader('Authorization', 'Basic ' + _jsBase.default.encode('**logout:logout'));
      xhr.send('');
      xhr.abort();
    }

    /**
     * @private
     */
    loadStylesheet() {
      if (!this.metadata.get(['themes'])) {
        return;
      }
      const path = this.basePath + this.themeManager.getStylesheet();
      const element = document.querySelector('#main-stylesheet');
      if (!element) {
        return;
      }
      element.setAttribute('href', path);
    }

    /**
     * @private
     */
    applyUserStyle() {
      const pageContentWidth = this.preferences.get('pageContentWidth');
      if (pageContentWidth) {
        document.body.dataset.contentWidth = pageContentWidth;
      }
    }

    /**
     * @private
     * @param {string} username
     * @param {string} token
     */
    setCookieAuth(username, token) {
      const date = new Date();
      date.setTime(date.getTime() + 1000 * 24 * 60 * 60 * 1000);
      document.cookie = `auth-token=${token}; SameSite=Lax; expires=${date.toUTCString()}; path=/`;
    }

    /**
     * @private
     */
    unsetCookieAuth() {
      document.cookie = `auth-token=; SameSite=Lax; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/`;
    }

    /**
     * User data.
     *
     * @typedef {Object} module:app~UserData
     * @property {Record} user
     * @property {Record} preferences
     * @property {Record} acl
     * @property {Record} settings
     * @property {Record} appParams
     * @property {string} language
     */

    /**
     * @private
     * @param {module:app~UserData|null} data
     * @param {function} callback
     */
    async initUserData(data, callback) {
      data = data || {};
      if (this.auth === null) {
        return;
      }
      if (!data.user) {
        data = await this.requestUserData();
      }
      this.language.name = data.language;
      await this.language.load();
      this.dateTime.setLanguage(this.language);
      const userData = data.user || null;
      const preferencesData = data.preferences || null;
      const aclData = data.acl || null;
      const settingData = data.settings || {};
      this.user.setMultiple(userData);
      this.preferences.setMultiple(preferencesData);
      this.settings.setMultiple(settingData);
      this.acl.set(aclData);
      this.appParams.setAll(data.appParams);
      if (!this.auth) {
        return;
      }
      const xhr = new XMLHttpRequest();
      xhr.open('GET', `${this.basePath}${this.apiUrl}/`);
      xhr.setRequestHeader('Authorization', `Basic ${this.auth}`);
      xhr.onreadystatechange = () => {
        if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
          const arr = _jsBase.default.decode(this.auth).split(':');
          this.setCookieAuth(arr[0], arr[1]);
          callback();
        }
        if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 401) {
          _ui.default.error('Auth error');
        }
      };
      xhr.send('');
    }

    /**
     * @private
     * @return {Promise<module:app~UserData>}
     */
    async requestUserData() {
      return _ajax.default.getRequest('App/user', {}, {
        appStart: true
      });
    }

    /**
     * @private
     */
    setupAjax() {
      var _this = this;
      /**
       * @param {XMLHttpRequest} xhr
       * @param {Object.<string, *>} options
       */
      const beforeSend = (xhr, options) => {
        if (this.auth !== null && !options.login) {
          xhr.setRequestHeader('Authorization', 'Basic ' + this.auth);
          xhr.setRequestHeader('Espo-Authorization', this.auth);
          xhr.setRequestHeader('Espo-Authorization-By-Token', 'true');
        }
        if (this.anotherUser !== null && !options.login) {
          xhr.setRequestHeader('X-Another-User', this.anotherUser);
        }
      };
      let appTimestampChangeProcessed = false;

      /**
       * @param {XMLHttpRequest} xhr
       * @param {Object.<string, *>} options
       */
      const onSuccess = (xhr, options) => {
        const appTimestampHeader = xhr.getResponseHeader('X-App-Timestamp');
        if (!appTimestampHeader || appTimestampChangeProcessed) {
          return;
        }
        const appTimestamp = parseInt(appTimestampHeader);

        // noinspection JSUnresolvedReference
        const bypassAppReload = options.bypassAppReload;
        if (this.appTimestamp &&
        // this.appTimestamp is set to current time if cache disabled.
        appTimestamp > this.appTimestamp && !bypassAppReload) {
          appTimestampChangeProcessed = true;
          _ui.default.confirm(this.language.translate('confirmAppRefresh', 'messages'), {
            confirmText: this.language.translate('Refresh'),
            cancelText: this.language.translate('Cancel'),
            backdrop: 'static',
            confirmStyle: 'success'
          }).then(() => {
            window.location.reload();
            if (this.broadcastChannel) {
              this.broadcastChannel.postMessage('reload');
            }
          });
        }
      };

      /**
       * @param {module:ajax.Xhr} xhr
       * @param {Object.<string, *>} options
       */
      const onError = function (xhr) {
        let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
        setTimeout(() => {
          if (xhr.errorIsHandled) {
            return;
          }
          switch (xhr.status) {
            case 200:
              _ui.default.error(_this.language.translate('Bad server response'));
              console.error('Bad server response: ' + xhr.responseText);
              break;
            case 401:
              // noinspection JSUnresolvedReference
              if (options.login) {
                break;
              }
              if (_this.auth && _this.router && !_this.router.hasConfirmLeaveOut()) {
                _this.logout(true);
                break;
              }
              if (_this.auth && _this.router && _this.router.hasConfirmLeaveOut()) {
                _ui.default.error(_this.language.translate('loggedOutLeaveOut', 'messages'), true);
                _this.router.trigger('logout');
                break;
              }
              if (_this.auth) {
                // noinspection JSUnresolvedReference
                const silent = !options.appStart;
                _this.logout(true, silent);
              }
              console.error('Error 401: Unauthorized.');
              break;
            case 403:
              // noinspection JSUnresolvedReference
              if (options.main) {
                _this.baseController.error403();
                break;
              }
              _this._processErrorAlert(xhr, 'Access denied');
              break;
            case 400:
              _this._processErrorAlert(xhr, 'Bad request');
              break;
            case 404:
              // noinspection JSUnresolvedReference
              if (options.main) {
                _this.baseController.error404();
                break;
              }
              _this._processErrorAlert(xhr, 'Not found', true);
              break;
            default:
              _this._processErrorAlert(xhr, null);
          }
          const statusReason = xhr.getResponseHeader('X-Status-Reason');
          if (statusReason) {
            console.error('Server side error ' + xhr.status + ': ' + statusReason);
          }
        }, 0);
      };
      const onTimeout = () => {
        _ui.default.error(this.language.translate('Timeout'), true);
      };
      const onOffline = () => {
        _ui.default.error(this.language.translate('No internet'));
      };
      _ajax.default.configure({
        apiUrl: this.basePath + this.apiUrl,
        timeout: this.ajaxTimeout,
        beforeSend: beforeSend,
        onSuccess: onSuccess,
        onError: onError,
        onTimeout: onTimeout,
        onOffline: onOffline
      });
    }

    /**
     * @private
     * @param {XMLHttpRequest} xhr
     * @param {string|null} label
     * @param {boolean} [noDetail]
     */
    _processErrorAlert(xhr, label, noDetail) {
      let msg = '';
      if (!label) {
        if (xhr.status === 0) {
          msg += this.language.translate('Network error');
        } else {
          msg += this.language.translate('Error') + ' ' + xhr.status;
        }
      } else {
        msg += this.language.translate(label);
      }
      const obj = {
        msg: msg,
        closeButton: true
      };
      let isMessageDone = false;
      if (noDetail) {
        isMessageDone = true;
      }
      if (!isMessageDone && xhr.responseText && xhr.responseText[0] === '{') {
        /** @type {Object.<string, *>|null} */
        let data = null;
        try {
          data = JSON.parse(xhr.responseText);
        } catch (e) {}
        if (data && data.messageTranslation && data.messageTranslation.label) {
          let msgDetail = this.language.translate(data.messageTranslation.label, 'messages', data.messageTranslation.scope);
          const msgData = data.messageTranslation.data || {};
          for (const key in msgData) {
            msgDetail = msgDetail.replace('{' + key + '}', msgData[key]);
          }
          obj.msg += '\n' + msgDetail;
          obj.closeButton = true;
          isMessageDone = true;
        }
        if (!isMessageDone && data && 'message' in data && data.message) {
          obj.msg += '\n' + data.message;
          obj.closeButton = true;
          isMessageDone = true;
        }
      }
      if (!isMessageDone) {
        const statusReason = xhr.getResponseHeader('X-Status-Reason');
        if (statusReason) {
          obj.msg += '\n' + statusReason;
          obj.closeButton = true;
        }
      }
      _ui.default.error(obj.msg, obj.closeButton);
    }

    /**
     * @private
     */
    initBroadcastChannel() {
      this.broadcastChannel = new _broadcastChannel.default();
      this.broadcastChannel.subscribe(event => {
        if (!this.auth && this.started) {
          if (event.data === 'logged-in') {
            // This works if the same instance opened in different tabs.
            // This does not work for different instances on the same domain
            // which may be the case in dev environment.
            window.location.reload();
          }
          return;
        }
        if (event.data === 'update:all') {
          this.metadata.loadSkipCache();
          this.settings.load();
          this.language.loadSkipCache();
          this.viewHelper.layoutManager.clearLoadedData();
          return;
        }
        if (event.data === 'update:metadata') {
          this.metadata.loadSkipCache();
          return;
        }
        if (event.data === 'update:config') {
          this.settings.load();
          return;
        }
        if (event.data === 'update:language') {
          this.language.loadSkipCache();
          return;
        }
        if (event.data === 'update:layout') {
          this.viewHelper.layoutManager.clearLoadedData();
          return;
        }
        if (event.data === 'update:appParams') {
          this.appParams.load();
          return;
        }
        if (event.data === 'reload') {
          window.location.reload();
          return;
        }
        if (event.data === 'logged-out' && this.started) {
          if (this.auth && this.router.hasConfirmLeaveOut()) {
            _ui.default.error(this.language.translate('loggedOutLeaveOut', 'messages'), true);
            this.router.trigger('logout');
            return;
          }
          this.logout(true);
        }
      });
      _di.container.set(_broadcastChannel.default, this.broadcastChannel);
    }

    /**
     * @private
     */
    async initTemplateBundles() {
      if (!this.responseCache) {
        return;
      }
      const key = 'templateBundlesCached';
      if (this.cache.get('app', key)) {
        return;
      }
      const files = ['client/lib/templates.tpl'];
      this.bundledModuleList.forEach(mod => {
        const file = this.internalModuleList.includes(mod) ? `client/modules/${mod}/lib/templates.tpl` : `client/custom/modules/${mod}/lib/templates.tpl`;
        files.push(file);
      });
      const baseUrl = _utils.default.obtainBaseUrl();
      const timestamp = this.loader.getCacheTimestamp();
      const promiseList = files.map(file => {
        const url = new URL(baseUrl + this.basePath + file);
        url.searchParams.append('t', this.appTimestamp);
        return new Promise(resolve => {
          fetch(url).then(response => {
            if (!response.ok) {
              console.error(`Could not fetch ${url}.`);
              resolve();
              return;
            }
            const promiseList = [];
            response.text().then(text => {
              const index = text.indexOf('\n');
              if (index <= 0) {
                resolve();
                return;
              }
              const delimiter = text.slice(0, index + 1);
              text = text.slice(index + 1);
              text.split(delimiter).forEach(item => {
                const index = item.indexOf('\n');
                const file = item.slice(0, index).trim();
                let content = item.slice(index + 1);

                // noinspection RegExpDuplicateCharacterInClass
                content = content.replace(/[\r|\n|\r\n]$/, '');
                const url = baseUrl + this.basePath + 'client/' + file;
                const urlObj = new URL(url);
                urlObj.searchParams.append('r', timestamp);
                promiseList.push(this.responseCache.put(urlObj, new Response(content)));
              });
            });
            Promise.all(promiseList).then(() => resolve());
          });
        });
      });
      await Promise.all(promiseList);
      this.cache.set('app', key, true);
    }
  }

  /**
   * @callback module:app~callback
   * @param {App} app A created application instance.
   */

  /**
   * Application options.
   *
   * @typedef {Object} module:app~Options
   * @property {string} [id] An application ID.
   * @property {string} [basePath] A base path.
   * @property {boolean} [useCache] Use cache.
   * @property {string} [apiUrl] An API URL.
   * @property {Number} [ajaxTimeout] A default ajax request timeout.
   * @property {string} [internalModuleList] A list of internal modules.
   *   Internal modules located in the `client/modules` directory.
   * @property {string} [bundledModuleList] A list of bundled modules.
   * @property {Number|null} [cacheTimestamp] A cache timestamp.
   * @property {Number|null} [appTimestamp] An application timestamp.
   * @property {string|null} [theme] A theme name.
   */

  Object.assign(App.prototype, _bullbone.Events);
  App.extend = _bullbone.View.extend;
  var _default = _exports.default = App;
});

define("action-handler", ["exports", "bullbone"], function (_exports, _bullbone) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 action-handler */

  /**
   * An action handler. To be extended by specific action handlers.
   */
  class ActionHandler {
    /**
     * @param {module:view} view A view.
     */
    constructor(view) {
      /**
       * @protected
       */
      this.view = view;
    }
  }
  ActionHandler.extend = _bullbone.View.extend;
  var _default = _exports.default = ActionHandler;
});

define("acl-portal-manager", ["exports", "acl-manager", "acl-portal"], function (_exports, _aclManager, _aclPortal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _aclManager = _interopRequireDefault(_aclManager);
  _aclPortal = _interopRequireDefault(_aclPortal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 acl-portal-manager */

  /**
   * An access checking class for a specific scope for portals.
   */
  class AclPortalManager extends _aclManager.default {
    // noinspection JSUnusedGlobalSymbols
    /**
     * Check if a user in an account of a model.
     *
     * @param {module:model} model A model.
     * @returns {boolean|null} True if in an account, null if not clear.
     */
    checkInAccount(model) {
      const impl = /** @type {module:acl-portal} */this.getImplementation(model.entityType);
      return impl.checkInAccount(model);
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Check if a user is a contact-owner to a model.
     *
     * @param {module:model} model A model.
     * @returns {boolean|null} True if in a contact-owner, null if not clear.
     */
    checkIsOwnContact(model) {
      const impl = /** @type {module:acl-portal} */
      this.getImplementation(model.entityType);
      return impl.checkIsOwnContact(model);
    }

    /**
     * @param {string} scope A scope.
     * @returns {module:acl-portal}
     */
    getImplementation(scope) {
      if (!(scope in this.implementationHash)) {
        let implementationClass = _aclPortal.default;
        if (scope in this.implementationClassMap) {
          implementationClass = this.implementationClassMap[scope];
        }
        const params = {
          aclAllowDeleteCreated: false,
          forbiddenFieldList: this.getScopeForbiddenFieldList(scope)
        };
        this.implementationHash[scope] = new implementationClass(this.getUser(), scope, params, this);
      }
      return this.implementationHash[scope];
    }
  }
  var _default = _exports.default = AclPortalManager;
});

define("views/list-with-categories", ["exports", "views/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/list-with-categories */

  class ListWithCategories extends _list.default {
    template = 'list-with-categories';
    quickCreate = true;
    storeViewAfterCreate = true;
    storeViewAfterUpdate = true;
    /** @type {string|null} */
    currentCategoryId = null;
    currentCategoryName = '';
    /** @type {string|null} */
    categoryScope = null;
    categoryField = 'category';
    categoryFilterType = 'inCategory';
    isExpanded = false;
    hasExpandedToggler = true;
    expandedTogglerDisabled = false;
    keepCurrentRootUrl = true;
    hasNavigationPanel = false;
    /** @private */
    nestedCollectionIsBeingFetched = false;
    /**
     * @type {module:collections/tree}
     * @private
     */
    nestedCategoriesCollection;

    /**
     * @protected
     * @type {boolean}
     */
    isCategoryMultiple;
    data() {
      const data = {};
      data.hasTree = (this.isExpanded || this.hasNavigationPanel) && !this.categoriesDisabled;
      data.hasNestedCategories = !this.categoriesDisabled;
      data.fallback = !data.hasTree && !data.hasNestedCategories;
      return data;
    }
    setup() {
      super.setup();
      this.addActionHandler('toggleExpandedFromNavigation', () => this.actionToggleExpandedFromNavigation());
      this.defaultMaxSize = this.collection.maxSize;
      if (!this.categoryScope) {
        this.categoryScope = `${this.scope}Category`;
      }
      this.categoryField = this.getMetadata().get(`scopes.${this.categoryScope}.categoryField`) || this.categoryField;
      this.isCategoryMultiple = this.getMetadata().get(`entityDefs.${this.scope}.fields.${this.categoryField}.type`) === 'linkMultiple';
      this.showEditLink = this.getAcl().check(this.categoryScope, 'edit') || this.getAcl().check(this.categoryScope, 'create');
      const isExpandedByDefault = this.getMetadata().get(['clientDefs', this.categoryScope, 'isExpandedByDefault']) || false;
      if (isExpandedByDefault) {
        this.isExpanded = true;
      }
      const isCollapsedByDefault = this.getMetadata().get(['clientDefs', this.categoryScope, 'isCollapsedByDefault']) || false;
      if (isCollapsedByDefault) {
        this.isExpanded = false;
      }
      this.categoriesDisabled = this.categoriesDisabled || this.getMetadata().get(['scopes', this.categoryScope, 'disabled']) || !this.getAcl().checkScope(this.categoryScope);
      if (this.categoriesDisabled) {
        this.isExpanded = true;
        this.hasExpandedToggler = false;
        this.hasNavigationPanel = false;
      } else if (!this.expandedTogglerDisabled) {
        if (!this.getUser().isPortal() && this.hasIsExpandedStoredValue()) {
          this.isExpanded = this.getIsExpandedStoredValue();
        }
        if (this.getUser().isPortal()) {
          this.hasExpandedToggler = false;
          this.isExpanded = false;
        }
      }
      if (this.hasNavigationPanelStoredValue()) {
        this.hasNavigationPanel = this.getNavigationPanelStoredValue();
      } else {
        this.hasNavigationPanel = this.getMetadata().get(`scopes.${this.categoryScope}.showNavigationPanel`) || this.hasNavigationPanel;
      }
      const params = this.options.params || {};
      if ('categoryId' in params) {
        this.currentCategoryId = params.categoryId;
      }
      this.applyCategoryToCollection();
      this.listenTo(this.collection, 'sync', (c, d, o) => {
        if (o && o.openCategory) {
          return;
        }
        this.controlListVisibility();
      });
    }

    /**
     * @inheritDoc
     */
    prepareCreateReturnDispatchParams(params) {
      if (this.currentCategoryId) {
        params.options.categoryId = this.currentCategoryId;
        params.options.categoryName = this.currentCategoryName;
      }
    }

    /**
     * @inheritDoc
     */
    setupReuse(params) {
      this.applyRoutingParams(params);
    }

    /**
     * @private
     * @param {Record} params
     */
    applyRoutingParams(params) {
      if ('categoryId' in params) {
        if (params.categoryId !== this.currentCategoryId) {
          this.openCategory(params.categoryId, params.categoryName);
        }
      }
      this.selectCurrentCategory();
    }

    /**
     * @private
     * @return {boolean}
     */
    hasTextFilter() {
      return !!this.collection.data.textFilter || this.collection.where && this.collection.where.find(it => it.type === 'textFilter');
    }
    hasNavigationPanelStoredValue() {
      return this.getStorage().has('state', `categories-navigation-panel-${this.scope}`);
    }
    getNavigationPanelStoredValue() {
      const value = this.getStorage().get('state', `categories-navigation-panel-${this.scope}`);
      return value === 'true' || value === true;
    }
    setNavigationPanelStoredValue(value) {
      return this.getStorage().set('state', `categories-navigation-panel-${this.scope}`, value);
    }
    hasIsExpandedStoredValue() {
      return this.getStorage().has('state', `categories-expanded-${this.scope}`);
    }
    getIsExpandedStoredValue() {
      const value = this.getStorage().get('state', `categories-expanded-${this.scope}`);
      return value === 'true' || value === true;
    }
    setIsExpandedStoredValue(value) {
      return this.getStorage().set('state', `categories-expanded-${this.scope}`, value);
    }
    afterRender() {
      this.$nestedCategoriesContainer = this.$el.find('.nested-categories-container');
      this.$listContainer = this.$el.find('.list-container');
      if (!this.hasView('list')) {
        if (!this.isExpanded) {
          this.hideListContainer();
        }
        this.loadList();
      } else {
        this.controlListVisibility();
      }
      if (!this.categoriesDisabled && (this.isExpanded || this.hasNavigationPanel) && !this.hasView('categories')) {
        this.loadCategories();
      }
      if (!this.hasView('nestedCategories') && !this.categoriesDisabled) {
        this.loadNestedCategories();
      }
      this.$el.focus();
    }

    /**
     * @private
     */
    clearCategoryViews() {
      this.clearNestedCategoriesView();
      this.clearCategoriesView();
    }

    /**
     * @private
     */
    clearCategoriesView() {
      this.clearView('categories');
    }

    /**
     * @private
     */
    clearNestedCategoriesView() {
      this.clearView('nestedCategories');
    }

    /**
     * @private
     */
    emptyListContainer() {
      this.$listContainer.empty();
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @private
     */
    async actionExpand() {
      this.isExpanded = true;
      this.setIsExpandedStoredValue(true);
      this.applyCategoryToCollection();
      this.clearNestedCategoriesView();
      if (this.getCategoriesView()) {
        this.getCategoriesView().isExpanded = true;
        this.getCategoriesView().expandToggleInactive = true;
      }
      this.reRender().then(() => {});
      this.emptyListContainer();
      await this.collection.fetch();
      if (this.getCategoriesView()) {
        this.getCategoriesView().expandToggleInactive = false;
        await this.getCategoriesView().reRender();
      }
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @private
     */
    async actionCollapse() {
      this.isExpanded = false;
      this.setIsExpandedStoredValue(false);
      this.applyCategoryToCollection();
      this.applyCategoryToNestedCategoriesCollection();
      this.clearNestedCategoriesView();
      if (this.getCategoriesView()) {
        this.getCategoriesView().isExpanded = false;
        this.getCategoriesView().expandToggleInactive = true;
      }
      this.reRender().then(() => {});
      this.emptyListContainer();
      await this.collection.fetch();
      if (this.getCategoriesView()) {
        this.getCategoriesView().expandToggleInactive = false;
        await this.getCategoriesView().reRender();
      }
    }

    // noinspection JSUnusedGlobalSymbols
    actionOpenCategory(data) {
      this.openCategory(data.id || null, data.name);
      this.selectCurrentCategory();
      this.navigateToCurrentCategory();
    }
    navigateToCurrentCategory() {
      let url = `#${this.scope}`;
      if (this.currentCategoryId) {
        url += `/list/categoryId=${this.currentCategoryId}`;
        if (this._primaryFilter) {
          url += `&primaryFilter=${this.getHelper().escapeString(this._primaryFilter)}`;
        }
      } else {
        if (this._primaryFilter) {
          url += `/list/primaryFilter=${this.getHelper().escapeString(this._primaryFilter)}`;
        }
      }
      this.getRouter().navigate(url);
      this.updateLastUrl();
    }
    selectCurrentCategory() {
      const categoriesView = this.getCategoriesView();
      if (categoriesView) {
        categoriesView.setSelected(this.currentCategoryId);
        categoriesView.reRender();
      }
    }

    /**
     * @param {string|null} id
     * @param {string|null} [name]
     */
    openCategory(id, name) {
      this.getNestedCategoriesView().isLoading = true;
      this.getNestedCategoriesView().reRender();
      this.getNestedCategoriesView().isLoading = false;
      this.nestedCategoriesCollection.reset();
      this.collection.reset();
      this.collection.offset = 0;
      this.collection.maxSize = this.defaultMaxSize;
      this.emptyListContainer();
      this.currentCategoryId = id;
      this.currentCategoryName = name || id;
      this.applyCategoryToNestedCategoriesCollection();
      this.applyCategoryToCollection();
      this.collection.abortLastFetch();
      if (this.nestedCategoriesCollection) {
        this.nestedCategoriesCollection.abortLastFetch();
        this.hideListContainer();
        this.$nestedCategoriesContainer.addClass('hidden');
        Espo.Ui.notifyWait();
        const promises = [this.nestedCategoriesCollection.fetch().then(() => this.updateHeader()), this.collection.fetch({
          openCategory: true
        })];
        Promise.all(promises).then(() => {
          Espo.Ui.notify(false);
          this.controlNestedCategoriesVisibility();
          this.controlListVisibility();
        });
        return;
      }
      this.collection.fetch().then(() => {
        Espo.Ui.notify(false);
      });
    }

    /**
     * @private
     */
    controlListVisibility() {
      if (this.isExpanded) {
        this.showListContainer();
        return;
      }
      if (this.nestedCollectionIsBeingFetched) {
        return;
      }
      if (!this.collection.models.length && this.nestedCategoriesCollection && this.nestedCategoriesCollection.models.length && !this.hasTextFilter()) {
        this.hideListContainer();
        return;
      }
      this.showListContainer();
    }

    /**
     * @private
     */
    controlNestedCategoriesVisibility() {
      this.$nestedCategoriesContainer.removeClass('hidden');
    }

    /**
     * @private
     * @param {function(import('collection').default)} callback
     */
    getTreeCollection(callback) {
      this.getCollectionFactory().create(this.categoryScope).then(collection => {
        collection.url = `${collection.entityType}/action/listTree`;
        collection.setOrder(null, null);

        // @todo Revise. To remove?
        this.collection.treeCollection = collection;
        collection.fetch().then(() => callback.call(this, collection));
      });
    }
    applyCategoryToNestedCategoriesCollection() {
      if (!this.nestedCategoriesCollection) {
        return;
      }
      this.nestedCategoriesCollection.parentId = this.currentCategoryId;
      this.nestedCategoriesCollection.currentCategoryId = this.currentCategoryId;
      this.nestedCategoriesCollection.currentCategoryName = this.currentCategoryName || this.currentCategoryId;
      this.nestedCategoriesCollection.where = [];
    }

    /**
     * @private
     * @param {function(import('collection').default)} callback
     */
    getNestedCategoriesCollection(callback) {
      this.getCollectionFactory().create(this.categoryScope, async collection => {
        this.nestedCategoriesCollection = collection;
        collection.setOrder(null, null);
        collection.url = `${collection.entityType}/action/listTree`;
        collection.data.checkIfEmpty = true;
        if (!this.getAcl().checkScope(this.scope, 'create')) {
          collection.data.onlyNotEmpty = true;
        }
        this.applyCategoryToNestedCategoriesCollection();
        this.nestedCollectionIsBeingFetched = true;

        // Needed even in expanded mode to display the header path.
        await collection.fetch();
        this.nestedCollectionIsBeingFetched = false;
        this.controlNestedCategoriesVisibility();
        this.controlListVisibility();
        this.updateHeader();
        callback.call(this, collection);
      });
    }

    /**
     * @return {module:views/record/list-nested-categories}
     */
    getNestedCategoriesView() {
      return /** @type module:views/record/list-nested-categories */this.getView('nestedCategories');
    }

    /**
     * @return {module:views/record/list-tree}
     */
    getCategoriesView() {
      return /** @type module:views/record/list-tree */this.getView('categories');
    }

    /**
     * @private
     */
    loadNestedCategories() {
      this.getNestedCategoriesCollection(collection => {
        this.createView('nestedCategories', 'views/record/list-nested-categories', {
          collection: collection,
          itemCollection: this.collection,
          selector: '.nested-categories-container',
          showEditLink: this.showEditLink,
          isExpanded: this.isExpanded,
          hasExpandedToggler: this.hasExpandedToggler,
          hasNavigationPanel: this.hasNavigationPanel,
          subjectEntityType: this.collection.entityType,
          primaryFilter: this._primaryFilter
        }, view => {
          view.render();
        });
      });
    }

    /**
     * @private
     */
    loadCategories() {
      this.getTreeCollection(collection => {
        this.createView('categories', 'views/record/list-tree', {
          collection: collection,
          selector: '.categories-container',
          selectable: true,
          showRoot: true,
          buttonsDisabled: true,
          checkboxes: false,
          showEditLink: this.showEditLink,
          isExpanded: this.isExpanded,
          hasExpandedToggler: this.hasExpandedToggler,
          readOnly: true
        }, view => {
          if (this.currentCategoryId) {
            view.setSelected(this.currentCategoryId);
          }
          view.render();
          this.listenTo(view, 'select', /** import('model').default */model => {
            if (!this.isExpanded) {
              let id = null;
              let name = null;
              if (model && model.id) {
                id = model.id;
                name = model.attributes.name;
              }
              this.openCategory(id, name);
              this.navigateToCurrentCategory();
              return;
            }
            this.currentCategoryId = null;
            this.currentCategoryName = '';
            if (model && model.id) {
              this.currentCategoryId = model.id;
              this.currentCategoryName = model.attributes.name;
            }
            this.collection.offset = 0;
            this.collection.maxSize = this.defaultMaxSize;
            this.collection.reset();
            this.applyCategoryToCollection();
            this.collection.abortLastFetch();
            this.openCategory(this.currentCategoryId, this.currentCategoryName);
            this.navigateToCurrentCategory();
          });
        });
      });
    }

    /**
     * @private
     * @todo Move to helper. Together with select-records view.
     */
    applyCategoryToCollection() {
      this.collection.whereFunction = () => {
        let filter;
        const isExpanded = this.isExpanded;
        if (!isExpanded && !this.hasTextFilter()) {
          if (this.isCategoryMultiple) {
            if (this.currentCategoryId) {
              filter = {
                attribute: this.categoryField,
                type: 'linkedWith',
                value: [this.currentCategoryId]
              };
            } else {
              filter = {
                attribute: this.categoryField,
                type: 'isNotLinked'
              };
            }
          } else {
            if (this.currentCategoryId) {
              filter = {
                attribute: this.categoryField + 'Id',
                type: 'equals',
                value: this.currentCategoryId
              };
            } else {
              filter = {
                attribute: this.categoryField + 'Id',
                type: 'isNull'
              };
            }
          }
        } else {
          if (this.currentCategoryId) {
            filter = {
              attribute: this.categoryField,
              type: this.categoryFilterType,
              value: this.currentCategoryId
            };
          }
        }
        if (filter) {
          return [filter];
        }
      };
    }

    /**
     * @inheritDoc
     */
    getCreateAttributes() {
      let data;
      if (this.isCategoryMultiple) {
        if (this.currentCategoryId) {
          const names = {};
          names[this.currentCategoryId] = this.getCurrentCategoryName();
          data = {};
          const idsAttribute = this.categoryField + 'Ids';
          const namesAttribute = this.categoryField + 'Names';
          data[idsAttribute] = [this.currentCategoryId];
          data[namesAttribute] = names;
          return data;
        }
        return null;
      }
      const idAttribute = this.categoryField + 'Id';
      const nameAttribute = this.categoryField + 'Name';
      data = {};
      data[idAttribute] = this.currentCategoryId;
      data[nameAttribute] = this.getCurrentCategoryName();
      return data;
    }

    /**
     * @private
     * @return {string|null}
     */
    getCurrentCategoryName() {
      if (this.currentCategoryName) {
        return this.currentCategoryName;
      }
      if (this.nestedCategoriesCollection && this.nestedCategoriesCollection.categoryData && this.nestedCategoriesCollection.categoryData.name) {
        return this.nestedCategoriesCollection.categoryData.name;
      }
      return this.currentCategoryId;
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @private
     */
    actionManageCategories() {
      this.clearCategoryViews();
      this.getRouter().navigate('#' + this.categoryScope, {
        trigger: true
      });
    }

    /**
     * @inheritDoc
     */
    getHeader() {
      if (!this.nestedCategoriesCollection) {
        return super.getHeader();
      }
      const path = this.nestedCategoriesCollection.path;
      if (!path || path.length === 0) {
        return super.getHeader();
      }
      let rootUrl = `#${this.scope}`;
      if (this._primaryFilter) {
        const filterPart = this.getHelper().escapeString(this._primaryFilter);
        rootUrl += `/list/primaryFilter=${filterPart}`;
      }
      const root = document.createElement('a');
      root.href = rootUrl;
      root.textContent = this.translate(this.scope, 'scopeNamesPlural');
      root.dataset.action = 'openCategory';
      root.classList.add('action');
      root.style.userSelect = 'none';

      /** @type {*[]} */
      const list = [root];
      const currentName = this.nestedCategoriesCollection.categoryData.name;
      const upperId = this.nestedCategoriesCollection.categoryData.upperId;
      const upperName = this.nestedCategoriesCollection.categoryData.upperName;
      if (path.length > 2) {
        list.push('...');
      }
      if (upperId) {
        const upperIdPart = this.getHelper().escapeString(upperId);
        let url = `${rootUrl}/list/categoryId=${upperIdPart}`;
        if (this._primaryFilter) {
          const filterPart = this.getHelper().escapeString(this._primaryFilter);
          url += `&primaryFilter=${filterPart}`;
        }
        const folder = document.createElement('a');
        folder.href = url;
        folder.textContent = upperName;
        folder.classList.add('action');
        folder.dataset.action = 'openCategory';
        folder.dataset.id = upperId;
        folder.dataset.name = upperName;
        folder.style.userSelect = 'none';
        list.push(folder);
      }
      const last = document.createElement('span');
      last.textContent = currentName;
      last.dataset.action = 'fullRefresh';
      last.style.cursor = 'pointer';
      last.style.userSelect = 'none';
      list.push(last);
      return this.buildHeaderHtml(list);
    }

    /**
     * @protected
     */
    updateHeader() {
      this.getView('header').reRender();
    }

    /**
     * @protected
     */
    hideListContainer() {
      this.$listContainer.addClass('hidden');
    }

    /**
     * @protected
     */
    showListContainer() {
      this.$listContainer.removeClass('hidden');
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @private
     * @return {Promise}
     */
    async actionToggleNavigationPanel() {
      this.hasNavigationPanel = !this.hasNavigationPanel;
      this.setNavigationPanelStoredValue(this.hasNavigationPanel);
      await this.reRender();
      this.loadNestedCategories();
    }

    /**
     * @inheritDoc
     */
    prepareRecordViewOptions(options) {
      super.prepareRecordViewOptions(options);
      options.forceDisplayTopBar = false;
    }

    /**
     * @private
     */
    async actionToggleExpandedFromNavigation() {
      this.isExpanded = !this.isExpanded;
      this.hasNavigationPanel = true;
      this.setNavigationPanelStoredValue(this.hasNavigationPanel);

      /** @type {HTMLAnchorElement} */
      const a = this.element.querySelector('a[data-role="expandButtonContainer"]');
      if (a) {
        a.classList.add('disabled');
      }
      Espo.Ui.notifyWait();
      if (this.isExpanded) {
        await this.actionExpand();
      } else {
        await this.actionCollapse();
      }
      Espo.Ui.notify();
    }
  }
  var _default = _exports.default = ListWithCategories;
});

define("views/global-stream", ["exports", "view", "views/stream/record/list", "views/record/search", "search-manager"], function (_exports, _view, _list, _search, _searchManager) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _list = _interopRequireDefault(_list);
  _search = _interopRequireDefault(_search);
  _searchManager = _interopRequireDefault(_searchManager);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class GlobalStreamView extends _view.default {
    // language=Handlebars
    templateContent = `
        <div class="page-header">
            <div class="row">
                <div class="col-sm-7 col-xs-5">
                    <h3>{{translate 'GlobalStream' category='scopeNames'}}</h3>
                </div>
                <div class="col-sm-5 col-xs-7"></div>
            </div>
        </div>
        <div class="search-container">{{{search}}}</div>
        <div class="row">
            <div class="col-md-8">
                <div class="list-container list-container-panel">{{{list}}}</div>
            </div>
        </div>
    `;

    /** @type {import('collections/note').default} */
    collection;
    setup() {
      this.wait(this.getCollectionFactory().create('Note').then(/** import('collections/note').default */collection => {
        this.collection = collection;
        this.collection.url = 'GlobalStream';
        this.collection.maxSize = this.getConfig().get('recordsPerPage');
        this.collection.paginationByNumber = true;
        this.setupSearchManager();
        this.createSearchView();
      }));
    }
    setupSearchManager() {
      const searchManager = new _searchManager.default(this.collection);
      searchManager.loadStored();
      this.collection.where = searchManager.getWhere();
      this.searchManager = searchManager;
    }
    createSearchView() {
      this.searchView = new _search.default({
        collection: this.collection,
        searchManager: this.searchManager,
        isWide: true,
        filtersLayoutName: 'filtersGlobal'
      });
      return this.assignView('search', this.searchView, '.search-container');
    }
    afterRender() {
      if (!this.listView) {
        this.fetchAndRender();
      }
    }
    fetchAndRender() {
      Espo.Ui.notifyWait();
      this.collection.fetch().then(() => {
        this.listView = new _list.default({
          collection: this.collection,
          isUserStream: true
        });
        this.assignView('list', this.listView, '.list-container').then(() => {
          Espo.Ui.notify(false);
          this.listView.render();
        });
      });
    }
  }
  var _default = _exports.default = GlobalStreamView;
});

define("views/edit", ["exports", "views/main"], function (_exports, _main) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _main = _interopRequireDefault(_main);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 module:views/edit */

  /**
   * An edit view.
   */
  class EditView extends _main.default {
    /** @inheritDoc */
    template = 'edit';

    /** @inheritDoc */
    name = 'Edit';

    /** @inheritDoc */
    optionsToPass = ['returnUrl', 'returnDispatchParams', 'attributes', 'rootUrl', 'duplicateSourceId', 'returnAfterCreate', 'highlightFieldList'];

    /**
     * A header view name.
     *
     * @type {string}
     */
    headerView = 'views/header';

    /**
     * A record view name.
     *
     * @type {string}
     */
    recordView = 'views/record/edit';

    /**
     * A root breadcrumb item not to be a link.
     *
     * @type {boolean}
     */
    rootLinkDisabled = false;

    /**
     * A root URL.
     *
     * @type {string}
     */
    rootUrl;

    /**
     * @private
     * @type {string}
     */
    nameAttribute;

    /** @inheritDoc */
    setup() {
      this.headerView = this.options.headerView || this.headerView;
      this.recordView = this.options.recordView || this.recordView;
      this.rootUrl = this.options.rootUrl || this.options.params.rootUrl || '#' + this.scope;
      this.nameAttribute = this.getMetadata().get(`clientDefs.${this.entityType}.nameAttribute`) || 'name';
      this.setupHeader();
      this.setupRecord();
    }

    /** @inheritDoc */
    setupFinal() {
      super.setupFinal();
      this.wait(this.getHelper().processSetupHandlers(this, 'edit'));
    }

    /**
     * Set up a header.
     */
    setupHeader() {
      this.createView('header', this.headerView, {
        model: this.model,
        fullSelector: '#main > .header',
        scope: this.scope
      });
    }

    /**
     * Set up a record.
     */
    setupRecord() {
      const o = {
        model: this.model,
        fullSelector: '#main > .record',
        scope: this.scope,
        shortcutKeysEnabled: true
      };
      this.optionsToPass.forEach(option => {
        o[option] = this.options[option];
      });
      const params = this.options.params || {};
      if (params.rootUrl) {
        o.rootUrl = params.rootUrl;
      }
      if (params.rootData) {
        o.rootData = params.rootData;
      }
      if (params.focusForCreate) {
        o.focusForCreate = true;
      }
      return this.createView('record', this.getRecordViewName(), o);
    }

    /**
     * @return {module:views/record/edit}
     */
    getRecordView() {
      return this.getView('record');
    }

    /**
     * Get a record view name.
     *
     * @returns {string}
     */
    getRecordViewName() {
      return this.getMetadata().get('clientDefs.' + this.scope + '.recordViews.edit') || this.recordView;
    }

    /** @inheritDoc */
    getHeader() {
      const scopeLabel = this.getLanguage().translate(this.scope, 'scopeNamesPlural');
      let root = document.createElement('span');
      root.text = scopeLabel;
      root.style.userSelect = 'none';
      if (!this.options.noHeaderLinks && !this.rootLinkDisabled) {
        const a = document.createElement('a');
        a.href = this.rootUrl;
        a.classList.add('action');
        a.dataset.action = 'navigateToRoot';
        a.text = scopeLabel;
        root = document.createElement('span');
        root.style.userSelect = 'none';
        root.append(a);
      }
      const iconHtml = this.getHeaderIconHtml();
      if (iconHtml) {
        root.insertAdjacentHTML('afterbegin', iconHtml);
      }
      if (this.model.isNew()) {
        const create = document.createElement('span');
        create.textContent = this.getLanguage().translate('create');
        create.style.userSelect = 'none';
        return this.buildHeaderHtml([root, create]);
      }
      const name = this.model.attributes[this.nameAttribute] || this.model.id;
      let title = document.createElement('span');
      title.textContent = name;
      if (!this.options.noHeaderLinks) {
        const url = `#${this.scope}/view/${this.model.id}`;
        const a = document.createElement('a');
        a.href = url;
        a.classList.add('action');
        a.append(title);
        title = a;
      }
      return this.buildHeaderHtml([root, title]);
    }

    /** @inheritDoc */
    updatePageTitle() {
      if (this.model.isNew()) {
        const title = this.getLanguage().translate('Create') + ' ' + this.getLanguage().translate(this.scope, 'scopeNames');
        this.setPageTitle(title);
        return;
      }
      const name = this.model.attributes[this.nameAttribute];
      const title = name ? name : this.getLanguage().translate(this.scope, 'scopeNames');
      this.setPageTitle(title);
    }
  }
  var _default = _exports.default = EditView;
});

define("views/user/record/detail", ["exports", "views/record/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserDetailRecordView extends _detail.default {
    sideView = 'views/user/record/detail-side';
    bottomView = 'views/user/record/detail-bottom';
    editModeDisabled = true;

    /**
     * @name model
     * @type module:models/user
     * @memberOf UserDetailRecordView#
     */

    setup() {
      super.setup();
      this.setupNonAdminFieldsAccess();
      if (this.getUser().isAdmin() && !this.model.isPortal()) {
        this.addButton({
          name: 'access',
          label: 'Access',
          style: 'default',
          onClick: () => this.actionAccess()
        });
      }
      const isPortalUser = this.model.isPortal() || this.model.id === this.getUser().id && this.getUser().isPortal();
      if ((this.model.id === this.getUser().id || this.getUser().isAdmin()) && this.getConfig().get('auth2FA') && (this.model.isRegular() || this.model.isAdmin() || isPortalUser && this.getConfig().get('auth2FAInPortal'))) {
        this.addButton({
          name: 'viewSecurity',
          label: 'Security'
        });
      }
      if (this.model.id === this.getUser().id && !this.model.isApi() && (this.getUser().isAdmin() || !this.getHelper().getAppParam('passwordChangeForNonAdminDisabled'))) {
        this.addDropdownItem({
          name: 'changePassword',
          label: 'Change Password',
          style: 'default'
        });
      }
      if (this.getUser().isAdmin() && (this.model.isRegular() || this.model.isAdmin() || this.model.isPortal()) && !this.model.isSuperAdmin()) {
        this.addDropdownItem({
          name: 'sendPasswordChangeLink',
          label: 'Send Password Change Link',
          action: 'sendPasswordChangeLink',
          hidden: !this.model.get('emailAddress')
        });
        this.addDropdownItem({
          name: 'generateNewPassword',
          label: 'Generate New Password',
          action: 'generateNewPassword',
          hidden: !this.model.get('emailAddress')
        });
        if (!this.model.get('emailAddress')) {
          this.listenTo(this.model, 'sync', () => {
            if (this.model.get('emailAddress')) {
              this.showActionItem('generateNewPassword');
              this.showActionItem('sendPasswordChangeLink');
            } else {
              this.hideActionItem('generateNewPassword');
              this.hideActionItem('sendPasswordChangeLink');
            }
          });
        }
      }
      if (this.model.isPortal() || this.model.isApi()) {
        this.hideActionItem('duplicate');
      }
      if (this.model.id === this.getUser().id) {
        this.listenTo(this.model, 'after:save', () => {
          this.getUser().set(this.model.getClonedAttributes());
        });
      }
      if (this.getUser().isAdmin() && this.model.isRegular() && !this.getConfig().get('authAnotherUserDisabled')) {
        this.addDropdownItem({
          label: 'Log in',
          name: 'login',
          action: 'login'
        });
      }
      this.setupFieldAppearance();
    }
    setupActionItems() {
      super.setupActionItems();
      if (this.model.isApi() && this.getUser().isAdmin()) {
        this.addDropdownItem({
          'label': 'Generate New API Key',
          'name': 'generateNewApiKey'
        });
      }
    }
    setupNonAdminFieldsAccess() {
      if (this.getUser().isAdmin()) {
        return;
      }
      let nonAdminReadOnlyFieldList = ['userName', 'isActive', 'teams', 'roles', 'password', 'portals', 'portalRoles', 'defaultTeam', 'contact', 'accounts', 'type', 'emailAddress'];
      nonAdminReadOnlyFieldList = nonAdminReadOnlyFieldList.filter(item => {
        if (!this.model.hasField(item)) {
          return true;
        }
        const aclDefs = /** @type {Object.<string, *>|null} */
        this.getMetadata().get(['entityAcl', 'User', 'fields', item]);
        if (!aclDefs) {
          return true;
        }
        if (aclDefs.nonAdminReadOnly) {
          return true;
        }
        return false;
      });
      nonAdminReadOnlyFieldList.forEach(field => {
        this.setFieldReadOnly(field, true);
      });
      if (!this.getAcl().checkScope('Team')) {
        this.setFieldReadOnly('defaultTeam', true);
      }
      this.hideField('layoutSet', true);
    }
    setupFieldAppearance() {
      this.controlFieldAppearance();
      this.listenTo(this.model, 'change', () => {
        this.controlFieldAppearance();
      });
    }
    controlFieldAppearance() {
      if (this.model.get('type') === 'portal') {
        this.hideField('roles');
        this.hideField('teams');
        this.hideField('defaultTeam');
        this.showField('portals');
        this.showField('portalRoles');
        this.showField('contact');
        this.showField('accounts');
        this.showPanel('portal');
        this.hideField('title');
      } else {
        this.showField('roles');
        this.showField('teams');
        this.showField('defaultTeam');
        this.hideField('portals');
        this.hideField('portalRoles');
        this.hideField('contact');
        this.hideField('accounts');
        this.hidePanel('portal');
        if (this.model.get('type') === 'api') {
          this.hideField('title');
          this.hideField('emailAddress');
          this.hideField('phoneNumber');
          this.hideField('name');
          this.hideField('gender');
          if (this.model.get('authMethod') === 'Hmac') {
            this.showField('secretKey');
          } else {
            this.hideField('secretKey');
          }
        } else {
          this.showField('title');
        }
      }
      if (this.model.id === this.getUser().id) {
        this.setFieldReadOnly('type');
      } else {
        if (this.model.get('type') === 'admin' || this.model.get('type') === 'regular') {
          this.setFieldNotReadOnly('type');
          this.setFieldOptionList('type', ['regular', 'admin']);
        } else {
          this.setFieldReadOnly('type');
        }
      }
      if (!this.getConfig().get('auth2FA') || !(this.model.isRegular() || this.model.isAdmin())) {
        this.hideField('auth2FA');
      }
    }

    // noinspection JSUnusedGlobalSymbols
    actionChangePassword() {
      Espo.Ui.notifyWait();
      this.createView('changePassword', 'views/modals/change-password', {
        userId: this.model.id
      }, view => {
        view.render();
        Espo.Ui.notify(false);
        this.listenToOnce(view, 'changed', () => {
          setTimeout(() => {
            this.getBaseController().logout();
          }, 2000);
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionPreferences() {
      this.getRouter().navigate('#Preferences/edit/' + this.model.id, {
        trigger: true
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionEmailAccounts() {
      this.getRouter().navigate('#EmailAccount/list/userId=' + this.model.id, {
        trigger: true
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionExternalAccounts() {
      this.getRouter().navigate('#ExternalAccount', {
        trigger: true
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionAccess() {
      Espo.Ui.notifyWait();
      Espo.Ajax.getRequest(`User/${this.model.id}/acl`).then(aclData => {
        this.createView('access', 'views/user/modals/access', {
          aclData: aclData,
          model: this.model
        }, view => {
          Espo.Ui.notify(false);
          view.render();
        });
      });
    }
    getGridLayout(callback) {
      const layoutName = this.options.layoutName || this.layoutName;
      this.getHelper().layoutManager.get(this.model.entityType, layoutName, simpleLayout => {
        const layout = Espo.Utils.cloneDeep(simpleLayout);
        const hasTab = layout.find(it => it.tabBreak) !== undefined;
        if (!this.getUser().isPortal()) {
          layout.push({
            label: "Teams and Access Control",
            name: "accessControl",
            tabBreak: hasTab,
            tabLabel: this.translate('Settings', 'scopeNames'),
            rows: [[{
              "name": "type"
            }, {
              "name": "isActive"
            }], [{
              "name": "teams"
            }, {
              "name": "defaultTeam"
            }], [{
              "name": "roles"
            }, false]]
          });
          if (this.model.isPortal()) {
            layout.push({
              "label": "Portal",
              "name": "portal",
              "rows": [[{
                "name": "portals"
              }, {
                "name": "accounts"
              }], [{
                "name": "portalRoles"
              }, {
                "name": "contact"
              }]]
            });
            if (this.getUser().isAdmin()) {
              layout.push({
                "label": "Misc",
                "name": "portalMisc",
                "rows": [[{
                  "name": "dashboardTemplate"
                }, false]]
              });
            }
          }
          if (this.model.isAdmin() || this.model.isRegular()) {
            layout.push({
              "label": "Misc",
              "name": "misc",
              "rows": [[{
                "name": "workingTimeCalendar"
              }, {
                "name": "layoutSet"
              }]]
            });
          }
        }
        if (this.getUser().isAdmin() && this.model.isApi()) {
          layout.push({
            "name": "auth",
            "rows": [[{
              "name": "authMethod"
            }, false], [{
              "name": "apiKey"
            }, {
              "name": "secretKey"
            }]]
          });
        }
        this.detailLayout = layout;
        const gridLayout = {
          type: 'record',
          layout: this.convertDetailLayout(layout)
        };
        callback(gridLayout);
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionGenerateNewApiKey() {
      this.confirm(this.translate('confirmation', 'messages'), () => {
        Espo.Ajax.postRequest('UserSecurity/apiKey/generate', {
          id: this.model.id
        }).then(data => {
          this.model.set(data);
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionViewSecurity() {
      this.createView('dialog', 'views/user/modals/security', {
        userModel: this.model
      }, view => {
        view.render();
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionSendPasswordChangeLink() {
      this.confirm({
        message: this.translate('sendPasswordChangeLinkConfirmation', 'messages', 'User'),
        confirmText: this.translate('Send', 'labels', 'Email')
      }).then(() => {
        Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
        Espo.Ajax.postRequest('UserSecurity/password/recovery', {
          id: this.model.id
        }).then(() => {
          Espo.Ui.success(this.translate('Done'));
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionGenerateNewPassword() {
      this.confirm(this.translate('generateAndSendNewPassword', 'messages', 'User')).then(() => {
        Espo.Ui.notifyWait();
        Espo.Ajax.postRequest('UserSecurity/password/generate', {
          id: this.model.id
        }).then(() => {
          Espo.Ui.success(this.translate('Done'));
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionLogin() {
      const anotherUser = this.model.get('userName');
      const username = this.getUser().get('userName');
      this.createView('dialog', 'views/user/modals/login-as', {
        model: this.model,
        anotherUser: anotherUser,
        username: username
      }).then(view => view.render());
    }
  }
  var _default = _exports.default = UserDetailRecordView;
});

define("views/user/record/detail-side", ["exports", "views/record/detail-side"], function (_exports, _detailSide) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detailSide = _interopRequireDefault(_detailSide);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserDetailSideRecordView extends _detailSide.default {
    setupPanels() {
      super.setupPanels();
      const userModel = /** @type {import('modules/user').default} */this.model;
      if (userModel.isApi() || userModel.isSystem()) {
        this.hidePanel('activities', true);
        this.hidePanel('history', true);
        this.hidePanel('tasks', true);
        this.hidePanel('stream', true);
        return;
      }
      const showActivities = this.getAcl().checkPermission('userCalendar', userModel);
      if (!showActivities && this.getAcl().getPermissionLevel('userCalendar') === 'team' && !this.model.has('teamsIds')) {
        this.listenToOnce(this.model, 'sync', () => {
          if (!this.getAcl().checkPermission('userCalendar', userModel)) {
            return;
          }
          this.onPanelsReady(() => {
            this.showPanel('activities', 'acl');
            this.showPanel('history', 'acl');
            if (!userModel.isPortal()) {
              this.showPanel('tasks', 'acl');
            }
          });
        });
      }
      if (!showActivities) {
        this.hidePanel('activities', false, 'acl');
        this.hidePanel('history', false, 'acl');
        this.hidePanel('tasks', false, 'acl');
      }
      if (userModel) {
        this.hidePanel('tasks', true);
      }
    }
  }
  _exports.default = UserDetailSideRecordView;
});

define("views/user/modals/select-position", ["exports", "views/modal", "model", "views/record/edit-for-modal", "views/fields/enum"], function (_exports, _modal, _model, _editForModal, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  _editForModal = _interopRequireDefault(_editForModal);
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserSelectPositionModalView extends _modal.default {
    templateContent = '<div class="record no-side-margin">{{{record}}}</div>';
    className = 'dialog dialog-record';
    shortcutKeys = {
      'Control+Enter': 'apply'
    };

    /**
     * @param {{
     *     positionList: string[],
     *     position: string|null,
     *     name: string,
     *     onApply: function(string|null),
     * }} options
     */
    constructor(options) {
      super(options);

      /** @private */
      this.props = options;
    }
    setup() {
      this.headerText = this.translate('changePosition', 'actions', 'User') + ' · ' + this.props.name;
      this.buttonList = [{
        name: 'save',
        label: 'Save',
        style: 'primary',
        onClick: () => this.apply()
      }, {
        name: 'cancel',
        label: 'Cancel'
      }];
      this.model = new _model.default();
      this.model.setMultiple({
        position: this.props.position
      });
      this.recordView = new _editForModal.default({
        model: this.model,
        detailLayout: [{
          rows: [[{
            view: new _enum.default({
              name: 'position',
              params: {
                options: ['', ...this.props.positionList]
              },
              labelText: this.translate('teamRole', 'fields', 'User')
            })
          }, false]]
        }]
      });
      this.assignView('record', this.recordView, '.record');
    }

    /**
     * @private
     */
    apply() {
      if (this.recordView.validate()) {
        return;
      }
      this.props.onApply(this.model.attributes.position);
      this.close();
    }
    onBackdropClick() {
      if (this.recordView.isChanged) {
        return;
      }
      this.close();
    }
  }
  _exports.default = UserSelectPositionModalView;
});

define("views/stream/reactions", ["exports", "view", "views/record/list", "helpers/misc/reactions"], function (_exports, _view, _list, _reactions) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _list = _interopRequireDefault(_list);
  _reactions = _interopRequireDefault(_reactions);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NoteReactionsView extends _view.default {
    // language=Handlebars
    templateContent = `
        {{#each dataList}}
            <a
                class="reaction-count small text-soft"
                role="button"
                title="{{label}}"
                data-type="{{type}}"
            >
                <span data-role="icon" class="{{iconClass}} {{#if reacted}} text-warning {{/if}}"></span>
                <span data-role="count">{{count}}</span>
            </a>
        {{/each}}
    `;

    /**
     * @private
     * @type {string[]}
     */
    availableReactions;

    /**
     * @type {Object.<string, string>}
     * @private
     */
    iconClassMap;

    /**
     * @private
     * @type {{destroy: function(), show: function()}}
     */
    popover;

    /**
     * @param {{
     *     model: import('model').default,
     * }} options
     */
    constructor(options) {
      super(options);
    }
    data() {
      /** @type {Record.<string, number>} */
      const counts = this.model.attributes.reactionCounts || {};
      /** @type {string[]} */
      const myReactions = this.model.attributes.myReactions || [];
      return {
        dataList: this.availableReactions.filter(type => counts[type]).map(type => {
          return {
            type: type,
            count: counts[type].toString(),
            label: this.translate('Reactions') + ' · ' + this.translate(type, 'reactions'),
            iconClass: this.iconClassMap[type],
            reacted: myReactions.includes(type)
          };
        })
      };
    }
    setup() {
      const reactionsHelper = new _reactions.default();
      this.availableReactions = reactionsHelper.getAvailableReactions();
      const list = reactionsHelper.getDefinitionList();
      this.iconClassMap = list.reduce((o, it) => {
        o[it.type] = it.iconClass;
        return o;
      }, {});
      this.addHandler('click', 'a.reaction-count', (e, target) => this.showUsers(target.dataset.type));
    }

    /**
     * @private
     * @param {string} type
     */
    async showUsers(type) {
      const a = this.element.querySelector(`a.reaction-count[data-type="${type}"]`);

      /*if (this.popover) {
          this.popover.destroy();
      }*/

      const popover = Espo.Ui.popover(a, {
        placement: 'bottom',
        content: `
                <div class="center-align for-list-view">
                    <span class="fas fa-spinner fa-spin text-soft"></span>
                </div>
            `,
        preventDestroyOnRender: true,
        noToggleInit: true,
        keepElementTitle: true,
        title: this.translate('Reactions') + ' · ' + this.translate(type, 'reactions'),
        onHide: () => {
          this.popover = undefined;
          this.trigger('popover-hidden');
        }
      }, this);
      this.popover = popover;
      const id = popover.show();
      document.querySelector(`#${id}`).classList.add('popover-list-view');
      const selector = `#${id} .popover-content`;

      /** @type {HTMLElement|null} */
      const container = document.querySelector(selector);

      /** @type {import('collection').default} */
      const users = await this.getCollectionFactory().create('User');
      users.url = `Note/${this.model.id}/reactors/${type}`;
      users.maxSize = this.getConfig().get('recordsPerPageSmall') || 5;
      await users.fetch();
      if (!document.body.contains(container)) {
        popover.hide();
        return;
      }
      const listView = new _list.default({
        collection: users,
        listLayout: [{
          name: 'name',
          view: 'views/user/fields/name',
          link: true
        }],
        checkboxes: false,
        displayTotalCount: false,
        headerDisabled: true,
        buttonsDisabled: true,
        rowActionsDisabled: true
      });
      await this.assignView('users', listView);
      listView.setSelector(selector);
      await listView.render();
      this.listenToOnce(listView, 'modal-shown', () => popover.destroy());
    }

    // @todo Prevent popover disappearing.
    reRenderWhenNoPopover() {
      if (this.popover) {
        this.once('popover-hidden', () => this.reRender());
        return;
      }
      this.reRender();
    }
  }
  _exports.default = NoteReactionsView;
});

define("views/stream/panel", ["exports", "views/record/panels/relationship", "underscore", "views/note/fields/post", "view-record-helper", "views/fields/text"], function (_exports, _relationship, _underscore, _post, _viewRecordHelper, _text) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _relationship = _interopRequireDefault(_relationship);
  _underscore = _interopRequireDefault(_underscore);
  _post = _interopRequireDefault(_post);
  _viewRecordHelper = _interopRequireDefault(_viewRecordHelper);
  _text = _interopRequireDefault(_text);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class PanelStreamView extends _relationship.default {
    template = 'stream/panel';
    rowActionsView = 'views/stream/record/row-actions/default';
    postingMode = false;
    postDisabled = false;
    relatedListFiltersDisabled = true;
    layoutName = null;
    filterList = ['all', 'posts', 'updates'];
    /** @type {import('collections/note').default} */
    collection;

    /** @private */
    _justPosted = false;

    /** @type {import('collections/note').default} */
    pinnedCollection;

    /**
     * @protected
     * @type {string}
     */
    placeholderText;

    /**
     * @private
     * @type {import('model').default}
     */
    seed;
    additionalEvents = {
      /** @this PanelStreamView */
      'focus textarea[data-name="post"]': function () {
        this.enablePostingMode(true);
      },
      /** @this PanelStreamView */
      'keydown textarea[data-name="post"]': function (e) {
        if (Espo.Utils.getKeyFromKeyEvent(e) === 'Control+Enter') {
          e.stopPropagation();
          e.preventDefault();
          this.post();
        }

        // Don't hide to be able to focus on the upload button.
        /*if (e.code === 'Tab') {
            let $text = $(e.currentTarget);
              if ($text.val() === '') {
                this.disablePostingMode();
            }
        }*/
      },
      /** @this PanelStreamView */
      'input textarea[data-name="post"]': function () {
        this.controlPreviewButton();
        this.controlPostButtonAvailability(this.postFieldView.getTextAreaElement().value);
      }
    };
    data() {
      const data = super.data();
      data.postDisabled = this.postDisabled;
      data.placeholderText = this.placeholderText;
      data.allowInternalNotes = this.allowInternalNotes;
      data.hasPinned = this.hasPinned;
      return data;
    }
    controlPreviewButton() {
      this.$previewButton = this.$previewButton || this.$el.find('.stream-post-preview');
      if (this.postFieldView.getTextAreaElement().value === '') {
        this.$previewButton.addClass('hidden');
      } else {
        this.$previewButton.removeClass('hidden');
      }
    }

    /**
     * @private
     * @param {boolean} [byFocus]
     */
    enablePostingMode(byFocus) {
      this.$el.find('.buttons-panel').removeClass('hide');
      if (!this.postingMode) {
        if (this.postFieldView.getTextAreaElement().value) {
          this.postFieldView.controlTextareaHeight();
        }
        let isClicked = false;
        $('body').on('click.stream-panel', e => {
          if (byFocus && !isClicked) {
            isClicked = true;
            return;
          }
          const $target = $(e.target);
          if ($target.parent().hasClass('remove-attachment')) {
            return;
          }
          if ($.contains(this.$postContainer.get(0), e.target)) {
            return;
          }
          if (this.postFieldView.getTextAreaElement().value !== '') {
            return;
          }
          if ($(e.target).closest('.popover-content').get(0)) {
            return;
          }
          const attachmentsIds = this.seed.get('attachmentsIds') || [];
          if (!attachmentsIds.length && (!this.getAttachmentsFieldView() || !this.getAttachmentsFieldView().isUploading)) {
            this.disablePostingMode();
          }
        });
      }
      this.postingMode = true;
      this.controlPreviewButton();
    }
    disablePostingMode() {
      this.postingMode = false;
      this.setPostText(null);
      if (this.getAttachmentsFieldView()) {
        this.getAttachmentsFieldView().empty();
      }
      this.$el.find('.buttons-panel').addClass('hide');
      $('body').off('click.stream-panel');
      this.postFieldView.getTextAreaElement().rows = 1;
    }
    setup() {
      this.events = {
        ...this.additionalEvents,
        ...this.events
      };
      this.addHandler('click', 'button.post', () => this.post());
      this.addActionHandler('switchInternalMode', () => this.switchInternalMode());
      this.addActionHandler('preview', () => this.preview());
      this.entityType = this.model.entityType;
      this.filter = this.getStoredFilter();
      this.setupTitle();
      this.placeholderText = this.placeholderText || this.translate('writeYourCommentHere', 'messages');
      this.allowInternalNotes = false;
      if (!this.getUser().isPortal()) {
        this.allowInternalNotes = this.getMetadata().get(['clientDefs', this.entityType, 'allowInternalNotes']);
      }
      this.hasPinned = this.model.entityType !== 'User';
      this.isInternalNoteMode = false;
      this.storageTextKey = `stream-post-${this.model.entityType}-${this.model.id}`;
      this.storageAttachmentsKey = `stream-post-attachments-${this.model.entityType}-${this.model.id}`;
      this.storageIsInernalKey = `stream-post-is-internal-${this.model.entityType}-${this.model.id}`;
      this.on('remove', () => {
        this.storeControl();
        $(window).off('beforeunload.stream-' + this.cid);
      });
      $(window).off(`beforeunload.stream-${this.cid}`);
      $(window).on(`beforeunload.stream-${this.cid}`, () => {
        this.storeControl();
      });

      /** @private */
      this.formRecordHelper = new _viewRecordHelper.default();
      const storedAttachments = this.getSessionStorage().get(this.storageAttachmentsKey);
      this.setupActions();
      const promise = this.getModelFactory().create('Note', model => {
        this.seed = model;
        if (storedAttachments) {
          this.hasStoredAttachments = true;
          this.seed.set({
            attachmentsIds: storedAttachments.idList,
            attachmentsNames: storedAttachments.names,
            attachmentsTypes: storedAttachments.types
          });
        }
        if (this.allowInternalNotes) {
          if (this.getMetadata().get(['entityDefs', 'Note', 'fields', 'isInternal', 'default'])) {
            this.isInternalNoteMode = true;
          }
          if (this.getSessionStorage().has(this.storageIsInernalKey)) {
            this.isInternalNoteMode = this.getSessionStorage().get(this.storageIsInernalKey);
          }
        }
        if (this.isInternalNoteMode) {
          this.seed.set('isInternal', true);
        }
        this.postFieldView = new _post.default({
          name: 'post',
          mode: 'edit',
          params: {
            required: true,
            rowsMin: 1,
            preview: false,
            attachmentField: 'attachments'
          },
          model: this.seed,
          placeholderText: this.placeholderText,
          noResize: true,
          recordHelper: this.formRecordHelper
        });
        this.assignView('postField', this.postFieldView, '.textarea-container').then(view => {
          this.initPostEvents(view);
        });
        this.wait(this.createCollection().then(() => this.setupPinned()));
        this.listenTo(this.seed, 'change:attachmentsIds', () => {
          this.controlPostButtonAvailability();
        });
      });
      this.wait(promise);
      if (!this.defs.hidden) {
        this.subscribeToWebSocket();
      }
      this.once('show', () => {
        if (!this.isSubscribedToWebSocket) {
          this.subscribeToWebSocket();
        }
      });
      this.on('remove', () => {
        if (this.isSubscribedToWebSocket) {
          this.unsubscribeFromWebSocket();
        }
      });
    }
    switchInternalMode() {
      this.isInternalNoteMode = !this.isInternalNoteMode;
      if (this.internalModeButtonElement) {
        this.isInternalNoteMode ? this.internalModeButtonElement.classList.add('enabled') : this.internalModeButtonElement.classList.remove('enabled');
      }
    }
    subscribeToWebSocket() {
      if (!this.getHelper().webSocketManager) {
        return;
      }
      if (this.model.entityType === 'User') {
        return;
      }
      const topic = `streamUpdate.${this.model.entityType}.${this.model.id}`;
      this.streamUpdateWebSocketTopic = topic;
      this.isSubscribedToWebSocket = true;
      this.getHelper().webSocketManager.subscribe(topic, (t, /** Record */data) => {
        if (data.createdById === this.getUser().id && this._justPosted) {
          return;
        }
        if (data.noteId) {
          const model = this.collection.get(data.noteId);
          if (model) {
            model.fetch().then(() => this.syncPinnedModel(model, true));
          }
          if (!data.pin) {
            return;
          }
        }
        this.collection.fetchNew();
      });
    }
    unsubscribeFromWebSocket() {
      this.getHelper().webSocketManager.unsubscribe(this.streamUpdateWebSocketTopic);
    }
    setupTitle() {
      this.title = this.translate('Stream');
      this.titleHtml = this.title;
      if (this.filter && this.filter !== 'all') {
        this.titleHtml += ' &middot; ' + this.translate(this.filter, 'filters', 'Note');
      }
    }

    /**
     * @private
     */
    storeControl() {
      let isNotEmpty = false;
      if (this.isRendered()) {
        const text = /** @type {string} */this.seed.attributes.post;
        if (text && text.length) {
          this.getSessionStorage().set(this.storageTextKey, text);
          isNotEmpty = true;
        } else if (this.hasStoredText) {
          this.getSessionStorage().clear(this.storageTextKey);
        }
      }
      const attachmentIdList = this.seed.get('attachmentsIds') || [];
      if (attachmentIdList.length) {
        this.getSessionStorage().set(this.storageAttachmentsKey, {
          idList: attachmentIdList,
          names: this.seed.get('attachmentsNames') || {},
          types: this.seed.get('attachmentsTypes') || {}
        });
        isNotEmpty = true;
      } else {
        if (this.hasStoredAttachments) {
          this.getSessionStorage().clear(this.storageAttachmentsKey);
        }
      }
      if (isNotEmpty) {
        this.getSessionStorage().set(this.storageIsInernalKey, this.isInternalNoteMode);
      } else {
        this.getSessionStorage().clear(this.storageIsInernalKey);
      }
    }

    /**
     * @private
     * @return {Promise}
     */
    createCollection() {
      return this.getCollectionFactory().create('Note', collection => {
        this.collection = collection;
        collection.url = `${this.model.entityType}/${this.model.id}/stream`;
        collection.maxSize = this.getConfig().get('recordsPerPageSmall') || 5;
        this.setFilter(this.filter);
      });
    }

    /** @private */
    initPostEvents(view) {
      this.listenTo(this.formRecordHelper, 'upload-files:attachments', () => {
        if (!this.postingMode) {
          this.enablePostingMode();
        }
      });
      this.listenTo(view, 'add-files', files => {
        this.getAttachmentsFieldView().uploadFiles(files);
        if (!this.postingMode) {
          this.enablePostingMode();
        }
      });
    }

    /**
     * @private
     * @param {string|null} text
     */
    setPostText(text) {
      this.seed.set('post', text, {
        silent: true
      });
      this.postFieldView.getTextAreaElement().value = text || '';
    }

    /**
     * @private
     */
    onSync() {
      if (this.hasPinned) {
        this.pinnedCollection.add(this.collection.pinnedList);
        this.createView('pinnedList', 'views/stream/record/list', {
          selector: '> .list-container[data-role="pinned"]',
          collection: this.pinnedCollection,
          model: this.model,
          noDataDisabled: true
        }, view => {
          view.render();
          this.listenTo(this.pinnedCollection, 'change', (/** import('model').default */model, /** Record */o) => {
            if (o.userReaction) {
              this.syncPinnedModel(model, false);
            }
          });
          this.listenTo(view, 'after:save', /** import('model').default */model => {
            this.syncPinnedModel(model, false);
          });
          this.listenTo(view, 'after:delete', /** import('model').default */model => {
            this.collection.remove(model.id);
            this.collection.trigger('update-sync');
          });
          this.listenTo(view, 'quote-reply', /** string */quoted => this.quoteReply(quoted));
        });
      }
      this.createView('list', 'views/stream/record/list', {
        selector: '> .list-container[data-role="stream"]',
        collection: this.collection,
        model: this.model
      }, view => {
        view.render();
        if (this.pinnedCollection) {
          this.listenTo(view, 'after:delete', /** import('model').default */model => {
            this.pinnedCollection.remove(model.id);
            this.pinnedCollection.trigger('update-sync');
          });
          this.listenTo(view, 'after:save', /** import('model').default */model => {
            this.syncPinnedModel(model, true);
          });
          this.listenTo(this.collection, 'change', (/** import('model').default */model, /** Record */o) => {
            if (o.userReaction) {
              this.syncPinnedModel(model, true);
            }
          });
          this.listenTo(view, 'quote-reply', /** string */quoted => this.quoteReply(quoted));
        }
      });
      this.stopListening(this.model, 'all');
      this.stopListening(this.model, 'destroy');
      setTimeout(() => {
        this.listenTo(this.model, 'all', event => {
          if (!['sync', 'after:relate'].includes(event)) {
            return;
          }
          this.collection.fetchNew();
        });
        this.listenTo(this.model, 'destroy', () => {
          this.stopListening(this.model, 'all');
        });
      }, 500);
    }
    afterRender() {
      this.$attachments = this.$el.find('div.attachments');
      this.$postContainer = this.$el.find('.post-container');
      this.$postButton = this.$el.find('button.post');
      const storedText = this.getSessionStorage().get(this.storageTextKey);
      if (storedText && storedText.length) {
        this.hasStoredText = true;
        this.setPostText(storedText);
      }
      this.controlPostButtonAvailability(storedText);
      if (this.allowInternalNotes) {
        this.internalModeButtonElement = /** @type {HTMLAnchorElement} */
        this.element.querySelector('.action[data-action="switchInternalMode"]');
      }
      if (this.isInternalNoteMode) {
        this.internalModeButtonElement.classList.add('enabled');
      }
      if (!this.defs.hidden) {
        this.collection.fetch().then(() => this.onSync());
      } else {
        this.once('show', () => {
          this.collection.fetch().then(() => this.onSync());
        });
      }
      const $a = this.$el.find('.buttons-panel a.stream-post-info');
      const text1 = this.translate('infoMention', 'messages', 'Stream');
      const text2 = this.translate('infoSyntax', 'messages', 'Stream');
      const syntaxItemList = [['code', '`{text}`'], ['multilineCode', '```{text}```'], ['strongText', '**{text}**'], ['emphasizedText', '*{text}*'], ['deletedText', '~~{text}~~'], ['blockquote', '> {text}'], ['link', '[{text}](url)']];
      const messageItemList = [];
      syntaxItemList.forEach(item => {
        const text = this.translate(item[0], 'syntaxItems', 'Stream');
        const result = item[1].replace('{text}', text);
        messageItemList.push(result);
      });
      const $ul = $('<ul>').append(messageItemList.map(text => $('<li>').text(text)));
      const messageHtml = this.getHelper().transformMarkdownInlineText(text1) + '<br><br>' + this.getHelper().transformMarkdownInlineText(text2) + ':<br>' + $ul.get(0).outerHTML;
      Espo.Ui.popover($a, {
        content: messageHtml
      }, this);
      this.createView('attachments', 'views/stream/fields/attachment-multiple', {
        model: this.seed,
        mode: 'edit',
        selector: 'div.attachments-container',
        name: 'attachments',
        recordHelper: this.formRecordHelper
      }, view => {
        view.render();
      });
    }

    /**
     * @private
     * @param {import('model').default} model
     * @param {boolean} toPinned
     */
    syncPinnedModel(model, toPinned) {
      if (toPinned && !this.pinnedCollection) {
        return;
      }
      const cModel = toPinned ? this.pinnedCollection.get(model.id) : this.collection.get(model.id);
      if (!cModel) {
        return;
      }
      cModel.setMultiple({
        post: model.attributes.post,
        attachmentsIds: model.attributes.attachmentsIds,
        attachmentsNames: model.attributes.attachmentsNames,
        attachmentsTypes: model.attributes.attachmentsTypes,
        data: model.attributes.data,
        reactionCounts: model.attributes.reactionCounts,
        myReactions: model.attributes.myReactions
      });
    }

    /**
     * @private
     */
    afterPost() {
      this.postFieldView.getTextAreaElement().rows = 1;
    }

    /**
     * @private
     * @return {import('views/fields/attachment-multiple').default}
     */
    getAttachmentsFieldView() {
      return this.getView('attachments');
    }

    /**
     * @private
     */
    async post() {
      this.postFieldView.fetchToModel();

      /** @type {string} */
      const message = this.seed.attributes.post || '';
      const textAreaElement = this.postFieldView.getTextAreaElement();
      this.disablePostButton();
      textAreaElement.disabled = true;
      const model = await this.getModelFactory().create('Note');
      if (this.getAttachmentsFieldView().validateReady()) {
        textAreaElement.disabled = false;
        this.enablePostButton();
        return;
      }

      /** @type {string[]} */
      const attachmentIds = this.seed.get('attachmentsIds') || [];
      if (message.trim() === '' && attachmentIds.length === 0) {
        Espo.Ui.error(this.translate('Post cannot be empty'));
        textAreaElement.disabled = false;
        this.controlPostButtonAvailability();
        textAreaElement.focus();
        return;
      }
      model.set('post', message || null);
      model.set('attachmentsIds', [...attachmentIds]);
      model.set('type', 'Post');
      model.set('isInternal', this.isInternalNoteMode);
      this.prepareNoteForPost(model);
      this._justPosted = true;
      setTimeout(() => this._justPosted = false, 1000);
      Espo.Ui.notifyWait();
      try {
        await model.save(null);
      } catch (e) {
        this.postFieldView.getTextAreaElement().disabled = false;
        this.controlPostButtonAvailability();
        return;
      }
      Espo.Ui.success(this.translate('Posted'));
      this.collection.fetchNew().then(() => this.afterPostAndFetch(model));
      textAreaElement.disabled = false;
      this.disablePostingMode();
      this.afterPost();
      if (this.getPreferences().get('followEntityOnStreamPost')) {
        this.model.set('isFollowed', true);
      }
      this.getSessionStorage().clear(this.storageTextKey);
      this.getSessionStorage().clear(this.storageAttachmentsKey);
      this.getSessionStorage().clear(this.storageIsInernalKey);
    }

    /**
     * @private
     * @param {import('model').default} model
     */
    afterPostAndFetch(model) {
      const rowView = this.getListView().getView(model.id);
      if (!rowView) {
        return;
      }
      const postView = rowView.getView('post');
      if (!(postView instanceof _text.default)) {
        console.error('Post field not obtained.');
        return;
      }
      postView.seeMore();
    }

    /**
     * @protected
     * @param {import('model').default} model
     */
    prepareNoteForPost(model) {
      model.set('parentId', this.model.id);
      model.set('parentType', this.model.entityType);
    }
    getButtonList() {
      return [];
    }
    setupActions() {
      this.actionList = [];
      this.actionList.push({
        action: 'viewPostList',
        text: this.translate('View Posts', 'labels', 'Note'),
        onClick: () => this.actionViewPostList()
      });
      if (this.model.entityType !== 'User') {
        this.actionList.push({
          action: 'viewAttachmentList',
          text: this.translate('View Attachments', 'labels', 'Note'),
          onClick: () => this.actionViewAttachmentList()
        });
      }
      if (this.model.entityType === 'User') {
        this.actionList.push({
          action: 'viewUserActivity',
          text: this.translate('View Activity', 'labels', 'Note'),
          onClick: () => this.actionViewUserActivity()
        });
      }
      this.actionList.push(false);
      this.filterList.forEach(item => {
        let selected;
        selected = item === 'all' ? !this.filter : item === this.filter;
        this.actionList.push({
          action: 'selectFilter',
          html: $('<span>').append($('<span>').addClass('check-icon fas fa-check pull-right').addClass(!selected ? ' hidden' : ''), $('<div>').text(this.translate(item, 'filters', 'Note'))).get(0).innerHTML,
          data: {
            name: item
          }
        });
      });
    }
    actionViewPostList() {
      const url = this.model.entityType + '/' + this.model.id + '/posts';
      const data = {
        entityType: 'Note',
        viewOptions: {
          url: url,
          title: `${this.translate('Stream')} @right ${this.translate('posts', 'filters', 'Note')}`,
          forceSelectAllAttributes: true,
          forcePagination: true,
          rowActionsView: 'views/stream/record/row-actions/detached'
        }
      };
      this.actionViewRelatedList(data);
    }

    /**
     * @private
     */
    actionViewAttachmentList() {
      const url = `${this.model.entityType}/${this.model.id}/streamAttachments`;
      const title = `${this.translate('Stream')} @right ${this.translate('Attachment', 'scopeNamesPlural')}`;
      const options = {
        model: this.model,
        link: 'streamAttachments',
        entityType: 'Attachment',
        title: title,
        layoutName: 'listForStream',
        defaultOrder: 'desc',
        defaultOrderBy: 'createdAt',
        url: url,
        listViewName: this.listViewName,
        createDisabled: true,
        selectDisabled: true,
        unlinkDisabled: true,
        removeDisabled: true,
        rowActionsView: null,
        filtersDisabled: true,
        searchPanelDisabled: true,
        massActionRemoveDisabled: true
      };
      Espo.Ui.notifyWait();
      this.createView('modal', 'views/modals/related-list', options, view => {
        Espo.Ui.notify();
        view.render();
        this.listenTo(view, 'action', (event, element) => {
          Espo.Utils.handleAction(this, event, element);
        });
      });
    }
    actionViewUserActivity() {
      const url = `User/${this.model.id}/stream/own`;
      const data = {
        entityType: 'Note',
        viewOptions: {
          url: url,
          title: this.translate('Stream') + ' @right ' + this.translate('activity', 'filters', 'Note'),
          forceSelectAllAttributes: true,
          filtersLayoutName: 'filtersGlobal',
          forcePagination: true,
          rowActionsView: 'views/stream/record/row-actions/detached'
        }
      };
      this.actionViewRelatedList(data);
    }
    getStoredFilter() {
      return this.getStorage().get('state', 'streamPanelFilter' + this.entityType) || null;
    }
    storeFilter(filter) {
      if (filter) {
        this.getStorage().set('state', 'streamPanelFilter' + this.entityType, filter);
      } else {
        this.getStorage().clear('state', 'streamPanelFilter' + this.entityType);
      }
    }
    setFilter(filter) {
      this.filter = filter;
      this.collection.data.filter = null;
      if (filter) {
        this.collection.data.filter = filter;
      }
    }

    /**
     * @return {import('views/stream/record/list').default}
     */
    getListView() {
      return this.getView('list');
    }
    async actionRefresh() {
      if (!this.getListView()) {
        return;
      }
      Espo.Ui.notifyWait();
      await this.getListView().showNewRecords();
      Espo.Ui.notify();
    }
    preview() {
      this.postFieldView.preview();
    }

    /**
     * @private
     * @param {string} [postEntered]
     */
    controlPostButtonAvailability(postEntered) {
      const attachmentsIdList = this.seed.get('attachmentsIds') || [];
      let post = this.seed.get('post');
      if (typeof postEntered !== 'undefined') {
        post = postEntered;
      }
      const isEmpty = !post && !attachmentsIdList.length;
      if (isEmpty) {
        if (this.$postButton.hasClass('disabled')) {
          return;
        }
        this.disablePostButton();
        return;
      }
      if (!this.$postButton.hasClass('disabled')) {
        return;
      }
      this.enablePostButton();
    }

    /**
     * @private
     */
    disablePostButton() {
      this.$postButton.addClass('disabled').attr('disabled', 'disabled');
    }

    /**
     * @private
     */
    enablePostButton() {
      this.$postButton.removeClass('disabled').removeAttr('disabled');
    }

    /**
     * @private
     */
    setupPinned() {
      if (!this.hasPinned) {
        return;
      }
      const promise = this.getCollectionFactory().create('Note').then(/** import('collections/note').default */collection => {
        this.pinnedCollection = collection;
        this.listenTo(this.collection, 'sync', () => {
          if (!this.collection.pinnedList) {
            return;
          }
          if (_underscore.default.isEqual(this.collection.pinnedList, this.pinnedCollection.models.map(m => m.attributes))) {
            return;
          }
          this.pinnedCollection.reset();
          this.pinnedCollection.add(this.collection.pinnedList);
          this.pinnedCollection.trigger('sync', this.pinnedCollection, {}, {});
        });
        this.listenTo(this.pinnedCollection, 'pin unpin', () => {
          this.collection.fetchNew();
        });
        this.listenTo(this.pinnedCollection, 'pin', id => {
          const model = this.collection.get(id);
          if (!model) {
            return;
          }
          model.set('isPinned', true);
        });
        this.listenTo(this.pinnedCollection, 'unpin', id => {
          const model = this.collection.get(id);
          if (!model) {
            return;
          }
          model.set('isPinned', false);
        });
      });
      this.wait(promise);
    }

    /**
     * @private
     * @param {string} quoted
     */
    quoteReply(quoted) {
      const quote = '> ' + quoted.split(/\r?\n|\r|\n/g).join('\n> ');
      let post = this.seed.attributes.post || '';
      if (post !== '') {
        post += '\n';
      }
      post += quote + '\n\n';
      this.setPostText(post);
      this.controlPreviewButton();
      this.controlPostButtonAvailability();
      this.postFieldView.controlTextareaHeight();
      this.enablePostingMode();
      setTimeout(() => {
        const textArea = this.postFieldView.getTextAreaElement();
        textArea.focus();
        textArea.setSelectionRange(textArea.value.length, textArea.value.length);
      }, 1);
    }
  }
  var _default = _exports.default = PanelStreamView;
});

define("views/stream/record/row-actions/default", ["exports", "views/record/row-actions/default", "helpers/misc/reactions", "views/stream/record/row-actions/reactions/reactions"], function (_exports, _default2, _reactions, _reactions2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _default2 = _interopRequireDefault(_default2);
  _reactions = _interopRequireDefault(_reactions);
  _reactions2 = _interopRequireDefault(_reactions2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class StreamDefaultNoteRowActionsView extends _default2.default {
    pinnedMaxCount;
    isDetached = false;

    /**
     * @private
     * @type {string[]}
     */
    availableReactions;

    /**
     * @private
     * @type {ReactionsHelper}
     */
    reactionHelper;
    setup() {
      super.setup();

      /** @type import('model').default */
      this.parentModel = this.options.parentModel;
      if (this.options.isThis && this.parentModel) {
        this.listenTo(this.model, 'change:isPinned', () => this.reRender());
        this.listenToOnce(this.parentModel, 'acl-edit-ready', () => this.reRender());
        this.pinnedMaxCount = this.getConfig().get('notePinnedMaxCount');
      }

      // @todo Use service.
      this.reactionHelper = new _reactions.default();
      this.availableReactions = this.reactionHelper.getAvailableReactions();
    }
    getActionList() {
      const list = [];
      if (this.options.acl.edit && this.options.isEditable) {
        list.push({
          action: 'quickEdit',
          label: 'Edit',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        });
      }
      if (this.options.acl.edit && this.options.isRemovable) {
        list.push({
          action: 'quickRemove',
          label: 'Remove',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        });
      }
      if (this.options.isThis && ['Post', 'EmailReceived', 'EmailSent'].includes(this.model.attributes.type) && this.parentModel && this.getAcl().checkModel(this.parentModel, 'edit') && !this.isDetached) {
        if (this.model.attributes.isPinned) {
          list.push({
            action: 'unpin',
            label: 'Unpin',
            data: {
              id: this.model.id
            },
            groupIndex: 2
          });
        } else if (this.pinnedMaxCount > 0) {
          list.push({
            action: 'pin',
            label: 'Pin',
            data: {
              id: this.model.id
            },
            groupIndex: 2
          });
        }
      }
      if (this.options.isThis && this.model.attributes.type === 'Post' && this.model.attributes.post && !this.isDetached) {
        list.push({
          action: 'quoteReply',
          label: 'Quote Reply',
          data: {
            id: this.model.id
          },
          groupIndex: 1
        });
      }
      if (this.hasReactions()) {
        this.getReactionItems().forEach(item => list.push(item));
      }
      return list;
    }

    /**
     * @private
     * @return {boolean}
     */
    hasReactions() {
      return this.model.attributes.type === 'Post' && this.availableReactions.length && !this.options.isNotification;
    }
    async prepareRender() {
      if (!this.hasReactions() || this.availableReactions.length === 1) {
        return;
      }
      const reactionsView = new _reactions2.default({
        reactions: this.availableReactions.map(type => {
          return {
            type: type,
            iconClass: this.reactionHelper.getIconClass(type),
            label: this.translate(type, 'reactions'),
            isReacted: this.isUserReacted(type)
          };
        })
      });
      await this.assignView('reactions', reactionsView, '[data-view-key="reactions"]');
    }

    /**
     * @private
     * @param {string} type
     * @return {boolean}
     */
    isUserReacted(type) {
      /** @type {string[]} */
      const myReactions = this.model.attributes.myReactions || [];
      return myReactions.includes(type);
    }

    /**
     * @private
     * @return {module:views/record/row-actions/actions~item[]}
     */
    getReactionItems() {
      const list = [];
      if (this.availableReactions.length > 1) {
        return [{
          viewKey: 'reactions',
          groupIndex: 11
        }];
      }
      this.availableReactions.forEach(type => {
        const iconClass = this.reactionHelper.getIconClass(type);
        const label = this.getHelper().escapeString(this.translate(type, 'reactions'));
        let html = iconClass ? `<span class="${iconClass} text-soft item-icon"></span><span class="item-text">${label}</span>` : label;
        const reacted = this.isUserReacted(type);
        if (reacted) {
          html = `<span class="check-icon fas fa-check pull-right"></span>` + `<div>${html}</div>`;
        }
        list.push({
          action: reacted ? 'unReact' : 'react',
          html: html,
          data: {
            id: this.model.id,
            type: type
          },
          groupIndex: 3
        });
      });
      return list;
    }
  }
  var _default = _exports.default = StreamDefaultNoteRowActionsView;
});

define("views/stream/notes/relate", ["exports", "views/stream/note"], function (_exports, _note) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _note = _interopRequireDefault(_note);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RelateNoteStreamView extends _note.default {
    template = 'stream/notes/create-related';
    messageName = 'relate';
    data() {
      return {
        ...super.data(),
        relatedTypeString: this.translateEntityType(this.entityType),
        iconHtml: this.getIconHtml(this.entityType, this.entityId)
      };
    }
    init() {
      if (this.getUser().isAdmin()) {
        this.isRemovable = true;
      }
      super.init();
    }
    setup() {
      const data = this.model.get('data') || {};
      this.entityType = this.model.get('relatedType') || data.entityType || null;
      this.entityId = this.model.get('relatedId') || data.entityId || null;
      this.entityName = this.model.get('relatedName') || data.entityName || null;
      this.messageData['relatedEntityType'] = this.translateEntityType(this.entityType);
      this.messageData['relatedEntity'] = $('<a>').attr('href', `#${this.entityType}/view/${this.entityId}`).text(this.entityName).attr('data-scope', this.entityType).attr('data-id', this.entityId);
      this.createMessage();
    }
  }
  var _default = _exports.default = RelateNoteStreamView;
});

define("views/stream/notes/email-received", ["exports", "views/stream/note", "views/email/fields/body", "views/fields/attachment-multiple"], function (_exports, _note, _body, _attachmentMultiple) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _note = _interopRequireDefault(_note);
  _body = _interopRequireDefault(_body);
  _attachmentMultiple = _interopRequireDefault(_attachmentMultiple);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailReceivedNoteStreamView extends _note.default {
    template = 'stream/notes/email-received';

    /**
     * @protected
     * @type {boolean}
     */
    isRemovable = false;

    /**
     * @protected
     * @type {boolean}
     */
    isSystemAvatar = true;

    /**
     * @private
     * @type {boolean}
     */
    detailsIsShown = false;

    /**
     * @private
     * @type {import('views/fields/base').default}
     */
    bodyFieldView;

    /**
     * @private
     * @type {import('views/fields/attachment-multiple').default}
     */
    attachmentsFieldView;

    /**
     * @private
     * @type {import('model').default}
     */
    formModel;

    /**
     * @private
     * @type {string}
     */
    emailId;

    /**
     * @private
     * @type {boolean}
     */
    emailNotLoaded = false;
    data() {
      return {
        ...super.data(),
        emailId: this.emailId,
        emailName: this.emailName,
        hasPost: this.hasPost && (!this.detailsIsShown || !this.bodyFieldView),
        mutedPost: this.hasPost && this.detailsIsShown && !this.bodyFieldView && !this.emailNotLoaded,
        hasAttachments: this.hasAttachments,
        emailIconClassName: this.getMetadata().get(['clientDefs', 'Email', 'iconClass']) || '',
        isPinned: this.isThis && this.model.get('isPinned') && this.model.collection && !this.model.collection.pinnedList,
        detailsIsShown: this.detailsIsShown,
        hasExpand: !this.options.isNotification
      };
    }
    setup() {
      this.addActionHandler('expandDetails', () => this.toggleDetails());
      const data =
      /**
       * @type {{
       *      emailId: string,
       *      emailName: string,
       *      personEntityType?: string,
       *      personEntityId?: string,
       *      personEntityName?: string,
       *      isInitial?: boolean,
       * }} */
      this.model.get('data') || {};
      this.emailId = data.emailId;
      this.emailName = data.emailName;
      if (this.parentModel && this.model.attributes.parentType === this.parentModel.entityType && this.model.attributes.parentId === this.parentModel.id) {
        if (this.model.attributes.post) {
          this.createField('post', null, null, 'views/stream/fields/post');
          this.hasPost = true;
        }
        if ((this.model.attributes.attachmentsIds || []).length) {
          this.createField('attachments', 'attachmentMultiple', {}, 'views/stream/fields/attachment-multiple', {
            previewSize: this.options.isNotification || this.options.isUserStream ? 'small' : 'medium'
          });
          this.hasAttachments = true;
        }
      }
      this.messageData['email'] = $('<a>').attr('href', `#Email/view/${data.emailId}`).text(data.emailName).attr('data-scope', 'Email').attr('data-id', data.emailId);
      this.setupEmailMessage(data);
      if (this.isThis) {
        this.messageName += 'This';
      }
      this.createMessage();
    }

    /**
     * @return {import('views/fields/text').default}
     */
    getPostView() {
      return this.getView('post');
    }

    /**
     * @protected
     * @param {Record} data
     */
    setupEmailMessage(data) {
      this.messageName = 'emailReceived';
      if (data.isInitial) {
        this.messageName += 'Initial';
      }
      if (data.personEntityId) {
        this.messageName += 'From';
        this.messageData['from'] = $('<a>').attr('href', `#${data.personEntityType}/view/${data.personEntityId}`).text(data.personEntityName).attr('data-scope', data.personEntityType).attr('data-id', data.personEntityId);
      }
      if (this.model.attributes.parentType === data.personEntityType && this.model.attributes.parentId === data.personEntityId) {
        this.isThis = true;
      }
    }

    /**
     * @private
     */
    async toggleDetails() {
      this.detailsIsShown = !this.detailsIsShown;
      if (!this.detailsIsShown && this.formModel) {
        this.formModel.abortLastFetch();
        Espo.Ui.notify();
      }
      const postView = this.getPostView();
      await this.reRender();
      if (!this.detailsIsShown || !this.emailId) {
        return;
      }
      if (postView) {
        postView.seeMoreText = false;
      }
      if (this.bodyFieldView) {
        this.bodyFieldView.toShowQuotePart = false;
        await this.bodyFieldView.reRender();
        return;
      }
      this.formModel = await this.getModelFactory().create('Email');
      this.formModel.id = this.emailId;
      Espo.Ui.notifyWait();
      try {
        await this.formModel.fetch();
      } catch (e) {
        this.emailNotLoaded = true;
        await this.reRender();
        return;
      }
      this.bodyFieldView = new _body.default({
        name: 'body',
        model: this.formModel,
        mode: 'detail',
        readOnly: true
      });
      await this.assignView('bodyField', this.bodyFieldView, '[data-name="body"]');
      if (!this.hasAttachments && this.formModel.attributes.attachmentsIds && this.formModel.attributes.attachmentsIds.length) {
        this.attachmentsFieldView = new _attachmentMultiple.default({
          name: 'attachments',
          model: this.formModel,
          mode: 'detail',
          readOnly: true
        });
        await this.assignView('attachmentsField', this.attachmentsFieldView, '[data-name="attachments"]');
      }
      Espo.Ui.notify();
      const minHeight = postView && postView.element ? postView.element.offsetHeight : null;
      await this.reRender();
      if (minHeight) {
        const bodyContainer = this.bodyFieldView.element;
        if (bodyContainer) {
          bodyContainer.style.minHeight = minHeight + 'px';
          setTimeout(() => bodyContainer.style.minHeight = '', 200);
        }
      }
    }
  }
  var _default = _exports.default = EmailReceivedNoteStreamView;
});

define("views/stream/modals/view-audit-log", ["exports", "views/modal", "views/stream/record/list", "jquery"], function (_exports, _modal, _list, _jquery) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _list = _interopRequireDefault(_list);
  _jquery = _interopRequireDefault(_jquery);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class StreamViewAuditLogModalView extends _modal.default {
    templateContent = '<div class="record list-container">{{{record}}}</div>';
    backdrop = true;

    /**
     * @param {{model: import('model').default}} options
     */
    constructor(options) {
      super(options);
    }
    setup() {
      const name = this.model.get('name') || this.model.id;
      this.$header = (0, _jquery.default)('<span>').append((0, _jquery.default)('<span>').text(name), ' <span class="chevron-right"></span> ', (0, _jquery.default)('<span>').text(this.translate('Audit Log')));
      this.buttonList = [{
        name: 'close',
        label: 'Close',
        onClick: dialog => {
          dialog.close();
        }
      }];
      this.wait(this.getCollectionFactory().create('Note').then(collection => {
        collection.url = `${this.model.entityType}/${this.model.id}/updateStream`;
        collection.maxSize = this.getConfig().get('recordsPerPage');
        const listView = new _list.default({
          collection: collection,
          model: this.model,
          // Prevents 'No Data' being displayed.
          skipBuildRows: true,
          type: 'listAuditLog'
        });
        Espo.Ui.notifyWait();
        return this.assignView('record', listView, '.record').then(() => {
          collection.fetch().then(() => Espo.Ui.notify(false));
        });
      }));
    }
  }
  var _default = _exports.default = StreamViewAuditLogModalView;
});

define("views/site/navbar", ["exports", "view", "jquery", "helpers/site/tabs"], function (_exports, _view, _jquery, _tabs) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _jquery = _interopRequireDefault(_jquery);
  _tabs = _interopRequireDefault(_tabs);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NavbarSiteView extends _view.default {
    template = 'site/navbar';

    /**
     * @private
     * @type {string|false|null}
     */
    currentTab = null;

    /**
     * @private
     * @type {HTMLElement}
     */
    navbarHeaderElement;
    events = {
      /** @this NavbarSiteView */
      'click .navbar-collapse.in a.nav-link': function (e) {
        const $a = (0, _jquery.default)(e.currentTarget);
        const href = $a.attr('href');
        if (href) {
          this.xsCollapse();
        }
      },
      /** @this NavbarSiteView */
      'click a.nav-link': function () {
        if (this.isSideMenuOpened) {
          this.closeSideMenu();
        }
      },
      /** @this NavbarSiteView */
      'click a.navbar-brand.nav-link': function () {
        this.xsCollapse();
      },
      /** @this NavbarSiteView */
      'click a.minimizer': function () {
        this.switchMinimizer();
      },
      /** @this NavbarSiteView */
      'click a.side-menu-button': function () {
        this.switchSideMenu();
      },
      /** @this NavbarSiteView */
      'click [data-action="toggleCollapsable"]': function () {
        this.toggleCollapsable();
      },
      /** @this NavbarSiteView */
      'click li.show-more a': function (e) {
        e.stopPropagation();
        this.showMoreTabs();
      },
      /** @this NavbarSiteView */
      'click .not-in-more > .nav-link-group': function (e) {
        this.handleGroupDropdownClick(e);
      },
      /** @this NavbarSiteView */
      'click .in-more .nav-link-group': function (e) {
        this.handleGroupDropdownClick(e);
      }
    };
    data() {
      return {
        tabDefsList1: this.tabDefsList.filter(item => !item.isInMore),
        tabDefsList2: this.tabDefsList.filter(item => item.isInMore),
        title: this.options.title,
        menuDataList: this.menuDataList,
        userId: this.getUser().id,
        logoSrc: this.getLogoSrc(),
        itemDataList: this.getItemDataList()
      };
    }

    /**
     * @private
     */
    handleGroupDropdownClick(e) {
      const $target = (0, _jquery.default)(e.currentTarget).parent();
      if ($target.parent().hasClass('more-dropdown-menu')) {
        e.stopPropagation();
        if ($target.hasClass('open')) {
          $target.removeClass('open');
          return;
        }
        this.handleGroupDropdownInMoreOpen($target);
        return;
      }
      if ($target.hasClass('open')) {
        return;
      }
      this.handleGroupDropdownOpen($target);
    }

    /**
     * @private
     */
    handleGroupMenuPosition($menu, $target) {
      if (this.navbarAdjustmentHandler && this.navbarAdjustmentHandler.handleGroupMenuPosition()) {
        this.handleGroupMenuPosition($menu, $target);
        return;
      }
      const rectItem = $target.get(0).getBoundingClientRect();
      const windowHeight = window.innerHeight;
      const isSide = this.isSide();
      if (!isSide && !$target.parent().hasClass('more-dropdown-menu')) {
        const maxHeight = windowHeight - rectItem.bottom;
        this.handleGroupMenuScrolling($menu, $target, maxHeight);
        return;
      }
      const itemCount = $menu.children().length;
      const tabHeight = isSide ? this.$tabs.find('> .tab:not(.tab-divider)').height() : this.$tabs.find('.tab-group > ul > li:visible').height();
      const menuHeight = tabHeight * itemCount;
      let top = rectItem.top - 1;
      if (top + menuHeight > windowHeight) {
        top = windowHeight - menuHeight - 2;
        if (top < 0) {
          top = 0;
        }
      }
      $menu.css({
        top: top + 'px'
      });
      const maxHeight = windowHeight - top;
      this.handleGroupMenuScrolling($menu, $target, maxHeight);
    }

    /**
     * @private
     */
    handleGroupMenuScrolling($menu, $target, maxHeight) {
      $menu.css({
        maxHeight: maxHeight + 'px'
      });
      const $window = (0, _jquery.default)(window);
      $window.off('scroll.navbar-tab-group');
      $window.on('scroll.navbar-tab-group', () => {
        if (!$menu.get(0) || !$target.get(0)) {
          return;
        }
        if (!$target.hasClass('open')) {
          return;
        }
        $menu.scrollTop($window.scrollTop());
      });
    }

    /**
     * @private
     */
    handleGroupDropdownOpen($target) {
      const $menu = $target.find('.dropdown-menu');
      this.handleGroupMenuPosition($menu, $target);
      setTimeout(() => {
        this.adjustBodyMinHeight();
      }, 50);
      $target.off('hidden.bs.dropdown');
      $target.on('hidden.bs.dropdown', () => {
        this.adjustBodyMinHeight();
      });
    }

    /**
     * @private
     */
    handleGroupDropdownInMoreOpen($target) {
      this.$el.find('.tab-group.tab.dropdown').removeClass('open');
      const $parentDropdown = this.$el.find('.more-dropdown-menu');
      $target.addClass('open');
      const $menu = $target.find('.dropdown-menu');
      const rectDropdown = $parentDropdown.get(0).getBoundingClientRect();
      const left = rectDropdown.right;
      $menu.css({
        left: left + 'px'
      });
      this.handleGroupMenuPosition($menu, $target);
      this.adjustBodyMinHeight();
      if (!this.isSide()) {
        if (left + $menu.width() > window.innerWidth) {
          $menu.css({
            left: rectDropdown.left - $menu.width() - 2
          });
        }
      }
    }

    /**
     * @private
     */
    isCollapsibleVisible() {
      return this.$el.find('.navbar-body').hasClass('in');
    }

    /**
     * @private
     */
    toggleCollapsable() {
      if (this.isCollapsibleVisible()) {
        this.hideCollapsable();
      } else {
        this.showCollapsable();
      }
    }

    /**
     * @private
     */
    hideCollapsable() {
      this.$el.find('.navbar-body').removeClass('in');
    }

    /**
     * @private
     */
    showCollapsable() {
      this.$el.find('.navbar-body').addClass('in');
    }

    /**
     * @private
     */
    xsCollapse() {
      this.hideCollapsable();
    }

    /**
     * @private
     * @return {boolean}
     */
    isMinimized() {
      return document.body.classList.contains('minimized');
    }
    switchSideMenu() {
      if (!this.isMinimized()) return;
      if (this.isSideMenuOpened) {
        this.closeSideMenu();
      } else {
        this.openSideMenu();
      }
    }
    openSideMenu() {
      this.isSideMenuOpened = true;
      document.body.classList.add('side-menu-opened');
      this.$sideMenuBackdrop = (0, _jquery.default)('<div>').addClass('side-menu-backdrop').click(() => this.closeSideMenu()).appendTo(document.body);
      this.$sideMenuBackdrop2 = (0, _jquery.default)('<div>').addClass('side-menu-backdrop').click(() => this.closeSideMenu()).appendTo(this.$navbarRightContainer);
    }

    /**
     * @private
     */
    closeSideMenu() {
      this.isSideMenuOpened = false;
      document.body.classList.remove('side-menu-opened');
      this.$sideMenuBackdrop.remove();
      this.$sideMenuBackdrop2.remove();
    }

    /**
     * @private
     */
    switchMinimizer() {
      if (this.isMinimized()) {
        if (this.isSideMenuOpened) {
          this.closeSideMenu();
        }
        document.body.classList.remove('minimized');
        this.getStorage().set('state', 'siteLayoutState', 'expanded');
      } else {
        document.body.classList.add('minimized');
        this.getStorage().set('state', 'siteLayoutState', 'collapsed');
      }
      if (window.Event) {
        try {
          window.dispatchEvent(new Event('resize'));
        } catch (e) {}
      }
    }
    getLogoSrc() {
      const companyLogoId = this.getConfig().get('companyLogoId');
      if (!companyLogoId) {
        return this.getBasePath() + (this.getThemeManager().getParam('logo') || 'client/img/logo.svg');
      }
      return `${this.getBasePath()}?entryPoint=LogoImage&id=${companyLogoId}`;
    }

    /**
     * @return {(Object|string)[]}
     */
    getTabList() {
      const tabList = this.tabsHelper.getTabList();
      if (this.isSide()) {
        tabList.unshift('Home');
      }
      return tabList;
    }
    setup() {
      this.addHandler('click', 'a.action', (/** MouseEvent */event, target) => {
        let actionData;
        const name = target.dataset.name;
        if (name) {
          const item = this.menuDataList.find(it => it.name === name);
          if (item.handler && item.actionFunction) {
            actionData = {
              handler: item.handler,
              actionFunction: item.actionFunction
            };
          }
        }
        Espo.Utils.handleAction(this, event, target, actionData);
      });
      this.getRouter().on('routed', e => {
        if (e.controller) {
          this.selectTab(e.controller);
          return;
        }
        this.selectTab(false);
      });

      /** @private */
      this.tabsHelper = new _tabs.default(this.getConfig(), this.getPreferences(), this.getUser(), this.getAcl(), this.getMetadata(), this.getLanguage());
      const itemDefs = this.getMetadata().get('app.clientNavbar.items') || {};

      /** @type {string[]} */
      this.itemList = Object.keys(itemDefs).filter(name => !itemDefs[name].disabled).sort((name1, name2) => {
        const order1 = itemDefs[name1].order || 0;
        const order2 = itemDefs[name2].order || 0;
        return order1 - order2;
      });
      const setup = () => {
        this.setupTabDefsList();
        return Promise.all(this.itemList.map(item => this.createItemView(item)));
      };
      const update = () => {
        setup().then(() => this.reRender());
      };
      setup();
      this.listenTo(this.getHelper().settings, 'sync', () => update());
      this.listenTo(this.getHelper().language, 'sync', () => update());
      this.listenTo(this.getHelper().preferences, 'update', (/** string[] */attributeList) => {
        if (!attributeList) {
          return;
        }
        if (attributeList.includes('tabList') || attributeList.includes('addCustomTabs') || attributeList.includes('useCustomTabList')) {
          update();
        }
      });
      this.once('remove', () => {
        (0, _jquery.default)(window).off('resize.navbar');
        (0, _jquery.default)(window).off('scroll.navbar');
        (0, _jquery.default)(window).off('scroll.navbar-tab-group');
        document.body.classList.remove('has-navbar');
      });
      this.setupMenu();
    }
    getItemDataList() {
      const defsMap = {};
      this.itemList.forEach(name => {
        defsMap[name] = this.getItemDefs(name);
      });
      return this.itemList.filter(name => {
        const item = defsMap[name];
        if (!item) {
          return false;
        }
        if (item.accessDataList && !Espo.Utils.checkAccessDataList(item.accessDataList, this.getAcl(), this.getUser())) {
          return false;
        }
        const view = this.getView(name + 'Item');
        if ('isAvailable' in view) {
          return view.isAvailable();
        }
        return true;
      }).map(name => {
        return {
          key: name + 'Item',
          name: name,
          class: defsMap[name].class || ''
        };
      });
    }

    /**
     *
     * @param {string} name
     * @return {{
     *     view: string,
     *     class: string,
     *     accessDataList?: module:utils~AccessDefs[],
     * }}
     */
    getItemDefs(name) {
      return this.getMetadata().get(['app', 'clientNavbar', 'items', name]);
    }

    /**
     * @param {string} name
     * @return {Promise}
     */
    createItemView(name) {
      const defs = this.getItemDefs(name);
      if (!defs || !defs.view) {
        return Promise.resolve();
      }
      const key = name + 'Item';
      return this.createView(key, defs.view, {
        selector: `[data-item="${name}"]`
      });
    }

    /**
     * @private
     */
    adjustTop() {
      const smallScreenWidth = this.getThemeManager().getParam('screenWidthXs');
      const navbarHeight = this.getNavbarHeight();
      const $window = (0, _jquery.default)(window);
      const $tabs = this.$tabs;
      const $more = this.$more;
      const $moreDropdown = this.$moreDropdown;
      $window.off('scroll.navbar');
      $window.off('resize.navbar');
      this.$moreDropdown.off('shown.bs.dropdown.navbar');
      this.off('show-more-tabs');
      $window.on('resize.navbar', () => updateWidth());
      $window.on('scroll.navbar', () => {
        if (!this.isMoreDropdownShown) {
          return;
        }
        $more.scrollTop($window.scrollTop());
      });
      this.$moreDropdown.on('shown.bs.dropdown.navbar', () => {
        $more.scrollTop($window.scrollTop());
      });
      this.on('show-more-tabs', () => {
        $more.scrollTop($window.scrollTop());
      });
      const updateMoreHeight = () => {
        const windowHeight = window.innerHeight;
        const windowWidth = window.innerWidth;
        if (windowWidth < smallScreenWidth) {
          $more.css('max-height', '');
          $more.css('overflow-y', '');
        } else {
          $more.css('overflow-y', 'hidden');
          $more.css('max-height', windowHeight - navbarHeight + 'px');
        }
      };
      $window.on('resize.navbar', () => updateMoreHeight());
      updateMoreHeight();
      const hideOneTab = () => {
        const count = $tabs.children().length;
        if (count <= 1) {
          return;
        }
        const $one = $tabs.children().eq(count - 2);
        $one.prependTo($more);
      };
      const unhideOneTab = () => {
        const $one = $more.children().eq(0);
        if ($one.length) {
          $one.insertBefore($moreDropdown);
        }
      };
      const $navbar = (0, _jquery.default)('#navbar .navbar');
      if (window.innerWidth >= smallScreenWidth) {
        $tabs.children('li').each(() => {
          hideOneTab();
        });
        $navbar.css('max-height', 'unset');
        $navbar.css('overflow', 'visible');
      }
      const navbarBaseWidth = this.navbarHeaderElement.clientWidth + this.$navbarRight.width();
      const tabCount = this.tabList.length;
      const navbarNeededHeight = navbarHeight + 1;
      this.adjustBodyMinHeightMethodName = 'adjustBodyMinHeightTop';
      const $moreDd = (0, _jquery.default)('#nav-more-tabs-dropdown');
      const $moreLi = $moreDd.closest('li');
      const updateWidth = () => {
        const windowWidth = window.innerWidth;
        const moreWidth = $moreLi.width();
        $more.children('li.not-in-more').each(() => {
          unhideOneTab();
        });
        if (windowWidth < smallScreenWidth) {
          return;
        }
        $navbar.css('max-height', navbarHeight + 'px');
        $navbar.css('overflow', 'hidden');
        $more.parent().addClass('hidden');
        const headerWidth = this.$el.width();
        const maxWidth = headerWidth - navbarBaseWidth - moreWidth;
        let width = $tabs.width();
        let i = 0;
        while (width > maxWidth) {
          hideOneTab();
          width = $tabs.width();
          i++;
          if (i >= tabCount) {
            setTimeout(() => updateWidth(), 100);
            break;
          }
        }
        $navbar.css('max-height', 'unset');
        $navbar.css('overflow', 'visible');
        if ($more.children().length > 0) {
          $moreDropdown.removeClass('hidden');
        }
      };
      const processUpdateWidth = isRecursive => {
        if ($navbar.height() > navbarNeededHeight) {
          updateWidth();
          setTimeout(() => processUpdateWidth(true), 200);
          return;
        }
        if (!isRecursive) {
          updateWidth();
          setTimeout(() => processUpdateWidth(true), 10);
        }
        setTimeout(() => processUpdateWidth(true), 1000);
      };
      if ($navbar.height() <= navbarNeededHeight && $more.children().length === 0) {
        $more.parent().addClass('hidden');
      }
      processUpdateWidth();
    }

    /**
     * @private
     */
    adjustSide() {
      const smallScreenWidth = this.getThemeManager().getParam('screenWidthXs');
      const $window = (0, _jquery.default)(window);
      const $tabs = this.$tabs;
      const $more = this.$more;

      /** @type {HTMLElement} */
      const tabsElement = this.$tabs.get(0);

      /** @type {HTMLElement} */
      const moreElement = this.$more.get(0);
      this.adjustBodyMinHeightMethodName = 'adjustBodyMinHeightSide';
      if ($more.children().length === 0) {
        $more.parent().addClass('hidden');
      }
      $window.off('scroll.navbar');
      $window.off('resize.navbar');
      this.$moreDropdown.off('shown.bs.dropdown.navbar');
      this.off('show-more-tabs');
      $window.on('scroll.navbar', () => {
        $window.scrollTop() ? this.$navbarRight.addClass('shadowed') : this.$navbarRight.removeClass('shadowed');
        $tabs.scrollTop($window.scrollTop());
        if (!this.isMoreDropdownShown) {
          return;
        }
        $more.scrollTop($window.scrollTop());
      });
      this.$moreDropdown.on('shown.bs.dropdown.navbar', () => {
        $more.scrollTop($window.scrollTop());
      });
      this.on('show-more-tabs', () => {
        $more.scrollTop($window.scrollTop());
      });
      const updateSizeForSide = () => {
        const windowHeight = window.innerHeight;
        const windowWidth = window.innerWidth;
        const navbarStaticItemsHeight = this.getStaticItemsHeight();
        this.$minimizer.removeClass('hidden');
        if (windowWidth < smallScreenWidth) {
          tabsElement.style.height = 'auto';
          if (moreElement) {
            moreElement.style.maxHeight = '';
          }
          return;
        }
        tabsElement.style.height = windowHeight - navbarStaticItemsHeight + 'px';
        if (moreElement) {
          moreElement.style.maxHeight = windowHeight + 'px';
        }
      };
      $window.on('resize.navbar', () => {
        updateSizeForSide();
        this.adjustBodyMinHeight();
      });
      updateSizeForSide();
      this.adjustBodyMinHeight();
    }

    /**
     * @private
     * @return {number}
     */
    getNavbarHeight() {
      return this.getFontSizeFactor() * (this.getThemeManager().getParam('navbarHeight') || 43);
    }

    /**
     * @private
     * @return {boolean}
     */
    isSide() {
      return this.getThemeManager().getParam('navbar') === 'side';
    }

    /**
     * @private
     * @return {number}
     */
    getStaticItemsHeight() {
      return this.getFontSizeFactor() * (this.getThemeManager().getParam('navbarStaticItemsHeight') || 97);
    }

    /**
     * @private
     */
    getFontSizeFactor() {
      return this.getThemeManager().getFontSizeFactor();
    }

    /**
     * @private
     */
    adjustBodyMinHeight() {
      if (!this.adjustBodyMinHeightMethodName) {
        return;
      }
      this[this.adjustBodyMinHeightMethodName]();
    }

    /**
     * @private
     */
    adjustBodyMinHeightSide() {
      let minHeight = this.$tabs.get(0).scrollHeight + this.getStaticItemsHeight();
      let moreHeight = 0;
      this.$more.find('> li:visible').each((i, el) => {
        const $el = (0, _jquery.default)(el);
        moreHeight += $el.outerHeight(true);
      });
      minHeight = Math.max(minHeight, moreHeight);
      const tabHeight = this.$tabs.find('> .tab:not(.tab-divider)').height();
      this.tabList.forEach((item, i) => {
        if (typeof item !== 'object') {
          return;
        }
        const $li = this.$el.find('li.tab[data-name="group-' + i + '"]');
        if (!$li.hasClass('open')) {
          return;
        }
        const tabCount = (item.itemList || []).length;
        const menuHeight = tabHeight * tabCount;
        if (menuHeight > minHeight) {
          minHeight = menuHeight;
        }
      });
      document.body.style.minHeight = minHeight + 'px';
    }

    /**
     * @private
     */
    adjustBodyMinHeightTop() {
      let minHeight = this.getNavbarHeight();
      this.$more.find('> li').each((i, el) => {
        const $el = (0, _jquery.default)(el);
        if (!this.isMoreTabsShown) {
          if ($el.hasClass('after-show-more')) {
            return;
          }
        } else {
          if ($el.hasClass('show-more')) {
            return;
          }
        }
        minHeight += $el.height();
      });
      const tabHeight = this.$tabs.find('.tab-group > ul > li:visible').height();
      this.tabList.forEach((item, i) => {
        if (typeof item !== 'object') {
          return;
        }
        const $li = this.$el.find('li.tab[data-name="group-' + i + '"]');
        if (!$li.hasClass('open')) {
          return;
        }
        const tabCount = (item.itemList || []).length;
        const menuHeight = tabHeight * tabCount;
        if (menuHeight > minHeight) {
          minHeight = menuHeight;
        }
      });
      document.body.style.minHeight = minHeight + 'px';
    }
    afterRender() {
      this.$tabs = this.$el.find('ul.tabs');
      this.$more = this.$tabs.find('li.more > ul');
      this.$minimizer = this.$el.find('a.minimizer');
      document.body.classList.add('has-navbar');
      const $moreDd = this.$moreDropdown = this.$tabs.find('li.more');
      $moreDd.on('shown.bs.dropdown', () => {
        this.isMoreDropdownShown = true;
        this.adjustBodyMinHeight();
      });
      $moreDd.on('hidden.bs.dropdown', () => {
        this.isMoreDropdownShown = false;
        this.hideMoreTabs();
        this.adjustBodyMinHeight();
      });
      this.selectTab(this.getRouter().getLast().controller);
      let layoutState = this.getStorage().get('state', 'siteLayoutState');
      if (!layoutState) {
        layoutState = (0, _jquery.default)(window).width() > 1320 ? 'expanded' : 'collapsed';
      }
      let layoutMinimized = false;
      if (layoutState === 'collapsed') {
        layoutMinimized = true;
      }
      if (layoutMinimized) {
        document.body.classList.add('minimized');
      }
      this.$navbar = this.$el.find('> .navbar');
      this.$navbarRightContainer = this.$navbar.find('> .navbar-body > .navbar-right-container');
      this.$navbarRight = this.$navbarRightContainer.children();
      this.navbarHeaderElement = this.element.querySelector('.navbar-header');
      const handlerClassName = this.getThemeManager().getParam('navbarAdjustmentHandler');
      if (handlerClassName) {
        Espo.loader.require(handlerClassName, Handler => {
          const handler = new Handler(this);
          this.navbarAdjustmentHandler = handler;
          handler.process();
        });
        return;
      }
      if (this.getThemeManager().getParam('skipDefaultNavbarAdjustment')) {
        return;
      }
      this.adjustAfterRender();
    }

    /**
     * @private
     */
    adjustAfterRender() {
      if (this.isSide()) {
        const processSide = () => {
          if (this.$navbar.height() < (0, _jquery.default)(window).height() / 2) {
            setTimeout(() => processSide(), 50);
            return;
          }
          if (this.getThemeManager().isUserTheme()) {
            setTimeout(() => this.adjustSide(), 10);
            return;
          }
          this.adjustSide();
        };
        processSide();
        return;
      }
      const process = () => {
        if (this.$el.width() < (0, _jquery.default)(window).width() / 2) {
          setTimeout(() => process(), 50);
          return;
        }
        if (this.getThemeManager().isUserTheme()) {
          setTimeout(() => this.adjustTop(), 10);
          return;
        }
        this.adjustTop();
      };
      process();
    }

    /**
     * @param {string|false} name
     */
    selectTab(name) {
      const $tabs = this.$el.find('ul.tabs');
      $tabs.find('li.active').removeClass('active');
      if (name) {
        $tabs.find(`li[data-name="${name}"]`).addClass('active');
      }
      this.currentTab = name;
      const url = this.getRouter().getCurrentUrl();
      this.urlList.filter(item => url.startsWith(item.url)).forEach(item => {
        $tabs.find(`li[data-name="${item.name}"]`).addClass('active');
      });
    }

    /**
     * @private
     */
    setupTabDefsList() {
      /** @type {{url: string, name: string}[]} */
      this.urlList = [];
      const allTabList = this.getTabList();
      this.tabList = allTabList.filter((item, i) => {
        if (!item) {
          return false;
        }
        if (typeof item !== 'object') {
          return this.tabsHelper.checkTabAccess(item);
        }
        if (this.tabsHelper.isTabDivider(item)) {
          if (!this.isSide()) {
            return false;
          }
          if (i === allTabList.length - 1) {
            return false;
          }
          return true;
        }
        if (this.tabsHelper.isTabUrl(item)) {
          return this.tabsHelper.checkTabAccess(item);
        }

        /** @type {(Record|string)[]} */
        let itemList = (item.itemList || []).filter(item => {
          if (this.tabsHelper.isTabDivider(item)) {
            return true;
          }
          return this.tabsHelper.checkTabAccess(item);
        });
        itemList = itemList.filter((item, i) => {
          if (!this.tabsHelper.isTabDivider(item)) {
            return true;
          }
          const nextItem = itemList[i + 1];
          if (!nextItem) {
            return true;
          }
          if (this.tabsHelper.isTabDivider(nextItem)) {
            return false;
          }
          return true;
        });
        itemList = itemList.filter((item, i) => {
          if (!this.tabsHelper.isTabDivider(item)) {
            return true;
          }
          if (i === 0 || i === itemList.length - 1) {
            return false;
          }
          return true;
        });
        item.itemList = itemList;
        return !!itemList.length;
      });
      let moreIsMet = false;
      this.tabList = this.tabList.filter((item, i) => {
        const nextItem = this.tabList[i + 1];
        const prevItem = this.tabList[i - 1];
        if (this.tabsHelper.isTabMoreDelimiter(item)) {
          moreIsMet = true;
        }
        if (!this.tabsHelper.isTabDivider(item)) {
          return true;
        }
        if (!nextItem) {
          return true;
        }
        if (this.tabsHelper.isTabDivider(nextItem)) {
          return false;
        }
        if (this.tabsHelper.isTabDivider(prevItem) && this.tabsHelper.isTabMoreDelimiter(nextItem) && moreIsMet) {
          return false;
        }
        return true;
      });
      if (moreIsMet) {
        let end = this.tabList.length;
        for (let i = this.tabList.length - 1; i >= 0; i--) {
          const item = this.tabList[i];
          if (!this.tabsHelper.isTabDivider(item)) {
            break;
          }
          end = this.tabList.length - 1;
        }
        this.tabList = this.tabList.slice(0, end);
      }
      const tabDefsList = [];
      const colorsDisabled = this.getConfig().get('scopeColorsDisabled') || this.getConfig().get('tabColorsDisabled');
      const tabIconsDisabled = this.getConfig().get('tabIconsDisabled');
      const params = {
        colorsDisabled: colorsDisabled,
        tabIconsDisabled: tabIconsDisabled
      };
      const vars = {
        moreIsMet: false,
        isHidden: false
      };
      this.tabList.forEach((tab, i) => {
        if (this.tabsHelper.isTabMoreDelimiter(tab)) {
          if (!vars.moreIsMet) {
            vars.moreIsMet = true;
            return;
          }
          if (i === this.tabList.length - 1) {
            return;
          }
          vars.isHidden = true;
          tabDefsList.push({
            name: 'show-more',
            isInMore: true,
            className: 'show-more',
            html: '<span class="fas fa-ellipsis-h more-icon"></span>'
          });
          return;
        }
        tabDefsList.push(this.prepareTabItemDefs(params, tab, i, vars));
      });
      this.tabDefsList = tabDefsList;
    }

    /**
     * @private
     * @param {{
     *     colorsDisabled: boolean,
     *     tabIconsDisabled: boolean,
     * }} params
     * @param {Record|string} tab
     * @param {number} i
     * @param {Object} vars
     * @return {{
     *     isAfterShowMore: boolean,
     *     isDivider: boolean,
     *     color: null,
     *     link: string,
     *     name: string,
     *     isInMore: boolean,
     *     shortLabel: string,
     *     label: string,
     *     isGroup: boolean,
     *     aClassName: string,
     *     iconClass: null
     * }}
     */
    prepareTabItemDefs(params, tab, i, vars) {
      let link;
      let iconClass = null;
      let color = null;
      let isGroup = false;
      let isDivider = false;
      let isUrl = false;
      let name = tab;
      let aClassName = 'nav-link';
      const label = this.tabsHelper.getTranslatedTabLabel(tab);
      if (tab === 'Home') {
        link = '#';
      } else if (this.tabsHelper.isTabDivider(tab)) {
        isDivider = true;
        aClassName = 'nav-divider-text';
        name = `divider-${i}`;
      } else if (this.tabsHelper.isTabUrl(tab)) {
        isUrl = true;
        name = `url-${i}`;
        link = tab.url || '#';
        color = tab.color;
        iconClass = tab.iconClass;
        this.urlList.push({
          name: name,
          url: link
        });
      } else if (this.tabsHelper.isTabGroup(tab)) {
        isGroup = true;
        color = tab.color;
        iconClass = tab.iconClass;
        name = `group-${i}`;
        link = null;
        aClassName = 'nav-link-group';
      } else {
        link = '#' + tab;
      }
      const shortLabel = label.substring(0, 2);
      if (!params.colorsDisabled && !isGroup && !isDivider && !isUrl) {
        color = this.getMetadata().get(['clientDefs', tab, 'color']);
      }
      if (color && !/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(color)) {
        color = null;
      }
      if (!params.tabIconsDisabled && !isGroup && !isDivider && !isUrl) {
        iconClass = this.getMetadata().get(['clientDefs', tab, 'iconClass']);
      }
      const o = {
        link: link,
        label: label,
        shortLabel: shortLabel,
        name: name,
        isInMore: vars.moreIsMet,
        color: color,
        iconClass: iconClass,
        isAfterShowMore: vars.isHidden,
        aClassName: aClassName,
        isGroup: isGroup,
        isDivider: isDivider
      };
      if (isGroup) {
        o.itemList = tab.itemList.map((tab, i) => {
          return this.prepareTabItemDefs(params, tab, i, vars);
        });
      }
      if (vars.isHidden) {
        o.className = 'after-show-more';
      }
      if (color && !iconClass) {
        o.colorIconClass = 'color-icon fas fa-square';
      }
      return o;
    }

    /**
     * @typedef {Object} MenuDataItem
     * @property {string} [link]
     * @property {string} [name]
     * @property {string} [html]
     * @property {string} [handler]
     * @property {string} [actionFunction]
     * @property {true} [divider]
     */

    /**
     * @private
     */
    setupMenu() {
      let avatarHtml = this.getHelper().getAvatarHtml(this.getUser().id, 'small', 20, 'avatar-link');
      if (avatarHtml) {
        avatarHtml += ' ';
      }

      /** @type {MenuDataItem[]} */
      this.menuDataList = [{
        link: `#User/view/${this.getUser().id}`,
        html: avatarHtml + this.getHelper().escapeString(this.getUser().get('name'))
      }, {
        divider: true
      }];

      /**
       * @type {Record<string, {
       *     order?: number,
       *     groupIndex?: number,
       *     link?: string,
       *     labelTranslation?: string,
       *     configCheck?: string,
       *     disabled:? boolean,
       *     handler?: string,
       *     actionFunction?: string,
       *     accessDataList?: module:utils~AccessDefs[],
       * }>} items
       */
      const items = this.getMetadata().get('app.clientNavbar.menuItems') || {};
      const nameList = Object.keys(items).sort((n1, n2) => {
        const o1 = items[n1].order;
        const o2 = items[n2].order;
        const g1 = items[n1].groupIndex;
        const g2 = items[n2].groupIndex;
        if (g2 === g1) {
          return o1 - o2;
        }
        return g1 - g2;
      });
      let currentGroup = 0;
      for (const name of nameList) {
        const item = items[name];
        if (item.groupIndex !== currentGroup) {
          currentGroup = item.groupIndex;
          this.menuDataList.push({
            divider: true
          });
        }
        if (item.disabled) {
          continue;
        }
        if (item.configCheck && !Espo.Utils.checkActionAvailability(this.getHelper(), item)) {
          continue;
        }
        if (item.accessDataList && !Espo.Utils.checkAccessDataList(item.accessDataList, this.getAcl(), this.getUser())) {
          continue;
        }
        this.menuDataList.push({
          name: name,
          link: item.link,
          label: this.getLanguage().translatePath(item.labelTranslation),
          handler: item.handler,
          actionFunction: item.actionFunction
        });
      }
    }
    showMoreTabs() {
      this.$el.find('.tab-group.tab.dropdown').removeClass('open');
      this.isMoreTabsShown = true;
      this.$more.addClass('more-expanded');
      this.adjustBodyMinHeight();
      this.trigger('show-more-tabs');
    }
    hideMoreTabs() {
      if (!this.isMoreTabsShown) {
        return;
      }
      this.$more.removeClass('more-expanded');
      this.adjustBodyMinHeight();
      this.isMoreTabsShown = false;
    }
  }
  var _default = _exports.default = NavbarSiteView;
});

define("views/site/master", ["exports", "view", "jquery", "views/collapsed-modal-bar"], function (_exports, _view, _jquery, _collapsedModalBar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _jquery = _interopRequireDefault(_jquery);
  _collapsedModalBar = _interopRequireDefault(_collapsedModalBar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/site/master */

  class MasterSiteView extends _view.default {
    template = 'site/master';
    views = {
      header: {
        id: 'header',
        view: 'views/site/header'
      },
      main: {
        id: 'main',
        view: false
      },
      footer: {
        fullSelector: 'body > footer',
        view: 'views/site/footer'
      }
    };

    /**
     * @type {string}
     */
    currentViewKey;

    /**
     * @type {string}
     */
    currentName;

    /**
     * @internal
     * @type {CollapsedModalBarView}
     */
    collapsedModalBarView;
    showLoadingNotification() {
      Espo.Ui.notifyWait();
    }
    hideLoadingNotification() {
      Espo.Ui.notify(false);
    }
    setup() {
      (0, _jquery.default)(window).on('resize.' + this.cid, () => {
        this.adjustContent();
      });
      this.collapsedModalBarView = new _collapsedModalBar.default();
      this.assignView('collapsedModalBar', this.collapsedModalBarView, '> .collapsed-modal-bar');
    }

    /**
     * @return {Bull.View|null}
     */
    getMainView() {
      return this.getView('main');
    }
    onRemove() {
      (0, _jquery.default)(window).off('resize.' + this.cid);
    }
    afterRender() {
      const params = this.getThemeManager().getParam('params');
      const $body = (0, _jquery.default)('body');
      for (const param in params) {
        const value = this.getThemeManager().getParam(param);
        $body.attr('data-' + Espo.Utils.camelCaseToHyphen(param), value);
      }
      const footerView = this.getView('footer');
      if (footerView) {
        const html = footerView.$el.html() || '';
        if ((html.match(/espocrm/gi) || []).length < 2) {
          const text = 'PHAgY2xhc3M9ImNyZWRpdCBzbWFsbCI+JmNvcHk7IDxhIGhyZWY9Imh0dHA6Ly93d3cuZXNwb2Nyb' + 'S5jb20iPkVzcG9DUk08L2E+PC9wPg==';
          let decText;
          if (typeof window.atob === "function") {
            decText = window.atob(text);
          } else if (typeof atob === "function") {
            decText = atob(text);
          }
          if (decText) {
            footerView.$el.html(decText);
          }
        }
      }
      this.$content = this.$el.find('> #content');
      this.adjustContent();
      const extensions = this.getHelper().getAppParam('extensions') || [];
      if (this.getConfig().get('maintenanceMode')) {
        this.createView('dialog', 'views/modal', {
          templateContent: '<div class="text-danger">{{complexText viewObject.options.message}}</div>',
          headerText: this.translate('maintenanceMode', 'fields', 'Settings'),
          backdrop: true,
          message: this.translate('maintenanceMode', 'messages'),
          buttonList: [{
            name: 'close',
            label: this.translate('Close')
          }]
        }, view => {
          view.render();
        });
      } else if (this.getHelper().getAppParam('auth2FARequired')) {
        this.createView('dialog', 'views/modals/auth2fa-required', {}, view => {
          view.render();
        });
      } else if (extensions.length !== 0) {
        this.processExtensions(extensions);
      }
    }
    adjustContent() {
      if (!this.isRendered()) {
        return;
      }
      if (window.innerWidth < this.getThemeManager().getParam('screenWidthXs')) {
        this.isSmallScreen = true;
        let height = window.innerHeight - this.$content.get(0).getBoundingClientRect().top;
        const $navbarCollapse = (0, _jquery.default)('#navbar .navbar-body');
        if ($navbarCollapse.hasClass('in') || $navbarCollapse.hasClass('collapsing')) {
          height += $navbarCollapse.height();
        }
        const footerHeight = (0, _jquery.default)('#footer').height() || 26;
        height -= footerHeight;
        if (height <= 0) {
          this.$content.css('minHeight', '');
          return;
        }
        this.$content.css('minHeight', height + 'px');
        return;
      }
      if (this.isSmallScreen) {
        this.$content.css('minHeight', '');
      }
      this.isSmallScreen = false;
    }

    /**
     * @param {{
     *     name: string,
     *     licenseStatus: string,
     *     licenseStatusMessage:? string,
     *     notify: boolean,
     * }[]} list
     */
    processExtensions(list) {
      const messageList = [];
      list.forEach(item => {
        if (!item.notify) {
          return;
        }
        const message = item.licenseStatusMessage ?? 'extensionLicense' + Espo.Utils.upperCaseFirst(Espo.Utils.hyphenToCamelCase(item.licenseStatus.toLowerCase()));
        messageList.push(this.translate(message, 'messages').replace('{name}', item.name));
      });
      if (!messageList.length) {
        return;
      }
      let message = messageList.join('\n\n');
      message = this.getHelper().transformMarkdownText(message);
      const dialog = new Espo.Ui.Dialog({
        backdrop: 'static',
        buttonList: [{
          name: 'close',
          text: this.translate('Close'),
          className: 'btn-s-wide',
          onClick: () => dialog.close()
        }],
        className: 'dialog-confirm text-danger',
        body: message.toString()
      });
      dialog.show();
    }
  }
  var _default = _exports.default = MasterSiteView;
});

define("views/site/header", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class HeaderSiteView extends _view.default {
    template = 'site/header';
    title = 'EspoCRM';
    navbarView = 'views/site/navbar';
    customViewPath = ['clientDefs', 'App', 'navbarView'];
    data = {
      title: this.title
    };
    setup() {
      const navbarView = this.getMetadata().get(this.customViewPath) || this.navbarView;
      this.createView('navbar', navbarView, {
        fullSelector: '#navbar',
        title: this.title
      });
    }
  }
  var _default = _exports.default = HeaderSiteView;
});

define("views/site/navbar/item", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * A navbar item.
   *
   * @abstract
   */
  class SiteNavbarItemView extends _view.default {
    /**
     * Whether this item is available.
     *
     * @return {boolean}
     */
    isAvailable() {
      return true;
    }
  }
  var _default = _exports.default = SiteNavbarItemView;
});

define("views/record/detail-bottom", ["exports", "views/record/panels-container"], function (_exports, _panelsContainer) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _panelsContainer = _interopRequireDefault(_panelsContainer);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/detail-bottom */

  /**
   * A detail-bottom record view.
   */
  class DetailBottomRecordView extends _panelsContainer.default {
    /** @inheritDoc */
    template = 'record/bottom';

    /** @inheritDoc */
    mode = 'detail';
    streamPanel = true;
    relationshipPanels = true;
    readOnly = false;
    portalLayoutDisabled = false;
    name = 'bottom';

    /** @inheritDoc */
    setupPanels() {
      const scope = this.scope;
      this.panelList = this.getMetadata().get(['clientDefs', scope, 'bottomPanels', this.type]) || this.panelList || [];
      this.panelList = [...this.panelList].map(item => {
        if ('reference' in item && item.reference) {
          // noinspection UnnecessaryLocalVariableJS
          /** @type {module:views/record/panels-container~panel} */
          const newItem = {
            ...this.getMetadata().get(`app.clientRecord.panels.${item.reference}`),
            ...item
          };
          return newItem;
        }
        return item;
      });
      this.panelList.forEach(item => {
        if ('index' in item) {
          return;
        }
        if ('order' in item) {
          item.index = item.order;
        }
      });
      if (this.streamPanel && this.getMetadata().get(['scopes', scope, 'stream'])) {
        this.setupStreamPanel();
      }
    }

    /**
     * Set up a stream panel.
     */
    setupStreamPanel() {
      let streamAllowed = this.getAcl().checkModel(this.model, 'stream', true);
      if (streamAllowed === null) {
        this.listenToOnce(this.model, 'sync', () => {
          streamAllowed = this.getAcl().checkModel(this.model, 'stream', true);
          if (streamAllowed) {
            this.onPanelsReady(() => {
              this.showPanel('stream', 'acl');
            });
          }
        });
      }
      if (streamAllowed !== false) {
        this.panelList.push({
          name: 'stream',
          label: 'Stream',
          view: this.getMetadata().get(['clientDefs', this.scope, 'streamPanelView']) || 'views/stream/panel',
          sticked: false,
          hidden: !streamAllowed,
          index: 2
        });
        if (!streamAllowed) {
          this.recordHelper.setPanelStateParam('stream', 'hiddenAclLocked', true);
        }
      }
    }
    init() {
      this.recordHelper = this.options.recordHelper;
      this.scope = this.entityType = this.model.name;
      this.readOnlyLocked = this.options.readOnlyLocked || this.readOnly;
      this.readOnly = this.options.readOnly || this.readOnly;
      this.inlineEditDisabled = this.options.inlineEditDisabled || this.inlineEditDisabled;
      this.portalLayoutDisabled = this.options.portalLayoutDisabled || this.portalLayoutDisabled;
      this.recordViewObject = this.options.recordViewObject;
    }
    setup() {
      this.type = this.mode;
      if ('type' in this.options) {
        this.type = this.options.type;
      }
      this.panelList = [];
      this.setupInitial();
      this.setupPanels();
      this.wait(true);
      Promise.all([new Promise(resolve => {
        this.getHelper().layoutManager.get(this.scope, 'bottomPanels' + Espo.Utils.upperCaseFirst(this.type), layoutData => {
          this.layoutData = layoutData;
          resolve();
        });
      })]).then(() => {
        const panelNameList = [];
        this.panelList = this.panelList.filter(p => {
          panelNameList.push(p.name);
          if (p.aclScope) {
            if (!this.getAcl().checkScope(p.aclScope)) {
              return;
            }
          }
          if (p.accessDataList) {
            if (!Espo.Utils.checkAccessDataList(p.accessDataList, this.getAcl(), this.getUser())) {
              return false;
            }
          }
          return true;
        });
        if (this.relationshipPanels) {
          const linkDefs = (this.model.defs || {}).links || {};
          if (this.layoutData) {
            for (const name in this.layoutData) {
              if (!linkDefs[name]) {
                continue;
              }
              const p = this.layoutData[name];
              if (!~panelNameList.indexOf(name) && !p.disabled) {
                this.addRelationshipPanel(name, p);
              }
            }
          }
        }
        this.panelList = this.panelList.map(p => {
          const item = Espo.Utils.clone(p);
          if (this.recordHelper.getPanelStateParam(p.name, 'hidden') !== null) {
            item.hidden = this.recordHelper.getPanelStateParam(p.name, 'hidden');
          } else {
            this.recordHelper.setPanelStateParam(p.name, 'hidden', item.hidden || false);
          }
          return item;
        });
        this.panelList.forEach(item => {
          item.actionsViewKey = item.name + 'Actions';
        });
        this.alterPanels();
        this.setupPanelsFinal();
        this.setupPanelViews();
        this.wait(false);
      });
    }

    /**
     * Set read-only.
     */
    setReadOnly() {
      this.readOnly = true;
    }

    /** @private */
    addRelationshipPanel(name, item) {
      const scope = this.scope;
      const scopesDefs = this.getMetadata().get('scopes') || {};
      let p;
      if (typeof item === 'string' || item instanceof String) {
        p = {
          name: item
        };
      } else {
        p = Espo.Utils.clone(item || {});
      }
      p.name = p.name || name;
      if (!p.name) {
        return;
      }
      if (typeof p.order === 'undefined') p.order = 5;
      name = p.name;
      const links = (this.model.defs || {}).links || {};
      if (!(name in links)) {
        return;
      }
      const foreignScope = links[name].entity;
      if ((scopesDefs[foreignScope] || {}).disabled) {
        return;
      }
      if (!this.getAcl().check(foreignScope, 'read')) {
        return;
      }
      let defs = this.getMetadata().get(['clientDefs', scope, 'relationshipPanels', name]) || {};
      defs = Espo.Utils.clone(defs);
      for (const i in defs) {
        if (i in p) {
          continue;
        }
        p[i] = defs[i];
      }
      if (!p.view) {
        p.view = 'views/record/panels/relationship';
      }
      if (this.recordHelper.getPanelStateParam(p.name, 'hidden') !== null) {
        p.hidden = this.recordHelper.getPanelStateParam(p.name, 'hidden');
      } else {
        this.recordHelper.setPanelStateParam(p.name, 'hidden', p.hidden || false);
      }
      this.panelList.push(p);
    }
  }
  var _default = _exports.default = DetailBottomRecordView;
});

define("views/record/row-actions/relationship", ["exports", "views/record/row-actions/default"], function (_exports, _default2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _default2 = _interopRequireDefault(_default2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RelationshipRowActionsView extends _default2.default {
    getActionList() {
      const list = [{
        action: 'quickView',
        label: 'View',
        data: {
          id: this.model.id
        },
        link: '#' + this.model.entityType + '/view/' + this.model.id,
        groupIndex: 0
      }];
      if (this.options.acl.edit && !this.options.editDisabled) {
        list.push({
          action: 'quickEdit',
          label: 'Edit',
          data: {
            id: this.model.id
          },
          link: '#' + this.model.entityType + '/edit/' + this.model.id,
          groupIndex: 0
        });
      }
      if (!this.options.unlinkDisabled) {
        list.push({
          action: 'unlinkRelated',
          label: 'Unlink',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        });
      }
      this.getAdditionalActionList().forEach(item => list.push(item));
      if (this.options.acl.delete && !this.options.removeDisabled) {
        list.push({
          action: 'removeRelated',
          label: 'Remove',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        });
      }
      return list;
    }
  }
  var _default = _exports.default = RelationshipRowActionsView;
});

define("views/record/panels/default-side", ["exports", "views/record/panels/side"], function (_exports, _side) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _side = _interopRequireDefault(_side);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * A default side panel.
   */
  class DefaultSidePanelView extends _side.default {
    /**
     * @protected
     * @type {boolean}
     */
    complexCreatedDisabled;

    /**
     * @protected
     * @type {boolean}
     */
    complexModifiedDisabled;
    data() {
      const data = super.data();
      if (this.complexCreatedDisabled && this.complexModifiedDisabled || !this.hasComplexCreated && !this.hasComplexModified) {
        data.complexDateFieldsDisabled = true;
      }
      data.hasComplexCreated = this.hasComplexCreated;
      data.hasComplexModified = this.hasComplexModified;
      return data;
    }
    setup() {
      this.fieldList = Espo.Utils.cloneDeep(this.fieldList);
      const allFieldList = this.getFieldManager().getEntityTypeFieldList(this.model.entityType);
      this.hasComplexCreated = allFieldList.includes('createdAt') || allFieldList.includes('createdBy');
      this.hasComplexModified = allFieldList.includes('modifiedAt') || allFieldList.includes('modifiedBy');
      super.setup();
    }
    setupFields() {
      super.setupFields();
      if (!this.complexCreatedDisabled) {
        if (this.hasComplexCreated) {
          this.fieldList.push({
            name: 'complexCreated',
            labelText: this.translate('Created'),
            isAdditional: true,
            view: 'views/fields/complex-created',
            readOnly: true
          });
          if (!this.model.get('createdById') && !this.model.get('createdAt')) {
            this.recordViewObject.hideField('complexCreated');
          }
        }
      } else {
        this.recordViewObject.hideField('complexCreated');
      }
      if (!this.complexModifiedDisabled) {
        if (this.hasComplexModified) {
          this.fieldList.push({
            name: 'complexModified',
            labelText: this.translate('Modified'),
            isAdditional: true,
            view: 'views/fields/complex-created',
            readOnly: true,
            options: {
              baseName: 'modified'
            }
          });
          if (!this.isModifiedVisible()) {
            this.recordViewObject.hideField('complexModified');
          }
        }
      } else {
        this.recordViewObject.hideField('complexModified');
      }
      if (!this.complexCreatedDisabled && this.hasComplexCreated) {
        this.listenTo(this.model, 'change', () => {
          if (!this.model.hasChanged('createdById') && !this.model.hasChanged('createdAt')) {
            return;
          }
          if (!this.model.get('createdById') && !this.model.get('createdAt')) {
            return;
          }
          this.recordViewObject.showField('complexCreated');
        });
      }
      if (!this.complexModifiedDisabled && this.hasComplexModified) {
        this.listenTo(this.model, 'change', () => {
          if (!this.model.hasChanged('modifiedById') && !this.model.hasChanged('modifiedAt')) {
            return;
          }
          if (!this.isModifiedVisible()) {
            return;
          }
          this.recordViewObject.showField('complexModified');
        });
      }
      if (this.getMetadata().get(['scopes', this.model.entityType, 'stream']) && !this.getUser().isPortal()) {
        this.fieldList.push({
          name: 'followers',
          labelText: this.translate('Followers'),
          isAdditional: true,
          view: 'views/fields/followers',
          readOnly: true
        });
        this.controlFollowersField();
        this.listenTo(this.model, 'change:followersIds', () => this.controlFollowersField());
      }
    }

    /**
     * @private
     * @return {boolean}
     */
    isModifiedVisible() {
      if (!this.hasComplexModified) {
        return false;
      }
      if (!this.model.get('modifiedById') && !this.model.get('modifiedAt')) {
        return false;
      }
      if (!this.model.get('modifiedById') && this.model.get('modifiedAt') === this.model.get('createdAt')) {
        return false;
      }
      return true;
    }
    controlFollowersField() {
      if (this.model.get('followersIds') && this.model.get('followersIds').length) {
        this.recordViewObject.showField('followers');
        return;
      }
      this.recordViewObject.hideField('followers');
    }
  }
  var _default = _exports.default = DefaultSidePanelView;
});

define("views/notification/items/base", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/notification/items/base */

  class BaseNotificationItemView extends _view.default {
    /** @type {string} */
    messageName;
    /** @type {string} */
    messageTemplate;
    messageData = null;
    isSystemAvatar = false;
    data() {
      return {
        avatar: this.getAvatarHtml()
      };
    }
    init() {
      this.createField('createdAt', null, null, 'views/fields/datetime-short');
      this.messageData = {};
    }
    createField(name, type, params, view) {
      type = type || this.model.getFieldType(name) || 'base';
      this.createView(name, view || this.getFieldManager().getViewName(type), {
        model: this.model,
        defs: {
          name: name,
          params: params || {}
        },
        selector: '.cell-' + name,
        mode: 'list'
      });
    }
    createMessage() {
      const parentType = this.model.get('relatedParentType') || null;
      if (!this.messageTemplate && this.messageName) {
        this.messageTemplate = this.translate(this.messageName, 'notificationMessages', parentType) || '';
      }
      if (this.messageTemplate.indexOf('{entityType}') === 0 && typeof this.messageData.entityType === 'string') {
        this.messageData.entityTypeUcFirst = Espo.Utils.upperCaseFirst(this.messageData.entityType);
        this.messageTemplate = this.messageTemplate.replace('{entityType}', '{entityTypeUcFirst}');
      }
      this.createView('message', 'views/stream/message', {
        messageTemplate: this.messageTemplate,
        selector: '.message',
        model: this.model,
        messageData: this.messageData
      });
    }
    getAvatarHtml() {
      let id = this.userId;
      if (this.isSystemAvatar || !id) {
        id = this.getHelper().getAppParam('systemUserId');
      }
      return this.getHelper().getAvatarHtml(id, 'small', 20);
    }

    /**
     * @param {string} entityType
     * @param {boolean} [isPlural]
     * @return {string}
     */
    translateEntityType(entityType, isPlural) {
      let string = isPlural ? this.translate(entityType, 'scopeNamesPlural') || '' : this.translate(entityType, 'scopeNames') || '';
      string = string.toLowerCase();
      if (this.toUpperCaseFirstLetter()) {
        string = Espo.Utils.upperCaseFirst(string);
      }
      return string;
    }

    /**
     * @property
     * @return {boolean}
     */
    toUpperCaseFirstLetter() {
      const language = this.getPreferences().get('language') || this.getConfig().get('language');
      return ['de_DE', 'nl_NL'].includes(language);
    }
  }
  var _default = _exports.default = BaseNotificationItemView;
});

define("views/modals/related-list", ["exports", "views/modal", "search-manager", "jquery", "helpers/record/select-related", "helpers/record/create-related"], function (_exports, _modal, _searchManager, _jquery, _selectRelated, _createRelated) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _searchManager = _interopRequireDefault(_searchManager);
  _jquery = _interopRequireDefault(_jquery);
  _selectRelated = _interopRequireDefault(_selectRelated);
  _createRelated = _interopRequireDefault(_createRelated);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/modals/related-records */

  /**
   * A related-list modal.
   *
   * @todo JSDocs constructor options.
   */
  class RelatedListModalView extends _modal.default {
    template = 'modals/related-list';
    className = 'dialog dialog-record';
    searchPanel = true;
    scope = '';
    noCreateScopeList = ['User', 'Team', 'Role', 'Portal'];
    backdrop = true;
    fixedHeaderHeight = true;
    mandatorySelectAttributeList = null;
    layoutName = 'listSmall';

    /** @inheritDoc */
    shortcutKeys = {
      /** @this RelatedListModalView */
      'Control+Space': function (e) {
        this.handleShortcutKeyCtrlSpace(e);
      },
      /** @this RelatedListModalView */
      'Control+Slash': function (e) {
        this.handleShortcutKeyCtrlSlash(e);
      },
      /** @this RelatedListModalView */
      'Control+Comma': function (e) {
        this.handleShortcutKeyCtrlComma(e);
      },
      /** @this RelatedListModalView */
      'Control+Period': function (e) {
        this.handleShortcutKeyCtrlPeriod(e);
      },
      /** @this RelatedListModalView */
      'Control+ArrowLeft': function () {
        this.handleShortcutKeyControlArrowLeft();
      },
      /** @this RelatedListModalView */
      'Control+ArrowRight': function () {
        this.handleShortcutKeyControlArrowRight();
      }
    };
    events = {
      /** @this RelatedListModalView */
      'click button[data-action="createRelated"]': function () {
        this.actionCreateRelated();
      },
      /** @this RelatedListModalView */
      'click .action': function (e) {
        const isHandled = Espo.Utils.handleAction(this, e.originalEvent, e.currentTarget);
        if (isHandled) {
          return;
        }
        this.trigger('action', e.originalEvent, e.currentTarget);
      }
    };
    setup() {
      this.primaryFilterName = this.options.primaryFilterName || null;
      this.buttonList = [{
        name: 'cancel',
        label: 'Close'
      }];
      this.scope = this.options.scope || this.options.entityType || this.scope;
      this.defaultOrderBy = this.options.defaultOrderBy;
      this.defaultOrder = this.options.defaultOrder;
      this.panelName = this.options.panelName;
      this.link = this.options.link;
      this.defs = this.options.defs || {};
      this.filterList = this.options.filterList;
      this.filter = this.options.filter;
      this.layoutName = this.options.layoutName || this.layoutName;
      this.url = this.options.url;
      this.listViewName = this.options.listViewName;
      this.rowActionsView = this.options.rowActionsView;
      this.createDisabled = this.options.createDisabled || this.createDisabled;
      this.selectDisabled = this.options.selectDisabled || this.selectDisabled;
      this.massUnlinkDisabled = this.options.massUnlinkDisabled || this.massUnlinkDisabled;
      this.massActionRemoveDisabled = this.options.massActionRemoveDisabled || this.massActionRemoveDisabled;
      this.massActionMassUpdateDisabled = this.options.massActionMassUpdateDisabled || this.massActionMassUpdateDisabled;
      this.panelCollection = this.options.panelCollection;
      if (this.options.searchPanelDisabled) {
        this.searchPanel = false;
      }
      if (this.panelCollection) {
        this.listenTo(this.panelCollection, 'sync', (c, r, o) => {
          if (o.skipCollectionSync) {
            return;
          }
          this.collection.fetch();
        });

        // Sync changing models.
        this.listenTo(this.panelCollection, 'change', (m, o) => {
          // Prevent change after save.
          if (o.xhr || !m.id) {
            return;
          }
          const model = this.collection.get(m.id);
          if (!model) {
            return;
          }
          const attributes = {};
          for (const name in m.attributes) {
            if (m.hasChanged(name)) {
              attributes[name] = m.attributes[name];
            }
          }
          model.set(attributes);
        });
        if (this.model) {
          this.listenTo(this.model, 'after:unrelate', () => {
            this.panelCollection.fetch({
              skipCollectionSync: true
            });
          });
        }
      } else if (this.model) {
        this.listenTo(this.model, 'after:relate', () => {
          this.collection.fetch();
        });
      }
      if (this.noCreateScopeList.indexOf(this.scope) !== -1) {
        this.createDisabled = true;
      }
      this.primaryFilterName = this.filter;
      if (!this.createDisabled) {
        if (!this.getAcl().check(this.scope, 'create') || this.getMetadata().get(['clientDefs', this.scope, 'createDisabled'])) {
          this.createDisabled = true;
        }
      }
      this.unlinkDisabled = this.unlinkDisabled || this.options.unlinkDisabled || this.defs.unlinkDisabled;
      if (!this.massUnlinkDisabled) {
        if (this.unlinkDisabled || this.defs.massUnlinkDisabled || this.defs.unlinkDisabled) {
          this.massUnlinkDisabled = true;
        }
        if (!this.getAcl().check(this.model, 'edit')) {
          this.massUnlinkDisabled = true;
        }
      }
      if (!this.selectDisabled) {
        this.buttonList.unshift({
          name: 'selectRelated',
          label: 'Select',
          pullLeft: true
        });
      }
      if (!this.createDisabled) {
        this.buttonList.unshift({
          name: 'createRelated',
          label: 'Create',
          pullLeft: true
        });
      }
      this.$header = (0, _jquery.default)('<span>');
      if (this.model) {
        if (this.model.get('name')) {
          this.$header.append((0, _jquery.default)('<span>').text(this.model.get('name')), ' <span class="chevron-right"></span> ');
        }
      }
      let title = this.options.title;
      if (title) {
        title = this.getHelper().escapeString(this.options.title).replace(/@right/, '<span class="chevron-right"></span>');
      }
      this.$header.append(title || (0, _jquery.default)('<span>').text(this.getLanguage().translate(this.link, 'links', this.entityType)));
      if (this.options.listViewUrl) {
        this.$header = (0, _jquery.default)('<a>').attr('href', this.options.listViewUrl).append(this.$header);
      }
      if (!this.options.listViewUrl && (!this.defs.fullFormDisabled && this.link && this.model.hasLink(this.link) || this.options.fullFormUrl)) {
        const url = this.options.fullFormUrl || '#' + this.model.entityType + '/related/' + this.model.id + '/' + this.link;
        this.buttonList.unshift({
          name: 'fullForm',
          label: 'Full Form',
          onClick: () => this.getRouter().navigate(url, {
            trigger: true
          })
        });
        this.$header = (0, _jquery.default)('<a>').attr('href', url).append(this.$header);
      }
      const iconHtml = this.getHelper().getScopeColorIconHtml(this.scope);
      if (iconHtml) {
        this.$header = (0, _jquery.default)('<span>').append(iconHtml).append(this.$header);
      }
      this.waitForView('list');
      if (this.searchPanel) {
        this.waitForView('search');
      }
      this.getCollectionFactory().create(this.scope, collection => {
        collection.maxSize = this.options.maxSize || this.getConfig().get('recordsPerPage');
        collection.url = this.url;
        collection.setOrder(this.defaultOrderBy, this.defaultOrder, true);
        collection.parentModel = this.model;
        this.collection = collection;
        if (this.panelCollection) {
          this.listenTo(collection, 'change', model => {
            const panelModel = this.panelCollection.get(model.id);
            if (panelModel) {
              panelModel.set(model.attributes);
            }
          });
          this.listenTo(collection, 'after:mass-remove', () => {
            this.panelCollection.fetch({
              skipCollectionSync: true
            });
          });
        }
        this.setupSearch();
        this.setupList();
      });

      // If the list not yet loaded.
      this.once('close', () => {
        if (this.collection.lastSyncPromise && this.collection.lastSyncPromise.getStatus() < 4) {
          Espo.Ui.notify(false);
        }
        this.collection.abortLastFetch();
      });
    }
    setFilter(filter) {
      this.searchManager.setPrimary(filter);
    }

    /**
     * @protected
     * @return {module:views/record/search}
     */
    getSearchView() {
      return this.getView('search');
    }

    /**
     * @protected
     * @return {module:views/record/list}
     */
    getRecordView() {
      return this.getView('list');
    }
    setupSearch() {
      this.searchManager = new _searchManager.default(this.collection, {
        emptyOnReset: true
      });
      const primaryFilterName = this.primaryFilterName;
      if (primaryFilterName) {
        this.searchManager.setPrimary(primaryFilterName);
      }
      this.collection.where = this.searchManager.getWhere();
      let filterList = Espo.Utils.clone(this.getMetadata().get(['clientDefs', this.scope, 'filterList']) || []);
      if (this.options.noDefaultFilters) {
        filterList = [];
      }
      if (this.filterList) {
        this.filterList.forEach(item1 => {
          let isFound = false;
          const name1 = item1.name || item1;
          if (!name1 || name1 === 'all') {
            return;
          }
          filterList.forEach(item2 => {
            const name2 = item2.name || item2;
            if (name1 === name2) {
              isFound = true;
            }
          });
          if (!isFound) {
            filterList.push(item1);
          }
        });
      }
      if (this.options.filtersDisabled) {
        filterList = [];
      }
      if (this.searchPanel) {
        this.createView('search', 'views/record/search', {
          collection: this.collection,
          fullSelector: this.containerSelector + ' .search-container',
          searchManager: this.searchManager,
          disableSavePreset: true,
          filterList: filterList,
          filtersLayoutName: this.options.filtersLayoutName
        }, view => {
          this.listenTo(view, 'reset', () => {});
        });
      }
    }
    setupList() {
      const viewName = this.listViewName || this.getMetadata().get(['clientDefs', this.scope, 'recordViews', 'listRelated']) || this.getMetadata().get(['clientDefs', this.scope, 'recordViews', 'list']) || 'views/record/list';

      // noinspection JSUnresolvedReference
      const rowActionList = this.defs.rowActionList;
      const promise = this.createView('list', viewName, {
        collection: this.collection,
        fullSelector: this.containerSelector + ' .list-container',
        rowActionsView: this.rowActionsView,
        listLayout: this.options.listLayout,
        layoutName: this.layoutName,
        searchManager: this.searchManager,
        buttonsDisabled: true,
        skipBuildRows: true,
        model: this.model,
        unlinkMassAction: !this.massUnlinkDisabled,
        massActionsDisabled: this.options.massActionsDisabled,
        massActionRemoveDisabled: this.massActionRemoveDisabled,
        massActionMassUpdateDisabled: this.massActionMassUpdateDisabled,
        mandatorySelectAttributeList: this.mandatorySelectAttributeList,
        additionalRowActionList: rowActionList,
        rowActionsOptions: {
          unlinkDisabled: this.unlinkDisabled,
          editDisabled: this.defs.editDisabled,
          removeDisabled: this.defs.removeDisabled
        },
        removeDisabled: this.defs.removeDisabled,
        forcePagination: this.options.forcePagination,
        pagination: this.getConfig().get('listPagination') || this.getMetadata().get(['clientDefs', this.scope, 'listPagination']) || null
      }, /** import('views/record/list').default */view => {
        this.listenTo(view, 'after:paginate', () => this.bodyElement.scrollTop = 0);
        this.listenTo(view, 'sort', () => this.bodyElement.scrollTop = 0);
        this.listenToOnce(view, 'select', model => {
          this.trigger('select', model);
          this.close();
        });
        if (this.multiple) {
          this.listenTo(view, 'check', () => {
            view.checkedList.length ? this.enableButton('select') : this.disableButton('select');
          });
          this.listenTo(view, 'select-all-results', () => this.enableButton('select'));
        }
        const fetch = () => {
          this.whenRendered().then(() => {
            Espo.Ui.notifyWait();
            this.collection.fetch().then(() => Espo.Ui.notify(false));
          });
          // Timeout to make notify work.
          /*setTimeout(() => {
              Espo.Ui.notifyWait();
                this.collection.fetch()
                  .then(() => Espo.Ui.notify(false));
          }, 1);*/
        };
        if (this.options.forceSelectAllAttributes || this.forceSelectAllAttributes) {
          fetch();
          return;
        }
        view.getSelectAttributeList(selectAttributeList => {
          if (!~selectAttributeList.indexOf('name')) {
            selectAttributeList.push('name');
          }
          const mandatorySelectAttributeList = this.options.mandatorySelectAttributeList || this.mandatorySelectAttributeList || [];
          mandatorySelectAttributeList.forEach(attribute => {
            if (!~selectAttributeList.indexOf(attribute)) {
              selectAttributeList.push(attribute);
            }
          });
          if (selectAttributeList) {
            this.collection.data.select = selectAttributeList.join(',');
          }
          fetch();
        });
      });
      this.wait(promise);
    }

    // noinspection JSUnusedGlobalSymbols
    actionUnlinkRelated(data) {
      const id = data.id;
      this.confirm({
        message: this.translate('unlinkRecordConfirmation', 'messages'),
        confirmText: this.translate('Unlink')
      }, () => {
        Espo.Ui.notifyWait();
        Espo.Ajax.deleteRequest(this.collection.url, {
          id: id
        }).then(() => {
          Espo.Ui.success(this.translate('Unlinked'));
          this.collection.fetch();
          this.model.trigger('after:unrelate');
          this.model.trigger('after:unrelate:' + this.link);
        });
      });
    }

    /**
     * @private
     */
    actionCreateRelated() {
      // noinspection JSUnresolvedReference
      const actionName = this.defs.createAction || 'createRelated';
      if (actionName === 'createRelated') {
        const helper = new _createRelated.default(this);
        helper.process(this.model, this.link);
        return;
      }
      const methodName = 'action' + Espo.Utils.upperCaseFirst(actionName);
      let p = this.getParentView();
      let view = null;
      while (p) {
        if (p[methodName]) {
          view = p;
          break;
        }
        p = p.getParentView();
      }
      p[methodName]({
        link: this.link,
        scope: this.scope
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionSelectRelated() {
      // noinspection JSUnresolvedReference
      const actionName = this.defs.selectAction || 'selectRelated';
      if (actionName === 'selectRelated') {
        const helper = new _selectRelated.default(this);
        helper.process(this.model, this.link);
        return;
      }
      const methodName = 'action' + Espo.Utils.upperCaseFirst(actionName);
      let p = this.getParentView();
      let view = null;
      while (p) {
        if (p[methodName]) {
          view = p;
          break;
        }
        p = p.getParentView();
      }
      p[methodName]({
        link: this.link,
        primaryFilterName: this.defs.selectPrimaryFilterName,
        boolFilterList: this.defs.selectBoolFilterList,
        massSelect: this.defs.massSelect
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionRemoveRelated(data) {
      const id = data.id;
      this.confirm({
        message: this.translate('removeRecordConfirmation', 'messages'),
        confirmText: this.translate('Remove')
      }, () => {
        const model = this.collection.get(id);
        Espo.Ui.notifyWait();
        model.destroy().then(() => {
          Espo.Ui.success(this.translate('Removed'));
          this.collection.fetch();
          this.model.trigger('after:unrelate');
          this.model.trigger('after:unrelate:' + this.link);
        });
      });
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlSlash(e) {
      if (!this.searchPanel) {
        return;
      }
      const $search = this.$el.find('input.text-filter').first();
      if (!$search.length) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      $search.focus();
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlSpace(e) {
      if (this.createDisabled) {
        return;
      }
      if (this.buttonList.findIndex(item => item.name === 'createRelated' && !item.hidden) === -1) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      this.actionCreateRelated();
    }

    /**
     * @protected
     */
    handleShortcutKeyCtrlComma() {
      if (!this.getSearchView()) {
        return;
      }
      this.getSearchView().selectPreviousPreset();
    }

    /**
     * @protected
     */
    handleShortcutKeyCtrlPeriod() {
      if (!this.getSearchView()) {
        return;
      }
      this.getSearchView().selectNextPreset();
    }

    /**
     * @protected
     */
    handleShortcutKeyControlArrowLeft() {
      this.getRecordView().trigger('request-page', 'previous');
    }

    /**
     * @protected
     */
    handleShortcutKeyControlArrowRight() {
      this.getRecordView().trigger('request-page', 'next');
    }
  }
  var _default = _exports.default = RelatedListModalView;
});

define("views/modals/mass-update", ["exports", "views/modal", "helpers/mass-action", "ui/select"], function (_exports, _modal, _massAction, _select) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _massAction = _interopRequireDefault(_massAction);
  _select = _interopRequireDefault(_select);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class MassUpdateModalView extends _modal.default {
    template = 'modals/mass-update';
    cssName = 'mass-update';
    className = 'dialog dialog-record';
    layoutName = 'massUpdate';
    ACTION_UPDATE = 'update';

    /**
     * @type {string[]}
     */
    fieldList;
    data() {
      return {
        scope: this.scope,
        fieldList: this.fieldList,
        entityType: this.entityType
      };
    }
    setup() {
      this.addActionHandler('addField', (e, target) => this.addField(target.dataset.name));
      this.addActionHandler('reset', () => this.reset());
      this.buttonList = [{
        name: 'update',
        label: 'Update',
        style: 'danger',
        disabled: true,
        onClick: () => this.actionUpdate()
      }, {
        name: 'cancel',
        label: 'Cancel'
      }];
      this.entityType = this.options.entityType || this.options.scope;
      this.scope = this.options.scope || this.entityType;
      this.ids = this.options.ids;
      this.where = this.options.where;
      this.searchParams = this.options.searchParams;
      this.byWhere = this.options.byWhere;
      this.hasActionMap = {};
      const totalCount = this.options.totalCount;
      this.helper = new _massAction.default(this);
      this.idle = this.byWhere && this.helper.checkIsIdle(totalCount);
      this.headerText = this.translate('Mass Update') + ' · ' + this.translate(this.scope, 'scopeNamesPlural');
      this.wait(this.load());

      /** @type {string[]} */
      this.addedFieldList = [];
    }

    /**
     * @private
     */
    async load() {
      this.model = await this.getModelFactory().create(this.entityType);
      const forbiddenList = this.getAcl().getScopeForbiddenFieldList(this.entityType, 'edit') || [];
      return new Promise(resolve => {
        this.getHelper().layoutManager.get(this.entityType, this.layoutName, layout => {
          layout = layout || [];
          this.fieldList = [];
          layout.forEach(field => {
            if (forbiddenList.includes(field)) {
              return;
            }
            if (this.model.hasField(field)) {
              this.fieldList.push(field);
            }
          });
          resolve();
        });
      });
    }

    /**
     * @private
     * @param {string} name
     */
    addField(name) {
      this.$el.find('[data-action="reset"]').removeClass('hidden');
      this.$el.find(`ul.filter-list li[data-name="${name}"]`).addClass('hidden');
      if (this.$el.find('ul.filter-list li:not(.hidden)').length === 0) {
        this.$el.find('button.select-field').addClass('disabled').attr('disabled', 'disabled');
      }
      this.addedFieldList.push(name);
      const label = this.getHelper().escapeString(this.translate(name, 'fields', this.entityType));
      const $cell = $('<div>').addClass('cell form-group').attr('data-name', name).append($('<label>').addClass('control-label').text(label)).append($('<div>').addClass('field').attr('data-name', name));
      const $row = $('<div>').addClass('item grid-auto-fill-md').attr('data-name', name).append($cell);
      this.$el.find('.fields-container').append($row);
      const type = this.model.getFieldType(name);
      const viewName = this.model.getFieldParam(name, 'view') || this.getFieldManager().getViewName(type);
      const actionList = this.getMetadata().get(['entityDefs', this.entityType, name, 'massUpdateActionList']) || this.getMetadata().get(['fields', type, 'massUpdateActionList']);
      const hasActionDropdown = actionList !== null;
      this.hasActionMap[name] = hasActionDropdown;
      this.disableButton('update');
      this.createView(name, viewName, {
        model: this.model,
        selector: '.field[data-name="' + name + '"]',
        defs: {
          name: name
        },
        mode: 'edit'
      }, view => {
        this.enableButton('update');
        view.render();
      });
      if (hasActionDropdown) {
        const $select = $('<select>').addClass('item-action form-control').attr('data-name', name);
        actionList.forEach(action => {
          const label = this.translate(Espo.Utils.upperCaseFirst(action));
          $select.append($('<option>').text(label).val(action));
        });
        const $cellAction = $('<div>').addClass('cell call-action form-group').attr('data-name', name).append($('<label>').addClass('control-label hidden-xs').html('&nbsp;')).append($('<div>').addClass('field').attr('data-name', name).append($select));
        $row.append($cellAction);
        _select.default.init($select.get(0));
      }
    }

    /**
     * @param {string} field
     * @return {module:views/fields/base}
     */
    getFieldView(field) {
      return this.getView(field);
    }

    /**
     * @private
     * @return {Promise<void>}
     */
    async actionUpdate() {
      if (this.byWhere) {
        await this.confirm(this.translate('confirmMassUpdate', 'messages'));
      }
      this.disableButton('update');
      const attributes = {};
      const actions = {};
      this.addedFieldList.forEach(field => {
        const action = this.fetchAction(field);
        const itemAttributes = this.getFieldView(field).fetch();
        const itemActualAttributes = {};
        this.getFieldManager().getEntityTypeFieldActualAttributeList(this.entityType, field).forEach(attribute => {
          actions[attribute] = action;
          itemActualAttributes[attribute] = itemAttributes[attribute];
        });
        Object.assign(attributes, itemActualAttributes);
      });
      this.model.set(attributes);
      let notValid = false;
      this.addedFieldList.forEach(field => {
        const view = this.getFieldView(field);
        notValid = view.validate() || notValid;
      });
      if (notValid) {
        Espo.Ui.error(this.translate('Not valid'));
        this.enableButton('update');
        return;
      }
      Espo.Ui.notify(this.translate('saving', 'messages'));
      let result;
      try {
        result = await Espo.Ajax.postRequest('MassAction', {
          action: 'update',
          entityType: this.entityType,
          params: {
            ids: this.ids || null,
            where: !this.ids || this.ids.length === 0 ? this.options.where : null,
            searchParams: !this.ids || this.ids.length === 0 ? this.options.searchParams : null
          },
          data: {
            values: attributes,
            actions: actions
          },
          idle: this.idle
        });
      } catch (e) {
        this.enableButton('update');
        return;
      }
      if (result.id) {
        const view = await this.helper.process(result.id, 'update');
        this.listenToOnce(view, 'close', () => this.close());
        this.listenToOnce(view, 'success', result => {
          this.trigger('after:update', {
            count: result.count,
            idle: true
          });
        });
        return;
      }
      this.trigger('after:update', {
        count: result.count
      });
    }

    /**
     * @private
     * @param {string} name
     * @return {string}
     */
    fetchAction(name) {
      if (!this.hasActionMap[name]) {
        return this.ACTION_UPDATE;
      }
      const $dropdown = this.$el.find(`select.item-action[data-name="${name}"]`);
      return $dropdown.val() || this.ACTION_UPDATE;
    }

    /**
     * @private
     */
    reset() {
      this.addedFieldList.forEach(field => {
        this.clearView(field);
        this.$el.find(`.item[data-name="${field}"]`).remove();
      });
      this.addedFieldList = [];
      this.hasActionMap = {};
      this.model.clear();
      this.$el.find('[data-action="reset"]').addClass('hidden');
      this.$el.find('button.select-field').removeClass('disabled').removeAttr('disabled');
      this.$el.find('ul.filter-list').find('li').removeClass('hidden');
      this.disableButton('update');
    }
  }
  var _default = _exports.default = MassUpdateModalView;
});

define("views/modals/mass-convert-currency", ["exports", "views/modal", "model", "helpers/mass-action"], function (_exports, _modal, _model, _massAction) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  _massAction = _interopRequireDefault(_massAction);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class MassConvertCurrencyModalView extends _modal.default {
    template = 'modals/mass-convert-currency';
    className = 'dialog dialog-record';
    buttonList = [{
      name: 'cancel',
      label: 'Cancel'
    }];
    data() {
      return {};
    }
    setup() {
      this.$header = $('<span>').append($('<span>').text(this.translate(this.options.entityType, 'scopeNamesPlural')), ' <span class="chevron-right"></span> ', $('<span>').text(this.translate('convertCurrency', 'massActions')));
      this.addButton({
        name: 'convert',
        text: this.translate('Update'),
        style: 'danger'
      }, true);
      const model = this.model = new _model.default();
      model.set('currency', this.getConfig().get('defaultCurrency'));
      model.set('baseCurrency', this.getConfig().get('baseCurrency'));
      model.set('currencyRates', this.getConfig().get('currencyRates'));
      model.set('currencyList', this.getConfig().get('currencyList'));
      this.createView('currency', 'views/fields/enum', {
        model: model,
        params: {
          options: this.getConfig().get('currencyList')
        },
        name: 'currency',
        selector: '.field[data-name="currency"]',
        mode: 'edit',
        labelText: this.translate('Convert to')
      });
      this.createView('baseCurrency', 'views/fields/enum', {
        model: model,
        params: {
          options: this.getConfig().get('currencyList')
        },
        name: 'baseCurrency',
        selector: '.field[data-name="baseCurrency"]',
        mode: 'detail',
        labelText: this.translate('baseCurrency', 'fields', 'Settings'),
        readOnly: true
      });
      this.createView('currencyRates', 'views/settings/fields/currency-rates', {
        model: model,
        name: 'currencyRates',
        selector: '.field[data-name="currencyRates"]',
        mode: 'edit',
        labelText: this.translate('currencyRates', 'fields', 'Settings')
      });
    }

    /**
     * @param {string} field
     * @return {module:views/fields/base}
     */
    getFieldView(field) {
      return this.getView(field);
    }

    // noinspection JSUnusedGlobalSymbols
    actionConvert() {
      this.disableButton('convert');
      this.getFieldView('currency').fetchToModel();
      this.getFieldView('currencyRates').fetchToModel();
      const currency = this.model.get('currency');
      const currencyRates = this.model.get('currencyRates');
      const hasWhere = !this.options.ids || this.options.ids.length === 0;
      const helper = new _massAction.default(this);
      const idle = hasWhere && helper.checkIsIdle(this.options.totalCount);
      Espo.Ajax.postRequest('MassAction', {
        entityType: this.options.entityType,
        action: 'convertCurrency',
        params: {
          ids: this.options.ids || null,
          where: hasWhere ? this.options.where : null,
          searchParams: hasWhere ? this.options.searchParams : null
        },
        data: {
          fieldList: this.options.fieldList || null,
          currency: currency,
          targetCurrency: currency,
          rates: currencyRates
        },
        idle: idle
      }).then(result => {
        if (result.id) {
          helper.process(result.id, 'convertCurrency').then(view => {
            this.listenToOnce(view, 'close', () => this.close());
            this.listenToOnce(view, 'success', result => {
              this.trigger('after:update', {
                count: result.count,
                idle: true
              });
            });
          });
          return;
        }
        this.trigger('after:update', {
          count: result.count
        });
        this.close();
      }).catch(() => {
        this.enableButton('convert');
      });
    }
  }
  var _default = _exports.default = MassConvertCurrencyModalView;
});

define("views/modals/detail", ["exports", "views/modal", "helpers/action-item-setup", "backbone", "helpers/record-modal"], function (_exports, _modal, _actionItemSetup, _backbone, _recordModal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _actionItemSetup = _interopRequireDefault(_actionItemSetup);
  _backbone = _interopRequireDefault(_backbone);
  _recordModal = _interopRequireDefault(_recordModal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/modals/detail */

  /**
   * A quick view modal.
   */
  class DetailModalView extends _modal.default {
    template = 'modals/detail';
    cssName = 'detail-modal';
    className = 'dialog dialog-record';
    editDisabled = false;
    fullFormDisabled = false;
    detailView = null;
    removeDisabled = true;
    backdrop = true;
    fitHeight = true;
    sideDisabled = false;
    bottomDisabled = false;
    fixedHeaderHeight = true;
    flexibleHeaderFontSize = true;
    duplicateAction = false;

    /**
     * @private
     * @type {string}
     */
    nameAttribute;
    shortcutKeys = {
      /** @this DetailModalView */
      'Control+Space': function (e) {
        if (this.editDisabled) {
          return;
        }
        if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT') {
          return;
        }
        if (this.buttonList.findIndex(item => item.name === 'edit') === -1) {
          return;
        }
        e.stopPropagation();
        e.preventDefault();
        this.actionEdit().then(view => {
          view.$el.find('.form-control:not([disabled])').first().focus();
        });
      },
      /** @this DetailModalView */
      'Control+Backslash': function (e) {
        this.getRecordView().handleShortcutKeyControlBackslash(e);
      },
      /** @this DetailModalView */
      'Control+ArrowLeft': function (e) {
        this.handleShortcutKeyControlArrowLeft(e);
      },
      /** @this DetailModalView */
      'Control+ArrowRight': function (e) {
        this.handleShortcutKeyControlArrowRight(e);
      }
    };

    /**
     * @typedef {Record} module:views/modals/detail~options
     *
     * @property {string} entityType An entity type.
     * @property {string} [id] An ID.
     * @property {string} [layoutName] A layout name.
     * @property {import('view-record-helper')} [recordHelper] A record helper.
     * @property {boolean} [editDisabled] Disable edit.
     * @property {boolean} [removeDisabled] Disable remove.
     * @property {boolean} [fullFormDisabled] Disable full-form.
     * @property {boolean} [quickEditDisabled] Disable quick edit.
     * @property {string} [rootUrl] A root URL.
     * @property {string} [fullFormUrl] A full-form URL. As of v9.0.
     */

    /**
     * @param {module:views/modals/detail~options} options
     */
    constructor(options) {
      super(options);
    }
    setup() {
      this.scope = this.scope || this.options.scope || this.options.entityType;
      this.entityType = this.options.entityType || this.scope;
      this.id = this.options.id;
      this.buttonList = [];
      if ('editDisabled' in this.options) {
        this.editDisabled = this.options.editDisabled;
      }
      if (this.options.removeDisabled !== undefined) {
        this.removeDisabled = this.options.removeDisabled;
      }
      this.editDisabled = this.getMetadata().get(['clientDefs', this.entityType, 'editDisabled']) || this.editDisabled;
      this.removeDisabled = this.getMetadata().get(['clientDefs', this.entityType, 'removeDisabled']) || this.removeDisabled;
      this.nameAttribute = this.getMetadata().get(`clientDefs.${this.entityType}.nameAttribute`) || 'name';
      this.fullFormDisabled = this.options.fullFormDisabled || this.fullFormDisabled;
      this.layoutName = this.options.layoutName || this.layoutName;
      this.setupRecordButtons();
      if (this.model) {
        this.controlRecordButtonsVisibility();
      }
      if (!this.fullFormDisabled) {
        this.buttonList.push({
          name: 'fullForm',
          label: 'Full Form'
        });
      }
      this.buttonList.push({
        name: 'cancel',
        label: 'Close',
        title: 'Esc'
      });
      if (this.model && this.model.collection && !this.navigateButtonsDisabled) {
        this.buttonList.push({
          name: 'previous',
          html: '<span class="fas fa-chevron-left"></span>',
          title: this.translate('Previous Entry'),
          position: 'right',
          className: 'btn-icon',
          style: 'text',
          disabled: true
        });
        this.buttonList.push({
          name: 'next',
          html: '<span class="fas fa-chevron-right"></span>',
          title: this.translate('Next Entry'),
          position: 'right',
          className: 'btn-icon',
          style: 'text',
          disabled: true
        });
        this.indexOfRecord = this.model.collection.indexOf(this.model);
      } else {
        this.navigateButtonsDisabled = true;
      }
      this.waitForView('record');
      this.sourceModel = this.model;
      this.getModelFactory().create(this.entityType).then(model => {
        if (!this.sourceModel) {
          this.model = model;
          this.model.id = this.id;
          this.setupAfterModelCreated();
          this.listenTo(this.model, 'sync', () => {
            this.controlRecordButtonsVisibility();
            this.trigger('model-sync');
          });
          this.listenToOnce(this.model, 'sync', () => {
            this.setupActionItems();
            this.createRecordView();
          });
          this.model.fetch();
          return;
        }
        this.model = this.sourceModel.clone();
        this.model.collection = this.sourceModel.collection.clone();
        this.setupAfterModelCreated();
        this.listenTo(this.model, 'change', () => {
          this.sourceModel.set(this.model.getClonedAttributes());
        });
        this.listenTo(this.model, 'sync', () => {
          this.controlRecordButtonsVisibility();
          this.trigger('model-sync');
        });
        this.once('after:render', () => {
          this.model.fetch();
        });
        this.setupActionItems();
        this.createRecordView();
      });
      this.listenToOnce(this.getRouter(), 'routed', () => {
        this.remove();
      });
      if (this.duplicateAction && this.getAcl().checkScope(this.entityType, 'create')) {
        this.addDropdownItem({
          name: 'duplicate',
          label: 'Duplicate',
          groupIndex: 0
        });
      }
    }

    /** @private */
    setupActionItems() {
      const actionItemSetup = new _actionItemSetup.default();
      actionItemSetup.setup(this, 'modalDetail', promise => this.wait(promise), item => this.addDropdownItem(item), name => this.showActionItem(name), name => this.hideActionItem(name), {
        listenToViewModelSync: true
      });
    }

    /**
     * @protected
     */
    setupAfterModelCreated() {}

    /**
     * @protected
     */
    setupRecordButtons() {
      if (!this.removeDisabled) {
        this.addRemoveButton();
      }
      if (!this.editDisabled) {
        this.addEditButton();
      }
    }

    /**
     * @protected
     */
    controlRecordButtonsVisibility() {
      if (this.getAcl().check(this.model, 'edit')) {
        this.showButton('edit');
      } else {
        this.hideButton('edit');
      }
      if (this.getAcl().check(this.model, 'delete')) {
        this.showActionItem('remove');
      } else {
        this.hideActionItem('remove');
      }
    }
    addEditButton() {
      this.addButton({
        name: 'edit',
        label: 'Edit',
        title: 'Ctrl+Space'
      }, true);
    }

    // noinspection JSUnusedGlobalSymbols
    removeEditButton() {
      this.removeButton('edit');
    }
    addRemoveButton() {
      this.addDropdownItem({
        name: 'remove',
        label: 'Remove',
        groupIndex: 0
      });
    }

    // noinspection JSUnusedGlobalSymbols
    removeRemoveButton() {
      this.removeButton('remove');
    }

    /**
     * @internal Used. Do not remove.
     */
    getScope() {
      return this.scope;
    }
    createRecordView(callback) {
      const model = this.model;
      const scope = this.getScope();
      this.headerHtml = '';
      this.headerHtml += $('<span>').text(this.getLanguage().translate(scope, 'scopeNames')).get(0).outerHTML;
      if (model.attributes[this.nameAttribute]) {
        this.headerHtml += ' ' + $('<span>').addClass('chevron-right').get(0).outerHTML;
        this.headerHtml += ' ' + $('<span>').text(model.attributes[this.nameAttribute]).get(0).outerHTML;
      }
      if (!this.fullFormDisabled) {
        const url = this.options.fullFormUrl || `#${scope}/view/${this.id}`;
        this.headerHtml = $('<a>').attr('href', url).addClass('action font-size-flexible').attr('title', this.translate('Full Form')).attr('data-action', 'fullForm').append(this.headerHtml).get(0).outerHTML;
      }
      this.headerHtml = this.getHelper().getScopeColorIconHtml(this.entityType) + this.headerHtml;
      if (!this.editDisabled) {
        const editAccess = this.getAcl().check(model, 'edit', true);
        if (editAccess) {
          this.showButton('edit');
        } else {
          this.hideButton('edit');
          if (editAccess === null) {
            this.listenToOnce(model, 'sync', () => {
              if (this.getAcl().check(model, 'edit')) {
                this.showButton('edit');
              }
            });
          }
        }
      }
      if (!this.removeDisabled) {
        const removeAccess = this.getAcl().check(model, 'delete', true);
        if (removeAccess) {
          this.showActionItem('remove');
        } else {
          this.hideActionItem('remove');
          if (removeAccess === null) {
            this.listenToOnce(model, 'sync', () => {
              if (this.getAcl().check(model, 'delete')) {
                this.showActionItem('remove');
              }
            });
          }
        }
      }
      const viewName = this.detailView || this.getMetadata().get(['clientDefs', model.entityType, 'recordViews', 'detailSmall']) || this.getMetadata().get(['clientDefs', model.entityType, 'recordViews', 'detailQuick']) || 'views/record/detail-small';
      const options = {
        model: model,
        fullSelector: this.containerSelector + ' .record-container',
        type: 'detailSmall',
        layoutName: this.layoutName || 'detailSmall',
        buttonsDisabled: true,
        inlineEditDisabled: true,
        sideDisabled: this.sideDisabled,
        bottomDisabled: this.bottomDisabled,
        recordHelper: this.options.recordHelper,
        exit: function () {}
      };
      this.createView('record', viewName, options, callback);
    }

    /**
     * @return {module:views/record/detail}
     */
    getRecordView() {
      return this.getView('record');
    }
    afterRender() {
      super.afterRender();
      setTimeout(() => {
        this.$el.children().first().scrollTop(0);
      }, 50);
      if (!this.navigateButtonsDisabled) {
        this.controlNavigationButtons();
      }
    }
    controlNavigationButtons() {
      const recordView = this.getRecordView();
      if (!recordView) {
        return;
      }
      const collection = this.model.collection;
      const indexOfRecord = this.indexOfRecord;
      let previousButtonEnabled = false;
      let nextButtonEnabled = false;
      if (indexOfRecord > 0 || collection.offset > 0) {
        previousButtonEnabled = true;
      }
      if (indexOfRecord < collection.total - 1 - collection.offset) {
        nextButtonEnabled = true;
      } else if (collection.total === -1) {
        nextButtonEnabled = true;
      } else if (collection.total === -2 && indexOfRecord < collection.length - 1 - collection.offset) {
        nextButtonEnabled = true;
      }
      if (previousButtonEnabled) {
        this.enableButton('previous');
      } else {
        this.disableButton('previous');
      }
      if (nextButtonEnabled) {
        this.enableButton('next');
      } else {
        this.disableButton('next');
      }
    }
    switchToModelByIndex(indexOfRecord) {
      if (!this.model.collection) {
        return;
      }
      const previousModel = this.model;
      this.sourceModel = this.model.collection.at(indexOfRecord);
      if (!this.sourceModel) {
        throw new Error("Model is not found in collection by index.");
      }
      this.indexOfRecord = indexOfRecord;
      this.id = this.sourceModel.id;
      this.scope = this.sourceModel.entityType;
      this.model = this.sourceModel.clone();
      this.model.collection = this.sourceModel.collection.clone();
      this.stopListening(previousModel, 'change');
      this.stopListening(previousModel, 'sync');
      this.listenTo(this.model, 'change', () => {
        this.sourceModel.set(this.model.getClonedAttributes());
      });
      this.listenTo(this.model, 'sync', () => {
        this.controlRecordButtonsVisibility();
        this.trigger('model-sync');
      });
      this.createRecordView(() => {
        this.reRender().then(() => {
          this.model.fetch();
        });
      });
      this.controlNavigationButtons();
      this.trigger('switch-model', this.model, previousModel);
    }
    actionPrevious() {
      if (!this.model.collection) {
        return;
      }
      const collection = this.model.collection;
      if (this.indexOfRecord <= 0 && !collection.offset) {
        return;
      }
      if (this.indexOfRecord === 0 && collection.offset > 0 && collection.maxSize) {
        collection.offset = Math.max(0, collection.offset - collection.maxSize);
        collection.fetch().then(() => {
          const indexOfRecord = collection.length - 1;
          if (indexOfRecord < 0) {
            return;
          }
          this.switchToModelByIndex(indexOfRecord);
        });
        return;
      }
      const indexOfRecord = this.indexOfRecord - 1;
      this.switchToModelByIndex(indexOfRecord);
    }
    actionNext() {
      if (!this.model.collection) {
        return;
      }
      const collection = this.model.collection;
      if (!(this.indexOfRecord < collection.total - 1 - collection.offset) && this.model.collection.total >= 0) {
        return;
      }
      if (collection.total === -2 && this.indexOfRecord >= collection.length - 1 - collection.offset) {
        return;
      }
      const indexOfRecord = this.indexOfRecord + 1;
      if (indexOfRecord <= collection.length - 1 - collection.offset) {
        this.switchToModelByIndex(indexOfRecord);
        return;
      }
      collection.fetch({
        more: true,
        remove: false
      }).then(() => {
        this.switchToModelByIndex(indexOfRecord);
      });
    }

    /**
     * @return {Promise<import('views/modals/edit').default>}
     */
    async actionEdit() {
      if (this.options.quickEditDisabled) {
        const options = {
          id: this.id,
          model: this.model,
          returnUrl: this.getRouter().getCurrentUrl()
        };
        if (this.options.rootUrl) {
          options.rootUrl = this.options.rootUrl;
        }
        this.getRouter().navigate(`#${this.scope}/edit/${this.id}`, {
          trigger: false
        });
        this.getRouter().dispatch(this.scope, 'edit', options);
        return Promise.reject();
      }
      const helper = new _recordModal.default();

      // noinspection UnnecessaryLocalVariableJS
      const modalView = await helper.showEdit(this, {
        entityType: this.entityType,
        id: this.id,
        fullFormDisabled: this.fullFormDisabled,
        collapseDisabled: true,
        afterSave: (model, o) => {
          this.model.set(model.getClonedAttributes());
          this.trigger('after:save', model, o);
          this.controlRecordButtonsVisibility();
          this.trigger('model-sync');

          // Triggers stream panel update.
          this.model.trigger('sync', this.model, null, {});
        },
        beforeRender: view => {
          this.listenToOnce(view, 'remove', () => this.dialog.show());
          this.listenToOnce(view, 'leave', () => this.remove());
        }
      });

      // Not to be hidden as it interferes with the collapsible modal.
      //this.dialog.hide();

      return modalView;
    }

    // noinspection JSUnusedGlobalSymbols
    actionRemove() {
      const model = this.getRecordView().model;
      this.confirm(this.translate('removeRecordConfirmation', 'messages'), () => {
        const $buttons = this.dialog.$el.find('.modal-footer button');
        $buttons.addClass('disabled').attr('disabled', 'disabled');
        model.destroy().then(() => {
          this.trigger('after:destroy', model);
          this.dialog.close();
          Espo.Ui.success(this.translate('Removed'));
        }).catch(() => {
          $buttons.removeClass('disabled').removeAttr('disabled');
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionFullForm() {
      let url;
      const router = this.getRouter();
      const scope = this.getScope();
      url = '#' + scope + '/view/' + this.id;
      let attributes = this.getRecordView().fetch();
      const model = this.getRecordView().model;
      attributes = _.extend(attributes, model.getClonedAttributes());
      const options = {
        attributes: attributes,
        returnUrl: _backbone.default.history.fragment,
        model: this.sourceModel || this.model,
        id: this.id
      };
      if (this.options.rootUrl) {
        options.rootUrl = this.options.rootUrl;
      }
      setTimeout(() => {
        router.dispatch(scope, 'view', options);
        router.navigate(url, {
          trigger: false
        });
      }, 10);
      this.trigger('leave');
      this.dialog.close();
    }

    // noinspection JSUnusedGlobalSymbols
    actionDuplicate() {
      Espo.Ui.notifyWait();
      Espo.Ajax.postRequest(this.scope + '/action/getDuplicateAttributes', {
        id: this.model.id
      }).then(attributes => {
        Espo.Ui.notify(false);
        const url = '#' + this.scope + '/create';
        this.getRouter().dispatch(this.scope, 'create', {
          attributes: attributes,
          returnUrl: this.getRouter().getCurrentUrl(),
          options: {
            duplicateSourceId: this.model.id,
            returnAfterCreate: true
          }
        });
        this.getRouter().navigate(url, {
          trigger: false
        });
      });
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyControlArrowLeft(e) {
      if (!this.model.collection) {
        return;
      }
      if (this.buttonList.findIndex(item => item.name === 'previous' && !item.disabled) === -1) {
        return;
      }
      if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT') {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      this.actionPrevious();
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyControlArrowRight(e) {
      if (!this.model.collection) {
        return;
      }
      if (this.buttonList.findIndex(item => item.name === 'next' && !item.disabled) === -1) {
        return;
      }
      if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT') {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      this.actionNext();
    }
  }
  var _default = _exports.default = DetailModalView;
});

define("views/lead-capture/form", ["exports", "view", "views/record/edit", "model"], function (_exports, _view, _edit, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _edit = _interopRequireDefault(_edit);
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class LeadCaptureFormView extends _view.default {
    // language=Handlebars
    templateContent = `
        <div class="block-center-5 margin-top">
            {{#if isPosted}}
                <div class="panel paned-default">
                    <div class="panel-body">
                        <div class="complex-text">{{complexText successText}}</div>
                    </div>
                </div>
            {{else}}
                <div class="record">{{{record}}}</div>
                <div class="button-container center-align">
                    <button
                        class="btn btn-primary btn-x-wide"
                        data-action="submit"
                    >{{translate 'Submit'}}</button>
                </div>
            {{/if}}
        </div>
    `;

    /**
     * @private
     * @type {boolean}
     */
    isPosted = false;

    /**
     * @private
     * @type {boolean}
     */
    isPosting = false;

    /**
     * @private
     * @type {HTMLElement}
     */
    submitButtonElement;

    /**
     * @param {{
     *     formData: {
     *         requestUrl: string,
     *         detailLayout: module:views/record/detail~panelDefs[],
     *         fieldDefs: Record,
     *         metadata: Record,
     *         language: Record.<string, Record>,
     *         successText: string,
     *         text: string|null,
     *         config: Record,
     *         appParams: Record,
     *         captchaKey: boolean,
     *         isDark: boolean,
     *         title: string|null,
     *     },
     * }} options
     */
    constructor(options) {
      super();
      this.formData = options.formData;
    }
    data() {
      return {
        isPosted: this.isPosted,
        successText: this.formData.successText
      };
    }
    setup() {
      this.getMetadata().setData(this.formData.metadata);
      this.getConfig().setMultiple(this.formData.config);
      this.getHelper().appParams.setAll(this.formData.appParams);
      this.getHelper().fieldManager.defs = this.getMetadata().get('fields');
      this.getDateTime().setSettingsAndPreferences(this.getConfig(), this.getPreferences());
      if (this.formData.captchaKey) {
        // noinspection JSUnresolvedReference
        grecaptcha.ready(() => {
          // noinspection SpellCheckingInspection
          /** @type {HTMLElement|null} */
          const badge = document.querySelector('.grecaptcha-badge');
          if (badge) {
            badge.style.zIndex = '4';
          }
        });
      }
      for (const it in this.formData.language) {
        this.getLanguage().setScopeData(it, this.formData.language[it]);
      }
      this.model = new _model.default({}, {
        defs: {
          fields: this.formData.fieldDefs
        },
        entityType: 'Lead'
      });
      this.model.url = this.formData.requestUrl;
      const detailLayout = [...(this.formData.text ? [{
        noteText: this.formData.text,
        noteStyle: 'default',
        rows: []
      }] : []), ...this.formData.detailLayout];
      this.recordView = new CustomEditView({
        model: this.model,
        detailLayout: detailLayout,
        sideView: null,
        bottomView: null,
        isWide: true,
        shortcutKeysEnabled: true,
        buttonsDisabled: true
      });
      this.assignView('record', this.recordView, '.record');
      this.whenReady().then(() => this.initAutocomplete());
      this.listenTo(this.recordView, 'save', () => this.actionCreate());
      this.addActionHandler('submit', () => this.actionCreate());
    }
    afterRender() {
      const subContainer = this.element.querySelector('.detail-button-container .sub-container');
      if (subContainer) {
        subContainer.classList.add('sub-container-center-5');
      }
      this.submitButtonElement = this.element.querySelector('button[data-action="submit"]');
    }
    async actionCreate() {
      if (this.isPosting) {
        return;
      }
      if (this.recordView.validate()) {
        Espo.Ui.error(this.translate('Not valid'));
        return;
      }
      this.isPosting = true;
      this.recordView.disableActionItems();
      this.submitButtonElement.classList.add('disabled');
      this.submitButtonElement.setAttribute('disabled', 'disabled');
      Espo.Ui.notifyWait();
      const token = await this.processCaptcha();
      const headers = token ? {
        'X-Captcha-Token': token
      } : undefined;

      /** @type {{redirectUrl: string|null}} */
      let result;
      try {
        result = await Espo.Ajax.postRequest(this.model.url, this.model.attributes, {
          headers: headers
        });
      } catch (e) {
        this.recordView.enableActionItems();
        this.submitButtonElement.classList.remove('disabled');
        this.submitButtonElement.removeAttribute('disabled');
        this.isPosting = false;
        return;
      }
      Espo.Ui.notify();
      this.isPosted = true;
      this.isPosting = false;
      this.recordView.remove();
      await this.reRender();
      if (result.redirectUrl) {
        document.location.href = result.redirectUrl;
      }
    }

    /**
     * @return {Promise<string|null>}
     */
    async processCaptcha() {
      if (!this.formData.captchaKey) {
        return null;
      }
      return new Promise(resolve => {
        // noinspection JSUnresolvedReference
        grecaptcha.ready(async () => {
          // noinspection JSUnresolvedReference
          const token = await grecaptcha.execute(this.formData.captchaKey, {
            action: 'leadCaptureSubmit'
          });
          resolve(token);
        });
      });
    }

    /**
     * @private
     */
    initAutocomplete() {
      const emailAddressView = this.recordView.getFieldView('emailAddress');
      if (emailAddressView) {
        this.listenTo(emailAddressView, 'after:render', () => {
          /** @type {HTMLInputElement} */
          const element = emailAddressView.element.querySelector('input');
          if (element) {
            element.autocomplete = 'email';
          }
        });
      }
      const phoneNumberView = this.recordView.getFieldView('phoneNumber');
      if (phoneNumberView) {
        this.listenTo(phoneNumberView, 'after:render', () => {
          /** @type {HTMLInputElement} */
          const element = phoneNumberView.element.querySelector('input');
          if (element) {
            element.autocomplete = 'tel';
          }
        });
      }
      const nameView = this.recordView.getFieldView('name');
      this.listenTo(nameView, 'after:render', () => {
        /** @type {HTMLInputElement} */
        const elementFirst = nameView.element.querySelector('input[data-name="firstName"]');
        if (elementFirst) {
          elementFirst.autocomplete = 'given-name';
        }

        /** @type {HTMLInputElement} */
        const elementLast = nameView.element.querySelector('input[data-name="lastName"]');
        if (elementLast) {
          elementLast.autocomplete = 'family-name';
        }
      });
      const firstName = this.recordView.getFieldView('firstName');
      if (firstName) {
        this.listenTo(firstName, 'after:render', () => {
          /** @type {HTMLInputElement} */
          const element = firstName.element.querySelector('input');
          if (element) {
            element.autocomplete = 'given-name';
          }
        });
      }
      const lastName = this.recordView.getFieldView('lastName');
      if (lastName) {
        this.listenTo(lastName, 'after:render', () => {
          /** @type {HTMLInputElement} */
          const element = lastName.element.querySelector('input');
          if (element) {
            element.autocomplete = 'family-name';
          }
        });
      }
    }
  }
  _exports.default = LeadCaptureFormView;
  class CustomEditView extends _edit.default {
    handleShortcutKeyCtrlEnter(e) {
      this.trigger('save');
    }
  }
});

define("views/fields/user-with-avatar", ["exports", "views/fields/user"], function (_exports, _user) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _user = _interopRequireDefault(_user);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserWithAvatarFieldView extends _user.default {
    listTemplate = 'fields/user-with-avatar/list';
    detailTemplate = 'fields/user-with-avatar/detail';
    data() {
      const data = super.data();
      if (this.mode === this.MODE_DETAIL || this.mode === this.MODE_LIST) {
        data.avatar = this.getAvatarHtml();
        data.isOwn = this.model.get(this.idName) === this.getUser().id;
      }
      return data;
    }
    getAvatarHtml() {
      const size = this.mode === this.MODE_DETAIL ? 18 : 16;
      return this.getHelper().getAvatarHtml(this.model.get(this.idName), 'small', size, 'avatar-link');
    }
    afterRender() {
      super.afterRender();
      if (this.isEditMode()) {
        this.controlEditModeAvatar();
      }
    }
    setup() {
      super.setup();
      this.addHandler('keydown', `input[data-name="${this.nameName}"]`, (e, target) => {
        target.classList.add('being-typed');
      });
      this.addHandler('change', `input[data-name="${this.nameName}"]`, (e, target) => {
        setTimeout(() => target.classList.remove('being-typed'), 200);
      });
      this.on('change', () => {
        if (!this.isEditMode()) {
          return;
        }
        const img = this.element.querySelector('img.avatar');
        if (img) {
          img.parentNode.removeChild(img);
        }
        this.controlEditModeAvatar();
      });
    }

    /**
     * @private
     */
    controlEditModeAvatar() {
      const nameElement = this.element.querySelector(`input[data-name="${this.nameName}"]`);
      nameElement.classList.remove('being-typed');
      const userId = this.model.attributes[this.idName];
      if (!userId) {
        return;
      }
      const avatarHtml = this.getHelper().getAvatarHtml(userId, 'small', 18, 'avatar-link');
      if (!avatarHtml) {
        return;
      }
      const img = new DOMParser().parseFromString(avatarHtml, 'text/html').body.childNodes[0];
      if (!(img instanceof HTMLImageElement)) {
        return;
      }
      img.classList.add('avatar-in-input');
      img.draggable = false;
      const input = this.element.querySelector('.input-group > input');
      if (!input) {
        return;
      }
      input.after(img);
    }
  }
  var _default = _exports.default = UserWithAvatarFieldView;
});

define("views/fields/url", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/url */

  /**
   * A URL field.
   *
   * @extends BaseFieldView<module:views/fields/url~params>
   */
  class UrlFieldView extends _varchar.default {
    /**
     * @typedef {Object} module:views/fields/url~options
     * @property {
     *     module:views/fields/varchar~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/url~params
     * @property {number} [maxLength] A max length.
     * @property {boolean} [required] Required.
     * @property {boolean} [copyToClipboard] To display a Copy-to-clipboard button.
     * @property {boolean} [strip] To strip.
     */

    /**
     * @param {
     *     module:views/fields/url~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'url';
    listTemplate = 'fields/url/list';
    detailTemplate = 'fields/url/detail';
    defaultProtocol = 'https:';

    /**
     * @inheritDoc
     * @type {Array<(function (): boolean)|string>}
     */
    validations = ['required', 'valid', 'maxLength'];
    noSpellCheck = true;
    optionalProtocol = true;
    DEFAULT_MAX_LENGTH = 255;
    data() {
      const data = super.data();
      data.url = this.getUrl();
      return data;
    }
    afterRender() {
      super.afterRender();
      if (this.isEditMode()) {
        this.$element.on('change', () => {
          const value = this.$element.val() || '';
          const parsedValue = this.parse(value);
          if (parsedValue === value) {
            return;
          }
          const decoded = parsedValue ? this.decodeURI(parsedValue) : '';
          this.$element.val(decoded);
        });
      }
    }
    getValueForDisplay() {
      const value = this.model.get(this.name);
      return value ? this.decodeURI(value) : null;
    }

    /**
     * @private
     * @param {string} value
     * @return {string}
     */
    decodeURI(value) {
      try {
        return decodeURI(value);
      } catch (e) {
        console.warn(`Malformed URI ${value}.`);
        return value;
      }
    }

    /**
     * @param {string} value
     * @return {string}
     */
    parse(value) {
      value = value.trim();
      if (this.params.strip) {
        value = this.strip(value);
      }
      try {
        if (value === decodeURI(value)) {
          value = encodeURI(value);
        }
      } catch (e) {
        console.warn(`Malformed URI ${value}.`);
        return value;
      }
      return value;
    }

    /**
     * @param {string} value
     * @return {string}
     */
    strip(value) {
      if (value.indexOf('//') !== -1) {
        value = value.substring(value.indexOf('//') + 2);
      }
      value = value.replace(/\/+$/, '');
      return value;
    }
    getUrl() {
      let url = this.model.get(this.name);
      if (url && url !== '') {
        if (url.indexOf('//') === -1) {
          url = this.defaultProtocol + '//' + url;
        }
        return url;
      }
      return url;
    }

    // noinspection JSUnusedGlobalSymbols
    validateValid() {
      const value = this.model.get(this.name);
      if (!value) {
        return false;
      }
      const patternName = this.optionalProtocol ? 'uriOptionalProtocol' : 'uri';

      /** @var {string} */
      const pattern = this.getMetadata().get(['app', 'regExpPatterns', patternName, 'pattern']);
      const regExp = new RegExp('^' + pattern + '$');
      if (regExp.test(value)) {
        return false;
      }
      const msg = this.translate('fieldInvalid', 'messages').replace('{field}', this.getLabelText());
      this.showValidationMessage(msg);
      return true;
    }

    // noinspection JSUnusedGlobalSymbols
    validateMaxLength() {
      const maxLength = this.params.maxLength || this.DEFAULT_MAX_LENGTH;
      const value = this.model.get(this.name);
      if (!value || !value.length) {
        return false;
      }
      if (value.length <= maxLength) {
        return false;
      }
      const msg = this.translate('fieldUrlExceedsMaxLength', 'messages').replace('{maxLength}', maxLength.toString()).replace('{field}', this.getLabelText());
      this.showValidationMessage(msg);
      return true;
    }
    fetch() {
      const data = super.fetch();
      const value = data[this.name];
      if (!value) {
        return data;
      }
      data[this.name] = this.parse(value);
      return data;
    }
  }
  var _default = _exports.default = UrlFieldView;
});

define("views/fields/url-multiple", ["exports", "views/fields/array"], function (_exports, _array) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _array = _interopRequireDefault(_array);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * An Url-Multiple field.
   */
  class UrlMultipleFieldView extends _array.default {
    type = 'urlMultiple';
    maxItemLength = 255;
    displayAsList = true;
    defaultProtocol = 'https:';
    setup() {
      super.setup();
      this.noEmptyString = true;
      this.params.pattern = '$uriOptionalProtocol';
    }
    addValueFromUi(value) {
      value = value.trim();
      if (this.params.strip) {
        value = this.strip(value);
      }
      try {
        if (value === decodeURI(value)) {
          value = encodeURI(value);
        }
      } catch (e) {
        console.warn(`Malformed URI ${value}.`);
      }
      super.addValueFromUi(value);
    }

    /**
     * @private
     * @param {string} value
     * @return {string}
     */
    decodeURI(value) {
      try {
        return decodeURI(value);
      } catch (e) {
        console.warn(`Malformed URI ${value}.`);
        return value;
      }
    }

    /**
     * @param {string} value
     * @return {string}
     */
    strip(value) {
      if (value.indexOf('//') !== -1) {
        value = value.substring(value.indexOf('//') + 2);
      }
      value = value.replace(/\/+$/, '');
      return value;
    }
    prepareUrl(url) {
      if (url.indexOf('//') === -1) {
        url = this.defaultProtocol + '//' + url;
      }
      return url;
    }
    getValueForDisplay() {
      /** @type {JQuery[]} */
      const $list = this.selected.map(value => {
        return $('<a>').attr('href', this.prepareUrl(value)).attr('target', '_blank').text(this.decodeURI(value));
      });
      return $list.map($item => $('<div>').addClass('multi-enum-item-container').append($item).get(0).outerHTML).join('');
    }
    getItemHtml(value) {
      const html = super.getItemHtml(value);
      const $item = $(html);
      $item.find('span.text').html($('<a>').attr('href', this.prepareUrl(value)).css('user-drag', 'none').attr('target', '_blank').text(this.decodeURI(value)));
      return $item.get(0).outerHTML;
    }
  }
  var _default = _exports.default = UrlMultipleFieldView;
});

define("views/fields/range-float", ["exports", "views/fields/range-int", "views/fields/float"], function (_exports, _rangeInt, _float) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _rangeInt = _interopRequireDefault(_rangeInt);
  _float = _interopRequireDefault(_float);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RangeFloatFieldView extends _rangeInt.default {
    type = 'rangeFloat';
    validations = ['required', 'float', 'range', 'order'];
    decimalPlacesRawValue = 10;
    setupAutoNumericOptions() {
      this.autoNumericOptions = {
        digitGroupSeparator: this.thousandSeparator || '',
        decimalCharacter: this.decimalMark,
        modifyValueOnWheel: false,
        selectOnFocus: false,
        decimalPlaces: this.decimalPlacesRawValue,
        decimalPlacesRawValue: this.decimalPlacesRawValue,
        allowDecimalPadding: false,
        showWarnings: false,
        formulaMode: true
      };
    }

    // noinspection JSUnusedGlobalSymbols
    validateFloat() {
      const validate = name => {
        if (isNaN(this.model.get(name))) {
          const msg = this.translate('fieldShouldBeFloat', 'messages').replace('{field}', this.getLabelText());
          this.showValidationMessage(msg, '[data-name="' + name + '"]');
          return true;
        }
      };
      let result = false;
      result = validate(this.fromField) || result;
      result = validate(this.toField) || result;
      return result;
    }
    parse(value) {
      return _float.default.prototype.parse.call(this, value);
    }
    formatNumber(value) {
      return _float.default.prototype.formatNumberDetail.call(this, value);
    }
  }
  var _default = _exports.default = RangeFloatFieldView;
});

define("views/fields/phone", ["exports", "views/fields/varchar", "ui/select", "intl-tel-input", "intl-tel-input-utils", "intl-tel-input-globals"], function (_exports, _varchar, _select, _intlTelInput, _intlTelInputUtils, _intlTelInputGlobals) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  _select = _interopRequireDefault(_select);
  _intlTelInput = _interopRequireDefault(_intlTelInput);
  _intlTelInputUtils = _interopRequireDefault(_intlTelInputUtils);
  _intlTelInputGlobals = _interopRequireDefault(_intlTelInputGlobals);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/phone */

  // noinspection NpmUsedModulesInstalled

  // noinspection NpmUsedModulesInstalled

  /**
   * @extends VarcharFieldView<module:views/fields/phone~params>
   */
  class PhoneFieldView extends _varchar.default {
    /**
     * @typedef {Object} module:views/fields/phone~options
     * @property {
     *     module:views/fields/phone~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/phone~params
     * @property {boolean} [required] Required.
     * @property {boolean} [onlyPrimary] Only primary.
     */

    /**
     * @param {
     *     module:views/fields/phone~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'phone';
    editTemplate = 'fields/phone/edit';
    detailTemplate = 'fields/phone/detail';
    listTemplate = 'fields/phone/list';

    /**
     * @inheritDoc
     * @type {Array<(function (): boolean)|string>}
     */
    validations = ['required', 'phoneData'];
    maxExtensionLength = 6;

    /**
     * @private
     * @type {RegExp}
     */
    validationRegExp;

    /**
     * @protected
     * @type {boolean}
     */
    isNumeric;
    events = {
      /** @this PhoneFieldView */
      'click [data-action="switchPhoneProperty"]': function (e) {
        const $target = $(e.currentTarget);
        const $block = $(e.currentTarget).closest('div.phone-number-block');
        const property = $target.data('property-type');
        const $input = $block.find('input.phone-number');
        if (property === 'primary') {
          if (!$target.hasClass('active')) {
            if ($input.val() !== '') {
              this.$el.find('button.phone-property[data-property-type="primary"]').removeClass('active').children().addClass('text-muted');
              $target.addClass('active').children().removeClass('text-muted');
            }
          }
          this.trigger('change');
          return;
        }
        let active = false;
        if ($target.hasClass('active')) {
          $target.removeClass('active').children().addClass('text-muted');
        } else {
          $target.addClass('active').children().removeClass('text-muted');
          active = true;
        }
        if (property === 'optOut') {
          active ? $input.addClass('text-strikethrough') : $input.removeClass('text-strikethrough');
        }
        if (property === 'invalid') {
          active ? $input.addClass('text-danger') : $input.removeClass('text-danger');
        }
        this.trigger('change');
      },
      /** @this PhoneFieldView */
      'click [data-action="removePhoneNumber"]': function (e) {
        const $block = $(e.currentTarget).closest('div.phone-number-block');
        this.removePhoneNumber($block);
        this.trigger('change');
        const $last = this.$el.find('.phone-number').last();
        if ($last.length) {
          // noinspection JSUnresolvedReference
          $last[0].focus({
            preventScroll: true
          });
        }
      },
      /** @this PhoneFieldView */
      'change input.phone-number': function (e) {
        const $input = $(e.currentTarget);
        const $block = $input.closest('div.phone-number-block');
        if (this._itemJustRemoved) {
          return;
        }
        if ($input.val() === '' && $block.length) {
          this.removePhoneNumber($block);
        } else {
          this.trigger('change');
        }
        this.manageAddButton();
      },
      /** @this PhoneFieldView */
      'keypress input.phone-number': function () {
        this.manageAddButton();
      },
      /** @this PhoneFieldView */
      'paste input.phone-number': function () {
        setTimeout(() => this.manageAddButton(), 10);
      },
      /** @this PhoneFieldView */
      'click [data-action="addPhoneNumber"]': function () {
        this.addPhoneNumber();
      },
      /** @this PhoneFieldView */
      'keydown input.phone-number': function (e) {
        const key = Espo.Utils.getKeyFromKeyEvent(e);
        const $target = $(e.currentTarget);
        if (key === 'Enter') {
          if (!this.$el.find('[data-action="addPhoneNumber"]').hasClass('disabled')) {
            this.addPhoneNumber();
            e.stopPropagation();
          }
          return;
        }
        if (key === 'Backspace' && $target.val() === '') {
          const $block = $target.closest('div.phone-number-block');
          this._itemJustRemoved = true;
          setTimeout(() => this._itemJustRemoved = false, 100);
          e.stopPropagation();
          this.removePhoneNumber($block);
          setTimeout(() => this.focusOnLast(true), 50);
        }
      }
    };
    validateRequired() {
      if (!this.isRequired()) {
        return;
      }
      if (!this.model.get(this.name)) {
        const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg, 'div.phone-number-block:nth-child(1) input.phone-number');
        return true;
      }
    }

    // noinspection JSUnusedGlobalSymbols
    validatePhoneData() {
      const data = this.model.get(this.dataFieldName);
      if (!data || !data.length) {
        return;
      }

      /** @var {string} */
      const pattern = '^' + this.getMetadata().get('app.regExpPatterns.phoneNumberLoose.pattern') + '$';
      this.validationRegExp = new RegExp(pattern);
      const numberList = [];
      let notValid = false;
      data.forEach((row, i) => {
        const number = row.phoneNumber;
        if (this.itemValidate(row, i)) {
          notValid = true;
        }
        const numberClean = String(number).replace(/[\s+]/g, '');
        if (numberList.includes(numberClean)) {
          const msg = this.translate('fieldValueDuplicate', 'messages').replace('{field}', this.getLabelText());
          this.showValidationMessage(msg, 'div.phone-number-block:nth-child(' + (i + 1).toString() + ') input.phone-number');
          notValid = true;
          return;
        }
        numberList.push(numberClean);
      });
      return notValid;
    }
    validateMaxCount() {
      /** @type {number|null} */
      const maxCount = this.maxCount;
      if (!maxCount) {
        return false;
      }
      const items = this.model.attributes[this.dataFieldName] || [];
      if (items.length <= maxCount) {
        return false;
      }
      const msg = this.translate('fieldExceedsMaxCount', 'messages').replace('{maxCount}', maxCount.toString());
      this.showValidationMessage(msg, 'div.phone-number-block:last-child input.phone-number');
      return true;
    }

    /**
     * @protected
     * @param {{number: string, type: string}} item A data item.
     * @param {number} i An index.
     * @return {boolean}
     * @internal Called in an extension. Do not change the signature.
     */
    itemValidate(item, i) {
      const number = item.number;
      const n = (i + 1).toString();
      const selector = `div.phone-number-block:nth-child(${n}) input.phone-number`;
      let notValid = false;
      if (!this.validationRegExp.test(number)) {
        notValid = true;
        const msg = this.translate('fieldPhoneInvalidCharacters', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg, selector);
      }
      if (!this.useInternational) {
        return notValid;
      }
      const element = this.$el.find(selector).get(0);
      if (!element) {
        return notValid;
      }
      const intlObj = this.intlTelInputMap.get(element);
      const isPossible = intlObj && intlObj.isPossibleNumber();
      if (intlObj && !isPossible) {
        notValid = true;
        const code = intlObj.getValidationError();
        const key = ['fieldPhoneInvalid', 'fieldPhoneInvalidCode', 'fieldPhoneTooShort', 'fieldPhoneTooLong'][code || 0] || 'fieldPhoneInvalid';
        const msg = this.translate(key, 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg, selector);
      }
      if (intlObj && isPossible && this.allowExtensions && intlObj.getExtension() && intlObj.getExtension().length > this.maxExtensionLength) {
        const msg = this.translate('fieldPhoneExtensionTooLong', 'messages').replace('{maxLength}', this.maxExtensionLength.toString()).replace('{field}', this.getLabelText());
        this.showValidationMessage(msg, selector);
        notValid = true;
      }
      return notValid;
    }
    data() {
      const number = this.model.get(this.name);
      let phoneNumberData;
      if (this.mode === this.MODE_EDIT) {
        phoneNumberData = Espo.Utils.cloneDeep(this.model.get(this.dataFieldName));
        if (this.model.isNew() || !this.model.get(this.name)) {
          if (!phoneNumberData || !phoneNumberData.length) {
            let optOut;
            if (this.model.isNew()) {
              optOut = this.phoneNumberOptedOutByDefault && this.model.entityType !== 'User';
            } else {
              optOut = this.model.get(this.isOptedOutFieldName);
            }
            phoneNumberData = [{
              phoneNumber: this.model.get(this.name) || '',
              primary: true,
              type: this.defaultType,
              optOut: optOut,
              invalid: false
            }];
          }
        }
      } else {
        phoneNumberData = this.model.get(this.dataFieldName) || false;
      }
      if (phoneNumberData) {
        phoneNumberData = Espo.Utils.cloneDeep(phoneNumberData);
        phoneNumberData.forEach(item => {
          const number = item.phoneNumber || '';
          item.erased = number.indexOf(this.erasedPlaceholder) === 0;
          if (!item.erased) {
            item.valueForLink = this.formatForLink(number);
            if (this.isReadMode()) {
              item.phoneNumber = this.formatNumber(item.phoneNumber);
            }
          }
          item.lineThrough = item.optOut || item.invalid || this.model.get('doNotCall');
        });
      }
      if ((!phoneNumberData || phoneNumberData.length === 0) && this.model.get(this.name)) {
        const o = {
          phoneNumber: this.formatNumber(number),
          primary: true,
          valueForLink: this.formatForLink(number)
        };
        if (this.isReadMode()) {
          o.phoneNumber = this.formatNumber(o.phoneNumber);
        }
        if (this.mode === 'edit' && this.model.isNew()) {
          o.type = this.defaultType;
        }
        phoneNumberData = [o];
      }
      const data = {
        ...super.data(),
        phoneNumberData: phoneNumberData,
        doNotCall: this.model.get('doNotCall'),
        lineThrough: this.model.get('doNotCall') || this.model.get(this.isOptedOutFieldName)
      };
      if (this.isReadMode()) {
        data.isOptedOut = this.model.get(this.isOptedOutFieldName);
        data.isInvalid = this.model.get(this.isInvalidFieldName);
        if (this.model.get(this.name)) {
          data.isErased = this.model.get(this.name).indexOf(this.erasedPlaceholder) === 0;
          if (!data.isErased) {
            data.valueForLink = this.formatForLink(this.model.get(this.name));
          }
        }
        data.valueIsSet = this.model.has(this.name);
        data.value = this.formatNumber(data.value);
      }
      data.itemMaxLength = this.itemMaxLength;
      data.onlyPrimary = this.params.onlyPrimary;

      // noinspection JSValidateTypes
      return data;
    }

    /**
     * @private
     * @param {string} number
     */
    formatForLink(number) {
      if (this.allowExtensions && this.useInternational) {
        if (number.includes(' ext. ')) {
          number = number.replace(' ext. ', ',');
        }
        return number;
      }
      return number.replace(/ /g, '');
    }
    focusOnLast(cursorAtEnd) {
      const $item = this.$el.find('input.form-control').last();
      $item.focus();
      if (cursorAtEnd && $item[0]) {
        // noinspection JSUnresolvedReference
        $item[0].setSelectionRange($item[0].value.length, $item[0].value.length);
      }
    }
    removePhoneNumber($block) {
      if ($block.parent().children().length === 1) {
        $block.find('input.phone-number').val('');
      } else {
        this.removePhoneNumberBlock($block);
      }
      this.trigger('change');
    }
    formatNumber(value) {
      if (!value || value === '' || !this.useInternational) {
        return value;
      }

      // noinspection JSUnresolvedReference
      return _intlTelInputUtils.default.formatNumber(value, null, _intlTelInputUtils.default.numberFormat.INTERNATIONAL);
    }
    addPhoneNumber() {
      const data = Espo.Utils.cloneDeep(this.fetchPhoneNumberData());
      const o = {
        phoneNumber: '',
        primary: !data.length,
        type: undefined,
        optOut: this.emailAddressOptedOutByDefault,
        invalid: false
      };
      data.push(o);
      this.model.set(this.dataFieldName, data, {
        silent: true
      });
      this.reRender().then(() => this.focusOnLast());
    }
    afterRender() {
      super.afterRender();
      this.manageButtonsVisibility();
      this.manageAddButton();
      if (this.mode === this.MODE_EDIT) {
        this.$el.find('select').toArray().forEach(selectElement => {
          _select.default.init($(selectElement));
        });
      }
    }
    afterRenderEdit() {
      super.afterRenderEdit();
      if (this.useInternational) {
        const inputElements = this.element.querySelectorAll('input.phone-number');
        inputElements.forEach(inputElement => {
          // noinspection JSUnusedGlobalSymbols
          const obj = (0, _intlTelInput.default)(inputElement, {
            nationalMode: false,
            autoInsertDialCode: false,
            separateDialCode: true,
            showFlags: false,
            preferredCountries: this.preferredCountryList,
            localizedCountries: this._codeNames,
            customPlaceholder: /** string */placeholder => {
              return placeholder.replace(/[0-9]/g, '0');
            }
          });
          this.intlTelInputMap.set(inputElement, obj);
          inputElement.addEventListener('blur', () => {
            if (!obj.isPossibleNumber()) {
              return;
            }
            let number = obj.getNumber();
            const ext = obj.getExtension();
            if (this.allowExtensions && ext) {
              number += ' ext. ' + ext;
            }
            obj.setNumber(number);
          });
        });
      }
    }
    removePhoneNumberBlock($block) {
      let changePrimary = false;
      if ($block.find('button[data-property-type="primary"]').hasClass('active')) {
        changePrimary = true;
      }
      $block.remove();
      if (changePrimary) {
        this.$el.find('button[data-property-type="primary"]').first().addClass('active').children().removeClass('text-muted');
      }
      this.manageButtonsVisibility();
      this.manageAddButton();
    }
    manageAddButton() {
      const $input = this.$el.find('input.phone-number');
      let c = 0;
      $input.each((i, input) => {
        // noinspection JSUnresolvedReference
        if (input.value !== '') {
          c++;
        }
      });
      if (c === $input.length) {
        this.$el.find('[data-action="addPhoneNumber"]').removeClass('disabled').removeAttr('disabled');
        return;
      }
      this.$el.find('[data-action="addPhoneNumber"]').addClass('disabled').attr('disabled', 'disabled');
    }
    manageButtonsVisibility() {
      const $primary = this.$el.find('button[data-property-type="primary"]');
      const $remove = this.$el.find('button[data-action="removePhoneNumber"]');
      const $container = this.$el.find('.phone-number-block-container');
      if ($primary.length > 1) {
        $primary.removeClass('hidden');
        $remove.removeClass('hidden');
        $container.addClass('many');
        return;
      }
      $container.removeClass('many');
      $primary.addClass('hidden');
      $remove.addClass('hidden');
    }
    setup() {
      this.dataFieldName = this.name + 'Data';
      this.defaultType = this.defaultType || this.getMetadata().get(`entityDefs.${this.model.entityType}.fields.${this.name}.defaultType`);
      this.isOptedOutFieldName = this.name + 'IsOptedOut';
      this.isInvalidFieldName = this.name + 'IsInvalid';
      this.phoneNumberOptedOutByDefault = this.getConfig().get('phoneNumberIsOptedOutByDefault');
      this.useInternational = this.getConfig().get('phoneNumberInternational') || false;
      this.allowExtensions = this.getConfig().get('phoneNumberExtensions') || false;
      this.preferredCountryList = this.getConfig().get('phoneNumberPreferredCountryList') || [];
      this.maxCount = this.getConfig().get('phoneNumberMaxCount');
      if (this.useInternational && !this.isListMode() && !this.isSearchMode()) {
        this._codeNames = _intlTelInputGlobals.default.getCountryData().reduce((map, item) => {
          map[item.iso2] = item.iso2.toUpperCase();
          return map;
        }, {});
      }
      if (this.model.has('doNotCall')) {
        this.listenTo(this.model, 'change:doNotCall', (model, value, o) => {
          if (this.mode !== 'detail' && this.mode !== 'list') {
            return;
          }
          if (!o.ui) {
            return;
          }
          this.reRender();
        });
      }
      this.erasedPlaceholder = 'ERASED:';
      this.itemMaxLength = this.getMetadata().get(['entityDefs', 'PhoneNumber', 'fields', 'name', 'maxLength']);
      this.intlTelInputMap = new Map();
      this.once('remove', () => {
        for (const obj of this.intlTelInputMap.values()) {
          obj.destroy();
        }
        this.intlTelInputMap.clear();
      });
      this.validations.push(() => this.validateMaxCount());
      this.isNumeric = this.getConfig().get('phoneNumberNumericSearch');
    }

    /**
     * @return {{
     *     phoneNumber: string,
     *     primary: boolean,
     *     type: string,
     *     optOut: boolean,
     *     invalid: boolean,
     * }[]}
     */
    fetchPhoneNumberData() {
      const $list = this.$el.find('div.phone-number-block');
      if (!$list.length) {
        return [];
      }
      const data = [];
      $list.each((i, d) => {
        const row = {};
        const $d = $(d);

        /** @type {HTMLInputElement} */
        const inputElement = $d.find('input.phone-number').get(0);
        if (!inputElement) {
          return;
        }
        row.phoneNumber = inputElement.value.trim();
        if (this.intlTelInputMap.has(inputElement)) {
          row.phoneNumber = this.intlTelInputMap.get(inputElement).getNumber();
          const ext = this.intlTelInputMap.get(inputElement).getExtension() || null;
          if (this.allowExtensions && ext) {
            row.phoneNumber += ' ext. ' + ext;
          }
        }
        if (row.phoneNumber === '') {
          return;
        }
        row.primary = $d.find('button[data-property-type="primary"]').hasClass('active');
        row.type = $d.find('select[data-property-type="type"]').val();
        row.optOut = $d.find('button[data-property-type="optOut"]').hasClass('active');
        row.invalid = $d.find('button[data-property-type="invalid"]').hasClass('active');
        data.push(row);
      });
      return data;
    }
    fetch() {
      const data = {};
      const addressData = this.fetchPhoneNumberData();
      if (this.params.onlyPrimary) {
        if (addressData.length > 0) {
          data[this.name] = addressData[0].phoneNumber;
          data[this.dataFieldName] = [{
            phoneNumber: addressData[0].phoneNumber,
            primary: true
          }];
        } else {
          data[this.name] = null;
          data[this.dataFieldName] = null;
        }
        return data;
      }
      data[this.dataFieldName] = addressData;
      data[this.name] = null;
      data[this.isOptedOutFieldName] = false;
      data[this.isInvalidFieldName] = false;
      let primaryIndex = 0;
      addressData.forEach((item, i) => {
        if (item.primary) {
          primaryIndex = i;
          if (item.optOut) {
            data[this.isOptedOutFieldName] = true;
          }
          if (item.invalid) {
            data[this.isInvalidFieldName] = true;
          }
        }
      });
      if (addressData.length && primaryIndex > 0) {
        const t = addressData[0];
        addressData[0] = addressData[primaryIndex];
        addressData[primaryIndex] = t;
      }
      if (addressData.length) {
        data[this.name] = addressData[0].phoneNumber;
      } else {
        data[this.isOptedOutFieldName] = null;
        data[this.isInvalidFieldName] = null;
      }
      return data;
    }

    /** @inheritDoc */
    fetchSearch() {
      const type = this.fetchSearchType() || 'startsWith';
      const name = this.isNumeric ? this.name + 'Numeric' : this.name;
      if (['isEmpty', 'isNotEmpty'].includes(type)) {
        if (type === 'isEmpty') {
          return {
            type: 'isNull',
            attribute: name,
            data: {
              type: type
            }
          };
        }
        return {
          type: 'isNotNull',
          attribute: name,
          data: {
            type: type
          }
        };
      }

      /** @type {string} */
      let value = this.$element.val().toString().trim();
      const originalValue = value;
      if (this.isNumeric && value) {
        value = value.replace(/[^0-9]/g, '');
      }
      if (!value) {
        return null;
      }
      return {
        type: type,
        value: value,
        attribute: name,
        data: {
          type: type,
          value: originalValue
        }
      };
    }
  }
  var _default = _exports.default = PhoneFieldView;
});

define("views/fields/person-name", ["exports", "views/fields/varchar", "ui/select"], function (_exports, _varchar, _select) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  _select = _interopRequireDefault(_select);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/person-name */

  class PersonNameFieldView extends _varchar.default {
    type = 'personName';
    detailTemplate = 'fields/person-name/detail';
    editTemplate = 'fields/person-name/edit';
    // noinspection JSUnusedGlobalSymbols
    editTemplateLastFirst = 'fields/person-name/edit-last-first';
    // noinspection JSUnusedGlobalSymbols
    editTemplateLastFirstMiddle = 'fields/person-name/edit-last-first-middle';
    // noinspection JSUnusedGlobalSymbols
    editTemplateFirstMiddleLast = 'fields/person-name/edit-first-middle-last';

    /** @inheritDoc */
    validations = ['required', 'pattern'];
    data() {
      const data = super.data();
      data.ucName = Espo.Utils.upperCaseFirst(this.name);
      data.salutationValue = this.model.get(this.salutationField);
      data.firstValue = this.model.get(this.firstField);
      data.lastValue = this.model.get(this.lastField);
      data.middleValue = this.model.get(this.middleField);
      data.salutationOptions = this.salutationOptions;
      if (this.isEditMode()) {
        data.firstMaxLength = this.model.getFieldParam(this.firstField, 'maxLength');
        data.lastMaxLength = this.model.getFieldParam(this.lastField, 'maxLength');
        data.middleMaxLength = this.model.getFieldParam(this.middleField, 'maxLength');
      }
      data.valueIsSet = this.model.has(this.firstField) || this.model.has(this.lastField);
      if (this.isDetailMode()) {
        data.isNotEmpty = !!data.firstValue || !!data.lastValue || !!data.salutationValue || !!data.middleValue;
      } else if (this.isListMode()) {
        data.isNotEmpty = !!data.firstValue || !!data.lastValue || !!data.middleValue;
      }
      if (data.isNotEmpty && this.isDetailMode() || this.isListMode()) {
        data.formattedValue = this.getFormattedValue();
      }
      return data;
    }
    setup() {
      super.setup();
      const ucName = Espo.Utils.upperCaseFirst(this.name);
      this.salutationField = 'salutation' + ucName;
      this.firstField = 'first' + ucName;
      this.lastField = 'last' + ucName;
      this.middleField = 'middle' + ucName;
      this.salutationOptions = this.model.getFieldParam(this.salutationField, 'options');
    }
    afterRender() {
      super.afterRender();
      if (this.isEditMode()) {
        this.$salutation = this.$el.find('[data-name="' + this.salutationField + '"]');
        this.$first = this.$el.find('[data-name="' + this.firstField + '"]');
        this.$last = this.$el.find('[data-name="' + this.lastField + '"]');
        if (this.formatHasMiddle()) {
          this.$middle = this.$el.find('[data-name="' + this.middleField + '"]');
        }
        this.$salutation.on('change', () => {
          this.trigger('change');
        });
        this.$first.on('change', () => {
          this.trigger('change');
        });
        this.$last.on('change', () => {
          this.trigger('change');
        });
        _select.default.init(this.$salutation);
      }
    }
    getFormattedValue() {
      let salutation = this.model.get(this.salutationField);
      const first = this.model.get(this.firstField);
      const last = this.model.get(this.lastField);
      const middle = this.model.get(this.middleField);
      if (salutation) {
        salutation = this.getLanguage().translateOption(salutation, 'salutationName', this.model.entityType);
      }
      return this.formatName({
        salutation: salutation,
        first: first,
        middle: middle,
        last: last
      });
    }
    _getTemplateName() {
      if (this.isEditMode()) {
        const prop = 'editTemplate' + Espo.Utils.upperCaseFirst(this.getFormat().toString());
        if (prop in this) {
          return this[prop];
        }
      }
      return super._getTemplateName();
    }
    getFormat() {
      this.format = this.format || this.getConfig().get('personNameFormat') || 'firstLast';
      return this.format;
    }
    formatHasMiddle() {
      const format = this.getFormat();
      return format === 'firstMiddleLast' || format === 'lastFirstMiddle';
    }
    validateRequired() {
      const isRequired = this.isRequired();
      const validate = name => {
        if (this.model.isRequired(name)) {
          if (!this.model.get(name)) {
            const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.translate(name, 'fields', this.model.entityType));
            this.showValidationMessage(msg, '[data-name="' + name + '"]');
            return true;
          }
        }
      };
      if (isRequired) {
        if (!this.model.get(this.firstField) && !this.model.get(this.lastField)) {
          const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
          this.showValidationMessage(msg, '[data-name="' + this.lastField + '"]');
          return true;
        }
      }
      let result = false;
      result = validate(this.salutationField) || result;
      result = validate(this.firstField) || result;
      result = validate(this.lastField) || result;
      result = validate(this.middleField) || result;
      return result;
    }
    validatePattern() {
      let result = false;
      result = this.fieldValidatePattern(this.firstField) || result;
      result = this.fieldValidatePattern(this.lastField) || result;
      result = this.fieldValidatePattern(this.middleField) || result;
      return result;
    }
    hasRequiredMarker() {
      if (this.isRequired()) {
        return true;
      }
      return this.model.getFieldParam(this.salutationField, 'required') || this.model.getFieldParam(this.firstField, 'required') || this.model.getFieldParam(this.middleField, 'required') || this.model.getFieldParam(this.lastField, 'required');
    }
    fetch() {
      const data = {};
      data[this.salutationField] = this.$salutation.val() || null;
      data[this.firstField] = this.$first.val().trim() || null;
      data[this.lastField] = this.$last.val().trim() || null;
      if (this.formatHasMiddle()) {
        data[this.middleField] = this.$middle.val().trim() || null;
      }
      data[this.name] = this.formatName({
        first: data[this.firstField],
        last: data[this.lastField],
        middle: data[this.middleField]
      });
      return data;
    }

    /**
     * @param {{first?: string, last?: string, middle?: string, salutation?: string}}data
     * @return {?string}
     */
    formatName(data) {
      let name;
      const format = this.getFormat();
      const arr = [];
      arr.push(data.salutation);
      if (format === 'firstLast') {
        arr.push(data.first);
        arr.push(data.last);
      } else if (format === 'lastFirst') {
        arr.push(data.last);
        arr.push(data.first);
      } else if (format === 'firstMiddleLast') {
        arr.push(data.first);
        arr.push(data.middle);
        arr.push(data.last);
      } else if (format === 'lastFirstMiddle') {
        arr.push(data.last);
        arr.push(data.first);
        arr.push(data.middle);
      } else {
        arr.push(data.first);
        arr.push(data.last);
      }
      name = arr.filter(item => !!item).join(' ').trim();
      if (name === '') {
        name = null;
      }
      return name;
    }
  }
  var _default = _exports.default = PersonNameFieldView;
});

define("views/fields/password", ["exports", "views/fields/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class PasswordFieldView extends _base.default {
    type = 'password';
    detailTemplate = 'fields/password/detail';
    editTemplate = 'fields/password/edit';
    validations = ['required', 'confirm'];
    changePassword() {
      this.$el.find('[data-action="change"]').addClass('hidden');
      this.$element.removeClass('hidden');
      this.changing = true;
    }
    data() {
      return {
        isNew: this.model.isNew(),
        ...super.data()
      };
    }
    setup() {
      super.setup();
      this.addActionHandler('change', () => this.changePassword());
    }

    // noinspection JSUnusedGlobalSymbols
    validateConfirm() {
      if (!this.model.has(this.name + 'Confirm')) {
        return;
      }
      if (this.model.get(this.name) !== this.model.get(this.name + 'Confirm')) {
        const msg = this.translate('fieldBadPasswordConfirm', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg);
        return true;
      }
    }
    afterRender() {
      super.afterRender();
      this.changing = false;
      if (this.params.readyToChange) {
        this.changePassword();
      }
    }
    fetch() {
      if (!this.model.isNew() && !this.changing) {
        return {};
      }
      return super.fetch();
    }
  }
  var _default = _exports.default = PasswordFieldView;
});

define("views/fields/multi-enum", ["exports", "views/fields/array", "helpers/reg-exp-pattern", "ui/multi-select"], function (_exports, _array, _regExpPattern, _multiSelect) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _array = _interopRequireDefault(_array);
  _regExpPattern = _interopRequireDefault(_regExpPattern);
  _multiSelect = _interopRequireDefault(_multiSelect);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/multi-enumeration */

  /**
   * A multi-enum field.
   */
  class MultiEnumFieldView extends _array.default {
    /**
     * @typedef {Object} module:views/fields/multi-enumeration~options
     * @property {
     *     module:views/fields/multi-enumeration~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/multi-enumeration~params
     * @property {string} [translation] A translation string. E.g. `Global.scopeNames`.
     * @property {string[]} [options] Select options.
     * @property {boolean} [required] Required.
     * @property {boolean} [displayAsLabel] Display as label.
     * @property {boolean} [displayAsList] Display as list (line breaks).
     * @property {string|'state'} [labelType] A label type.
     * @property {string} [optionsReference] A reference to options. E.g. `Account.industry`.
     * @property {string} [optionsPath] An options metadata path.
     * @property {boolean} [isSorted] To sort options.
     * @property {Object.<string, string>} [translatedOptions] Option translations.
     * @property {Object.<string, 'warning'|'danger'|'success'|'info'|'primary'>} [style] A style map.
     * @property {number} [maxCount] A max number of items.
     * @property {boolean} [allowCustomOptions] Allow custom options.
     * @property {string} [pattern] A regular expression pattern.
     */

    /**
     * @param {
     *     module:views/fields/multi-enumeration~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'multiEnum';
    listTemplate = 'fields/array/list';
    detailTemplate = 'fields/array/detail';
    editTemplate = 'fields/multi-enum/edit';

    /** @const */
    MAX_ITEM_LENGTH = 100;

    /**
     * @protected
     * @type {boolean}
     */
    restoreOnBackspace = false;
    validationElementSelector = '.selectize-control';
    events = {};

    // noinspection JSCheckFunctionSignatures
    /** @inheritDoc */
    data() {
      // noinspection JSValidateTypes
      return {
        ...super.data(),
        optionList: this.params.options || []
      };
    }

    /**
     * @deprecated As of v8.3.0.
     * @todo Remove.
     */
    getTranslatedOptions() {
      return (this.params.options || []).map(item => {
        if (this.translatedOptions !== null) {
          if (item in this.translatedOptions) {
            return this.translatedOptions[item];
          }
        }
        return item;
      });
    }
    translateValueToEditLabel(value) {
      let label = value;
      if (~(this.params.options || []).indexOf(value)) {
        label = this.getLanguage().translateOption(value, this.name, this.scope);
      }
      if (this.translatedOptions) {
        if (value in this.translatedOptions) {
          label = this.translatedOptions[value];
        }
      }
      if (label === '') {
        label = this.translate('None');
      }
      return label;
    }
    afterRender() {
      if (this.isSearchMode()) {
        this.renderSearch();
        return;
      }
      if (this.isEditMode()) {
        this.$element = this.$el.find('[data-name="' + this.name + '"]');
        const items = [];
        const valueList = Espo.Utils.clone(this.selected);
        for (const i in valueList) {
          let value = valueList[i];
          const originalValue = value;
          if (value === '') {
            value = valueList[i] = '__emptystring__';
          }
          if (!~(this.params.options || []).indexOf(value)) {
            items.push({
              value: value,
              text: this.translateValueToEditLabel(originalValue)
            });
          }
        }
        this.$element.val(valueList.join(this.itemDelimiter));
        (this.params.options || []).forEach(value => {
          const originalValue = value;
          if (value === '') {
            value = '__emptystring__';
          }
          items.push({
            value: value,
            text: this.translateValueToEditLabel(originalValue),
            style: this.styleMap[value] || undefined
          });
        });

        /** @type {module:ui/multi-select~Options} */
        const multiSelectOptions = {
          items: items,
          delimiter: this.itemDelimiter,
          matchAnyWord: this.matchAnyWord,
          draggable: true,
          allowCustomOptions: this.allowCustomOptions,
          restoreOnBackspace: this.restoreOnBackspace,
          create: input => this.createCustomOptionCallback(input)
        };
        _multiSelect.default.init(this.$element, multiSelectOptions);
        this.$element.on('change', () => {
          this.trigger('change');
        });
      }
    }

    /**
     * @protected
     * @param {string} input
     * @return {{text: string, value: string}|null}
     */
    createCustomOptionCallback(input) {
      if (input.length > this.MAX_ITEM_LENGTH) {
        const message = this.translate('arrayItemMaxLength', 'messages').replace('{max}', this.MAX_ITEM_LENGTH.toString());
        this.showValidationMessage(message, '.selectize-control');
        return null;
      }
      if (this.params.pattern) {
        const helper = new _regExpPattern.default();
        const result = helper.validate(this.params.pattern, input, this.name, this.entityType);
        if (result) {
          this.showValidationMessage(result.message, '.selectize-control');
          return null;
        }
      }
      return {
        value: input,
        text: input
      };
    }
    focusOnInlineEdit() {
      _multiSelect.default.focus(this.$element);
    }
    fetch() {
      let list = this.$element.val().split(this.itemDelimiter);
      if (list.length === 1 && list[0] === '') {
        list = [];
      }
      for (const i in list) {
        if (list[i] === '__emptystring__') {
          list[i] = '';
        }
      }
      if (this.params.isSorted && this.translatedOptions) {
        list = list.sort((v1, v2) => {
          return (this.translatedOptions[v1] || v1).localeCompare(this.translatedOptions[v2] || v2);
        });
      }
      const data = {};
      data[this.name] = list;
      return data;
    }
    validateRequired() {
      if (!this.isRequired()) {
        return;
      }
      const value = this.model.get(this.name);
      if (!value || value.length === 0) {
        const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg, '.selectize-control');
        return true;
      }
    }
    validateMaxCount() {
      if (!this.params.maxCount) {
        return;
      }
      const itemList = this.model.get(this.name) || [];
      if (itemList.length > this.params.maxCount) {
        const msg = this.translate('fieldExceedsMaxCount', 'messages').replace('{field}', this.getLabelText()).replace('{maxCount}', this.params.maxCount.toString());
        this.showValidationMessage(msg, '.selectize-control');
        return true;
      }
    }
  }
  var _default = _exports.default = MultiEnumFieldView;
});

define("views/fields/link-parent", ["exports", "views/fields/base", "helpers/record-modal", "ui/select", "ui/autocomplete"], function (_exports, _base, _recordModal, _select, _autocomplete) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _recordModal = _interopRequireDefault(_recordModal);
  _select = _interopRequireDefault(_select);
  _autocomplete = _interopRequireDefault(_autocomplete);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/link-parent */

  /**
   * A link-parent field (belongs-to-parent relation).
   *
   * @extends BaseFieldView<module:views/fields/link-parent~params>
   */
  class LinkParentFieldView extends _base.default {
    /**
     * @typedef {Object} module:views/fields/link-parent~options
     * @property {
     *     module:views/fields/link~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/link-parent~params
     * @property {boolean} [required] Required.
     * @property {boolean} [autocompleteOnEmpty] Autocomplete on empty input.
     * @property {string[]} [entityList] An entity type list.
     */

    /**
     * @param {
     *     module:views/fields/link-parent~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'linkParent';
    listTemplate = 'fields/link-parent/list';
    detailTemplate = 'fields/link-parent/detail';
    editTemplate = 'fields/link-parent/edit';
    searchTemplate = 'fields/link-parent/search';
    listLinkTemplate = 'fields/link-parent/list-link';

    /**
     * A name attribute name.
     *
     * @type {string}
     */
    nameName;

    /**
     * An ID attribute name.
     *
     * @type {string}
     */
    idName;

    /**
     * A type attribute name.
     *
     * @type {string}
     */
    typeName;

    /**
     * A current foreign entity type.
     *
     * @type {string|null}
     */
    foreignScope = null;

    /**
     * A foreign entity type list.
     *
     * @type {string[]}
     */
    foreignScopeList = null;

    /**
     * Autocomplete disabled.
     *
     * @protected
     * @type {boolean}
     */
    autocompleteDisabled = false;

    /**
     * A select-record view.
     *
     * @protected
     * @type {string}
     */
    selectRecordsView = 'views/modals/select-records';

    /**
     * Create disabled.
     *
     * @protected
     * @type {boolean}
     */
    createDisabled = false;

    /**
     * A search type list.
     *
     * @protected
     * @type {string[]}
     */
    searchTypeList = ['is', 'isEmpty', 'isNotEmpty'];

    /**
     * A select primary filter.
     *
     * @protected
     * @type {string|null}
     */
    selectPrimaryFilterName = null;

    /**
     * A select bool filter list.
     *
     * @protected
     * @type {string[]|null}
     */
    selectBoolFilterList = null;

    /**
     * An autocomplete max record number.
     *
     * @protected
     * @type {number|null}
     */
    autocompleteMaxCount = null;

    /**
     * Select all attributes.
     *
     * @protected
     * @type {boolean}
     */
    forceSelectAllAttributes = false;

    /**
     * Mandatory select attributes.
     *
     * @protected
     * @type {string[]|null}
     */
    mandatorySelectAttributeList = null;

    /** @inheritDoc */
    initialSearchIsNotIdle = true;

    /**
     * Trigger autocomplete on empty input.
     *
     * @protected
     * @type {boolean}
     */
    autocompleteOnEmpty;

    /**
     * @protected
     * @type {boolean}
     */
    displayScopeColorInListMode = true;

    /**
     * @protected
     * @type {boolean}
     */
    displayEntityType;

    /** @inheritDoc */
    events = {
      /** @this LinkParentFieldView */
      'auxclick a[href]:not([role="button"])': function (e) {
        if (!this.isReadMode()) {
          return;
        }
        const isCombination = e.button === 1 && (e.ctrlKey || e.metaKey);
        if (!isCombination) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        this.quickView();
      }
    };

    // noinspection JSCheckFunctionSignatures
    data() {
      let nameValue = this.model.get(this.nameName);
      if (!nameValue && this.model.get(this.idName) && this.model.get(this.typeName)) {
        nameValue = this.translate(this.model.get(this.typeName), 'scopeNames');
      }
      let iconHtml = null;
      if ((this.mode === this.MODE_DETAIL || this.mode === this.MODE_LIST && this.displayScopeColorInListMode) && this.foreignScope) {
        iconHtml = this.getHelper().getScopeColorIconHtml(this.foreignScope);
      }

      // noinspection JSValidateTypes
      return {
        ...super.data(),
        idName: this.idName,
        nameName: this.nameName,
        typeName: this.typeName,
        idValue: this.model.get(this.idName),
        nameValue: nameValue,
        typeValue: this.model.get(this.typeName),
        foreignScope: this.foreignScope,
        foreignScopeList: this.foreignScopeList,
        valueIsSet: this.model.has(this.idName) || this.model.has(this.typeName),
        iconHtml: iconHtml,
        displayEntityType: this.displayEntityType && this.model.get(this.typeName)
      };
    }

    /**
     * Get advanced filters (field filters) to be applied when select a record.
     * Can be extended.
     *
     * @protected
     * @return {Object.<string,module:search-manager~advancedFilter>|null}
     */
    getSelectFilters() {
      return null;
    }

    /**
     * Get a select bool filter list. Applied when select a record.
     * Can be extended.
     *
     * @protected
     * @return {string[]|null}
     */
    getSelectBoolFilterList() {
      return this.selectBoolFilterList;
    }

    /**
     * Get a select primary filter. Applied when select a record.
     * Can be extended.
     *
     * @protected
     * @return {string|null}
     */
    getSelectPrimaryFilterName() {
      return this.selectPrimaryFilterName;
    }

    /**
     * Attributes to pass to a model when creating a new record.
     * Can be extended.
     *
     * @return {Object.<string,*>|null}
     */
    getCreateAttributes() {
      return null;
    }

    /** @inheritDoc */
    setup() {
      this.nameName = this.name + 'Name';
      this.typeName = this.name + 'Type';
      this.idName = this.name + 'Id';
      this.foreignScopeList = this.options.foreignScopeList || this.foreignScopeList;
      this.foreignScopeList = this.foreignScopeList || this.params.entityList || this.model.getLinkParam(this.name, 'entityList') || [];
      this.foreignScopeList = Espo.Utils.clone(this.foreignScopeList).filter(item => {
        if (!this.getMetadata().get(['scopes', item, 'disabled'])) {
          return true;
        }
      });
      this.foreignScope = this.model.get(this.typeName) || this.foreignScopeList[0];
      if (this.foreignScope && !~this.foreignScopeList.indexOf(this.foreignScope)) {
        this.foreignScopeList.unshift(this.foreignScope);
      }
      this.listenTo(this.model, `change:${this.typeName}`, () => {
        this.foreignScope = this.model.get(this.typeName) || this.foreignScopeList[0];
      });
      this.autocompleteOnEmpty = this.params.autocompleteOnEmpty || this.autocompleteOnEmpty;
      if ('createDisabled' in this.options) {
        this.createDisabled = this.options.createDisabled;
      }
      if (!this.isListMode()) {
        this.addActionHandler('selectLink', () => this.actionSelect());
        this.addActionHandler('clearLink', () => this.actionClearLink());
        this.events[`change select[data-name="${this.typeName}"]`] = e => {
          this.foreignScope = e.currentTarget.value;
          this.$elementName.val('');
          this.$elementId.val('');
        };
      }
    }

    /**
     * @protected
     */
    actionClearLink() {
      if (this.foreignScopeList.length) {
        this.foreignScope = this.foreignScopeList[0];
        _select.default.setValue(this.$elementType, this.foreignScope);
      }
      this.$elementName.val('');
      this.$elementId.val('');
      this.trigger('change');
    }

    /**
     * @protected
     */
    async actionSelect() {
      const viewName = this.getMetadata().get(`clientDefs.${this.foreignScope}.modalViews.select`) || this.selectRecordsView;
      const createButton = !this.createDisabled && this.isEditMode();

      /** @type {module:views/modals/select-records~Options} */
      const options = {
        scope: this.foreignScope,
        createButton: createButton,
        filters: this.getSelectFilters(),
        boolFilterList: this.getSelectBoolFilterList(),
        primaryFilterName: this.getSelectPrimaryFilterName(),
        createAttributes: createButton ? this.getCreateAttributes() : null,
        mandatorySelectAttributeList: this.getMandatorySelectAttributeList(),
        forceSelectAllAttributes: this.isForceSelectAllAttributes(),
        layoutName: this.getSelectLayout(),
        onSelect: models => {
          this.select(models[0]);
        }
      };
      Espo.Ui.notifyWait();
      const view = await this.createView('modal', viewName, options);
      await view.render();
      Espo.Ui.notify();
    }

    /** @inheritDoc */
    setupSearch() {
      const type = this.getSearchParamsData().type;
      if (type === 'is' || !type) {
        this.searchData.idValue = this.getSearchParamsData().idValue || this.searchParams.valueId;
        this.searchData.nameValue = this.getSearchParamsData().nameValue || this.searchParams.valueName;
        this.searchData.typeValue = this.getSearchParamsData().typeValue || this.searchParams.valueType;
      }
      this.events['change select.search-type'] = e => {
        const type = $(e.currentTarget).val();
        this.handleSearchType(type);
      };
    }

    /**
     * Handle a search type.
     *
     * @protected
     * @param {string} type A type.
     */
    handleSearchType(type) {
      if (['is'].includes(type)) {
        this.$el.find('div.primary').removeClass('hidden');
      } else {
        this.$el.find('div.primary').addClass('hidden');
      }
    }

    /**
     * Select.
     *
     * @param {module:model} model A model.
     * @protected
     */
    select(model) {
      this.$elementName.val(model.get('name') || model.id);
      this.$elementId.val(model.get('id'));
      this.trigger('change');
    }

    /**
     * Attributes to select regardless availability on a list layout.
     * Can be extended.
     *
     * @protected
     * @return {string[]|null}
     */
    getMandatorySelectAttributeList() {
      return this.mandatorySelectAttributeList;
    }

    /**
     * Select all attributes. Can be extended.
     *
     * @protected
     * @return {boolean}
     */
    isForceSelectAllAttributes() {
      return this.forceSelectAllAttributes;
    }

    /**
     * Get an autocomplete max record number. Can be extended.
     *
     * @protected
     * @return {number}
     */
    getAutocompleteMaxCount() {
      if (this.autocompleteMaxCount) {
        return this.autocompleteMaxCount;
      }
      return this.getConfig().get('recordsPerPage');
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * Compose an autocomplete URL. Can be extended.
     *
     * @protected
     * @type {string} [q]
     * @return {string}
     */
    getAutocompleteUrl(q) {
      let url = this.foreignScope + '?maxSize=' + this.getAutocompleteMaxCount();
      if (!this.isForceSelectAllAttributes()) {
        let select = ['id', 'name'];
        if (this.getMandatorySelectAttributeList()) {
          select = select.concat(this.getMandatorySelectAttributeList());
        }
        url += '&select=' + select.join(',');
      }
      const boolList = this.getSelectBoolFilterList();
      if (boolList) {
        url += '&' + $.param({
          'boolFilterList': boolList
        });
      }
      const primary = this.getSelectPrimaryFilterName();
      if (primary) {
        url += '&' + $.param({
          'primaryFilter': primary
        });
      }
      const advanced = this.getSelectFilters();
      if (advanced && Object.keys(advanced).length) {
        url += '&' + $.param({
          'where': advanced
        });
      }
      return url;
    }
    afterRender() {
      if (this.isEditMode() || this.isSearchMode()) {
        this.$elementId = this.$el.find('input[data-name="' + this.idName + '"]');
        this.$elementName = this.$el.find('input[data-name="' + this.nameName + '"]');
        this.$elementType = this.$el.find('select[data-name="' + this.typeName + '"]');
        this.$elementName.on('change', () => {
          if (this.$elementName.val() === '') {
            this.$elementName.val('');
            this.$elementId.val('');
            this.trigger('change');
          }
        });
        this.$elementType.on('change', () => {
          this.$elementName.val('');
          this.$elementId.val('');
          this.trigger('change');
        });
        this.$elementName.on('blur', e => {
          setTimeout(() => {
            if (this.mode === this.MODE_EDIT) {
              e.currentTarget.value = this.model.get(this.nameName) || '';
            }
          }, 100);
        });
        if (!this.autocompleteDisabled) {
          /** @type {module:ajax.AjaxPromise & Promise<any>} */
          let lastAjaxPromise;
          const autocomplete = new _autocomplete.default(this.$elementName.get(0), {
            name: this.name,
            focusOnSelect: true,
            handleFocusMode: 2,
            autoSelectFirst: true,
            triggerSelectOnValidInput: false,
            forceHide: true,
            minChars: this.autocompleteOnEmpty ? 0 : 1,
            onSelect: item => {
              this.getModelFactory().create(this.foreignScope, model => {
                model.set(item.attributes);
                this.select(model);
                this.$elementName.focus();
              });
            },
            lookupFunction: query => {
              return Promise.resolve(this.getAutocompleteUrl(query)).then(url => {
                if (lastAjaxPromise && lastAjaxPromise.getReadyState() < 4) {
                  lastAjaxPromise.abort();
                }
                lastAjaxPromise = Espo.Ajax.getRequest(url, {
                  q: query
                });
                return lastAjaxPromise;
              }).then(/** {list: Record[]} */response => {
                return response.list.map(item => ({
                  value: item.name,
                  attributes: item
                }));
              });
            }
          });
          this.once('render remove', () => autocomplete.dispose());
          _select.default.init(this.$elementType, {});
          this.$elementType.on('change', () => autocomplete.clear());
        }
      }
      if (this.isSearchMode()) {
        const type = this.$el.find('select.search-type').val();
        this.handleSearchType(type);
        this.$el.find('select.search-type').on('change', () => {
          this.trigger('change');
        });
      }
    }

    /** @inheritDoc */
    getValueForDisplay() {
      return this.model.get(this.nameName);
    }

    /** @inheritDoc */
    validateRequired() {
      if (this.isRequired()) {
        if (this.model.get(this.idName) === null || !this.model.get(this.typeName)) {
          const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
          this.showValidationMessage(msg);
          return true;
        }
      }
    }

    /** @inheritDoc */
    fetch() {
      const data = {};
      data[this.typeName] = this.$elementType.val() || null;
      data[this.nameName] = this.$elementName.val() || null;
      data[this.idName] = this.$elementId.val() || null;
      if (data[this.idName] === null) {
        data[this.typeName] = null;
      }
      return data;
    }

    /** @inheritDoc */
    fetchSearch() {
      const type = this.$el.find('select.search-type').val();
      if (type === 'isEmpty') {
        return {
          type: 'isNull',
          field: this.idName,
          data: {
            type: type
          }
        };
      }
      if (type === 'isNotEmpty') {
        return {
          type: 'isNotNull',
          field: this.idName,
          data: {
            type: type
          }
        };
      }
      const entityType = this.$elementType.val();
      const entityName = this.$elementName.val();
      const entityId = this.$elementId.val();
      if (!entityType) {
        return null;
      }
      if (entityId) {
        return {
          type: 'and',
          attribute: this.idName,
          value: [{
            type: 'equals',
            field: this.idName,
            value: entityId
          }, {
            type: 'equals',
            field: this.typeName,
            value: entityType
          }],
          data: {
            type: 'is',
            idValue: entityId,
            nameValue: entityName,
            typeValue: entityType
          }
        };
      }
      return {
        type: 'and',
        attribute: this.idName,
        value: [{
          type: 'isNotNull',
          field: this.idName
        }, {
          type: 'equals',
          field: this.typeName,
          value: entityType
        }],
        data: {
          type: 'is',
          typeValue: entityType
        }
      };
    }

    /** @inheritDoc */
    getSearchType() {
      return this.getSearchParamsData().type || this.searchParams.typeFront;
    }

    /**
     * @protected
     */
    quickView() {
      const id = this.model.get(this.idName);
      const entityType = this.model.get(this.typeName);
      if (!id || !entityType) {
        return;
      }
      const helper = new _recordModal.default();
      helper.showDetail(this, {
        id: id,
        entityType: entityType
      });
    }

    /**
     * @protected
     * @return {string|undefined}
     * @since 9.1.0
     */
    getSelectLayout() {
      return undefined;
    }
  }
  var _default = _exports.default = LinkParentFieldView;
});

define("views/fields/link-multiple-with-role", ["exports", "views/fields/link-multiple", "ui/select"], function (_exports, _linkMultiple, _select) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkMultiple = _interopRequireDefault(_linkMultiple);
  _select = _interopRequireDefault(_select);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * A link-multiple field with a relation column.
   * @deprecated Prefer using `link-multiple-with-columns` instead.
   */
  class LinkMultipleWithRoleFieldView extends _linkMultiple.default {
    /**
     * A role field type.
     */
    roleType = 'enum';
    /**
     * A relation column name.
     */
    columnName = 'role';
    /**
     * The role field is defined in a foreign entity.
     */
    roleFieldIsForeign = true;
    /**
     * A value to fetch for an empty role.
     */
    emptyRoleValue = null;
    /**
     * A role placeholder text.
     */
    rolePlaceholderText = null;
    /**
     * A role value max length.
     * @protected
     */
    roleMaxLength = 50;
    /**
     * @protected
     */
    forceRoles = false;

    /** @const */
    ROLE_TYPE_ENUM = 'enum';
    // noinspection JSUnusedGlobalSymbols
    /** @const */
    ROLE_TYPE_VARCHAR = 'varchar';

    /**
     * @type {Record}
     * @protected
     */
    columns;
    setup() {
      super.setup();
      this.columnsName = this.name + 'Columns';
      this.columns = Espo.Utils.cloneDeep(this.model.get(this.columnsName) || {});
      this.listenTo(this.model, 'change:' + this.columnsName, () => {
        this.columns = Espo.Utils.cloneDeep(this.model.get(this.columnsName) || {});
      });
      this.roleField = this.getMetadata().get(['entityDefs', this.model.entityType, 'fields', this.name, 'columns', this.columnName]);
      this.displayRoleAsLabel = this.getMetadata().get(['entityDefs', this.model.entityType, 'fields', this.roleField, 'displayAsLabel']);
      this.roleFieldScope = this.roleFieldIsForeign ? this.foreignScope : this.model.entityType;
      if (this.roleType === this.ROLE_TYPE_ENUM && !this.forceRoles) {
        this.roleList = this.getMetadata().get(['entityDefs', this.roleFieldScope, 'fields', this.roleField, 'options']);
        if (!this.roleList) {
          this.roleList = [];
          this.skipRoles = true;
        }
      }
      if (this.roleType === this.ROLE_TYPE_ENUM) {
        this.styleMap = /** @type {Record<string>}*/this.getMetadata().get(['entityDefs', this.model.entityType, 'fields', this.roleField, 'style']) || {};
      }
    }
    getAttributeList() {
      const list = super.getAttributeList();
      list.push(this.name + 'Columns');
      return list;
    }
    getDetailLinkHtml(id, name) {
      // Do not use the `html` method to avoid XSS.

      name = name || this.nameHash[id] || id;
      if (!name && id) {
        name = this.translate(this.foreignScope, 'scopeNames');
      }
      let role = (this.columns[id] || {})[this.columnName] || '';
      if (this.emptyRoleValue && role === this.emptyRoleValue) {
        role = '';
      }
      const $el = $('<div>').append($('<a>').attr('href', '#' + this.foreignScope + '/view/' + id).attr('data-id', id).text(name));
      if (this.isDetailMode()) {
        const iconHtml = this.getIconHtml(id);
        if (iconHtml) {
          $el.prepend(iconHtml);
        }
      }
      if (role) {
        let style = this.styleMap[role];
        let className = 'text';
        if (this.displayRoleAsLabel && style && style !== 'default') {
          className = 'label label-sm label';
          if (style === 'muted') {
            style = 'default';
          }
        } else {
          style = style || 'muted';
        }
        className = className + '-' + style;
        const text = this.roleType === this.ROLE_TYPE_ENUM ? this.getLanguage().translateOption(role, this.roleField, this.roleFieldScope) : role;
        $el.append($('<span>').text(' '), $('<span>').addClass('text-muted middle-dot'), $('<span>').text(' '), $('<span>').text(text).addClass('small').addClass(className));
      }
      return $el.get(0).outerHTML;
    }
    getValueForDisplay() {
      if (this.isDetailMode() || this.isListMode()) {
        const names = [];
        this.ids.forEach(id => {
          names.push(this.getDetailLinkHtml(id));
        });
        return names.join('');
      }
    }
    deleteLink(id) {
      this.trigger('delete-link', id);
      this.trigger('delete-link:' + id);
      this.deleteLinkHtml(id);
      const index = this.ids.indexOf(id);
      if (index > -1) {
        this.ids.splice(index, 1);
      }
      delete this.nameHash[id];
      delete this.columns[id];
      this.afterDeleteLink(id);
      this.trigger('change');
    }
    addLink(id, name) {
      if (!~this.ids.indexOf(id)) {
        this.ids.push(id);
        this.nameHash[id] = name;
        this.columns[id] = {};
        this.columns[id][this.columnName] = null;
        this.addLinkHtml(id, name);
        this.trigger('add-link', id);
        this.trigger('add-link:' + id);
      }
      this.trigger('change');
    }

    /**
     * Build a role select element.
     *
     * @param {string} id
     * @param {string|null} roleValue
     * @return {JQuery}
     */
    getJQSelect(id, roleValue) {
      // Do not use the `html` method to avoid XSS.

      const $role = $('<select>').addClass('role form-control input-sm').attr('data-id', id);
      this.roleList.forEach(role => {
        const text = this.getLanguage().translateOption(role, this.roleField, this.roleFieldScope);
        const $option = $('<option>').val(role).text(text);
        if (role === (roleValue || '')) {
          $option.attr('selected', 'selected');
        }
        const style = this.styleMap[role];
        if (style) {
          $option.addClass('text-' + style);
        }
        $role.append($option);
      });
      return $role;
    }

    /**
     * @inheritDoc
     */
    addLinkHtml(id, name) {
      // Do not use the `html` method to avoid XSS.

      name = name || id;
      if (this.isSearchMode() || this.skipRoles) {
        return super.addLinkHtml(id, name);
      }
      const $container = this.$el.find('.link-container');
      const itemElement = this.prepareEditItemElement(id, name);
      $container.append(itemElement);
      const $el = $(itemElement);
      let $role;
      if (this.roleType === this.ROLE_TYPE_ENUM) {
        $role = $el.find('select.role');
      } else {
        $role = $el.find('input.role');
      }
      if ($role && $role.get(0) && $role.get(0).tagName === 'SELECT') {
        _select.default.init($role);
      }
      if (this.isEditMode() && $role) {
        const fetch = $target => {
          if (!$target || !$target.length) {
            return;
          }
          if ($target.val() === null) {
            return;
          }
          let value = $target.val().toString().trim();
          const id = $target.data('id');
          if (value === '') {
            value = null;
          }
          this.columns[id] = this.columns[id] || {};
          this.columns[id][this.columnName] = value;
        };
        $role.on('change', e => {
          fetch($(e.currentTarget));
          this.trigger('change');
        });
        fetch($role);
      }
      return $el;
    }

    /**
     * @inheritDoc
     */
    prepareEditItemElement(id, name) {
      if (this.isSearchMode() || this.skipRoles) {
        return super.prepareEditItemElement(id, name);
      }
      const role = (this.columns[id] || {})[this.columnName];
      const $el = $('<div>').addClass('form-inline clearfix').addClass('list-group-item link-with-role link-group-item-with-columns').addClass('link-' + id);
      const $remove = $('<a>').attr('role', 'button').attr('tabindex', '0').attr('data-id', id).attr('data-action', 'clearLink').addClass('pull-right').append($('<span>').addClass('fas fa-times'));
      const $left = $('<div>').addClass('pull-left');
      const $right = $('<div>').append($remove);
      const $name = $('<div>').addClass('link-item-name').text(name).append('&nbsp;');
      let $role;
      if (this.roleType === this.ROLE_TYPE_ENUM) {
        $role = this.getJQSelect(id, role);
      } else {
        const text = this.rolePlaceholderText || this.translate(this.roleField, 'fields', this.roleFieldScope);
        $role = $('<input>').addClass('role form-control input-sm').attr('maxlength', this.roleMaxLength) // @todo Get the value from metadata.
        .attr('placeholder', text).attr('data-id', id).attr('value', role || '');
      }
      if ($role) {
        $left.append($('<span>').addClass('link-item-column').addClass('link-item-column-' + $role.get(0).tagName.toLowerCase()).append($role));
      }
      $left.append($name);
      $el.append($left).append($right);
      return $el.get(0);
    }
    fetch() {
      const data = super.fetch();
      if (!this.skipRoles) {
        data[this.columnsName] = Espo.Utils.cloneDeep(this.columns);
      }
      return data;
    }
  }

  // noinspection JSDeprecatedSymbols
  var _default = _exports.default = LinkMultipleWithRoleFieldView;
});

define("views/fields/link-multiple-with-primary", ["exports", "views/fields/link-multiple"], function (_exports, _linkMultiple) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkMultiple = _interopRequireDefault(_linkMultiple);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * A link-multiple field with a primary.
   */
  class LinkMultipleWithPrimaryFieldView extends _linkMultiple.default {
    /**
     * @protected
     * @type {string}
     */
    primaryLink;
    switchPrimary(id) {
      const $switch = this.$el.find(`[data-id="${id}"][data-action="switchPrimary"]`);
      if (!$switch.hasClass('active')) {
        this.$el.find('button[data-action="switchPrimary"]').removeClass('active').children().addClass('text-muted');
        $switch.addClass('active').children().removeClass('text-muted');
        this.setPrimaryId(id);
        this.trigger('change');
      }
    }

    /**
     * @inheritDoc
     */
    getAttributeList() {
      const list = super.getAttributeList();
      list.push(this.primaryIdAttribute);
      list.push(this.primaryNameAttribute);
      return list;
    }
    setup() {
      this.primaryLink = this.options.primaryLink || this.primaryLink || this.model.getFieldParam(this.name, 'primaryLink');
      this.primaryIdAttribute = this.primaryLink + 'Id';
      this.primaryNameAttribute = this.primaryLink + 'Name';
      super.setup();
      this.primaryId = this.model.get(this.primaryIdAttribute);
      this.primaryName = this.model.get(this.primaryNameAttribute);
      this.listenTo(this.model, 'change:' + this.primaryIdAttribute, () => {
        this.primaryId = this.model.get(this.primaryIdAttribute);
        this.primaryName = this.model.get(this.primaryNameAttribute);
      });
      this.events['click [data-action="switchPrimary"]'] = e => {
        const $target = $(e.currentTarget);
        const id = $target.data('id');
        this.switchPrimary(id);
      };
    }

    /**
     * @protected
     * @param {string|null} id An ID.
     */
    setPrimaryId(id) {
      this.primaryId = id;
      this.primaryName = id ? this.nameHash[id] : null;
    }

    /**
     * @protected
     */
    renderLinks() {
      if (this.primaryId) {
        this.addLinkHtml(this.primaryId, this.primaryName);
      }
      this.ids.forEach(id => {
        if (id !== this.primaryId) {
          this.addLinkHtml(id, this.nameHash[id]);
        }
      });
    }

    /**
     * @inheritDoc
     */
    getValueForDisplay() {
      if (this.isDetailMode() || this.isListMode()) {
        const itemList = [];
        if (this.primaryId) {
          itemList.push(this.getDetailLinkHtml(this.primaryId, this.primaryName));
        }
        if (!this.ids.length) {
          return;
        }
        this.ids.forEach(id => {
          if (id !== this.primaryId) {
            itemList.push(this.getDetailLinkHtml(id));
          }
        });
        return itemList.map(item => $('<div>').addClass('link-multiple-item').append(item).get(0).outerHTML).join('');
      }
    }

    /**
     * @inheritDoc
     */
    deleteLink(id) {
      if (id === this.primaryId) {
        this.setPrimaryId(null);
      }
      super.deleteLink(id);
    }

    /**
     * @inheritDoc
     */
    deleteLinkHtml(id) {
      super.deleteLinkHtml(id);
      this.managePrimaryButton();
    }

    /**
     * @inheritDoc
     */
    addLinkHtml(id, name) {
      // Do not use the `html` method to avoid XSS.

      name = name || id;
      if (this.isSearchMode()) {
        return super.addLinkHtml(id, name);
      }
      const $container = this.$el.find('.link-container');
      const $el = $('<div>').addClass('form-inline clearfix ').addClass('list-group-item link-with-role link-group-item-with-primary').addClass('link-' + id).attr('data-id', id);
      const $name = $('<div>').text(name).append('&nbsp;');
      const $remove = $('<a>').attr('role', 'button').attr('tabindex', '0').attr('data-id', id).attr('data-action', 'clearLink').addClass('pull-right').append($('<span>').addClass('fas fa-times'));
      const $left = $('<div>');
      const $right = $('<div>');
      $left.append($name);
      $right.append($remove);
      $el.append($left);
      $el.append($right);
      const isPrimary = id === this.primaryId;
      const $star = $('<span>').addClass('fas fa-star fa-sm').addClass(!isPrimary ? 'text-muted' : '');
      const $button = $('<button>').attr('type', 'button').addClass('btn btn-link btn-sm pull-right hidden').attr('title', this.translate('Primary')).attr('data-action', 'switchPrimary').attr('data-id', id).append($star);
      $button.insertBefore($el.children().first().children().first());
      $container.append($el);
      this.managePrimaryButton();
      return $el;
    }

    /**
     * @protected
     */
    managePrimaryButton() {
      const $primary = this.$el.find('button[data-action="switchPrimary"]');
      if ($primary.length > 1) {
        $primary.removeClass('hidden');
      } else {
        $primary.addClass('hidden');
      }
      if ($primary.filter('.active').length === 0) {
        const $first = $primary.first();
        if ($first.length) {
          $first.addClass('active').children().removeClass('text-muted');
          const id = $first.data('id');
          this.setPrimaryId(id);
          if (id !== this.primaryId) {
            this.trigger('change');
          }
        }
      }
    }
    fetch() {
      const data = super.fetch();
      data[this.primaryIdAttribute] = this.primaryId;
      data[this.primaryNameAttribute] = this.primaryName;

      // noinspection JSValidateTypes
      return data;
    }
  }
  var _default = _exports.default = LinkMultipleWithPrimaryFieldView;
});

define("views/fields/link-multiple-with-columns", ["exports", "views/fields/link-multiple", "helpers/reg-exp-pattern", "ui/select", "ui/autocomplete"], function (_exports, _linkMultiple, _regExpPattern, _select, _autocomplete) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkMultiple = _interopRequireDefault(_linkMultiple);
  _regExpPattern = _interopRequireDefault(_regExpPattern);
  _select = _interopRequireDefault(_select);
  _autocomplete = _interopRequireDefault(_autocomplete);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/link-multiple-with-columns */

  /**
   * A link-multiple field with relation column(s).
   */
  class LinkMultipleWithColumnsFieldView extends _linkMultiple.default {
    /** @const */
    COLUMN_TYPE_VARCHAR = 'varchar';
    /** @const */
    COLUMN_TYPE_ENUM = 'enum';
    /** @const */
    COLUMN_TYPE_BOOL = 'bool';

    /** @inheritDoc */
    setup() {
      super.setup();
      const columnsDefsInitial = this.columnsDefs || {};
      this.validations.push('columnPattern');

      /**
       * @type {Object.<string,*>}
       */
      this.columnsDefs = {};
      this.columnsName = this.name + 'Columns';
      this.columns = Espo.Utils.cloneDeep(this.model.get(this.columnsName) || {});
      this.listenTo(this.model, 'change:' + this.columnsName, () => {
        this.columns = Espo.Utils.cloneDeep(this.model.get(this.columnsName) || {});
      });
      const columns = this.getMetadata().get(['entityDefs', this.model.entityType, 'fields', this.name, 'columns']) || {};

      /** @type {string[]} */
      this.columnList = this.columnList || Object.keys(columns);
      this.columnList.forEach(column => {
        if (column in columnsDefsInitial) {
          this.columnsDefs[column] = Espo.Utils.cloneDeep(columnsDefsInitial[column]);
          return;
        }
        if (column in columns) {
          const field = columns[column];
          const o = {};
          o.field = field;
          o.scope = this.foreignScope;
          if (!this.getMetadata().get(['entityDefs', this.foreignScope, 'fields', field, 'type']) && this.getMetadata().get(['entityDefs', this.model.entityType, 'fields', field, 'type'])) {
            o.scope = this.model.entityType;
          }
          const fieldDefs = this.getMetadata().get(['entityDefs', o.scope, 'fields', field]) || {};
          o.type = fieldDefs.type;
          if (o.type === this.COLUMN_TYPE_ENUM || o.type === this.COLUMN_TYPE_VARCHAR) {
            o.options = fieldDefs.options;
          }
          if ('default' in fieldDefs) {
            o.default = fieldDefs.default;
          }
          if ('maxLength' in fieldDefs) {
            o.maxLength = fieldDefs.maxLength;
          }
          if ('pattern' in fieldDefs) {
            o.pattern = fieldDefs.pattern;
          }
          this.columnsDefs[column] = o;
        }
      });
      if (this.isEditMode() || this.isDetailMode()) {
        this.events['click a[data-action="toggleBoolColumn"]'] = e => {
          const id = $(e.currentTarget).data('id');
          const column = $(e.currentTarget).data('column');
          this.toggleBoolColumn(id, column);
        };
      }
      this.on('render', this.disposeColumnAutocompletes, this);
      this.once('remove', this.disposeColumnAutocompletes, this);
    }
    toggleBoolColumn(id, column) {
      this.columns[id][column] = !this.columns[id][column];
      this.reRender();
    }

    /** @inheritDoc */
    getAttributeList() {
      return [...super.getAttributeList(), this.name + 'Columns'];
    }

    /**
     * Get an item HTML for detail mode.
     *
     * @param {string} id An ID.
     * @param {string} [name] An name.
     * @return {string}
     */
    getDetailLinkHtml(id, name) {
      // Do not use the `html` method to avoid XSS.

      name = name || this.nameHash[id] || id;
      const $a = $('<a>').attr('href', '#' + this.foreignScope + '/view/' + id).attr('data-id', id).text(name);
      if (this.mode === this.MODE_LIST) {
        $a.addClass('text-default');
      }
      const $el = $('<div>').append($a);
      if (this.isDetailMode()) {
        const iconHtml = this.getIconHtml(id);
        if (iconHtml) {
          $el.prepend(iconHtml);
        }
      }
      this.columnList.forEach(column => {
        const value = (this.columns[id] || {})[column] || '';
        const type = this.columnsDefs[column].type;
        const field = this.columnsDefs[column].field;
        const scope = this.columnsDefs[column].scope;
        if (type !== this.COLUMN_TYPE_ENUM && type !== this.COLUMN_TYPE_VARCHAR) {
          return;
        }
        const text = type === this.COLUMN_TYPE_ENUM ? this.getLanguage().translateOption(value, field, scope) : value;
        if (!text) {
          return;
        }
        $el.append($('<span>').text(' '), $('<span>').addClass('text-muted middle-dot'), $('<span>').text(' '), $('<span>').text(text).addClass('text-muted small'));
      });
      return $el.get(0).innerHTML;
    }

    /** @inheritDoc */
    getValueForDisplay() {
      if (this.isDetailMode() || this.isListMode()) {
        const itemList = [];
        this.ids.forEach(id => {
          itemList.push(this.getDetailLinkHtml(id));
        });
        return itemList.map(item => {
          return $('<div>').addClass('link-multiple-item').html(item).get(0).outerHTML;
        }).join('');
      }
    }

    /** @inheritDoc */
    deleteLink(id) {
      this.trigger('delete-link', id);
      this.trigger('delete-link:' + id);
      this.deleteLinkHtml(id);
      const index = this.ids.indexOf(id);
      if (index > -1) {
        this.ids.splice(index, 1);
      }
      delete this.nameHash[id];
      delete this.columns[id];
      this.afterDeleteLink(id);
      this.trigger('change');
    }

    /**
     * Get a column values.
     *
     * @param {string} id An ID.
     * @param {string} column A column.
     * @return {*}
     */
    getColumnValue(id, column) {
      return (this.columns[id] || {})[column];
    }
    addLink(id, name) {
      if (!~this.ids.indexOf(id)) {
        this.ids.push(id);
        this.nameHash[id] = name;
        this.columns[id] = {};
        this.columnList.forEach(column => {
          this.columns[id][column] = null;
          if ('default' in this.columnsDefs[column]) {
            this.columns[id][column] = this.columnsDefs[column].default;
          }
        });
        this.addLinkHtml(id, name);
        this.afterAddLink(id);
        this.trigger('add-link', id);
        this.trigger('add-link:' + id);
      }
      this.trigger('change');
    }

    /**
     * @param {string} column
     * @param {string} id
     * @param {*} value
     * @return {JQuery}
     */
    getJQSelect(column, id, value) {
      // Do not use the `html` method to avoid XSS.

      const field = this.columnsDefs[column].field;
      const scope = this.columnsDefs[column].scope;
      const options = this.columnsDefs[column].options || [];
      const $select = $('<select>').addClass('role form-control input-sm').attr('data-id', id).attr('data-column', column);
      options.forEach(itemValue => {
        const text = this.getLanguage().translateOption(itemValue, field, scope);
        const $option = $('<option>').val(itemValue).text(text);
        if (itemValue === (value || '')) {
          $option.attr('selected', 'selected');
        }
        $select.append($option);
      });
      return $select;
    }

    /**
     * @param {string} column
     * @param {string} id
     * @param {*} value
     * @return {JQuery}
     */
    getJQInput(column, id, value) {
      // Do not use the `html` method to avoid XSS.

      const field = this.columnsDefs[column].field;
      const scope = this.columnsDefs[column].scope;
      const maxLength = this.columnsDefs[column].maxLength;
      const text = this.translate(field, 'fields', scope);
      const $input = $('<input>').addClass('role form-control input-sm').attr('data-column', column).attr('placeholder', text).attr('data-id', id).attr('value', value || '');
      if (maxLength) {
        $input.attr('maxlength', maxLength);
      }
      return $input;
    }

    /**
     * @param {string} column
     * @param {string} id
     * @param {*} value
     * @return {JQuery}
     */
    getJQLi(column, id, value) {
      // Do not use the `html` method to avoid XSS.

      const field = this.columnsDefs[column].field;
      const scope = this.columnsDefs[column].scope;
      const text = this.translate(field, 'fields', scope);
      return $('<li>').append($('<a>').attr('role', 'button').attr('tabindex', '0').attr('data-action', 'toggleBoolColumn').attr('data-column', column).attr('data-id', id).append($('<span>').addClass('check-icon fas fa-check pull-right').addClass(!value ? 'hidden' : '')).append($('<div>').text(text)));
    }

    /** @inheritDoc */
    addLinkHtml(id, name) {
      if (this.isSearchMode()) {
        return super.addLinkHtml(id, name);
      }

      // Do not use the `html` method to avoid XSS.

      const $container = this.$el.find('.link-container');
      const $el = $('<div>').addClass('form-inline clearfix').addClass('list-group-item link-with-role link-group-item-with-columns').addClass('link-' + id);
      const $remove = $('<a>').attr('role', 'button').attr('tabindex', '0').attr('data-id', id).attr('data-action', 'clearLink').addClass('pull-right').append($('<span>').addClass('fas fa-times'));
      const $name = $('<div>').addClass('link-item-name').text(name).append('&nbsp;');
      const $columnList = [];
      const $liList = [];
      this.columnList.forEach(column => {
        const value = (this.columns[id] || {})[column];
        const type = this.columnsDefs[column].type;
        if (type === this.COLUMN_TYPE_ENUM) {
          $columnList.push(this.getJQSelect(column, id, value));
          return;
        }
        if (type === this.COLUMN_TYPE_VARCHAR) {
          $columnList.push(this.getJQInput(column, id, value));
          return;
        }
        if (type === this.COLUMN_TYPE_BOOL) {
          $liList.push(this.getJQLi(column, id, value));
        }
      });
      const $left = $('<div>');
      const $right = $('<div>');
      $columnList.forEach($item => $left.append($('<span>').addClass('link-item-column').addClass('link-item-column-' + $item.get(0).tagName.toLowerCase()).append($item)));
      if ($liList.length) {
        const $ul = $('<ul>').addClass('dropdown-menu');
        $liList.forEach($item => $ul.append($item));
        $left.append($('<div>').addClass('btn-group pull-right').append($('<button>').attr('type', 'button').attr('data-toggle', 'dropdown').addClass('btn btn-link btn-sm dropdown-toggle').append($('<span>').addClass('caret'))).append($ul));
      }
      $left.append($name);
      $right.append($remove);
      $el.append($left);
      $el.append($right);
      $container.append($el);
      if (this.isEditMode()) {
        $columnList.forEach($column => {
          if ($column.get(0) && $column.get(0).tagName === 'SELECT') {
            _select.default.init($column);
          }
          const fetch = $target => {
            if (!$target || !$target.length) {
              return;
            }
            const column = $target.data('column');
            let value = $target.val().toString().trim();
            const id = $target.data('id');
            if (value === '') {
              value = null;
            }
            this.columns[id] = this.columns[id] || {};
            this.columns[id][column] = value;
          };
          $column.on('change', e => {
            const $target = $(e.currentTarget);
            fetch($target);
            this.trigger('change');
          });
          fetch($column);
        });
        this.initAutocomplete(id);
      }
      return $el;
    }
    initAutocomplete(id) {
      if (!this._autocompleteList) {
        /** @type {Autocomplete[]} */
        this._autocompleteList = [];
      }
      this.columnList.forEach(column => {
        const type = this.columnsDefs[column].type;
        if (type !== this.COLUMN_TYPE_VARCHAR) {
          return;
        }
        const options = this.columnsDefs[column].options;
        if (!(options && options.length)) {
          return;
        }
        const $element = this.$el.find(`[data-column="${column}"][data-id="${id}"]`);
        if (!$element.length) {
          return;
        }
        const autocomplete = new _autocomplete.default($element.get(0), {
          name: this.name + 'Column' + id,
          triggerSelectOnValidInput: true,
          autoSelectFirst: true,
          handleFocusMode: 1,
          focusOnSelect: true,
          onSelect: () => {
            this.trigger('change');
            $element.trigger('change');
          },
          lookup: options
        });
        this._autocompleteList.push(autocomplete);
        this.once('delete-link:' + id, () => autocomplete.dispose());
      });
    }
    disposeColumnAutocompletes() {
      if (this._autocompleteList && this._autocompleteList.length) {
        this._autocompleteList.forEach(autocomplete => {
          autocomplete.dispose();
        });
        this._autocompleteList = [];
      }
    }

    // noinspection JSUnusedGlobalSymbols
    validateColumnPattern() {
      let result = false;
      const columnList = this.columnList.filter(column => this.columnsDefs[column].type === this.COLUMN_TYPE_VARCHAR).filter(column => this.columnsDefs[column].pattern);
      for (const column of columnList) {
        for (const id of this.ids) {
          const value = this.getColumnValue(id, column);
          if (!value) {
            continue;
          }
          if (this.validateColumnPatternValue(id, column, value)) {
            result = true;
          }
        }
      }
      return result;
    }
    validateColumnPatternValue(id, column, value) {
      const pattern = this.columnsDefs[column].pattern;
      const field = this.columnsDefs[column].field;
      const scope = this.columnsDefs[column].scope;
      const helper = new _regExpPattern.default();
      const result = helper.validate(pattern, value, field, scope);
      if (!result) {
        return false;
      }
      this.showValidationMessage(result.message, '[data-column="' + column + '"][data-id="' + id + '"]');
      return true;
    }
    fetch() {
      const data = super.fetch();
      data[this.columnsName] = Espo.Utils.cloneDeep(this.columns);

      // noinspection JSValidateTypes
      return data;
    }
  }
  var _default = _exports.default = LinkMultipleWithColumnsFieldView;
});

define("views/fields/image", ["exports", "views/fields/file"], function (_exports, _file) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _file = _interopRequireDefault(_file);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ImageFieldView extends _file.default {
    type = 'image';
    showPreview = true;
    accept = ['image/*'];
    defaultType = 'image/jpeg';
    previewSize = 'small';
  }
  var _default = _exports.default = ImageFieldView;
});

define("views/fields/foreign-array", ["exports", "views/fields/array"], function (_exports, _array) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _array = _interopRequireDefault(_array);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignArrayFieldView extends _array.default {
    type = 'foreign';
    setupOptions() {
      this.params.options = [];
      const field = this.params.field;
      const link = this.params.link;
      if (!field || !link) {
        return;
      }
      const scope = this.getMetadata().get(['entityDefs', this.model.entityType, 'links', link, 'entity']);
      if (!scope) {
        return;
      }
      let {
        optionsPath,
        translation,
        options,
        isSorted,
        displayAsLabel,
        style
      } = this.getMetadata().get(['entityDefs', scope, 'fields', field]);
      options = optionsPath ? this.getMetadata().get(optionsPath) : options;
      this.params.options = Espo.Utils.clone(options) || [];
      this.params.translation = translation;
      this.params.isSorted = isSorted || false;
      this.params.displayAsLabel = displayAsLabel || false;
      this.styleMap = style || {};
      this.translatedOptions = Object.fromEntries(this.params.options.map(item => [item, this.getLanguage().translateOption(item, field, scope)]));
    }
  }
  var _default = _exports.default = ForeignArrayFieldView;
});

define("views/fields/enum-int", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EnumIntFieldView extends _enum.default {
    type = 'enumInt';
    listTemplate = 'fields/enum/detail';
    detailTemplate = 'fields/enum/detail';
    editTemplate = 'fields/enum/edit';
    searchTemplate = 'fields/enum/search';
    validations = [];
    fetch() {
      const raw = this.$element.val();
      if (raw === '') {
        return {
          [this.name]: null
        };
      }
      const value = parseInt(raw);
      const data = {};
      data[this.name] = value;
      return data;
    }
    parseItemForSearch(item) {
      return parseInt(item);
    }
  }
  var _default = _exports.default = EnumIntFieldView;
});

define("views/fields/entity-type", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EntityTypeFieldView extends _enum.default {
    /**
     * @type {Record<string, Record>}
     * @protected
     */
    scopesMetadataDefs;

    /**
     * @param {string} entityType
     * @return {boolean}
     */
    checkAvailability(entityType) {
      const defs = this.scopesMetadataDefs[entityType] || {};
      if (defs.entity && defs.object) {
        return true;
      }
    }
    setupOptions() {
      const scopes = this.scopesMetadataDefs = this.getMetadata().get('scopes');
      this.params.options = Object.keys(scopes).filter(scope => {
        if (this.checkAvailability(scope)) {
          return true;
        }
      }).sort((v1, v2) => {
        return this.translate(v1, 'scopeNames').localeCompare(this.translate(v2, 'scopeNames'));
      });
      this.params.options.unshift('');
    }
    setup() {
      this.params.translation = 'Global.scopeNames';
      this.setupOptions();
      super.setup();
    }
  }
  var _default = _exports.default = EntityTypeFieldView;
});

define("views/fields/email", ["exports", "views/fields/varchar", "helpers/misc/mailto", "ui/autocomplete"], function (_exports, _varchar, _mailto, _autocomplete) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  _mailto = _interopRequireDefault(_mailto);
  _autocomplete = _interopRequireDefault(_autocomplete);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/email */

  /**
   * @extends VarcharFieldView<module:views/fields/email~params>
   */
  class EmailFieldView extends _varchar.default {
    /**
     * @typedef {Object} module:views/fields/email~options
     * @property {
     *     module:views/fields/email~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/email~params
     * @property {boolean} [required] Required.
     * @property {boolean} [onlyPrimary] Only primary.
     */

    /**
     * @param {
     *     module:views/fields/email~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'email';
    editTemplate = 'fields/email/edit';
    detailTemplate = 'fields/email/detail';
    listTemplate = 'fields/email/list';

    /**
     * @inheritDoc
     * @type {Array<(function (): boolean)|string>}
     */
    validations = ['required', 'emailData'];
    events = {
      /** @this EmailFieldView */
      'click [data-action="mailTo"]': function (e) {
        this.mailTo($(e.currentTarget).data('email-address'));
      },
      /** @this EmailFieldView */
      'click [data-action="switchEmailProperty"]': function (e) {
        const $target = $(e.currentTarget);
        const $block = $(e.currentTarget).closest('div.email-address-block');
        const property = $target.data('property-type');
        const $input = $block.find('input.email-address');
        if (property === 'primary') {
          if (!$target.hasClass('active')) {
            if ($input.val() !== '') {
              this.$el.find('button.email-property[data-property-type="primary"]').removeClass('active').children().addClass('text-muted');
              $target.addClass('active').children().removeClass('text-muted');
            }
          }
          this.trigger('change');
          return;
        }
        let active = false;
        if ($target.hasClass('active')) {
          $target.removeClass('active').children().addClass('text-muted');
        } else {
          $target.addClass('active').children().removeClass('text-muted');
          active = true;
        }
        if (property === 'optOut') {
          active ? $input.addClass('text-strikethrough') : $input.removeClass('text-strikethrough');
        }
        if (property === 'invalid') {
          active ? $input.addClass('text-danger') : $input.removeClass('text-danger');
        }
        this.trigger('change');
      },
      /** @this EmailFieldView */
      'click [data-action="removeEmailAddress"]': function (e) {
        const $block = $(e.currentTarget).closest('div.email-address-block');
        this.removeEmailAddress($block);
        const $last = this.$el.find('.email-address').last();
        if ($last.length) {
          // noinspection JSUnresolvedReference
          $last[0].focus({
            preventScroll: true
          });
        }
      },
      /** @this EmailFieldView */
      'change input.email-address': function (e) {
        const $input = $(e.currentTarget);
        const $block = $input.closest('div.email-address-block');
        if (this._itemJustRemoved) {
          return;
        }
        if ($input.val() === '' && $block.length) {
          this.removeEmailAddress($block);
        } else {
          this.trigger('change');
        }
        this.trigger('change');
        this.manageAddButton();
      },
      /** @this EmailFieldView */
      'keypress input.email-address': function () {
        this.manageAddButton();
      },
      /** @this EmailFieldView */
      'paste input.email-address': function () {
        setTimeout(() => this.manageAddButton(), 10);
      },
      /** @this EmailFieldView */
      'click [data-action="addEmailAddress"]': function () {
        this.addEmailAddress();
      },
      /** @this EmailFieldView */
      'keydown input.email-address': function (e) {
        const key = Espo.Utils.getKeyFromKeyEvent(e);
        const $target = $(e.currentTarget);
        if (key === 'Enter') {
          if (!this.$el.find('[data-action="addEmailAddress"]').hasClass('disabled')) {
            this.addEmailAddress();
            e.stopPropagation();
          }
          return;
        }
        if (key === 'Backspace' && $target.val() === '') {
          const $block = $target.closest('div.email-address-block');
          this._itemJustRemoved = true;
          setTimeout(() => this._itemJustRemoved = false, 100);
          e.stopPropagation();
          this.removeEmailAddress($block);
          setTimeout(() => this.focusOnLast(true), 50);
        }
      }
    };

    // noinspection JSUnusedGlobalSymbols
    validateEmailData() {
      const data = this.model.get(this.dataFieldName);
      if (!data || !data.length) {
        return;
      }
      const addressList = [];
      const regExp = new RegExp(/^[-!#$%&'*+/=?^_`{|}~A-Za-z0-9]+(?:\.[-!#$%&'*+/=?^_`{|}~A-Za-z0-9]+)*/.source + /@([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?\.)+[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9]/.source);
      let notValid = false;
      data.forEach((row, i) => {
        const address = row.emailAddress || '';
        const addressLowerCase = String(address).toLowerCase();
        if (!regExp.test(addressLowerCase) && address.indexOf(this.erasedPlaceholder) !== 0) {
          const msg = this.translate('fieldShouldBeEmail', 'messages').replace('{field}', this.getLabelText());
          this.reRender();
          this.showValidationMessage(msg, 'div.email-address-block:nth-child(' + (i + 1).toString() + ') input');
          notValid = true;
          return;
        }
        if (addressList.includes(addressLowerCase)) {
          const msg = this.translate('fieldValueDuplicate', 'messages').replace('{field}', this.getLabelText());
          this.showValidationMessage(msg, 'div.email-address-block:nth-child(' + (i + 1).toString() + ') input');
          notValid = true;
          return;
        }
        addressList.push(addressLowerCase);
      });
      if (notValid) {
        return true;
      }
    }
    validateRequired() {
      if (this.isRequired()) {
        if (!this.model.get(this.name)) {
          const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
          this.showValidationMessage(msg, 'div.email-address-block:nth-child(1) input');
          return true;
        }
      }
    }
    validateMaxCount() {
      /** @type {number|null} */
      const maxCount = this.maxCount;
      if (!maxCount) {
        return false;
      }
      const items = this.model.attributes[this.dataFieldName] || [];
      if (items.length <= maxCount) {
        return false;
      }
      const msg = this.translate('fieldExceedsMaxCount', 'messages').replace('{maxCount}', maxCount.toString());
      this.showValidationMessage(msg, 'div.email-address-block:last-child input');
      return true;
    }
    data() {
      let emailAddressData;
      if (this.mode === this.MODE_EDIT) {
        emailAddressData = Espo.Utils.clone(this.model.get(this.dataFieldName));
        if (this.model.isNew() || !this.model.get(this.name)) {
          if (!emailAddressData || !emailAddressData.length) {
            let optOut;
            if (this.model.isNew()) {
              optOut = this.emailAddressOptedOutByDefault && this.model.entityType !== 'User';
            } else {
              optOut = this.model.get(this.isOptedOutFieldName);
            }
            emailAddressData = [{
              emailAddress: this.model.get(this.name) || '',
              primary: true,
              optOut: optOut,
              invalid: false
            }];
          }
        }
      } else {
        emailAddressData = this.model.get(this.dataFieldName) || false;
      }
      if ((!emailAddressData || emailAddressData.length === 0) && this.model.get(this.name)) {
        emailAddressData = [{
          emailAddress: this.model.get(this.name),
          primary: true,
          optOut: false,
          invalid: false
        }];
      }
      if (emailAddressData) {
        emailAddressData = Espo.Utils.cloneDeep(emailAddressData);
        emailAddressData.forEach(item => {
          const address = item.emailAddress || '';
          item.erased = address.indexOf(this.erasedPlaceholder) === 0;
          item.lineThrough = item.optOut || item.invalid;
        });
      }
      const data = {
        ...super.data(),
        emailAddressData: emailAddressData
      };
      if (this.isReadMode()) {
        data.isOptedOut = this.model.get(this.isOptedOutFieldName);
        data.isInvalid = this.model.get(this.isInvalidFieldName);
        if (this.model.get(this.name)) {
          data.isErased = this.model.get(this.name).indexOf(this.erasedPlaceholder) === 0;
        }
        data.valueIsSet = this.model.has(this.name);
      }
      data.itemMaxLength = this.itemMaxLength;
      data.onlyPrimary = this.params.onlyPrimary;
      return data;
    }
    getAutocompleteMaxCount() {
      if (this.autocompleteMaxCount) {
        return this.autocompleteMaxCount;
      }
      return this.getConfig().get('recordsPerPage');
    }
    focusOnLast(cursorAtEnd) {
      const $item = this.$el.find('input.form-control').last();
      $item.focus();
      if (cursorAtEnd && $item[0]) {
        // Not supported for email inputs.
        // $item[0].setSelectionRange($item[0].value.length, $item[0].value.length);
      }
    }
    removeEmailAddress($block) {
      if ($block.parent().children().length === 1) {
        $block.find('input.email-address').val('');
      } else {
        this.removeEmailAddressBlock($block);
      }
      this.trigger('change');
    }
    addEmailAddress() {
      const data = Espo.Utils.cloneDeep(this.fetchEmailAddressData());
      const o = {
        emailAddress: '',
        primary: !data.length,
        optOut: this.emailAddressOptedOutByDefault,
        invalid: false,
        lower: ''
      };
      data.push(o);
      this.model.set(this.dataFieldName, data, {
        silent: true
      });
      this.reRender().then(() => this.focusOnLast());
    }
    removeEmailAddressBlock($block) {
      let changePrimary = false;
      if ($block.find('button[data-property-type="primary"]').hasClass('active')) {
        changePrimary = true;
      }
      $block.remove();
      if (changePrimary) {
        this.$el.find('button[data-property-type="primary"]').first().addClass('active').children().removeClass('text-muted');
      }
      this.manageButtonsVisibility();
      this.manageAddButton();
    }
    afterRender() {
      super.afterRender();
      this.manageButtonsVisibility();
      this.manageAddButton();
      if (this.mode === this.MODE_SEARCH) {
        const autocomplete = new _autocomplete.default(this.$element.get(0), {
          name: this.name,
          autoSelectFirst: true,
          triggerSelectOnValidInput: true,
          focusOnSelect: true,
          minChars: 1,
          forceHide: true,
          handleFocusMode: 1,
          onSelect: item => {
            this.$element.val(item.emailAddress);
          },
          formatResult: item => {
            return this.getHelper().escapeString(item.name) + ' &#60;' + this.getHelper().escapeString(item.id) + '&#62;';
          },
          lookupFunction: query => {
            return Espo.Ajax.getRequest('EmailAddress/search', {
              q: query,
              maxSize: this.getAutocompleteMaxCount(),
              entityType: this.entityType
            }).then(/** Record[] */response => {
              return response.map(item => {
                return {
                  id: item.emailAddress,
                  name: item.entityName,
                  emailAddress: item.emailAddress,
                  entityId: item.entityId,
                  entityName: item.entityName,
                  entityType: item.entityType,
                  data: item.emailAddress,
                  value: item.emailAddress
                };
              });
            });
          }
        });
        this.once('render remove', () => autocomplete.dispose());
      }
    }
    manageAddButton() {
      const $input = this.$el.find('input.email-address');
      let c = 0;
      $input.each((i, /** HTMLInputElement */input) => {
        if (input.value !== '') {
          c++;
        }
      });
      if (c === $input.length) {
        this.$el.find('[data-action="addEmailAddress"]').removeClass('disabled').removeAttr('disabled');
        return;
      }
      this.$el.find('[data-action="addEmailAddress"]').addClass('disabled').attr('disabled', 'disabled');
    }
    manageButtonsVisibility() {
      const $primary = this.$el.find('button[data-property-type="primary"]');
      const $remove = this.$el.find('button[data-action="removeEmailAddress"]');
      if ($primary.length > 1) {
        $primary.removeClass('hidden');
        $remove.removeClass('hidden');
      } else {
        $primary.addClass('hidden');
        $remove.addClass('hidden');
      }
    }
    mailTo(emailAddress) {
      const attributes = {
        status: 'Draft',
        to: emailAddress
      };
      const scope = this.model.entityType;
      switch (scope) {
        case 'Account':
        case 'Lead':
          attributes.parentType = scope;
          attributes.parentName = this.model.get('name');
          attributes.parentId = this.model.id;
          break;
        case 'Contact':
          if (this.getConfig().get('b2cMode')) {
            attributes.parentType = 'Contact';
            attributes.parentName = this.model.get('name');
            attributes.parentId = this.model.id;
          } else {
            if (this.model.get('accountId')) {
              attributes.parentType = 'Account';
              attributes.parentName = this.model.get('accountName');
              attributes.parentId = this.model.get('accountId');
            }
          }
          break;
      }
      if (this.model.collection && 'patentModel' in this.model.collection && this.model.collection.parentModel) {
        if (this.checkParentTypeAvailability(this.model.collection.parentModel.entityType)) {
          attributes.parentType = this.model.collection.parentModel.entityType;
          attributes.parentId = this.model.collection.parentModel.id;
          attributes.parentName = this.model.collection.parentModel.get('name');
        }
      }
      if (!attributes.parentId) {
        if (this.checkParentTypeAvailability(this.model.entityType)) {
          attributes.parentType = this.model.entityType;
          attributes.parentId = this.model.id;
          attributes.parentName = this.model.get('name');
        }
      } else {
        if (attributes.parentType && !this.checkParentTypeAvailability(attributes.parentType)) {
          attributes.parentType = null;
          attributes.parentId = null;
          attributes.parentName = null;
        }
      }
      if (['Contact', 'Lead', 'Account'].includes(this.model.entityType)) {
        attributes.nameHash = {};
        attributes.nameHash[emailAddress] = this.model.get('name');
      }
      const helper = new _mailto.default(this.getConfig(), this.getPreferences(), this.getAcl());
      if (helper.toUse()) {
        document.location.href = helper.composeLink(attributes);
        return;
      }
      const viewName = this.getMetadata().get('clientDefs.' + this.scope + '.modalViews.compose') || 'views/modals/compose-email';
      Espo.Ui.notifyWait();
      this.createView('quickCreate', viewName, {
        attributes: attributes
      }, view => {
        view.render();
        view.notify(false);
      });
    }
    checkParentTypeAvailability(parentType) {
      return ~(this.getMetadata().get(['entityDefs', 'Email', 'fields', 'parent', 'entityList']) || []).indexOf(parentType);
    }
    setup() {
      this.dataFieldName = this.name + 'Data';
      this.isOptedOutFieldName = this.name + 'IsOptedOut';
      this.isInvalidFieldName = this.name + 'IsInvalid';
      this.erasedPlaceholder = 'ERASED:';
      this.emailAddressOptedOutByDefault = this.getConfig().get('emailAddressIsOptedOutByDefault');
      this.maxCount = this.getConfig().get('emailAddressMaxCount');
      this.itemMaxLength = this.getMetadata().get(['entityDefs', 'EmailAddress', 'fields', 'name', 'maxLength']) || 255;
      this.validations.push(() => this.validateMaxCount());
    }

    /**
     * @return {{
     *     emailAddress: string,
     *     primary: boolean,
     *     optOut: boolean,
     *     invalid: boolean,
     *     lower: string,
     * }[]}
     */
    fetchEmailAddressData() {
      const data = [];
      const $list = this.$el.find('div.email-address-block');
      if ($list.length) {
        $list.each((i, d) => {
          const row = {};
          const $d = $(d);
          row.emailAddress = $d.find('input.email-address').val().trim();
          if (row.emailAddress === '') {
            return;
          }
          row.primary = $d.find('button[data-property-type="primary"]').hasClass('active');
          row.optOut = $d.find('button[data-property-type="optOut"]').hasClass('active');
          row.invalid = $d.find('button[data-property-type="invalid"]').hasClass('active');
          row.lower = row.emailAddress.toLowerCase();
          data.push(row);
        });
      }
      return data;
    }
    fetch() {
      const data = {};
      const addressData = this.fetchEmailAddressData();
      if (this.params.onlyPrimary) {
        if (addressData.length > 0) {
          data[this.name] = addressData[0].emailAddress;
          data[this.dataFieldName] = [{
            emailAddress: addressData[0].emailAddress,
            lower: addressData[0].lower,
            primary: true
          }];
        } else {
          data[this.name] = null;
          data[this.dataFieldName] = null;
        }
        return data;
      }
      data[this.dataFieldName] = addressData;
      data[this.name] = null;
      data[this.isOptedOutFieldName] = false;
      data[this.isInvalidFieldName] = false;
      let primaryIndex = 0;
      addressData.forEach((item, i) => {
        if (item.primary) {
          primaryIndex = i;
          if (item.optOut) {
            data[this.isOptedOutFieldName] = true;
          }
          if (item.invalid) {
            data[this.isInvalidFieldName] = true;
          }
        }
      });
      if (addressData.length && primaryIndex > 0) {
        const t = addressData[0];
        addressData[0] = addressData[primaryIndex];
        addressData[primaryIndex] = t;
      }
      if (addressData.length) {
        data[this.name] = addressData[0].emailAddress;
      } else {
        data[this.isOptedOutFieldName] = null;
        data[this.isInvalidFieldName] = null;
      }
      return data;
    }
    fetchSearch() {
      const type = this.fetchSearchType();
      if (['isEmpty', 'isNotEmpty'].includes(type)) {
        return {
          type: type === 'isEmpty' ? 'isNull' : 'isNotNull',
          attribute: this.name,
          data: {
            type: type
          }
        };
      }
      return super.fetchSearch();
    }
  }
  var _default = _exports.default = EmailFieldView;
});

define("views/fields/datetime-short", ["exports", "views/fields/datetime", "moment"], function (_exports, _datetime, _moment) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _datetime = _interopRequireDefault(_datetime);
  _moment = _interopRequireDefault(_moment);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/datetime-short */

  class DatetimeShortFieldView extends _datetime.default {
    /**
     * @protected
     * @type {boolean}
     */
    shortInListMode = true;

    /**
     * @protected
     * @type {boolean}
     */
    shortInDetailMode = true;
    data() {
      const data = super.data();
      if (this.toApplyShort()) {
        data.titleDateValue = super.getDateStringValue();
      }
      return data;
    }

    /**
     * @private
     * @return {boolean}
     */
    toApplyShort() {
      return this.shortInListMode && this.mode === this.MODE_LIST || this.shortInDetailMode && this.mode === this.MODE_DETAIL;
    }
    getDateStringValue() {
      if (!this.toApplyShort()) {
        return super.getDateStringValue();
      }
      const value = this.model.get(this.name);
      if (!value) {
        return super.getDateStringValue();
      }
      let timeFormat = this.getDateTime().timeFormat;
      if (this.params.hasSeconds) {
        timeFormat = timeFormat.replace(/:mm/, ':mm:ss');
      }
      const m = this.getDateTime().toMoment(value);
      const now = (0, _moment.default)().tz(this.getDateTime().timeZone || 'UTC');
      const dt = now.clone().startOf('day');
      const ranges = {
        'today': [dt.unix(), dt.add(1, 'days').unix()],
        'tomorrow': [dt.unix(), dt.add(1, 'days').unix()],
        'yesterday': [dt.add(-3, 'days').unix(), dt.add(1, 'days').unix()]
      };
      if (m.unix() > ranges['yesterday'][0] && m.unix() < ranges['yesterday'][1] && this.getLanguage().has('yesterdayShort', 'strings', 'Global')) {
        return this.translate('yesterdayShort', 'strings') + ' ' + m.format(timeFormat);
      }
      if (m.unix() > now.clone().startOf('day').unix() && m.unix() < now.clone().add(1, 'days').startOf('day').unix()) {
        return m.format(timeFormat);
      }
      const readableFormat = this.getDateTime().getReadableShortDateFormat();
      return m.format('YYYY') === now.format('YYYY') ? m.format(readableFormat) : m.format(readableFormat + ', YY');
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = DatetimeShortFieldView;
});

define("views/fields/currency-converted", ["exports", "views/fields/currency"], function (_exports, _currency) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _currency = _interopRequireDefault(_currency);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class CurrencyConvertedFieldView extends _currency.default {
    data() {
      let data = super.data();
      const currencyValue = this.getConfig().get('defaultCurrency');
      data.currencyValue = currencyValue;
      data.currencySymbol = this.getMetadata().get(['app', 'currency', 'symbolMap', currencyValue]) || '';
      return data;
    }
  }
  var _default = _exports.default = CurrencyConvertedFieldView;
});

define("views/fields/checklist", ["exports", "views/fields/array"], function (_exports, _array) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _array = _interopRequireDefault(_array);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/checklist */

  class ChecklistFieldView extends _array.default {
    type = 'checklist';
    listTemplate = 'fields/array/list';
    detailTemplate = 'fields/checklist/detail';
    editTemplate = 'fields/checklist/edit';
    isInversed = false;
    events = {};
    data() {
      return {
        optionDataList: this.getOptionDataList(),
        ...super.data()
      };
    }
    setup() {
      super.setup();
      this.params.options = this.params.options || [];
      this.isInversed = this.params.isInversed || this.options.isInversed || this.isInversed;
    }
    afterRender() {
      if (this.isSearchMode()) {
        this.renderSearch();
      }
      if (this.isEditMode()) {
        this.$el.find('input').on('change', () => {
          this.trigger('change');
        });
      }
    }
    getOptionDataList() {
      let valueList = this.model.get(this.name) || [];
      let list = [];
      this.params.options.forEach(item => {
        let isChecked = ~valueList.indexOf(item);
        let dataName = item;
        let id = this.cid + '-' + Espo.Utils.camelCaseToHyphen(item.replace(/\s+/g, '-'));
        if (this.isInversed) {
          isChecked = !isChecked;
        }
        list.push({
          name: item,
          isChecked: isChecked,
          dataName: dataName,
          id: id,
          label: this.translatedOptions[item] || item
        });
      });
      return list;
    }
    fetch() {
      let list = [];
      this.params.options.forEach(item => {
        let $item = this.$el.find('input[data-name="' + item + '"]');
        let isChecked = $item.get(0) && $item.get(0).checked;
        if (this.isInversed) {
          isChecked = !isChecked;
        }
        if (isChecked) {
          list.push(item);
        }
      });
      let data = {};
      data[this.name] = list;
      return data;
    }
    validateRequired() {
      if (!this.isRequired()) {
        return;
      }
      let value = this.model.get(this.name);
      if (!value || value.length === 0) {
        let msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg, '.checklist-item-container:last-child input');
        return true;
      }
    }
    validateMaxCount() {
      if (!this.params.maxCount) {
        return;
      }
      let itemList = this.model.get(this.name) || [];
      if (itemList.length > this.params.maxCount) {
        let msg = this.translate('fieldExceedsMaxCount', 'messages').replace('{field}', this.getLabelText()).replace('{maxCount}', this.params.maxCount.toString());
        this.showValidationMessage(msg, '.checklist-item-container:last-child input');
        return true;
      }
    }
  }
  var _default = _exports.default = ChecklistFieldView;
});

define("views/email-template/record/detail", ["exports", "views/record/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _detail.default {
    duplicateAction = true;
    saveAndContinueEditingAction = true;
    setup() {
      super.setup();
      this.listenToInsertField();
      this.hideField('insertField');
      this.on('before:set-edit-mode', () => this.showField('insertField'));
      this.on('before:set-detail-mode', () => this.hideField('insertField'));
    }
    listenToInsertField() {
      this.listenTo(this.model, 'insert-field', /** {entityType: string, field: string} */o => {
        const tag = `{${o.entityType}.${o.field}}`;
        const bodyView = /** @type {import('views/fields/wysiwyg').default} */
        this.getFieldView('body');
        if (!bodyView) {
          return;
        }
        if (this.model.attributes.isHtml) {
          const $anchor = $(window.getSelection().anchorNode);
          if (!$anchor.closest('.note-editing-area').length) {
            return;
          }
          bodyView.insertText(tag);
          return;
        }
        const $body = $(bodyView.element.querySelector('textarea.main-element'));
        let text = $body.val();
        text += tag;
        $body.val(text);
      });
    }
  }
  _exports.default = _default;
});

define("views/email/detail", ["exports", "views/detail", "email-helper", "helpers/record-modal", "views/attachment/modals/select-one"], function (_exports, _detail, _emailHelper, _recordModal, _selectOne) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  _emailHelper = _interopRequireDefault(_emailHelper);
  _recordModal = _interopRequireDefault(_recordModal);
  _selectOne = _interopRequireDefault(_selectOne);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailDetailView extends _detail.default {
    setup() {
      super.setup();
      const status = this.model.get('status');
      if (status === 'Draft') {
        this.menu = {
          'buttons': [],
          'dropdown': [],
          'actions': []
        };
      } else {
        this.addMenuItem('buttons', {
          name: 'reply',
          label: 'Reply',
          action: this.getPreferences().get('emailReplyToAllByDefault') ? 'replyToAll' : 'reply',
          style: 'danger',
          className: 'btn-s-wide'
        }, true);
        this.addMenuItem('dropdown', false);
        if (status === 'Archived') {
          if (!this.model.get('parentId')) {
            this.addMenuItem('dropdown', {
              label: 'Create Lead',
              action: 'createLead',
              acl: 'create',
              aclScope: 'Lead'
            });
            this.addMenuItem('dropdown', {
              label: 'Create Contact',
              action: 'createContact',
              acl: 'create',
              aclScope: 'Contact'
            });
          }
        }
        this.addMenuItem('dropdown', {
          label: 'Create Task',
          action: 'createTask',
          acl: 'create',
          aclScope: 'Task'
        });
        if (this.model.get('parentType') !== 'Case' || !this.model.get('parentId')) {
          this.addMenuItem('dropdown', {
            label: 'Create Case',
            action: 'createCase',
            acl: 'create',
            aclScope: 'Case'
          });
        }
        if (this.getAcl().checkScope('Document', 'create')) {
          if (this.model.get('attachmentsIds') === undefined || this.model.getLinkMultipleIdList('attachments').length) {
            this.addMenuItem('dropdown', {
              text: this.translate('Create Document', 'labels', 'Document'),
              action: 'createDocument',
              acl: 'create',
              aclScope: 'Document',
              hidden: this.model.get('attachmentsIds') === undefined
            });
            if (this.model.get('attachmentsIds') === undefined) {
              this.listenToOnce(this.model, 'sync', () => {
                if (this.model.getLinkMultipleIdList('attachments').length) {
                  this.showHeaderActionItem('createDocument');
                }
              });
            }
          }
        }
      }
      this.listenTo(this.model, 'change:isImportant change:inTrash change:inArchive change:groupStatusFolder', () => {
        if (!this.isRendered()) {
          return;
        }
        const headerView = this.getHeaderView();
        if (headerView) {
          headerView.reRender();
        }
      });
      this.shortcutKeys['Control+Backspace'] = e => {
        if ($(e.target).hasClass('note-editable')) {
          return;
        }
        const recordView = /** @type {module:views/email/record/detail} */this.getRecordView();
        if (!this.model.get('isUsers') || this.model.get('inArchive')) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        recordView.actionMoveToArchive();
      };
      this.shortcutKeys['Control+Delete'] = e => {
        if ($(e.target).hasClass('note-editable')) {
          return;
        }
        const recordView = /** @type {module:views/email/record/detail} */this.getRecordView();
        if (!this.model.get('isUsers') || this.model.get('inTrash')) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        recordView.actionMoveToTrash();
      };
      this.shortcutKeys['Control+KeyI'] = e => {
        if ($(e.target).hasClass('note-editable')) {
          return;
        }
        const recordView = /** @type {module:views/email/record/detail} */this.getRecordView();
        if (!this.model.get('isUsers')) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        this.model.get('isImportant') ? recordView.actionMarkAsNotImportant() : recordView.actionMarkAsImportant();
      };
      this.shortcutKeys['Control+KeyM'] = e => {
        if ($(e.target).hasClass('note-editable')) {
          return;
        }
        const recordView = /** @type {module:views/email/record/detail} */this.getRecordView();
        if (!this.model.get('isUsers')) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        recordView.actionMoveToFolder();
      };
    }

    // noinspection JSUnusedGlobalSymbols
    async actionCreateLead() {
      const attributes = {};
      const emailHelper = new _emailHelper.default();
      const fromString = this.model.get('fromString') || this.model.get('fromName');
      if (fromString) {
        const fromName = emailHelper.parseNameFromStringAddress(fromString);
        if (fromName) {
          const firstName = fromName.split(' ').slice(0, -1).join(' ');
          const lastName = fromName.split(' ').slice(-1).join(' ');
          attributes.firstName = firstName;
          attributes.lastName = lastName;
        }
      }
      if (this.model.get('replyToString')) {
        const str = this.model.get('replyToString');
        const p = str.split(';')[0];
        attributes.emailAddress = emailHelper.parseAddressFromStringAddress(p);
        const fromName = emailHelper.parseNameFromStringAddress(p);
        if (fromName) {
          const firstName = fromName.split(' ').slice(0, -1).join(' ');
          const lastName = fromName.split(' ').slice(-1).join(' ');
          attributes.firstName = firstName;
          attributes.lastName = lastName;
        }
      }
      if (!attributes.emailAddress) {
        attributes.emailAddress = this.model.get('from');
      }
      attributes.originalEmailId = this.model.id;
      const helper = new _recordModal.default();
      const modalView = await helper.showCreate(this, {
        entityType: 'Lead',
        attributes: attributes,
        afterSave: () => {
          this.model.fetch();
          this.removeMenuItem('createContact');
          this.removeMenuItem('createLead');
        }
      });
      this.listenTo(modalView, 'before:save', () => {
        this.getRecordView().blockUpdateWebSocket(true);
      });
    }

    // noinspection JSUnusedGlobalSymbols
    async actionCreateCase() {
      const attributes = {};
      const parentId = this.model.get('parentId');
      const parentType = this.model.get('parentType');
      const parentName = this.model.get('parentName');
      const accountId = this.model.get('accountId');
      const accountName = this.model.get('accountName');
      if (parentId) {
        if (parentType === 'Account') {
          attributes.accountId = parentId;
          attributes.accountName = parentName;
        } else if (parentType === 'Contact') {
          attributes.contactId = parentId;
          attributes.contactName = parentName;
          attributes.contactsIds = [parentId];
          attributes.contactsNames = {};
          attributes.contactsNames[parentId] = parentName;
          if (accountId) {
            attributes.accountId = accountId;
            attributes.accountName = accountName || accountId;
          }
        } else if (parentType === 'Lead') {
          attributes.leadId = parentId;
          attributes.leadName = parentName;
        }
      }
      attributes.originalEmailId = this.model.id;
      attributes.name = this.model.get('name');
      attributes.description = this.model.get('bodyPlain') || '';
      const attachmentIds = this.model.get('attachmentsIds') || [];
      Espo.Ui.notifyWait();
      if (attachmentIds.length) {
        /** @type {Record} data */
        const data = await Espo.Ajax.postRequest(`Email/${this.model.id}/attachments/copy`, {
          parentType: 'Case',
          field: 'attachments'
        });
        attributes.attachmentsIds = data.ids;
        attributes.attachmentsNames = data.names;
      }
      const helper = new _recordModal.default();
      const modalView = await helper.showCreate(this, {
        entityType: 'Case',
        attributes: attributes,
        afterSave: () => {
          this.model.fetch();
          this.removeMenuItem('createCase');
        }
      });
      this.listenTo(modalView, 'before:save', () => this.getRecordView().blockUpdateWebSocket(true));
    }

    // noinspection JSUnusedGlobalSymbols
    actionCreateTask() {
      const attributes = {};
      attributes.parentId = this.model.get('parentId');
      attributes.parentName = this.model.get('parentName');
      attributes.parentType = this.model.get('parentType');
      attributes.originalEmailId = this.model.id;
      const subject = this.model.attributes.name;
      attributes.description = `[${this.translate('Email', 'scopeNames')}: ${subject}](#Email/view/${this.model.id})\n`;
      const fullFormUrl = `#Task/create?emailId=${attributes.originalEmailId}`;
      const helper = new _recordModal.default();
      helper.showCreate(this, {
        entityType: 'Task',
        attributes: attributes,
        fullFormUrl: fullFormUrl,
        afterSave: () => {
          this.model.fetch();
        },
        beforeRender: view => {
          const nameFieldView = view.getRecordView().getFieldView('name');
          const nameOptionList = [];
          if (nameFieldView && nameFieldView.params.options) {
            nameOptionList.push(...nameFieldView.params.options);
          }
          nameOptionList.push(this.translate('replyToEmail', 'nameOptions', 'Task'));
          view.getRecordView().setFieldOptionList('name', nameOptionList);
        }
      });
    }

    // noinspection JSUnusedGlobalSymbols
    async actionCreateContact() {
      const attributes = {};
      const emailHelper = new _emailHelper.default();
      const fromString = this.model.get('fromString') || this.model.get('fromName');
      if (fromString) {
        const fromName = emailHelper.parseNameFromStringAddress(fromString);
        if (fromName) {
          const firstName = fromName.split(' ').slice(0, -1).join(' ');
          const lastName = fromName.split(' ').slice(-1).join(' ');
          attributes.firstName = firstName;
          attributes.lastName = lastName;
        }
      }
      if (this.model.get('replyToString')) {
        const str = this.model.get('replyToString');
        const p = str.split(';')[0];
        attributes.emailAddress = emailHelper.parseAddressFromStringAddress(p);
        const fromName = emailHelper.parseNameFromStringAddress(p);
        if (fromName) {
          const firstName = fromName.split(' ').slice(0, -1).join(' ');
          const lastName = fromName.split(' ').slice(-1).join(' ');
          attributes.firstName = firstName;
          attributes.lastName = lastName;
        }
      }
      if (!attributes.emailAddress) {
        attributes.emailAddress = this.model.get('from');
      }
      attributes.originalEmailId = this.model.id;
      const helper = new _recordModal.default();
      const modalView = await helper.showCreate(this, {
        entityType: 'Contact',
        attributes: attributes,
        afterSave: () => {
          this.model.fetch();
          this.removeMenuItem('createContact');
          this.removeMenuItem('createLead');
        }
      });
      this.listenTo(modalView, 'before:save', () => {
        this.getRecordView().blockUpdateWebSocket(true);
      });
    }
    actionReply(data, e, cc) {
      const emailHelper = new _emailHelper.default();
      const attributes = emailHelper.getReplyAttributes(this.model, data, cc);
      Espo.Ui.notifyWait();
      const viewName = this.getMetadata().get('clientDefs.Email.modalViews.compose') || 'views/modals/compose-email';
      this.createView('quickCreate', viewName, {
        attributes: attributes,
        focusForCreate: true
      }, view => {
        view.render();
        view.notify(false);
        this.listenTo(view, 'after:save', () => {
          this.model.fetch();
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionReplyToAll(data, e) {
      this.actionReply(data, e, true);
    }

    // noinspection JSUnusedGlobalSymbols
    actionForward() {
      const emailHelper = new _emailHelper.default();
      Espo.Ui.notifyWait();
      Espo.Ajax.postRequest('Email/action/getDuplicateAttributes', {
        id: this.model.id
      }).then(duplicateAttributes => {
        const model = this.model.clone();
        model.set('body', duplicateAttributes.body);
        const attributes = emailHelper.getForwardAttributes(model);
        attributes.attachmentsIds = duplicateAttributes.attachmentsIds;
        attributes.attachmentsNames = duplicateAttributes.attachmentsNames;
        Espo.Ui.notifyWait();
        const viewName = this.getMetadata().get('clientDefs.Email.modalViews.compose') || 'views/modals/compose-email';
        this.createView('quickCreate', viewName, {
          attributes: attributes
        }, view => {
          view.render();
          view.notify(false);
        });
      });
    }
    getHeader() {
      const name = this.model.attributes.name;
      const isImportant = this.model.attributes.isImportant;
      const inTrash = this.model.attributes.groupFolderId ? this.model.attributes.groupStatusFolder === 'Trash' : this.model.attributes.inTrash;
      const inArchive = this.model.attributes.groupFolderId ? this.model.attributes.groupStatusFolder === 'Archive' : this.model.attributes.inArchive;
      const scopeLabel = this.getLanguage().translate(this.scope, 'scopeNamesPlural');
      let root = document.createElement('span');
      root.text = scopeLabel;
      root.style.userSelect = 'none';
      if (!this.rootLinkDisabled) {
        const a = document.createElement('a');
        a.href = this.rootUrl;
        a.classList.add('action');
        a.dataset.action = 'navigateToRoot';
        a.text = scopeLabel;
        root = document.createElement('span');
        root.style.userSelect = 'none';
        root.append(a);
      }
      const iconHtml = this.getHeaderIconHtml();
      if (iconHtml) {
        root.insertAdjacentHTML('afterbegin', iconHtml);
      }
      let styleClass = null;
      if (isImportant) {
        styleClass = 'text-warning';
      } else if (inTrash) {
        styleClass = 'text-muted';
      } else if (inArchive) {
        styleClass = 'text-info';
      }
      const title = document.createElement('span');
      title.classList.add('font-size-flexible', 'title');
      title.textContent = name;
      if (styleClass) {
        title.classList.add(styleClass);
      }
      if (this.getRecordMode() === 'detail') {
        title.title = this.translate('clickToRefresh', 'messages');
        title.dataset.action = 'fullRefresh';
        title.style.cursor = 'pointer';
      }
      return this.buildHeaderHtml([root, title]);
    }
    actionNavigateToRoot(data, event) {
      event.stopPropagation();
      this.getRouter().checkConfirmLeaveOut(() => {
        const rootUrl = this.options.rootUrl || this.options.params.rootUrl || '#' + this.scope;
        const options = {
          isReturn: true,
          isReturnThroughLink: true
        };
        this.getRouter().navigate(rootUrl, {
          trigger: false
        });
        this.getRouter().dispatch(this.scope, null, options);
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionCreateDocument() {
      const attachmentIdList = this.model.getLinkMultipleIdList('attachments');
      if (!attachmentIdList.length) {
        return;
      }
      const names = this.model.get('attachmentsNames') || {};
      const types = this.model.get('attachmentsTypes') || {};
      const proceed = async id => {
        const attributes = {};
        if (this.model.get('accountId')) {
          attributes.accountsIds = [this.model.get('accountId')];
          attributes.accountsNames = {};
          attributes.accountsNames[this.model.get('accountId')] = this.model.get('accountName');
        }
        Espo.Ui.notifyWait();
        const attachment = await Espo.Ajax.postRequest(`Attachment/copy/${id}`, {
          relatedType: 'Document',
          field: 'file'
        });
        attributes.fileId = attachment.id;
        attributes.fileName = attachment.name;
        attributes.name = attachment.name;
        const helper = new _recordModal.default();
        await helper.showCreate(this, {
          entityType: 'Document',
          attributes: attributes
        });
      };
      if (attachmentIdList.length === 1) {
        proceed(attachmentIdList[0]);
        return;
      }
      const dataList = [];
      attachmentIdList.forEach(id => {
        dataList.push({
          id: id,
          name: names[id] || id,
          type: types[id]
        });
      });
      const modalView = new _selectOne.default({
        dataList: dataList,
        fieldLabel: this.translate('attachments', 'fields', 'Email'),
        onSelect: id => proceed(id)
      });
      this.assignView('selectModal', modalView);
      modalView.render();
    }
  }
  var _default = _exports.default = EmailDetailView;
});

define("views/email/record/list", ["exports", "views/record/list", "helpers/mass-action"], function (_exports, _list, _massAction) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  _massAction = _interopRequireDefault(_massAction);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/email/record/list */

  class EmailListRecordView extends _list.default {
    rowActionsView = 'views/email/record/row-actions/default';
    massActionList = ['remove', 'massUpdate'];

    /**
     * @type {string[]}
     * @private
     */
    toRemoveIdList;
    collectionEventSyncList = ['moving-to-trash', 'retrieving-from-trash', 'moving-to-archive'];
    setup() {
      super.setup();
      if (this.collection.url === this.entityType) {
        this.addMassAction({
          name: 'retrieveFromTrash',
          groupIndex: -6
        }, false);
        this.addMassAction({
          name: 'moveToTrash',
          groupIndex: -5
        }, false);
        this.addMassAction({
          name: 'moveToArchive',
          groupIndex: -5
        }, false);
        this.addMassAction({
          name: 'moveToFolder',
          groupIndex: -5
        }, true);
        this.addMassAction({
          name: 'markAsImportant',
          groupIndex: -4
        }, false);
        this.addMassAction({
          name: 'markAsNotImportant',
          groupIndex: -4
        }, false);
        this.addMassAction({
          name: 'markAsRead',
          groupIndex: -3
        }, false);
        this.addMassAction({
          name: 'markAsNotRead',
          groupIndex: -3
        }, false);
        this.dropdownItemList.push({
          name: 'markAllAsRead',
          label: 'Mark all as read'
        });
        this.controlEmailMassActionsVisibility();
        this.listenTo(this.collection, 'select-folder', () => this.controlEmailMassActionsVisibility());
      }
      this.listenTo(this.collection, 'moving-to-trash', (id, keep) => {
        const model = this.collection.get(id);
        if (model) {
          model.attributes.groupFolderId ? model.set('groupFolderStatus', 'Trash') : model.set('inTrash', true);
        }
        if (this.rootData.selectedFolderId !== 'trash' && this.rootData.selectedFolderId !== 'all') {
          if (keep) {
            this.toRemoveIdList.push(id);
            return;
          }
          this.removeRecordFromList(id);
        }
      });
      this.listenTo(this.collection, 'retrieving-from-trash', (id, keep) => {
        const model = this.collection.get(id);
        if (model) {
          model.attributes.groupFolderId ? model.set('groupFolderStatus', null) : model.set('inTrash', false);
        }
        if (this.rootData.selectedFolderId === 'all') {
          return;
        }
        if (this.rootData.selectedFolderId === 'trash') {
          if (keep) {
            this.toRemoveIdList.push(id);
            return;
          }
          this.removeRecordFromList(id);
        }
      });
      this.listenTo(this.collection, 'moving-to-archive', (id, keep) => {
        const model = this.collection.get(id);
        if (model) {
          model.attributes.groupFolderId ? model.set('groupFolderStatus', 'Archive') : model.set('inArchive', true);
        }
        if (this.rootData.selectedFolderId === 'sent' || this.rootData.selectedFolderId === 'all') {
          return;
        }
        if (this.rootData.selectedFolderId !== 'archive') {
          if (keep) {
            this.toRemoveIdList.push(id);
            return;
          }
          this.removeRecordFromList(id);
        }
      });
      this.toRemoveIdList = [];
    }

    /**
     * @internal
     */
    removeQueuedRecord() {
      this.toRemoveIdList.forEach(id => this.removeRecordFromList(id));
    }

    // noinspection JSUnusedGlobalSymbols
    massActionMarkAsRead() {
      const ids = [];
      for (const i in this.checkedList) {
        ids.push(this.checkedList[i]);
      }
      Espo.Ajax.postRequest('Email/inbox/read', {
        ids: ids
      });
      ids.forEach(id => {
        const model = this.collection.get(id);
        if (model) {
          model.set('isRead', true);
        }
      });
    }

    // noinspection JSUnusedGlobalSymbols
    massActionMarkAsNotRead() {
      const ids = [];
      for (const i in this.checkedList) {
        ids.push(this.checkedList[i]);
      }
      Espo.Ajax.deleteRequest('Email/inbox/read', {
        ids: ids
      });
      ids.forEach(id => {
        const model = this.collection.get(id);
        if (model) {
          model.set('isRead', false);
        }
      });
    }
    massActionMarkAsImportant() {
      const ids = [];
      for (const i in this.checkedList) {
        ids.push(this.checkedList[i]);
      }
      Espo.Ajax.postRequest('Email/inbox/important', {
        ids: ids
      });
      ids.forEach(id => {
        const model = this.collection.get(id);
        if (model) {
          model.set('isImportant', true);
        }
      });
    }
    massActionMarkAsNotImportant() {
      const ids = [];
      for (const i in this.checkedList) {
        ids.push(this.checkedList[i]);
      }
      Espo.Ajax.deleteRequest('Email/inbox/important', {
        ids: ids
      });
      ids.forEach(id => {
        const model = this.collection.get(id);
        if (model) {
          model.set('isImportant', false);
        }
      });
    }

    // noinspection JSUnusedGlobalSymbols
    massActionMoveToTrash() {
      const ids = [];
      for (const i in this.checkedList) {
        ids.push(this.checkedList[i]);
      }
      Espo.Ajax.postRequest('Email/inbox/inTrash', {
        ids: ids
      }).then(() => {
        Espo.Ui.warning(this.translate('Moved to Trash', 'labels', 'Email'));
      });
      if (this.rootData.selectedFolderId === 'trash') {
        return;
      }
      ids.forEach(id => {
        this.collection.trigger('moving-to-trash', id);
        this.uncheckRecord(id, null, true);
      });
    }

    // noinspection JSUnusedGlobalSymbols
    massActionRetrieveFromTrash() {
      const ids = [];
      for (const i in this.checkedList) {
        ids.push(this.checkedList[i]);
      }
      Espo.Ajax.deleteRequest('Email/inbox/inTrash', {
        ids: ids
      }).then(() => {
        Espo.Ui.success(this.translate('Done'));
      });
      if (this.rootData.selectedFolderId !== 'trash') {
        return;
      }
      ids.forEach(id => {
        this.collection.trigger('retrieving-from-trash', id);
        this.uncheckRecord(id, null, true);
      });
    }

    /**
     * @param {string} folderId
     */
    async massMoveToFolder(folderId) {
      const params = this.getMassActionSelectionPostData();
      const helper = new _massAction.default(this);
      const idle = !!params.searchParams && helper.checkIsIdle();
      Espo.Ui.notify(this.translate('pleaseWait', 'messages'));

      /** @type {{id?: string, count?: number}} */
      const result = await Espo.Ajax.postRequest('MassAction', {
        entityType: this.entityType,
        action: 'moveToFolder',
        params: params,
        idle: idle,
        data: {
          folderId: folderId
        }
      });
      Espo.Ui.notify();
      if (result.id) {
        const view = await helper.process(result.id, 'moveToFolder');
        this.listenToOnce(view, 'close:success', async () => {
          await this.collection.fetch();
          Espo.Ui.success(this.translate('Done'));
        });
        return;
      }
      if (result.count === 0) {
        Espo.Ui.warning(this.translate('No Records Moved', 'labels', 'Email'));
        return;
      }
      if (folderId === 'archive') {
        [...this.checkedList].forEach(id => {
          this.collection.trigger('moving-to-archive', id);
          this.uncheckRecord(id, null, true);
        });
        Espo.Ui.info(this.translate('Moved to Archive', 'labels', 'Email'));
        return;
      }
      await this.collection.fetch();
      Espo.Ui.success(this.translate('Done'));
    }

    /**
     * @private
     * @return {string|null|undefined}
     */
    getSelectedFolderId() {
      return this.rootData.selectedFolderId;
    }

    // noinspection JSUnusedGlobalSymbols
    massActionMoveToFolder() {
      const selectedFolderId = this.getSelectedFolderId();
      this.createView('dialog', 'views/email-folder/modals/select-folder', {
        headerText: this.translate('Move to Folder', 'labels', 'Email'),
        isGroup: selectedFolderId && (selectedFolderId.startsWith('group:') || selectedFolderId === 'all'),
        noArchive: selectedFolderId === 'all',
        currentFolderId: this.rootData.selectedFolderId
      }, view => {
        view.render();
        this.listenToOnce(view, 'select', async folderId => {
          this.clearView('dialog');
          if (this.allResultIsChecked) {
            await this.confirm(this.translate('confirmation', 'messages'));
          }
          await this.massMoveToFolder(folderId);
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    massActionMoveToArchive() {
      this.massMoveToFolder('archive');
    }
    actionMarkAsImportant(data) {
      data = data || {};
      const id = data.id;
      Espo.Ajax.postRequest('Email/inbox/important', {
        id: id
      });
      const model = this.collection.get(id);
      if (model) {
        model.set('isImportant', true);
      }
    }
    actionMarkAsNotImportant(data) {
      data = data || {};
      const id = data.id;
      Espo.Ajax.deleteRequest('Email/inbox/important', {
        id: id
      });
      const model = this.collection.get(id);
      if (model) {
        model.set('isImportant', false);
      }
    }
    actionMarkAllAsRead() {
      Espo.Ajax.postRequest('Email/inbox/read', {
        all: true
      });
      this.collection.forEach(model => {
        model.set('isRead', true);
      });
      this.collection.trigger('all-marked-read');
    }

    // noinspection JSUnusedGlobalSymbols
    actionMoveToArchive(data) {
      const id = data.id;
      Espo.Ui.notifyWait();
      Espo.Ajax.postRequest('Email/inbox/folders/archive', {
        id: id
      }).then(() => {
        Espo.Ui.info(this.translate('Moved to Archive', 'labels', 'Email'));
        this.collection.trigger('moving-to-archive', id);
      });
    }
    actionMoveToTrash(data) {
      const id = data.id;
      Espo.Ui.notifyWait();
      Espo.Ajax.postRequest('Email/inbox/inTrash', {
        id: id
      }).then(() => {
        Espo.Ui.warning(this.translate('Moved to Trash', 'labels', 'Email'));
        this.collection.trigger('moving-to-trash', id);
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionRetrieveFromTrash(data) {
      const id = data.id;
      Espo.Ui.notifyWait();
      this.retrieveFromTrash(id).then(() => {
        Espo.Ui.warning(this.translate('Retrieved from Trash', 'labels', 'Email'));
        this.collection.trigger('retrieving-from-trash', id);
      });
    }

    /**
     * @param {string} id
     * @return {Promise}
     */
    retrieveFromTrash(id) {
      return Espo.Ajax.deleteRequest('Email/inbox/inTrash', {
        id: id
      });
    }
    massRetrieveFromTrashMoveToFolder(folderId) {
      const ids = [];
      for (const i in this.checkedList) {
        ids.push(this.checkedList[i]);
      }
      Espo.Ajax.deleteRequest('Email/inbox/inTrash', {
        ids: ids
      }).then(() => {
        ids.forEach(id => {
          this.collection.trigger('retrieving-from-trash', id);
        });
        return Espo.Ajax.postRequest(`Email/inbox/folders/${folderId}`, {
          ids: ids
        }).then(() => {
          Espo.Ui.success(this.translate('Done'));
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @todo Use one API request.
     */
    actionRetrieveFromTrashMoveToFolder(data) {
      const id = data.id;
      const folderId = data.folderId;
      Espo.Ui.notifyWait();
      this.retrieveFromTrash(id).then(() => {
        return this.moveToFolder(id, folderId);
      }).then(() => {
        this.collection.fetch().then(() => {
          Espo.Ui.success(this.translate('Done'));
        });
      });
    }

    /**
     * @param {string} id
     * @param {string} folderId
     * @return {Promise}
     */
    moveToFolder(id, folderId) {
      return Espo.Ajax.postRequest(`Email/inbox/folders/${folderId}`, {
        id: id
      });
    }
    actionMoveToFolder(data) {
      const id = data.id;
      const folderId = data.folderId;
      if (folderId) {
        Espo.Ui.notifyWait();
        this.moveToFolder(id, folderId).then(() => {
          if (folderId === 'archive') {
            this.collection.trigger('moving-to-archive', id);
            Espo.Ui.info(this.translate('Moved to Archive', 'labels', 'Email'));
            return;
          }
          this.collection.fetch().then(() => Espo.Ui.success(this.translate('Done')));
        });
        return;
      }
      const model = this.collection.get(id);
      if (!model) {
        return;
      }
      const currentFolderId = this.rootData.selectedFolderId;
      this.createView('dialog', 'views/email-folder/modals/select-folder', {
        headerText: this.translate('Move to Folder', 'labels', 'Email'),
        isGroup: !!model.attributes.groupFolderId || !model.attributes.isUsers,
        noArchive: !model.attributes.groupFolderId && !model.attributes.isUsers,
        currentFolderId: currentFolderId
      }, view => {
        view.render();
        this.listenToOnce(view, 'select', folderId => {
          this.clearView('dialog');
          Espo.Ui.notifyWait();
          this.moveToFolder(id, folderId).then(() => {
            this.collection.fetch().then(() => {
              Espo.Ui.success(this.translate('Done'));
            });
          });
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @private
     * @param {{id: string}} data
     */
    actionMarkAsRead(data) {
      const id = data.id;
      const model = this.collection.get(id);
      Espo.Ajax.postRequest('Email/inbox/read', {
        ids: [id]
      });
      if (model) {
        model.set('isRead', true);
      }
    }

    // noinspection JSUnusedGlobalSymbols
    actionSend(data) {
      const id = data.id;
      this.confirm({
        message: this.translate('sendConfirm', 'messages', 'Email'),
        confirmText: this.translate('Send', 'labels', 'Email')
      }).then(() => {
        const model = this.collection.get(id);
        if (!model) {
          return;
        }
        Espo.Ui.notify(this.translate('Sending...', 'labels', 'Email'));
        model.save({
          status: 'Sending'
        }).then(() => {
          Espo.Ui.success(this.translate('emailSent', 'messages', 'Email'));
          if (this.rootData.selectedFolderId === 'drafts') {
            this.removeRecordFromList(id);
            this.uncheckRecord(id, null, true);
            this.collection.trigger('draft-sent');
          }
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    toggleMassMarkAsImportant() {
      const allImportant = !this.checkedList.map(id => this.collection.get(id)).find(m => !m.get('isImportant'));
      if (allImportant) {
        this.massActionMarkAsNotImportant();
        return;
      }
      this.massActionMarkAsImportant();
    }

    /**
     * @private
     */
    controlEmailMassActionsVisibility() {
      const moveToArchive = this.rootData.selectedFolderId !== 'trash' && this.rootData.selectedFolderId !== 'archive' && this.rootData.selectedFolderId !== 'all';
      const moveToTrash = this.rootData.selectedFolderId !== 'trash' && this.rootData.selectedFolderId !== 'all';
      const markAsImportant = this.rootData.selectedFolderId !== 'important' && this.rootData.selectedFolderId !== 'all';
      const markAsNotRead = this.rootData.selectedFolderId !== 'all';
      moveToArchive ? this.showMassAction('moveToArchive') : this.hideMassAction('moveToArchive');
      moveToTrash ? this.showMassAction('moveToTrash') : this.hideMassAction('moveToTrash');
      markAsImportant ? this.showMassAction('markAsImportant') : this.hideMassAction('markAsImportant');
      markAsNotRead ? this.showMassAction('markAsNotRead') : this.hideMassAction('markAsNotRead');
      if (this.rootData.selectedFolderId === 'trash') {
        this.showMassAction('retrieveFromTrash');
      } else {
        this.hideMassAction('retrieveFromTrash');
      }
    }
  }
  var _default = _exports.default = EmailListRecordView;
});

define("views/email/record/edit", ["exports", "views/record/edit", "views/email/record/detail"], function (_exports, _edit, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/email/record/edit */

  class EmailEditRecordView extends _edit.default {
    init() {
      super.init();
      _detail.default.prototype.layoutNameConfigure.call(this);
    }
    setup() {
      super.setup();
      if (['Archived', 'Sent'].includes(this.model.get('status'))) {
        this.shortcutKeyCtrlEnterAction = 'save';
      }
      this.addDropdownItem({
        name: 'send',
        label: 'Send',
        onClick: () => this.actionSend()
      });
      this.controlSendButton();
      if (this.model.get('status') === 'Draft') {
        this.setFieldReadOnly('dateSent');

        // Not implemented for detail view yet.
        this.hideField('selectTemplate');
      }
      this.handleAttachmentField();
      this.handleCcField();
      this.handleBccField();
      this.listenTo(this.model, 'change:attachmentsIds', () => this.handleAttachmentField());
      this.listenTo(this.model, 'change:cc', () => this.handleCcField());
      this.listenTo(this.model, 'change:bcc', () => this.handleBccField());
    }
    handleAttachmentField() {
      if ((this.model.get('attachmentsIds') || []).length === 0 && !this.isNew && this.model.get('status') !== 'Draft') {
        this.hideField('attachments');
        return;
      }
      this.showField('attachments');
    }
    handleCcField() {
      if (!this.model.get('cc') && this.model.get('status') !== 'Draft') {
        this.hideField('cc');
      } else {
        this.showField('cc');
      }
    }
    handleBccField() {
      if (!this.model.get('bcc') && this.model.get('status') !== 'Draft') {
        this.hideField('bcc');
      } else {
        this.showField('bcc');
      }
    }
    controlSendButton() {
      const status = this.model.get('status');
      if (status === 'Draft') {
        this.showActionItem('send');
        return;
      }
      this.hideActionItem('send');
    }

    // noinspection JSUnusedGlobalSymbols
    actionSaveDraft() {
      this.actionSaveAndContinueEditing();
    }

    // noinspection JSUnusedGlobalSymbols
    actionSend() {
      _detail.default.prototype.send.call(this).then(() => this.exit()).catch(() => {});
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlS(e) {
      if (this.inlineEditModeIsOn || this.buttonsDisabled) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      if (this.mode !== this.MODE_EDIT) {
        return;
      }
      if (!this.saveAndContinueEditingAction) {
        return;
      }
      if (!this.hasAvailableActionItem('saveAndContinueEditing')) {
        return;
      }
      this.actionSaveAndContinueEditing();
    }
  }
  var _default = _exports.default = EmailEditRecordView;
});

define("views/email/modals/schedule-send", ["exports", "views/modal", "model", "views/record/edit-for-modal", "views/fields/datetime", "moment"], function (_exports, _modal, _model, _editForModal, _datetime, _moment) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  _editForModal = _interopRequireDefault(_editForModal);
  _datetime = _interopRequireDefault(_datetime);
  _moment = _interopRequireDefault(_moment);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class EmailScheduleSendModalView extends _modal.default {
    // language=Handlebars
    templateContent = `<div class="record no-side-margin">{{{record}}}</div>`;

    /**
     * @type {Model}
     */
    formModel;

    /**
     * @type {EditForModalRecordView}
     */
    recordView;

    /**
     * @param {{
     *     model: import('model').default,
     *     onSave: function(): void,
     * }} options
     */
    constructor(options) {
      super(options);
      this.onSave = options.onSave;
    }
    setup() {
      this.headerText = this.translate('Schedule Send', 'labels', 'Email');
      this.buttonList.push({
        name: 'schedule',
        label: 'Schedule',
        style: 'danger',
        onClick: () => this.actionSchedule()
      });
      this.buttonList.push({
        name: 'cancel',
        label: 'Cancel',
        onClick: () => this.close()
      });
      this.formModel = new _model.default({
        now: this.getDateTime().getNow(),
        sendAt: this.getSendAt()
      });
      this.recordView = new _editForModal.default({
        model: this.formModel,
        detailLayout: [{
          rows: [[{
            view: new _datetime.default({
              name: 'sendAt',
              labelText: this.translate('sendAt', 'fields', 'Email'),
              params: {
                required: true,
                after: 'now'
              },
              otherFieldLabelText: this.translate('Now')
            })
          }, false]]
        }]
      });
      this.assignView('record', this.recordView, '.record');
    }

    /**
     * @private
     * @return {string}
     */
    getSendAt() {
      const sendAtMoment = _moment.default.utc(this.getDateTime().getNow(10));
      if (sendAtMoment.isBefore((0, _moment.default)().add(1, 'minutes'))) {
        sendAtMoment.add(10, 'minutes');
      }
      return sendAtMoment.format(this.getDateTime().internalDateTimeFormat);
    }
    async actionSchedule() {
      if (this.recordView.validate()) {
        return;
      }
      this.disableButton('schedule');
      Espo.Ui.notifyWait();
      this.model.set({
        status: 'Draft',
        sendAt: this.formModel.attributes.sendAt
      });
      try {
        await this.model.save();
      } catch (e) {
        this.enableButton('schedule');
        return;
      }
      const name = this.model.attributes.subject;
      const url = `#Email/view/${this.model.id}`;
      const message = this.translate('Scheduled') + '\n' + `[${name}](${url})`;
      Espo.Ui.notify(message, 'success', 4000);
      this.onSave();
    }
  }
  _exports.default = EmailScheduleSendModalView;
});

define("views/email/modals/import-eml", ["exports", "views/modal", "views/record/edit-for-modal", "model", "views/fields/file"], function (_exports, _modal, _editForModal, _model, _file) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _editForModal = _interopRequireDefault(_editForModal);
  _model = _interopRequireDefault(_model);
  _file = _interopRequireDefault(_file);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ImportEmlModal extends _modal.default {
    // language=Handlebars
    templateContent = `
        <div class="record no-side-margin">{{{record}}}</div>
    `;
    setup() {
      this.headerText = this.translate('Import EML', 'labels', 'Email');
      this.addButton({
        name: 'import',
        label: 'Proceed',
        style: 'danger',
        onClick: () => this.actionImport()
      });
      this.addButton({
        name: 'cancel',
        label: 'Cancel',
        onClick: () => this.close()
      });
      this.model = new _model.default({}, {
        entityType: 'ImportEml'
      });
      this.recordView = new _editForModal.default({
        model: this.model,
        detailLayout: [{
          rows: [[{
            view: new _file.default({
              name: 'file',
              params: {
                required: true,
                accept: ['.eml']
              },
              labelText: this.translate('file', 'otherFields', 'Email')
            })
          }]]
        }]
      });
      this.assignView('record', this.recordView, '.record');
    }
    actionImport() {
      if (this.recordView.validate()) {
        return;
      }
      this.disableButton('import');
      Espo.Ui.notifyWait();
      Espo.Ajax.postRequest('Email/importEml', {
        fileId: this.model.attributes.fileId
      }).then(/** {id: string} */response => {
        Espo.Ui.notify(false);
        this.getRouter().navigate(`Email/view/${response.id}`, {
          trigger: true
        });
      }).catch(() => this.enableButton('import'));
    }
  }
  var _default = _exports.default = ImportEmlModal;
});

define("views/email/fields/person-string-data", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _varchar.default {
    listTemplate = 'email/fields/person-string-data/list';
    getAttributeList() {
      return ['personStringData', 'isReplied'];
    }
    data() {
      const data = super.data();
      data.isReplied = this.model.get('isReplied');
      return data;
    }
  }
  _exports.default = _default;
});

define("views/email/fields/from-address-varchar", ["exports", "views/fields/base", "views/email/fields/email-address", "helpers/record-modal", "email-helper"], function (_exports, _base, _emailAddress, _recordModal, _emailHelper) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _emailAddress = _interopRequireDefault(_emailAddress);
  _recordModal = _interopRequireDefault(_recordModal);
  _emailHelper = _interopRequireDefault(_emailHelper);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailFromAddressVarchar extends _base.default {
    detailTemplate = 'email/fields/email-address-varchar/detail';
    validations = ['required', 'email'];
    skipCurrentInAutocomplete = true;
    emailAddressRegExp = new RegExp(/^[-!#$%&'*+/=?^_`{|}~A-Za-z0-9]+(?:\.[-!#$%&'*+/=?^_`{|}~A-Za-z0-9]+)*/.source + /@([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?\.)+[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9]/.source);
    setup() {
      super.setup();
      this.erasedPlaceholder = 'ERASED:';
      this.on('render', () => {
        if (this.mode === this.MODE_SEARCH) {
          return;
        }
        this.initAddressList();
      });
    }
    events = {
      /** @this EmailFromAddressVarchar */
      'click [data-action="createContact"]': function (e) {
        const address = $(e.currentTarget).data('address');
        this.createPerson('Contact', address);
      },
      /** @this EmailFromAddressVarchar */
      'click [data-action="createLead"]': function (e) {
        const address = $(e.currentTarget).data('address');
        this.createPerson('Lead', address);
      },
      /** @this EmailFromAddressVarchar */
      'click [data-action="addToContact"]': function (e) {
        const address = $(e.currentTarget).data('address');
        this.addToPerson('Contact', address);
      },
      /** @this EmailFromAddressVarchar */
      'click [data-action="addToLead"]': function (e) {
        const address = $(e.currentTarget).data('address');
        this.addToPerson('Lead', address);
      },
      /** @this EmailFromAddressVarchar */
      'auxclick a[href][data-scope][data-id]': function (e) {
        const isCombination = e.button === 1 && (e.ctrlKey || e.metaKey);
        if (!isCombination) {
          return;
        }
        const $target = $(e.currentTarget);
        const id = $target.attr('data-id');
        const scope = $target.attr('data-scope');
        e.preventDefault();
        e.stopPropagation();
        this.quickView({
          id: id,
          scope: scope
        });
      }
    };

    // noinspection JSCheckFunctionSignatures
    data() {
      const data = super.data();
      const address = this.model.get(this.name);
      if (address && !(address in this.idHash) && this.model.get('parentId')) {
        if (this.getAcl().check('Contact', 'edit')) {
          data.showCreate = true;
        }
      }
      data.valueIsSet = this.model.has(this.name);
      return data;
    }
    afterRender() {
      super.afterRender();
      if (this.mode === this.MODE_SEARCH && this.getAcl().check('Email', 'create')) {
        _emailAddress.default.prototype.initSearchAutocomplete.call(this);
      }
      if (this.mode === this.MODE_EDIT && this.getAcl().check('Email', 'create')) {
        _emailAddress.default.prototype.initSearchAutocomplete.call(this);
      }
      if (this.mode === this.MODE_SEARCH) {
        this.$input.on('input', () => {
          this.trigger('change');
        });
      }
    }

    // noinspection JSUnusedGlobalSymbols
    getAutocompleteMaxCount() {
      return _emailAddress.default.prototype.getAutocompleteMaxCount.call(this);
    }
    initAddressList() {
      this.nameHash = {};
      this.typeHash = this.model.get('typeHash') || {};
      this.idHash = this.model.get('idHash') || {};
      _.extend(this.nameHash, this.model.get('nameHash') || {});
    }
    getAttributeList() {
      const list = super.getAttributeList();
      list.push('nameHash');
      list.push('idHash');
      list.push('accountId');
      return list;
    }
    getValueForDisplay() {
      if (this.mode === this.MODE_DETAIL) {
        const address = this.model.get(this.name);
        return this.getDetailAddressHtml(address);
      }
      return super.getValueForDisplay();
    }

    /**
     * @protected
     * @param {string} address
     * @return {string}
     */
    getDetailAddressHtml(address) {
      if (!address) {
        return '';
      }
      const fromString = this.model.get('fromString') || this.model.get('fromName');
      const name = this.nameHash[address] || this.parseNameFromStringAddress(fromString) || null;
      const entityType = this.typeHash[address] || null;
      const id = this.idHash[address] || null;
      if (id) {
        let avatarHtml = '';
        if (entityType === 'User') {
          avatarHtml = this.getHelper().getAvatarHtml(id, 'small', 18, 'avatar-link');
        }
        return $('<div class="email-address-detail-item">').append(avatarHtml, $('<a>').attr('href', `#${entityType}/view/${id}`).attr('data-scope', entityType).attr('data-id', id).text(name), ' ', $('<span>').addClass('text-muted middle-dot'), ' ', $('<span>').text(address)).get(0).outerHTML;
      }
      const $div = $('<div>');
      if (this.getAcl().check('Contact', 'create') || this.getAcl().check('Lead', 'create')) {
        $div.append(this.getCreateHtml(address));
      }
      if (name) {
        $div.append($('<span>').addClass('email-address-line').text(name).append(' ', $('<span>').addClass('text-muted middle-dot'), ' ', $('<span>').text(address)));
        return $div.get(0).outerHTML;
      }
      $div.append($('<span>').addClass('email-address-line').text(address));
      return $div.get(0).outerHTML;
    }
    getCreateHtml(address) {
      const $ul = $('<ul>').addClass('dropdown-menu').attr('role', 'menu');
      const $container = $('<span>').addClass('dropdown email-address-create-dropdown pull-right').append($('<button>').addClass('dropdown-toggle btn btn-link btn-sm').attr('data-toggle', 'dropdown').append($('<span>').addClass('caret text-muted')), $ul);
      if (this.getAcl().check('Contact', 'create')) {
        $ul.append($('<li>').append($('<a>').attr('role', 'button').attr('tabindex', '0').attr('data-action', 'createContact').attr('data-address', address).text(this.translate('Create Contact', 'labels', 'Email'))));
      }
      if (this.getAcl().check('Lead', 'create')) {
        $ul.append($('<li>').append($('<a>').attr('role', 'button').attr('tabindex', '0').attr('data-action', 'createLead').attr('data-address', address).text(this.translate('Create Lead', 'labels', 'Email'))));
      }
      if (this.getAcl().check('Contact', 'edit')) {
        $ul.append($('<li>').append($('<a>').attr('role', 'button').attr('tabindex', '0').attr('data-action', 'addToContact').attr('data-address', address).text(this.translate('Add to Contact', 'labels', 'Email'))));
      }
      if (this.getAcl().check('Lead', 'edit')) {
        $ul.append($('<li>').append($('<a>').attr('role', 'button').attr('tabindex', '0').attr('data-action', 'addToLead').attr('data-address', address).text(this.translate('Add to Lead', 'labels', 'Email'))));
      }
      if (this.name === 'from' && this.getAcl().check('EmailFilter', 'create')) {
        if ($ul.children().length) {
          $ul.append(`<li class="divider"></li>`);
        }
        const url = '#EmailFilter/create?from=' + encodeURI(address) + '&returnUrl=' + encodeURI(this.getRouter().getCurrentUrl());
        $ul.append($('<li>').append($('<a>').attr('tabindex', '0').attr('href', url).text(this.translate('Create EmailFilter', 'labels', 'EmailFilter'))));
      }
      return $container.get(0).outerHTML;
    }

    /**
     * @param {string} value
     * @return {string|null}
     */
    parseNameFromStringAddress(value) {
      value = value || '';
      const emailHelper = new _emailHelper.default();
      return emailHelper.parseNameFromStringAddress(value);
    }

    /**
     * @internal Called with a different context from another view.
     * @param {string} scope
     * @param {string} address
     */
    createPerson(scope, address) {
      const fromString = this.model.get('fromString') || this.model.get('fromName');
      let name = this.nameHash[address] || null;
      if (!name && this.name === 'from' && fromString) {
        const emailHelper = new _emailHelper.default();
        name = emailHelper.parseNameFromStringAddress(fromString);
      }
      if (name) {
        name = this.getHelper().escapeString(name);
      }
      const attributes = {
        emailAddress: address
      };
      if (this.model.get('accountId') && scope === 'Contact') {
        attributes.accountId = this.model.get('accountId');
        attributes.accountName = this.model.get('accountName');
      }
      if (name) {
        const firstName = name.split(' ').slice(0, -1).join(' ');
        const lastName = name.split(' ').slice(-1).join(' ');
        attributes.firstName = firstName;
        attributes.lastName = lastName;
      }
      const helper = new _recordModal.default();
      helper.showCreate(this, {
        entityType: scope,
        attributes: attributes,
        afterSave: model => {
          const nameHash = Espo.Utils.clone(this.model.get('nameHash') || {});
          const typeHash = Espo.Utils.clone(this.model.get('typeHash') || {});
          const idHash = Espo.Utils.clone(this.model.get('idHash') || {});
          idHash[address] = model.id;
          nameHash[address] = model.attributes.name;
          typeHash[address] = scope;
          this.idHash = idHash;
          this.nameHash = nameHash;
          this.typeHash = typeHash;
          const attributes = {
            nameHash: nameHash,
            idHash: idHash,
            typeHash: typeHash
          };
          setTimeout(() => {
            this.model.set(attributes);
            if (this.model.attributes.icsContents) {
              this.model.fetch();
            }
          }, 50);
        }
      });
    }

    /**
     * @internal Called with a different context from another view.
     * @param {string} scope
     * @param {string} address
     */
    async addToPerson(scope, address) {
      const fromString = this.model.get('fromString') || this.model.get('fromName');
      let name = this.nameHash[address] || null;
      if (!name && this.name === 'from' && fromString) {
        const emailHelper = new _emailHelper.default();
        name = emailHelper.parseNameFromStringAddress(fromString);
      }
      if (name) {
        name = this.getHelper().escapeString(name);
      }
      const attributes = {
        emailAddress: address
      };
      if (this.model.get('accountId') && scope === 'Contact') {
        attributes.accountId = this.model.get('accountId');
        attributes.accountName = this.model.get('accountName');
      }
      const filters = {};
      if (name) {
        filters['name'] = {
          type: 'equals',
          field: 'name',
          value: name
        };
      }
      const afterSave = /** import('model').default */model => {
        const nameHash = Espo.Utils.clone(this.model.get('nameHash') || {});
        const typeHash = Espo.Utils.clone(this.model.get('typeHash') || {});
        const idHash = Espo.Utils.clone(this.model.get('idHash') || {});
        idHash[address] = model.id;
        nameHash[address] = model.attributes.name;
        typeHash[address] = scope;
        this.idHash = idHash;
        this.nameHash = nameHash;
        this.typeHash = typeHash;
        const attributes = {
          nameHash: nameHash,
          idHash: idHash,
          typeHash: typeHash
        };
        setTimeout(() => {
          this.model.set(attributes);
          if (this.model.attributes.icsContents) {
            this.model.fetch();
          }
        }, 50);
      };
      const viewName = this.getMetadata().get(`clientDefs.${scope}.modalViews.select`) || 'views/modals/select-records';

      /** @type {module:views/modals/select-records~Options} */
      const options = {
        entityType: scope,
        createButton: false,
        filters: filters,
        onSelect: async models => {
          const model = models[0];
          if (!model.attributes.emailAddress) {
            await model.save({
              emailAddress: address
            }, {
              patch: true
            });
            afterSave(model);
            return;
          }
          await model.fetch();
          const emailAddressData = [...(model.attributes.emailAddressData || [])];
          emailAddressData.push({
            emailAddress: address,
            primary: emailAddressData.length === 0
          });
          await model.save({
            emailAddressData: emailAddressData
          }, {
            patch: true
          });
          afterSave(model);
        }
      };
      Espo.Ui.notifyWait();
      const view = await this.createView('modal', viewName, options);
      await view.render();
      Espo.Ui.notify();
    }
    fetchSearch() {
      const value = this.$element.val().trim();
      if (value) {
        return {
          type: 'equals',
          value: value
        };
      }
      return null;
    }

    // noinspection JSUnusedGlobalSymbols
    validateEmail() {
      const address = this.model.get(this.name);
      if (!address) {
        return;
      }
      const addressLowerCase = String(address).toLowerCase();
      if (!this.emailAddressRegExp.test(addressLowerCase) && address.indexOf(this.erasedPlaceholder) !== 0) {
        const msg = this.translate('fieldShouldBeEmail', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg);
        return true;
      }
    }
    quickView(data) {
      const helper = new _recordModal.default();
      helper.showDetail(this, {
        id: data.id,
        entityType: data.scope
      });
    }
  }
  var _default = _exports.default = EmailFromAddressVarchar;
});

define("views/dashlets/options/base", ["exports", "views/modal", "model", "views/record/edit-for-modal"], function (_exports, _modal, _model, _editForModal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  _editForModal = _interopRequireDefault(_editForModal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class BaseDashletOptionsModalView extends _modal.default {
    template = 'dashlets/options/base';
    cssName = 'options-modal';
    className = 'dialog dialog-record';

    /**
     * @protected
     * @type {string}
     */
    name;

    /**
     * @protected
     * @type {boolean}
     */
    escapeDisabled = true;

    /**
     * @protected
     * @type {boolean}
     */
    saveDisabled = false;
    buttonList = [{
      name: 'save',
      label: 'Apply',
      style: 'primary',
      title: 'Ctrl+Enter'
    }, {
      name: 'cancel',
      label: 'Cancel',
      title: 'Esc'
    }];
    shortcutKeys = {
      /** @this BaseDashletOptionsModalView */
      'Control+Enter': function (e) {
        this.handleShortcutKeyCtrlEnter(e);
      },
      /** @this BaseDashletOptionsModalView */
      'Escape': function (e) {
        if (this.saveDisabled) {
          return;
        }
        e.stopPropagation();
        e.preventDefault();
        const focusedFieldView = this.getRecordView().getFocusedFieldView();
        if (focusedFieldView) {
          this.model.set(focusedFieldView.fetch(), {
            skipReRender: true
          });
        }
        if (this.getRecordView().isChanged) {
          this.confirm(this.translate('confirmLeaveOutMessage', 'messages')).then(() => this.actionClose());
          return;
        }
        this.actionClose();
      }
    };
    data() {
      return {
        options: this.optionsData
      };
    }
    getDetailLayout() {
      let layout = this.getMetadata().get(['dashlets', this.name, 'options', 'layout']);
      if (layout) {
        return layout;
      }
      layout = [{
        rows: []
      }];
      let i = 0;
      let row = [];
      for (const field in this.fields) {
        if (!(i % 2)) {
          row = [];
          layout[0].rows.push(row);
        }
        row.push({
          name: field
        });
        i++;
      }
      return layout;
    }
    init() {
      super.init();
      this.fields = Espo.Utils.cloneDeep(this.options.fields);
      this.fieldList = Object.keys(this.fields);
      this.optionsData = this.options.optionsData;
      this.name = this.options.name;
    }
    setup() {
      this.id = 'dashlet-options';
      const model = this.model = new _model.default();
      model.name = 'DashletOptions';
      model.setDefs({
        fields: this.fields
      });
      model.set(this.optionsData);
      this.dataObject = {
        dashletName: this.name,
        userId: this.options.userId
      };
      model.dashletName = this.name;
      model.userId = this.options.userId;
      this.middlePanelDefs = {};
      this.middlePanelDefsList = [];
      this.setupBeforeFinal();
      this.recordView = new _editForModal.default({
        model: model,
        detailLayout: this.getDetailLayout(),
        dataObject: this.dataObject
      });
      this.assignView('record', this.recordView, '.record');
      this.$header = $('<span>').append($('<span>').text(this.getLanguage().translate('Dashlet Options')), ' &middot; ', $('<span>').text(this.getLanguage().translate(this.name, 'dashlets')));
    }
    setupBeforeFinal() {}
    onBackdropClick() {
      if (this.getRecordView().isChanged) {
        return;
      }
      this.close();
    }

    /**
     * @return {module:views/record/edit}
     */
    getRecordView() {
      return this.recordView;
    }

    /**
     * @return {Object|null}
     */
    fetchAttributes() {
      const attributes = this.getRecordView().fetch();
      if (this.getRecordView().validate()) {
        return null;
      }
      return attributes;
    }
    actionSave() {
      const attributes = this.fetchAttributes();
      if (attributes == null) {
        return;
      }
      this.trigger('save', attributes);
    }
    getFieldViews(withHidden) {
      if (!this.hasView('record')) {
        return {};
      }
      return this.getRecordView().getFieldViews(withHidden);
    }
    getFieldView(name) {
      return (this.getFieldViews(true) || {})[name] || null;
    }
    hideField(name, locked) {
      if (!this.getRecordView()) {
        this.whenRendered().then(() => this.hideField(name), locked);
        return;
      }
      this.getRecordView().hideField(name, locked);
    }
    showField(name) {
      if (!this.getRecordView()) {
        this.whenRendered().then(() => this.showField(name));
        return;
      }
      this.getRecordView().showField(name);
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlEnter(e) {
      e.preventDefault();
      e.stopPropagation();
      if (document.activeElement instanceof HTMLInputElement) {
        document.activeElement.dispatchEvent(new Event('change', {
          bubbles: true
        }));
      }
      this.actionSave();
    }
  }
  var _default = _exports.default = BaseDashletOptionsModalView;
});

define("views/dashlets/fields/records/expanded-layout/modals/edit-item", ["exports", "views/modal", "views/record/edit-for-modal", "model", "views/fields/bool"], function (_exports, _modal, _editForModal, _model, _bool) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _editForModal = _interopRequireDefault(_editForModal);
  _model = _interopRequireDefault(_model);
  _bool = _interopRequireDefault(_bool);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ExpandedLayoutEditItemModalFieldView extends _modal.default {
    // language=Handlebars
    templateContent = `
        <div class="record-container no-side-margin">{{{record}}}</div>
    `;

    /**
     * @private
     * @type {EditForModalRecordView}
     */
    recordView;

    /**
     * @private
     * @type {Model}
     */
    formModel;

    /**
     * @param {{
     *     onApply: function({soft: boolean, small: boolean}),
     *     label: string,
     *     data: {
     *         soft: boolean,
     *         small: boolean,
     *     },
     * }} options
     */
    constructor(options) {
      super();
      this.options = options;
    }
    setup() {
      this.headerText = this.translate('Edit') + ' · ' + this.options.label;
      this.formModel = new _model.default();
      this.formModel.setMultiple({
        ...this.options.data
      });
      this.recordView = new _editForModal.default({
        model: this.formModel,
        detailLayout: [{
          rows: [[{
            view: new _bool.default({
              name: 'soft',
              labelText: this.translate('soft', 'otherFields', 'DashletOptions')
            })
          }, {
            view: new _bool.default({
              name: 'small',
              labelText: this.translate('small', 'otherFields', 'DashletOptions')
            })
          }]]
        }]
      });
      this.assignView('record', this.recordView);
      this.buttonList = [{
        name: 'apply',
        style: 'danger',
        label: 'Apply',
        onClick: () => this.actionApply()
      }, {
        name: 'cancel',
        label: 'Cancel',
        onClick: () => this.actionClose()
      }];
    }

    /**
     * @private
     */
    actionApply() {
      if (this.recordView.validate()) {
        return;
      }
      this.options.onApply({
        soft: this.formModel.attributes.soft,
        small: this.formModel.attributes.small
      });
      this.close();
    }
  }
  _exports.default = ExpandedLayoutEditItemModalFieldView;
});

define("views/dashlets/abstract/record-list", ["exports", "views/dashlets/abstract/base", "search-manager", "helpers/record-modal"], function (_exports, _base, _searchManager, _recordModal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _searchManager = _interopRequireDefault(_searchManager);
  _recordModal = _interopRequireDefault(_recordModal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RecordListDashletView extends _base.default {
    templateContent = '<div class="list-container">{{{list}}}</div>';

    /**
     * @protected
     * @type {string[]}
     */
    additionalRowActionList = undefined;

    /**
     * A scope.
     * @type {string}
     */
    scope;
    listView = null;
    listViewColumn = 'views/record/list';
    listViewExpanded = 'views/record/list-expanded';
    layoutType = 'expanded';
    optionsFields = {
      title: {
        type: 'varchar',
        required: true
      },
      autorefreshInterval: {
        type: 'enumFloat',
        options: [0, 0.5, 1, 2, 5, 10]
      },
      displayRecords: {
        type: 'enumInt',
        options: [3, 4, 5, 10, 15]
      }
    };
    rowActionsView = 'views/record/row-actions/view-and-edit';

    /**
     * @protected
     * @type {boolean}
     */
    hasCollaborators;
    init() {
      this.name = this.options.name || this.name;
      this.scope = this.getMetadata().get(`dashlets.${this.name}.entityType`) || this.scope;
      this.additionalRowActionList = this.getMetadata().get(`dashlets.${this.name}.rowActionList`) || this.additionalRowActionList;
      this.hasCollaborators = !!this.getMetadata().get(`scopes.${this.scope}.collaborators`);
      super.init();
    }
    checkAccess() {
      return this.getAcl().check(this.scope, 'read');
    }

    /**
     * @return {module:search-manager~data}
     */
    getSearchData() {
      /** @type {module:search-manager~data} */
      const data = Espo.Utils.cloneDeep(this.getOption('searchData'));
      if (!this.hasCollaborators) {
        return data;
      }
      if (this.getOption('includeShared')) {
        if (!data.bool) {
          data.bool = {};
        }
        data.bool.shared = true;
      }
      return data;
    }
    afterRender() {
      this.getCollectionFactory().create(this.scope, collection => {
        const searchData = this.getSearchData();
        this.searchManager = new _searchManager.default(collection, {
          defaultData: searchData
        });
        if (!this.scope) {
          this.$el.find('.list-container').html(this.translate('selectEntityType', 'messages', 'DashletOptions'));
          return;
        }
        if (!this.checkAccess()) {
          this.$el.find('.list-container').html(this.translate('No Access'));
          return;
        }
        if (this.collectionUrl) {
          collection.url = this.collectionUrl;
        }
        this.collection = collection;
        collection.orderBy = this.getOption('orderBy') || this.getOption('sortBy') || this.collection.orderBy;
        if (this.getOption('orderBy')) {
          collection.order = 'asc';
        }
        if (this.hasOption('asc')) {
          collection.order = this.getOption('asc') ? 'asc' : false;
        }
        if (this.getOption('sortDirection') === 'asc') {
          collection.order = 'asc';
        } else if (this.getOption('sortDirection') === 'desc') {
          collection.order = 'desc';
        }
        if (this.getOption('order') === 'asc') {
          collection.order = 'asc';
        } else if (this.getOption('order') === 'desc') {
          collection.order = 'desc';
        }
        collection.maxSize = this.getOption('displayRecords');
        collection.where = this.searchManager.getWhere();
        const viewName = this.listView || (this.layoutType === 'expanded' ? this.listViewExpanded : this.listViewColumn);
        this.createView('list', viewName, {
          collection: collection,
          selector: '.list-container',
          pagination: !!this.getOption('pagination'),
          type: 'listDashlet',
          rowActionsView: this.rowActionsView,
          checkboxes: false,
          showMore: true,
          listLayout: this.getOption(this.layoutType + 'Layout'),
          skipBuildRows: true,
          additionalRowActionList: this.additionalRowActionList
        }, view => {
          view.getSelectAttributeList(selectAttributeList => {
            if (selectAttributeList) {
              collection.data.select = selectAttributeList.join(',');
            }
            collection.fetch();
          });
        });
      });
    }
    setupActionList() {
      if (this.scope && this.getAcl().checkScope(this.scope, 'create')) {
        this.actionList.unshift({
          name: 'create',
          text: this.translate('Create ' + this.scope, 'labels', this.scope),
          iconHtml: '<span class="fas fa-plus"></span>',
          url: `#${this.scope}/create`
        });
      }
    }
    actionRefresh() {
      this.refreshInternal();
    }
    autoRefresh() {
      this.refreshInternal({
        skipNotify: true
      });
    }

    /**
     * @private
     * @param {{skipNotify?: boolean}} [options]
     * @return {Promise<void>}
     */
    async refreshInternal() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      if (!this.collection) {
        return;
      }
      if (!options.skipNotify) {
        Espo.Ui.notifyWait();
      }
      this.collection.where = this.searchManager.getWhere();
      await this.collection.fetch({
        previousDataList: this.collection.models.map(model => {
          return Espo.Utils.cloneDeep(model.attributes);
        })
      });
      if (!options.skipNotify) {
        Espo.Ui.notify();
      }
    }

    // noinspection JSUnusedGlobalSymbols
    actionCreate() {
      const attributes = this.getCreateAttributes() || {};
      if (this.getOption('populateAssignedUser')) {
        if (this.getMetadata().get(['entityDefs', this.scope, 'fields', 'assignedUsers'])) {
          attributes['assignedUsersIds'] = [this.getUser().id];
          attributes['assignedUsersNames'] = {};
          attributes['assignedUsersNames'][this.getUser().id] = this.getUser().get('name');
        } else {
          attributes['assignedUserId'] = this.getUser().id;
          attributes['assignedUserName'] = this.getUser().get('name');
        }
      }
      const helper = new _recordModal.default();
      helper.showCreate(this, {
        entityType: this.scope,
        attributes: attributes,
        afterSave: () => this.actionRefresh()
      });
    }
    getCreateAttributes() {}
    getColor() {
      if (!this.scope) {
        return null;
      }
      return this.getMetadata().get(`clientDefs.${this.scope}.color`);
    }
  }
  var _default = _exports.default = RecordListDashletView;
});

define("ui/timepicker", ["exports", "jquery"], function (_exports, _jquery) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _jquery = _interopRequireDefault(_jquery);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * A timepicker.
   */
  class Timepicker {
    /**
     * @param {HTMLElement} element
     * @param {{
     *     step: number,
     *     timeFormat: string,
     *     scrollDefaultNow?: boolean,
     * }} options
     */
    constructor(element, options) {
      /**
       * @private
       */
      this.$element = (0, _jquery.default)(element);
      const modalBodyElement = element.closest('.modal-body');
      this.$element.timepicker({
        step: options.step,
        timeFormat: options.timeFormat,
        appendTo: modalBodyElement ? (0, _jquery.default)(modalBodyElement) : 'body',
        scrollDefaultNow: options.scrollDefaultNow || false
      });
    }

    /**
     * Set the min time.
     *
     * @param {string|null} minTime
     */
    setMinTime(minTime) {
      this.$element.timepicker('option', 'minTime', minTime);
    }

    /**
     * Set the max time.
     *
     * @param {string|null} maxTime
     */
    setMaxTime(maxTime) {
      this.$element.timepicker('option', 'maxTime', maxTime);
    }

    /**
     * Add a 'change' event listener.
     *
     * @param {function} callback
     */
    addChangeEventListener(callback) {
      this.$element.on('change', callback);
    }

    /**
     * Show.
     */
    show() {
      this.$element.timepicker('show');
    }

    /**
     * Destroy.
     */
    destroy() {
      if (!this.$element[0]) {
        return;
      }
      this.$element.timepicker('remove');
    }
  }
  var _default = _exports.default = Timepicker;
});

define("helpers/misc/foreign-field", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 helpers/misc/foreign-field */

  class _default {
    /**
     * @param {module:views/fields/base} view A field view.
     */
    constructor(view) {
      /**
       * @private
       * @type {module:views/fields/base}
       */
      this.view = view;
      const metadata = view.getMetadata();
      const model = view.model;
      const field = view.params.field;
      const link = view.params.link;
      const entityType = metadata.get(['entityDefs', model.entityType, 'links', link, 'entity']) || model.entityType;
      const fieldDefs = metadata.get(['entityDefs', entityType, 'fields', field]) || {};
      const type = fieldDefs.type;
      const ignoreList = ['default', 'audited', 'readOnly', 'required'];

      /** @private */
      this.foreignParams = {};
      view.getFieldManager().getParamList(type).forEach(defs => {
        const name = defs.name;
        if (ignoreList.includes(name)) {
          return;
        }
        this.foreignParams[name] = fieldDefs[name] || null;
      });
    }

    /**
     * @return {Object.<string, *>}
     */
    getForeignParams() {
      return Espo.Utils.cloneDeep(this.foreignParams);
    }
  }
  _exports.default = _default;
});

define("helpers/misc/field-language", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 helpers/misc/field-language */

  /**
   * A field-language util.
   */
  class FieldLanguage {
    /**
     * @param {module:metadata} metadata A metadata.
     * @param {module:language} language A language.
     */
    constructor(metadata, language) {
      /**
       * @private
       * @type {module:metadata}
       */
      this.metadata = metadata;

      /**
       * @private
       * @type {module:language}
       */
      this.language = language;
    }

    /**
     * Translate an attribute.
     *
     * @param {string} scope A scope.
     * @param {string} name An attribute name.
     * @returns {string}
     */
    translateAttribute(scope, name) {
      let label = this.language.translate(name, 'fields', scope);
      if (name.indexOf('Id') === name.length - 2) {
        const baseField = name.slice(0, name.length - 2);
        if (this.metadata.get(['entityDefs', scope, 'fields', baseField])) {
          label = this.language.translate(baseField, 'fields', scope) + ' (' + this.language.translate('id', 'fields') + ')';
        }
      } else if (name.indexOf('Name') === name.length - 4) {
        const baseField = name.slice(0, name.length - 4);
        if (this.metadata.get(['entityDefs', scope, 'fields', baseField])) {
          label = this.language.translate(baseField, 'fields', scope) + ' (' + this.language.translate('name', 'fields') + ')';
        }
      } else if (name.indexOf('Type') === name.length - 4) {
        const baseField = name.slice(0, name.length - 4);
        if (this.metadata.get(['entityDefs', scope, 'fields', baseField])) {
          label = this.language.translate(baseField, 'fields', scope) + ' (' + this.language.translate('type', 'fields') + ')';
        }
      }
      if (name.indexOf('Ids') === name.length - 3) {
        const baseField = name.slice(0, name.length - 3);
        if (this.metadata.get(['entityDefs', scope, 'fields', baseField])) {
          label = this.language.translate(baseField, 'fields', scope) + ' (' + this.language.translate('ids', 'fields') + ')';
        }
      } else if (name.indexOf('Names') === name.length - 5) {
        const baseField = name.slice(0, name.length - 5);
        if (this.metadata.get(['entityDefs', scope, 'fields', baseField])) {
          label = this.language.translate(baseField, 'fields', scope) + ' (' + this.language.translate('names', 'fields') + ')';
        }
      } else if (name.indexOf('Types') === name.length - 5) {
        const baseField = name.slice(0, name.length - 5);
        if (this.metadata.get(['entityDefs', scope, 'fields', baseField])) {
          label = this.language.translate(baseField, 'fields', scope) + ' (' + this.language.translate('types', 'fields') + ')';
        }
      }
      return label;
    }
  }
  var _default = _exports.default = FieldLanguage;
});

define("handlers/select-related", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 handlers/select-related */

  /**
   * @typedef {Object} module:handlers/select-related~filters
   * @property {Object.<string, module:search-manager~advancedFilter>} [advanced]
   *  Advanced filters map. A field name as a key.
   * @property {string[]} [bool] Bool filters.
   * @property {string} [primary] A primary filter.
   * @property {string} [orderBy] A field to order by.
   * @property {'asc'|'desc'} [order] An order direction.
   */

  /**
   * Prepares filters for selecting records to relate.
   *
   * @abstract
   */
  class SelectRelatedHandler {
    /**
     * @protected
     * @type {import('view-helper').default}
     */
    viewHelper;

    /**
     * @param {import('view-helper').default} viewHelper
     */
    constructor(viewHelper) {
      this.viewHelper = viewHelper;
    }

    /**
     * Get filters for selecting records to relate.
     *
     * @abstract
     * @param {module:model} model A model.
     * @return {Promise<module:handlers/select-related~filters>} Filters.
     */
    getFilters(model) {
      return Promise.resolve({});
    }
  }
  var _default = _exports.default = SelectRelatedHandler;
});

define("handlers/row-action", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 handlers/row-action */

  /**
   * @abstract
   */
  class RowActionHandler {
    /**
     * @param {module:views/record/list} view
     */
    constructor(view) {
      // noinspection JSUnusedGlobalSymbols
      /** @protected */
      this.view = view;

      /**
       * @protected
       * @type {module:collection}
       */
      this.collection = this.view.collection;
    }

    /**
     * @param {module:model} model A model.
     * @param {string} action An action.
     * @return {boolean}
     */
    isAvailable(model, action) {
      return true;
    }

    /**
     * @param {module:model} model A model.
     * @param {string} action An action.
     */
    process(model, action) {}
  }
  var _default = _exports.default = RowActionHandler;
});

define("handlers/login", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 handlers/login */

  /**
   * Custom login handling. To be extended.
   *
   * @abstract
   */
  class LoginHandler {
    /**
     * @param {module:views/login} loginView A login view.
     * @param {Object.<string, *>} data Additional metadata.
     */
    constructor(loginView, data) {
      /**
       * A login view.
       * @protected
       * @type {module:views/login}
       */
      this.loginView = loginView;

      /**
       * Additional metadata.
       * @protected
       * @type {Object.<string, *>}
       */
      this.data = data;
    }

    /**
     * Process. Called on 'Sign in' button click.
     *
     * @public
     * @abstract
     * @return {Promise<Object.<string, string>>} Resolved with headers to be sent to the `App/user` endpoint.
     */
    process() {
      return Promise.resolve({});
    }
  }
  var _default = _exports.default = LoginHandler;
});

define("handlers/map/renderer", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 handlers/map/renderer
   */

  /**
   * A map renderer.
   *
   * @abstract
   */
  class MapRenderer {
    /**
     * @typedef {Object} module:handlers/map/renderer~addressData
     * @property {string|null} street
     * @property {string|null} city
     * @property {string|null} country
     * @property {string|null} state
     * @property {string|null} postalCode
     */

    /**
     * @param {import('views/fields/map').default} view A field view.
     */
    constructor(view) {
      this.view = view;
    }

    /**
     * @param {module:handlers/map/renderer~addressData} addressData
     * @abstract
     */
    render(addressData) {}
  }
  var _default = _exports.default = MapRenderer;
});

define("controllers/record", ["exports", "controller"], function (_exports, _controller) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _controller = _interopRequireDefault(_controller);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 controllers/record */

  /**
   * A record controller.
   * @template {module:model} TModel
   */
  class RecordController extends _controller.default {
    /** @inheritDoc */
    defaultAction = 'list';
    constructor(params, injections) {
      super(params, injections);

      /**
       * @private
       * @type {Object}
       */
      this.collectionMap = {};
    }

    /** @inheritDoc */
    checkAccess(action) {
      if (this.getAcl().check(this.name, action)) {
        return true;
      }
      return false;
    }

    /**
     * Get a view name/path.
     *
     * @protected
     * @param {'list'|'detail'|'edit'|'create'|'listRelated'|string} type A type.
     * @returns {string}
     */
    getViewName(type) {
      return this.getMetadata().get(['clientDefs', this.name, 'views', type]) || 'views/' + Espo.Utils.camelCaseToHyphen(type);
    }

    // noinspection JSUnusedGlobalSymbols
    beforeList() {
      this.handleCheckAccess('read');
    }

    /**
     * @param {{
     *     isReturn?: boolean,
     *     primaryFilter?: string,
     * } | Record} options
     */
    actionList(options) {
      const isReturn = options.isReturn || this.getRouter().backProcessed;
      let key = 'list';
      if (options.primaryFilter) {
        key += 'Filter' + Espo.Utils.upperCaseFirst(options.primaryFilter);
      }
      if (!isReturn && this.getStoredMainView(key)) {
        this.clearStoredMainView(key);
      }
      this.getCollection().then(collection => {
        const mediator = {};
        const abort = () => {
          collection.abortLastFetch();
          mediator.abort = true;
          Espo.Ui.notify(false);
        };
        this.listenToOnce(this.baseController, 'action', abort);
        this.listenToOnce(collection, 'sync', () => this.stopListening(this.baseController, 'action', abort));
        const viewOptions = {
          scope: this.name,
          collection: collection,
          params: options,
          mediator: mediator
        };
        const viewName = this.getViewName('list');
        const params = {
          useStored: isReturn,
          key: key
        };
        this.main(viewName, viewOptions, null, params);
      });
    }
    beforeView() {
      this.handleCheckAccess('read');
    }

    /**
     * @protected
     * @param {{
     *     returnUrl?: string,
     *     returnDispatchParams?: Record,
     * } | Record} options
     * @param {module:model} model
     * @param {string|null} [view]
     */
    createViewView(options, model, view) {
      view = view || this.getViewName('detail');
      this.main(view, {
        scope: this.name,
        model: model,
        returnUrl: options.returnUrl,
        returnDispatchParams: options.returnDispatchParams,
        params: options
      });
    }

    /**
     * @protected
     * @param {module:model} model
     * @param {Record} options
     */
    prepareModelView(model, options) {}

    // noinspection JSUnusedGlobalSymbols
    /**
     * @param {{
     *     model?: module:model,
     *     id?: string,
     *     isReturn?: boolean,
     *     isAfterCreate?: boolean,
     * } | Record} options
     */
    actionView(options) {
      const id = options.id;
      const isReturn = this.getRouter().backProcessed;
      if (isReturn) {
        if (this.lastViewActionOptions && this.lastViewActionOptions.id === id) {
          options = Espo.Utils.clone(this.lastViewActionOptions);
          if (options.model && options.model.get('deleted')) {
            delete options.model;
          }
        }
        options.isReturn = true;
      } else {
        delete this.lastViewActionOptions;
      }
      this.lastViewActionOptions = options;
      const createView = model => {
        this.prepareModelView(model, options);
        this.createViewView.call(this, options, model);
      };
      if ('model' in options) {
        const model = options.model;
        createView(model);
        this.showLoadingNotification();
        model.fetch().then(() => this.hideLoadingNotification()).catch(xhr => {
          if (xhr.status === 403 && options.isAfterCreate) {
            this.hideLoadingNotification();
            xhr.errorIsHandled = true;
            model.trigger('fetch-forbidden');
          }
        });
        this.listenToOnce(this.baseController, 'action', () => {
          model.abortLastFetch();
          this.hideLoadingNotification();
        });
        return;
      }
      this.getModel().then(model => {
        model.id = id;
        this.showLoadingNotification();
        model.fetch({
          main: true
        }).then(() => {
          this.hideLoadingNotification();
          if (model.get('deleted')) {
            this.listenToOnce(model, 'after:restore-deleted', () => {
              createView(model);
            });
            this.prepareModelView(model, options);
            this.createViewView(options, model, 'views/deleted-detail');
            return;
          }
          createView(model);
        });
        this.listenToOnce(this.baseController, 'action', () => {
          model.abortLastFetch();
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    beforeCreate() {
      this.handleCheckAccess('create');
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * @protected
     * @param {module:model} model
     * @param {Record} options
     */
    prepareModelCreate(model, options) {
      this.listenToOnce(model, 'before:save', () => {
        const key = 'list';
        const stored = this.getStoredMainView(key);
        if (!stored) {
          return;
        }
        if (!('storeViewAfterCreate' in stored) || !stored.storeViewAfterCreate) {
          this.clearStoredMainView(key);
        }
      });
      this.listenToOnce(model, 'after:save', () => {
        const key = 'list';
        const stored = this.getStoredMainView(key);
        if (!stored) {
          return;
        }
        if (!('storeViewAfterCreate' in stored) || !stored.storeViewAfterCreate) {
          return;
        }
        if (!('collection' in stored) || !stored.collection) {
          return;
        }
        this.listenToOnce(stored, 'after:render', () => stored.collection.fetch());
      });
    }

    /**
     * @param {{
     *     options?: Record,
     *     relate?: model:model~setRelateItem | model:model~setRelateItem[],
     *     returnUrl?: string,
     *     returnDispatchParams?: Record,
     *     attributes?: Record,
     * } | Record} [options]
     */
    create(options) {
      options = options || {};
      const optionsOptions = options.options || {};
      this.getModel().then(model => {
        if (options.relate) {
          model.setRelate(options.relate);
        }
        const o = {
          scope: this.name,
          model: model,
          returnUrl: options.returnUrl,
          returnDispatchParams: options.returnDispatchParams,
          params: options
        };
        for (const k in optionsOptions) {
          o[k] = optionsOptions[k];
        }
        if (options.attributes) {
          model.set(options.attributes);
        }
        this.prepareModelCreate(model, options);
        this.main(this.getViewName('edit'), o);
      });
    }

    /**
     * @param {{
     *     options?: Record,
     *     relate?: model:model~setRelateItem | model:model~setRelateItem[],
     *     returnUrl?: string,
     *     returnDispatchParams?: Record,
     *     options?: Record,
     *     attributes?: Record,
     * } | Record} [options]
     */
    actionCreate(options) {
      this.create(options);
    }

    // noinspection JSUnusedGlobalSymbols
    beforeEdit() {
      this.handleCheckAccess('edit');
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * @protected
     * @param {module:model} model
     * @param {Record} options
     */
    prepareModelEdit(model, options) {
      this.listenToOnce(model, 'before:save', () => {
        const key = 'list';
        const stored = this.getStoredMainView(key);
        if (!stored) {
          return;
        }
        if (!('storeViewAfterUpdate' in stored) || !stored.storeViewAfterUpdate) {
          this.clearStoredMainView(key);
        }
      });
    }

    /**
     * @param {{
     *     id: string,
     *     options?: Record,
     *     model?: import('model').default,
     *     returnUrl?: string,
     *     attributes?: Record,
     *     highlightFieldList?: string[],
     *     returnDispatchParams?: Record,
     * }} options
     */
    actionEdit(options) {
      const id = options.id;
      const optionsOptions = options.options || {};
      this.getModel().then(model => {
        model.id = id;
        if (options.model) {
          model = options.model;
        }
        this.prepareModelEdit(model, options);
        this.showLoadingNotification();
        model.fetch({
          main: true
        }).then(() => {
          this.hideLoadingNotification();
          const o = {
            scope: this.name,
            model: model,
            returnUrl: options.returnUrl,
            returnDispatchParams: options.returnDispatchParams,
            params: options
          };
          for (const k in optionsOptions) {
            o[k] = optionsOptions[k];
          }
          if (options.attributes) {
            o.attributes = options.attributes;
          }
          if (options.highlightFieldList) {
            o.highlightFieldList = options.highlightFieldList;
          }
          this.main(this.getViewName('edit'), o);
        });
        this.listenToOnce(this.baseController, 'action', () => {
          model.abortLastFetch();
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    beforeMerge() {
      this.handleCheckAccess('edit');
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @param {{
     *     ids: string,
     *     collection: import('collection').default,
     * }} options
     */
    actionMerge(options) {
      const ids = options.ids.split(',');
      this.getModel().then(model => {
        const models = [];
        const proceed = () => {
          this.main('views/merge', {
            models: models,
            scope: this.name,
            collection: options.collection
          });
        };
        let i = 0;
        ids.forEach(id => {
          const current = model.clone();
          current.id = id;
          models.push(current);
          this.listenToOnce(current, 'sync', () => {
            i++;
            if (i === ids.length) {
              proceed();
            }
          });
          current.fetch();
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @param {{
     *     id: string,
     *     link: string,
     * }} options
     */
    actionRelated(options) {
      const id = options.id;
      const link = options.link;
      const viewName = this.getViewName('listRelated');
      let model;
      this.getModel().then(m => {
        model = m;
        model.id = id;
        return model.fetch({
          main: true
        });
      }).then(() => {
        const foreignEntityType = model.getLinkParam(link, 'entity');
        if (!foreignEntityType) {
          this.baseController.error404();
          throw new Error(`Bad link '${link}'.`);
        }
        return this.collectionFactory.create(foreignEntityType);
      }).then(collection => {
        collection.url = model.entityType + '/' + id + '/' + link;
        this.main(viewName, {
          scope: this.name,
          model: model,
          collection: collection,
          link: link
        });
      });
    }

    /**
     * Get a collection for the current controller.
     *
     * @protected
     * @param {boolean} [usePreviouslyFetched=false] Use a previously fetched. @todo Revise.
     * @return {Promise<module:collection>}
     */
    getCollection(usePreviouslyFetched) {
      if (!this.name) {
        throw new Error('No collection for unnamed controller');
      }
      const entityType = this.entityType || this.name;
      if (usePreviouslyFetched && entityType in this.collectionMap) {
        const collection = this.collectionMap[entityType];
        return Promise.resolve(collection);
      }
      return this.collectionFactory.create(entityType, collection => {
        this.collectionMap[entityType] = collection;
        this.listenTo(collection, 'sync', () => collection.isFetched = true);
      });
    }

    /**
     * Get a model for the current controller.
     *
     * @protected
     * @param {Function} [callback]
     * @param {Object} [context]
     * @return {Promise<module:model>}
     */
    getModel(callback, context) {
      context = context || this;
      if (!this.name) {
        throw new Error('No collection for unnamed controller');
      }
      const modelName = this.entityType || this.name;
      return this.modelFactory.create(modelName, model => {
        if (callback) {
          callback.call(context, model);
        }
      });
    }
  }
  var _default = _exports.default = RecordController;
});

define("views/settings/fields/theme", ["exports", "views/fields/enum", "theme-manager", "ui/select"], function (_exports, _enum, _themeManager, _select) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  _themeManager = _interopRequireDefault(_themeManager);
  _select = _interopRequireDefault(_select);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ThemeSettingsFieldView extends _enum.default {
    // language=Handlebars
    editTemplateContent = `
        <div class="grid-auto-fit-xxs">
            <div>
                <select data-name="{{name}}" class="form-control main-element">
                    {{options
                        params.options value
                        scope=scope
                        field=name
                        translatedOptions=translatedOptions
                        includeMissingOption=true
                        styleMap=params.style
                    }}
                </select>
            </div>
            {{#if navbarOptionList.length}}
            <div>
                <select data-name="themeNavbar" class="form-control">
                    {{options navbarOptionList navbar translatedOptions=navbarTranslatedOptions}}
                </select>
            </div>
            {{/if}}
        </div>
    `;
    data() {
      const data = super.data();
      data.navbarOptionList = this.getNavbarOptionList();
      data.navbar = this.getNavbarValue() || this.getDefaultNavbar();
      data.navbarTranslatedOptions = {};
      data.navbarOptionList.forEach(item => {
        data.navbarTranslatedOptions[item] = this.translate(item, 'themeNavbars');
      });
      return data;
    }
    setup() {
      super.setup();
      this.initThemeManager();
      this.model.on('change:theme', (m, v, o) => {
        this.initThemeManager();
        if (o.ui) {
          this.reRender().then(() => _select.default.focus(this.$element, {
            noTrigger: true
          }));
        }
      });
    }
    afterRenderEdit() {
      this.$navbar = this.$el.find('[data-name="themeNavbar"]');
      this.$navbar.on('change', () => this.trigger('change'));
      _select.default.init(this.$navbar);
    }

    /**
     * @protected
     * @return {string}
     */
    getNavbarValue() {
      const params = this.model.get('themeParams') || {};
      return params.navbar;
    }

    /**
     * @protected
     * @return {Record|null}
     */
    getNavbarDefs() {
      if (!this.themeManager) {
        return null;
      }
      const params = this.themeManager.getParam('params');
      if (!params || !params.navbar) {
        return null;
      }
      return Espo.Utils.cloneDeep(params.navbar);
    }

    /**
     * @private
     * @return {string[]}
     */
    getNavbarOptionList() {
      const defs = this.getNavbarDefs();
      if (!defs) {
        return [];
      }
      const optionList = defs.options || [];
      if (!optionList.length || optionList.length === 1) {
        return [];
      }
      return optionList;
    }

    /**
     * @protected
     * @return {string|null}
     */
    getDefaultNavbar() {
      const defs = this.getNavbarDefs() || {};
      return defs.default || null;
    }

    /**
     * @private
     */
    initThemeManager() {
      const theme = this.model.get('theme');
      if (!theme) {
        this.themeManager = null;
        return;
      }
      this.themeManager = new _themeManager.default(this.getConfig(), this.getPreferences(), this.getMetadata(), theme);
    }
    getAttributeList() {
      return [this.name, 'themeParams'];
    }
    setupOptions() {
      this.params.options = Object.keys(this.getMetadata().get('themes') || {}).sort((v1, v2) => {
        if (v2 === 'EspoRtl') {
          return -1;
        }
        return this.translate(v1, 'theme').localeCompare(this.translate(v2, 'theme'));
      });
    }
    fetch() {
      const data = super.fetch();
      const params = {};
      if (this.$navbar.length) {
        params.navbar = this.$navbar.val();
      }
      data.themeParams = params;
      return data;
    }
  }
  _exports.default = ThemeSettingsFieldView;
});

define("views/settings/fields/tab-list", ["exports", "views/fields/array"], function (_exports, _array) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _array = _interopRequireDefault(_array);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class TabListFieldView extends _array.default {
    addItemModalView = 'views/settings/modals/tab-list-field-add';
    noGroups = false;
    noDelimiters = false;
    setup() {
      super.setup();
      this.selected.forEach(item => {
        if (item && typeof item === 'object') {
          if (!item.id) {
            item.id = this.generateItemId();
          }
        }
      });
      this.events['click [data-action="editGroup"]'] = e => {
        const id = $(e.currentTarget).parent().data('value').toString();
        this.editGroup(id);
      };
    }
    generateItemId() {
      return Math.floor(Math.random() * 1000000 + 1).toString();
    }
    setupOptions() {
      this.params.options = Object.keys(this.getMetadata().get('scopes')).filter(scope => {
        if (this.getMetadata().get(`scopes.${scope}.disabled`)) {
          return false;
        }
        if (!this.getAcl().checkScope(scope)) {
          return false;
        }
        return this.getMetadata().get(`scopes.${scope}.tab`);
      }).sort((v1, v2) => {
        return this.translate(v1, 'scopeNamesPlural').localeCompare(this.translate(v2, 'scopeNamesPlural'));
      });
      if (!this.noDelimiters) {
        this.params.options.push('_delimiter_');
        this.params.options.push('_delimiter-ext_');
      }
      this.translatedOptions = {};
      this.params.options.forEach(item => {
        this.translatedOptions[item] = this.translate(item, 'scopeNamesPlural');
      });
      this.translatedOptions['_delimiter_'] = '. . .';
      this.translatedOptions['_delimiter-ext_'] = '. . .';
    }
    addValue(value) {
      if (value && typeof value === 'object') {
        if (!value.id) {
          value.id = this.generateItemId();
        }
        const html = this.getItemHtml(value);
        this.$list.append(html);
        this.selected.push(value);
        this.trigger('change');
        return;
      }
      super.addValue(value);
    }
    removeValue(value) {
      const index = this.getGroupIndexById(value);
      if (~index) {
        this.$list.children(`[data-value="${value}"]`).remove();
        this.selected.splice(index, 1);
        this.trigger('change');
        return;
      }
      super.removeValue(value);
    }
    getItemHtml(value) {
      if (value && typeof value === 'object') {
        return this.getGroupItemHtml(value);
      }
      return super.getItemHtml(value);
    }
    getGroupItemHtml(item) {
      const label = item.text || '';
      const $label = $('<span>').text(label);
      let $icon = null;
      if (item.type === 'group') {
        $icon = $('<span>').addClass('far fa-list-alt').addClass('text-muted');
      }
      if (item.type === 'url') {
        $icon = $('<span>').addClass('fas fa-link fa-sm').addClass('text-muted');
      }
      if (item.type === 'divider') {
        $label.addClass('text-soft').addClass('text-italic');
      }
      const $item = $('<span>').append($label);
      if ($icon) {
        $item.prepend($icon, ' ');
      }
      return $('<div>').addClass('list-group-item').attr('data-value', item.id).css('cursor', 'default').append($('<a>').attr('role', 'button').attr('tabindex', '0').attr('data-value', item.id).attr('data-action', 'editGroup').css('margin-right', '7px').append($('<span>').addClass('fas fa-pencil-alt fa-sm')), $item, '&nbsp;', $('<a>').addClass('pull-right').attr('role', 'button').attr('tabindex', '0').attr('data-value', item.id).attr('data-action', 'removeValue').append($('<span>').addClass('fas fa-times'))).get(0).outerHTML;
    }
    fetchFromDom() {
      const selected = [];
      this.$el.find('.list-group .list-group-item').each((i, el) => {
        const value = $(el).data('value').toString();
        const groupItem = this.getGroupValueById(value);
        if (groupItem) {
          selected.push(groupItem);
          return;
        }
        selected.push(value);
      });
      this.selected = selected;
    }
    getGroupIndexById(id) {
      for (let i = 0; i < this.selected.length; i++) {
        const item = this.selected[i];
        if (item && typeof item === 'object') {
          if (item.id === id) {
            return i;
          }
        }
      }
      return -1;
    }
    getGroupValueById(id) {
      for (const item of this.selected) {
        if (item && typeof item === 'object') {
          if (item.id === id) {
            return item;
          }
        }
      }
      return null;
    }
    editGroup(id) {
      const item = Espo.Utils.cloneDeep(this.getGroupValueById(id) || {});
      const index = this.getGroupIndexById(id);
      const tabList = Espo.Utils.cloneDeep(this.selected);
      const view = {
        divider: 'views/settings/modals/edit-tab-divider',
        url: 'views/settings/modals/edit-tab-url'
      }[item.type] || 'views/settings/modals/edit-tab-group';
      this.createView('dialog', view, {
        itemData: item,
        parentType: this.model.entityType
      }, view => {
        view.render();
        this.listenToOnce(view, 'apply', itemData => {
          for (const a in itemData) {
            tabList[index][a] = itemData[a];
          }
          this.model.set(this.name, tabList);
          view.close();
        });
      });
    }
    getAddItemModalOptions() {
      return {
        ...super.getAddItemModalOptions(),
        noGroups: this.noGroups
      };
    }
    getValueForDisplay() {
      const labels = this.translatedOptions || {};

      /** @var {string[]} */
      const list = this.selected.map(item => {
        if (typeof item !== 'string') {
          return ' - ' + (item.text || '?');
        }
        return labels[item] || item;
      });
      return list.map(text => {
        return $('<div>').addClass('multi-enum-item-container').text(text).get(0).outerHTML;
      }).join('');
    }
  }
  var _default = _exports.default = TabListFieldView;
});

define("multi-collection", ["exports", "collection"], function (_exports, _collection) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _collection = _interopRequireDefault(_collection);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 multi-collection */

  /**
   * A collection that can contain entities of different entity types.
   */
  class MultiCollection extends _collection.default {
    /**
     * A model seed map.
     *
     * @public
     * @type {Object.<string, module:model>}
     */
    seeds = null;

    /** @inheritDoc */
    prepareAttributes(response, options) {
      this.total = response.total;
      if (!('list' in response)) {
        throw new Error("No 'list' in response.");
      }

      /** @type {({_scope?: string} & Object.<string, *>)[]} */
      const list = response.list;
      return list.map(attributes => {
        const entityType = attributes._scope;
        if (!entityType) {
          throw new Error("No '_scope' attribute.");
        }
        attributes = _.clone(attributes);
        delete attributes['_scope'];
        const model = this.seeds[entityType].clone();
        model.set(attributes);
        return model;
      });
    }

    /** @inheritDoc */
    clone(options) {
      const collection = super.clone(options);
      collection.seeds = this.seeds;
      return collection;
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = MultiCollection;
});

define("app-portal", ["exports", "app", "acl-portal-manager"], function (_exports, _app, _aclPortalManager) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _app = _interopRequireDefault(_app);
  _aclPortalManager = _interopRequireDefault(_aclPortalManager);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 app-portal */

  /**
   * A portal application class.
   */
  class AppPortal extends _app.default {
    aclName = 'aclPortal';
    masterView = 'views/site-portal/master';
    createAclManager() {
      return new _aclPortalManager.default(this.user, null, this.settings.get('aclAllowDeleteCreated'));
    }
  }
  var _default = _exports.default = AppPortal;
});

define("views/stream", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class StreamView extends _view.default {
    template = 'stream';
    filterList = ['all', 'posts', 'updates'];
    filter = false;
    events = {
      /** @this StreamView */
      'click button[data-action="refresh"]': function () {
        this.actionRefresh();
      },
      /** @this StreamView */
      'click button[data-action="selectFilter"]': function (e) {
        const data = $(e.currentTarget).data();
        this.actionSelectFilter(data);
      }
    };
    data() {
      let filter = this.filter;
      if (filter === false) {
        filter = 'all';
      }
      const hasGlobalStreamAccess = this.getAcl().checkScope('GlobalStream');
      return {
        displayTitle: this.options.displayTitle,
        filterList: this.filterList,
        filter: filter,
        hasMenu: hasGlobalStreamAccess,
        hasGlobalStreamAccess: hasGlobalStreamAccess
      };
    }
    setup() {
      this.filter = this.options.filter || this.filter;
      this.addActionHandler('createPost', () => this.actionCreatePost());
      this.addHandler('keydown.stream', '', /** KeyboardEvent */event => this.onKeyDown(event));
    }
    afterRender() {
      Espo.Ui.notifyWait();
      this.getCollectionFactory().create('Note', collection => {
        this.collection = collection;
        collection.url = 'Stream';
        this.setFilter(this.filter);
        collection.fetch().then(() => {
          this.createView('list', 'views/stream/record/list', {
            selector: '.list-container',
            collection: collection,
            isUserStream: true
          }, view => {
            view.notify(false);
            view.render().then(view => {
              view.$el.find('> .list > .list-group');
            });
          });
        });
      });
      this.element.querySelector('.button-container').focus({
        preventScroll: true
      });
    }

    /**
     * @return {module:views/stream/record/list}
     */
    getRecordView() {
      return this.getView('list');
    }
    async actionSelectFilter(data) {
      const name = data.name;
      const filter = name;
      let internalFilter = name;
      if (filter === 'all') {
        internalFilter = false;
      }
      this.filter = internalFilter;
      this.setFilter(this.filter);
      this.filterList.forEach(item => {
        const $el = this.$el.find('.button-container button[data-action="selectFilter"][data-name="' + item + '"]');
        if (item === filter) {
          $el.addClass('active');
        } else {
          $el.removeClass('active');
        }
      });
      let url = '#Stream';
      if (this.filter) {
        url += '/' + filter;
      }
      this.getRouter().navigate(url);
      Espo.Ui.notifyWait();
      this.getRecordView().element.innerHTML = '';
      this.collection.abortLastFetch();
      this.collection.reset();
      try {
        await this.collection.fetch();
      } catch (e) {
        return;
      }
      Espo.Ui.notify(false);
    }
    setFilter(filter) {
      this.collection.data.filter = null;
      if (filter) {
        this.collection.data.filter = filter;
      }
      this.collection.offset = 0;
      this.collection.maxSize = this.getConfig().get('recordsPerPage') || this.collection.maxSize;
    }
    actionCreatePost() {
      this.createView('dialog', 'views/stream/modals/create-post', {}, view => {
        view.render();
        this.listenToOnce(view, 'after:save', () => {
          view.close();
          this.getRecordView().showNewRecords();
        });
      });
    }
    actionRefresh() {
      if (!this.getRecordView()) {
        return;
      }
      const iconEl = this.element.querySelector('button[data-action="refresh"] .icon');
      if (iconEl) {
        iconEl.classList.add('animation-spin-fast');
        setTimeout(() => iconEl.classList.remove('animation-spin-fast'), 500);
      }
      Espo.Ui.notifyWait();
      this.getRecordView().showNewRecords().then(() => Espo.Ui.notify(false));
    }

    /**
     * @private
     * @param {KeyboardEvent} event
     */
    onKeyDown(event) {
      const key = Espo.Utils.getKeyFromKeyEvent(event);
      if (key === 'Control+Space') {
        event.stopPropagation();
        event.preventDefault();
        this.actionCreatePost();
      }
    }
  }
  var _default = _exports.default = StreamView;
});

define("views/popup-notification", ["exports", "view", "jquery"], function (_exports, _view, _jquery) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _jquery = _interopRequireDefault(_jquery);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * To be extended with an own template.
   *
   * @abstract
   */
  class PopupNotificationView extends _view.default {
    type = 'default';
    style = 'default';
    closeButton = true;
    soundPath = 'client/sounds/pop_cork';
    init() {
      super.init();
      const id = this.options.id;
      const containerSelector = this.containerSelector = '#' + id;
      this.setSelector(containerSelector);
      this.notificationSoundsDisabled = this.getConfig().get('notificationSoundsDisabled');
      this.soundPath = this.getBasePath() + (this.getConfig().get('popupNotificationSound') || this.soundPath);
      this.on('render', () => {
        this.element = undefined;
        (0, _jquery.default)(containerSelector).remove();
        const className = 'popup-notification-' + Espo.Utils.toDom(this.type);
        (0, _jquery.default)('<div>').attr('id', id).addClass('popup-notification').addClass(className).addClass('popup-notification-' + this.style).appendTo('#popup-notifications-container');
        this.setElement(containerSelector);
      });
      this.on('after:render', () => {
        this.$el.find('[data-action="close"]').on('click', () => {
          this.resolveCancel();
        });
      });
      this.once('after:render', () => {
        this.onShow();
      });
      this.once('remove', function () {
        (0, _jquery.default)(containerSelector).remove();
      });
      this.notificationData = this.options.notificationData;
      this.notificationId = this.options.notificationId;
      this.id = this.options.id;
    }
    data() {
      return {
        closeButton: this.closeButton,
        notificationData: this.notificationData,
        notificationId: this.notificationId
      };
    }
    playSound() {
      if (this.notificationSoundsDisabled) {
        return;
      }
      const html = '<audio autoplay="autoplay">' + '<source src="' + this.soundPath + '.mp3" type="audio/mpeg" />' + '<source src="' + this.soundPath + '.ogg" type="audio/ogg" />' + '<embed hidden="true" autostart="true" loop="false" src="' + this.soundPath + '.mp3" />' + '</audio>';
      const $audio = (0, _jquery.default)(html);
      $audio.get(0).volume = 0.3;
      // noinspection JSUnresolvedReference
      $audio.get(0).play();
    }

    /**
     * @protected
     */
    onShow() {
      if (!this.options.isFirstCheck) {
        this.playSound();
      }
    }

    /**
     * An on-confirm action. To be extended.
     *
     * @protected
     */
    onConfirm() {}

    /**
     * An on-cancel action. To be extended.
     *
     * @protected
     */
    onCancel() {}
    resolveConfirm() {
      this.onConfirm();
      this.trigger('confirm');
      this.remove();
    }
    resolveCancel() {
      this.onCancel();
      this.trigger('cancel');
      this.remove();
    }

    // noinspection JSCheckFunctionSignatures
    /**
     * @deprecated Use `resolveConfirm`.
     */
    confirm() {
      console.warn(`Method 'confirm' in views/popup-notification is deprecated. Use 'resolveConfirm' instead.`);
      this.resolveConfirm();
    }

    /**
     * @deprecated Use `resolveCancel`.
     */
    cancel() {
      console.warn(`Method 'cancel' in views/popup-notification is deprecated. Use 'resolveCancel' instead.`);
      this.resolveCancel();
    }
  }
  var _default = _exports.default = PopupNotificationView;
});

define("views/merge", ["exports", "views/main"], function (_exports, _main) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _main = _interopRequireDefault(_main);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class MergeView extends _main.default {
    template = 'merge';
    name = 'Merge';
    headerView = 'views/header';
    recordView = 'views/record/merge';
    setup() {
      this.models = this.options.models;
      this.setupHeader();
      this.setupRecord();
    }
    setupHeader() {
      this.createView('header', this.headerView, {
        model: this.model,
        fullSelector: '#main > .page-header'
      });
    }
    setupRecord() {
      this.createView('body', this.recordView, {
        fullSelector: '#main > .body',
        models: this.models,
        collection: this.collection
      });
    }
    getHeader() {
      return this.buildHeaderHtml([$('<a>').attr('href', '#' + this.models[0].entityType).text(this.getLanguage().translate(this.models[0].entityType, 'scopeNamesPlural')), $('<span>').text(this.getLanguage().translate('Merge'))]);
    }
    updatePageTitle() {
      this.setPageTitle(this.getLanguage().translate('Merge'));
    }
  }
  var _default = _exports.default = MergeView;
});

define("views/login", ["exports", "view", "js-base64", "jquery"], function (_exports, _view, _jsBase, _jquery) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _jsBase = _interopRequireDefault(_jsBase);
  _jquery = _interopRequireDefault(_jquery);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/login */

  class LoginView extends _view.default {
    /** @inheritDoc */
    template = 'login';

    /** @inheritDoc */
    views = {
      footer: {
        fullSelector: 'body > footer',
        view: 'views/site/footer'
      }
    };

    /**
     * @type {string|null}
     * @private
     */
    anotherUser = null;

    /** @private */
    isPopoverDestroyed = false;

    /**
     * @type {module:handlers/login}
     * @private
     */
    handler = null;

    /**
     * @type {boolean}
     * @private
     */
    fallback = false;

    /**
     * @type {string|null}
     * @private
     */
    method = null;

    /** @inheritDoc */
    events = {
      /** @this LoginView */
      'submit #login-form': function (e) {
        e.preventDefault();
        this.login();
      },
      /** @this LoginView */
      'click #sign-in': function () {
        this.signIn();
      },
      /** @this LoginView */
      'click a[data-action="passwordChangeRequest"]': function () {
        this.showPasswordChangeRequest();
      },
      /** @this LoginView */
      'click a[data-action="showFallback"]': function () {
        this.showFallback();
      },
      /** @this LoginView */
      'keydown': function (e) {
        if (Espo.Utils.getKeyFromKeyEvent(e) === 'Control+Enter') {
          e.preventDefault();
          if (this.handler && (!this.fallback || !this.$username.val())) {
            this.signIn();
            return;
          }
          this.login();
        }
      }
    };

    /** @inheritDoc */
    data() {
      return {
        logoSrc: this.getLogoSrc(),
        showForgotPassword: this.getConfig().get('passwordRecoveryEnabled'),
        anotherUser: this.anotherUser,
        hasSignIn: !!this.handler,
        hasFallback: !!this.handler && this.fallback,
        method: this.method,
        signInText: this.signInText,
        logInText: this.logInText
      };
    }

    /** @inheritDoc */
    setup() {
      this.addActionHandler('toggleShowPassword', () => this.toggleShowPassword());
      this.anotherUser = this.options.anotherUser || null;
      const loginData = this.getConfig().get('loginData') || {};
      this.fallback = !!loginData.fallback;
      this.method = loginData.method;
      if (loginData.handler) {
        this.wait(Espo.loader.requirePromise(loginData.handler).then(Handler => {
          this.handler = new Handler(this, loginData.data || {});
        }));
        this.signInText = this.getLanguage().has(this.method, 'signInLabels', 'Global') ? this.translate(this.method, 'signInLabels') : this.translate('Sign in');
      }
      this.wait(this.getHelper().processSetupHandlers(this, 'login'));
      if (this.getLanguage().has('Log in', 'labels', 'Global')) {
        this.logInText = this.translate('Log in');
      }
      this.logInText = this.getLanguage().has('Log in', 'labels', 'Global') ? this.translate('Log in') : this.translate('Login');
    }

    /**
     * @private
     * @return {string}
     */
    getLogoSrc() {
      const companyLogoId = this.getConfig().get('companyLogoId');
      if (!companyLogoId) {
        return this.getBasePath() + (this.getConfig().get('logoSrc') || 'client/img/logo.svg');
      }
      return this.getBasePath() + '?entryPoint=LogoImage&id=' + companyLogoId;
    }

    /** @inheritDoc */
    afterRender() {
      this.$submit = this.$el.find('#btn-login');
      this.$signIn = this.$el.find('#sign-in');
      this.$username = this.$el.find('#field-userName');
      this.$password = this.$el.find('#field-password');
      if (this.options.prefilledUsername) {
        this.$username.val(this.options.prefilledUsername);
      }
      if (this.handler) {
        this.$username.closest('.cell').addClass('hidden');
        this.$password.closest('.cell').addClass('hidden');
        this.$submit.closest('.cell').addClass('hidden');
      }
    }

    /** @private */
    signIn() {
      this.disableForm();
      this.handler.process().then(headers => {
        this.proceed(headers);
      }).catch(() => {
        this.undisableForm();
      });
    }

    /** @private */
    login() {
      let authString;
      let userName = this.$username.val();
      const password = this.$password.val();
      const trimmedUserName = userName.trim();
      if (trimmedUserName !== userName) {
        this.$username.val(trimmedUserName);
        userName = trimmedUserName;
      }
      if (userName === '') {
        this.processEmptyUsername();
        return;
      }
      this.disableForm();
      try {
        authString = _jsBase.default.encode(userName + ':' + password);
      } catch (e) {
        Espo.Ui.error(this.translate('Error') + ': ' + e.message, true);
        this.undisableForm();
        throw e;
      }
      const headers = {
        'Authorization': 'Basic ' + authString,
        'Espo-Authorization': authString
      };
      this.proceed(headers, userName, password);
    }

    /**
     * @private
     * @param {Object.<string, string>} headers
     * @param {string} [userName]
     * @param {string} [password]
     */
    proceed(headers, userName, password) {
      headers = Espo.Utils.clone(headers);
      const initialHeaders = Espo.Utils.clone(headers);
      headers['Espo-Authorization-By-Token'] = 'false';
      headers['Espo-Authorization-Create-Token-Secret'] = 'true';
      if (this.anotherUser !== null) {
        headers['X-Another-User'] = this.anotherUser;
      }
      this.notifyLoading();
      Espo.Ajax.getRequest('App/user', null, {
        login: true,
        headers: headers
      }).then(data => {
        Espo.Ui.notify(false);
        this.triggerLogin(userName, data);
      }).catch(xhr => {
        this.undisableForm();
        if (xhr.status === 401) {
          const data = xhr.responseJSON || {};
          const statusReason = xhr.getResponseHeader('X-Status-Reason');
          if (statusReason === 'second-step-required') {
            xhr.errorIsHandled = true;
            this.onSecondStepRequired(initialHeaders, userName, password, data);
            return;
          }
          if (statusReason === 'error') {
            this.onError();
            return;
          }
          this.onWrongCredentials();
        }
      });
    }

    /**
     * Trigger login to proceed to the application.
     *
     * @private
     * @param {string|null} userName A username.
     * @param {Object.<string, *>} data Data returned from the `App/user` request.
     */
    triggerLogin(userName, data) {
      if (this.anotherUser) {
        data.anotherUser = this.anotherUser;
      }
      if (!userName) {
        userName = (data.user || {}).userName;
      }
      this.trigger('login', userName, data);
    }

    /** @private */
    processEmptyUsername() {
      this.isPopoverDestroyed = false;
      const $el = this.$username;
      const message = this.getLanguage().translate('userCantBeEmpty', 'messages', 'User');
      $el.popover({
        placement: 'bottom',
        container: 'body',
        content: message,
        trigger: 'manual'
      }).popover('show');
      const $cell = $el.closest('.form-group');
      $cell.addClass('has-error');
      $el.one('mousedown click', () => {
        $cell.removeClass('has-error');
        if (this.isPopoverDestroyed) {
          return;
        }
        $el.popover('destroy');
        this.isPopoverDestroyed = true;
      });
    }

    /** @private */
    disableForm() {
      this.$submit.addClass('disabled').attr('disabled', 'disabled');
      this.$signIn.addClass('disabled').attr('disabled', 'disabled');
    }

    /** @private */
    undisableForm() {
      this.$submit.removeClass('disabled').removeAttr('disabled');
      this.$signIn.removeClass('disabled').removeAttr('disabled');
    }

    /**
     * @private
     * @param {Object.<string, string>} headers
     * @param {string} userName
     * @param {string} password
     * @param {Object.<string, *>} data
     */
    onSecondStepRequired(headers, userName, password, data) {
      const view = data.view || 'views/login-second-step';
      this.trigger('redirect', view, headers, userName, password, data);
    }

    /** @private */
    onError() {
      this.onFail('loginError');
    }

    /** @private */
    onWrongCredentials() {
      const msg = this.handler ? 'failedToLogIn' : 'wrongUsernamePassword';
      this.onFail(msg);
    }

    /** @private */
    onFail(msg) {
      const $cell = (0, _jquery.default)('#login .form-group');
      $cell.addClass('has-error');
      this.$el.one('mousedown click', () => {
        $cell.removeClass('has-error');
      });
      Espo.Ui.error(this.translate(msg, 'messages', 'User'));
    }

    /** @private */
    showFallback() {
      this.$el.find('[data-action="showFallback"]').addClass('hidden');
      this.$el.find('.panel-body').addClass('fallback-shown');
      this.$username.closest('.cell').removeClass('hidden');
      this.$password.closest('.cell').removeClass('hidden');
      this.$submit.closest('.cell').removeClass('hidden');
    }

    /** @private */
    notifyLoading() {
      Espo.Ui.notifyWait();
    }

    /** @private */
    showPasswordChangeRequest() {
      this.notifyLoading();
      this.createView('passwordChangeRequest', 'views/modals/password-change-request', {
        url: window.location.href
      }, view => {
        view.render();
        Espo.Ui.notify(false);
      });
    }

    /**
     * @private
     */
    toggleShowPassword() {
      const input = this.element.querySelector('[id="field-password"]');
      const button = this.element.querySelector('[data-action="toggleShowPassword"]');
      const icon = button.children[0];
      if (!(input instanceof HTMLInputElement)) {
        return;
      }
      if (input.type === 'password') {
        input.type = 'text';

        //button.classList.remove('text-soft');
        //button.classList.add('text-primary');

        icon.classList.remove('fa-eye');
        icon.classList.add('fa-eye-slash');
      } else {
        input.type = 'password';

        //button.classList.remove('text-primary');
        //button.classList.add('text-soft');

        icon.classList.remove('fa-eye-slash');
        icon.classList.add('fa-eye');
      }
      input.focus();
      input.setSelectionRange(input.value.length, input.value.length);
    }
  }
  var _default = _exports.default = LoginView;
});

define("views/login-second-step", ["exports", "view", "js-base64", "jquery"], function (_exports, _view, _jsBase, _jquery) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _jsBase = _interopRequireDefault(_jsBase);
  _jquery = _interopRequireDefault(_jquery);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class LoginSecondStepView extends _view.default {
    /** @inheritDoc */
    template = 'login-second-step';

    /** @inheritDoc */
    views = {
      footer: {
        fullSelector: 'body > footer',
        view: 'views/site/footer'
      }
    };

    /**
     * @type {string|null}
     * @private
     */
    anotherUser = null;

    /**
     * Response from the first step.
     *
     * @type {Object.<string, *>}
     * @private
     */
    loginData = null;

    /**
     * Headers composed in the first step.
     *
     * @type {Object.<string, string>}
     * @private
     */
    headers = null;

    /** @private */
    isPopoverDestroyed = false;

    /** @inheritDoc */
    events = {
      /** @this LoginSecondStepView */
      'submit #login-form': function (e) {
        e.preventDefault();
        this.send();
      },
      /** @this LoginSecondStepView */
      'click [data-action="backToLogin"]': function () {
        this.trigger('back');
      },
      /** @this LoginSecondStepView */
      'keydown': function (e) {
        if (Espo.Utils.getKeyFromKeyEvent(e) === 'Control+Enter') {
          e.preventDefault();
          this.send();
        }
      }
    };

    /** @inheritDoc */
    data() {
      return {
        message: this.message
      };
    }

    /** @inheritDoc */
    setup() {
      this.message = this.translate(this.options.loginData.message, 'messages', 'User');
      this.anotherUser = this.options.anotherUser || null;
      this.headers = this.options.headers || {};
      this.loginData = this.options.loginData;
    }

    /** @inheritDoc */
    afterRender() {
      this.$code = (0, _jquery.default)('[data-name="field-code"]');
      this.$submit = this.$el.find('#btn-send');
      this.$code.focus();
    }

    /** @private */
    send() {
      const code = this.$code.val().trim().replace(/\s/g, '');
      const userName = this.options.userName;
      const token = this.loginData.token;
      const headers = Espo.Utils.clone(this.headers);
      if (code === '') {
        this.processEmptyCode();
        return;
      }
      this.disableForm();
      if (userName && token) {
        const authString = _jsBase.default.encode(userName + ':' + token);
        headers['Authorization'] = 'Basic ' + authString;
        headers['Espo-Authorization'] = authString;
      }
      headers['Espo-Authorization-Code'] = code;
      headers['Espo-Authorization-Create-Token-Secret'] = 'true';
      if (this.anotherUser !== null) {
        headers['X-Another-User'] = this.anotherUser;
      }
      this.notifyLoading();
      Espo.Ajax.getRequest('App/user', null, {
        login: true,
        headers: headers
      }).then(data => {
        Espo.Ui.notify(false);
        this.triggerLogin(userName, data);
      }).catch(xhr => {
        this.undisableForm();
        if (xhr.status === 401) {
          const statusReason = xhr.getResponseHeader('X-Status-Reason');
          if (statusReason === 'error') {
            this.onError();
            return;
          }
          this.onWrongCredentials();
        }
      });
    }

    /**
     * Trigger login to proceed to the application.
     *
     * @private
     * @param {string} userName A username.
     * @param {Object.<string, *>} data Data returned from the `App/user` request.
     */
    triggerLogin(userName, data) {
      if (this.anotherUser) {
        data.anotherUser = this.anotherUser;
      }
      if (!userName) {
        userName = (data.user || {}).userName;
      }
      this.trigger('login', userName, data);
    }

    /** @private */
    processEmptyCode() {
      this.isPopoverDestroyed = false;
      const message = this.getLanguage().translate('codeIsRequired', 'messages', 'User');
      const $el = this.$code;
      $el.popover({
        placement: 'bottom',
        container: 'body',
        content: message,
        trigger: 'manual'
      }).popover('show');
      const $cell = $el.closest('.form-group');
      $cell.addClass('has-error');
      $el.one('mousedown click', () => {
        $cell.removeClass('has-error');
        if (this.isPopoverDestroyed) {
          return;
        }
        $el.popover('destroy');
        this.isPopoverDestroyed = true;
      });
    }

    /** @private */
    onFail(msg) {
      const $cell = (0, _jquery.default)('#login .form-group');
      $cell.addClass('has-error');
      this.$el.one('mousedown click', () => {
        $cell.removeClass('has-error');
      });
      Espo.Ui.error(this.translate(msg, 'messages', 'User'));
    }

    /** @private */
    onError() {
      this.onFail('loginError');
    }

    /** @private */
    onWrongCredentials() {
      this.onFail('wrongCode');
    }

    /** @private */
    notifyLoading() {
      Espo.Ui.notifyWait();
    }

    /** @private */
    disableForm() {
      this.$submit.addClass('disabled').attr('disabled', 'disabled');
    }

    /** @private */
    undisableForm() {
      this.$submit.removeClass('disabled').removeAttr('disabled');
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = LoginSecondStepView;
});

define("views/list-tree", ["exports", "views/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ListTreeView extends _list.default {
    searchPanel = false;
    createButton = false;
    name = 'listTree';
    getRecordViewName() {
      return this.getMetadata().get(['clientDefs', this.scope, 'recordViews', 'listTree']) || 'views/record/list-tree';
    }
  }
  var _default = _exports.default = ListTreeView;
});

define("views/list-related", ["exports", "views/main", "search-manager", "helpers/record/create-related"], function (_exports, _main, _searchManager, _createRelated) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _main = _interopRequireDefault(_main);
  _searchManager = _interopRequireDefault(_searchManager);
  _createRelated = _interopRequireDefault(_createRelated);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 module:views/list-related */

  /**
   * A list-related view.
   */
  class ListRelatedView extends _main.default {
    /** @inheritDoc */
    template = 'list';

    /** @inheritDoc */
    name = 'ListRelated';

    /**
     * A header view name.
     *
     * @type {string}
     */
    headerView = 'views/header';

    /**
     * A search view name.
     *
     * @type {string}
     */
    searchView = 'views/record/search';

    /**
     * A record/list view name.
     *
     * @type {string}
     */
    recordView = 'views/record/list';

    /**
     * Has a search panel.
     *
     * @type {boolean}
     */
    searchPanel = true;

    /**
     * @type {module:search-manager}
     */
    searchManager = null;

    /**
     * @inheritDoc
     */
    optionsToPass = [];

    /**
     * Use a current URL as a root URL when open a record. To be able to return to the same URL.
     */
    keepCurrentRootUrl = false;

    /**
     * A view mode.
     *
     * @type {string}
     */
    viewMode = '';

    /**
     * An available view mode list.
     *
     * @type {string[]|null}
     */
    viewModeList = null;

    /**
     * A default view mode.
     *
     * @type {string}
     */
    defaultViewMode = 'list';

    /**
     * @const
     */
    MODE_LIST = 'list';

    /**
     * @private
     */
    rowActionsView = 'views/record/row-actions/relationship';

    /**
     * A create button.
     *
     * @protected
     */
    createButton = true;

    /**
     * @protected
     */
    unlinkDisabled = false;

    /**
     * @protected
     */
    filtersDisabled = false;

    /**
     * @private
     * @type {string}
     */
    nameAttribute;

    /**
     * @inheritDoc
     */
    shortcutKeys = {
      /** @this ListRelatedView */
      'Control+Space': function (e) {
        this.handleShortcutKeyCtrlSpace(e);
      },
      /** @this ListRelatedView */
      'Control+Slash': function (e) {
        this.handleShortcutKeyCtrlSlash(e);
      },
      /** @this ListRelatedView */
      'Control+Comma': function (e) {
        this.handleShortcutKeyCtrlComma(e);
      },
      /** @this ListRelatedView */
      'Control+Period': function (e) {
        this.handleShortcutKeyCtrlPeriod(e);
      }
    };

    /**
     * @inheritDoc
     */
    setup() {
      this.link = this.options.link;
      if (!this.link) {
        console.error(`Link not passed.`);
        throw new Error();
      }
      if (!this.model) {
        console.error(`Model not passed.`);
        throw new Error();
      }
      if (!this.collection) {
        console.error(`Collection not passed.`);
        throw new Error();
      }
      this.rootUrl = this.options.rootUrl || this.options.params.rootUrl || `#${this.scope}`;
      this.nameAttribute = this.getMetadata().get(`clientDefs.${this.scope}.nameAttribute`) || 'name';

      /** @type {Record} */
      this.panelDefs = this.getMetadata().get(['clientDefs', this.scope, 'relationshipPanels', this.link]) || {};
      if (this.panelDefs.fullFormDisabled) {
        console.error(`Full-form disabled.`);
        throw new Error();
      }
      this.collection.maxSize = this.getConfig().get('recordsPerPage') || this.collection.maxSize;
      this.collectionUrl = this.collection.url;
      this.collectionMaxSize = this.collection.maxSize;
      if (this.panelDefs.primaryFilter) {
        this.collection.data.primaryFilter = this.panelDefs.primaryFilter;
      }
      this.foreignScope = this.collection.entityType;
      this.setupModes();
      this.setViewMode(this.viewMode);
      if (this.getMetadata().get(['clientDefs', this.foreignScope, 'searchPanelDisabled'])) {
        this.searchPanel = false;
      }
      if (this.getUser().isPortal()) {
        if (this.getMetadata().get(['clientDefs', this.foreignScope, 'searchPanelInPortalDisabled'])) {
          this.searchPanel = false;
        }
      }
      if (this.getMetadata().get(['clientDefs', this.foreignScope, 'createDisabled'])) {
        this.createButton = false;
      }

      // noinspection JSUnresolvedReference
      if (this.panelDefs.create === false || this.panelDefs.createDisabled || this.panelDefs.createAction) {
        this.createButton = false;
      }
      this.entityType = this.collection.entityType;
      this.headerView = this.options.headerView || this.headerView;
      this.recordView = this.options.recordView || this.recordView;
      this.searchView = this.options.searchView || this.searchView;
      this.setupHeader();
      this.defaultOrderBy = this.panelDefs.orderBy || this.collection.orderBy;
      this.defaultOrder = this.panelDefs.orderDirection || this.collection.order;
      if (this.panelDefs.orderBy && !this.panelDefs.orderDirection) {
        this.defaultOrder = 'asc';
      }
      this.collection.setOrder(this.defaultOrderBy, this.defaultOrder, true);
      if (this.searchPanel) {
        this.setupSearchManager();
      }
      this.setupSorting();
      if (this.searchPanel) {
        this.setupSearchPanel();
      }
      if (this.createButton) {
        this.setupCreateButton();
      }
      if (this.options.params && this.options.params.fromAdmin) {
        this.keepCurrentRootUrl = true;
      }
      this.wait(this.getHelper().processSetupHandlers(this, 'list'));
      this.addActionHandler('fullRefresh', () => this.actionFullRefresh());
      this.addActionHandler('removeRelated', () => this.actionRemoveRelated());
    }

    /**
     * Set up modes.
     */
    setupModes() {
      this.defaultViewMode = this.options.defaultViewMode || this.getMetadata().get(['clientDefs', this.foreignScope, 'listRelatedDefaultViewMode']) || this.defaultViewMode;
      this.viewMode = this.viewMode || this.defaultViewMode;
      const viewModeList = this.options.viewModeList || this.viewModeList || this.getMetadata().get(['clientDefs', this.foreignScope, 'listRelatedViewModeList']);
      this.viewModeList = viewModeList ? viewModeList : [this.MODE_LIST];
      if (this.viewModeList.length > 1) {
        let viewMode = null;
        const modeKey = 'listRelatedViewMode' + this.scope + this.link;
        if (this.getStorage().has('state', modeKey)) {
          const storedViewMode = this.getStorage().get('state', modeKey);
          if (storedViewMode && this.viewModeList.includes(storedViewMode)) {
            viewMode = storedViewMode;
          }
        }
        if (!viewMode) {
          viewMode = this.defaultViewMode;
        }
        this.viewMode = /** @type {string} */viewMode;
      }
    }

    /**
     * Set up a header.
     */
    setupHeader() {
      this.createView('header', this.headerView, {
        collection: this.collection,
        fullSelector: '#main > .page-header',
        scope: this.scope,
        isXsSingleRow: true
      });
    }

    /**
     * Set up a create button.
     */
    setupCreateButton() {
      this.menu.buttons.unshift({
        action: 'quickCreate',
        iconHtml: '<span class="fas fa-plus fa-sm"></span>',
        text: this.translate('Create ' + this.foreignScope, 'labels', this.foreignScope),
        style: 'default',
        acl: 'create',
        aclScope: this.foreignScope,
        title: 'Ctrl+Space'
      });
    }

    /**
     * Set up a search panel.
     *
     * @protected
     */
    setupSearchPanel() {
      this.createSearchView();
    }

    /**
     * Create a search view.
     *
     * @return {Promise<module:view>}
     * @protected
     */
    createSearchView() {
      let filterList = Espo.Utils.clone(this.getMetadata().get(['clientDefs', this.foreignScope, 'filterList']) || []);
      if (this.panelDefs.filterList) {
        this.panelDefs.filterList.forEach(item1 => {
          let isFound = false;
          const name1 = item1.name || item1;
          if (!name1 || name1 === 'all') {
            return;
          }
          filterList.forEach(item2 => {
            const name2 = item2.name || item2;
            if (name1 === name2) {
              isFound = true;
            }
          });
          if (!isFound) {
            filterList.push(item1);
          }
        });
      }
      if (this.filtersDisabled) {
        filterList = [];
      }
      return this.createView('search', this.searchView, {
        collection: this.collection,
        fullSelector: '#main > .search-container',
        searchManager: this.searchManager,
        scope: this.foreignScope,
        viewMode: this.viewMode,
        viewModeList: this.viewModeList,
        isWide: true,
        filterList: filterList
      }, view => {
        if (this.viewModeList.length > 1) {
          this.listenTo(view, 'change-view-mode', mode => this.switchViewMode(mode));
        }
      });
    }

    /**
     * Switch a view mode.
     *
     * @param {string} mode
     */
    switchViewMode(mode) {
      this.clearView('list');
      this.collection.isFetched = false;
      this.collection.reset();
      this.setViewMode(mode, true);
      this.loadList();
    }

    /**
     * Set a view mode.
     *
     * @param {string} mode A mode.
     * @param {boolean} [toStore=false] To preserve a mode being set.
     */
    setViewMode(mode, toStore) {
      this.viewMode = mode;
      this.collection.url = this.collectionUrl;
      this.collection.maxSize = this.collectionMaxSize;
      if (toStore) {
        const modeKey = 'listViewMode' + this.scope + this.link;
        this.getStorage().set('state', modeKey, mode);
      }
      if (this.searchView && this.getView('search')) {
        this.getSearchView().setViewMode(mode);
      }
      const methodName = 'setViewMode' + Espo.Utils.upperCaseFirst(this.viewMode);
      if (this[methodName]) {
        this[methodName]();
      }
    }

    /**
     * Set up a search manager.
     */
    setupSearchManager() {
      const collection = this.collection;
      const searchManager = new _searchManager.default(collection);
      if (this.panelDefs.primaryFilter) {
        searchManager.setPrimary(this.panelDefs.primaryFilter);
      }
      searchManager.scope = this.foreignScope;
      collection.where = searchManager.getWhere();
      this.searchManager = searchManager;
    }

    /**
     * Set up sorting.
     */
    setupSorting() {}

    /**
     * @protected
     * @return {module:views/record/search}
     */
    getSearchView() {
      return this.getView('search');
    }

    /**
     * @protected
     * @return {module:view}
     */
    getRecordView() {
      return this.getView('list');
    }

    /**
     * Get a record view name.
     *
     * @returns {string}
     */
    getRecordViewName() {
      if (this.viewMode === this.MODE_LIST) {
        return this.panelDefs.recordListView || this.getMetadata().get(['clientDefs', this.foreignScope, 'recordViews', this.MODE_LIST]) || this.recordView;
      }
      const propertyName = 'record' + Espo.Utils.upperCaseFirst(this.viewMode) + 'View';
      return this.getMetadata().get(['clientDefs', this.foreignScope, 'recordViews', this.viewMode]) || this[propertyName];
    }

    /**
     * @inheritDoc
     */
    afterRender() {
      Espo.Ui.notify(false);
      if (!this.hasView('list')) {
        this.loadList();
      }

      // noinspection JSUnresolvedReference
      this.$el.get(0).focus({
        preventScroll: true
      });
    }

    /**
     * Load a record list view.
     */
    loadList() {
      if ('isFetched' in this.collection && this.collection.isFetched) {
        this.createListRecordView(false);
        return;
      }
      Espo.Ui.notifyWait();
      this.createListRecordView(true);
    }

    /**
     * Prepare record view options. Options can be modified in an extended method.
     *
     * @param {Object} options Options
     */
    prepareRecordViewOptions(options) {}

    /**
     * Create a record list view.
     */
    createListRecordView() {
      /** @type {module:views/record/list~options | Bull.View~Options} */
      let o = {
        collection: this.collection,
        selector: '.list-container',
        scope: this.foreignScope,
        skipBuildRows: true,
        shortcutKeysEnabled: true
      };
      this.optionsToPass.forEach(option => {
        o[option] = this.options[option];
      });
      if (this.keepCurrentRootUrl) {
        o.keepCurrentRootUrl = true;
      }
      if (this.panelDefs.layout && typeof this.panelDefs.layout === 'string') {
        o.layoutName = this.panelDefs.layout;
      }
      o.rowActionsView = this.panelDefs.readOnly ? false : this.panelDefs.rowActionsView || this.rowActionsView;
      if (this.getConfig().get('listPagination') || this.getMetadata().get(['clientDefs', this.foreignScope, 'listPagination'])) {
        o.pagination = true;
      }
      const massUnlinkDisabled = this.panelDefs.massUnlinkDisabled || this.panelDefs.unlinkDisabled || this.unlinkDisabled;

      /** @type {module:views/record/list~options | Bull.View~Options} */
      o = {
        unlinkMassAction: !massUnlinkDisabled,
        skipBuildRows: true,
        buttonsDisabled: true,
        forceDisplayTopBar: true,
        rowActionsOptions: {
          unlinkDisabled: this.panelDefs.unlinkDisabled || this.unlinkDisabled,
          editDisabled: this.panelDefs.editDisabled,
          removeDisabled: this.panelDefs.removeDisabled
        },
        additionalRowActionList: this.panelDefs.rowActionList,
        ...o,
        settingsEnabled: true,
        removeDisabled: this.panelDefs.removeDisabled
      };
      if (this.getHelper().isXsScreen()) {
        o.type = 'listSmall';
      }
      this.prepareRecordViewOptions(o);
      const listViewName = this.getRecordViewName();
      this.createView('list', listViewName, o, view => {
        if (!this.hasParentView()) {
          view.undelegateEvents();
          return;
        }
        this.listenTo(view, 'after:paginate', () => window.scrollTo({
          top: 0
        }));
        this.listenTo(view, 'sort', () => window.scrollTo({
          top: 0
        }));
        this.listenToOnce(view, 'after:render', () => {
          if (!this.hasParentView()) {
            view.undelegateEvents();
            this.clearView('list');
          }
        });
        view.getSelectAttributeList(selectAttributeList => {
          if (this.options.mediator && this.options.mediator.abort) {
            return;
          }
          if (selectAttributeList) {
            this.collection.data.select = selectAttributeList.join(',');
          }
          Espo.Ui.notifyWait();
          this.collection.fetch({
            main: true
          }).then(() => Espo.Ui.notify(false));
        });
      });
    }

    /**
     * A quick-create action.
     *
     * @protected
     */
    async actionQuickCreate() {
      const link = this.link;
      const helper = new _createRelated.default(this);
      return helper.process(this.model, link, {
        afterSave: () => {
          this.collection.fetch();
        }
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * An `unlink-related` action.
     *
     * @protected
     */
    actionUnlinkRelated(data) {
      const id = data.id;
      this.confirm({
        message: this.translate('unlinkRecordConfirmation', 'messages'),
        confirmText: this.translate('Unlink')
      }, () => {
        Espo.Ui.notifyWait();
        Espo.Ajax.deleteRequest(this.collection.url, {
          id: id
        }).then(() => {
          Espo.Ui.success(this.translate('Unlinked'));
          this.collection.fetch();
          this.model.trigger('after:unrelate');
          this.model.trigger('after:unrelate:' + this.link);
        });
      });
    }

    /**
     * @inheritDoc
     */
    getHeader() {
      const name = this.model.attributes[this.nameAttribute] || this.model.id;
      const recordUrl = `#${this.scope}/view/${this.model.id}`;
      const title = document.createElement('a');
      title.href = recordUrl;
      title.classList.add('font-size-flexible', 'title');
      title.textContent = name;
      title.style.userSelect = 'none';
      if (this.model.attributes.deleted) {
        title.style.textDecoration = 'line-through';
      }
      const scopeLabel = this.getLanguage().translate(this.scope, 'scopeNamesPlural');
      let root = document.createElement('span');
      root.text = scopeLabel;
      root.style.userSelect = 'none';
      if (!this.rootLinkDisabled) {
        const a = document.createElement('a');
        a.href = this.rootUrl;
        a.classList.add('action');
        a.dataset.action = 'navigateToRoot';
        a.text = scopeLabel;
        root = document.createElement('span');
        root.style.userSelect = 'none';
        root.append(a);
      }
      const iconHtml = this.getHeaderIconHtml();
      if (iconHtml) {
        root.insertAdjacentHTML('afterbegin', iconHtml);
      }
      const link = document.createElement('span');
      link.textContent = this.translate(this.link, 'links', this.scope);
      link.title = this.translate('clickToRefresh', 'messages');
      link.dataset.action = 'fullRefresh';
      link.style.cursor = 'pointer';
      link.style.userSelect = 'none';
      return this.buildHeaderHtml([root, title, link]);
    }

    /**
     * @inheritDoc
     */
    updatePageTitle() {
      this.setPageTitle(this.getLanguage().translate(this.link, 'links', this.scope));
    }

    /**
     * Create attributes for an entity being created.
     *
     * @return {Object}
     */
    getCreateAttributes() {}

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlSpace(e) {
      if (!this.createButton) {
        return;
      }
      if (!this.getAcl().checkScope(this.foreignScope, 'create')) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      this.actionQuickCreate({
        focusForCreate: true
      });
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlSlash(e) {
      if (!this.searchPanel) {
        return;
      }
      const $search = this.$el.find('input.text-filter').first();
      if (!$search.length) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      $search.focus();
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlComma(e) {
      if (!this.getSearchView()) {
        return;
      }
      this.getSearchView().selectPreviousPreset();
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlPeriod(e) {
      if (!this.getSearchView()) {
        return;
      }
      this.getSearchView().selectNextPreset();
    }

    /**
     * @protected
     */
    async actionFullRefresh() {
      Espo.Ui.notifyWait();
      await this.collection.fetch();
      Espo.Ui.notify();
    }

    /**
     * @protected
     * @param {{id: string}} data
     * @return {Promise<void>}
     */
    async actionRemoveRelated(data) {
      const id = data.id;
      await this.confirm({
        message: this.translate('removeRecordConfirmation', 'messages'),
        confirmText: this.translate('Remove')
      });
      const model = this.collection.get(id);
      if (!model) {
        return;
      }
      Espo.Ui.notifyWait();
      await model.destroy();
      Espo.Ui.success(this.translate('Removed'));
      this.collection.fetch().then(() => {});
      this.model.trigger('after:unrelate');
      this.model.trigger(`after:unrelate:${this.link}`);
    }
  }
  var _default = _exports.default = ListRelatedView;
});

define("views/home", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class HomeView extends _view.default {
    template = 'home';
    setup() {
      const viewName = this.getMetadata().get(['clientDefs', 'Home', 'view']) || 'views/dashboard';
      this.createView('content', viewName, {
        selector: '> .home-content'
      });
    }
  }
  var _default = _exports.default = HomeView;
});

define("views/header", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/header */

  class HeaderView extends _view.default {
    template = 'header';
    data() {
      const data = {};
      if ('getHeader' in this.getParentMainView()) {
        data.header = this.getParentMainView().getHeader();
      }
      data.scope = this.scope || this.getParentMainView().scope;
      data.items = this.getItems();
      const dropdown = (data.items || {}).dropdown || [];
      data.hasVisibleDropdownItems = false;
      dropdown.forEach(item => {
        if (!item.hidden) {
          data.hasVisibleDropdownItems = true;
        }
      });
      data.noBreakWords = this.options.fontSizeFlexible;
      data.isXsSingleRow = this.options.isXsSingleRow;
      data.menuItemsHidden = this.menuItemsHidden;
      if ((data.items.buttons || []).length < 2) {
        data.isHeaderAdditionalSpace = true;
      }
      return data;
    }
    setup() {
      this.scope = this.options.scope;
      if (this.model) {
        this.listenTo(this.model, 'after:save', () => {
          if (this.isRendered()) {
            this.reRender();
          }
        });
      }
      this.wasRendered = false;
      if (this.options.fontSizeFlexible) {
        this.on('action-item-update', () => {
          if (this.isRendered()) {
            this.adjustFontSize();
          }
        });
      }
    }

    /**
     * Hide all menu items.
     *
     * @return {Promise}
     */
    hideAllMenuItems() {
      this.menuItemsHidden = true;
      return this.reRender();
    }

    /**
     * Show all menu items.
     *
     * @return {Promise}
     */
    showAllActionItems() {
      this.menuItemsHidden = false;
      return this.reRender();
    }
    afterRender() {
      if (this.options.fontSizeFlexible) {
        /** @private */
        this.$headerBreadcrumps = this.$el.find('.header-breadcrumbs');
        this._titleIsInitiated = false;
        this.adjustFontSize();
      }
      if (this.wasRendered) {
        this.getParentMainView().trigger('header-rendered');
      }
      this.wasRendered = true;
    }

    /**
     * @private
     * @param {number} step
     */
    adjustFontSize() {
      let step = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
      if (!step) {
        this.fontSizePercentage = 100;
      }
      const $container = this.$headerBreadcrumps;
      const containerWidth = $container.width();
      let childrenWidth = 0;
      $container.children().each((i, el) => {
        childrenWidth += $(el).outerWidth(true);
      });
      if (containerWidth >= childrenWidth) {
        return;
      }
      if (step > 7) {
        if (this._titleIsInitiated) {
          return;
        }
        this._titleIsInitiated = true;
        $container.addClass('overlapped');
        this.$el.find('.title').each((i, el) => {
          const $el = $(el);
          const text = $(el).text();
          $el.attr('title', text);
          let isInitialized = false;
          $el.on('touchstart', () => {
            if (!isInitialized) {
              $el.attr('title', '');
              isInitialized = true;
              Espo.Ui.popover($el, {
                content: text,
                noToggleInit: true
              }, this);
            }
            $el.popover('toggle');
          });
        });
        return;
      }
      this.fontSizePercentage -= 4;
      const $flexible = this.$el.find('.font-size-flexible');
      $flexible.css('font-size', this.fontSizePercentage + '%');
      $flexible.css('position', 'relative');
      if (step > 6) {
        $flexible.css('top', '-1px');
      } else if (step > 4) {
        $flexible.css('top', '-1px');
      }
      this.adjustFontSize(step + 1);
    }

    /**
     * @private
     * @returns {{
     *     buttons?: module:views/main~MenuItem[],
     *     dropdown?: module:views/main~MenuItem[],
     *     actions?: module:views/main~MenuItem[],
     * }}
     */
    getItems() {
      return this.getParentMainView().getMenu() || {};
    }

    /**
     * @return {module:views/main}
     */
    getParentMainView() {
      return /** @type module:views/main */this.getParentView();
    }
  }
  var _default = _exports.default = HeaderView;
});

define("views/deleted-detail", ["exports", "views/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class DeletedDetailView extends _detail.default {
    recordView = 'views/record/deleted-detail';
    menuDisabled = true;
    setup() {
      super.setup();
      if (this.model.get('deleted')) {
        this.menuDisabled = true;
      }
    }
    getRecordViewName() {
      return this.recordView;
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = DeletedDetailView;
});

define("views/dashlet", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/dashlet */

  /**
   * A dashlet container view.
   */
  class DashletView extends _view.default {
    /** @inheritDoc */
    template = 'dashlet';

    /**
     * A dashlet name.
     *
     * @type {string}
     */
    name;

    /**
     * A dashlet ID.
     *
     * @type {string}
     */
    id;

    /**
     * An options view name.
     *
     * @protected
     * @type {string|null}
     */
    optionsView = null;

    /** @inheritDoc */
    data() {
      const bodyView = this.getBodyView();
      return {
        name: this.name,
        id: this.id,
        title: this.getTitle(),
        actionList: bodyView ? bodyView.getActionItemDataList() : [],
        buttonList: bodyView ? bodyView.buttonList : [],
        noPadding: bodyView ? bodyView.noPadding : false,
        color: bodyView ? bodyView.getColor() : null
      };
    }

    /** @inheritDoc */
    events = {
      /** @this DashletView */
      'click .action': function (e) {
        const isHandled = Espo.Utils.handleAction(this, e.originalEvent, e.currentTarget);
        if (isHandled) {
          return;
        }
        this.getBodyView().handleAction(e.originalEvent, e.currentTarget);
      },
      /** @this DashletView */
      'mousedown .panel-heading .dropdown-menu': function (e) {
        // Prevent dragging.
        e.stopPropagation();
      },
      /** @this DashletView */
      'shown.bs.dropdown .panel-heading .btn-group': function (e) {
        this.controlDropdownShown($(e.currentTarget).parent());
      },
      /** @this DashletView */
      'hide.bs.dropdown .panel-heading .btn-group': function () {
        this.controlDropdownHide();
      }
    };
    controlDropdownShown($dropdownContainer) {
      const $panel = this.$el.children().first();
      const dropdownBottom = $dropdownContainer.find('.dropdown-menu').get(0).getBoundingClientRect().bottom;
      const panelBottom = $panel.get(0).getBoundingClientRect().bottom;
      if (dropdownBottom < panelBottom) {
        return;
      }
      $panel.addClass('has-dropdown-opened');
    }
    controlDropdownHide() {
      this.$el.children().first().removeClass('has-dropdown-opened');
    }

    /** @inheritDoc */
    setup() {
      this.name = this.options.name;
      this.id = this.options.id;
      this.on('resize', () => {
        const bodyView = this.getView('body');
        if (!bodyView) {
          return;
        }
        bodyView.trigger('resize');
      });
      const viewName = this.getMetadata().get(['dashlets', this.name, 'view']) || 'views/dashlets/' + Espo.Utils.camelCaseToHyphen(this.name);
      this.createView('body', viewName, {
        selector: '.dashlet-body',
        id: this.id,
        name: this.name,
        readOnly: this.options.readOnly,
        locked: this.options.locked
      });
    }

    /**
     * Refresh.
     */
    refresh() {
      this.getBodyView().actionRefresh();
    }
    actionRefresh() {
      this.refresh();
    }
    actionOptions() {
      const optionsView = this.getMetadata().get(['dashlets', this.name, 'options', 'view']) || this.optionsView || 'views/dashlets/options/base';
      Espo.Ui.notifyWait();
      this.createView('options', optionsView, {
        name: this.name,
        optionsData: this.getOptionsData(),
        fields: this.getBodyView().optionsFields
      }, view => {
        view.render();
        Espo.Ui.notify(false);
        this.listenToOnce(view, 'save', attributes => {
          const id = this.id;
          Espo.Ui.notify(this.translate('saving', 'messages'));
          this.getPreferences().once('sync', () => {
            this.getPreferences().trigger('update');
            Espo.Ui.notify(false);
            view.close();
            this.trigger('change');
          });
          const o = this.getPreferences().get('dashletsOptions') || {};
          o[id] = attributes;
          this.getPreferences().save({
            dashletsOptions: o
          }, {
            patch: true
          });
        });
      });
    }

    /**
     * Get options data.
     *
     * @returns {Object}
     */
    getOptionsData() {
      return this.getBodyView().optionsData;
    }

    /**
     * Get an option value.
     *
     * @param {string} key A option name.
     * @returns {*}
     */
    getOption(key) {
      return this.getBodyView().getOption(key);
    }

    /**
     * Get a dashlet title.
     *
     * @returns {string}
     */
    getTitle() {
      return this.getBodyView().getTitle();
    }

    /**
     * @return {module:views/dashlets/abstract/base}
     */
    getBodyView() {
      return this.getView('body');
    }

    // noinspection JSUnusedGlobalSymbols
    actionRemove() {
      this.confirm(this.translate('confirmation', 'messages'), () => {
        this.trigger('remove-dashlet');
        this.$el.remove();
        this.remove();
      });
    }
  }
  var _default = _exports.default = DashletView;
});

define("views/dashboard", ["exports", "view", "gridstack", "underscore"], function (_exports, _view, _gridstack, _underscore) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _gridstack = _interopRequireDefault(_gridstack);
  _underscore = _interopRequireDefault(_underscore);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/dashboard */

  class DashboardView extends _view.default {
    template = 'dashboard';
    dashboardLayout = null;
    currentTab = null;

    /**
     * @private
     * @type {number}
     */
    cellHeight;
    WIDTH_MULTIPLIER = 3;
    HEIGHT_MULTIPLIER = 4;

    /**
     * @private
     * @type {Object.<string, import('views/dashlet').default>|null}
     */
    preservedDashletViews = null;

    /**
     * @private
     * @type {Object.<string, HTMLElement>|null}
     */
    preservedDashletElements = null;
    events = {
      /** @this DashboardView */
      'click button[data-action="selectTab"]': function (e) {
        const tab = parseInt($(e.currentTarget).data('tab'));
        this.selectTab(tab);
      },
      /** @this DashboardView */
      'click .dashboard-buttons [data-action="addDashlet"]': function () {
        this.createView('addDashlet', 'views/modals/add-dashlet', {}, view => {
          view.render();
          this.listenToOnce(view, 'add', name => this.addDashlet(name));
        });
      },
      /** @this DashboardView */
      'click .dashboard-buttons [data-action="editTabs"]': function () {
        this.editTabs();
      }
    };
    data() {
      return {
        displayTitle: this.options.displayTitle,
        currentTab: this.currentTab,
        tabCount: this.dashboardLayout.length,
        dashboardLayout: this.dashboardLayout,
        layoutReadOnly: this.layoutReadOnly,
        hasAdd: !this.layoutReadOnly && !this.getPreferences().get('dashboardLocked')
      };
    }
    generateId() {
      return Math.floor(Math.random() * 10000001).toString();
    }
    setupCurrentTabLayout() {
      if (!this.dashboardLayout) {
        const defaultLayout = [{
          "name": "My Espo",
          "layout": []
        }];
        if (this.getConfig().get('forcedDashboardLayout')) {
          this.dashboardLayout = this.getConfig().get('forcedDashboardLayout') || [];
        } else if (this.getUser().get('portalId')) {
          this.dashboardLayout = this.getConfig().get('dashboardLayout') || [];
        } else {
          this.dashboardLayout = this.getPreferences().get('dashboardLayout') || defaultLayout;
        }
        if (this.dashboardLayout.length === 0 || Object.prototype.toString.call(this.dashboardLayout) !== '[object Array]') {
          this.dashboardLayout = defaultLayout;
        }
      }
      const dashboardLayout = this.dashboardLayout || [];
      if (dashboardLayout.length <= this.currentTab) {
        this.currentTab = 0;
      }
      let tabLayout = dashboardLayout[this.currentTab].layout || [];
      tabLayout = _gridstack.default.Utils.sort(tabLayout);
      this.currentTabLayout = tabLayout;
    }

    /**
     * @param {number} tab
     */
    storeCurrentTab(tab) {
      this.getStorage().set('state', 'dashboardTab', tab);
    }

    /**
     * @param {number} tab
     */
    selectTab(tab) {
      this.$el.find('.page-header button[data-action="selectTab"]').removeClass('active');
      this.$el.find(`.page-header button[data-action="selectTab"][data-tab="${tab}"]`).addClass('active');
      this.currentTab = tab;
      this.storeCurrentTab(tab);
      this.setupCurrentTabLayout();
      this.dashletIdList.forEach(id => this.clearView(`dashlet-${id}`));
      this.dashletIdList = [];
      this.reRender();
    }
    setup() {
      this.currentTab = this.getStorage().get('state', 'dashboardTab') || 0;
      this.setupCurrentTabLayout();
      this.cellHeight = this.getThemeManager().getParam('dashboardCellHeight');
      this.dashletIdList = [];
      this.screenWidthXs = this.getThemeManager().getParam('screenWidthXs');
      if (this.getUser().isPortal()) {
        this.layoutReadOnly = true;
        this.dashletsReadOnly = true;
      } else {
        const forbiddenPreferencesFieldList = this.getAcl().getScopeForbiddenFieldList('Preferences', 'edit');
        if (forbiddenPreferencesFieldList.includes('dashboardLayout')) {
          this.layoutReadOnly = true;
        }
        if (forbiddenPreferencesFieldList.includes('dashletsOptions')) {
          this.dashletsReadOnly = true;
        }
      }
      this.once('remove', () => {
        if (this.grid) {
          this.grid.destroy();
        }
        if (this.fallbackModeTimeout) {
          clearTimeout(this.fallbackModeTimeout);
        }
        $(window).off('resize.dashboard');
      });
    }
    afterRender() {
      this.$dashboard = this.$el.find('> .dashlets');
      if (window.innerWidth >= this.screenWidthXs) {
        this.initGridstack();
      } else {
        this.initFallbackMode();
      }
      $(window).off('resize.dashboard');
      $(window).on('resize.dashboard', this.onResize.bind(this));
    }
    onResize() {
      if (this.isFallbackMode() && window.innerWidth >= this.screenWidthXs) {
        this.initGridstack();
      } else if (!this.isFallbackMode() && window.innerWidth < this.screenWidthXs) {
        this.initFallbackMode();
      }
    }
    isFallbackMode() {
      return this.$dashboard.hasClass('fallback');
    }
    preserveDashletViews() {
      this.preservedDashletViews = {};
      this.preservedDashletElements = {};
      this.currentTabLayout.forEach(o => {
        const key = `dashlet-${o.id}`;
        const view = this.getView(key);
        this.unchainView(key);
        this.preservedDashletViews[o.id] = view;
        const element = view.element;
        this.preservedDashletElements[o.id] = element;
        const parent = element.parentNode;
        parent.removeChild(element);
      });
    }

    /**
     * @param {string} id
     */
    async addPreservedDashlet(id) {
      /** @type {import('view').default} */
      const view = this.preservedDashletViews[id];
      /** @type {HTMLElement} */
      const element = this.preservedDashletElements[id];
      if (!element || !view) {
        return;
      }
      const container = this.element.querySelector(`.dashlet-container[data-id="${id}"]`);
      if (!container) {
        return;
      }
      container.append(...element.childNodes);
      view.element = undefined;
      await this.setView(`dashlet-${id}`, view);
    }
    clearPreservedDashlets() {
      this.preservedDashletViews = null;
      this.preservedDashletElements = null;
    }
    hasPreservedDashlets() {
      return !!this.preservedDashletViews;
    }
    initFallbackMode() {
      if (this.grid) {
        this.grid.destroy(false);
        this.grid = null;
        this.preserveDashletViews();
      }
      this.$dashboard.empty();
      const $dashboard = this.$dashboard;
      $dashboard.addClass('fallback');
      this.currentTabLayout.forEach(o => {
        const $item = this.prepareFallbackItem(o);
        $dashboard.append($item);
      });
      this.currentTabLayout.forEach(o => {
        if (!o.id || !o.name) {
          return;
        }
        if (!this.getMetadata().get(`dashlets.${o.name}`)) {
          console.error(`Dashlet ${o.name} doesn't exist or not available.`);
          return;
        }
        if (this.hasPreservedDashlets()) {
          this.addPreservedDashlet(o.id);
          return;
        }
        this.createDashletView(o.id, o.name);
      });
      this.clearPreservedDashlets();
      if (this.fallbackModeTimeout) {
        clearTimeout(this.fallbackModeTimeout);
      }
      this.$dashboard.css('height', '');
      this.fallbackControlHeights();
    }
    fallbackControlHeights() {
      this.currentTabLayout.forEach(o => {
        const $container = this.$dashboard.find(`.dashlet-container[data-id="${o.id}"]`);
        const headerHeight = $container.find('.panel-heading').outerHeight();
        const $body = $container.find('.dashlet-body');
        const bodyEl = $body.get(0);
        if (!bodyEl) {
          return;
        }
        if (bodyEl.scrollHeight > bodyEl.offsetHeight) {
          const height = bodyEl.scrollHeight + headerHeight;
          $container.css('height', `${height}px`);
        }
      });
      this.fallbackModeTimeout = setTimeout(() => this.fallbackControlHeights(), 300);
    }
    initGridstack() {
      if (this.isFallbackMode()) {
        this.preserveDashletViews();
      }
      this.$dashboard.empty();
      const $gridstack = this.$gridstack = this.$dashboard;
      $gridstack.removeClass('fallback');
      if (this.fallbackModeTimeout) {
        clearTimeout(this.fallbackModeTimeout);
      }
      let disableDrag = false;
      let disableResize = false;
      if (this.getUser().isPortal() || this.getPreferences().get('dashboardLocked')) {
        disableDrag = true;
        disableResize = true;
      }
      const paramCellHeight = this.cellHeight;
      const paramCellMargin = this.getThemeManager().getParam('dashboardCellMargin');
      const factor = this.getThemeManager().getFontSizeFactor();
      const cellHeight = Math.ceil(factor * paramCellHeight * 1.14);
      const margin = Math.round(factor * paramCellMargin / 2);
      const grid = this.grid = _gridstack.default.init({
        cellHeight: cellHeight,
        margin: margin,
        column: 12,
        handle: '.panel-heading',
        disableDrag: disableDrag,
        disableResize: disableResize,
        disableOneColumnMode: true,
        draggable: {
          distance: 10
        },
        dragInOptions: {
          scroll: false
        },
        float: false,
        animate: false,
        scroll: false
      }, $gridstack.get(0));
      grid.removeAll();
      this.currentTabLayout.forEach(o => {
        const $item = this.prepareGridstackItem(o.id, o.name);
        if (!this.getMetadata().get(['dashlets', o.name])) {
          return;
        }
        grid.addWidget($item.get(0), {
          x: o.x * this.WIDTH_MULTIPLIER,
          y: o.y * this.HEIGHT_MULTIPLIER,
          w: o.width * this.WIDTH_MULTIPLIER,
          h: o.height * this.HEIGHT_MULTIPLIER
        });
      });
      $gridstack.find('.grid-stack-item').css('position', 'absolute');
      this.currentTabLayout.forEach(o => {
        if (!o.id || !o.name) {
          return;
        }
        if (!this.getMetadata().get(`dashlets.${o.name}`)) {
          console.error(`Dashlet ${o.name} doesn't exist or not available.`);
          return;
        }
        if (this.hasPreservedDashlets()) {
          this.addPreservedDashlet(o.id);
          return;
        }
        this.createDashletView(o.id, o.name);
      });
      this.clearPreservedDashlets();
      this.grid.on('change', () => {
        this.fetchLayout();
        this.saveLayout();
      });

      // noinspection SpellCheckingInspection
      this.grid.on('resizestop', e => {
        const id = $(e.target).data('id');
        const view = this.getView(`dashlet-${id}`);
        if (!view) {
          return;
        }
        view.trigger('resize');
      });
    }
    fetchLayout() {
      this.dashboardLayout[this.currentTab].layout = _underscore.default.map(this.$gridstack.find('.grid-stack-item'), el => {
        const $el = $(el);
        const x = $el.attr('gs-x');
        const y = $el.attr('gs-y');
        const h = $el.attr('gs-h');
        const w = $el.attr('gs-w');
        return {
          id: $el.data('id'),
          name: $el.data('name'),
          x: x / this.WIDTH_MULTIPLIER,
          y: y / this.HEIGHT_MULTIPLIER,
          width: w / this.WIDTH_MULTIPLIER,
          height: h / this.HEIGHT_MULTIPLIER
        };
      });
    }

    /**
     * @param {string} id
     * @param {string} name
     * @return {JQuery}
     */
    prepareGridstackItem(id, name) {
      const $item = $('<div>').addClass('grid-stack-item');
      const $container = $('<div class="grid-stack-item-content dashlet-container"></div>');
      $container.attr('data-id', id);
      $container.attr('data-name', name);
      $item.attr('data-id', id);
      $item.attr('data-name', name);
      $item.append($container);
      return $item;
    }

    /**
     * @param {Record} o
     * @return {JQuery}
     */
    prepareFallbackItem(o) {
      const $item = $('<div>');
      const $container = $('<div class="dashlet-container">');
      $container.attr('data-id', o.id);
      $container.attr('data-name', o.name);
      $container.attr('data-x', o.x);
      $container.attr('data-y', o.y);
      $container.attr('data-height', o.height);
      $container.attr('data-width', o.width);
      $container.css('height', o.height * this.cellHeight * this.HEIGHT_MULTIPLIER + 'px');
      $item.attr('data-id', o.id);
      $item.attr('data-name', o.name);
      $item.append($container);
      return $item;
    }

    /**
     * @param {Record} [attributes]
     */
    saveLayout(attributes) {
      if (this.layoutReadOnly) {
        return;
      }
      attributes = {
        ...(attributes || {}),
        ...{
          dashboardLayout: this.dashboardLayout
        }
      };
      this.getPreferences().save(attributes, {
        patch: true
      });
      this.getPreferences().trigger('update');
    }

    /**
     * @param {string} id
     */
    removeDashlet(id) {
      let revertToFallback = false;
      if (this.isFallbackMode()) {
        this.initGridstack();
        revertToFallback = true;
      }
      const $item = this.$gridstack.find('.grid-stack-item[data-id="' + id + '"]');

      // noinspection JSUnresolvedReference
      this.grid.removeWidget($item.get(0), true);
      const layout = this.dashboardLayout[this.currentTab].layout;
      layout.forEach((o, i) => {
        if (o.id === id) {
          layout.splice(i, 1);
        }
      });
      const o = {};
      o.dashletsOptions = this.getPreferences().get('dashletsOptions') || {};
      delete o.dashletsOptions[id];
      o.dashboardLayout = this.dashboardLayout;
      if (this.layoutReadOnly) {
        return;
      }
      this.getPreferences().save(o, {
        patch: true
      });
      this.getPreferences().trigger('update');
      const index = this.dashletIdList.indexOf(id);
      if (~index) {
        this.dashletIdList.splice(index, index);
      }
      this.clearView('dashlet-' + id);
      this.setupCurrentTabLayout();
      if (revertToFallback) {
        this.initFallbackMode();
      }
    }

    /**
     * @param {string} name
     */
    addDashlet(name) {
      let revertToFallback = false;
      if (this.isFallbackMode()) {
        this.initGridstack();
        revertToFallback = true;
      }
      const id = 'd' + Math.floor(Math.random() * 1000001).toString();
      const $item = this.prepareGridstackItem(id, name);
      this.grid.addWidget($item.get(0), {
        x: 0,
        y: 0,
        w: 2 * this.WIDTH_MULTIPLIER,
        h: 2 * this.HEIGHT_MULTIPLIER
      });
      this.createDashletView(id, name, name, view => {
        this.fetchLayout();
        this.saveLayout();
        this.setupCurrentTabLayout();
        if (view.getBodyView() && view.getBodyView().afterAdding) {
          view.getBodyView().afterAdding();
        }
        if (revertToFallback) {
          this.initFallbackMode();
        }
      });
    }

    /**
     * @param {string} id
     * @param {string} name
     * @param {string} [label]
     * @param {function(import('views/dashlet').default)} [callback]
     * @return {Promise<import('views/dashlet').default>}
     */
    createDashletView(id, name, label, callback) {
      const o = {
        id: id,
        name: name
      };
      if (label) {
        o.label = label;
      }
      return this.createView(`dashlet-${id}`, 'views/dashlet', {
        label: name,
        name: name,
        id: id,
        selector: `> .dashlets .dashlet-container[data-id="${id}"]`,
        readOnly: this.dashletsReadOnly,
        locked: this.getPreferences().get('dashboardLocked')
      }, view => {
        this.dashletIdList.push(id);
        view.render();
        this.listenToOnce(view, 'change', () => {
          this.clearView(id);
          this.createDashletView(id, name, label);
        });
        this.listenToOnce(view, 'remove-dashlet', () => {
          this.removeDashlet(id);
        });
        if (callback) {
          callback.call(this, view);
        }
      });
    }
    editTabs() {
      const dashboardLocked = this.getPreferences().get('dashboardLocked');
      this.createView('editTabs', 'views/modals/edit-dashboard', {
        dashboardLayout: this.dashboardLayout,
        dashboardLocked: dashboardLocked,
        fromDashboard: true
      }, view => {
        view.render();
        this.listenToOnce(view, 'after:save', data => {
          view.close();
          const dashboardLayout = [];
          data.dashboardTabList.forEach(name => {
            let layout = [];
            let id = null;
            this.dashboardLayout.forEach(d => {
              if (d.name === name) {
                layout = d.layout;
                id = d.id;
              }
            });
            if (name in data.renameMap) {
              name = data.renameMap[name];
            }
            const o = {
              name: name,
              layout: layout
            };
            if (id) {
              o.id = id;
            }
            dashboardLayout.push(o);
          });
          this.dashletIdList.forEach(item => {
            this.clearView(`dashlet-${item}`);
          });
          this.dashboardLayout = dashboardLayout;
          this.saveLayout({
            dashboardLocked: data.dashboardLocked
          });
          this.storeCurrentTab(0);
          this.currentTab = 0;
          this.setupCurrentTabLayout();
          this.reRender();
        });
      });
    }
  }
  var _default = _exports.default = DashboardView;
});

define("views/clear-cache", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ClearCacheView extends _view.default {
    template = 'clear-cache';
    el = '> body';
    events = {
      /** @this ClearCacheView */
      'click .action[data-action="clearLocalCache"]': function () {
        this.clearLocalCache();
      },
      /** @this ClearCacheView */
      'click .action[data-action="returnToApplication"]': function () {
        this.returnToApplication();
      }
    };
    data() {
      return {
        cacheIsEnabled: !!this.options.cache
      };
    }
    clearLocalCache() {
      this.options.cache.clear();
      this.$el.find('.action[data-action="clearLocalCache"]').remove();
      this.$el.find('.message-container').removeClass('hidden');
      this.$el.find('.message-container span').html(this.translate('Cache has been cleared'));
      this.$el.find('.action[data-action="returnToApplication"]').removeClass('hidden');
    }
    returnToApplication() {
      this.getRouter().navigate('', {
        trigger: true
      });
    }
  }
  var _default = _exports.default = ClearCacheView;
});

define("views/about", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class AboutView extends _view.default {
    template = 'about';
    data() {
      return {
        version: this.version,
        text: this.getHelper().transformMarkdownText(this.text)
      };
    }
    setup() {
      this.wait(Espo.Ajax.getRequest('App/about').then(data => {
        this.text = data.text;
        this.version = data.version;
      }));
    }
  }
  var _default = _exports.default = AboutView;
});

define("views/wysiwyg/modals/insert-link", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class WysiwygInsertLinkModal extends _modal.default {
    className = 'dialog dialog-record';
    template = 'wysiwyg/modals/insert-link';
    events = {
      /** @this {WysiwygInsertLinkModal} */
      'input [data-name="url"]': function () {
        this.controlInputs();
      },
      /** @this {WysiwygInsertLinkModal} */
      'paste [data-name="url"]': function () {
        this.controlInputs();
      }
    };
    shortcutKeys = {
      /** @this {WysiwygInsertLinkModal} */
      'Control+Enter': function () {
        if (this.hasAvailableActionItem('insert')) {
          this.actionInsert();
        }
      }
    };
    data() {
      return {
        labels: this.options.labels || {}
      };
    }
    setup() {
      const labels = this.options.labels || {};
      this.headerText = labels.insert;
      this.buttonList = [{
        name: 'insert',
        text: this.translate('Insert'),
        style: 'primary',
        disabled: true
      }];
      this.linkInfo = this.options.linkInfo || {};
      if (this.linkInfo.url) {
        this.enableButton('insert');
      }
    }
    afterRender() {
      this.$url = this.$el.find('[data-name="url"]');
      this.$text = this.$el.find('[data-name="text"]');
      this.$openInNewWindow = this.$el.find('[data-name="openInNewWindow"]');
      const linkInfo = this.linkInfo;
      this.$url.val(linkInfo.url || '');
      this.$text.val(linkInfo.text || '');
      if ('isNewWindow' in linkInfo) {
        this.$openInNewWindow.get(0).checked = !!linkInfo.isNewWindow;
      }
    }
    controlInputs() {
      const url = this.$url.val().trim();
      if (url) {
        this.enableButton('insert');
      } else {
        this.disableButton('insert');
      }
    }
    actionInsert() {
      const url = this.$url.val().trim();
      const text = this.$text.val().trim();
      const openInNewWindow = this.$openInNewWindow.get(0).checked;
      const data = {
        url: url,
        text: text || url,
        isNewWindow: openInNewWindow,
        range: this.linkInfo.range
      };
      this.trigger('insert', data);
      this.close();
    }
  }
  _exports.default = WysiwygInsertLinkModal;
});

define("views/wysiwyg/modals/insert-image", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class WysiwygInsertImageModal extends _modal.default {
    className = 'dialog dialog-record';
    template = 'wysiwyg/modals/insert-image';
    events = {
      /** @this {WysiwygInsertImageModal} */
      'click [data-action="insert"]': function () {
        this.actionInsert();
      },
      /** @this {WysiwygInsertImageModal} */
      'input [data-name="url"]': function () {
        this.controlInsertButton();
      },
      /** @this {WysiwygInsertImageModal} */
      'paste [data-name="url"]': function () {
        this.controlInsertButton();
      }
    };
    shortcutKeys = {
      /** @this {WysiwygInsertImageModal} */
      'Control+Enter': function () {
        if (!this.$el.find('[data-name="insert"]').hasClass('disabled')) {
          this.actionInsert();
        }
      }
    };
    data() {
      return {
        labels: this.options.labels || {}
      };
    }
    setup() {
      const labels = this.options.labels || {};
      this.headerText = labels.insert;
      this.buttonList = [];
    }
    afterRender() {
      const $files = this.$el.find('[data-name="files"]');
      $files.replaceWith($files.clone().on('change', e => {
        this.trigger('upload', e.target.files || e.target.value);
        this.close();
      }).val(''));
    }
    controlInsertButton() {
      const value = this.$el.find('[data-name="url"]').val().trim();
      const $button = this.$el.find('[data-name="insert"]');
      if (value) {
        $button.removeClass('disabled').removeAttr('disabled');
      } else {
        $button.addClass('disabled').attr('disabled', 'disabled');
      }
    }
    actionInsert() {
      const url = this.$el.find('[data-name="url"]').val().trim();
      this.trigger('insert', url);
      this.close();
    }
  }
  _exports.default = WysiwygInsertImageModal;
});

define("views/working-time-range/fields/users", ["exports", "views/fields/link-multiple"], function (_exports, _linkMultiple) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkMultiple = _interopRequireDefault(_linkMultiple);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _linkMultiple.default {
    getSelectPrimaryFilterName() {
      return 'active';
    }
  }
  _exports.default = _default;
});

define("views/working-time-range/fields/date-end", ["exports", "views/fields/date", "moment"], function (_exports, _date, _moment) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _date = _interopRequireDefault(_date);
  _moment = _interopRequireDefault(_moment);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _date.default {
    setup() {
      super.setup();
      this.validations.push(() => this.validateAfterOrSame());
    }
    validateAfterOrSame() {
      const field = 'dateStart';
      const value = this.model.get(this.name);
      const otherValue = this.model.get(field);
      if (value && otherValue) {
        if ((0, _moment.default)(value).unix() < (0, _moment.default)(otherValue).unix()) {
          const msg = this.translate('fieldShouldAfter', 'messages').replace('{field}', this.getLabelText()).replace('{otherField}', this.translate(field, 'fields', this.model.entityType));
          this.showValidationMessage(msg);
          return true;
        }
      }
      return false;
    }
  }
  _exports.default = _default;
});

define("views/working-time-calendar/fields/time-ranges", ["exports", "views/fields/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _base.default {
    // language=Handlebars
    listTemplateContent = `
        <div class="item-list">
        {{#each itemDataList}}
            <span class="item" data-key="{{key}}"
            >{{{var viewKey ../this}}}</span>{{#unless isLast}} &nbsp;&middot;&nbsp; {{/unless}}
        {{/each}}
        </div>
        {{#unless itemDataList.length}}
        <span class="none-value">{{translate 'None'}}</span>
        {{/unless}}
    `;

    // language=Handlebars
    detailTemplateContent = `
        <div class="item-list">
        {{#each itemDataList}}
            <div class="item" data-key="{{key}}">
                {{{var viewKey ../this}}}
            </div>
        {{/each}}
        </div>
        {{#unless itemDataList.length}}
        <span class="none-value">{{translate 'None'}}</span>
        {{/unless}}
    `;

    // language=Handlebars
    editTemplateContent = `
        <div class="item-list">
        {{#each itemDataList}}
            <div class="item" data-key="{{key}}">
                {{{var viewKey ../this}}}
            </div>
        {{/each}}
        </div>
        <div class="add-item-container margin-top-sm">
            <a
                role="button"
                tabindex="0"
                class="add-item"
                title="{{translate 'Add'}}"
            ><span class="fas fa-plus"></span></a>
        </div>
    `;

    // noinspection JSCheckFunctionSignatures
    data() {
      const data = super.data();
      data.itemDataList = this.itemKeyList.map((key, i) => {
        return {
          key: key.toString(),
          viewKey: this.composeViewKey(key),
          isLast: i === this.itemKeyList.length - 1
        };
      });

      // noinspection JSValidateTypes
      return data;
    }
    setup() {
      super.setup();
      this.validations = [() => this.validateRequired(), () => this.validateValid()];
      this.addHandler('click', '.add-item', () => this.addItem());
      this.addHandler('click', '.remove-item', (e, target) => {
        this.removeItem(parseInt(target.dataset.key));
      });
    }
    prepare() {
      this.initItems();
      return this.createItemViews();
    }
    initItems() {
      this.itemKeyList = [];
      this.getItemListFromModel().forEach((item, i) => {
        this.itemKeyList.push(i);
      });
    }

    /**
     * @returns {Promise}
     */
    createItemView(item, key) {
      const viewName = this.isEditMode() ? 'views/working-time-calendar/fields/time-ranges/item-edit' : 'views/working-time-calendar/fields/time-ranges/item-detail';
      return this.createView(this.composeViewKey(key), viewName, {
        value: item,
        selector: `.item[data-key="${key}"]`,
        key: key
      }).then(view => {
        this.listenTo(view, 'change', () => {
          this.trigger('change');
        });
        return view;
      });
    }

    /**
     * @returns {Promise}
     */
    createItemViews() {
      this.itemKeyList.forEach(key => {
        this.clearView(this.composeViewKey(key));
      });
      if (!this.model.has(this.name)) {
        return Promise.resolve();
      }
      const itemList = this.getItemListFromModel();
      const promiseList = [];
      this.itemKeyList.forEach((key, i) => {
        const item = itemList[i];
        const promise = this.createItemView(item, key);
        promiseList.push(promise);
      });
      return Promise.all(promiseList);
    }

    /**
     * @param {string} key
     * @return {import('./time-ranges/item-edit').default}
     */
    getItemView(key) {
      // noinspection JSValidateTypes
      return this.getView(this.composeViewKey(key));
    }
    composeViewKey(key) {
      return `item-${key}`;
    }

    /**
     * @return {[string|null, string|null][]}
     */
    getItemListFromModel() {
      return Espo.Utils.cloneDeep(this.model.get(this.name) || []);
    }
    addItem() {
      const itemList = this.getItemListFromModel();
      let value = null;
      if (itemList.length) {
        value = itemList[itemList.length - 1][1];
      }
      const item = [value, null];
      itemList.push(item);
      let key = this.itemKeyList[this.itemKeyList.length - 1];
      if (typeof key === 'undefined') {
        key = 0;
      }
      key++;
      this.itemKeyList.push(key);
      this.$el.find('.item-list').append($('<div>').addClass('item').attr('data-key', key));
      this.createItemView(item, key).then(view => view.render()).then(() => {
        this.trigger('change');
      });
    }
    removeItem(key) {
      const index = this.itemKeyList.indexOf(key);
      if (key === -1) {
        return;
      }
      const itemList = this.getItemListFromModel();
      this.itemKeyList.splice(index, 1);
      itemList.splice(index, 1);
      this.model.set(this.name, itemList, {
        ui: true
      });
      this.clearView(this.composeViewKey(key));
      this.$el.find(`.item[data-key="${key}"`).remove();
      this.trigger('change');
    }
    fetch() {
      const itemList = [];
      this.itemKeyList.forEach(key => {
        itemList.push(this.getItemView(key).fetch());
      });
      const data = {};
      data[this.name] = Espo.Utils.cloneDeep(itemList);
      if (data[this.name].length === 0) {
        data[this.name] = null;
      }
      return data;
    }
    validateRequired() {
      if (!this.isRequired()) {
        return false;
      }
      if (this.getItemListFromModel().length) {
        return false;
      }
      const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
      this.showValidationMessage(msg, '.add-item-container');
      return true;
    }
    validateValid() {
      if (!this.isRangesInvalid()) {
        return false;
      }
      const msg = this.translate('fieldInvalid', 'messages').replace('{field}', this.getLabelText());
      this.showValidationMessage(msg, '.add-item-container');
      return true;
    }
    isRangesInvalid() {
      const itemList = this.getItemListFromModel();
      for (let i = 0; i < itemList.length; i++) {
        const item = itemList[i];
        if (this.isRangeInvalid(item[0], item[1], true)) {
          return true;
        }
        if (i === 0) {
          continue;
        }
        const prevItem = itemList[i - 1];
        if (this.isRangeInvalid(prevItem[1], item[0])) {
          return true;
        }
        if (prevItem[1] === '00:00') {
          return true;
        }
      }
      return false;
    }

    /**
     * @param {string|null} from
     * @param {string|null} to
     * @param {boolean} [isRange]
     */
    isRangeInvalid(from, to) {
      let isRange = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
      if (from === null || to === null) {
        return true;
      }
      const fromNumber = parseFloat(from.replace(':', '.'));
      const toNumber = parseFloat(to.replace(':', '.'));
      if (isRange && fromNumber === toNumber && to !== '00:00') {
        return true;
      }
      if (isRange && to === '00:00' && fromNumber) {
        return false;
      }
      return fromNumber > toNumber;
    }
  }
  _exports.default = _default;
});

define("views/working-time-calendar/fields/time-ranges/item-edit", ["exports", "view", "moment", "ui/timepicker"], function (_exports, _view, _moment, _timepicker) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _moment = _interopRequireDefault(_moment);
  _timepicker = _interopRequireDefault(_timepicker);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class TimeRangeItemEdit extends _view.default {
    // language=Handlebars
    templateContent = `
        <div class="row">
            <div class="start-container col-xs-5">
                <input
                    class="form-control numeric-text"
                    type="text"
                    data-name="start"
                    value="{{start}}"
                    autocomplete="espo-start"
                    spellcheck="false"
                >
            </div>
            <div class="start-container col-xs-1 center-align">
                <span class="field-row-text-item">&nbsp;–&nbsp;</span>
            </div>
            <div class="end-container col-xs-5">
                <input
                    class="form-control numeric-text"
                    type="text"
                    data-name="end"
                    value="{{end}}"
                    autocomplete="espo-end"
                    spellcheck="false"
                >
            </div>
            <div class="col-xs-1 center-align">
                <a
                    role="button"
                    tabindex="0"
                    class="remove-item field-row-text-item"
                    data-key="{{key}}"
                    title="{{translate 'Remove'}}"
                ><span class="fas fa-times"></span></a>
            </div>
        </div>
    `;
    timeFormatMap = {
      'HH:mm': 'H:i',
      'hh:mm A': 'h:i A',
      'hh:mm a': 'h:i a',
      'hh:mmA': 'h:iA',
      'hh:mma': 'h:ia'
    };
    minuteStep = 30;

    /**
     * @private
     * @type {HTMLInputElement}
     */
    startElement;

    /**
     * @private
     * @type {HTMLInputElement}
     */
    endElement;

    /**
     * @private
     * @type {import('ui/timepicker').default}
     */
    startTimepicker;

    /**
     * @private
     * @type {import('ui/timepicker').default}
     */
    endTimepicker;
    data() {
      const data = {};
      data.start = this.convertTimeToDisplay(this.value[0]);
      data.end = this.convertTimeToDisplay(this.value[1]);
      data.key = this.key;
      return data;
    }
    setup() {
      this.value = this.options.value || [null, null];
      this.key = this.options.key;
      this.on('remove', () => this.destroyTimepickers());
    }
    convertTimeToDisplay(value) {
      if (!value) {
        return '';
      }
      const m = (0, _moment.default)(value, 'HH:mm');
      if (!m.isValid()) {
        return '';
      }
      return m.format(this.getDateTime().timeFormat);
    }

    /**
     * @param {string} value
     * @return {string|null}
     */
    convertTimeFromDisplay(value) {
      if (!value) {
        return null;
      }
      const m = (0, _moment.default)(value, this.getDateTime().timeFormat);
      if (!m.isValid()) {
        return null;
      }
      return m.format('HH:mm');
    }
    afterRender() {
      this.startElement = this.element.querySelector('[data-name="start"]');
      this.endElement = this.element.querySelector('[data-name="end"]');
      if (this.startElement) {
        this.startTimepicker = this.initTimepicker(this.startElement);
        this.endTimepicker = this.initTimepicker(this.endElement);
        this.setMinTime();
        this.startTimepicker.addChangeEventListener(() => this.setMinTime());
      }
    }
    setMinTime() {
      const value = this.startElement.value;
      const parsedValue = this.convertTimeFromDisplay(value);
      if (parsedValue !== '00:00') {
        this.endTimepicker.setMaxTime(this.convertTimeToDisplay('24:00'));
      } else {
        this.endTimepicker.setMaxTime(null);
      }
      if (!value) {
        this.endTimepicker.setMinTime(null);
        return;
      }
      const minValue = (0, _moment.default)(parsedValue, 'HH:mm').add(this.minuteStep, 'minute').format(this.getDateTime().timeFormat);
      this.endTimepicker.setMinTime(minValue);
    }

    /**
     * @private
     * @param {HTMLInputElement} element
     * @return {Timepicker}
     */
    initTimepicker(element) {
      const timepicker = new _timepicker.default(element, {
        step: this.minuteStep,
        timeFormat: this.timeFormatMap[this.getDateTime().timeFormat]
      });
      timepicker.addChangeEventListener(() => this.trigger('change'));
      element.setAttribute('autocomplete', 'espo-time-range-item');
      return timepicker;
    }
    destroyTimepickers() {
      if (this.startTimepicker) {
        this.startTimepicker.destroy();
      }
      if (this.endTimepicker) {
        this.endTimepicker.destroy();
      }
    }
    fetch() {
      return [this.convertTimeFromDisplay(this.startElement.value), this.convertTimeFromDisplay(this.endElement.value)];
    }
  }
  _exports.default = TimeRangeItemEdit;
});

define("views/working-time-calendar/fields/time-ranges/item-detail", ["exports", "view", "moment"], function (_exports, _view, _moment) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _moment = _interopRequireDefault(_moment);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _view.default {
    templateContent = `
        {{start}}
        &nbsp;–&nbsp;
        {{end}}
    `;
    data() {
      return {
        start: this.convertTimeToDisplay(this.value[0]),
        end: this.convertTimeToDisplay(this.value[1])
      };
    }
    setup() {
      this.value = this.options.value;
    }
    convertTimeToDisplay(value) {
      if (!value) {
        return '';
      }
      const m = (0, _moment.default)(value, 'HH:mm');
      if (!m.isValid()) {
        return '';
      }
      return m.format(this.getDateTime().timeFormat);
    }
  }
  _exports.default = _default;
});

define("views/webhook/record/list", ["exports", "views/record/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _list.default {
    massActionList = ['remove', 'massUpdate', 'export'];
  }
  _exports.default = _default;
});

define("views/webhook/fields/user", ["exports", "views/fields/link"], function (_exports, _link) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _link = _interopRequireDefault(_link);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _link.default {
    selectPrimaryFilterName = 'activeApi';
  }
  _exports.default = _default;
});

define("views/webhook/fields/event", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _varchar.default {
    setupOptions() {
      const itemList = [];
      const scopeList = this.getMetadata().getScopeObjectList().sort((v1, v2) => v1.localeCompare(v2));
      scopeList.forEach(scope => {
        itemList.push(`${scope}.create`);
        itemList.push(`${scope}.update`);
        itemList.push(`${scope}.delete`);
      });
      this.params.options = itemList;
    }
  }
  _exports.default = _default;
});

define("views/user-security/modals/two-factor-sms", ["exports", "views/modal", "model"], function (_exports, _modal, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class TwoFactorSmsModalView extends _modal.default {
    template = 'user-security/modals/two-factor-sms';
    className = 'dialog dialog-record';
    shortcutKeys = {
      'Control+Enter': 'apply'
    };
    setup() {
      this.addActionHandler('sendCode', () => this.actionSendCode());
      this.buttonList = [{
        name: 'apply',
        label: 'Apply',
        style: 'danger',
        hidden: true,
        onClick: () => this.actionApply()
      }, {
        name: 'cancel',
        label: 'Cancel'
      }];
      this.headerHtml = '&nbsp';
      const codeLength = this.getConfig().get('auth2FASmsCodeLength') || 7;
      const model = new _model.default();
      model.name = 'UserSecurity';
      model.set('phoneNumber', null);
      model.setDefs({
        fields: {
          'code': {
            type: 'varchar',
            required: true,
            maxLength: codeLength
          },
          'phoneNumber': {
            type: 'enum',
            required: true
          }
        }
      });
      this.internalModel = model;
      this.wait(Espo.Ajax.postRequest('UserSecurity/action/getTwoFactorUserSetupData', {
        id: this.model.id,
        password: this.model.get('password'),
        auth2FAMethod: this.model.get('auth2FAMethod'),
        reset: this.options.reset
      }).then(data => {
        this.phoneNumberList = data.phoneNumberList;
        this.createView('record', 'views/record/edit-for-modal', {
          scope: 'None',
          selector: '.record',
          model: model,
          detailLayout: [{
            rows: [[{
              name: 'phoneNumber',
              labelText: this.translate('phoneNumber', 'fields', 'User')
            }, false], [{
              name: 'code',
              labelText: this.translate('Code', 'labels', 'User')
            }, false]]
          }]
        }, view => {
          view.setFieldOptionList('phoneNumber', this.phoneNumberList);
          if (this.phoneNumberList.length) {
            model.set('phoneNumber', this.phoneNumberList[0]);
          }
          view.hideField('code');
        });
      }));
    }
    afterRender() {
      this.$sendCode = this.$el.find('[data-action="sendCode"]');
      this.$pInfo = this.$el.find('p.p-info');
      this.$pButton = this.$el.find('p.p-button');
      this.$pInfoAfter = this.$el.find('p.p-info-after');
    }
    actionSendCode() {
      this.$sendCode.attr('disabled', 'disabled').addClass('disabled');
      Espo.Ajax.postRequest('TwoFactorSms/action/sendCode', {
        id: this.model.id,
        phoneNumber: this.internalModel.get('phoneNumber')
      }).then(() => {
        this.showActionItem('apply');
        this.$pInfo.addClass('hidden');
        this.$pButton.addClass('hidden');
        this.$pInfoAfter.removeClass('hidden');
        this.getRecordView().setFieldReadOnly('phoneNumber');
        this.getRecordView().showField('code');
      }).catch(() => {
        this.$sendCode.removeAttr('disabled').removeClass('disabled');
      });
    }

    /**
     * @return {import('views/record/edit').default}
     */
    getRecordView() {
      return this.getView('record');
    }
    actionApply() {
      const data = this.getRecordView().processFetch();
      if (!data) {
        return;
      }
      this.model.set('code', data.code);
      this.hideActionItem('apply');
      this.hideActionItem('cancel');
      Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
      this.model.save().then(() => {
        Espo.Ui.notify(false);
        this.trigger('done');
      }).catch(() => {
        this.showActionItem('apply');
        this.showActionItem('cancel');
      });
    }
  }
  _exports.default = TwoFactorSmsModalView;
});

define("views/user-security/modals/two-factor-email", ["exports", "views/modal", "model"], function (_exports, _modal, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class TwoFactorEmailModalView extends _modal.default {
    template = 'user-security/modals/two-factor-email';
    className = 'dialog dialog-record';
    shortcutKeys = {
      'Control+Enter': 'apply'
    };
    setup() {
      this.addActionHandler('sendCode', () => this.actionSendCode());
      this.buttonList = [{
        name: 'apply',
        label: 'Apply',
        style: 'danger',
        hidden: true,
        onClick: () => this.actionApply()
      }, {
        name: 'cancel',
        label: 'Cancel'
      }];
      this.headerHtml = '&nbsp';
      const codeLength = this.getConfig().get('auth2FAEmailCodeLength') || 7;
      const model = new _model.default();
      model.entityType = model.name = 'UserSecurity';
      model.set('emailAddress', null);
      model.setDefs({
        fields: {
          'code': {
            type: 'varchar',
            required: true,
            maxLength: codeLength
          },
          'emailAddress': {
            type: 'enum',
            required: true
          }
        }
      });
      this.internalModel = model;
      this.wait(Espo.Ajax.postRequest('UserSecurity/action/getTwoFactorUserSetupData', {
        id: this.model.id,
        password: this.model.get('password'),
        auth2FAMethod: this.model.get('auth2FAMethod'),
        reset: this.options.reset
      }).then(data => {
        this.emailAddressList = data.emailAddressList;
        this.createView('record', 'views/record/edit-for-modal', {
          scope: 'None',
          selector: '.record',
          model: model,
          detailLayout: [{
            rows: [[{
              name: 'emailAddress',
              labelText: this.translate('emailAddress', 'fields', 'User')
            }, false], [{
              name: 'code',
              labelText: this.translate('Code', 'labels', 'User')
            }, false]]
          }]
        }, view => {
          view.setFieldOptionList('emailAddress', this.emailAddressList);
          if (this.emailAddressList.length) {
            model.set('emailAddress', this.emailAddressList[0]);
          }
          view.hideField('code');
        });
      }));
    }
    afterRender() {
      this.$sendCode = this.$el.find('[data-action="sendCode"]');
      this.$pInfo = this.$el.find('p.p-info');
      this.$pButton = this.$el.find('p.p-button');
      this.$pInfoAfter = this.$el.find('p.p-info-after');
    }
    actionSendCode() {
      this.$sendCode.attr('disabled', 'disabled').addClass('disabled');
      Espo.Ajax.postRequest('TwoFactorEmail/action/sendCode', {
        id: this.model.id,
        emailAddress: this.internalModel.get('emailAddress')
      }).then(() => {
        this.showActionItem('apply');
        this.$pInfo.addClass('hidden');
        this.$pButton.addClass('hidden');
        this.$pInfoAfter.removeClass('hidden');
        this.getRecordView().setFieldReadOnly('emailAddress');
        this.getRecordView().showField('code');
      }).catch(() => {
        this.$sendCode.removeAttr('disabled').removeClass('disabled');
      });
    }

    /**
     * @return {import('views/record/edit').default}
     */
    getRecordView() {
      return this.getView('record');
    }
    actionApply() {
      const data = this.getRecordView().processFetch();
      if (!data) {
        return;
      }
      this.model.set('code', data.code);
      this.hideActionItem('apply');
      this.hideActionItem('cancel');
      Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
      this.model.save().then(() => {
        Espo.Ui.notify(false);
        this.trigger('done');
      }).catch(() => {
        this.showActionItem('apply');
        this.showActionItem('cancel');
      });
    }
  }
  _exports.default = TwoFactorEmailModalView;
});

define("views/user-security/modals/totp", ["exports", "views/modal", "model"], function (_exports, _modal, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class TotpModalView extends _modal.default {
    template = 'user-security/modals/totp';
    className = 'dialog dialog-record';
    shortcutKeys = {
      'Control+Enter': 'apply'
    };
    setup() {
      this.buttonList = [{
        name: 'apply',
        label: 'Apply',
        style: 'danger',
        onClick: () => this.actionApply()
      }, {
        name: 'cancel',
        label: 'Cancel'
      }];
      this.headerHtml = '&nbsp';
      const model = new _model.default();
      model.name = 'UserSecurity';
      this.wait(Espo.Ajax.postRequest('UserSecurity/action/getTwoFactorUserSetupData', {
        id: this.model.id,
        password: this.model.get('password'),
        auth2FAMethod: this.model.get('auth2FAMethod'),
        reset: this.options.reset
      }).then(/** Record */data => {
        this.label = data.label;
        this.secret = data.auth2FATotpSecret;
        model.set('secret', data.auth2FATotpSecret);
      }));
      model.setDefs({
        fields: {
          code: {
            type: 'varchar',
            required: true,
            maxLength: 7
          },
          secret: {
            type: 'varchar',
            readOnly: true
          }
        }
      });
      this.createView('record', 'views/record/edit-for-modal', {
        scope: 'None',
        selector: '.record',
        model: model,
        detailLayout: [{
          rows: [[{
            name: 'secret',
            labelText: this.translate('Secret', 'labels', 'User')
          }, false], [{
            name: 'code',
            labelText: this.translate('Code', 'labels', 'User')
          }, false]]
        }]
      });
      Espo.loader.requirePromise('lib!qrcodejs').then(lib => {
        QRCode = lib;
      });
    }
    afterRender() {
      new QRCode(this.$el.find('.qrcode').get(0), {
        text: `otpauth://totp/${this.label}?secret=${this.secret}`,
        width: 256,
        height: 256,
        colorDark: '#000000',
        colorLight: '#ffffff',
        correctLevel: QRCode.CorrectLevel.H
      });
    }

    /**
     * @return {import('views/record/edit').default}
     */
    getRecordView() {
      return this.getView('record');
    }
    actionApply() {
      const data = this.getRecordView().processFetch();
      if (!data) {
        return;
      }
      this.model.set('code', data.code);
      this.hideActionItem('apply');
      this.hideActionItem('cancel');
      Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
      this.model.save().then(() => {
        Espo.Ui.notify(false);
        this.trigger('done');
      }).catch(() => {
        this.showActionItem('apply');
        this.showActionItem('cancel');
      });
    }
  }
  _exports.default = TotpModalView;
});

define("views/user/password-change-request", ["exports", "view", "model"], function (_exports, _view, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _view.default {
    template = 'user/password-change-request';
    data() {
      return {
        requestId: this.options.requestId,
        notFound: this.options.notFound,
        notFoundMessage: this.notFoundMessage
      };
    }
    setup() {
      this.addHandler('click', '#btn-submit', () => this.submit());
      const model = this.model = new _model.default();
      model.entityType = model.name = 'User';
      this.createView('password', 'views/user/fields/password', {
        model: model,
        mode: 'edit',
        selector: '.field[data-name="password"]',
        defs: {
          name: 'password',
          params: {
            required: true,
            maxLength: 255
          }
        },
        strengthParams: this.options.strengthParams
      });
      this.createView('passwordConfirm', 'views/fields/password', {
        model: model,
        mode: 'edit',
        selector: '.field[data-name="passwordConfirm"]',
        defs: {
          name: 'passwordConfirm',
          params: {
            required: true,
            maxLength: 255
          }
        }
      });
      this.createView('generatePassword', 'views/user/fields/generate-password', {
        model: model,
        mode: 'detail',
        readOnly: true,
        selector: '.field[data-name="generatePassword"]',
        defs: {
          name: 'generatePassword'
        },
        strengthParams: this.options.strengthParams
      });
      this.createView('passwordPreview', 'views/fields/base', {
        model: model,
        mode: 'detail',
        readOnly: true,
        selector: '.field[data-name="passwordPreview"]',
        defs: {
          name: 'passwordPreview'
        }
      });
      this.model.on('change:passwordPreview', () => this.reRender());
      const url = this.baseUrl = window.location.href.split('?')[0];
      this.notFoundMessage = this.translate('passwordChangeRequestNotFound', 'messages', 'User').replace('{url}', url);
    }

    /**
     * @param {string} name
     * @return {import('views/fields/base').default}
     */
    getFieldView(name) {
      return /** @type {import('views/fields/base').default} */this.getView(name);
    }
    submit() {
      this.getFieldView('password').fetchToModel();
      this.getFieldView('passwordConfirm').fetchToModel();
      const notValid = this.getFieldView('password').validate() || this.getFieldView('passwordConfirm').validate();
      const password = this.model.get('password');
      if (notValid) {
        return;
      }
      const $submit = this.$el.find('.btn-submit');
      $submit.addClass('disabled');
      Espo.Ajax.postRequest('User/changePasswordByRequest', {
        requestId: this.options.requestId,
        password: password
      }).then(data => {
        this.$el.find('.password-change').remove();
        const url = data.url || this.baseUrl;
        const msg = this.translate('passwordChangedByRequest', 'messages', 'User') + ' <a href="' + url + '">' + this.translate('Login', 'labels', 'User') + '</a>.';
        this.$el.find('.msg-box').removeClass('hidden').html('<span class="text-success">' + msg + '</span>');
      }).catch(() => {
        return $submit.removeClass('disabled');
      });
    }
  }
  _exports.default = _default;
});

define("views/user/list", ["exports", "views/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _list.default {
    storeViewAfterUpdate = false;
  }
  _exports.default = _default;
});

define("views/user/detail", ["exports", "views/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _detail.default {
    setup() {
      super.setup();
      if (this.getUser().isPortal()) {
        this.rootLinkDisabled = true;
      }
      if (this.model.id === this.getUser().id || this.getUser().isAdmin()) {
        if (this.getUserModel().isRegular() || this.getUserModel().isAdmin() || this.getUserModel().isPortal()) {
          this.addMenuItem('dropdown', {
            name: 'preferences',
            label: 'Preferences',
            action: 'preferences',
            link: `#Preferences/edit/${this.model.id}`,
            onClick: () => this.actionPreferences()
          });
        }
        if (this.getUserModel().isRegular() || this.getUserModel().isAdmin()) {
          if (this.getAcl().check('EmailAccountScope') && this.model.id === this.getUser().id || this.getUser().isAdmin()) {
            this.addMenuItem('dropdown', {
              name: 'emailAccounts',
              label: "Email Accounts",
              action: 'emailAccounts',
              link: `#EmailAccount/list/userId=${this.model.id}` + `&userName=${encodeURIComponent(this.model.attributes.name)}`,
              onClick: () => this.actionEmailAccounts()
            });
          }
          if (this.model.id === this.getUser().id && this.getAcl().checkScope('ExternalAccount')) {
            this.addMenuItem('buttons', {
              name: 'externalAccounts',
              label: 'External Accounts',
              action: 'externalAccounts',
              link: '#ExternalAccount',
              onClick: () => this.actionExternalAccounts()
            });
          }
        }
      }
      if (this.getAcl().checkScope('Calendar') && (this.getUserModel().isRegular() || this.getUserModel().isAdmin())) {
        const showActivities = this.getAcl().checkPermission('userCalendar', this.getUserModel());
        if (!showActivities && this.getAcl().getPermissionLevel('userCalendar') === 'team' && !this.model.has('teamsIds')) {
          this.listenToOnce(this.model, 'sync', () => {
            if (this.getAcl().checkPermission('userCalendar', this.getUserModel())) {
              this.showHeaderActionItem('calendar');
            }
          });
        }
        this.addMenuItem('buttons', {
          name: 'calendar',
          iconHtml: '<span class="far fa-calendar-alt"></span>',
          text: this.translate('Calendar', 'scopeNames'),
          link: `#Calendar/show/userId=${this.model.id}` + `&userName=${encodeURIComponent(this.model.attributes.name)}`,
          hidden: !showActivities
        });
      }
    }

    /**
     * @type {import('models/user').default}
     */
    getUserModel() {
      return /** @type {import('models/user').default} */this.model;
    }
    actionPreferences() {
      this.getRouter().navigate(`#Preferences/edit/${this.model.id}`, {
        trigger: true
      });
    }
    actionEmailAccounts() {
      this.getRouter().navigate(`#EmailAccount/list/userId=${this.model.id}&userName=${encodeURIComponent(this.model.attributes.name)}`, {
        trigger: true
      });
    }
    actionExternalAccounts() {
      this.getRouter().navigate('#ExternalAccount', {
        trigger: true
      });
    }
  }
  _exports.default = _default;
});

define("views/user/record/list", ["exports", "views/record/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserListRecordView extends _list.default {
    rowActionsView = 'views/user/record/row-actions/default';
    quickEditDisabled = true;
    massActionList = ['remove', 'massUpdate', 'export'];
    checkAllResultMassActionList = ['massUpdate', 'export'];
    setupMassActionItems() {
      super.setupMassActionItems();
      if (this.scope === 'ApiUser') {
        this.removeMassAction('massUpdate');
        this.removeMassAction('export');
        this.layoutName = 'listApi';
      }
      if (this.scope === 'PortalUser') {
        this.layoutName = 'listPortal';
      }
      if (!this.getUser().isAdmin()) {
        this.removeMassAction('massUpdate');
        this.removeMassAction('export');
      }
    }
    getModelScope(id) {
      const model = /** @type {import('models/user').default} */
      this.collection.get(id);
      if (model.isPortal()) {
        return 'PortalUser';
      }
      return this.scope;
    }
  }
  _exports.default = UserListRecordView;
});

define("views/user/record/edit", ["exports", "views/record/edit", "views/user/record/detail"], function (_exports, _edit, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserEditRecordView extends _edit.default {
    /**
     * @name model
     * @type module:models/user
     * @memberOf UserEditRecordView#
     */

    setup() {
      super.setup();
      this.setupNonAdminFieldsAccess();
      if (this.model.id === this.getUser().id) {
        this.listenTo(this.model, 'after:save', () => {
          this.getUser().set(this.model.getClonedAttributes());
        });
      }
      this.hideField('sendAccessInfo');
      this.passwordInfoMessage = this.getPasswordSendingMessage();
      if (!this.passwordInfoMessage) {
        this.hideField('passwordInfo');
      }
      let passwordChanged = false;
      this.listenToOnce(this.model, 'change:password', () => {
        passwordChanged = true;
        if (this.model.isNew()) {
          this.controlSendAccessInfoFieldForNew();
          return;
        }
        this.controlSendAccessInfoField();
      });
      this.listenTo(this.model, 'change', model => {
        if (!this.model.isNew() && !passwordChanged) {
          return;
        }
        if (!model.hasChanged('emailAddress') && !model.hasChanged('portalsIds') && !model.hasChanged('password')) {
          return;
        }
        if (this.model.isNew()) {
          this.controlSendAccessInfoFieldForNew();
          return;
        }
        this.controlSendAccessInfoField();
      });
      _detail.default.prototype.setupFieldAppearance.call(this);
      this.hideField('passwordPreview');
      this.listenTo(this.model, 'change:passwordPreview', (model, value) => {
        value = value || '';
        if (value.length) {
          this.showField('passwordPreview');
        } else {
          this.hideField('passwordPreview');
        }
      });
      this.listenTo(this.model, 'after:save', () => {
        this.model.unset('password', {
          silent: true
        });
        this.model.unset('passwordConfirm', {
          silent: true
        });
      });
    }
    controlSendAccessInfoField() {
      if (this.isPasswordSendable() && this.model.get('password')) {
        this.showField('sendAccessInfo');
        return;
      }
      this.hideField('sendAccessInfo');
      if (!this.model.has('sendAccessInfo')) {
        return;
      }
      this.model.set('sendAccessInfo', false);
    }
    controlSendAccessInfoFieldForNew() {
      const skipSettingTrue = this.recordHelper.getFieldStateParam('sendAccessInfo', 'hidden') === false;
      if (this.isPasswordSendable()) {
        this.showField('sendAccessInfo');
        if (!skipSettingTrue) {
          this.model.set('sendAccessInfo', true);
        }
        return;
      }
      this.hideField('sendAccessInfo');
      this.model.set('sendAccessInfo', false);
    }

    // noinspection SpellCheckingInspection
    isPasswordSendable() {
      if (this.model.isPortal()) {
        if (!(this.model.get('portalsIds') || []).length) {
          return false;
        }
      }
      if (!this.model.get('emailAddress')) {
        return false;
      }
      return true;
    }
    setupNonAdminFieldsAccess() {
      _detail.default.prototype.setupNonAdminFieldsAccess.call(this);
    }

    // noinspection JSUnusedGlobalSymbols
    controlFieldAppearance() {
      _detail.default.prototype.controlFieldAppearance.call(this);
    }
    getGridLayout(callback) {
      this.getHelper().layoutManager.get(this.model.entityType, this.options.layoutName || this.layoutName, simpleLayout => {
        /** @type {module:views/record/detail~panelDefs[]} */
        const layout = Espo.Utils.cloneDeep(simpleLayout);

        /** @type {module:views/record/detail~panelDefs[]} */
        const panels = [];
        panels.push({
          "label": "Teams and Access Control",
          "name": "accessControl",
          "rows": [[{
            "name": "type"
          }, {
            "name": "isActive"
          }], [{
            "name": "teams"
          }, {
            "name": "defaultTeam"
          }], [{
            "name": "roles"
          }, false]]
        });
        panels.push({
          "label": "Portal",
          "name": "portal",
          "rows": [[{
            "name": "portals"
          }, {
            "name": "accounts"
          }], [{
            "name": "portalRoles"
          }, {
            "name": "contact"
          }]]
        });
        if (this.getUser().isAdmin() && this.model.isPortal()) {
          panels.push({
            "label": "Misc",
            "name": "portalMisc",
            "rows": [[{
              "name": "dashboardTemplate"
            }, false]]
          });
        }
        if (this.model.isAdmin() || this.model.isRegular()) {
          panels.push({
            "label": "Misc",
            "name": "misc",
            "rows": [[{
              "name": "workingTimeCalendar"
            }, {
              "name": "layoutSet"
            }]]
          });
        }
        if (this.type === this.TYPE_EDIT && this.getUser().isAdmin() && !this.model.isApi()) {
          panels.push({
            label: 'Password',
            rows: [[{
              name: 'password',
              type: 'password',
              params: {
                required: false,
                readyToChange: true
              },
              view: 'views/user/fields/password'
            }, {
              name: 'generatePassword',
              view: 'views/user/fields/generate-password',
              customLabel: ''
            }], [{
              name: 'passwordConfirm',
              type: 'password',
              params: {
                required: false,
                readyToChange: true
              }
            }, {
              name: 'passwordPreview',
              view: 'views/fields/base',
              params: {
                readOnly: true
              }
            }], [{
              name: 'sendAccessInfo'
            }, {
              name: 'passwordInfo',
              type: 'text',
              customLabel: '',
              customCode: this.passwordInfoMessage
            }]]
          });
        }
        if (this.getUser().isAdmin() && this.model.isApi()) {
          panels.push({
            "name": "auth",
            "rows": [[{
              "name": "authMethod"
            }, false]]
          });
        }
        let hasTab = false;
        for (const [i, panel] of layout.entries()) {
          if (panel.tabBreak && i > 0) {
            layout.splice(i, 0, ...panels);
            hasTab = true;
            break;
          }
        }
        if (!hasTab) {
          layout.push(...panels);
        }
        this.detailLayout = layout;
        const gridLayout = {
          type: 'record',
          layout: this.convertDetailLayout(layout)
        };
        callback(gridLayout);
      });
    }
    getPasswordSendingMessage() {
      if (this.getConfig().get('outboundEmailFromAddress')) {
        return '';
      }
      let msg = this.translate('setupSmtpBefore', 'messages', 'User').replace('{url}', '#Admin/outboundEmails');
      msg = this.getHelper().transformMarkdownInlineText(msg);
      return msg;
    }
    fetch() {
      const data = super.fetch();
      if (!this.isNew) {
        if ('password' in data && (data['password'] === '' || data['password'] == null)) {
          delete data['password'];
          delete data['passwordConfirm'];
          this.model.unset('password');
          this.model.unset('passwordConfirm');
        }
      }
      return data;
    }
    exit(after) {
      if (after === 'create' || after === 'save') {
        this.model.unset('sendAccessInfo', {
          silent: true
        });
      }
      super.exit(after);
    }

    // noinspection JSUnusedGlobalSymbols
    errorHandlerUserNameExists() {
      Espo.Ui.error(this.translate('userNameExists', 'messages', 'User'));
    }
  }
  var _default = _exports.default = UserEditRecordView;
});

define("views/user/record/edit-quick", ["exports", "views/record/edit", "views/user/record/detail"], function (_exports, _edit, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _edit.default {
    sideView = 'views/record/edit-side';
    setup() {
      super.setup();
      _detail.default.prototype.setupNonAdminFieldsAccess.call(this);
      _detail.default.prototype.setupFieldAppearance.call(this);
    }

    // noinspection JSUnusedGlobalSymbols
    controlFieldAppearance() {
      _detail.default.prototype.controlFieldAppearance.call(this);
    }
  }
  _exports.default = _default;
});

define("views/user/record/detail-quick", ["exports", "views/record/detail", "views/user/record/detail"], function (_exports, _detail, _detail2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  _detail2 = _interopRequireDefault(_detail2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _detail.default {
    sideView = 'views/user/record/detail-quick-side';
    bottomView = null;
    setup() {
      super.setup();
      _detail2.default.prototype.setupNonAdminFieldsAccess.call(this);
      _detail2.default.prototype.setupFieldAppearance.call(this);
    }

    // noinspection JSUnusedGlobalSymbols
    controlFieldAppearance() {
      _detail2.default.prototype.controlFieldAppearance.call(this);
    }
  }
  _exports.default = _default;
});

define("views/user/record/detail-quick-side", ["exports", "views/record/detail-side", "views/user/record/detail-side"], function (_exports, _detailSide, _detailSide2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detailSide = _interopRequireDefault(_detailSide);
  _detailSide2 = _interopRequireDefault(_detailSide2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class _default extends _detailSide.default {
    setupPanels() {
      _detailSide2.default.prototype.setupPanels.call(this);
    }
  }
  _exports.default = _default;
});

define("views/user/record/detail-bottom", ["exports", "views/record/detail-bottom"], function (_exports, _detailBottom) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detailBottom = _interopRequireDefault(_detailBottom);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserDetailBottomRecordView extends _detailBottom.default {
    setupPanels() {
      super.setupPanels();
      const userModel = /** @type {import('models/user').default} */this.model;
      const streamAllowed = this.getAcl().checkPermission('userCalendar', userModel);
      if (!streamAllowed && this.getAcl().getPermissionLevel('userCalendar') === 'team' && !this.model.has('teamsIds')) {
        this.listenToOnce(this.model, 'sync', () => {
          if (this.getAcl().checkPermission('userCalendar', userModel)) {
            this.onPanelsReady(() => {
              this.showPanel('stream', 'acl');
            });
          }
        });
      }
      this.panelList.push({
        "name": "stream",
        "label": "Stream",
        "view": "views/user/record/panels/stream",
        "sticked": false,
        "hidden": !streamAllowed
      });
      if (!streamAllowed) {
        this.recordHelper.setPanelStateParam('stream', 'hiddenAclLocked', true);
      }
    }
  }
  var _default = _exports.default = UserDetailBottomRecordView;
});

define("views/user/record/row-actions/relationship-followers", ["exports", "views/record/row-actions/relationship"], function (_exports, _relationship) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _relationship = _interopRequireDefault(_relationship);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class UserRelationshipFollowersRowActionsView extends _relationship.default {
    getActionList() {
      const list = [];
      const model = /** @type {import('models/user').default} */this.model;
      list.push({
        action: 'quickView',
        label: 'View',
        data: {
          id: this.model.id
        },
        link: `#${this.model.entityType}/view/${this.model.id}`
      });
      if (this.getUser().isAdmin() || this.getAcl().getPermissionLevel('followerManagementPermission') !== 'no' || model.isPortal() && this.getAcl().getPermissionLevel('portalPermission') === 'yes' || this.model.id === this.getUser().id) {
        list.push({
          action: 'unlinkRelated',
          label: 'Unlink',
          data: {
            id: this.model.id
          }
        });
      }
      return list;
    }
  }
  _exports.default = UserRelationshipFollowersRowActionsView;
});

define("views/user/record/row-actions/default", ["exports", "views/record/row-actions/default"], function (_exports, _default) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _default = _interopRequireDefault(_default);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserDefaultRowActionsView extends _default.default {
    getActionList() {
      let scope = 'User';
      const model = /** @type {import('models/user').default} */this.model;
      if (model.isPortal()) {
        scope = 'PortalUser';
      } else if (model.isApi()) {
        scope = 'ApiUser';
      }
      const list = [{
        action: 'quickView',
        label: 'View',
        data: {
          id: this.model.id,
          scope: scope
        },
        link: `#${scope}/view/${this.model.id}`
      }];
      if (this.options.acl.edit) {
        list.push({
          action: 'quickEdit',
          label: 'Edit',
          data: {
            id: this.model.id,
            scope: scope
          },
          link: `#${scope}/edit/${this.model.id}`
        });
      }
      this.getAdditionalActionList().forEach(item => list.push(item));
      return list;
    }
  }
  _exports.default = UserDefaultRowActionsView;
});

define("views/user/record/panels/stream", ["exports", "views/stream/panel"], function (_exports, _panel) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _panel = _interopRequireDefault(_panel);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _panel.default {
    setup() {
      const model = /** @type import('models/user').default */this.model;
      if (this.model.id === this.getUser().id) {
        this.placeholderText = this.translate('writeMessageToSelf', 'messages');
      } else {
        this.placeholderText = this.translate('writeMessageToUser', 'messages').replace('{user}', this.model.get('name'));
      }
      super.setup();
      this.setupPermission(model);
    }

    /**
     * @private
     * @param {import('models/user').default} model
     */
    setupPermission(model) {
      const permission = this.getAcl().checkPermission('message', model);
      if (permission) {
        return;
      }
      this.postDisabled = true;
      if (permission !== null) {
        return;
      }
      this.listenToOnce(this.model, 'sync', async () => {
        if (!this.getAcl().checkPermission('message', model)) {
          return;
        }
        this.postDisabled = false;
        await this.whenRendered();
        const container = this.element.querySelector('.post-container');
        if (container) {
          container.classList.remove('hidden');
        }
      });
    }
    prepareNoteForPost(model) {
      const userIdList = [this.model.id];
      const userNames = {};
      userNames[userIdList] = this.model.get('name');
      model.set('usersIds', userIdList);
      model.set('usersNames', userNames);
      model.set('targetType', 'users');
    }
  }
  _exports.default = _default;
});

define("views/user/record/panels/default-side", ["exports", "views/record/panels/default-side"], function (_exports, _defaultSide) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _defaultSide = _interopRequireDefault(_defaultSide);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _defaultSide.default {
    complexCreatedDisabled = true;
    complexModifiedDisabled = true;
  }
  _exports.default = _default;
});

define("views/user/modals/select-followers", ["exports", "views/modals/select-records"], function (_exports, _selectRecords) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _selectRecords = _interopRequireDefault(_selectRecords);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _selectRecords.default {
    setup() {
      this.filterList = ['active'];
      if (this.getAcl().getPermissionLevel('portalPermission')) {
        this.filterList.push('activePortal');
      }
      super.setup();
    }
  }
  _exports.default = _default;
});

define("views/user/modals/security", ["exports", "views/modal", "model"], function (_exports, _modal, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserSecurityModalView extends _modal.default {
    templateContent = '<div class="record no-side-margin">{{{record}}}</div>';
    className = 'dialog dialog-record';
    shortcutKeys = {
      'Control+Enter': 'apply'
    };
    setup() {
      this.buttonList = [{
        name: 'apply',
        label: 'Apply',
        hidden: true,
        style: 'danger',
        onClick: () => this.apply()
      }, {
        name: 'cancel',
        label: 'Close'
      }];
      this.dropdownItemList = [{
        name: 'reset',
        text: this.translate('Reset 2FA'),
        hidden: true,
        onClick: () => this.reset()
      }];
      this.userModel = this.options.userModel;
      this.$header = $('<span>').append($('<span>').text(this.translate('Security')), ' <span class="chevron-right"></span> ', $('<span>').text(this.userModel.get('userName')));
      const model = this.model = new _model.default();
      model.name = 'UserSecurity';
      model.id = this.userModel.id;
      model.url = 'UserSecurity/' + this.userModel.id;
      const auth2FAMethodList = this.getConfig().get('auth2FAMethodList') || [];
      model.setDefs({
        fields: {
          'auth2FA': {
            type: 'bool',
            labelText: this.translate('auth2FAEnable', 'fields', 'User')
          },
          'auth2FAMethod': {
            type: 'enum',
            options: auth2FAMethodList,
            translation: 'Settings.options.auth2FAMethodList'
          }
        }
      });
      this.wait(model.fetch().then(() => {
        this.initialAttributes = Espo.Utils.cloneDeep(model.attributes);
        if (model.get('auth2FA')) {
          this.showActionItem('reset');
        }
        this.createView('record', 'views/record/edit-for-modal', {
          scope: 'None',
          selector: '.record',
          model: this.model,
          detailLayout: [{
            rows: [[{
              name: 'auth2FA',
              labelText: this.translate('auth2FAEnable', 'fields', 'User')
            }, {
              name: 'auth2FAMethod',
              labelText: this.translate('auth2FAMethod', 'fields', 'User')
            }]]
          }]
        }, view => {
          this.controlFieldsVisibility(view);
          this.listenTo(this.model, 'change:auth2FA', () => {
            this.controlFieldsVisibility(view);
          });
        });
      }));
      this.listenTo(this.model, 'change', () => {
        if (this.initialAttributes) {
          this.isChanged() ? this.showActionItem('apply') : this.hideActionItem('apply');
        }
      });
    }
    controlFieldsVisibility(view) {
      if (this.model.get('auth2FA')) {
        view.showField('auth2FAMethod');
        view.setFieldRequired('auth2FAMethod');
      } else {
        view.hideField('auth2FAMethod');
        view.setFieldNotRequired('auth2FAMethod');
      }
    }
    isChanged() {
      return this.initialAttributes.auth2FA !== this.model.get('auth2FA') || this.initialAttributes.auth2FAMethod !== this.model.get('auth2FAMethod');
    }
    reset() {
      this.confirm(this.translate('security2FaResetConfirmation', 'messages', 'User'), () => {
        this.apply(true);
      });
    }

    /**
     * @return {module:views/record/edit}
     */
    getRecordView() {
      return this.getView('record');
    }
    apply(reset) {
      const data = this.getRecordView().processFetch();
      if (!data) {
        return;
      }
      this.hideActionItem('apply');
      new Promise(resolve => {
        this.createView('dialog', 'views/user/modals/password', {}, passwordView => {
          passwordView.render();
          this.listenToOnce(passwordView, 'cancel', () => this.showActionItem('apply'));
          this.listenToOnce(passwordView, 'proceed', data => {
            this.model.set('password', data.password);
            passwordView.close();
            resolve();
          });
        });
      }).then(() => this.processApply(reset));
    }
    processApply(reset) {
      if (this.model.get('auth2FA')) {
        const auth2FAMethod = this.model.get('auth2FAMethod');
        const view = this.getMetadata().get(['app', 'authentication2FAMethods', auth2FAMethod, 'userApplyView']);
        if (view) {
          Espo.Ui.notifyWait();
          this.createView('dialog', view, {
            model: this.model,
            reset: reset
          }, view => {
            Espo.Ui.notify(false);
            view.render();
            this.listenToOnce(view, 'cancel', () => {
              this.close();
            });
            this.listenToOnce(view, 'apply', () => {
              view.close();
              this.processSave();
            });
            this.listenToOnce(view, 'done', () => {
              Espo.Ui.success(this.translate('Done'));
              this.trigger('done');
              view.close();
              this.close();
            });
          });
          return;
        }
        if (reset) {
          this.model.set('auth2FA', false);
        }
        this.processSave();
        return;
      }
      this.processSave();
    }
    processSave() {
      this.hideActionItem('apply');
      this.model.save().then(() => {
        this.close();
        Espo.Ui.success(this.translate('Done'));
      }).catch(() => this.showActionItem('apply'));
    }
  }
  var _default = _exports.default = UserSecurityModalView;
});

define("views/user/modals/password", ["exports", "views/modal", "model"], function (_exports, _modal, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _modal.default {
    templateContent = '<div class="record no-side-margin">{{{record}}}</div>';
    className = 'dialog dialog-record';
    shortcutKeys = {
      'Control+Enter': 'apply'
    };
    setup() {
      this.buttonList = [{
        name: 'apply',
        label: 'Apply',
        style: 'danger',
        onClick: () => this.actionApply()
      }, {
        name: 'cancel',
        label: 'Cancel'
      }];
      this.headerHtml = '&nbsp';
      this.userModel = this.options.userModel;
      const model = this.model = new _model.default();
      model.name = 'UserSecurity';
      model.setDefs({
        fields: {
          'password': {
            type: 'password',
            required: true
          }
        }
      });
      this.createView('record', 'views/record/edit-for-modal', {
        scope: 'None',
        selector: '.record',
        model: this.model,
        detailLayout: [{
          rows: [[{
            name: 'password',
            labelText: this.translate('yourPassword', 'fields', 'User'),
            params: {
              readyToChange: true
            }
          }, false]]
        }]
      });
    }

    /**
     * @return {import('views/record/edit').default}
     */
    getRecordView() {
      return this.getView('record');
    }
    actionApply() {
      const data = this.getRecordView().processFetch();
      if (!data) {
        return;
      }
      this.trigger('proceed', data);
    }
  }
  _exports.default = _default;
});

define("views/user/modals/mass-update", ["exports", "views/modals/mass-update"], function (_exports, _massUpdate) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _massUpdate = _interopRequireDefault(_massUpdate);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _massUpdate.default {
    setup() {
      if (this.options.scope === 'ApiUser') {
        this.layoutName = 'massUpdateApi';
      } else if (this.options.scope === 'PortalUser') {
        this.layoutName = 'massUpdatePortal';
      }
      super.setup();
    }
  }
  _exports.default = _default;
});

define("views/user/modals/login-as", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _modal.default {
    backdrop = true;

    // language=Handlebars
    templateContent = `
        <div class="well">
            {{translate 'loginAs' category='messages' scope='User'}}
        </div>
        <a href="{{viewObject.url}}" class="text-large">{{translate 'Login Link' scope='User'}}</a>
    `;
    setup() {
      this.$header = $('<span>').append($('<span>').text(this.model.get('name')), ' ', $('<span>').addClass('chevron-right'), ' ', $('<span>').text(this.translate('Login')));
      this.url = `?entryPoint=loginAs&anotherUser=${this.options.anotherUser}&username=${this.options.username}`;
    }
  }
  _exports.default = _default;
});

define("views/user/modals/detail", ["exports", "views/modals/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _detail.default {
    editDisabled = true;
    getScope() {
      const model = /** @type {import('models/user').default} */this.model;
      if (model.isPortal()) {
        return 'PortalUser';
      }
      return 'User';
    }
  }
  _exports.default = _default;
});

define("views/user/modals/access", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _modal.default {
    cssName = 'user-access';
    multiple = false;
    template = 'user/modals/access';
    backdrop = true;
    styleMap = {
      yes: 'success',
      all: 'success',
      account: 'info',
      contact: 'info',
      team: 'info',
      own: 'warning',
      no: 'danger',
      enabled: 'success',
      disabled: 'danger',
      'not-set': 'default'
    };
    data() {
      return {
        valuePermissionDataList: this.getValuePermissionList(),
        levelListTranslation: this.getLanguage().get('Role', 'options', 'levelList') || {},
        styleMap: this.styleMap
      };
    }
    getValuePermissionList() {
      const list = this.getMetadata().get(['app', 'acl', 'valuePermissionList'], []);
      const dataList = [];
      list.forEach(item => {
        const o = {};
        o.name = item;
        o.value = this.options.aclData[item];
        dataList.push(o);
      });
      return dataList;
    }
    setup() {
      this.buttonList = [{
        name: 'cancel',
        label: 'Cancel'
      }];
      const fieldTable = Espo.Utils.cloneDeep(this.options.aclData.fieldTable || {});
      for (const scope in fieldTable) {
        const scopeData = fieldTable[scope] || {};
        for (const field in scopeData) {
          if (this.getMetadata().get(['app', 'acl', 'mandatory', 'scopeFieldLevel', scope, field]) !== null) {
            delete scopeData[field];
          }
          if (scopeData[field] && this.getMetadata().get(['entityDefs', scope, 'fields', field, 'readOnly'])) {
            if (scopeData[field].edit === 'no' && scopeData[field].read === 'yes') {
              delete scopeData[field];
            }
          }
        }
      }
      this.createView('table', 'views/role/record/table', {
        acl: {
          data: this.options.aclData.table,
          fieldData: fieldTable
        },
        final: true,
        selector: '.user-access-table'
      });
      this.headerText = this.translate('Access');
    }
  }
  _exports.default = _default;
});

define("views/user/fields/user-name", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _varchar.default {
    setup() {
      super.setup();
      this.validations.push(() => this.validateUserName());
    }
    afterRender() {
      super.afterRender();
      const userNameRegularExpression = this.getUserNameRegularExpression();
      if (this.isEditMode()) {
        this.$element.on('change', () => {
          let value = this.$element.val();
          const re = new RegExp(userNameRegularExpression, 'gi');
          value = value.replace(re, '').replace(/[\s]/g, '_').toLowerCase();
          this.$element.val(value);
          this.trigger('change');
        });
      }
    }
    getUserNameRegularExpression() {
      return this.getConfig().get('userNameRegularExpression') || '[^a-z0-9\-@_\.\s]';
    }
    validateUserName() {
      const value = this.model.get(this.name);
      if (!value) {
        return;
      }
      const userNameRegularExpression = this.getUserNameRegularExpression();
      const re = new RegExp(userNameRegularExpression, 'gi');
      if (!re.test(value)) {
        return;
      }
      const msg = this.translate('fieldInvalid', 'messages').replace('{field}', this.getLabelText());
      this.showValidationMessage(msg);
      return true;
    }
  }
  _exports.default = _default;
});

define("views/user/fields/teams", ["exports", "views/fields/link-multiple-with-role"], function (_exports, _linkMultipleWithRole) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkMultipleWithRole = _interopRequireDefault(_linkMultipleWithRole);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _linkMultipleWithRole.default {
    forceRoles = true;
    setup() {
      super.setup();
      this.roleListMap = {};
      this.loadRoleList(() => {
        if (this.isEditMode()) {
          if (this.isRendered() || this.isBeingRendered()) {
            this.reRender();
          }
        }
      });
      this.listenTo(this.model, 'change:teamsIds', () => {
        let toLoad = false;
        this.ids.forEach(id => {
          if (!(id in this.roleListMap)) {
            toLoad = true;
          }
        });
        if (toLoad) {
          this.loadRoleList(() => {
            this.reRender();
          });
        }
      });
    }
    loadRoleList(callback, context) {
      if (!this.getAcl().checkScope('Team', 'read')) {
        return;
      }
      const ids = this.ids || [];
      if (ids.length === 0) {
        return;
      }
      this.getCollectionFactory().create('Team', teams => {
        teams.maxSize = 50;
        teams.where = [{
          type: 'in',
          field: 'id',
          value: ids
        }];
        this.listenToOnce(teams, 'sync', () => {
          teams.models.forEach(model => {
            this.roleListMap[model.id] = model.get('positionList') || [];
          });
          callback.call(context);
        });
        teams.fetch();
      });
    }
    getDetailLinkHtml(id, name) {
      name = name || this.nameHash[id] || id;
      let role = (this.columns[id] || {})[this.columnName] || '';
      const $el = $('<div>').append($('<a>').attr('href', '#' + this.foreignScope + '/view/' + id).attr('data-id', id).text(name));
      if (role) {
        role = this.getHelper().escapeString(role);
        $el.append($('<span>').text(' '), $('<span>').addClass('text-muted middle-dot'), $('<span>').text(' '), $('<span>').addClass('text-muted').text(role));
      }
      return $el.get(0).outerHTML;
    }
    getJQSelect(id, roleValue) {
      /** @var {string[]} */
      const roleList = Espo.Utils.clone(this.roleListMap[id] || []);
      if (!roleList.length && !roleValue) {
        return null;
      }
      roleList.unshift('');
      if (roleValue && roleList.indexOf(roleValue) === -1) {
        roleList.push(roleValue);
      }
      const $role = $('<select>').addClass('role form-control input-sm pull-right').attr('data-id', id);
      roleList.forEach(role => {
        const $option = $('<option>').val(role).text(role);
        if (role === (roleValue || '')) {
          $option.attr('selected', 'selected');
        }
        $role.append($option);
      });
      return $role;
    }
  }
  _exports.default = _default;
});

define("views/user/fields/record-access-level", ["exports", "views/fields/bool"], function (_exports, _bool) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _bool = _interopRequireDefault(_bool);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _bool.default {
    getValueForDisplay() {
      /** @type {Record<string, string>} */
      const levels = this.model.attributes.recordAccessLevels || {};
      const action = this.name.substring('recordAccessLevel'.length);
      return levels[action] || false;
    }
  }
  _exports.default = _default;
});

define("views/user/fields/password", ["exports", "views/fields/password"], function (_exports, _password) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _password = _interopRequireDefault(_password);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserPasswordFieldView extends _password.default {
    validations = ['required', 'strength', 'confirm'];
    init() {
      const tooltipItemList = [];
      this.strengthParams = this.options.strengthParams || {
        passwordStrengthLength: this.getConfig().get('passwordStrengthLength'),
        passwordStrengthLetterCount: this.getConfig().get('passwordStrengthLetterCount'),
        passwordStrengthNumberCount: this.getConfig().get('passwordStrengthNumberCount'),
        passwordStrengthBothCases: this.getConfig().get('passwordStrengthBothCases'),
        passwordStrengthSpecialCharacterCount: this.getConfig().get('passwordStrengthSpecialCharacterCount')
      };
      const minLength = this.strengthParams.passwordStrengthLength;
      if (minLength) {
        tooltipItemList.push('* ' + this.translate('passwordStrengthLength', 'messages', 'User').replace('{length}', minLength.toString()));
      }
      const requiredLetterCount = this.strengthParams.passwordStrengthLetterCount;
      if (requiredLetterCount) {
        tooltipItemList.push('* ' + this.translate('passwordStrengthLetterCount', 'messages', 'User').replace('{count}', requiredLetterCount.toString()));
      }
      const requiredNumberCount = this.strengthParams.passwordStrengthNumberCount;
      if (requiredNumberCount) {
        tooltipItemList.push('* ' + this.translate('passwordStrengthNumberCount', 'messages', 'User').replace('{count}', requiredNumberCount.toString()));
      }
      const bothCases = this.strengthParams.passwordStrengthBothCases;
      if (bothCases) {
        tooltipItemList.push('* ' + this.translate('passwordStrengthBothCases', 'messages', 'User'));
      }
      if (this.strengthParams.passwordStrengthSpecialCharacterCount) {
        tooltipItemList.push('* ' + this.translate('passwordStrengthSpecialCharacterCount', 'messages', 'User').replace('{count}', this.strengthParams.passwordStrengthSpecialCharacterCount.toString()));
      }
      if (tooltipItemList.length) {
        this.tooltip = true;
        this.tooltipText = this.translate('Requirements', 'labels', 'User') + ':\n' + tooltipItemList.join('\n');
      }
      super.init();
    }

    // noinspection JSUnusedGlobalSymbols
    validateStrength() {
      if (!this.model.get(this.name)) {
        return;
      }
      const password = this.model.get(this.name);
      const minLength = this.strengthParams.passwordStrengthLength;
      if (minLength) {
        if (password.length < minLength) {
          const msg = this.translate('passwordStrengthLength', 'messages', 'User').replace('{length}', minLength.toString());
          this.showValidationMessage(msg);
          return true;
        }
      }
      const requiredLetterCount = this.strengthParams.passwordStrengthLetterCount;
      if (requiredLetterCount) {
        let letterCount = 0;
        password.split('').forEach(c => {
          if (c.toLowerCase() !== c.toUpperCase()) {
            letterCount++;
          }
        });
        if (letterCount < requiredLetterCount) {
          const msg = this.translate('passwordStrengthLetterCount', 'messages', 'User').replace('{count}', requiredLetterCount.toString());
          this.showValidationMessage(msg);
          return true;
        }
      }
      const requiredNumberCount = this.strengthParams.passwordStrengthNumberCount;
      if (requiredNumberCount) {
        let numberCount = 0;
        password.split('').forEach(c => {
          if (c >= '0' && c <= '9') {
            numberCount++;
          }
        });
        if (numberCount < requiredNumberCount) {
          const msg = this.translate('passwordStrengthNumberCount', 'messages', 'User').replace('{count}', requiredNumberCount.toString());
          this.showValidationMessage(msg);
          return true;
        }
      }
      const bothCases = this.strengthParams.passwordStrengthBothCases;
      if (bothCases) {
        let ucCount = 0;
        password.split('').forEach(c => {
          if (c.toLowerCase() !== c.toUpperCase() && c === c.toUpperCase()) {
            ucCount++;
          }
        });
        let lcCount = 0;
        password.split('').forEach(c => {
          if (c.toLowerCase() !== c.toUpperCase() && c === c.toLowerCase()) {
            lcCount++;
          }
        });
        if (!ucCount || !lcCount) {
          const msg = this.translate('passwordStrengthBothCases', 'messages', 'User');
          this.showValidationMessage(msg);
          return true;
        }
      }
      const requiredSpecialCharacterCount = this.strengthParams.passwordStrengthSpecialCharacterCount;
      if (requiredSpecialCharacterCount) {
        let count = 0;
        password.split('').forEach(c => {
          if ("'-!\"#$%&()*,./:;?@[]^_`{|}~+<=>".includes(c)) {
            count++;
          }
        });
        if (count < requiredSpecialCharacterCount) {
          const msg = this.translate('passwordStrengthSpecialCharacterCount', 'messages', 'User').replace('{count}', requiredSpecialCharacterCount.toString());
          this.showValidationMessage(msg);
          return true;
        }
      }
    }
  }
  var _default = _exports.default = UserPasswordFieldView;
});

define("views/user/fields/name", ["exports", "views/fields/person-name"], function (_exports, _personName) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _personName = _interopRequireDefault(_personName);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _personName.default {
    listTemplate = 'user/fields/name/list-link';
    listLinkTemplate = 'user/fields/name/list-link';
    data() {
      const model = /** @type {import('models/user').default} */this.model;

      // noinspection JSValidateTypes
      return {
        ...super.data(),
        avatar: this.getAvatarHtml(),
        frontScope: model.isPortal() ? 'PortalUser' : 'User',
        isOwn: this.model.id === this.getUser().id
      };
    }
    getAvatarHtml() {
      return this.getHelper().getAvatarHtml(this.model.id, 'small', 20, 'avatar-link');
    }
  }
  _exports.default = _default;
});

define("views/user/fields/generate-password", ["exports", "views/fields/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserGeneratePasswordFieldView extends _base.default {
    templateContent = `
        <button
            type="button"
            class="btn btn-default"
            data-action="generatePassword"
        >{{translate 'Generate' scope='User'}}</button>`;
    events = {
      /** @this {UserGeneratePasswordFieldView} */
      'click [data-action="generatePassword"]': function () {
        this.actionGeneratePassword();
      }
    };
    setup() {
      super.setup();
      this.listenTo(this.model, 'change:password', (model, value, o) => {
        if (o.isGenerated) {
          return;
        }
        if (value !== undefined) {
          this.model.set('passwordPreview', null);
          return;
        }
        this.model.unset('passwordPreview');
      });
      this.strengthParams = this.options.strengthParams || {};
      this.passwordStrengthLength = this.strengthParams.passwordStrengthLength || this.getConfig().get('passwordStrengthLength');
      this.passwordStrengthLetterCount = this.strengthParams.passwordStrengthLetterCount || this.getConfig().get('passwordStrengthLetterCount');
      this.passwordStrengthNumberCount = this.strengthParams.passwordStrengthNumberCount || this.getConfig().get('passwordStrengthNumberCount');
      this.passwordStrengthSpecialCharacterCount = this.strengthParams.passwordStrengthSpecialCharacterCount || this.getConfig().get('passwordStrengthSpecialCharacterCount');
      this.passwordGenerateLength = this.strengthParams.passwordGenerateLength || this.getConfig().get('passwordGenerateLength');
      this.passwordGenerateLetterCount = this.strengthParams.passwordGenerateLetterCount || this.getConfig().get('passwordGenerateLetterCount');
      this.passwordGenerateNumberCount = this.strengthParams.passwordGenerateNumberCount || this.getConfig().get('passwordGenerateNumberCount');
    }
    fetch() {
      return {};
    }
    actionGeneratePassword() {
      let length = this.passwordStrengthLength;
      let letterCount = this.passwordStrengthLetterCount;
      let numberCount = this.passwordStrengthNumberCount;
      const specialCharacterCount = this.passwordStrengthSpecialCharacterCount;
      const generateLength = this.passwordGenerateLength || 10;
      const generateLetterCount = this.passwordGenerateLetterCount || 4;
      const generateNumberCount = this.passwordGenerateNumberCount || 2;
      length = typeof length === 'undefined' ? generateLength : length;
      letterCount = typeof letterCount === 'undefined' ? generateLetterCount : letterCount;
      numberCount = typeof numberCount === 'undefined' ? generateNumberCount : numberCount;
      if (length < generateLength) {
        length = generateLength;
      }
      if (letterCount < generateLetterCount) {
        letterCount = generateLetterCount;
      }
      if (numberCount < generateNumberCount) {
        numberCount = generateNumberCount;
      }
      const password = this.generatePassword(length, letterCount, numberCount, true, specialCharacterCount);
      this.model.set({
        password: password,
        passwordConfirm: password,
        passwordPreview: password
      }, {
        isGenerated: true
      });
    }

    /**
     * @private
     * @param {number} length
     * @param {number} letters
     * @param {number} numbers
     * @param {boolean} bothCases
     * @param {number} specialCharacters
     * @return {string}
     */
    generatePassword(length, letters, numbers, bothCases, specialCharacters) {
      const chars = ['ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', '0123456789', 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz', "'-!\"#$%&()*,./:;?@[]^_`{|}~+<=>"];
      let upperCase = 0;
      let lowerCase = 0;
      if (bothCases) {
        upperCase = 1;
        lowerCase = 1;
        if (letters >= 2) {
          letters = letters - 2;
        } else {
          letters = 0;
        }
      }
      let either = length - (letters + numbers + upperCase + lowerCase + specialCharacters);
      if (either < 0) {
        either = 0;
      }
      const setList = [letters, numbers, either, upperCase, lowerCase, specialCharacters];
      const shuffle = function (array) {
        let currentIndex = array.length;
        while (0 !== currentIndex) {
          const randomArray = new Uint32Array(1);
          crypto.getRandomValues(randomArray);
          const randomIndex = Math.floor(randomArray[0] / (0xFFFFFFFF + 1) * currentIndex);
          currentIndex -= 1;
          const tempValue = array[currentIndex];
          array[currentIndex] = array[randomIndex];
          array[randomIndex] = tempValue;
        }
        return array;
      };
      const array = setList.map((len, i) => {
        return Array(len).fill(chars[i]).map(x => {
          const randomArray = new Uint32Array(1);
          crypto.getRandomValues(randomArray);
          const randomIndex = Math.floor(randomArray[0] / (0xFFFFFFFF + 1) * x.length);
          return x[randomIndex];
        }).join('');
      }).concat();
      return shuffle(array).join('');
    }
  }
  var _default = _exports.default = UserGeneratePasswordFieldView;
});

define("views/user/fields/default-team", ["exports", "views/fields/link"], function (_exports, _link) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _link = _interopRequireDefault(_link);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserDefaultTeamFieldView extends _link.default {
    setup() {
      super.setup();
      this.validations.push('isUsers');
    }
    getOnEmptyAutocomplete() {
      const names = this.model.get('teamsNames') || {};
      const list = this.model.getTeamIdList().map(id => ({
        id: id,
        name: names[id] || id
      }));
      return Promise.resolve(list);
    }

    // noinspection JSUnusedGlobalSymbols
    validateIsUsers() {
      const id = this.model.get('defaultTeamId');
      if (!id) {
        return false;
      }
      if (this.model.getTeamIdList().includes(id)) {
        return false;
      }
      const msg = this.translate('defaultTeamIsNotUsers', 'messages', 'User');
      this.showValidationMessage(msg);
      return true;
    }
  }
  var _default = _exports.default = UserDefaultTeamFieldView;
});

define("views/user/fields/contact", ["exports", "views/fields/link"], function (_exports, _link) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _link = _interopRequireDefault(_link);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _link.default {
    select(model) {
      super.select(model);
      const attributes = {};
      if (model.get('accountId')) {
        const names = {};
        names[model.get('accountId')] = model.get('accountName');
        attributes.accountsIds = [model.get('accountId')];
        attributes.accountsNames = names;
      }
      attributes.firstName = model.get('firstName');
      attributes.lastName = model.get('lastName');
      attributes.salutationName = model.get('salutationName');
      attributes.emailAddress = model.get('emailAddress');
      attributes.emailAddressData = model.get('emailAddressData');
      attributes.phoneNumber = model.get('phoneNumber');
      attributes.phoneNumberData = model.get('phoneNumberData');
      if (this.model.isNew() && !this.model.get('userName') && attributes.emailAddress) {
        attributes.userName = attributes.emailAddress;
      }
      this.model.set(attributes);
    }
  }
  _exports.default = _default;
});

define("views/user/fields/avatar", ["exports", "views/fields/image", "model", "views/fields/colorpicker"], function (_exports, _image, _model, _colorpicker) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _image = _interopRequireDefault(_image);
  _model = _interopRequireDefault(_model);
  _colorpicker = _interopRequireDefault(_colorpicker);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserAvatarFieldView extends _image.default {
    getAttributeList() {
      if (this.isEditMode()) {
        return [...super.getAttributeList(), 'avatarColor'];
      }
      return [];
    }
    setup() {
      super.setup();
      this.on('after:inline-save', () => {
        this.suspendCache = true;
        this.reRender();
      });
      this.setupSub();
    }
    setupSub() {
      this.subModel = new _model.default();
      const syncModels = () => {
        this.subModel.set({
          color: this.model.attributes.avatarColor
        });
      };
      syncModels();
      this.listenTo(this.model, 'change:avatarColor', (m, v, o) => {
        if (o.fromView !== this) {
          syncModels();
        }
      });
      this.listenTo(this.subModel, 'change', (m, o) => {
        if (o.ui) {
          this.trigger('change');
        }
      });
    }
    onEditModeSet() {
      if (!this.hasColor()) {
        return;
      }
      this.colorView = new _colorpicker.default({
        name: 'color',
        model: this.subModel,
        labelText: this.translate('avatarColor', 'fields', 'User'),
        mode: 'edit'
      });
      return this.assignView('colorField', this.colorView, '[data-sub-field="color"]');
    }
    afterRender() {
      super.afterRender();
      if (this.isEditMode()) {
        const colorEl = document.createElement('div');
        colorEl.setAttribute('data-sub-field', 'color');
        colorEl.classList.add('avatar-field-color');

        // noinspection JSCheckFunctionSignatures
        this.element.appendChild(colorEl);
        if (this.colorView) {
          this.colorView.reRender().then(() => {
            const el = this.colorView.element.querySelector('input');
            el.placeholder = this.translate('avatarColor', 'fields', 'User');
          });
        }
      }
    }
    hasColor() {
      if (this.recordHelper && this.recordHelper.getFieldStateParam('avatarColor', 'readOnly')) {
        return false;
      }
      const userType = this.model.get('type');
      return ['regular', 'admin', 'api'].includes(userType);
    }
    fetch() {
      if (!this.hasColor()) {
        return super.fetch();
      }

      // noinspection JSValidateTypes
      return {
        ...super.fetch(),
        avatarColor: this.subModel.attributes.color
      };
    }

    /**
     * @protected
     * @param {File} file
     * @return {Promise<unknown>}
     */
    handleUploadingFile(file) {
      return new Promise((resolve, reject) => {
        const fileReader = new FileReader();
        fileReader.onload = e => {
          this.createView('crop', 'views/modals/image-crop', {
            contents: e.target.result
          }).then(view => {
            view.render();
            let cropped = false;
            this.listenToOnce(view, 'crop', dataUrl => {
              cropped = true;
              setTimeout(() => {
                fetch(dataUrl).then(result => result.blob()).then(blob => {
                  resolve(new File([blob], 'avatar.jpg', {
                    type: 'image/jpeg'
                  }));
                });
              }, 10);
            });
            this.listenToOnce(view, 'remove', () => {
              if (!cropped) {
                setTimeout(() => this.render(), 10);
                reject();
              }
              this.clearView('crop');
            });
          });
        };
        fileReader.readAsDataURL(file);
      });
    }
    getValueForDisplay() {
      if (!this.isReadMode()) {
        return '';
      }
      const id = this.model.get(this.idName);
      const userId = this.model.id;
      let t = this.cacheTimestamp = this.cacheTimestamp || Date.now();
      if (this.suspendCache) {
        t = Date.now();
      }
      const src = this.getBasePath() + '?entryPoint=avatar&size=' + this.previewSize + '&id=' + userId + '&t=' + t + '&attachmentId=' + (id || 'false');

      // noinspection HtmlRequiredAltAttribute,RequiredAttributes
      const $img = $('<img>').attr('src', src).attr('alt', this.labelText).css({
        maxWidth: (this.imageSizes[this.previewSize] || {})[0],
        maxHeight: (this.imageSizes[this.previewSize] || {})[1]
      });
      if (!this.isDetailMode()) {
        if (this.getCache()) {
          t = this.getCache().get('app', 'timestamp');
        }
        const src = `${this.getBasePath()}?entryPoint=avatar&size=${this.previewSize}&id=${userId}&t=${t}`;
        $img.attr('width', '16').attr('src', src).css('maxWidth', '16px');
      }
      if (!id) {
        return $img.get(0).outerHTML;
      }
      return $('<a>').attr('data-id', id).attr('data-action', 'showImagePreview').attr('href', this.getBasePath() + '?entryPoint=image&id=' + id).append($img).get(0).outerHTML;
    }
  }
  var _default = _exports.default = UserAvatarFieldView;
});

define("views/user/fields/auto-follow-entity-type-list", ["exports", "views/fields/multi-enum"], function (_exports, _multiEnum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _multiEnum = _interopRequireDefault(_multiEnum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class _default extends _multiEnum.default {
    setup() {
      this.params.options = Object.keys(this.getMetadata().get('scopes')).filter(scope => {
        return this.getMetadata().get(`scopes.${scope}.entity`) && this.getMetadata().get(`scopes.${scope}.stream`);
      }).sort((v1, v2) => {
        return this.translate(v1, 'scopeNamesPlural').localeCompare(this.translate(v2, 'scopeNamesPlural'));
      });
      super.setup();
    }
  }
  _exports.default = _default;
});

define("views/template/record/edit", ["exports", "views/record/edit"], function (_exports, _edit) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _edit.default {
    saveAndContinueEditingAction = true;
    setup() {
      super.setup();
      if (!this.model.isNew()) {
        this.setFieldReadOnly('entityType');
      }
      if (this.model.get('entityType')) {
        this.showField('variables');
      } else {
        this.hideField('variables');
      }
      if (this.model.isNew()) {
        const storedData = {};
        this.listenTo(this.model, 'change:entityType', () => {
          const entityType = this.model.get('entityType');
          if (!entityType) {
            this.model.set('header', null);
            this.model.set('body', null);
            this.model.set('footer', null);
            this.hideField('variables');
            return;
          }
          this.showField('variables');
          if (entityType in storedData) {
            this.model.set('header', storedData[entityType].header);
            this.model.set('body', storedData[entityType].body);
            this.model.set('footer', storedData[entityType].footer);
            this.model.set('style', storedData[entityType].style);
            return;
          }
          let header, body, footer;
          let sourceType = null;
          let style = null;
          if (this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', entityType])) {
            sourceType = entityType;
          } else {
            const scopeType = this.getMetadata().get(['scopes', entityType, 'type']);
            if (scopeType && this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', scopeType])) {
              sourceType = scopeType;
            }
          }
          if (sourceType) {
            header = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', sourceType, 'header']);
            body = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', sourceType, 'body']);
            footer = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', sourceType, 'footer']);
            style = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', sourceType, 'style']);
          }
          body = body || null;
          header = header || null;
          footer = footer || null;
          this.model.set('body', body);
          this.model.set('header', header);
          this.model.set('footer', footer);
          this.model.set('style', style);
        });
        this.listenTo(this.model, 'change', (e, o) => {
          if (!o.ui) {
            return;
          }
          if (!this.model.hasChanged('header') && !this.model.hasChanged('body') && !this.model.hasChanged('footer') && !this.model.hasChanged('style')) {
            return;
          }
          const entityType = this.model.get('entityType');
          if (!entityType) {
            return;
          }
          storedData[entityType] = {
            header: this.model.get('header'),
            body: this.model.get('body'),
            footer: this.model.get('footer'),
            style: this.model.get('style')
          };
        });
      }
    }
  }
  _exports.default = _default;
});

define("views/template/record/detail", ["exports", "views/record/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _detail.default {
    saveAndContinueEditingAction = true;
    setup() {
      super.setup();
      this.hideField('variables');
      this.on('after:set-edit-mode', () => this.showField('variables'));
      this.on('after:set-detail-mode', () => this.hideField('variables'));
    }
  }
  _exports.default = _default;
});

define("views/template/fields/variables", ["exports", "views/fields/base", "ui/select"], function (_exports, _base, _select) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _select = _interopRequireDefault(_select);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _base.default {
    inlineEditDisabled = true;
    detailTemplate = 'template/fields/variables/detail';
    editTemplate = 'template/fields/variables/edit';

    // noinspection JSCheckFunctionSignatures
    data() {
      // noinspection JSValidateTypes
      return {
        attributeList: this.attributeList,
        entityType: this.model.get('entityType'),
        translatedOptions: this.translatedOptions
      };
    }
    setup() {
      this.addHandler('change', '[data-name="variables"]', () => {
        const attribute = this.$el.find('[data-name="variables"]').val();
        const $copy = this.$el.find('[data-name="copy"]');
        if (attribute !== '') {
          if (this.textVariables[attribute]) {
            $copy.val('{{{' + attribute + '}}}');
          } else {
            $copy.val('{{' + attribute + '}}');
          }
        } else {
          $copy.val('');
        }
      });
      this.setupAttributeList();
      this.setupTranslatedOptions();
      this.listenTo(this.model, 'change:entityType', () => {
        this.setupAttributeList();
        this.setupTranslatedOptions();
        this.reRender();
      });
    }
    setupAttributeList() {
      this.translatedOptions = {};
      const entityType = this.model.get('entityType');
      const fieldList = this.getFieldManager().getEntityTypeFieldList(entityType);
      const ignoreFieldList = [];
      fieldList.forEach(field => {
        const aclDefs = /** @type {Record} */
        this.getMetadata().get(['entityAcl', entityType, 'fields', field]) || {};
        const fieldDefs = /** @type {Record} */
        this.getMetadata().get(['entityDefs', entityType, 'fields', field]) || {};
        if (aclDefs.onlyAdmin || aclDefs.forbidden || aclDefs.internal || fieldDefs.disabled || fieldDefs.utility || fieldDefs.directAccessDisabled && !fieldDefs.loaderClassName || fieldDefs.templatePlaceholderDisabled) {
          ignoreFieldList.push(field);
        }
      });
      let attributeList = this.getFieldManager().getEntityTypeAttributeList(entityType) || [];
      const forbiddenList = Espo.Utils.clone(this.getAcl().getScopeForbiddenAttributeList(entityType));
      ignoreFieldList.forEach(field => {
        this.getFieldManager().getEntityTypeFieldAttributeList(entityType, field).forEach(attribute => {
          forbiddenList.push(attribute);
        });
      });
      attributeList = attributeList.filter(item => {
        if (~forbiddenList.indexOf(item)) {
          return;
        }
        const fieldType = this.getMetadata().get(['entityDefs', entityType, 'fields', item, 'type']);
        if (fieldType === 'map') {
          return;
        }
        return true;
      });
      attributeList.push('id');
      if (this.getMetadata().get(`entityDefs.${entityType}.fields.name.type`) === 'personName') {
        if (!~attributeList.indexOf('name')) {
          attributeList.unshift('name');
        }
      }
      this.addAdditionalPlaceholders(entityType, attributeList);
      attributeList = attributeList.sort((v1, v2) => {
        return this.translate(v1, 'fields', entityType).localeCompare(this.translate(v2, 'fields', entityType));
      });
      this.attributeList = attributeList;
      this.textVariables = {};
      this.attributeList.forEach(item => {
        if (~['text', 'wysiwyg'].indexOf(this.getMetadata().get(['entityDefs', entityType, 'fields', item, 'type']))) {
          this.textVariables[item] = true;
        }
      });
      if (!~this.attributeList.indexOf('now')) {
        this.attributeList.unshift('now');
      }
      if (!~this.attributeList.indexOf('today')) {
        this.attributeList.unshift('today');
      }

      // noinspection SpellCheckingInspection
      attributeList.unshift('pagebreak');
      this.attributeList.unshift('');
      const links = /** @type {Record<string, Record>} */this.getMetadata().get(`entityDefs.${entityType}.links`) || {};
      const linkList = Object.keys(links).sort((v1, v2) => {
        return this.translate(v1, 'links', entityType).localeCompare(this.translate(v2, 'links', entityType));
      });
      linkList.forEach(link => {
        const type = links[link].type;
        if (type !== 'belongsTo') {
          return;
        }
        const scope = links[link].entity;
        if (!scope) {
          return;
        }
        if (links[link].disabled || links[link].utility) {
          return;
        }
        if (this.getMetadata().get(['entityAcl', entityType, 'links', link, 'onlyAdmin']) || this.getMetadata().get(['entityAcl', entityType, 'links', link, 'forbidden']) || this.getMetadata().get(['entityAcl', entityType, 'links', link, 'internal'])) {
          return;
        }
        const fieldList = this.getFieldManager().getEntityTypeFieldList(scope);
        const ignoreFieldList = [];
        fieldList.forEach(field => {
          const aclDefs = /** @type {Record} */
          this.getMetadata().get(['entityAcl', scope, 'fields', field]) || {};
          const fieldDefs = /** @type {Record} */
          this.getMetadata().get(['entityDefs', scope, 'fields', field]) || {};
          if (aclDefs.onlyAdmin || aclDefs.forbidden || aclDefs.internal || fieldDefs.disabled || fieldDefs.utility || fieldDefs.directAccessDisabled || fieldDefs.templatePlaceholderDisabled) {
            ignoreFieldList.push(field);
          }
        });
        let attributeList = this.getFieldManager().getEntityTypeAttributeList(scope) || [];
        const forbiddenList = Espo.Utils.clone(this.getAcl().getScopeForbiddenAttributeList(scope));
        ignoreFieldList.forEach(field => {
          this.getFieldManager().getEntityTypeFieldAttributeList(scope, field).forEach(attribute => {
            forbiddenList.push(attribute);
          });
        });
        attributeList = attributeList.filter(item => {
          if (~forbiddenList.indexOf(item)) {
            return;
          }
          const fieldType = this.getMetadata().get(['entityDefs', scope, 'fields', item, 'type']);
          if (fieldType === 'map') {
            return;
          }
          return true;
        });
        attributeList.push('id');
        if (this.getMetadata().get(`entityDefs.${scope}.fields.name.type`) === 'personName') {
          attributeList.unshift('name');
        }
        const originalAttributeList = Espo.Utils.clone(attributeList);
        this.addAdditionalPlaceholders(scope, attributeList, link, entityType);
        attributeList.sort((v1, v2) => {
          return this.translate(v1, 'fields', scope).localeCompare(this.translate(v2, 'fields', scope));
        });
        attributeList.forEach(item => {
          if (~originalAttributeList.indexOf(item)) {
            this.attributeList.push(`${link}.${item}`);
          } else {
            this.attributeList.push(item);
          }
        });
        attributeList.forEach(item => {
          const variable = `${link}.${item}`;
          if (~['text', 'wysiwyg'].indexOf(this.getMetadata().get(['entityDefs', scope, 'fields', item, 'type']))) {
            this.textVariables[variable] = true;
          }
        });
      });
      return this.attributeList;
    }
    addAdditionalPlaceholders(entityType, attributeList, link, superEntityType) {
      let value;
      function removeItem(attributeList, item) {
        for (let i = 0; i < attributeList.length; i++) {
          if (attributeList[i] === item) {
            attributeList.splice(i, 1);
          }
        }
      }
      const fieldDefs = this.getMetadata().get(['entityDefs', entityType, 'fields']) || {};
      for (const field in fieldDefs) {
        const fieldType = fieldDefs[field].type;
        let item = field;
        if (link) {
          item = `${link}.${item}`;
        }
        if (fieldType === 'image') {
          removeItem(attributeList, field + 'Name');
          removeItem(attributeList, field + 'Id');
          value = 'imageTag ' + item + 'Id';
          attributeList.push(value);
          this.translatedOptions[value] = this.translate(field, 'fields', entityType);
          if (link) {
            this.translatedOptions[value] = this.translate(link, 'links', superEntityType) + ' . ' + this.translatedOptions[value];
          }
        } else if (fieldType === 'barcode') {
          removeItem(attributeList, field);
          const barcodeType = this.getMetadata().get(['entityDefs', entityType, 'fields', field, 'codeType']);
          value = `barcodeImage ${item} type='${barcodeType}'`;
          attributeList.push(value);
          this.translatedOptions[value] = this.translate(field, 'fields', entityType);
          if (link) {
            this.translatedOptions[value] = this.translate(link, 'links', superEntityType) + ' . ' + this.translatedOptions[value];
          }
        }
      }
    }
    setupTranslatedOptions() {
      const entityType = this.model.get('entityType');
      this.attributeList.forEach(item => {
        const link = item.split('.')[0];

        // noinspection SpellCheckingInspection
        if (~['today', 'now', 'pagebreak'].indexOf(item)) {
          if (!this.getMetadata().get(['entityDefs', entityType, 'fields', item])) {
            this.translatedOptions[item] = this.getLanguage().translateOption(item, 'placeholders', 'Template');
            return;
          }
        }
        let field = item;
        let scope = entityType;
        let isForeign = false;
        if (~item.indexOf('.')) {
          isForeign = true;
          field = item.split('.')[1];
          scope = this.getMetadata().get(`entityDefs.${entityType}.links.${link}.entity`);
        }
        if (this.translatedOptions[item]) {
          return;
        }
        this.translatedOptions[item] = this.translate(field, 'fields', scope);
        if (field.indexOf('Id') === field.length - 2) {
          const baseField = field.substr(0, field.length - 2);
          if (this.getMetadata().get(['entityDefs', scope, 'fields', baseField])) {
            this.translatedOptions[item] = this.translate(baseField, 'fields', scope) + ' (' + this.translate('id', 'fields') + ')';
          }
        } else if (field.indexOf('Name') === field.length - 4) {
          const baseField = field.substr(0, field.length - 4);
          if (this.getMetadata().get(['entityDefs', scope, 'fields', baseField])) {
            this.translatedOptions[item] = this.translate(baseField, 'fields', scope) + ' (' + this.translate('name', 'fields') + ')';
          }
        } else if (field.indexOf('Type') === field.length - 4) {
          const baseField = field.substr(0, field.length - 4);
          if (this.getMetadata().get(['entityDefs', scope, 'fields', baseField])) {
            this.translatedOptions[item] = this.translate(baseField, 'fields', scope) + ' (' + this.translate('type', 'fields') + ')';
          }
        }
        if (field.indexOf('Ids') === field.length - 3) {
          const baseField = field.substr(0, field.length - 3);
          if (this.getMetadata().get(['entityDefs', scope, 'fields', baseField])) {
            this.translatedOptions[item] = this.translate(baseField, 'fields', scope) + ' (' + this.translate('ids', 'fields') + ')';
          }
        } else if (field.indexOf('Names') === field.length - 5) {
          const baseField = field.substr(0, field.length - 5);
          if (this.getMetadata().get(['entityDefs', scope, 'fields', baseField])) {
            this.translatedOptions[item] = this.translate(baseField, 'fields', scope) + ' (' + this.translate('names', 'fields') + ')';
          }
        } else if (field.indexOf('Types') === field.length - 5) {
          const baseField = field.substr(0, field.length - 5);
          if (this.getMetadata().get(['entityDefs', scope, 'fields', baseField])) {
            this.translatedOptions[item] = this.translate(baseField, 'fields', scope) + ' (' + this.translate('types', 'fields') + ')';
          }
        }
        if (isForeign) {
          this.translatedOptions[item] = this.translate(link, 'links', entityType) + ' . ' + this.translatedOptions[item];
        }
      });
    }
    afterRender() {
      super.afterRender();
      if (this.mode === this.MODE_EDIT) {
        _select.default.init(this.$el.find('[data-name="variables"]'));
      }
    }
    fetch() {}
  }
  _exports.default = _default;
});

define("views/template/fields/style", ["exports", "views/fields/text"], function (_exports, _text) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _text = _interopRequireDefault(_text);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * @type {{
   *     edit: import('ace-builds').edit,
   *     require: import('ace-builds').require,
   * }}
   */
  let ace;
  class TemplateStyleFieldView extends _text.default {
    // language=Handlebars
    detailTemplateContent = `
        {{#if isNotEmpty}}
            <div id="{{containerId}}">{{value}}</div>
        {{else}}
            <span class="none-value">{{translate 'None'}}</span>
        {{/if}}
    `;

    // language=Handlebars
    editTemplateContent = `
        <div id="{{containerId}}">{{value}}</div>
    `;
    height = 46;
    maxLineDetailCount = 80;
    maxLineEditCount = 200;
    setup() {
      super.setup();
      this.height = this.options.height || this.params.height || this.height;
      this.maxLineDetailCount = this.options.maxLineDetailCount || this.params.maxLineDetailCount || this.maxLineDetailCount;
      this.maxLineEditCount = this.options.maxLineEditCount || this.params.maxLineEditCount || this.maxLineEditCount;
      this.containerId = 'editor-' + Math.floor(Math.random() * 10000 + 1).toString();
      if (this.mode === this.MODE_EDIT || this.mode === this.MODE_DETAIL) {
        this.wait(this.requireAce());
      }
      this.on('remove', () => {
        if (this.editor) {
          this.editor.destroy();
        }
      });
    }
    requireAce() {
      return Espo.loader.requirePromise('lib!ace').then(lib => {
        ace = lib;
        const list = [Espo.loader.requirePromise('lib!ace-ext-language_tools'), Espo.loader.requirePromise('lib!ace-mode-css')];
        if (this.getThemeManager().getParam('isDark')) {
          list.push(Espo.loader.requirePromise('lib!ace-theme-tomorrow_night'));
        }
        return Promise.all(list);
      });
    }
    data() {
      const data = super.data();
      data.containerId = this.containerId;
      return data;
    }
    afterRender() {
      super.afterRender();
      this.$editor = this.$el.find('#' + this.containerId);
      if (this.$editor.length && (this.mode === this.MODE_EDIT || this.mode === this.MODE_DETAIL || this.mode === this.MODE_LIST)) {
        this.$editor.css('fontSize', 'var(--font-size-base)');
        if (this.mode === this.MODE_EDIT) {
          this.$editor.css('minHeight', this.height + 'px');
        }
        const editor = this.editor = ace.edit(this.containerId);
        editor.setOptions({
          fontFamily: 'var(--font-family-monospace)'
        });
        editor.setFontSize('var(--font-size-base)');
        editor.container.style.lineHeight = 'var(--line-height-computed)';
        editor.renderer.updateFontSize();
        editor.setOptions({
          maxLines: this.mode === this.MODE_EDIT ? this.maxLineEditCount : this.maxLineDetailCount,
          enableLiveAutocompletion: true
        });
        if (this.getThemeManager().getParam('isDark')) {
          editor.setOptions({
            theme: 'ace/theme/tomorrow_night'
          });
        }
        if (this.isEditMode()) {
          editor.getSession().on('change', () => {
            this.trigger('change', {
              ui: true
            });
          });
          editor.getSession().setUseWrapMode(true);
        }
        if (this.isReadMode()) {
          editor.setReadOnly(true);
          editor.renderer.$cursorLayer.element.style.display = 'none';
          editor.renderer.setShowGutter(false);
        }
        editor.setShowPrintMargin(false);
        editor.getSession().setUseWorker(false);
        editor.commands.removeCommand('find');
        editor.setHighlightActiveLine(false);
        const Mode = ace.require('ace/mode/css').Mode;
        editor.session.setMode(new Mode());
      }
    }
    fetch() {
      const data = {};
      data[this.name] = this.editor.getValue();
      return data;
    }
  }
  var _default = _exports.default = TemplateStyleFieldView;
});

define("views/template/fields/font-face", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class _default extends _enum.default {
    setupOptions() {
      const engine = this.getConfig().get('pdfEngine') || 'Dompdf';
      let fontFaceList = this.getMetadata().get(['app', 'pdfEngines', engine, 'fontFaceList']) || [];
      fontFaceList = Espo.Utils.clone(fontFaceList);
      fontFaceList.unshift('');
      this.params.options = fontFaceList;
    }
  }
  _exports.default = _default;
});

define("views/template/fields/entity-type", ["exports", "views/fields/entity-type"], function (_exports, _entityType) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _entityType = _interopRequireDefault(_entityType);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _entityType.default {
    checkAvailability(entityType) {
      const defs = this.scopesMetadataDefs[entityType] || {};
      if (defs.pdfTemplate) {
        return true;
      }
      if (defs.entity && defs.object) {
        return true;
      }
      return false;
    }
  }
  _exports.default = _default;
});

define("views/template/fields/body", ["exports", "views/fields/wysiwyg"], function (_exports, _wysiwyg) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _wysiwyg = _interopRequireDefault(_wysiwyg);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class BodyTemplateFieldView extends _wysiwyg.default {
    htmlPurificationForEditDisabled = true;
    noStylesheet = true;
    useIframe = true;
    tableClassName = 'bordered';
    handlebars = true;
  }
  var _default = _exports.default = BodyTemplateFieldView;
});

define("views/team/record/list", ["exports", "views/record/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _list.default {
    quickDetailDisabled = true;
    quickEditDisabled = true;
    checkAllResultDisabled = true;
  }
  _exports.default = _default;
});

define("views/team/record/edit", ["exports", "views/record/edit"], function (_exports, _edit) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _edit.default {}
  _exports.default = _default;
});

define("views/team/record/detail", ["exports", "views/record/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _detail.default {}
  _exports.default = _default;
});

define("views/team/modals/detail", ["exports", "views/modals/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _detail.default {
    editDisabled = true;
  }
  _exports.default = _default;
});

define("views/stream/message", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class MessageStreamView extends _view.default {
    data() {
      return this.dataForTemplate;
    }
    setup() {
      let template = this.options.messageTemplate;
      const data = Espo.Utils.clone(this.options.messageData || {});
      this.dataForTemplate = {};
      for (let key in data) {
        const value = data[key] || '';
        if (key.indexOf('html:') === 0) {
          key = key.substring(5);
          this.dataForTemplate[key] = value;
          template = template.replace('{' + key + '}', '{{{' + key + '}}}');
          continue;
        }
        if (value instanceof jQuery) {
          this.dataForTemplate[key] = value.get(0).outerHTML;
          template = template.replace('{' + key + '}', '{{{' + key + '}}}');
          continue;
        }
        if (value instanceof Element) {
          this.dataForTemplate[key] = value.outerHTML;
          template = template.replace('{' + key + '}', '{{{' + key + '}}}');
          continue;
        }
        if (!value.indexOf) {
          continue;
        }
        if (value.indexOf('field:') === 0) {
          const field = value.substring(6);
          this.createField(key, field);
          const keyEscaped = this.getHelper().escapeString(key);
          template = template.replace('{' + key + '}', `<span data-key="${keyEscaped}">\{\{\{${key}\}\}\}</span>`);
          continue;
        }
        this.dataForTemplate[key] = value;
        template = template.replace(`{${key}}`, `{{${key}}}`);
      }
      this.templateContent = template;
    }
    createField(key, name, type, params) {
      type = type || this.model.getFieldType(name) || 'base';
      this.createView(key, this.getFieldManager().getViewName(type), {
        model: this.model,
        defs: {
          name: name,
          params: params || {}
        },
        mode: 'detail',
        readOnly: true,
        selector: `[data-key="${key}"]`
      });
    }
  }
  var _default = _exports.default = MessageStreamView;
});

define("views/stream/record/edit", ["exports", "views/record/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EditStreamView extends _base.default {
    template = 'stream/record/edit';
    postingMode = false;
    dependencyDefs = {
      'targetType': {
        map: {
          'users': [{
            action: 'hide',
            fields: ['teams', 'portals']
          }, {
            action: 'show',
            fields: ['users']
          }, {
            action: 'setNotRequired',
            fields: ['teams', 'portals']
          }, {
            action: 'setRequired',
            fields: ['users']
          }],
          'teams': [{
            action: 'hide',
            fields: ['users', 'portals']
          }, {
            action: 'show',
            fields: ['teams']
          }, {
            action: 'setRequired',
            fields: ['teams']
          }, {
            action: 'setNotRequired',
            fields: ['users', 'portals']
          }],
          'portals': [{
            action: 'hide',
            fields: ['users', 'teams']
          }, {
            action: 'show',
            fields: ['portals']
          }, {
            action: 'setRequired',
            fields: ['portals']
          }, {
            action: 'setNotRequired',
            fields: ['users', 'teams']
          }]
        },
        default: [{
          action: 'hide',
          fields: ['teams', 'users', 'portals']
        }, {
          action: 'setNotRequired',
          fields: ['teams', 'users', 'portals']
        }]
      }
    };
    data() {
      const data = super.data();
      data.interactiveMode = this.options.interactiveMode;
      return data;
    }
    setup() {
      super.setup();
      this.seed = this.model.clone();
      if (this.options.interactiveMode) {
        this.events['focus textarea[data-name="post"]'] = () => {
          this.enablePostingMode();
        };
        this.addHandler('keydown', 'textarea[data-name="post"]', /** KeyboardEvent */e => {
          if (Espo.Utils.getKeyFromKeyEvent(e) === 'Control+Enter') {
            e.stopPropagation();
            e.preventDefault();
            this.post();
          }

          // Don't hide to be able to focus on the upload button.
          /*if (e.code === 'Tab') {
              let $text = $(e.currentTarget);
                if ($text.val() === '') {
                  this.disablePostingMode();
              }
          }*/
        });
        this.events['click button.post'] = () => {
          this.post();
        };
      }
      const optionList = ['self'];
      this.model.set('type', 'Post');
      this.model.set('targetType', 'self');
      const messagePermission = this.getAcl().getPermissionLevel('message');
      const portalPermission = this.getAcl().getPermissionLevel('portal');
      if (messagePermission === 'team' || messagePermission === 'all') {
        optionList.push('users');
        optionList.push('teams');
      }
      if (messagePermission === 'all') {
        optionList.push('all');
      }
      if (portalPermission === 'yes') {
        optionList.push('portals');
        if (!~optionList.indexOf('users')) {
          optionList.push('users');
        }
      }
      this.createField('targetType', 'views/fields/enum', {
        options: optionList
      });
      this.createField('users', 'views/note/fields/users', {});
      this.createField('teams', 'views/fields/teams', {});
      this.createField('portals', 'views/fields/link-multiple', {});
      this.createField('post', 'views/note/fields/post', {
        required: true,
        rowsMin: 1,
        noResize: true
      });
      this.createField('attachments', 'views/stream/fields/attachment-multiple', {});
      this.listenTo(this.model, 'change', () => {
        if (this.postingMode) {
          this.setConfirmLeaveOut(true);
        }
      });
    }
    disablePostingMode() {
      this.postingMode = false;
      this.$el.find('.post-control').addClass('hidden');
      this.setConfirmLeaveOut(false);
      $('body').off('click.stream-create-post');
      this.getFieldView('post').$element.prop('rows', 1);
    }
    enablePostingMode() {
      this.$el.find('.post-control').removeClass('hidden');
      if (!this.postingMode) {
        const $body = $('body');
        $body.off('click.stream-create-post');
        $body.on('click.stream-create-post', e => {
          if ($.contains(window.document.body, e.target) && !$.contains(this.$el.get(0), e.target) && !$(e.target).closest('.modal-dialog').length) {
            if (this.getFieldView('post') && this.getFieldView('post').$element.val() === '') {
              if (!(this.model.get('attachmentsIds') || []).length) {
                this.disablePostingMode();
              }
            }
          }
        });
      }
      this.postingMode = true;
    }
    afterRender() {
      this.$postButton = this.$el.find('button.post');
      const postView = this.getFieldView('post');
      if (postView) {
        this.stopListening(postView, 'add-files');
        this.listenTo(postView, 'add-files', files => {
          this.enablePostingMode();
          const attachmentsView = /** @type module:views/fields/attachment-multiple */
          this.getFieldView('attachments');
          if (!attachmentsView) {
            return;
          }
          attachmentsView.uploadFiles(files);
        });
      }
    }
    validate() {
      let notValid = super.validate();
      const message = this.model.get('post') || '';
      if (message.trim() === '' && !(this.model.get('attachmentsIds') || []).length) {
        notValid = true;
      }
      return notValid;
    }
    post() {
      this.save();
    }
    beforeBeforeSave() {
      this.disablePostButton();
    }
    beforeSave() {
      Espo.Ui.notifyWait();
    }
    afterSave() {
      Espo.Ui.success(this.translate('Posted'));
      if (this.options.interactiveMode) {
        this.model.clear();
        this.model.set('targetType', 'self');
        this.model.set('type', 'Post');
        this.disablePostingMode();
        this.enablePostButton();
        this.getFieldView('post').$element.prop('rows', 1);
      }
    }
    afterNotValid() {
      this.enablePostButton();
    }
    disablePostButton() {
      this.trigger('disable-post-button');
      this.$postButton.addClass('disable').attr('disabled', 'disabled');
    }
    enablePostButton() {
      this.trigger('enable-post-button');
      this.$postButton.removeClass('disable').removeAttr('disabled');
    }
  }
  var _default = _exports.default = EditStreamView;
});

define("views/stream/record/row-actions/update", ["exports", "views/stream/record/row-actions/default"], function (_exports, _default2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _default2 = _interopRequireDefault(_default2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class StreamUpdateNoteRowActionsView extends _default2.default {
    setup() {
      super.setup();
      this.addActionHandler('restore', () => this.actionRestore());
    }
    getActionList() {
      const list = super.getActionList();
      if (this.hasRestore()) {
        list.unshift({
          label: 'Restore',
          data: {
            id: this.model.id
          },
          action: 'restore'
        });
      }
      return list;
    }
    hasRestore() {
      if (this.options.listType !== 'listAuditLog') {
        return false;
      }
      const entityType = this.model.get('parentType');
      if (this.getMetadata().get(`clientDefs.${entityType}.editDisabled`)) {
        return false;
      }
      if (!this.getAcl().checkScope(entityType, 'edit')) {
        return false;
      }
      const fieldList = this.getFieldList();
      if (!fieldList.length) {
        return false;
      }
      for (const field of fieldList) {
        if (!this.getAcl().checkField(entityType, field, 'edit')) {
          return false;
        }
      }
      return true;
    }
    async actionRestore() {
      await this.confirm({
        message: this.translate('confirmRestoreFromAudit', 'messages'),
        confirmText: this.translate('Proceed')
      });
      const entityType = this.model.get('parentType');
      const entityId = this.model.get('parentId');
      this.getRouter().dispatch(entityType, 'edit', {
        id: entityId,
        attributes: this.getPreviousAttributes(),
        highlightFieldList: this.getFieldList()
      });
      this.getRouter().navigate(`#${entityType}/edit/${entityId}`, {
        trigger: false
      });
    }

    /**
     * @return {string[]}
     */
    getFieldList() {
      const data = /** @type {Record} */this.model.get('data') || {};
      return /** @type {string[]} */data.fields || [];
    }

    /**
     * @return {Record}
     */
    getPreviousAttributes() {
      const data = /** @type {Record} */this.model.get('data') || {};
      const attributes = /** @type {Record} */data.attributes || {};
      return attributes.was || {};
    }
  }
  var _default = _exports.default = StreamUpdateNoteRowActionsView;
});

define("views/stream/record/row-actions/detached", ["exports", "views/stream/record/row-actions/default"], function (_exports, _default2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _default2 = _interopRequireDefault(_default2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class _default extends _default2.default {
    isDetached = true;
  }
  _exports.default = _default;
});

define("views/stream/notes/update", ["exports", "views/stream/note"], function (_exports, _note) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _note = _interopRequireDefault(_note);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UpdateNoteStreamView extends _note.default {
    template = 'stream/notes/update';
    messageName = 'update';
    rowActionsView = 'views/stream/record/row-actions/update';
    data() {
      const fieldsString = this.fieldDataList.map(it => it.label).join(', ');
      return {
        ...super.data(),
        fieldDataList: this.fieldDataList,
        parentType: this.model.get('parentType'),
        iconHtml: this.getIconHtml(),
        fieldsString: fieldsString
      };
    }
    init() {
      if (this.getUser().isAdmin()) {
        this.isRemovable = true;
      }
      super.init();
    }
    setup() {
      this.addActionHandler('expandDetails', (e, target) => this.toggleDetails(e, target));
      this.createMessage();
      this.wait(true);
      this.getModelFactory().create(this.model.get('parentType'), model => {
        const modelWas = model;
        const modelBecame = model.clone();
        const data = this.model.get('data');
        data.attributes = data.attributes || {};
        modelWas.set(data.attributes.was);
        modelBecame.set(data.attributes.became);
        this.fieldDataList = [];
        const fields = this.fieldList = data.fields;
        fields.forEach(field => {
          const type = model.getFieldType(field) || 'base';
          const viewName = this.getMetadata().get(['entityDefs', model.entityType, 'fields', field, 'view']) || this.getFieldManager().getViewName(type);
          const attributeList = this.getFieldManager().getEntityTypeFieldAttributeList(model.entityType, field);
          let hasValue = false;
          for (const attribute of attributeList) {
            if (attribute in data.attributes.was) {
              hasValue = true;
              break;
            }
          }
          if (!hasValue) {
            this.fieldDataList.push({
              field: field,
              noValues: true,
              label: this.translate(field, 'fields', this.model.attributes.parentType)
            });
            return;
          }
          this.createView(field + 'Was', viewName, {
            model: modelWas,
            readOnly: true,
            defs: {
              name: field
            },
            mode: 'detail',
            inlineEditDisabled: true,
            selector: `.row[data-name="${field}"] .cell-was`
          });
          this.createView(field + 'Became', viewName, {
            model: modelBecame,
            readOnly: true,
            defs: {
              name: field
            },
            mode: 'detail',
            inlineEditDisabled: true,
            selector: `.row[data-name="${field}"] .cell-became`
          });
          this.fieldDataList.push({
            field: field,
            was: field + 'Was',
            became: field + 'Became',
            label: this.translate(field, 'fields', this.model.attributes.parentType)
          });
        });
        this.wait(false);
      });
    }

    /**
     * @param {MouseEvent} event
     * @param {HTMLElement} target
     */
    toggleDetails(event, target) {
      const $details = this.$el.find('> .details');
      const $fields = this.$el.find('> .fields');
      if ($details.hasClass('hidden')) {
        $details.removeClass('hidden');
        $fields.addClass('hidden');
        this.fieldList.forEach(field => {
          const wasField = this.getView(field + 'Was');
          const becomeField = this.getView(field + 'Became');
          if (wasField && becomeField) {
            wasField.trigger('panel-show-propagated');
            becomeField.trigger('panel-show-propagated');
          }
        });
        $(target).find('span').removeClass('fa-chevron-down').addClass('fa-chevron-up');
        return;
      }
      $details.addClass('hidden');
      $fields.removeClass('hidden');
      $(target).find('span').addClass('fa-chevron-down').removeClass('fa-chevron-up');
    }
  }
  var _default = _exports.default = UpdateNoteStreamView;
});

define("views/stream/notes/unrelate", ["exports", "views/stream/notes/relate"], function (_exports, _relate) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _relate = _interopRequireDefault(_relate);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UnrelateNoteStreamView extends _relate.default {
    template = 'stream/notes/create-related';
    messageName = 'unrelate';
  }
  var _default = _exports.default = UnrelateNoteStreamView;
});

define("views/stream/notes/status", ["exports", "views/stream/note"], function (_exports, _note) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _note = _interopRequireDefault(_note);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class StatusNoteStreamView extends _note.default {
    template = 'stream/notes/status';
    messageName = 'status';
    data() {
      return {
        ...super.data(),
        style: this.style,
        statusText: this.statusText,
        iconHtml: this.getIconHtml()
      };
    }
    init() {
      if (this.getUser().isAdmin()) {
        this.isRemovable = true;
      }
      super.init();
    }
    setup() {
      const data = this.model.get('data');
      const field = data.field;
      const value = data.value;
      this.style = data.style || 'default';
      this.statusText = this.getLanguage().translateOption(value, field, this.model.get('parentType'));
      let fieldLabel = this.translate(field, 'fields', this.model.get('parentType'));
      if (!this.isToUpperCaseStringItems()) {
        fieldLabel = fieldLabel.toLowerCase();
      }
      this.messageData['field'] = fieldLabel;
      this.createMessage();
    }
  }
  var _default = _exports.default = StatusNoteStreamView;
});

define("views/stream/notes/post", ["exports", "views/stream/note", "views/stream/reactions"], function (_exports, _note, _reactions) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _note = _interopRequireDefault(_note);
  _reactions = _interopRequireDefault(_reactions);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class PostNoteStreamView extends _note.default {
    template = 'stream/notes/post';
    messageName = 'post';
    isEditable = true;
    isRemovable = true;
    data() {
      const data = super.data();
      data.showAttachments = !!(this.model.get('attachmentsIds') || []).length;
      data.showPost = !!this.model.get('post');
      data.isInternal = this.isInternal;
      data.isPinned = this.isThis && this.model.get('isPinned') && this.model.collection && !this.model.collection.pinnedList;
      return data;
    }
    setup() {
      this.addActionHandler('react', (e, target) => this.react(target.dataset.type));
      this.addActionHandler('unReact', (e, target) => this.unReact(target.dataset.type));
      this.createField('post', null, null, 'views/stream/fields/post');
      this.createField('attachments', 'attachmentMultiple', {}, 'views/stream/fields/attachment-multiple', {
        previewSize: this.options.isNotification || this.options.isUserStream ? 'small' : 'medium'
      });
      this.isInternal = this.model.get('isInternal');
      this.setupReactions();
      if (!this.model.get('post') && this.model.get('parentId')) {
        this.messageName = 'attach';
        if (this.isThis) {
          this.messageName += 'This';
        }
      }
      this.listenTo(this.model, 'change', () => {
        if (this.model.hasChanged('post') || this.model.hasChanged('attachmentsIds')) {
          this.reRender();
        }
      });
      if (this.messageName === 'postThis') {
        this.messageTemplate = '{user}';
      }
      if (this.model.get('parentId')) {
        this.createMessage();
        return;
      }
      if (this.model.get('isGlobal')) {
        this.messageName = 'postTargetAll';
        this.createMessage();
        return;
      }
      if (this.model.has('teamsIds') && this.model.get('teamsIds').length) {
        const teamIdList = this.model.get('teamsIds');
        const teamNameHash = this.model.get('teamsNames') || {};
        this.messageName = 'postTargetTeam';
        if (teamIdList.length > 1) {
          this.messageName = 'postTargetTeams';
        }
        const teamHtmlList = [];
        teamIdList.forEach(teamId => {
          const teamName = teamNameHash[teamId];
          if (!teamName) {
            return;
          }
          teamHtmlList.push($('<a>').attr('href', '#Team/view/' + teamId).text(teamName).get(0).outerHTML);
        });
        this.messageData['html:target'] = teamHtmlList.join(', ');
        this.createMessage();
        return;
      }
      if (this.model.has('portalsIds') && this.model.get('portalsIds').length) {
        const portalIdList = this.model.get('portalsIds');
        const portalNameHash = this.model.get('portalsNames') || {};
        this.messageName = 'postTargetPortal';
        if (portalIdList.length > 1) {
          this.messageName = 'postTargetPortals';
        }
        const portalHtmlList = [];
        portalIdList.forEach(portalId => {
          const portalName = portalNameHash[portalId];
          if (!portalName) {
            return;
          }
          portalHtmlList.push($('<a>').attr('href', '#Portal/view/' + portalId).text(portalName).get(0).outerHTML);
        });
        this.messageData['html:target'] = portalHtmlList.join(', ');
        this.createMessage();
        return;
      }
      if (!this.model.has('usersIds') || !this.model.get('usersIds').length) {
        this.createMessage();
        return;
      }
      const userIdList = this.model.get('usersIds');
      const userNameHash = this.model.get('usersNames') || {};
      this.messageName = 'postTarget';
      if (userIdList.length === 1 && userIdList[0] === this.model.get('createdById')) {
        this.messageName = 'postTargetSelf';
        this.createMessage();
        return;
      }
      const userHtmlList = [];
      userIdList.forEach(userId => {
        if (userId === this.getUser().id) {
          this.messageName = 'postTargetYou';
          if (userIdList.length > 1) {
            if (userId === this.model.get('createdById')) {
              this.messageName = 'postTargetSelfAndOthers';
            } else {
              this.messageName = 'postTargetYouAndOthers';
            }
          }
          return;
        }
        if (userId === this.model.get('createdById')) {
          this.messageName = 'postTargetSelfAndOthers';
          return;
        }
        const userName = userNameHash[userId];
        if (!userName) {
          return;
        }
        userHtmlList.push($('<a>').attr('href', '#User/view/' + userId).attr('data-scope', 'User').attr('data-id', userId).text(userName).get(0).outerHTML);
      });
      this.messageData['html:target'] = userHtmlList.join(', ');
      this.createMessage();
    }

    /**
     * @private
     * @param {string} type
     */
    async react(type) {
      Espo.Ui.notifyWait();
      const previousMyReactions = this.model.attributes.myReactions;
      const previousReactionCounts = this.model.attributes.reactionCounts;
      const reactionCounts = {
        ...previousReactionCounts
      };
      if (!(type in reactionCounts)) {
        reactionCounts[type] = 0;
      }
      reactionCounts[type]++;
      this.model.set({
        myReactions: [type],
        reactionCounts: reactionCounts
      }, {
        userReaction: true
      });
      try {
        await Espo.Ajax.postRequest(`Note/${this.model.id}/myReactions/${type}`);
      } catch (e) {
        this.model.set({
          myReactions: previousMyReactions,
          reactionCounts: previousReactionCounts
        }, {
          userReaction: true
        });
        return;
      }
      Espo.Ui.success(this.translate('Reacted') + ' · ' + this.translate(type, 'reactions'));
      await this.model.fetch({
        userReaction: true,
        keepRowActions: true
      });
    }

    /**
     * @private
     * @param {string} type
     */
    async unReact(type) {
      Espo.Ui.notifyWait();
      const previousMyReactions = this.model.attributes.myReactions;
      const previousReactionCounts = this.model.attributes.reactionCounts;
      const reactionCounts = {
        ...previousReactionCounts
      };
      if (!(type in reactionCounts)) {
        reactionCounts[type] = 0;
      }
      reactionCounts[type]--;
      this.model.set({
        myReactions: [],
        reactionCounts: reactionCounts
      }, {
        userReaction: true
      });
      try {
        await Espo.Ajax.deleteRequest(`Note/${this.model.id}/myReactions/${type}`);
      } catch (e) {
        this.model.set({
          myReactions: previousMyReactions,
          reactionCounts: previousReactionCounts
        }, {
          userReaction: true
        });
        return;
      }
      Espo.Ui.warning(this.translate('Reaction Removed'));
      await this.model.fetch({
        userReaction: true,
        keepRowActions: true
      });
    }

    /**
     * @private
     */
    setupReactions() {
      const view = new _reactions.default({
        model: this.model
      });
      this.assignView('reactions', view, '.reactions-container');
      this.listenTo(this.model, 'change:reactionCounts change:myReactions', () => view.reRenderWhenNoPopover());
    }
  }
  var _default = _exports.default = PostNoteStreamView;
});

define("views/stream/notes/mention-in-post", ["exports", "views/stream/note"], function (_exports, _note) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _note = _interopRequireDefault(_note);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class MentionInPostNoteStreamView extends _note.default {
    template = 'stream/notes/post';
    messageName = 'mentionInPost';
    data() {
      return {
        ...super.data(),
        showAttachments: !!(this.model.get('attachmentsIds') || []).length,
        showPost: !!this.model.get('post')
      };
    }
    setup() {
      if (this.model.get('post')) {
        this.createField('post', null, null, 'views/stream/fields/post');
      }
      if ((this.model.get('attachmentsIds') || []).length) {
        this.createField('attachments', 'attachmentMultiple', {}, 'views/stream/fields/attachment-multiple', {
          previewSize: this.options.isNotification ? 'small' : null
        });
      }
      this.messageData['mentioned'] = this.options.userId;
      if (!this.model.get('parentId')) {
        this.messageName = 'mentionInPostTarget';
      }
      if (!this.isUserStream || this.options.userId !== this.getUser().id) {
        this.createMessage();
        return;
      }
      if (this.model.get('parentId')) {
        this.messageName = 'mentionYouInPost';
        this.createMessage();
        return;
      }
      this.messageName = 'mentionYouInPostTarget';
      if (this.model.get('isGlobal')) {
        this.messageName = 'mentionYouInPostTargetAll';
        this.createMessage();
        return;
      }
      this.messageName = 'mentionYouInPostTarget';
      if (this.model.has('teamsIds') && this.model.get('teamsIds').length) {
        const teamIdList = this.model.get('teamsIds');
        const teamNameHash = this.model.get('teamsNames') || {};
        const teamHtmlList = [];
        teamIdList.forEach(teamId => {
          const teamName = teamNameHash[teamId];
          if (!teamName) {
            return;
          }
          teamHtmlList.push($('<a>').attr('href', '#Team/view/' + teamId).text(teamName).get(0).outerHTML);
        });
        this.messageData['html:target'] = teamHtmlList.join(', ');
        this.createMessage();
        return;
      }
      if (this.model.has('usersIds') && this.model.get('usersIds').length) {
        const userIdList = this.model.get('usersIds');
        const userNameHash = this.model.get('usersNames') || {};
        if (userIdList.length === 1 && userIdList[0] === this.model.get('createdById')) {
          this.messageName = 'mentionYouInPostTargetNoTarget';
          this.createMessage();
          return;
        }
        const userHtmlList = [];
        userIdList.forEach(userId => {
          const userName = userNameHash[userId];
          if (!userName) {
            return;
          }
          userHtmlList.push($('<a>').attr('href', '#User/view/' + userId).text(userName).get(0).outerHTML);
        });
        this.messageData['html:target'] = userHtmlList.join(', ');
        this.createMessage();
        return;
      }
      if (this.model.get('targetType') === 'self') {
        this.messageName = 'mentionYouInPostTargetNoTarget';
      }
      this.createMessage();
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = MentionInPostNoteStreamView;
});

define("views/stream/notes/email-sent", ["exports", "views/stream/notes/email-received"], function (_exports, _emailReceived) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _emailReceived = _interopRequireDefault(_emailReceived);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailSentNoteStreamView extends _emailReceived.default {
    isSystemAvatar = false;
    setupEmailMessage(data) {
      this.messageName = 'emailSent';
      this.messageData['by'] = $('<a>').attr('href', `#${data.personEntityType}/view/${data.personEntityId}`).text(data.personEntityName).attr('data-scope', data.personEntityType).attr('data-id', data.personEntityId);
    }
  }
  var _default = _exports.default = EmailSentNoteStreamView;
});

define("views/stream/notes/create", ["exports", "views/stream/note"], function (_exports, _note) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _note = _interopRequireDefault(_note);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/stream/notes/create */

  class CreateNoteStreamView extends _note.default {
    template = 'stream/notes/create';
    assigned = false;
    messageName = 'create';
    isRemovable = false;
    data() {
      return {
        ...super.data(),
        statusText: this.statusText,
        statusStyle: this.statusStyle,
        iconHtml: this.getIconHtml()
      };
    }
    setup() {
      if (this.model.get('data')) {
        this.setupData();
      }
      this.createMessage();
    }

    /**
     * @typedef {{
     *    assignedUserId?: string,
     *     assignedUserName?: string,
     *     assignedUsers?: {id: string, name: string|null}[],
     *     statusField?: string,
     *     statusValue?: string|null,
     *     statusStyle?: string|null,
     * }} module:views/stream/notes/create~data
     */

    setupData() {
      /** @type {module:views/stream/notes/create~data} */
      const data = this.model.get('data') || {};
      this.setupUsersData();
      if (data.statusField) {
        const statusField = this.statusField = data.statusField;
        const statusValue = data.statusValue;
        this.statusStyle = data.statusStyle || 'default';
        this.statusText = this.getLanguage().translateOption(statusValue, statusField, this.model.attributes.parentType);
      }
    }
    setupUsersData() {
      /** @type {module:views/stream/notes/create~data} */
      const data = this.model.get('data') || {};
      this.assignedUserId = data.assignedUserId || null;
      this.assignedUserName = data.assignedUserName || data.assignedUserId || null;
      if (data.assignedUsers) {
        if (data.assignedUsers.length === 1) {
          this.assignedUserId = data.assignedUsers[0].id;
          this.assignedUserName = data.assignedUsers[0].name;
        } else if (data.assignedUsers.length > 1) {
          this.setupUsersDataMulti();
          if (this.isThis) {
            this.messageName += 'This';
          }
          return;
        }
      }
      this.messageData['assignee'] = $('<span>').addClass('nowrap name-avatar').append(this.getHelper().getAvatarHtml(this.assignedUserId, 'small', 16, 'avatar-link'), $('<a>').attr('href', `#User/view/${this.assignedUserId}`).text(this.assignedUserName).attr('data-scope', 'User').attr('data-id', this.assignedUserId));
      let isYou = false;
      if (this.isUserStream) {
        if (this.assignedUserId === this.getUser().id) {
          isYou = true;
        }
      }
      if (this.assignedUserId) {
        this.messageName = 'createAssigned';
        if (this.isThis) {
          this.messageName += 'This';
          if (this.assignedUserId === this.model.attributes.createdById) {
            this.messageName += 'Self';
          }
        } else {
          if (this.assignedUserId === this.model.attributes.createdById) {
            this.messageName += 'Self';
          } else if (isYou) {
            this.messageName += 'You';
          }
        }
      }
    }
    setupUsersDataMulti() {
      this.messageName = 'createAssigned';

      /** @type {module:views/stream/notes/create~data} */
      const data = this.model.get('data') || {};
      this.messageData['assignee'] = this.createUsersElement(data.assignedUsers);
    }

    /**
     * @private
     * @param {{id: string, name: ?string}[]} users
     * @return {HTMLElement}
     */
    createUsersElement(users) {
      const wrapper = document.createElement('span');
      users.forEach((it, i) => {
        const a = document.createElement('a');
        a.href = `#User/view/${it.id}`;
        a.text = it.name || it.id;
        a.dataset.id = it.id;
        a.dataset.scope = 'User';
        const span = document.createElement('span');
        span.className = 'nowrap name-avatar';
        span.innerHTML = this.getHelper().getAvatarHtml(it.id, 'small', 16, 'avatar-link');
        span.appendChild(a);
        wrapper.appendChild(span);
        if (i < users.length - 1) {
          wrapper.appendChild(document.createTextNode(', '));
        }
      });
      return wrapper;
    }
  }
  var _default = _exports.default = CreateNoteStreamView;
});

define("views/stream/notes/create-related", ["exports", "views/stream/note"], function (_exports, _note) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _note = _interopRequireDefault(_note);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class CreateRelatedNoteStreamView extends _note.default {
    template = 'stream/notes/create-related';
    messageName = 'createRelated';
    data() {
      return {
        ...super.data(),
        relatedTypeString: this.translateEntityType(this.entityType),
        iconHtml: this.getIconHtml(this.entityType, this.entityId)
      };
    }
    init() {
      if (this.getUser().isAdmin()) {
        this.isRemovable = true;
      }
      super.init();
    }
    setup() {
      const data = this.model.get('data') || {};
      this.entityType = this.model.get('relatedType') || data.entityType || null;
      this.entityId = this.model.get('relatedId') || data.entityId || null;
      this.entityName = this.model.get('relatedName') || data.entityName || null;
      this.messageData['relatedEntityType'] = this.translateEntityType(this.entityType);
      this.messageData['relatedEntity'] = $('<a>').attr('href', `#${this.entityType}/view/${this.entityId}`).text(this.entityName).attr('data-scope', this.entityType).attr('data-id', this.entityId);
      this.createMessage();
    }
  }
  var _default = _exports.default = CreateRelatedNoteStreamView;
});

define("views/stream/notes/assign", ["exports", "views/stream/note"], function (_exports, _note) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _note = _interopRequireDefault(_note);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/stream/notes/assign */

  class AssignNoteStreamView extends _note.default {
    template = 'stream/notes/assign';
    messageName = 'assign';
    init() {
      if (this.getUser().isAdmin()) {
        this.isRemovable = true;
      }
      super.init();
    }
    data() {
      return {
        ...super.data(),
        iconHtml: this.getIconHtml()
      };
    }

    /**
     * @typedef {{
     *    assignedUserId?: string,
     *     assignedUserName?: string,
     *     addedAssignedUsers?: {id: string, name: string|null}[],
     *     removedAssignedUsers?: {id: string, name: string|null}[],
     * }} module:views/stream/notes/assign~data
     */

    setup() {
      this.setupData();
      this.createMessage();
    }
    setupData() {
      /** @type {module:views/stream/notes/assign~data} */
      const data = this.model.get('data') || {};
      this.assignedUserId = data.assignedUserId || null;
      this.assignedUserName = data.assignedUserName || data.assignedUserId || null;
      if (data.addedAssignedUsers) {
        this.setupDataMulti(data);
        if (this.isThis) {
          this.messageName += 'This';
        }
        return;
      }
      this.messageData['assignee'] = $('<span>').addClass('nowrap name-avatar').append(this.getHelper().getAvatarHtml(data.assignedUserId, 'small', 16, 'avatar-link'), $('<a>').attr('href', `#User/view/${data.assignedUserId}`).text(this.assignedUserName).attr('data-scope', 'User').attr('data-id', data.assignedUserId));
      if (this.isUserStream) {
        if (this.assignedUserId) {
          if (this.assignedUserId === this.model.get('createdById')) {
            this.messageName += 'Self';
          } else {
            if (this.assignedUserId === this.getUser().id) {
              this.messageName += 'You';
            }
          }
        } else {
          this.messageName += 'Void';
        }
        return;
      }
      if (this.assignedUserId) {
        if (this.assignedUserId === this.model.get('createdById')) {
          this.messageName += 'Self';
        }
        return;
      }
      this.messageName += 'Void';
    }

    /**
     * @private
     * @param {module:views/stream/notes/assign~data} data
     */
    setupDataMulti(data) {
      this.messageName = 'assignMultiAdd';
      const added = data.addedAssignedUsers;
      const removed = data.removedAssignedUsers;
      if (!added || !removed) {
        return;
      }
      if (added.length && removed.length) {
        this.messageName = 'assignMultiAddRemove';
      } else if (removed.length) {
        this.messageName = 'assignMultiRemove';
      }
      if (added.length) {
        this.messageData['assignee'] = this.createUsersElement(added);
      }
      if (removed.length) {
        this.messageData['removedAssignee'] = this.createUsersElement(removed);
      }
    }

    /**
     * @private
     * @param {{id: string, name: ?string}[]} users
     * @return {HTMLElement}
     */
    createUsersElement(users) {
      const wrapper = document.createElement('span');
      users.forEach((it, i) => {
        const a = document.createElement('a');
        a.href = `#User/view/${it.id}`;
        a.text = it.name || it.id;
        a.dataset.id = it.id;
        a.dataset.scope = 'User';
        const span = document.createElement('span');
        span.className = 'nowrap name-avatar';
        span.innerHTML = this.getHelper().getAvatarHtml(it.id, 'small', 16, 'avatar-link');
        span.appendChild(a);
        wrapper.appendChild(span);
        if (i < users.length - 1) {
          wrapper.appendChild(document.createTextNode(', '));
        }
      });
      return wrapper;
    }
  }
  var _default = _exports.default = AssignNoteStreamView;
});

define("views/stream/modals/create-post", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class CreatePostModalView extends _modal.default {
    templateContent = '<div class="record">{{{record}}}</div>';
    shortcutKeys = {
      'Control+Enter': 'post'
    };
    setup() {
      this.headerText = this.translate('Create Post');
      this.buttonList = [{
        name: 'post',
        label: 'Post',
        style: 'primary',
        title: 'Ctrl+Enter',
        onClick: () => this.post()
      }, {
        name: 'cancel',
        label: 'Cancel',
        title: 'Esc',
        onClick: dialog => {
          dialog.close();
        }
      }];
      this.wait(true);
      this.getModelFactory().create('Note', model => {
        this.createView('record', 'views/stream/record/edit', {
          model: model,
          selector: '.record'
        }, view => {
          this.listenTo(view, 'after:save', () => {
            this.trigger('after:save');
          });
          this.listenTo(view, 'disable-post-button', () => this.disableButton('post'));
          this.listenTo(view, 'enable-post-button', () => this.enableButton('post'));
        });
        this.wait(false);
      });
    }

    /**
     * @return {module:views/record/edit}
     */
    getRecordView() {
      return this.getView('record');
    }
    post() {
      this.getRecordView().save();
    }
  }
  var _default = _exports.default = CreatePostModalView;
});

define("views/stream/fields/post", ["exports", "views/fields/text"], function (_exports, _text) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _text = _interopRequireDefault(_text);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class StreamPostFieldView extends _text.default {
    data() {
      const data = super.data();
      if (this.isDetailMode() || this.isListMode()) {
        data.htmlValue = this.getTransformedValue();
      }
      return data;
    }

    /**
     * @private
     * @return {Handlebars.SafeString|string}
     */
    getTransformedValue() {
      let text = super.getValueForDisplay();
      if (typeof text !== 'string' && !(text instanceof String)) {
        return '';
      }

      /** @type {Record} */
      const data = this.model.attributes.data || {};
      const mentionData = /** @type {Record.<string, {id: string, name: string}>} */
      data.mentions || {};
      const items = Object.keys(mentionData).sort((a, b) => b.length - a.length);
      if (!items.length) {
        return this.getHelper().transformMarkdownText(text);
      }
      items.forEach(item => {
        const name = mentionData[item].name;
        const id = mentionData[item].id;
        const part = `[${name}](#User/view/${id})`;
        text = text.replace(new RegExp(item, 'g'), part);
      });
      let html = this.getHelper().transformMarkdownText(text).toString();
      const body = new DOMParser().parseFromString(html, 'text/html').body;
      items.forEach(item => {
        const id = mentionData[item].id;
        const url = `#User/view/${id}`;
        const avatarHtml = this.getHelper().getAvatarHtml(id, 'small', 16, 'avatar-link');
        if (!avatarHtml) {
          return;
        }
        const img = new DOMParser().parseFromString(avatarHtml, 'text/html').body.childNodes[0];
        body.querySelectorAll(`a[href="${url}"]`).forEach(a => {
          if (id === this.getUser().id) {
            a.classList.add('text-warning');
          }
          const span = document.createElement('span');
          span.classList.add('nowrap', 'name-avatar');
          span.append(img.cloneNode());
          a.parentNode.replaceChild(span, a);
          span.append(a);
        });
      });
      html = body.innerHTML;
      return html;
    }
  }
  _exports.default = StreamPostFieldView;
});

define("views/stream/fields/attachment-multiple", ["exports", "views/fields/attachment-multiple"], function (_exports, _attachmentMultiple) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _attachmentMultiple = _interopRequireDefault(_attachmentMultiple);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _attachmentMultiple.default {
    showPreviews = true;
    showPreviewsInListMode = true;
  }
  _exports.default = _default;
});

define("views/site-portal/navbar", ["exports", "views/site/navbar"], function (_exports, _navbar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _navbar = _interopRequireDefault(_navbar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _navbar.default {
    getLogoSrc() {
      const companyLogoId = this.getConfig().get('companyLogoId');
      if (!companyLogoId) {
        return this.getBasePath() + (this.getThemeManager().getParam('logo') || 'client/img/logo.svg');
      }
      return `${this.getBasePath()}?entryPoint=LogoImage&id=${companyLogoId}&t=${companyLogoId}`;
    }
    getTabList() {
      const tabList = [...(this.getConfig().get('tabList') || [])];
      if (this.getThemeManager().getParam('navbarIsVertical') || tabList.length) {
        tabList.unshift('Home');
      }
      return tabList;
    }
  }
  _exports.default = _default;
});

define("views/site-portal/master", ["exports", "views/site/master"], function (_exports, _master) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _master = _interopRequireDefault(_master);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _master.default {
    views = {
      header: {
        id: 'header',
        view: 'views/site-portal/header'
      },
      main: {
        id: 'main',
        view: false
      },
      footer: {
        fullSelector: 'body > footer',
        view: 'views/site/footer'
      }
    };
    afterRender() {
      super.afterRender();
      this.element.querySelector('#main').classList.add('main-portal');

      //this.$el.find('#main').addClass('main-portal');
    }
  }
  _exports.default = _default;
});

define("views/site-portal/header", ["exports", "views/site/header"], function (_exports, _header) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _header = _interopRequireDefault(_header);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _header.default {
    navbarView = 'views/site-portal/navbar';
    customViewPath = ['clientDefs', 'App', 'portalNavbarView'];
  }
  _exports.default = _default;
});

define("views/site/footer", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class FooterSiteView extends _view.default {
    template = 'site/footer';
  }
  var _default = _exports.default = FooterSiteView;
});

define("views/site/navbar/quick-create", ["exports", "view", "helpers/record-modal"], function (_exports, _view, _recordModal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _recordModal = _interopRequireDefault(_recordModal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class QuickCreateNavbarView extends _view.default {
    templateContent = `
        <a
            id="nav-quick-create-dropdown"
            class="dropdown-toggle"
            data-toggle="dropdown"
            role="button"
            tabindex="0"
            title="{{translate 'Create'}}"
        ><i class="fas fa-plus icon"></i></a>
        <ul class="dropdown-menu" role="menu" aria-labelledby="nav-quick-create-dropdown">
            <li class="dropdown-header">{{translate 'Create'}}</li>
            {{#each list}}
                <li><a
                    href="#{{./this}}/create"
                    data-name="{{./this}}"
                    data-action="quickCreate"
                >{{translate this category='scopeNames'}}</a></li>
            {{/each}}
        </ul>
    `;
    data() {
      return {
        list: this.list
      };
    }
    setup() {
      this.addActionHandler('quickCreate', (e, element) => {
        e.preventDefault();
        this.processCreate(element.dataset.name);
      });
      const scopes = this.getMetadata().get('scopes') || {};

      /** @type {string[]} */
      const list = this.getConfig().get('quickCreateList') || [];
      this.list = list.filter(scope => {
        if (!scopes[scope]) {
          return false;
        }
        if ((scopes[scope] || {}).disabled) {
          return;
        }
        if ((scopes[scope] || {}).acl) {
          return this.getAcl().check(scope, 'create');
        }
        return true;
      });
    }
    isAvailable() {
      return this.list.length > 0;
    }

    /**
     * @private
     * @param {string} scope
     */
    async processCreate(scope) {
      Espo.Ui.notifyWait();
      const type = this.getMetadata().get(`clientDefs.${scope}.quickCreateModalType`);
      if (type) {
        const viewName = this.getMetadata().get(`clientDefs.${scope}.modalViews.${type}`);
        if (viewName) {
          const view = await this.createView('modal', viewName, {
            scope: scope
          });
          await view.render();
          Espo.Ui.notify();
          return;
        }
      }
      const helper = new _recordModal.default();
      await helper.showCreate(this, {
        entityType: scope
      });
    }
  }
  var _default = _exports.default = QuickCreateNavbarView;
});

define("views/search/filter", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/search/filter */

  class FilterView extends _view.default {
    template = 'search/filter';
    data() {
      return {
        name: this.name,
        scope: this.model.entityType,
        notRemovable: this.options.notRemovable
      };
    }
    setup() {
      const name = this.name = this.options.name;
      let type = this.model.getFieldType(name);
      if (!type && name === 'id') {
        type = 'id';
      }
      if (type) {
        const viewName = this.model.getFieldParam(name, 'view') || this.getFieldManager().getViewName(type);
        this.createView('field', viewName, {
          mode: 'search',
          model: this.model,
          selector: '.field',
          defs: {
            name: name
          },
          searchParams: this.options.params
        }, view => {
          this.listenTo(view, 'change', () => {
            this.trigger('change');
          });
          this.listenTo(view, 'search', () => {
            this.trigger('search');
          });
        });
      }
    }

    /**
     * @return {module:views/fields/base}
     */
    getFieldView() {
      return this.getView('field');
    }
    populateDefaults() {
      const view = this.getView('field');
      if (!view) {
        return;
      }
      if (!('populateSearchDefaults' in view)) {
        return;
      }
      view.populateSearchDefaults();
    }
  }
  var _default = _exports.default = FilterView;
});

define("views/record/panel-actions", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class PanelActionsView extends _view.default {
    template = 'record/panel-actions';
    data() {
      return {
        defs: this.options.defs,
        buttonList: this.getButtonList(),
        actionList: this.getActionList(),
        entityType: this.options.entityType,
        scope: this.options.scope
      };
    }
    setup() {
      this.buttonList = this.options.defs.buttonList || [];
      this.actionList = this.options.defs.actionList || [];
      this.defs = this.options.defs;
    }
    getButtonList() {
      const list = [];
      this.buttonList.forEach(item => {
        if (item.hidden) {
          return;
        }
        list.push(item);
      });
      return list;
    }
    getActionList() {
      return this.actionList.filter(item => !item.hidden).map(item => {
        item = Espo.Utils.clone(item);
        if (item.action) {
          item.data = Espo.Utils.clone(item.data || {});
          item.data.panel = this.options.defs.name;
        }
        return item;
      });
    }
  }
  var _default = _exports.default = PanelActionsView;
});

define("views/record/merge", ["exports", "view", "jquery"], function (_exports, _view, _jquery) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _jquery = _interopRequireDefault(_jquery);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class MergeRecordView extends _view.default {
    template = 'record/merge';
    scope = '';
    events = {
      /** @this MergeRecordView */
      'change input[type="radio"][name="check-all"]': function (e) {
        e.stopPropagation();
        const id = e.currentTarget.value;
        (0, _jquery.default)('input[data-id="' + id + '"]').prop('checked', true);
      },
      /** @this MergeRecordView */
      'click button[data-action="cancel"]': function () {
        this.getRouter().navigate('#' + this.scope, {
          trigger: true
        });
      },
      /** @this MergeRecordView */
      'click button[data-action="merge"]': function () {
        const id = (0, _jquery.default)('input[type="radio"][name="check-all"]:checked').val();
        let model;
        this.models.forEach(m => {
          if (m.id === id) {
            model = m;
          }
        });
        const attributes = {};
        (0, _jquery.default)('input.field-radio:checked').each((i, el) => {
          const field = el.name;
          const id = (0, _jquery.default)(el).attr('data-id');
          if (model.id === id) {
            return;
          }
          const fieldType = model.getFieldParam(field, 'type');
          const fields = this.getFieldManager().getActualAttributeList(fieldType, field);
          let modelFrom;
          this.models.forEach(itemModel => {
            if (itemModel.id === id) {
              modelFrom = itemModel;
            }
          });
          fields.forEach(field => {
            attributes[field] = modelFrom.get(field);
          });
        });
        Espo.Ui.notifyWait();
        const sourceIdList = this.models.filter(m => m.id !== model.id).map(m => m.id);
        Espo.Ajax.postRequest('Action', {
          entityType: this.scope,
          action: 'merge',
          id: model.id,
          data: {
            sourceIdList: sourceIdList,
            attributes: attributes
          }
        }).then(() => {
          Espo.Ui.success(this.translate('Merged'), {
            suppress: true
          });
          this.getRouter().navigate('#' + this.scope + '/view/' + model.id, {
            trigger: true
          });
          if (this.collection) {
            this.collection.fetch();
          }
        });
      }
    };
    data() {
      const rows = [];
      this.fields.forEach(field => {
        const o = {
          name: field,
          scope: this.scope
        };
        o.columns = [];
        this.models.forEach(model => {
          o.columns.push({
            id: model.id,
            fieldVariable: model.id + '-' + field,
            isReadOnly: this.readOnlyFields[field] || false
          });
        });
        rows.push(o);
      });
      return {
        rows: rows,
        modelList: this.models,
        scope: this.scope,
        hasCreatedAt: this.hasCreatedAt,
        width: Math.round((80 - this.models.length * 5) / this.models.length * 10) / 10,
        dataList: this.getDataList()
      };
    }
    afterRender() {
      (0, _jquery.default)('input[data-id="' + this.models[0].id + '"]').prop('checked', true);
    }
    setup() {
      this.scope = this.options.models[0].name;
      this.models = this.options.models;
      const fieldManager = this.getFieldManager();
      let differentFieldList = [];
      const fieldsDefs = this.models[0].defs.fields;
      this.readOnlyFields = {};
      for (const field in fieldsDefs) {
        const type = fieldsDefs[field].type;
        if (type === 'linkMultiple') {
          continue;
        }
        if (fieldsDefs[field].disabled || fieldsDefs[field].utility || fieldsDefs[field].mergeDisabled) {
          continue;
        }
        if (field === 'createdAt' || field === 'modifiedAt') {
          continue;
        }
        if (fieldManager.isMergeable(type)) {
          const actualAttributeList = fieldManager.getActualAttributeList(type, field);
          let differs = false;
          actualAttributeList.forEach(field => {
            const values = [];
            this.models.forEach(model => {
              values.push(model.get(field));
            });
            const firstValue = values[0];
            values.forEach(value => {
              if (!_.isEqual(firstValue, value)) {
                differs = true;
              }
            });
          });
          if (differs) {
            differentFieldList.push(field);
            if (this.models[0].isFieldReadOnly(field)) {
              this.readOnlyFields[field] = true;
            }
          }
        }
      }
      differentFieldList.sort((v1, v2) => {
        return this.translate(v1, 'fields', this.scope).localeCompare(this.translate(v2, 'fields', this.scope));
      });
      differentFieldList = differentFieldList.sort((v1, v2) => {
        if (!this.readOnlyFields[v1] && this.readOnlyFields[v2]) {
          return -1;
        }
        return 1;
      });
      this.fields = differentFieldList;
      this.fields.forEach(field => {
        const type = this.models[0].getFieldParam(field, 'type');
        this.models.forEach(model => {
          const viewName = model.getFieldParam(field, 'view') || this.getFieldManager().getViewName(type);
          this.createView(model.id + '-' + field, viewName, {
            model: model,
            fullSelector: '.merge [data-id="' + model.id + '"] .field[data-name="' + field + '"]',
            defs: {
              name: field
            },
            mode: 'detail',
            readOnly: true
          });
        });
      });
      this.hasCreatedAt = this.getMetadata().get(['entityDefs', this.scope, 'fields', 'createdAt']);
      if (this.hasCreatedAt) {
        this.models.forEach(model => {
          this.createView(model.id + '-' + 'createdAt', 'views/fields/datetime', {
            model: model,
            fullSelector: '.merge [data-id="' + model.id + '"] .field[data-name="createdAt"]',
            defs: {
              name: 'createdAt'
            },
            mode: 'detail',
            readOnly: true
          });
        });
      }
    }
    getDataList() {
      const dataList = [];
      this.models.forEach(model => {
        const o = {};
        o.id = model.id;
        o.name = model.get('name');
        o.createdAtViewName = model.id + '-' + 'createdAt';
        dataList.push(o);
      });
      return dataList;
    }
  }
  var _default = _exports.default = MergeRecordView;
});

define("views/record/list-tree", ["exports", "views/record/list", "helpers/record-modal"], function (_exports, _list, _recordModal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  _recordModal = _interopRequireDefault(_recordModal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/list-tree */

  class ListTreeRecordView extends _list.default {
    template = 'record/list-tree';
    showMore = false;
    showCount = false;
    checkboxes = false;
    rowActionsView = false;
    presentationType = 'tree';
    header = false;
    listContainerEl = ' > .list > ul';
    checkAllResultDisabled = true;
    showRoot = false;
    massActionList = ['remove'];
    selectable = false;
    createDisabled = false;
    selectedData = null;
    level = 0;
    itemViewName = 'views/record/list-tree-item';
    expandToggleInactive = false;

    // noinspection JSCheckFunctionSignatures
    data() {
      const data = super.data();
      data.createDisabled = this.createDisabled;
      data.showRoot = this.showRoot;
      if (data.showRoot) {
        data.rootName = this.rootName || this.translate('Top Level');
      }
      if (this.level === 0 && this.selectable && (this.selectedData || {}).id === null) {
        data.rootIsSelected = true;
      }
      if (this.level === 0) {
        data.isExpanded = this.isExpanded;
      }
      data.noData = data.createDisabled && !data.rowDataList.length && !data.showRoot;
      data.expandToggleInactive = this.expandToggleInactive;
      data.hasExpandToggle = !this.getUser().isPortal();
      return data;
    }
    setup() {
      if ('selectable' in this.options) {
        this.selectable = this.options.selectable;
      }
      this.readOnly = this.options.readOnly;
      this.createDisabled = this.readOnly || this.options.createDisabled || this.createDisabled;
      this.isExpanded = this.options.isExpanded;
      if ('showRoot' in this.options) {
        this.showRoot = this.options.showRoot;
        if ('rootName' in this.options) {
          this.rootName = this.options.rootName;
        }
      }
      if ('level' in this.options) {
        this.level = this.options.level;
      }
      this.rootView = this.options.rootView || this;
      if (this.level === 0) {
        this.selectedData = {
          id: null,
          path: [],
          names: {}
        };
      }
      if ('selectedData' in this.options) {
        this.selectedData = this.options.selectedData;
      }
      super.setup();
      if (this.selectable) {
        this.on('select', o => {
          if (o.id) {
            this.$el.find('a.link[data-id="' + o.id + '"]').addClass('text-bold');
            if (this.level === 0) {
              this.$el.find('a.link').removeClass('text-bold');
              this.$el.find('a.link[data-id="' + o.id + '"]').addClass('text-bold');
              this.setSelected(o.id);
              o.selectedData = this.selectedData;
            }
          }
          if (this.level > 0) {
            this.getParentView().trigger('select', o);
          }
        });
      }
    }

    /**
     * @param {string|null} id
     */
    setSelected(id) {
      if (id === null) {
        this.selectedData.id = null;
      } else {
        this.selectedData.id = id;
      }
      this.rowList.forEach(key => {
        const view = /** @type module:views/record/list-tree-item */this.getView(key);
        if (view.model.id === id) {
          view.setIsSelected();
        } else {
          view.isSelected = false;
        }
        if (view.hasView('children')) {
          view.getChildrenView().setSelected(id);
        }
      });
    }
    buildRows(callback) {
      this.checkedList = [];
      this.rowList = [];
      if (this.collection.length > 0) {
        this.wait(true);
        const modelList = this.collection.models;
        const count = modelList.length;
        let built = 0;
        modelList.forEach(model => {
          const key = model.id;
          this.rowList.push(key);
          this.createView(key, this.itemViewName, {
            model: model,
            collection: this.collection,
            selector: this.getRowSelector(model.id),
            createDisabled: this.createDisabled,
            readOnly: this.readOnly,
            level: this.level,
            isSelected: model.id === this.selectedData.id,
            selectedData: this.selectedData,
            selectable: this.selectable,
            setViewBeforeCallback: this.options.skipBuildRows && !this.isRendered(),
            rootView: this.rootView
          }, () => {
            built++;
            if (built === count) {
              if (typeof callback === 'function') {
                callback();
              }
              this.wait(false);
            }
          });
        });
        return;
      }
      if (typeof callback === 'function') {
        callback();
      }
    }
    getRowSelector(id) {
      return 'li[data-id="' + id + '"]';
    }
    getCellSelector(model, item) {
      return `${this.getSelector() + this.getRowSelector(model.id)} span.cell[data-name="${item.name}"]`;
    }
    getCreateAttributes() {
      return {};
    }

    // noinspection JSUnusedGlobalSymbols
    actionCreate(data, e) {
      e.stopPropagation();
      const attributes = this.getCreateAttributes();
      let maxOrder = 0;
      this.collection.models.forEach(m => {
        if (m.get('order') > maxOrder) {
          maxOrder = m.get('order');
        }
      });
      attributes.order = maxOrder + 1;
      attributes.parentId = null;
      attributes.parentName = null;
      if (this.model) {
        attributes.parentId = this.model.id;
        attributes.parentName = this.model.attributes.name;
      }
      const scope = this.collection.entityType;
      const helper = new _recordModal.default();
      helper.showCreate(this, {
        entityType: scope,
        attributes: attributes,
        afterSave: model => {
          const collection = /** @type {import('collections/tree').default} collection */
          this.collection;
          model.set('childCollection', collection.createSeed());
          if (model.attributes.parentId !== attributes.parentId) {
            let v = this;
            while (1) {
              if (v.level) {
                v = v.getParentView().getParentView();
              } else {
                break;
              }
            }
            v.collection.fetch();
            return;
          }
          this.collection.fetch();
        }
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionSelectRoot() {
      this.trigger('select', {
        id: null
      });
      if (this.selectable) {
        this.$el.find('a.link').removeClass('text-bold');
        this.$el.find('a.link[data-action="selectRoot"]').addClass('text-bold');
        this.setSelected(null);
      }
    }
  }
  var _default = _exports.default = ListTreeRecordView;
});

define("views/record/list-tree-item", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/list-tree-item */

  class ListTreeRecordItemView extends _view.default {
    template = 'record/list-tree-item';
    isEnd = false;
    level = 0;
    listViewName = 'views/record/list-tree';
    data() {
      return {
        name: this.model.get('name'),
        isUnfolded: this.isUnfolded,
        showFold: this.isUnfolded && !this.isEnd,
        showUnfold: !this.isUnfolded && !this.isEnd,
        isEnd: this.isEnd,
        isSelected: this.isSelected,
        readOnly: this.readOnly
      };
    }
    events = {
      /** @this ListTreeRecordItemView */
      'click [data-action="unfold"]': function (e) {
        this.unfold();
        e.stopPropagation();
      },
      /** @this ListTreeRecordItemView */
      'click [data-action="fold"]': function (e) {
        this.fold();
        e.stopPropagation();
      },
      /** @this ListTreeRecordItemView */
      'click [data-action="remove"]': function (e) {
        this.actionRemove();
        e.stopPropagation();
      }
    };
    setIsSelected() {
      this.isSelected = true;
      this.selectedData.id = this.model.id;
      const path = this.selectedData.path;
      const names = this.selectedData.names;
      path.length = 0;
      let view = this;
      while (1) {
        path.unshift(view.model.id);
        names[view.model.id] = view.model.get('name');
        if (view.getParentListView().level) {
          view = view.getParentView().getParentView();
        } else {
          break;
        }
      }
    }
    setup() {
      if ('level' in this.options) {
        this.level = this.options.level;
      }
      if ('isSelected' in this.options) {
        this.isSelected = this.options.isSelected;
      }
      if ('selectedData' in this.options) {
        this.selectedData = this.options.selectedData;
      }
      this.readOnly = this.options.readOnly;
      if ('createDisabled' in this.options) {
        this.createDisabled = this.options.createDisabled;
      }
      if (this.readOnly) {
        this.createDisabled = true;
      }
      this.rootView = this.options.rootView;
      this.scope = this.model.entityType;
      this.isUnfolded = false;
      const childCollection = this.model.get('childCollection');
      if (childCollection && childCollection.length === 0 || this.model.isEnd) {
        if (this.createDisabled) {
          this.isEnd = true;
        }
      } else if (childCollection) {
        childCollection.models.forEach(model => {
          if (~this.selectedData.path.indexOf(model.id)) {
            this.isUnfolded = true;
          }
        });
        if (this.isUnfolded) {
          this.createChildren();
        }
      }
      this.on('select', o => {
        this.getParentListView().trigger('select', o);
      });
    }

    /**
     * @return {module:views/record/list-tree}
     */
    getParentListView() {
      return /** @type module:views/record/list-tree */this.getParentView();
    }
    createChildren() {
      const childCollection = this.model.get('childCollection');
      let callback = null;
      if (this.isRendered()) {
        callback = view => {
          this.listenToOnce(view, 'after:render', () => {
            this.trigger('children-created');
          });
          view.render();
        };
      }
      this.createView('children', this.listViewName, {
        collection: childCollection,
        selector: '> .children',
        createDisabled: this.options.createDisabled,
        readOnly: this.options.readOnly,
        level: this.level + 1,
        selectedData: this.selectedData,
        model: this.model,
        selectable: this.options.selectable,
        rootView: this.rootView
      }, callback);
    }
    checkLastChildren() {
      Espo.Ajax.getRequest(this.collection.entityType + '/action/lastChildrenIdList', {
        parentId: this.model.id
      }).then(idList => {
        const childrenView = this.getChildrenView();
        idList.forEach(id => {
          const model = this.model.get('childCollection').get(id);
          if (model) {
            model.isEnd = true;
          }
          const itemView = childrenView.getView(id);
          if (!itemView) {
            return;
          }
          itemView.isEnd = true;
          itemView.afterIsEnd();
        });
        this.model.lastAreChecked = true;
      });
    }
    unfold() {
      if (this.createDisabled) {
        this.once('children-created', () => {
          if (!this.model.lastAreChecked) {
            this.checkLastChildren();
          }
        });
      }
      const childCollection = this.model.get('childCollection');
      if (childCollection !== null) {
        this.createChildren();
        this.isUnfolded = true;
        this.afterUnfold();
        this.trigger('after:unfold');
        return;
      }
      this.getCollectionFactory().create(this.scope, collection => {
        collection.url = this.collection.url;
        collection.parentId = this.model.id;
        Espo.Ui.notifyWait();
        this.listenToOnce(collection, 'sync', () => {
          Espo.Ui.notify(false);
          this.model.set('childCollection', collection);
          this.createChildren();
          this.isUnfolded = true;
          if (collection.length || !this.createDisabled) {
            this.afterUnfold();
            this.trigger('after:unfold');
          } else {
            this.isEnd = true;
            this.afterIsEnd();
          }
        });
        collection.fetch();
      });
    }
    fold() {
      this.clearView('children');
      this.isUnfolded = false;
      this.afterFold();
    }
    afterRender() {
      if (this.isUnfolded) {
        this.afterUnfold();
      } else {
        this.afterFold();
      }
      if (this.isEnd) {
        this.afterIsEnd();
      }
      if (!this.readOnly) {
        const $remove = this.$el.find('> .cell [data-action="remove"]');
        this.$el.find('> .cell').on('mouseenter', function () {
          $remove.removeClass('hidden');
        });
        this.$el.find('> .cell').on('mouseleave', function () {
          $remove.addClass('hidden');
        });
      }
    }
    afterFold() {
      this.$el.find('a[data-action="fold"][data-id="' + this.model.id + '"]').addClass('hidden');
      this.$el.find('a[data-action="unfold"][data-id="' + this.model.id + '"]').removeClass('hidden');
      this.$el.find(' > .children').addClass('hidden');
    }
    afterUnfold() {
      this.$el.find('a[data-action="unfold"][data-id="' + this.model.id + '"]').addClass('hidden');
      this.$el.find('a[data-action="fold"][data-id="' + this.model.id + '"]').removeClass('hidden');
      this.$el.find(' > .children').removeClass('hidden');
    }
    afterIsEnd() {
      this.$el.find('a[data-action="unfold"][data-id="' + this.model.id + '"]').addClass('hidden');
      this.$el.find('a[data-action="fold"][data-id="' + this.model.id + '"]').addClass('hidden');
      this.$el.find('span[data-name="white-space"][data-id="' + this.model.id + '"]').removeClass('hidden');
      this.$el.find(' > .children').addClass('hidden');
    }
    getCurrentPath() {
      let pointer = this;
      const path = [];
      while (true) {
        path.unshift(pointer.model.id);
        if (pointer.getParentView() === this.rootView) {
          break;
        }
        pointer = pointer.getParentView().getParentView();
      }
      return path;
    }
    actionRemove() {
      this.confirm({
        message: this.translate('removeRecordConfirmation', 'messages', this.scope),
        confirmText: this.translate('Remove')
      }, () => {
        this.model.destroy({
          wait: true
        }).then(() => this.remove());
      });
    }

    /**
     * @return module:views/record/list-tree
     */
    getChildrenView() {
      return /** @type module:views/record/list-tree */this.getView('children');
    }
  }
  var _default = _exports.default = ListTreeRecordItemView;
});

define("views/record/list-pagination", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RecordListPagination extends _view.default {
    template = 'record/list-pagination';
    isComponent = true;
    data() {
      const total = this.collection.total;
      const offset = this.collection.offset;
      const length = this.collection.length;
      const next = this.collection.hasNextPage();
      const last = next && total >= 0;
      const from = offset + 1;
      const to = offset + length;
      const currentPageNumber = this.getCurrentPageNumber();
      const lastPageNumber = this.getLastPageNumber();
      const noTotal = !this.displayTotalCount || total < 0;
      return {
        hasGoToPage: lastPageNumber > 1 || total < 0,
        currentPageNumber: currentPageNumber,
        lastPageNumber: lastPageNumber,
        hasLastPageNumber: lastPageNumber > 1,
        total: this.getHelper().numberUtil.formatInt(total),
        from: this.getHelper().numberUtil.formatInt(from),
        to: this.getHelper().numberUtil.formatInt(to),
        previous: this.collection.hasPreviousPage(),
        next: next,
        last: last,
        noTotal: noTotal,
        noData: to === 0
      };
    }
    setup() {
      this.recordView = /** @type {import('views/record/list').default} */this.options.recordView;
      this.listenTo(this.collection, 'update', () => {
        if (!this.element) {
          // A hack. Prevents warnings in console.
          return;
        }
        this.reRender();
      });
      this.addHandler('change', 'input.page-input', (e, /** HTMLInputElement */input) => {
        if (input.value === '') {
          input.value = this.getCurrentPageNumber();
          return;
        }
        const result = this.goToNumber(parseInt(input.value));
        if (!result) {
          input.value = this.getCurrentPageNumber();
        }
      });
      this.addHandler('focus', 'input.page-input', (e, /** HTMLInputElement */input) => {
        input.select();
      });
      this.addHandler('input', 'input.page-input', (e, /** HTMLInputElement */input) => {
        input.value = input.value.replace(/[^0-9.]/g, '');
      });
      this.addHandler('click', '.page-input-group > .input-group-addon', e => {
        e.preventDefault();
        e.stopImmediatePropagation();
      });
      this.displayTotalCount = this.options.displayTotalCount;
    }

    /**
     * @return {number|null}
     */
    getCurrentPageNumber() {
      return Math.floor(this.collection.offset / this.collection.maxSize) + 1;
    }

    /**
     * @return {number|null}
     */
    getLastPageNumber() {
      return this.collection.total >= 0 ? Math.floor(this.collection.total / this.collection.maxSize) + 1 : null;
    }

    /**
     * @param {Number} number
     * @return {Promise|null}
     */
    goToNumber(number) {
      const offset = (number - 1) * this.collection.maxSize;
      if (this.collection.total >= 0 && offset > this.collection.total) {
        Espo.Ui.warning(this.translate('pageNumberIsOutOfBound', 'messages'));
        return null;
      }
      Espo.Ui.notifyWait();
      return this.collection.setOffset(offset).then(() => {
        Espo.Ui.notify(false);
        this.recordView.trigger('after:paginate');
      });
    }
  }
  var _default = _exports.default = RecordListPagination;
});

define("views/record/list-nested-categories", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/list-nested-categories */

  class ListNestedCategoriesRecordView extends _view.default {
    template = 'record/list-nested-categories';
    isLoading = false;
    events = {
      'click .action': function (e) {
        Espo.Utils.handleAction(this, e.originalEvent, e.currentTarget);
      }
    };

    /**
     * @type {import('collections/tree').default}
     */
    collection;
    constructor(options) {
      super(options);
      this.collection = options.collection;
    }

    /**
     * @type {import('collection').default}
     */
    itemCollection;

    /**
     * @type {boolean}
     */
    hasNavigationPanel;

    /**
     * @type {boolean}
     */
    isExpanded;

    /**
     * @protected
     * @type {string}
     */
    subjectEntityType;
    data() {
      const data = {};
      if (!this.isLoading) {
        data.list = this.getDataList();
      }
      data.scope = this.collection.entityType;
      data.isExpanded = this.isExpanded;
      data.isLoading = this.isLoading;
      data.currentId = this.collection.currentCategoryId;
      data.currentName = this.collection.currentCategoryName;
      data.categoryData = this.collection.categoryData;
      data.showFolders = !this.isExpanded;
      data.hasExpandedToggler = this.options.hasExpandedToggler;
      data.showEditLink = this.options.showEditLink;
      data.hasNavigationPanel = this.hasNavigationPanel;
      const categoryData = this.collection.categoryData || {};
      data.upperLink = categoryData.upperId ? '#' + this.subjectEntityType + '/list/categoryId=' + categoryData.upperId : '#' + this.subjectEntityType;
      if (this.options.primaryFilter) {
        const part = 'primaryFilter=' + this.getHelper().escapeString(this.options.primaryFilter);
        if (categoryData.upperId) {
          data.upperLink += '&' + part;
        } else {
          data.upperLink += '/list/' + part;
        }
      }
      data.isExpandedResult = data.isExpanded || this.itemCollection.data.textFilter || this.itemCollection.where && this.itemCollection.where.find(it => it.type === 'textFilter');
      return data;
    }

    /**
     * @private
     * @return {{
     *     id: string,
     *     name: string,
     *     recordCount: number,
     *     isEmpty: boolean,
     *     link: string,
     * }[]}
     */
    getDataList() {
      const list = [];
      this.collection.forEach(model => {
        let url = `#${this.subjectEntityType}/list/categoryId=${model.id}`;
        if (this.options.primaryFilter) {
          url += '&primaryFilter=' + this.getHelper().escapeString(this.options.primaryFilter);
        }
        const o = {
          id: model.id,
          name: model.get('name'),
          recordCount: model.get('recordCount'),
          isEmpty: model.get('isEmpty'),
          link: url
        };
        list.push(o);
      });
      return list;
    }
    setup() {
      this.isExpanded = this.options.isExpanded;
      this.subjectEntityType = this.options.subjectEntityType;
      this.hasNavigationPanel = this.options.hasNavigationPanel;
      this.itemCollection = this.options.itemCollection;
      this.listenTo(this.collection, 'sync', () => this.reRender());
      this.listenTo(this.itemCollection, 'sync', () => this.reRender());
    }

    // noinspection JSUnusedGlobalSymbols
    actionShowMore() {
      this.$el.find('.category-item.show-more').addClass('hidden');
      this.collection.fetch({
        remove: false,
        more: true
      });
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = ListNestedCategoriesRecordView;
});

define("views/record/kanban", ["exports", "views/record/list", "helpers/record-modal"], function (_exports, _list, _recordModal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  _recordModal = _interopRequireDefault(_recordModal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/kanban */

  /**
   * A kanban record view.
   */
  class KanbanRecordView extends _list.default {
    template = 'record/kanban';
    itemViewName = 'views/record/kanban-item';
    rowActionsView = 'views/record/row-actions/default-kanban';
    type = 'kanban';
    name = 'kanban';
    showCount = true;
    headerDisabled = false;
    layoutName = 'kanban';
    portalLayoutDisabled = false;
    minColumnWidthPx = 220;
    showMore = true;
    quickDetailDisabled = false;
    quickEditDisabled = false;
    _internalLayout = null;
    buttonsDisabled = false;
    backDragStarted = true;
    paginationDisabled = true;
    columnResize = false;

    /**
     * @private
     * @type {{
     *     list: Record[],
     *     total: number,
     *     name: string,
     *     label?: string,
     *     style?: string|null,
     * }[]}
     */
    groupRawDataList;

    /**
     * @private
     * @type {import('collection').default[]}
     */
    subCollectionList;

    /**
     * A button list.
     *
     * @protected
     * @type {module:views/record/list~button[]}
     */
    buttonList = [];

    /**
     * @private
     * @type {import('collection').default}
     */
    seedCollection;

    /**
     * Layout item definitions.
     *
     * @typedef module:views/record/kanban~layoutItemDefs
     * @type {Object}
     * @property {string} name A name (usually a field name).
     * @property {string} [view] An overridden field view name.
     * @property {boolean} [link] To use `listLink` mode (link to the detail view).
     * @property {'left'|'right'} [align] An alignment.
     * @property {boolean} [isLarge] Large.
     * @property {boolean} [isMuted] Muted.
     * @property {boolean} [hidden] Hidden by default.
     */

    /**
     * Kanban view options.
     *
     * @typedef {Record} module:views/record/kanban~options
     * @property {import('collection').default} collection A collection.
     * @property {module:views/record/kanban~layoutItemDefs[]} [listLayout] A layout.
     * @property {boolean} [keepCurrentRootUrl] Keep a current root URL.
     * @property {string|'kanban'} [type] A type.
     * @property {boolean} [rowActionsDisabled] Disable row actions.
     * @property {boolean} [buttonsDisabled] Disable buttons.
     * @property {Record} [rowActionsOptions] Row-actions options.
     * @property {string[]} [additionalRowActionList] Additional row-action list.
     * @property {import('helpers/list/settings').default} [settingsHelper] A settings helper.
     * @property {string} [layoutName] A layout name.
     * @property {boolean} [skipBuildRows] Do not build rows on initialization. Use when the collection will be fetched
     *    afterward.
     * @property {boolean} [rowActionsDisabled] Disable row actions.
     * @property {boolean} [displayTotalCount] Display total count.
     * @property {boolean} [showCount] To show a record count.
     * @property {boolean} [topBarDisabled] Disable the top bar.
     * @property {function(string, string[]): Promise} [onGroupOrder] On group order function.
     * @property {function(string): Promise<Record>} [getCreateAttributes] Get create attributes function.
     * @property {function(import('model').default): Promise} [groupChangeSaveHandler] Handles record saving after drop.
     * @property {function(string)} [createActionHandler] A create handler.
     * @property {string} [statusField] A status field.
     * @property {boolean} [canChangeGroup] Can change group.
     * @property {boolean} [canCreate] Can create.
     * @property {boolean} [canReOrder] Can re-order.
     * @property {boolean} [moveOverRowAction] Enable a move-over row action.
     */

    /**
     * @param {module:views/record/kanban~options} options Options.
     */
    constructor(options) {
      super(options);

      /** @private */
      this.onGroupOrder = options.onGroupOrder;
      /** @private */
      this.getCreateAttributes = options.getCreateAttributes;
      /** @private */
      this.createActionHandler = options.createActionHandler;
      /** @private */
      this.groupChangeSaveHandler = options.groupChangeSaveHandler;
    }
    events = {
      /** @this KanbanRecordView */
      'click a.link': function (e) {
        if (e.ctrlKey || e.metaKey || e.shiftKey) {
          return;
        }
        e.stopPropagation();
        if (!this.scope || this.selectable) {
          return;
        }
        e.preventDefault();
        const id = $(e.currentTarget).data('id');
        const model = this.collection.get(id);
        const scope = this.getModelScope(id);
        const options = {
          id: id,
          model: model
        };
        if (this.options.keepCurrentRootUrl) {
          options.rootUrl = this.getRouter().getCurrentUrl();
        }
        this.getRouter().navigate('#' + scope + '/view/' + id, {
          trigger: false
        });
        this.getRouter().dispatch(scope, 'view', options);
      },
      /** @this KanbanRecordView */
      'click [data-action="groupShowMore"]': function (e) {
        const $target = $(e.currentTarget);
        const group = $target.data('name');
        this.groupShowMore(group);
      },
      /** @this KanbanRecordView */
      'click .action': function (e) {
        Espo.Utils.handleAction(this, e.originalEvent, e.currentTarget, {
          actionItems: [...this.buttonList],
          className: 'list-action-item'
        });
      },
      /** @this KanbanRecordView */
      'mouseenter th.group-header': function (e) {
        if (!this.isCreatable) {
          return;
        }
        const group = $(e.currentTarget).attr('data-name');
        this.showPlus(group);
      },
      /** @this KanbanRecordView */
      'mouseleave th.group-header': function (e) {
        const group = $(e.currentTarget).attr('data-name');
        this.hidePlus(group);
      },
      /** @this KanbanRecordView */
      'click [data-action="createInGroup"]': function (e) {
        const group = $(e.currentTarget).attr('data-group');
        this.actionCreateInGroup(group);
      },
      /** @this KanbanRecordView */
      'mousedown .kanban-columns td': function (e) {
        if ($(e.originalEvent.target).closest('.item').length) {
          return;
        }
        this.initBackDrag(e.originalEvent);
      },
      /** @this KanbanRecordView */
      'auxclick a.link': function (e) {
        const isCombination = e.button === 1 && (e.ctrlKey || e.metaKey);
        if (!isCombination) {
          return;
        }
        const $target = $(e.currentTarget);
        const id = $target.attr('data-id');
        if (!id) {
          return;
        }
        if (this.quickDetailDisabled) {
          return;
        }
        const $quickView = $target.parent().closest(`[data-id="${id}"]`).find(`ul.list-row-dropdown-menu[data-id="${id}"] a[data-action="quickView"]`);
        if (!$quickView.length) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        this.actionQuickView({
          id: id
        });
      }
    };

    // noinspection JSCheckFunctionSignatures
    data() {
      const topBar = !this.options.topBarDisabled && (this.displayTotalCount || this.buttonList.length && !this.buttonsDisabled || !!this._listSettingsHelper);

      // noinspection JSValidateTypes
      return {
        scope: this.scope,
        header: this.header,
        topBar: topBar,
        showCount: this.showCount && this.collection.total > 0,
        buttonList: this.buttonList,
        displayTotalCount: this.displayTotalCount && this.collection.total >= 0 && !this._renderEmpty,
        totalCount: this.collection.total,
        groupDataList: this.groupDataList,
        minTableWidthPx: this.minColumnWidthPx * this.groupDataList.length,
        isEmptyList: this.collection.models.length === 0,
        totalCountFormatted: this.getNumberUtil().formatInt(this.collection.total),
        noDataDisabled: this._renderEmpty
      };
    }
    init() {
      /** @type {module:views/record/list~columnDefs[]|null} */
      this.listLayout = this.options.listLayout || this.listLayout;
      this.type = this.options.type || this.type;
      this.layoutName = this.options.layoutName || this.layoutName || this.type;
      this.rowActionsView = _.isUndefined(this.options.rowActionsView) ? this.rowActionsView : this.options.rowActionsView;
      if (this.massActionsDisabled && !this.selectable) {
        this.checkboxes = false;
      }
      this.rowActionsDisabled = this.options.rowActionsDisabled || this.rowActionsDisabled;
      if ('buttonsDisabled' in this.options) {
        this.buttonsDisabled = this.options.buttonsDisabled;
      }
    }

    /** @inheritDoc */
    getModelScope(id) {
      return this.scope;
    }

    /** @inheritDoc */
    setup() {
      if (typeof this.collection === 'undefined') {
        throw new Error('Collection has not been injected into Record.List view.');
      }
      this.listenTo(this.collection, 'sync', (c, response) => {
        this.subCollectionList = undefined;

        // noinspection JSUnresolvedReference
        this.groupRawDataList = response.groups;
      });
      this.layoutLoadCallbackList = [];
      this.entityType = this.collection.entityType || null;
      this.scope = this.options.scope || this.entityType;
      this.buttonList = Espo.Utils.clone(this.buttonList);
      if ('showCount' in this.options) {
        this.showCount = this.options.showCount;
      }
      this.displayTotalCount = this.showCount && this.getConfig().get('displayListViewRecordCount');
      this.minColumnWidthPx = this.getConfig().get('kanbanMinColumnWidth') || this.minColumnWidthPx;
      if ('displayTotalCount' in this.options) {
        this.displayTotalCount = this.options.displayTotalCount;
      }
      if (this.getUser().isPortal() && !this.portalLayoutDisabled && this.getMetadata().get(['clientDefs', this.scope, 'additionalLayouts', this.layoutName + 'Portal'])) {
        this.layoutName += 'Portal';
      }
      if ('canReOrder' in this.options) {
        this.orderDisabled = !this.options.canReOrder;
      } else {
        this.orderDisabled = this.getMetadata().get(['scopes', this.scope, 'kanbanOrderDisabled']);
        if (this.getUser().isPortal()) {
          this.orderDisabled = true;
        }
      }
      this.statusField = this.options.statusField || this.getMetadata().get(['scopes', this.scope, 'statusField']);
      if (!this.statusField) {
        throw new Error(`No status field for entity type '${this.scope}'.`);
      }
      this.seedCollection = this.collection.clone();
      this.seedCollection.reset();
      this.seedCollection.url = this.scope;
      this.seedCollection.maxSize = this.collection.maxSize;
      this.seedCollection.entityType = this.collection.entityType;
      this.seedCollection.orderBy = this.collection.defaultOrderBy;
      this.seedCollection.order = this.collection.defaultOrder;
      this.setupRowActionDefs();
      this.setupSettings();
      this.listenTo(this.collection, 'sync', () => {
        this._renderEmpty = false;
        this.buildRowsAndRender();
      });
      this.collection.listenTo(this.collection, 'change:' + this.statusField, this.onChangeGroup.bind(this), this);
      this.buildRows();
      this.on('remove', () => {
        $(window).off('resize.kanban-a-' + this.cid);
        $(window).off('scroll.kanban-' + this.cid);
        $(window).off('resize.kanban-' + this.cid);
      });
      if ('canChangeGroup' in this.options) {
        this.statusFieldIsEditable = this.options.canChangeGroup;
      } else {
        this.statusFieldIsEditable = this.getAcl().checkScope(this.entityType, 'edit') && !this.getAcl().getScopeForbiddenFieldList(this.entityType, 'edit').includes(this.statusField) && !this.getMetadata().get(['clientDefs', this.scope, 'editDisabled']) && !this.getMetadata().get(['entityDefs', this.entityType, 'fields', this.statusField, 'readOnly']);
      }
      if ('canCreate' in this.options) {
        this.isCreatable = this.options.canCreate;
      } else {
        this.isCreatable = this.statusFieldIsEditable && this.getAcl().check(this.entityType, 'create') && !this.getMetadata().get(`clientDefs.${this.scope}.createDisabled`);
      }

      /** @private */
      this.moveOverRowAction = true;
      if ('moveOverRowAction' in this.options) {
        this.moveOverRowAction = this.options.moveOverRowAction;
      }
      this._renderEmpty = this.options.skipBuildRows;
      this.wait(this.getHelper().processSetupHandlers(this, 'record/kanban'));

      /**
       * @private
       * @type {boolean}
       */
      this.hasStars = this.getMetadata().get(`scopes.${this.entityType}.stars`) || false;
    }
    afterRender() {
      const $window = $(window);
      this.$listKanban = this.$el.find('.list-kanban');
      this.$content = $('#content');
      this.$groupColumnList = this.$listKanban.find('.group-column-list');
      this.$container = this.$el.find('.list-kanban-container');
      $window.off('resize.kanban-a-' + this.cid);
      $window.on('resize.kanban-a-' + this.cid, () => this.adjustMinHeight());
      this.$container.on('scroll', () => this.syncHeadScroll());
      this.adjustMinHeight();
      if (this.statusFieldIsEditable) {
        this.initSortable();
      }
      this.initStickableHeader();
      this.$showMore = this.$el.find('.group-column .show-more');
      this.plusElementMap = {};
      this.groupDataList.forEach(item => {
        const value = CSS.escape(item.name);
        this.plusElementMap[item.name] = this.$el.find(`.kanban-head .create-button[data-group="${value}"]`);
      });
    }

    /**
     * @private
     */
    initStickableHeader() {
      const $container = this.$headContainer = this.$el.find('.kanban-head-container');
      const topBarHeight = (this.getThemeManager().getParam('navbarHeight') || 30) * this.getThemeManager().getFontSizeFactor();
      const screenWidthXs = this.getThemeManager().getParam('screenWidthXs');
      const $middle = this.$el.find('.kanban-columns-container');
      const $window = $(window);
      const $block = $('<div>').addClass('kanban-head-placeholder').html('&nbsp;').hide().insertAfter($container);
      $window.off('scroll.kanban-' + this.cid);
      $window.on('scroll.kanban-' + this.cid, () => {
        controlSticking();
      });
      $window.off('resize.kanban-' + this.cid);
      $window.on('resize.kanban-' + this.cid, () => controlSticking());
      const controlSticking = () => {
        const width = $middle.width();
        if ($(window.document).width() < screenWidthXs) {
          $container.removeClass('sticked');
          $container.css('width', '');
          $block.hide();
          $container.show();
          $container.get(0).scrollLeft = 0;
          $container.children().css('width', '');
          return;
        }
        const stickTop = this.$listKanban.offset().top - topBarHeight;
        const edge = $middle.offset().top + $middle.outerHeight(true);
        const scrollTop = $window.scrollTop();
        if (scrollTop < edge) {
          if (scrollTop > stickTop) {
            const containerWidth = this.$container.width() - 3;
            $container.children().css('width', width);
            $container.css('width', containerWidth + 'px');
            if (!$container.hasClass('sticked')) {
              $container.addClass('sticked');
              $block.show();
            }
          } else {
            $container.css('width', '');
            if ($container.hasClass('sticked')) {
              $container.removeClass('sticked');
              $block.hide();
            }
          }
          $container.show();
          this.syncHeadScroll();
          return;
        }
        $container.css('width', width + 'px');
        $container.hide();
        $block.show();
        $container.get(0).scrollLeft = 0;
        $container.children().css('width', '');
      };
    }

    /**
     * @private
     */
    initSortable() {
      const $list = this.$groupColumnList;
      $list.find('> .item').on('touchstart', e => {
        e.originalEvent.stopPropagation();
      });
      const orderDisabled = this.orderDisabled;
      const $groupColumnList = this.$el.find('.group-column-list');
      $list.sortable({
        distance: 10,
        connectWith: '.group-column-list',
        cancel: '.btn-group *',
        containment: this.getSelector(),
        scroll: false,
        over: function () {
          $(this).addClass('drop-hover');
        },
        out: function () {
          $(this).removeClass('drop-hover');
        },
        sort: e => {
          if (!this.blockScrollControl) {
            this.controlHorizontalScroll(e.originalEvent);
          }
        },
        start: (e, ui) => {
          $groupColumnList.addClass('drop-active');
          $list.sortable('refreshPositions');
          $(ui.item).find('.btn-group.open > .dropdown-toggle').parent().removeClass('open');
          this.draggedGroupFrom = $(ui.item).closest('.group-column-list').data('name');
          this.$showMore.addClass('hidden');
          this.sortIsStarted = true;
          this.sortWasCentered = false;
          this.$draggable = ui.item;
        },
        stop: (e, ui) => {
          this.blockScrollControl = false;
          this.sortIsStarted = false;
          this.$draggable = null;
          const $item = $(ui.item);
          this.$el.find('.group-column-list').removeClass('drop-active');
          const group = $item.closest('.group-column-list').data('name');
          const id = $item.data('id');
          const draggedGroupFrom = this.draggedGroupFrom;
          this.draggedGroupFrom = null;
          this.$showMore.removeClass('hidden');
          if (group !== draggedGroupFrom) {
            const model = this.collection.get(id);
            if (!model) {
              $list.sortable('cancel');
              return;
            }
            const attributes = {};
            attributes[this.statusField] = group;
            this.handleAttributesOnGroupChange(model, attributes, group);
            $list.sortable('disable');
            const processSave = async () => {
              if (this.groupChangeSaveHandler) {
                model.set(attributes, {
                  isDrop: true
                });
                return this.groupChangeSaveHandler(model);
              }
              return model.save(attributes, {
                patch: true,
                isDrop: true
              });
            };
            processSave().then(() => {
              Espo.Ui.success(this.translate('Saved'));
              $list.sortable('destroy');
              this.initSortable();
              this.moveModelBetweenGroupCollections(model, draggedGroupFrom, group);
              if (!orderDisabled) {
                this.reOrderGroup(group);
                this.storeGroupOrder(group);
              }
              this.rebuildGroupDataList();
            }).catch(() => {
              $list.sortable('cancel');
              $list.sortable('enable');
            });
            return;
          }
          if (orderDisabled) {
            $list.sortable('cancel');
            $list.sortable('enable');
            return;
          }
          this.reOrderGroup(group);
          this.storeGroupOrder(group);
          this.rebuildGroupDataList();
        }
      });
    }

    /**
     * @param {string} group
     * @param {string} [id] Prepend. To be used after save.
     * @return {Promise}
     */
    storeGroupOrder(group, id) {
      const ids = this.getGroupOrderFromDom(group);
      if (id) {
        ids.unshift(id);
      }
      if (this.onGroupOrder) {
        return this.onGroupOrder(group, ids);
      }
      return Espo.Ajax.putRequest('Kanban/order', {
        entityType: this.entityType,
        group: group,
        ids: ids
      });
    }

    /**
     * @private
     * @param {string} group
     * @return {string[]}
     */
    getGroupOrderFromDom(group) {
      const ids = [];
      const $group = this.$el.find('.group-column-list[data-name="' + group + '"]');
      $group.children().each((i, el) => {
        ids.push($(el).data('id'));
      });
      return ids;
    }

    /**
     * @param {string} group
     */
    reOrderGroup(group) {
      const groupCollection = this.getGroupCollection(group);
      const ids = this.getGroupOrderFromDom(group);
      const modelMap = {};
      groupCollection.models.forEach(m => {
        modelMap[m.id] = m;
      });
      while (groupCollection.models.length) {
        groupCollection.pop({
          silent: true
        });
      }
      ids.forEach(id => {
        const model = modelMap[id];
        if (!model) {
          return;
        }
        groupCollection.add(model, {
          silent: true
        });
      });
    }

    /**
     * @private
     */
    rebuildGroupDataList() {
      this.groupDataList.forEach(item => {
        item.dataList = [];
        for (const model of item.collection.models) {
          item.dataList.push({
            key: model.id,
            id: model.id
          });
        }
      });
    }

    /**
     * @private
     * @param {import('model').default} model
     * @param {string} groupFrom
     * @param {string} groupTo
     */
    moveModelBetweenGroupCollections(model, groupFrom, groupTo) {
      let collection = this.getGroupCollection(groupFrom);
      if (!collection) {
        return;
      }
      collection.remove(model.id, {
        silent: true
      });
      collection = this.getGroupCollection(groupTo);
      if (!collection) {
        return;
      }
      collection.add(model, {
        silent: true
      });
    }
    handleAttributesOnGroupChange(model, attributes, group) {}
    adjustMinHeight() {
      if (this.collection.models.length === 0 || !this.$container) {
        return;
      }
      let height = this.getHelper().calculateContentContainerHeight(this.$el.find('.kanban-columns-container'));
      const containerEl = this.$container.get(0);
      if (containerEl && containerEl.scrollWidth > containerEl.clientWidth) {
        height -= 18;
      }
      if (height < 100) {
        height = 100;
      }
      this.$listKanban.find('td.group-column').css({
        minHeight: height + 'px'
      });
    }
    getListLayout(callback) {
      if (this.listLayout) {
        callback.call(this, this.listLayout);
        return;
      }
      this._loadListLayout(listLayout => {
        this.listLayout = listLayout;
        callback.call(this, listLayout);
      });
    }
    getSelectAttributeList(callback) {
      super.getSelectAttributeList(attributeList => {
        if (attributeList) {
          if (!~attributeList.indexOf(this.statusField)) {
            attributeList.push(this.statusField);
          }
        }
        callback(attributeList);
      });
    }
    buildRows(callback) {
      let groupList = this.groupRawDataList;
      if (this.subCollectionList && groupList) {
        this.subCollectionList.forEach((collection, i) => {
          const group = groupList[i];
          if (!group) {
            console.warn("No group.", collection);
            return;
          }
          group.list = collection.models.map(model => model.getClonedAttributes());
          group.total = collection.total;
        });
      }
      if (!groupList) {
        groupList = [];
      }
      this.collection.reset();

      /** @type {import('collection').default[]} */
      this.subCollectionList = [];
      this.wait(true);

      /**
       * @type {{
       *     name: string,
       *     label: string,
       *     style: string,
       *     hasShowMore: boolean,
       *     collection: import('collection').default,
       *     dataList: Record[],
       * }[]}
       */
      this.groupDataList = [];
      let count = 0;
      let loadedCount = 0;
      this.getListLayout(listLayout => {
        this.listLayout = listLayout;
        groupList.forEach(item => {
          const collection = this.seedCollection.clone();
          this.listenTo(collection, 'destroy', (model, attributes, o) => {
            if (o.fromList) {
              return;
            }
            this.removeRecordFromList(model.id);
          });
          collection.total = item.total;
          collection.url = this.collection.url;
          collection.where = this.collection.where;
          collection.entityType = this.seedCollection.entityType;
          collection.maxSize = this.seedCollection.maxSize;
          collection.orderBy = this.seedCollection.orderBy;
          collection.order = this.seedCollection.order;
          collection.whereAdditional = [{
            field: this.statusField,
            type: 'equals',
            value: item.name
          }];
          collection.data.groupName = item.name;
          collection.add(item.list);
          this.subCollectionList.push(collection);
          this.collection.add(collection.models);
          const itemDataList = [];
          collection.models.forEach(model => {
            count++;
            itemDataList.push({
              key: model.id,
              id: model.id
            });
          });
          const style = item.style || this.getMetadata().get(`entityDefs.${this.scope}.fields.${this.statusField}.style.${item.name}`);
          const label = item.label || this.getLanguage().translateOption(item.name, this.statusField, this.scope);
          const o = {
            name: item.name,
            label: label,
            dataList: itemDataList,
            collection: collection,
            hasShowMore: collection.total > collection.length || collection.total === -1,
            style: style
          };
          this.groupDataList.push(o);
        });
        if (count === 0) {
          this.wait(false);
          if (callback) {
            callback();
          }
          return;
        }
        this.groupDataList.forEach(groupItem => {
          groupItem.dataList.forEach((item, j) => {
            const model = groupItem.collection.get(item.id);
            this.buildRow(j, model, () => {
              loadedCount++;
              if (loadedCount === count) {
                this.wait(false);
                if (callback) {
                  callback();
                }
              }
            });
          });
        });
      });
    }
    buildRow(i, model, callback) {
      const key = model.id;
      const hiddenMap = this._listSettingsHelper ? this._listSettingsHelper.getHiddenColumnMap() : {};
      const itemLayout = this.listLayout.filter(item => {
        const name = item.name;
        if (!name) {
          return true;
        }
        if (hiddenMap[name]) {
          return false;
        }
        if (item.hidden && !(name in hiddenMap)) {
          return false;
        }
        return true;
      });
      this.createView(key, this.itemViewName, {
        model: model,
        selector: `.item[data-id="${model.id}"]`,
        itemLayout: itemLayout,
        rowActionsDisabled: this.rowActionsDisabled,
        rowActionsView: this.rowActionsView,
        rowActionHandlers: this._rowActionHandlers || {},
        setViewBeforeCallback: this.options.skipBuildRows && !this.isRendered(),
        statusFieldIsEditable: this.statusFieldIsEditable,
        moveOverRowAction: this.moveOverRowAction,
        additionalRowActionList: this._additionalRowActionList,
        scope: this.scope,
        hasStars: this.hasStars
      }, callback);
    }

    /**
     * @param {string} id
     */
    removeRecordFromList(id) {
      this.collection.remove(id);
      if (this.collection.total > 0) {
        this.collection.total--;
      }
      this.collection.trigger('update-total');
      this.totalCount = this.collection.total;
      this.$el.find('.total-count-span').text(this.totalCount.toString());
      this.clearView(id);
      this.$el.find('.item[data-id="' + id + '"]').remove();
      this.subCollectionList.forEach(collection => {
        if (collection.get(id)) {
          collection.remove(id);
        }
      });
      for (const groupItem of this.groupDataList) {
        for (let j = 0; j < groupItem.dataList.length; j++) {
          const item = groupItem.dataList[j];
          if (item.id !== id) {
            continue;
          }
          groupItem.dataList.splice(j, 1);
          if (groupItem.collection.total > 0) {
            groupItem.collection.total--;
          }
          groupItem.hasShowMore = groupItem.collection.total > groupItem.collection.length || groupItem.collection.total === -1;
          break;
        }
      }
    }

    /**
     * @protected
     * @param {import('model').default} model A model.
     * @param {string} value A group.
     * @param {Record} o Options.
     */
    onChangeGroup(model, value, o) {
      const id = model.id;
      const group = model.get(this.statusField);
      this.subCollectionList.forEach(collection => {
        if (collection.get(id)) {
          collection.remove(id);
          if (collection.total > 0) {
            collection.total--;
          }
        }
      });
      let dataItem;
      for (const groupItem of this.groupDataList) {
        for (let j = 0; j < groupItem.dataList.length; j++) {
          const item = groupItem.dataList[j];
          if (item.id === id) {
            dataItem = item;
            groupItem.dataList.splice(j, 1);
            break;
          }
        }
      }
      if (!group) {
        return;
      }
      if (o.isDrop) {
        return;
      }
      for (const groupItem of this.groupDataList) {
        if (groupItem.name !== group) {
          continue;
        }
        groupItem.collection.unshift(model);
        groupItem.collection.total++;
        if (dataItem) {
          groupItem.dataList.unshift(dataItem);
          groupItem.hasShowMore = groupItem.collection.total > groupItem.collection.length || groupItem.collection.total === -1;
        }
      }
      const $item = this.$el.find('.item[data-id="' + id + '"]');
      const $column = this.$el.find('.group-column[data-name="' + group + '"] .group-column-list');
      if ($column.length) {
        $column.prepend($item);
      } else {
        $item.remove();
      }
      if (!this.orderDisabled) {
        this.storeGroupOrder(group);
      }
    }
    groupShowMore(group) {
      let groupItem;
      for (const i in this.groupDataList) {
        groupItem = this.groupDataList[i];
        if (groupItem.name === group) {
          break;
        }
        groupItem = null;
      }
      if (!groupItem) {
        return;
      }
      const collection = groupItem.collection;
      const $list = this.$el.find('.group-column-list[data-name="' + group + '"]');
      const $showMore = this.$el.find('.group-column[data-name="' + group + '"] .show-more');
      collection.data.select = this.collection.data.select;
      this.showMoreRecords({}, collection, $list, $showMore, () => {
        this.noRebuild = false;
        collection.models.forEach(model => {
          if (this.collection.get(model.id)) {
            return;
          }
          this.collection.add(model);
          groupItem.dataList.push({
            key: model.id,
            id: model.id
          });
        });
      });
    }
    getDomRowItem(id) {
      return this.$el.find('.item[data-id="' + id + '"]');
    }
    getRowContainerHtml(id) {
      return $('<div>').attr('data-id', id).addClass('item').get(0).outerHTML;
    }

    // noinspection JSUnusedGlobalSymbols
    actionMoveOver(data) {
      const model = this.collection.get(data.id);
      this.createView('moveOverDialog', 'views/modals/kanban-move-over', {
        model: model,
        statusField: this.statusField
      }, view => {
        view.render();
      });
    }

    /**
     *
     * @param {string} group
     * @return {module:collection}
     */
    getGroupCollection(group) {
      let collection = null;
      this.subCollectionList.forEach(itemCollection => {
        if (itemCollection.data.groupName === group) {
          collection = itemCollection;
        }
      });
      return collection;
    }

    /**
     * @param {string} group
     */
    showPlus(group) {
      const $el = this.plusElementMap[group];
      if (!$el) {
        return;
      }
      $el.removeClass('hidden');
    }

    /**
     * @param {string} group
     */
    hidePlus(group) {
      const $el = this.plusElementMap[group];
      if (!$el) {
        return;
      }
      $el.addClass('hidden');
    }

    /**
     * @param {string} group
     */
    async actionCreateInGroup(group) {
      if (this.createActionHandler) {
        this.createActionHandler(group);
        return;
      }
      const getCreateAttributes = () => {
        if (this.getCreateAttributes) {
          return this.getCreateAttributes(group);
        }
        return Promise.resolve({
          [this.statusField]: group
        });
      };
      const attributes = await getCreateAttributes();
      const helper = new _recordModal.default();
      await helper.showCreate(this, {
        attributes: attributes,
        entityType: this.scope,
        afterSave: async model => {
          if (this.orderDisabled) {
            await this.collection.fetch({
              maxSize: this.collection.maxSize
            });
            return;
          }
          await this.storeGroupOrder(group, model.id);
          await this.collection.fetch({
            maxSize: this.collection.maxSize
          });
        },
        beforeRender: view => {
          view.getRecordView().setFieldReadOnly(this.statusField, true);
        }
      });
    }

    /**
     * @private
     * @param {MouseEvent} e
     */
    initBackDrag(e) {
      this.backDragStarted = true;
      const containerEl = this.$container.get(0);
      containerEl.style.cursor = 'grabbing';
      containerEl.style.userSelect = 'none';
      const $document = $(document);
      const startLeft = containerEl.scrollLeft;
      const startX = e.clientX;
      $document.on(`mousemove.${this.cid}`, e => {
        // noinspection JSUnresolvedReference
        const dx = e.originalEvent.clientX - startX;
        containerEl.scrollLeft = startLeft - dx;
        this.syncHeadScroll();
      });
      $document.one('mouseup.' + this.cid, () => {
        this.stopBackDrag();
      });
    }
    stopBackDrag() {
      this.$container.get(0).style.cursor = 'default';
      this.$container.get(0).style.userSelect = 'none';
      $(document).off('mousemove.' + this.cid);
    }
    syncHeadScroll() {
      if (!this.$headContainer.hasClass('sticked')) {
        return;
      }
      this.$headContainer.get(0).scrollLeft = this.$container.get(0).scrollLeft;
    }
    controlHorizontalScroll(e) {
      if (!this.sortIsStarted) {
        return;
      }
      if (!this.$draggable) {
        return;
      }
      const draggableRect = this.$draggable.get(0).getBoundingClientRect();
      const itemLeft = draggableRect.left;
      const itemRight = draggableRect.right;
      const containerEl = this.$container.get(0);
      const rect = containerEl.getBoundingClientRect();
      const marginSens = 70;
      let step = 2;
      const interval = 5;
      const marginSensStepRatio = 4;
      const stepRatio = 3;
      const isRight = rect.right - marginSens < itemRight && containerEl.scrollLeft + containerEl.offsetWidth < containerEl.scrollWidth;
      const isLeft = rect.left + marginSens > itemLeft && containerEl.scrollLeft > 0;
      this.$groupColumnList.sortable('refreshPositions');
      if (isRight && this.sortWasCentered) {
        const margin = rect.right - itemRight;
        if (margin < marginSens / marginSensStepRatio) {
          step *= stepRatio;
        }
        const stepActual = Math.min(step, containerEl.offsetWidth - containerEl.scrollLeft);
        containerEl.scrollLeft = containerEl.scrollLeft + stepActual;
        this.syncHeadScroll();
        if (containerEl.scrollLeft + containerEl.offsetWidth === containerEl.scrollWidth) {
          this.blockScrollControl = false;
          return;
        }
        this.blockScrollControl = true;
        setTimeout(() => this.controlHorizontalScroll(e), interval);
        return;
      }
      if (isLeft && this.sortWasCentered) {
        const margin = -(rect.left - itemLeft);
        if (margin < marginSens / marginSensStepRatio) {
          step *= stepRatio;
        }
        const stepActual = Math.min(step, containerEl.scrollLeft);
        containerEl.scrollLeft = containerEl.scrollLeft - stepActual;
        this.syncHeadScroll();
        if (containerEl.scrollLeft === 0) {
          this.blockScrollControl = false;
          return;
        }
        this.blockScrollControl = true;
        setTimeout(() => this.controlHorizontalScroll(e), interval);
        return;
      }
      if (this.blockScrollControl && !isLeft && !isRight) {
        this.blockScrollControl = false;
      }
      if (!isLeft && !isRight) {
        this.sortWasCentered = true;
      }
    }

    /** @inheritDoc */
    async afterSettingsChange(options) {
      this._internalLayout = null;
      Espo.Ui.notifyWait();
      await this.collection.fetch({
        maxSize: this.collection.maxSize
      });
      Espo.Ui.notify(false);
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Set can create.
     *
     * @param {boolean} canCreate
     * @sinc 8.4.0
     */
    setCanCreate(canCreate) {
      this.isCreatable = canCreate;
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * Set can re-order.
     *
     * @param {boolean} canReOrder
     * @sinc 8.4.0
     */
    setCanReOrder(canReOrder) {
      this.orderDisabled = !canReOrder;
    }
  }
  var _default = _exports.default = KanbanRecordView;
});

define("views/record/kanban-item", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class KanbanRecordItem extends _view.default {
    template = 'record/kanban-item';
    data() {
      return {
        layoutDataList: this.layoutDataList,
        rowActionsDisabled: this.rowActionsDisabled,
        isStarred: this.hasStars && this.model.attributes.isStarred
      };
    }
    setup() {
      this.itemLayout = this.options.itemLayout;
      this.rowActionsView = this.options.rowActionsView;
      this.rowActionsDisabled = this.options.rowActionsDisabled;
      this.hasStars = this.options.hasStars;
      this.layoutDataList = [];
      this.itemLayout.forEach((item, i) => {
        const name = item.name;
        const key = name + 'Field';
        const o = {
          name: name,
          isAlignRight: item.align === 'right',
          isLarge: item.isLarge,
          isMuted: item.isMuted,
          isFirst: i === 0,
          key: key
        };
        this.layoutDataList.push(o);
        let viewName = item.view || this.model.getFieldParam(name, 'view');
        if (!viewName) {
          const type = this.model.getFieldType(name) || 'base';
          viewName = this.getFieldManager().getViewName(type);
        }
        let mode = 'list';
        if (item.link) {
          mode = 'listLink';
        }
        this.createView(key, viewName, {
          model: this.model,
          name: name,
          mode: mode,
          readOnly: true,
          selector: '.field[data-name="' + name + '"]'
        });
      });
      if (!this.rowActionsDisabled) {
        const acl = {
          edit: this.getAcl().checkModel(this.model, 'edit'),
          delete: this.getAcl().checkModel(this.model, 'delete')
        };
        this.createView('itemMenu', this.rowActionsView, {
          selector: '.item-menu-container',
          model: this.model,
          acl: acl,
          moveOverRowAction: this.options.moveOverRowAction,
          statusFieldIsEditable: this.options.statusFieldIsEditable,
          rowActionHandlers: this.options.rowActionHandlers || {},
          additionalActionList: this.options.additionalRowActionList,
          scope: this.options.scope
        });
      }
    }
  }
  var _default = _exports.default = KanbanRecordItem;
});

define("views/record/edit-small", ["exports", "views/record/edit"], function (_exports, _edit) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * For bc.
   *
   * @deprecated As of v9.0.0. Extend from EditRecordView instead.
   */
  class EditSmallRecordView extends _edit.default {}
  var _default = _exports.default = EditSmallRecordView;
});

define("views/record/edit-side", ["exports", "views/record/detail-side"], function (_exports, _detailSide) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detailSide = _interopRequireDefault(_detailSide);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/edit-side */

  class EditSideRecordView extends _detailSide.default {
    /** @inheritDoc */
    mode = 'edit';

    /** @inheritDoc */
    defaultPanelDefs = {
      name: 'default',
      label: false,
      view: 'views/record/panels/side',
      isForm: true,
      options: {
        fieldList: [{
          name: ':assignedUser'
        }, {
          name: 'teams',
          view: 'views/fields/teams'
        }]
      }
    };
  }
  var _default = _exports.default = EditSideRecordView;
});

define("views/record/edit-bottom", ["exports", "views/record/detail-bottom"], function (_exports, _detailBottom) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detailBottom = _interopRequireDefault(_detailBottom);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EditBottomRecordView extends _detailBottom.default {
    mode = 'edit';
    streamPanel = false;
    relationshipPanels = false;
  }
  var _default = _exports.default = EditBottomRecordView;
});

define("views/record/detail-small", ["exports", "views/record/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * For bc.
   *
   * @deprecated As of v9.0.0. Extend from DetailRecordView instead.
   */
  class DetailSmallRecordView extends _detail.default {}
  var _default = _exports.default = DetailSmallRecordView;
});

define("views/record/detail-middle", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/record/detail-middle */

  /**
   * A detail-middle record view.
   */
  class DetailMiddleRecordView extends _view.default {
    init() {
      this.recordHelper = this.options.recordHelper;
      this.scope = this.model.entityType;
    }
    data() {
      return {
        hiddenPanels: this.recordHelper.getHiddenPanels(),
        hiddenFields: this.recordHelper.getHiddenFields()
      };
    }

    /**
     * Show a panel.
     *
     * @param {string} name
     */
    showPanel(name) {
      if (this.recordHelper.getPanelStateParam(name, 'hiddenLocked')) {
        return;
      }
      this.showPanelInternal(name);
      this.recordHelper.setPanelStateParam(name, 'hidden', false);
    }

    /**
     * @param {string} name
     */
    showPanelInternal(name) {
      if (this.isRendered()) {
        this.$el.find('.panel[data-name="' + name + '"]').removeClass('hidden');
      }
      const wasShown = !this.recordHelper.getPanelStateParam(name, 'hidden');
      if (!wasShown && this.options.panelFieldListMap && this.options.panelFieldListMap[name]) {
        this.options.panelFieldListMap[name].forEach(field => {
          const view = this.getFieldView(field);
          if (!view) {
            return;
          }
          view.reRender();
        });
      }
    }

    /**
     * Hide a panel.
     *
     * @param {string} name
     */
    hidePanel(name) {
      this.hidePanelInternal(name);
      this.recordHelper.setPanelStateParam(name, 'hidden', true);
    }

    /**
     * @public
     * @param {string} name A name.
     */
    hidePanelInternal(name) {
      if (this.isRendered()) {
        this.$el.find('.panel[data-name="' + name + '"]').addClass('hidden');
      }
    }

    /**
     * Hide a field.
     *
     * @param {string} name A name.
     */
    hideField(name) {
      this.recordHelper.setFieldStateParam(name, 'hidden', true);
      const processHtml = () => {
        const fieldView = this.getFieldView(name);
        if (fieldView) {
          const $field = fieldView.$el;
          const $cell = $field.closest('.cell[data-name="' + name + '"]');
          const $label = $cell.find('label.control-label[data-name="' + name + '"]');
          $field.addClass('hidden');
          $label.addClass('hidden');
          $cell.addClass('hidden-cell');
        } else {
          this.$el.find('.cell[data-name="' + name + '"]').addClass('hidden-cell');
          this.$el.find('.field[data-name="' + name + '"]').addClass('hidden');
          this.$el.find('label.control-label[data-name="' + name + '"]').addClass('hidden');
        }
      };
      if (this.isRendered()) {
        processHtml();
      } else {
        this.once('after:render', () => {
          processHtml();
        });
      }
      const view = this.getFieldView(name);
      if (view) {
        view.setDisabled();
      }
    }

    /**
     * Show a field.
     *
     * @param {string} name A name.
     */
    showField(name) {
      if (this.recordHelper.getFieldStateParam(name, 'hiddenLocked')) {
        return;
      }
      this.recordHelper.setFieldStateParam(name, 'hidden', false);
      const processHtml = () => {
        const fieldView = this.getFieldView(name);
        if (fieldView) {
          const $field = fieldView.$el;
          const $cell = $field.closest('.cell[data-name="' + name + '"]');
          const $label = $cell.find('label.control-label[data-name="' + name + '"]');
          $field.removeClass('hidden');
          $label.removeClass('hidden');
          $cell.removeClass('hidden-cell');
        } else {
          this.$el.find('.cell[data-name="' + name + '"]').removeClass('hidden-cell');
          this.$el.find('.field[data-name="' + name + '"]').removeClass('hidden');
          this.$el.find('label.control-label[data-name="' + name + '"]').removeClass('hidden');
        }
      };
      if (this.isRendered()) {
        processHtml();
      } else {
        this.once('after:render', () => {
          processHtml();
        });
      }
      const view = this.getFieldView(name);
      if (view) {
        if (!view.disabledLocked) {
          view.setNotDisabled();
        }
      }
    }

    /**
     * Get field views.
     *
     * @return {Object.<string, module:views/fields/base>}
     */
    getFieldViews() {
      const fieldViews = {};
      for (const viewKey in this.nestedViews) {
        // noinspection JSUnresolvedReference
        const name = this.nestedViews[viewKey].name;
        fieldViews[name] = this.nestedViews[viewKey];
      }
      return fieldViews;
    }

    /**
     * Get a field view.
     *
     * @param {string} name A field name.
     * @return {module:views/fields/base}
     */
    getFieldView(name) {
      return (this.getFieldViews() || {})[name];
    }

    /**
     * For backward compatibility.
     *
     * @todo Remove.
     */
    getView(name) {
      let view = super.getView(name);
      if (!view) {
        view = this.getFieldView(name);
      }
      return view;
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = DetailMiddleRecordView;
});

define("views/record/deleted-detail", ["exports", "views/record/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class DeletedDetailRecordView extends _detail.default {
    bottomView = null;
    sideView = 'views/record/deleted-detail-side';
    setupBeforeFinal() {
      super.setupBeforeFinal();
      this.buttonList = [];
      this.dropdownItemList = [];
      this.addDropdownItem({
        name: 'restoreDeleted',
        label: 'Restore'
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionRestoreDeleted() {
      Espo.Ui.notifyWait();
      Espo.Ajax.postRequest(this.model.entityType + '/action/restoreDeleted', {
        id: this.model.id
      }).then(() => {
        Espo.Ui.notify(false);
        this.model.set('deleted', false);
        this.model.trigger('after:restore-deleted');
      });
    }
  }
  var _default = _exports.default = DeletedDetailRecordView;
});

define("views/record/deleted-detail-side", ["exports", "views/record/detail-side"], function (_exports, _detailSide) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detailSide = _interopRequireDefault(_detailSide);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class DeletedDetailSideRecordView extends _detailSide.default {
    additionalPanelsDisabled = true;
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = DeletedDetailSideRecordView;
});

define("views/record/row-actions/view-only", ["exports", "views/record/row-actions/default"], function (_exports, _default2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _default2 = _interopRequireDefault(_default2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ViewOnlyRowActionsView extends _default2.default {
    getActionList() {
      return [{
        action: 'quickView',
        label: 'View',
        data: {
          id: this.model.id
        },
        link: '#' + this.model.entityType + '/view/' + this.model.id,
        groupIndex: 0
      }];
    }
  }
  var _default = _exports.default = ViewOnlyRowActionsView;
});

define("views/record/row-actions/view-and-remove", ["exports", "views/record/row-actions/default"], function (_exports, _default2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _default2 = _interopRequireDefault(_default2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ViewAndRemoveRowActionsView extends _default2.default {
    getActionList() {
      /** @type module:views/record/list~rowAction[] */
      const actionList = [{
        action: 'quickView',
        label: 'View',
        data: {
          id: this.model.id
        },
        link: '#' + this.model.entityType + '/view/' + this.model.id,
        groupIndex: 0
      }];
      if (this.options.acl.delete) {
        actionList.push({
          action: 'quickRemove',
          label: 'Remove',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        });
      }
      return actionList;
    }
  }
  var _default = _exports.default = ViewAndRemoveRowActionsView;
});

define("views/record/row-actions/view-and-edit", ["exports", "views/record/row-actions/default"], function (_exports, _default2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _default2 = _interopRequireDefault(_default2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ViewAndEditRowActionsView extends _default2.default {
    getActionList() {
      let list = [{
        action: 'quickView',
        label: 'View',
        data: {
          id: this.model.id
        },
        link: '#' + this.model.entityType + '/view/' + this.model.id,
        groupIndex: 0
      }];
      if (this.options.acl.edit) {
        list = list.concat([{
          action: 'quickEdit',
          label: 'Edit',
          data: {
            id: this.model.id
          },
          link: '#' + this.model.entityType + '/edit/' + this.model.id,
          groupIndex: 0
        }]);
      }
      this.getAdditionalActionList().forEach(item => list.push(item));
      return list;
    }
  }
  var _default = _exports.default = ViewAndEditRowActionsView;
});

define("views/record/row-actions/remove-only", ["exports", "views/record/row-actions/default"], function (_exports, _default2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _default2 = _interopRequireDefault(_default2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RemoveOnlyRowActionsView extends _default2.default {
    getActionList() {
      if (this.options.acl.delete) {
        return [{
          action: 'quickRemove',
          label: 'Remove',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        }];
      }
      return [];
    }
  }
  var _default = _exports.default = RemoveOnlyRowActionsView;
});

define("views/record/row-actions/relationship-view-only", ["exports", "views/record/row-actions/relationship"], function (_exports, _relationship) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _relationship = _interopRequireDefault(_relationship);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RelationshipViewOnlyActionsView extends _relationship.default {
    getActionList() {
      return [{
        action: 'viewRelated',
        label: 'View',
        data: {
          id: this.model.id
        },
        link: '#' + this.model.entityType + '/view/' + this.model.id,
        groupIndex: 0
      }];
    }
  }
  var _default = _exports.default = RelationshipViewOnlyActionsView;
});

define("views/record/row-actions/relationship-view-and-unlink", ["exports", "views/record/row-actions/relationship"], function (_exports, _relationship) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _relationship = _interopRequireDefault(_relationship);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RelationshipViewAndUnlinkActionsView extends _relationship.default {
    getActionList() {
      const list = [{
        action: 'quickView',
        label: 'View',
        data: {
          id: this.model.id
        },
        link: '#' + this.model.entityType + '/view/' + this.model.id,
        groupIndex: 0
      }];
      if (this.options.acl.edit && !this.options.unlinkDisabled) {
        list.push({
          action: 'unlinkRelated',
          label: 'Unlink',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        });
      }
      return list;
    }
  }
  var _default = _exports.default = RelationshipViewAndUnlinkActionsView;
});

define("views/record/row-actions/relationship-view-and-edit", ["exports", "views/record/row-actions/relationship"], function (_exports, _relationship) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _relationship = _interopRequireDefault(_relationship);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RelationshipViewAndEditActionsView extends _relationship.default {
    getActionList() {
      const list = [{
        action: 'quickView',
        label: 'View',
        data: {
          id: this.model.id
        },
        link: '#' + this.model.entityType + '/view/' + this.model.id,
        groupIndex: 0
      }];
      if (this.options.acl.edit) {
        list.push({
          action: 'quickEdit',
          label: 'Edit',
          data: {
            id: this.model.id
          },
          link: '#' + this.model.entityType + '/edit/' + this.model.id,
          groupIndex: 0
        });
      }
      return list;
    }
  }
  var _default = _exports.default = RelationshipViewAndEditActionsView;
});

define("views/record/row-actions/relationship-unlink-only", ["exports", "views/record/row-actions/relationship"], function (_exports, _relationship) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _relationship = _interopRequireDefault(_relationship);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RelationshipUnlinkOnlyActionsView extends _relationship.default {
    getActionList() {
      if (this.options.acl.edit && !this.options.unlinkDisabled) {
        return [{
          action: 'unlinkRelated',
          label: 'Unlink',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        }];
      }
      return [];
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = RelationshipUnlinkOnlyActionsView;
});

define("views/record/row-actions/relationship-remove-only", ["exports", "views/record/row-actions/relationship"], function (_exports, _relationship) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _relationship = _interopRequireDefault(_relationship);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RelationshipRemoveOnlyActionsView extends _relationship.default {
    getActionList() {
      if (this.options.acl.delete) {
        return [{
          action: 'removeRelated',
          label: 'Remove',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        }];
      }
      return [];
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = RelationshipRemoveOnlyActionsView;
});

define("views/record/row-actions/relationship-no-unlink", ["exports", "views/record/row-actions/relationship"], function (_exports, _relationship) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _relationship = _interopRequireDefault(_relationship);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RelationshipNoUnlinkActionsView extends _relationship.default {
    getActionList() {
      let list = [{
        action: 'quickView',
        label: 'View',
        data: {
          id: this.model.id
        },
        link: '#' + this.model.entityType + '/view/' + this.model.id,
        groupIndex: 0
      }];
      if (this.options.acl.edit) {
        list = list.concat([{
          action: 'quickEdit',
          label: 'Edit',
          data: {
            id: this.model.id
          },
          link: '#' + this.model.entityType + '/edit/' + this.model.id,
          groupIndex: 0
        }]);
      }
      if (this.options.acl.delete) {
        list.push({
          action: 'removeRelated',
          label: 'Remove',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        });
      }
      return list;
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = RelationshipNoUnlinkActionsView;
});

define("views/record/row-actions/relationship-no-remove", ["exports", "views/record/row-actions/relationship"], function (_exports, _relationship) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _relationship = _interopRequireDefault(_relationship);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class UserRelationshipNoRemoveRowActionsView extends _relationship.default {
    getActionList() {
      const list = [];
      list.push({
        action: 'quickView',
        label: 'View',
        data: {
          id: this.model.id
        },
        link: `#${this.model.entityType}/view/${this.model.id}`,
        groupIndex: 0
      });
      if (this.options.acl.edit) {
        list.push({
          action: 'quickEdit',
          label: 'Edit',
          data: {
            id: this.model.id
          },
          link: `#${this.model.entityType}/edit/${this.model.id}`,
          groupIndex: 0
        });
        if (!this.options.unlinkDisabled) {
          list.push({
            action: 'unlinkRelated',
            label: 'Unlink',
            data: {
              id: this.model.id
            },
            groupIndex: 0
          });
        }
      }
      return list;
    }
  }
  _exports.default = UserRelationshipNoRemoveRowActionsView;
});

define("views/record/row-actions/relationship-edit-and-remove", ["exports", "views/record/row-actions/relationship"], function (_exports, _relationship) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _relationship = _interopRequireDefault(_relationship);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RelationshipEditAndRemoveActionsView extends _relationship.default {
    getActionList() {
      const list = [];
      if (this.options.acl.edit) {
        list.push({
          action: 'quickEdit',
          label: 'Edit',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        });
      }
      if (this.options.acl.delete) {
        list.push({
          action: 'quickRemove',
          label: 'Remove',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        });
      }
      return list;
    }
  }
  var _default = _exports.default = RelationshipEditAndRemoveActionsView;
});

define("views/record/row-actions/empty", ["exports", "views/record/row-actions/default"], function (_exports, _default2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _default2 = _interopRequireDefault(_default2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmptyRowActionsView extends _default2.default {
    getActionList() {
      return [];
    }
  }
  var _default = _exports.default = EmptyRowActionsView;
});

define("views/record/row-actions/edit-and-remove", ["exports", "views/record/row-actions/default"], function (_exports, _default2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _default2 = _interopRequireDefault(_default2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EditAndRemoveRowActionsView extends _default2.default {
    getActionList() {
      let list = [];
      if (this.options.acl.edit) {
        list.push({
          action: 'quickEdit',
          label: 'Edit',
          data: {
            id: this.model.id
          },
          link: '#' + this.model.entityType + '/edit/' + this.model.id,
          groupIndex: 0
        });
      }
      if (this.options.acl.delete) {
        list.push({
          action: 'quickRemove',
          label: 'Remove',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        });
      }
      return list;
    }
  }
  var _default = _exports.default = EditAndRemoveRowActionsView;
});

define("views/record/row-actions/default-kanban", ["exports", "views/record/row-actions/default"], function (_exports, _default2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _default2 = _interopRequireDefault(_default2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class DefaultKanbanRowActionsView extends _default2.default {
    getActionList() {
      const list = [{
        action: 'quickView',
        label: 'View',
        data: {
          id: this.model.id
        },
        link: '#' + this.model.entityType + '/view/' + this.model.id,
        groupIndex: 0
      }];
      if (this.options.moveOverRowAction && this.options.statusFieldIsEditable) {
        list.push({
          action: 'moveOver',
          label: 'Move Over',
          data: {
            id: this.model.id
          },
          groupIndex: 1
        });
      }
      if (this.options.acl.edit) {
        list.push({
          action: 'quickEdit',
          label: 'Edit',
          data: {
            id: this.model.id
          },
          link: '#' + this.model.entityType + '/edit/' + this.model.id,
          groupIndex: 0
        });
      }
      this.getAdditionalActionList().forEach(item => list.push(item));
      if (this.options.acl.delete) {
        list.push({
          action: 'quickRemove',
          label: 'Remove',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        });
      }
      return list;
    }
  }
  var _default = _exports.default = DefaultKanbanRowActionsView;
});

define("views/preferences/edit", ["exports", "views/edit"], function (_exports, _edit) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _edit.default {
    userName = '';
    setup() {
      super.setup();
      this.userName = this.model.get('name');
    }
    getHeader() {
      return this.buildHeaderHtml([$('<span>').text(this.translate('Preferences')), $('<span>').text(this.userName)]);
    }
    updatePageTitle() {
      let title = this.getLanguage().translate(this.scope, 'scopeNames');
      if (this.model.id !== this.getUser().id && this.userName) {
        title += ' · ' + this.userName;
      }
      this.setPageTitle(title);
    }
  }
  _exports.default = _default;
});

define("views/preferences/record/edit", ["exports", "views/record/edit"], function (_exports, _edit) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class PreferencesEditRecordView extends _edit.default {
    sideView = null;
    saveAndContinueEditingAction = false;
    buttonList = [{
      name: 'save',
      label: 'Save',
      style: 'primary'
    }, {
      name: 'cancel',
      label: 'Cancel'
    }];
    dynamicLogicDefs = {
      fields: {
        'tabList': {
          visible: {
            conditionGroup: [{
              type: 'isTrue',
              attribute: 'useCustomTabList'
            }]
          }
        },
        'addCustomTabs': {
          visible: {
            conditionGroup: [{
              type: 'isTrue',
              attribute: 'useCustomTabList'
            }]
          }
        }
      }
    };
    setup() {
      super.setup();
      const model = /** @type {import('models/preferences').default} */this.model;
      this.addDropdownItem({
        name: 'reset',
        text: this.getLanguage().translate('Reset to Default', 'labels', 'Admin'),
        style: 'danger',
        onClick: () => this.actionReset()
      });
      const forbiddenEditFieldList = this.getAcl().getScopeForbiddenFieldList('Preferences', 'edit');
      if (!forbiddenEditFieldList.includes('dashboardLayout') && !model.isPortal()) {
        this.addDropdownItem({
          name: 'resetDashboard',
          text: this.getLanguage().translate('Reset Dashboard to Default', 'labels', 'Preferences'),
          onClick: () => this.actionResetDashboard()
        });
      }
      if (model.isPortal()) {
        this.layoutName = 'detailPortal';
      }
      if (this.model.id === this.getUser().id) {
        const preferencesModel = this.getPreferences();
        this.on('save', (a, attributeList) => {
          const data = this.model.getClonedAttributes();
          delete data['smtpPassword'];
          preferencesModel.set(data);
          preferencesModel.trigger('update', attributeList);
        });
      }
      if (!this.getUser().isAdmin() || model.isPortal()) {
        this.hidePanel('dashboard');
        this.hideField('dashboardLayout');
      }
      this.controlFollowCreatedEntityListVisibility();
      this.listenTo(this.model, 'change:followCreatedEntities', this.controlFollowCreatedEntityListVisibility);
      this.controlColorsField();
      this.listenTo(this.model, 'change:scopeColorsDisabled', () => this.controlColorsField());
      let hideNotificationPanel = true;
      if (!this.getConfig().get('assignmentEmailNotifications') || model.isPortal()) {
        this.hideField('receiveAssignmentEmailNotifications');
        this.hideField('assignmentEmailNotificationsIgnoreEntityTypeList');
      } else {
        hideNotificationPanel = false;
        this.controlAssignmentEmailNotificationsVisibility();
        this.listenTo(this.model, 'change:receiveAssignmentEmailNotifications', () => {
          this.controlAssignmentEmailNotificationsVisibility();
        });
      }
      if ((this.getConfig().get('assignmentEmailNotificationsEntityList') || []).length === 0) {
        this.hideField('assignmentEmailNotificationsIgnoreEntityTypeList');
      }
      if ((this.getConfig().get('assignmentNotificationsEntityList') || []).length === 0 || model.isPortal()) {
        this.hideField('assignmentNotificationsIgnoreEntityTypeList');
      } else {
        hideNotificationPanel = false;
      }
      if (this.getConfig().get('emailForceUseExternalClient')) {
        this.hideField('emailUseExternalClient');
      }
      if (!this.getConfig().get('mentionEmailNotifications') || model.isPortal()) {
        this.hideField('receiveMentionEmailNotifications');
      } else {
        hideNotificationPanel = false;
      }
      if (!this.getConfig().get('streamEmailNotifications') && !model.isPortal()) {
        this.hideField('receiveStreamEmailNotifications');
      } else if (!this.getConfig().get('portalStreamEmailNotifications') && model.isPortal()) {
        this.hideField('receiveStreamEmailNotifications');
      } else {
        hideNotificationPanel = false;
      }
      if (hideNotificationPanel) {
        this.hidePanel('notifications');
      }
      if (this.getConfig().get('userThemesDisabled')) {
        this.hideField('theme');
      }
      this.on('save', /** Record */initialAttributes => {
        if (this.model.get('language') !== initialAttributes.language || this.model.get('theme') !== initialAttributes.theme || (this.model.get('themeParams') || {}).navbar !== (initialAttributes.themeParams || {}).navbar || this.model.get('pageContentWidth') !== initialAttributes.pageContentWidth) {
          this.setConfirmLeaveOut(false);
          window.location.reload();
        }
      });
    }
    controlFollowCreatedEntityListVisibility() {
      if (!this.model.get('followCreatedEntities')) {
        this.showField('followCreatedEntityTypeList');
      } else {
        this.hideField('followCreatedEntityTypeList');
      }
    }
    controlColorsField() {
      if (this.model.get('scopeColorsDisabled')) {
        this.hideField('tabColorsDisabled');
      } else {
        this.showField('tabColorsDisabled');
      }
    }
    controlAssignmentEmailNotificationsVisibility() {
      if (this.model.get('receiveAssignmentEmailNotifications')) {
        this.showField('assignmentEmailNotificationsIgnoreEntityTypeList');
      } else {
        this.hideField('assignmentEmailNotificationsIgnoreEntityTypeList');
      }
    }
    actionReset() {
      this.confirm(this.translate('resetPreferencesConfirmation', 'messages'), () => {
        Espo.Ajax.deleteRequest(`Preferences/${this.model.id}`).then(data => {
          Espo.Ui.success(this.translate('resetPreferencesDone', 'messages'));
          this.model.set(data);
          for (const attribute in data) {
            this.setInitialAttributeValue(attribute, data[attribute]);
          }
          this.getPreferences().set(this.model.getClonedAttributes());
          this.getPreferences().trigger('update');
          this.setIsNotChanged();
        });
      });
    }
    actionResetDashboard() {
      this.confirm(this.translate('confirmation', 'messages'), () => {
        Espo.Ajax.postRequest('Preferences/action/resetDashboard', {
          id: this.model.id
        }).then(data => {
          const isChanged = this.isChanged;
          Espo.Ui.success(this.translate('Done'));
          this.model.set(data);
          for (const attribute in data) {
            this.setInitialAttributeValue(attribute, data[attribute]);
          }
          this.getPreferences().set(this.model.getClonedAttributes());
          this.getPreferences().trigger('update');
          if (!isChanged) {
            this.setIsNotChanged();
          }
        });
      });
    }
    exit(after) {
      if (after === 'cancel') {
        let url = `#User/view/${this.model.id}`;
        if (!this.getAcl().checkModel(this.getUser())) {
          url = '#';
        }
        this.getRouter().navigate(url, {
          trigger: true
        });
      }
    }
    handleShortcutKeyCtrlS(e) {
      this.handleShortcutKeyCtrlEnter(e);
    }
  }
  var _default = _exports.default = PreferencesEditRecordView;
});

define("views/preferences/fields/week-start", ["exports", "views/fields/enum-int"], function (_exports, _enumInt) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enumInt = _interopRequireDefault(_enumInt);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _enumInt.default {
    setupOptions() {
      this.params.options = Espo.Utils.clone(this.params.options);
      this.params.options.unshift(-1);
      this.translatedOptions = {};
      const dayList = this.getLanguage().get('Global', 'lists', 'dayNames') || [];
      dayList.forEach((item, i) => this.translatedOptions[i] = item);
      const defaultWeekStart = this.getConfig().get('weekStart');
      this.translatedOptions[-1] = `${this.translate('Default')} · ${dayList[defaultWeekStart]}`;
    }
  }
  _exports.default = _default;
});

define("views/preferences/fields/time-zone", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _enum.default {
    setupOptions() {
      this.params.options = Espo.Utils.clone(this.getHelper().getAppParam('timeZoneList')) || [];
      this.params.options.unshift('');
      this.translatedOptions = this.translatedOptions || {};
      this.translatedOptions[''] = `${this.translate('Default')} · ${this.getConfig().get('timeZone')}`;
    }
  }
  _exports.default = _default;
});

define("views/preferences/fields/time-format", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _enum.default {
    setupOptions() {
      this.params.options = Espo.Utils.clone(this.getMetadata().get(['app', 'dateTime', 'timeFormatList']) || []);
      this.params.options.unshift('');
      this.translatedOptions = this.translatedOptions || {};
      this.translatedOptions[''] = `${this.translate('Default')} · ${this.getConfig().get('timeFormat')}`;
    }
  }
  _exports.default = _default;
});

define("views/preferences/fields/theme", ["exports", "views/settings/fields/theme"], function (_exports, _theme) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _theme = _interopRequireDefault(_theme);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _theme.default {
    setupOptions() {
      this.params.options = Object.keys(this.getMetadata().get('themes') || {}).sort((v1, v2) => {
        if (v2 === 'EspoRtl') {
          return -1;
        }
        return this.translate(v1, 'themes').localeCompare(this.translate(v2, 'themes'));
      });
      this.params.options.unshift('');
    }
    setupTranslation() {
      super.setupTranslation();
      this.translatedOptions = this.translatedOptions || {};
      const defaultTheme = this.getConfig().get('theme');
      const defaultTranslated = this.translatedOptions[defaultTheme] || defaultTheme;
      this.translatedOptions[''] = `${this.translate('Default')} · ${defaultTranslated}`;
    }
    afterRenderDetail() {
      const navbar = this.getNavbarValue() || this.getDefaultNavbar();
      if (navbar) {
        this.$el.append(' ').append($('<span>').addClass('text-muted chevron-right')).append(' ').append($('<span>').text(this.translate(navbar, 'themeNavbars')));
      }
    }
  }
  _exports.default = _default;
});

define("views/preferences/fields/tab-list", ["exports", "views/settings/fields/tab-list"], function (_exports, _tabList) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _tabList = _interopRequireDefault(_tabList);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _tabList.default {
    setup() {
      super.setup();
      this.params.options = this.params.options.filter(scope => {
        if (scope === '_delimiter_' || scope === 'Home') {
          return true;
        }
        const defs = this.getMetadata().get(['scopes', scope]);
        if (!defs) {
          return;
        }
        if (defs.disabled) {
          return;
        }
        if (defs.acl) {
          return this.getAcl().check(scope);
        }
        if (defs.tabAclPermission) {
          const level = this.getAcl().getPermissionLevel(defs.tabAclPermission);
          return level && level !== 'no';
        }
        return true;
      });
    }
  }
  _exports.default = _default;
});

define("views/preferences/fields/signature", ["exports", "views/fields/wysiwyg"], function (_exports, _wysiwyg) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _wysiwyg = _interopRequireDefault(_wysiwyg);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _wysiwyg.default {
    fetchEmptyValueAsNull = true;
    setupToolbar() {
      super.setupToolbar();
      const codeviewName = this.getConfig().get('wysiwygCodeEditorDisabled') ? 'codeview' : 'aceCodeview';
      this.toolbar = [["style", ["bold", "italic", "underline", "clear"]], ["color", ["color"]], ["height", ["height"]], ['table', ['espoLink']], ["misc", [codeviewName, "fullscreen"]]];
    }
  }
  _exports.default = _default;
});

define("views/preferences/fields/language", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _enum.default {
    setupOptions() {
      this.params.options = Espo.Utils.clone(this.getMetadata().get(['app', 'language', 'list']) || []).sort((v1, v2) => {
        return this.getLanguage().translateOption(v1, 'language').localeCompare(this.getLanguage().translateOption(v2, 'language'));
      });
      this.params.options.unshift('');
      this.translatedOptions = Espo.Utils.clone(this.getLanguage().translate('language', 'options') || {});
      const defaultTranslated = this.translatedOptions[this.getConfig().get('language')] || this.getConfig().get('language');
      this.translatedOptions[''] = `${this.translate('Default')} · ${defaultTranslated}`;
    }
  }
  _exports.default = _default;
});

define("views/preferences/fields/default-currency", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _enum.default {
    setupOptions() {
      this.params.options = Espo.Utils.clone(this.getConfig().get('currencyList') || []);
      this.params.options.unshift('');
      this.translatedOptions = this.translatedOptions || {};
      this.translatedOptions[''] = `${this.translate('Default')} · ${this.getConfig().get('defaultCurrency')}`;
    }
  }
  _exports.default = _default;
});

define("views/preferences/fields/date-format", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _enum.default {
    setupOptions() {
      this.params.options = Espo.Utils.clone(this.getMetadata().get(['app', 'dateTime', 'dateFormatList']) || []);
      this.params.options.unshift('');
      this.translatedOptions = this.translatedOptions || {};
      this.translatedOptions[''] = `${this.translate('Default')} · ${this.getConfig().get('dateFormat')}`;
    }
  }
  _exports.default = _default;
});

define("views/preferences/fields/dashboard-tab-list", ["exports", "views/fields/array"], function (_exports, _array) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _array = _interopRequireDefault(_array);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class _default extends _array.default {
    maxItemLength = 36;
    setup() {
      super.setup();
      this.translatedOptions = {};
      const list = this.model.get(this.name) || [];
      list.forEach(value => {
        this.translatedOptions[value] = value;
      });
      this.validations.push('uniqueLabel');
    }
    getItemHtml(value) {
      value = value.toString();
      const translatedValue = this.translatedOptions[value] || value;
      return $('<div>').addClass('list-group-item link-with-role form-inline').attr('data-value', value).append($('<div>').addClass('pull-left').css('width', '92%').css('display', 'inline-block').append($('<input>').attr('maxLength', this.maxItemLength).attr('data-name', 'translatedValue').attr('data-value', value).addClass('role form-control input-sm').attr('value', translatedValue).css('width', '65%'))).append($('<div>').css('width', '8%').css('display', 'inline-block').css('vertical-align', 'top').append($('<a>').attr('role', 'button').attr('tabindex', '0').addClass('pull-right').attr('data-value', value).attr('data-action', 'removeValue').append($('<span>').addClass('fas fa-times')))).append($('<br>').css('clear', 'both')).get(0).outerHTML;
    }

    /**
     * @private
     * @return {boolean}
     */
    validateUniqueLabel() {
      const keyList = this.model.get(this.name) || [];
      const labels = this.model.get('translatedOptions') || {};
      const metLabelList = [];
      for (const key of keyList) {
        const label = labels[key];
        if (!label) {
          return true;
        }
        if (metLabelList.indexOf(label) !== -1) {
          return true;
        }
        metLabelList.push(label);
      }
      return false;
    }
    fetch() {
      const data = super.fetch();
      data.translatedOptions = {};
      (data[this.name] || []).forEach(value => {
        const valueInternal = CSS.escape(value);
        data.translatedOptions[value] = this.$el.find(`input[data-name="translatedValue"][data-value="${valueInternal}"]`).val() || value;
      });
      return data;
    }
  }
  _exports.default = _default;
});

define("views/preferences/fields/calendar-slot-duration", ["exports", "views/fields/enum-int"], function (_exports, _enumInt) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enumInt = _interopRequireDefault(_enumInt);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class _default extends _enumInt.default {
    setupOptions() {
      super.setupOptions();
      this.translatedOptions = {};
      this.translatedOptions[''] = this.translate('Default');
    }
  }
  _exports.default = _default;
});

define("views/preferences/fields/calendar-scroll-hour", ["exports", "views/fields/enum-int", "moment"], function (_exports, _enumInt, _moment) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enumInt = _interopRequireDefault(_enumInt);
  _moment = _interopRequireDefault(_moment);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class PreferencesCalendarScrollHourView extends _enumInt.default {
    setupOptions() {
      super.setupOptions();
      this.translatedOptions = {};
      this.translatedOptions[''] = this.translate('Default');
      const timeFormat = this.getDateTime().getTimeFormat();
      const today = this.getDateTime().getToday();
      this.params.options.forEach(item => {
        if (item === '') {
          return;
        }
        const itemString = today + ' ' + item.toString().padStart(2, '0') + ':00';
        this.translatedOptions[item] = _moment.default.utc(itemString).format(timeFormat);
      });
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = PreferencesCalendarScrollHourView;
});

define("views/preferences/fields/auto-follow-entity-type-list", ["exports", "views/fields/multi-enum"], function (_exports, _multiEnum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _multiEnum = _interopRequireDefault(_multiEnum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class _default extends _multiEnum.default {
    setup() {
      this.params.options = Object.keys(this.getMetadata().get('scopes')).filter(scope => {
        if (this.getMetadata().get(`scopes.${scope}.disabled`)) {
          return;
        }
        return this.getMetadata().get(`scopes.${scope}.entity`) && this.getMetadata().get(`scopes.${scope}.stream`);
      }).sort((v1, v2) => {
        return this.translate(v1, 'scopeNamesPlural').localeCompare(this.translate(v2, 'scopeNamesPlural'));
      });
      super.setup();
    }
  }
  _exports.default = _default;
});

define("views/preferences/fields/assignment-notifications-ignore-entity-type-list", ["exports", "views/fields/checklist"], function (_exports, _checklist) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _checklist = _interopRequireDefault(_checklist);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class _default extends _checklist.default {
    isInversed = true;
    setupOptions() {
      this.params.options = Espo.Utils.clone(this.getConfig().get('assignmentNotificationsEntityList')) || [];
    }
  }
  _exports.default = _default;
});

define("views/preferences/fields/assignment-email-notifications-ignore-entity-type-list", ["exports", "views/fields/checklist"], function (_exports, _checklist) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _checklist = _interopRequireDefault(_checklist);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class _default extends _checklist.default {
    isInversed = true;
    setupOptions() {
      this.params.options = Espo.Utils.clone(this.getConfig().get('assignmentEmailNotificationsEntityList')) || [];
    }
  }
  _exports.default = _default;
});

define("views/portal-user/list", ["exports", "views/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class PortalUserListView extends _list.default {
    defaultOrderBy = 'createdAt';
    defaultOrder = 'desc';
    async actionCreate(data) {
      /**
       * @type {
       *     module:views/modals/select-records~Options &
       *     {onSkip: function()}
       * }
       */
      const options = {
        entityType: 'Contact',
        primaryFilterName: 'notPortalUsers',
        createButton: false,
        mandatorySelectAttributeList: ['salutationName', 'firstName', 'lastName', 'accountName', 'accountId', 'emailAddress', 'emailAddressData', 'phoneNumber', 'phoneNumberData'],
        onSelect: models => {
          const model = models[0];
          const attributes = {};
          attributes.contactId = model.id;
          attributes.contactName = model.attributes.name;
          if (model.attributes.accountId) {
            const names = {};
            names[model.attributes.accountId] = model.attributes.accountName;
            attributes.accountsIds = [model.attributes.accountId];
            attributes.accountsNames = names;
          }
          attributes.firstName = model.get('firstName');
          attributes.lastName = model.get('lastName');
          attributes.salutationName = model.get('salutationName');
          attributes.emailAddress = model.get('emailAddress');
          attributes.emailAddressData = model.get('emailAddressData');
          attributes.phoneNumber = model.get('phoneNumber');
          attributes.phoneNumberData = model.get('phoneNumberData');
          attributes.userName = attributes.emailAddress;
          if (attributes.userName) {
            attributes.userName = attributes.userName.toLowerCase();
          }
          attributes.type = 'portal';
          const url = `#${this.scope}/create`;
          this.getRouter().dispatch(this.scope, 'create', {
            attributes: attributes
          });
          this.getRouter().navigate(url, {
            trigger: false
          });
        },
        onSkip: () => {
          const attributes = {
            type: 'portal'
          };
          const url = `#${this.scope}/create`;
          this.getRouter().dispatch(this.scope, 'create', {
            attributes: attributes
          });
          this.getRouter().navigate(url, {
            trigger: false
          });
        }
      };

      // As the file is supposed to bundled separately, resort to async module loading.
      /** @type {typeof import('modules/crm/views/contact/modals/select-for-portal-user').default} */
      const SelectForPortalUserModalView = await Espo.loader.requirePromise('modules/crm/views/contact/modals/select-for-portal-user');
      const view = new SelectForPortalUserModalView(options);
      await this.assignView('modal', view);
      await view.render();
    }
  }
  var _default = _exports.default = PortalUserListView;
});

define("views/o-auth-provider/fields/authorization-params", ["exports", "views/fields/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * @type {{
   *     edit: import('ace-builds').edit,
   *     require: import('ace-builds').require,
   * }}
   */
  let ace;
  class OAuthProviderAuthorizationParamsFieldView extends _base.default {
    // language=Handlebars
    detailTemplateContent = `
        {{#if isNotEmpty}}
            <div id="{{containerId}}">{{value}}</div>
        {{else}}
            {{#if isSet}}
                <span class="none-value">{{translate 'None'}}</span>
            {{else}}
                <span class="loading-value"></span>
            {{/if}}
        {{/if}}
    `;

    // language=Handlebars
    editTemplateContent = `
        <div id="{{containerId}}">{{value}}</div>
    `;
    height = 46;
    maxLineDetailCount = 80;
    maxLineEditCount = 200;
    data() {
      const data = super.data();
      const value = this.model.attributes[this.name];
      data.containerId = this.containerId;
      data.isNotEmpty = value != null;
      data.isSet = value !== undefined;
      try {
        data.value = value ? JSON.stringify(value, null, '  ') : null;
      } catch (e) {
        data.value = null;
      }
      return data;
    }
    setup() {
      super.setup();
      this.height = this.options.height || this.params.height || this.height;
      this.maxLineDetailCount = this.options.maxLineDetailCount || this.params.maxLineDetailCount || this.maxLineDetailCount;
      this.maxLineEditCount = this.options.maxLineEditCount || this.params.maxLineEditCount || this.maxLineEditCount;
      this.containerId = 'editor-' + Math.floor(Math.random() * 10000 + 1).toString();
      if (this.mode === this.MODE_EDIT || this.mode === this.MODE_DETAIL) {
        this.wait(this.requireAce());
      }
      this.on('remove', () => {
        if (this.editor) {
          this.editor.destroy();
        }
      });
      this.validations.push(() => this.validateJson());
    }
    requireAce() {
      return Espo.loader.requirePromise('lib!ace').then(lib => {
        ace = lib;
        const list = [Espo.loader.requirePromise('lib!ace-ext-language_tools'), Espo.loader.requirePromise('lib!ace-mode-json')];
        if (this.getThemeManager().getParam('isDark')) {
          list.push(Espo.loader.requirePromise('lib!ace-theme-tomorrow_night'));
        }
        return Promise.all(list);
      });
    }
    afterRender() {
      super.afterRender();
      this.$editor = this.$el.find('#' + this.containerId);
      if (this.$editor.length && (this.mode === this.MODE_EDIT || this.mode === this.MODE_DETAIL || this.mode === this.MODE_LIST)) {
        this.$editor.css('fontSize', 'var(--font-size-base)');
        if (this.mode === this.MODE_EDIT) {
          this.$editor.css('minHeight', this.height + 'px');
        }
        const editor = this.editor = ace.edit(this.containerId);
        editor.setOptions({
          fontFamily: 'var(--font-family-monospace)'
        });
        editor.setFontSize('var(--font-size-base)');
        editor.container.style.lineHeight = 'var(--line-height-computed)';
        editor.renderer.updateFontSize();
        editor.setOptions({
          maxLines: this.mode === this.MODE_EDIT ? this.maxLineEditCount : this.maxLineDetailCount,
          enableLiveAutocompletion: true
        });
        if (this.getThemeManager().getParam('isDark')) {
          editor.setOptions({
            theme: 'ace/theme/tomorrow_night'
          });
        }
        if (this.isEditMode()) {
          editor.getSession().on('change', () => {
            this.trigger('change', {
              ui: true
            });
          });
          editor.getSession().setUseWrapMode(true);
        }
        if (this.isReadMode()) {
          editor.setReadOnly(true);
          editor.renderer.$cursorLayer.element.style.display = 'none';
          editor.renderer.setShowGutter(false);
        }
        editor.setShowPrintMargin(false);
        editor.getSession().setUseWorker(false);
        editor.commands.removeCommand('find');
        editor.setHighlightActiveLine(false);
        const Mode = ace.require('ace/mode/json').Mode;
        editor.session.setMode(new Mode());
      }
    }

    /**
     * @private
     * @return {boolean}
     */
    validateJson() {
      const raw = this.editor.getValue();
      if (!raw) {
        return false;
      }
      try {
        JSON.parse(raw);
      } catch (e) {
        const message = this.translate('Not valid');
        this.showValidationMessage(message, '.ace_editor');
        return true;
      }
      return false;
    }
    fetch() {
      let value = null;
      const raw = this.editor.getValue();
      if (!raw) {
        return {
          [this.name]: null
        };
      }
      try {
        value = JSON.parse(raw);
      } catch (e) {}
      return {
        [this.name]: value
      };
    }
  }
  _exports.default = OAuthProviderAuthorizationParamsFieldView;
});

define("views/o-auth-account/records/panels/connection", ["exports", "views/record/panels/side"], function (_exports, _side) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _side = _interopRequireDefault(_side);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class OAuthAccountConnectionPanelView extends _side.default {
    // language=Handlebars
    templateContent = `
        {{#if hasDisconnect}}
            <div class="margin-bottom">
                <span
                    class="label label-success label-md"
                >{{translate 'Connected' scope='ExternalAccount'}}</span>
            </div>
            <button
                class="btn btn-default"
                data-action="disconnect"
            >{{translate 'Disconnect' scope='ExternalAccount'}}</button>
        {{/if}}

        {{#if hasConnect}}
            <div class="margin-bottom">
                <span
                    class="label label-default label-md"
                >{{translate 'Disconnected' scope='ExternalAccount'}}</span>
            </div>
            <button
                class="btn btn-default"
                data-action="connect"
            >{{translate 'Connect' scope='ExternalAccount'}}</button>
        {{/if}}
    `;

    /**
     * @private
     * @type {boolean}
     */
    inProcess = false;
    data() {
      const isSet = this.model.attributes.hasAccessToken !== undefined;
      const hasDisconnect = !this.inProcess && isSet && this.model.attributes.hasAccessToken;
      const hasConnect = !this.inProcess && isSet && !this.model.attributes.hasAccessToken && this.model.attributes.providerIsActive;

      // noinspection JSValidateTypes
      return {
        hasDisconnect,
        hasConnect
      };
    }
    setup() {
      super.setup();
      this.listenTo(this.model, 'sync', () => this.reRender());
      this.addActionHandler('connect', () => this.actionConnect());
      this.addActionHandler('disconnect', () => this.actionDisconnect());
    }

    /**
     * @private
     */
    async actionDisconnect() {
      this.inProcess = true;
      await this.reRender();
      Espo.Ui.notifyWait();
      await Espo.Ajax.deleteRequest(`OAuth/${this.model.id}/connection`);
      await this.model.fetch();
      Espo.Ui.notify();
      this.inProcess = false;
      await this.reRender();
    }

    /**
     * @private
     */
    async actionConnect() {
      const data = this.model.attributes.data || {};
      const endpoint = data.endpoint;
      const redirectUri = data.redirectUri;
      const clientId = data.clientId;
      const scope = data.scope;
      const prompt = data.prompt;
      const params = data.params;
      const proxy = window.open('about:blank', 'ConnectWithOAuth', 'location=0,status=0,width=800,height=800');
      const info = await this.processWithData({
        endpoint,
        redirectUri,
        clientId,
        scope,
        prompt,
        params
      }, proxy);
      this.inProcess = true;
      await this.reRender();
      Espo.Ui.notifyWait();
      try {
        await Espo.Ajax.postRequest(`OAuth/${this.model.id}/connection`, {
          code: info.code
        });
      } catch (e) {
        this.inProcess = false;
        await this.reRender();
        return;
      }
      await this.model.fetch();
      Espo.Ui.notify();
      this.inProcess = false;
      await this.reRender();
    }

    /**
     * @private
     * @param {{
     *     endpoint: string,
     *     clientId: string,
     *     redirectUri: string,
     *     scope: string|null,
     *     prompt: string,
     *     params: Record|null,
     * }} data
     * @param {WindowProxy} proxy
     * @return {Promise<{code: string}>}
     */
    processWithData(data, proxy) {
      const state = undefined;
      const params = {
        client_id: data.clientId,
        redirect_uri: data.redirectUri,
        response_type: 'code',
        prompt: data.prompt
      };
      if (data.scope) {
        params.scope = data.scope;
      }
      if (data.params) {
        for (const name in data.params) {
          params[name] = data.params[name];
        }
      }
      const partList = Object.entries(params).map(_ref => {
        let [key, value] = _ref;
        return key + '=' + encodeURIComponent(value);
      });
      const url = data.endpoint + '?' + partList.join('&');
      return this.processWindow(url, state, proxy);
    }

    /**
     * @private
     * @param {string} url
     * @param {string} state
     * @param {WindowProxy} proxy
     * @return {Promise<{code: string}>}
     */
    processWindow(url, state, proxy) {
      proxy.location.href = url;
      return new Promise((resolve, reject) => {
        const fail = () => {
          window.clearInterval(interval);
          if (!proxy.closed) {
            proxy.close();
          }
          reject();
        };
        const interval = window.setInterval(() => {
          if (proxy.closed) {
            fail();
            return;
          }
          let url;
          try {
            url = proxy.location.href;
          } catch (e) {
            return;
          }
          if (!url) {
            return;
          }
          const parsedData = this.parseWindowUrl(url);
          if (!parsedData) {
            fail();
            Espo.Ui.error('Could not parse URL', true);
            return;
          }
          if ((parsedData.error || parsedData.code) && state && parsedData.state !== state) {
            fail();
            Espo.Ui.error('State mismatch', true);
            return;
          }
          if (parsedData.error) {
            fail();
            Espo.Ui.error(parsedData.errorDescription || this.translate('Error'), true);
            return;
          }
          if (parsedData.code) {
            window.clearInterval(interval);
            proxy.close();
            resolve({
              code: parsedData.code
            });
          }
        }, 300);
      });
    }

    /**
     * @private
     * @param {string} url
     * @return {?{
     *     code: ?string,
     *     state: ?string,
     *     error: ?string,
     *     errorDescription: ?string,
     * }}
     */
    parseWindowUrl(url) {
      try {
        const params = new URL(url).searchParams;
        return {
          code: params.get('code'),
          state: params.get('state'),
          error: params.get('error'),
          errorDescription: params.get('errorDescription')
        };
      } catch (e) {
        return null;
      }
    }
  }
  _exports.default = OAuthAccountConnectionPanelView;
});

define("views/notification/panel", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NotificationPanelView extends _view.default {
    template = 'notification/panel';
    setup() {
      this.addActionHandler('markAllNotificationsRead', () => this.actionMarkAllRead());
      this.addActionHandler('openNotifications', () => this.actionOpenNotifications());
      this.addActionHandler('closePanel', () => this.close());
      this.addHandler('keydown', '', /** KeyboardEvent */event => {
        if (event.code === 'Escape') {
          this.close();
        }
      });
      const promise = this.getCollectionFactory().create('Notification', collection => {
        this.collection = collection;
        this.collection.maxSize = this.getConfig().get('notificationsMaxSize') || 5;
        this.listenTo(this.collection, 'sync', () => {
          this.trigger('collection-fetched');
        });
      });
      this.wait(promise);
      this.navbarPanelHeightSpace = this.getThemeManager().getParam('navbarPanelHeightSpace') || 100;
      this.navbarPanelBodyMaxHeight = this.getThemeManager().getParam('navbarPanelBodyMaxHeight') || 600;
      this.once('remove', () => {
        $(window).off('resize.notifications-height');
        if (this.overflowWasHidden) {
          $('body').css('overflow', 'unset');
          this.overflowWasHidden = false;
        }
        if (this.collection) {
          this.collection.abortLastFetch();
        }
      });
    }
    afterRender() {
      this.collection.fetch().then(() => this.createRecordView()).then(view => view.render());
      const $window = $(window);
      $window.off('resize.notifications-height');
      $window.on('resize.notifications-height', this.processSizing.bind(this));
      this.processSizing();
      $('#navbar li.notifications-badge-container').addClass('open');
      this.$el.find('> .panel').focus();
    }
    onRemove() {
      $('#navbar li.notifications-badge-container').removeClass('open');
    }

    /**
     * @return {Promise<module:views/record/list-expanded>}
     */
    createRecordView() {
      const viewName = this.getMetadata().get(['clientDefs', 'Notification', 'recordViews', 'list']) || 'views/notification/record/list';
      return /** @type {Promise<module:views/record/list-expanded>} */this.createView('list', viewName, {
        selector: '.list-container',
        collection: this.collection,
        showCount: false,
        listLayout: {
          rows: [[{
            name: 'data',
            view: 'views/notification/fields/container',
            options: {
              containerSelector: this.getSelector()
            }
          }]],
          right: {
            name: 'read',
            view: 'views/notification/fields/read',
            width: '10px'
          }
        }
      });
    }
    actionMarkAllRead() {
      Espo.Ajax.postRequest('Notification/action/markAllRead').then(() => this.trigger('all-read'));
    }
    processSizing() {
      const $window = $(window);
      const windowHeight = $window.height();
      const windowWidth = $window.width();
      const diffHeight = this.$el.find('.panel-heading').outerHeight();
      const cssParams = {};
      if (windowWidth <= this.getThemeManager().getParam('screenWidthXs')) {
        cssParams.height = windowHeight - diffHeight + 'px';
        cssParams.overflow = 'auto';
        $('body').css('overflow', 'hidden');
        this.overflowWasHidden = true;
        this.$el.find('.panel-body').css(cssParams);
        return;
      }
      cssParams.height = 'unset';
      cssParams.overflow = 'none';
      if (this.overflowWasHidden) {
        $('body').css('overflow', 'unset');
        this.overflowWasHidden = false;
      }
      if (windowHeight - this.navbarPanelBodyMaxHeight < this.navbarPanelHeightSpace) {
        const maxHeight = windowHeight - this.navbarPanelHeightSpace;
        cssParams.maxHeight = maxHeight + 'px';
      }
      this.$el.find('.panel-body').css(cssParams);
    }
    close() {
      this.trigger('close');
    }
    actionOpenNotifications() {
      this.getRouter().navigate('#Notification', {
        trigger: true
      });
      this.close();
    }
  }
  var _default = _exports.default = NotificationPanelView;
});

define("views/notification/list", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NotificationListView extends _view.default {
    template = 'notification/list';
    setup() {
      this.addActionHandler('refresh', () => this.actionRefresh());
      this.addActionHandler('markAllNotificationsRead', () => this.actionMarkAllRead());
      const promise = this.getCollectionFactory().create('Notification').then(collection => {
        this.collection = collection;
        this.collection.maxSize = this.getConfig().get('recordsPerPage') || 20;
      });
      this.wait(promise);
    }
    actionRefresh() {
      Espo.Ui.notifyWait();
      const $btn = this.$el.find('[data-action="refresh"]');
      $btn.addClass('disabled').attr('disabled', 'disabled');
      this.animateRefreshButton();
      this.getRecordView().showNewRecords().then(() => {
        Espo.Ui.notify(false);
      }).finally(() => $btn.removeClass('disabled').removeAttr('disabled'));
    }
    animateRefreshButton() {
      const iconEl = this.element.querySelector('button[data-action="refresh"] span');
      if (iconEl) {
        iconEl.classList.add('animation-spin-fast');
        setTimeout(() => iconEl.classList.remove('animation-spin-fast'), 500);
      }
    }
    afterRender() {
      const viewName = this.getMetadata().get(['clientDefs', 'Notification', 'recordViews', 'list']) || 'views/notification/record/list';
      const options = {
        selector: '.list-container',
        collection: this.collection,
        showCount: false,
        listLayout: {
          rows: [[{
            name: 'data',
            view: 'views/notification/fields/container',
            options: {
              containerSelector: this.getSelector()
            }
          }]],
          right: {
            name: 'read',
            view: 'views/notification/fields/read-with-menu',
            width: '10px'
          }
        }
      };
      this.collection.fetch().then(() => this.createView('list', viewName, options)).then(view => view.render()).then(view => {
        view.$el.find('> .list > .list-group');
      });
    }
    actionMarkAllRead() {
      Espo.Ui.notifyWait();
      const $link = this.$el.find('[data-action="markAllNotificationsRead"]');
      $link.attr('disabled', 'disabled').addClass('disabled');
      Espo.Ajax.postRequest('Notification/action/markAllRead').then(() => {
        this.trigger('all-read');
        Espo.Ui.notify(false);
        this.$el.find('.badge-circle-warning').remove();
      }).finally(() => $link.removeAttr('disabled').removeClass('disabled'));
    }

    /**
     * @return {module:views/notification/record/list}
     */
    getRecordView() {
      return this.getView('list');
    }
  }
  var _default = _exports.default = NotificationListView;
});

define("views/notification/badge", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NotificationBadgeView extends _view.default {
    template = 'notification/badge';
    notificationsCheckInterval = 10;
    groupedCheckInterval = 15;
    waitInterval = 2;

    /** @private */
    useWebSocket = false;
    timeout = null;
    groupedTimeout = null;

    /**
     * @type {Object.<string, {
     *     portalDisabled?: boolean,
     *     grouped?: boolean,
     *     disabled?: boolean,
     *     interval?: Number,
     *     url?: string,
     *     useWebSocket?: boolean,
     *     view?: string,
     *     webSocketCategory?: string,
     * }>}
     */
    popupNotificationsData;
    soundPath = 'client/sounds/pop_cork';
    setup() {
      this.addActionHandler('showNotifications', () => this.showNotifications());
      this.soundPath = this.getBasePath() + (this.getConfig().get('notificationSound') || this.soundPath);
      this.notificationSoundsDisabled = true;
      this.useWebSocket = !!this.getHelper().webSocketManager;
      const clearTimeouts = () => {
        if (this.timeout) {
          clearTimeout(this.timeout);
        }
        if (this.groupedTimeout) {
          clearTimeout(this.groupedTimeout);
        }
        for (const name in this.popupTimeouts) {
          clearTimeout(this.popupTimeouts[name]);
        }
      };
      this.once('remove', () => clearTimeouts());
      this.listenToOnce(this.getHelper().router, 'logout', () => clearTimeouts());
      this.notificationsCheckInterval = this.getConfig().get('notificationsCheckInterval') || this.notificationsCheckInterval;
      this.groupedCheckInterval = this.getConfig().get('popupNotificationsCheckInterval') || this.groupedCheckInterval;
      this.lastId = 0;
      this.shownNotificationIds = [];
      this.closedNotificationIds = [];
      this.popupTimeouts = {};
      delete localStorage['messageBlockPlayNotificationSound'];
      delete localStorage['messageClosePopupNotificationId'];
      delete localStorage['messageNotificationRead'];
      window.addEventListener('storage', e => {
        if (e.key === 'messageClosePopupNotificationId') {
          const id = localStorage.getItem('messageClosePopupNotificationId');
          if (id) {
            const key = 'popup-' + id;
            if (this.hasView(key)) {
              this.markPopupRemoved(id);
              this.clearView(key);
            }
          }
        }
        if (e.key === 'messageNotificationRead') {
          if (!this.isBroadcastingNotificationRead && localStorage.getItem('messageNotificationRead')) {
            this.checkUpdates();
          }
        }
      }, false);
    }
    afterRender() {
      this.$badge = this.$el.find('.notifications-button');
      this.$number = this.$el.find('.number-badge');
      this.runCheckUpdates(true);
      this.$popupContainer = $('#popup-notifications-container');
      if (!$(this.$popupContainer).length) {
        this.$popupContainer = $('<div>').attr('id', 'popup-notifications-container').addClass('hidden').appendTo('body');
      }
      const popupNotificationsData = this.popupNotificationsData = this.getMetadata().get('app.popupNotifications') || {};
      for (const name in popupNotificationsData) {
        this.checkPopupNotifications(name);
      }
      if (this.hasGroupedPopupNotifications()) {
        this.checkGroupedPopupNotifications();
      }
    }
    playSound() {
      if (this.notificationSoundsDisabled) {
        return;
      }
      const audioElement = /** @type {HTMLAudioElement} */$('<audio>').attr('autoplay', 'autoplay').append($('<source>').attr('src', this.soundPath + '.mp3').attr('type', 'audio/mpeg')).append($('<source>').attr('src', this.soundPath + '.ogg').attr('type', 'audio/ogg')).append($('<embed>').attr('src', this.soundPath + '.mp3').attr('hidden', 'true').attr('autostart', 'true').attr('false', 'false')).get(0);
      audioElement.volume = 0.3;
      audioElement.play();
    }

    /**
     * @private
     * @param {number} count
     */
    showNotRead(count) {
      this.$badge.attr('title', this.translate('New notifications') + ': ' + count);
      this.$number.removeClass('hidden').html(count.toString());
      this.getHelper().pageTitle.setNotificationNumber(count);
    }

    /**
     * @private
     */
    hideNotRead() {
      this.$badge.attr('title', this.translate('Notifications'));
      this.$number.addClass('hidden').html('');
      this.getHelper().pageTitle.setNotificationNumber(0);
    }

    /**
     * @private
     */
    checkBypass() {
      const last = this.getRouter().getLast() || {};
      const pageAction = (last.options || {}).page || null;
      if (last.controller === 'Admin' && last.action === 'page' && ['upgrade', 'extensions'].includes(pageAction)) {
        return true;
      }
      return false;
    }

    /**
     * @private
     * @param {boolean} [isFirstCheck]
     */
    async checkUpdates(isFirstCheck) {
      if (this.checkBypass()) {
        return;
      }

      /** @type {number} */
      const count = await Espo.Ajax.getRequest('Notification/action/notReadCount');
      if (!isFirstCheck && count > this.unreadCount) {
        const blockSound = localStorage.getItem('messageBlockPlayNotificationSound');
        if (!blockSound) {
          this.playSound();
          localStorage.setItem('messageBlockPlayNotificationSound', 'true');
          setTimeout(() => {
            delete localStorage['messageBlockPlayNotificationSound'];
          }, this.notificationsCheckInterval * 1000);
        }
      }
      this.unreadCount = count;
      if (count) {
        this.showNotRead(count);
        return;
      }
      this.hideNotRead();
    }
    runCheckUpdates(isFirstCheck) {
      this.checkUpdates(isFirstCheck);
      if (this.useWebSocket) {
        this.initWebSocketCheckUpdates();
        return;
      }
      this.timeout = setTimeout(() => this.runCheckUpdates(), this.notificationsCheckInterval * 1000);
    }

    /**
     * @private
     */
    initWebSocketCheckUpdates() {
      let isBlocked = false;
      let hasBeenBlocked = false;
      const onWebSocketNewNotification = () => {
        if (isBlocked) {
          hasBeenBlocked = true;
          return;
        }
        this.checkUpdates();
        isBlocked = true;
        setTimeout(() => {
          const reRun = hasBeenBlocked;
          isBlocked = false;
          hasBeenBlocked = false;
          if (reRun) {
            onWebSocketNewNotification();
          }
        }, this.waitInterval * 1000);
      };
      const webSocketManager = this.getHelper().webSocketManager;
      webSocketManager.subscribe('newNotification', () => onWebSocketNewNotification());
      webSocketManager.subscribeToReconnect(onWebSocketNewNotification);
      this.once('remove', () => webSocketManager.unsubscribe('newNotification'));
      this.once('remove', () => webSocketManager.unsubscribeFromReconnect(onWebSocketNewNotification));
    }

    /**
     * @private
     * @return {boolean}
     */
    hasGroupedPopupNotifications() {
      for (const name in this.popupNotificationsData) {
        const data = this.popupNotificationsData[name] || {};
        if (!data.grouped) {
          continue;
        }
        if (data.portalDisabled && this.getUser().isPortal()) {
          continue;
        }
        return true;
      }
      return false;
    }

    /**
     * @private
     */
    checkGroupedPopupNotifications() {
      if (!this.checkBypass()) {
        Espo.Ajax.getRequest('PopupNotification/action/grouped').then(result => {
          for (const type in result) {
            const list = result[type];
            list.forEach(item => this.showPopupNotification(type, item));
          }
        });
      }
      if (this.useWebSocket) {
        return;
      }
      this.groupedTimeout = setTimeout(() => this.checkGroupedPopupNotifications(), this.groupedCheckInterval * 1000);
    }
    checkPopupNotifications(name, isNotFirstCheck) {
      const data = this.popupNotificationsData[name] || {};
      const url = data.url;
      const interval = data.interval;
      const disabled = data.disabled || false;
      if (disabled) {
        return;
      }
      if (data.portalDisabled && this.getUser().isPortal()) {
        return;
      }
      const useWebSocket = this.useWebSocket && data.useWebSocket;
      if (useWebSocket) {
        const category = 'popupNotifications.' + (data.webSocketCategory || name);
        this.getHelper().webSocketManager.subscribe(category, (c, response) => {
          if (!response.list) {
            return;
          }
          response.list.forEach(item => {
            this.showPopupNotification(name, item);
          });
        });
      }
      if (data.grouped) {
        return;
      }
      if (!url) {
        return;
      }
      if (!interval) {
        return;
      }
      new Promise(resolve => {
        if (this.checkBypass()) {
          resolve();
          return;
        }
        Espo.Ajax.getRequest(url).then(list => list.forEach(item => this.showPopupNotification(name, item, isNotFirstCheck))).finally(() => resolve());
      }).then(() => {
        if (useWebSocket) {
          return;
        }
        this.popupTimeouts[name] = setTimeout(() => this.checkPopupNotifications(name, true), interval * 1000);
      });
    }
    showPopupNotification(name, data, isNotFirstCheck) {
      const view = this.popupNotificationsData[name].view;
      if (!view) {
        return;
      }
      let id = data.id || null;
      if (id) {
        id = name + '_' + id;
        if (~this.shownNotificationIds.indexOf(id)) {
          const notificationView = this.getView('popup-' + id);
          if (notificationView) {
            notificationView.trigger('update-data', data.data);
          }
          return;
        }
        if (~this.closedNotificationIds.indexOf(id)) {
          return;
        }
      } else {
        id = this.lastId++;
      }
      this.shownNotificationIds.push(id);
      this.createView('popup-' + id, view, {
        notificationData: data.data || {},
        notificationId: data.id,
        id: id,
        isFirstCheck: !isNotFirstCheck
      }, view => {
        view.render();
        this.$popupContainer.removeClass('hidden');
        this.listenTo(view, 'remove', () => {
          this.markPopupRemoved(id);
          localStorage.setItem('messageClosePopupNotificationId', id);
        });
      });
    }
    markPopupRemoved(id) {
      const index = this.shownNotificationIds.indexOf(id);
      if (index > -1) {
        this.shownNotificationIds.splice(index, 1);
      }
      if (this.shownNotificationIds.length === 0) {
        this.$popupContainer.addClass('hidden');
      }
      this.closedNotificationIds.push(id);
    }
    broadcastNotificationsRead() {
      if (!this.useWebSocket) {
        return;
      }
      this.isBroadcastingNotificationRead = true;
      localStorage.setItem('messageNotificationRead', 'true');
      setTimeout(() => {
        this.isBroadcastingNotificationRead = false;
        delete localStorage['messageNotificationRead'];
      }, 500);
    }
    showNotifications() {
      this.closeNotifications();
      const $container = $('<div>').attr('id', 'notifications-panel');
      $container.appendTo(this.$el.find('.notifications-panel-container'));
      this.createView('panel', 'views/notification/panel', {
        fullSelector: '#notifications-panel'
      }, view => {
        view.render();
        this.$el.closest('.navbar-body').removeClass('in');
        this.listenTo(view, 'all-read', () => {
          this.hideNotRead();
          this.$el.find('.badge-circle-warning').remove();
          this.broadcastNotificationsRead();
        });
        this.listenTo(view, 'collection-fetched', () => {
          this.checkUpdates();
          this.broadcastNotificationsRead();
        });
        this.listenToOnce(view, 'close', () => {
          this.closeNotifications();
        });
      });
      const $document = $(document);
      $document.on('mouseup.notification', e => {
        if (!$container.is(e.target) && $container.has(e.target).length === 0 && !$(e.target).closest('div.modal-dialog').length && !e.target.classList.contains('modal')) {
          this.closeNotifications();
        }
      });
      if (window.innerWidth < this.getThemeManager().getParam('screenWidthXs')) {
        this.listenToOnce(this.getRouter(), 'route', () => {
          this.closeNotifications();
        });
      }
    }
    closeNotifications() {
      const $container = $('#notifications-panel');
      $container.remove();
      const $document = $(document);
      if (this.hasView('panel')) {
        this.getView('panel').remove();
      }
      $document.off('mouseup.notification');
    }
  }
  var _default = _exports.default = NotificationBadgeView;
});

define("views/notification/record/list", ["exports", "views/record/list-expanded"], function (_exports, _listExpanded) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _listExpanded = _interopRequireDefault(_listExpanded);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/notification/record/list */

  class NotificationListRecordView extends _listExpanded.default {
    /**
     * @name collection
     * @type module:collections/note
     * @memberOf NotificationListRecordView#
     */

    setup() {
      super.setup();
      this.listenTo(this.collection, 'sync', (c, r, options) => {
        if (!options.fetchNew) {
          return;
        }
        const lengthBeforeFetch = options.lengthBeforeFetch || 0;
        if (lengthBeforeFetch === 0) {
          this.reRender();
          return;
        }
        const $list = this.$el.find(this.listContainerEl);
        const rowCount = this.collection.length - lengthBeforeFetch;
        for (let i = rowCount - 1; i >= 0; i--) {
          const model = this.collection.at(i);
          $list.prepend($(this.getRowContainerHtml(model.id)));
          this.buildRow(i, model, view => {
            view.render();
          });
        }
      });
      this.events['auxclick a[href][data-scope][data-id]'] = e => {
        const isCombination = e.button === 1 && (e.ctrlKey || e.metaKey);
        if (!isCombination) {
          return;
        }
        const $target = $(e.currentTarget);
        const id = $target.attr('data-id');
        const scope = $target.attr('data-scope');
        e.preventDefault();
        e.stopPropagation();
        this.actionQuickView({
          id: id,
          scope: scope
        });
      };
    }

    /**
     * @return {Promise}
     */
    showNewRecords() {
      return this.collection.fetchNew();
    }
  }
  var _default = _exports.default = NotificationListRecordView;
});

define("views/notification/items/user-reaction", ["exports", "views/notification/items/base", "helpers/misc/reactions"], function (_exports, _base, _reactions) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _reactions = _interopRequireDefault(_reactions);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class UserReactionNotificationItemView extends _base.default {
    // language=Handlebars
    templateContent = `
        <div class="stream-head-container">
            <div class="pull-left">
                {{{avatar}}}
            </div>
            <div class="stream-head-text-container">
                <span
                    class="{{reactionIconClass}} text-muted action icon"
                    style="cursor: pointer;"
                    title="{{translate 'View'}}"
                    data-action="quickView"
                    data-id="{{noteId}}"
                    data-scope="Note"
                ></span><span class="text-muted message">{{{message}}}</span>
            </div>
        </div>

        <div class="stream-date-container">
            <span class="text-muted small">{{{createdAt}}}</span>
        </div>
    `;
    messageName = 'userPostReaction';

    /**
     * @private
     * @type {string|null}
     */
    reactionIconClass;

    /**
     * @private
     * @type {string}
     */
    noteId;
    data() {
      return {
        ...super.data(),
        reactionIconClass: this.reactionIconClass,
        noteId: this.noteId
      };
    }
    setup() {
      const data = /** @type {Object.<string, *>} */this.model.attributes.data || {};
      const relatedParentId = this.model.attributes.relatedParentId;
      const relatedParentType = this.model.attributes.relatedParentType;
      this.userId = this.model.attributes.createdById || data.userId;
      this.noteId = this.model.attributes.relatedId;
      const userName = data.userName || this.model.attributes.createdByName;
      this.messageData['type'] = this.translate(data.type, 'reactions');
      const reactionsHelper = new _reactions.default();
      this.reactionIconClass = reactionsHelper.getIconClass(data.type);
      const userElement = document.createElement('a');
      userElement.href = `#User/view/${this.model.attributes.createdById}`;
      userElement.dataset.id = this.model.attributes.createdById;
      userElement.dataset.scope = 'User';
      userElement.textContent = userName;
      this.messageData['user'] = userElement;
      if (relatedParentId && relatedParentType) {
        const relatedParentElement = document.createElement('a');
        relatedParentElement.href = `#${relatedParentType}/view/${relatedParentId}`;
        relatedParentElement.dataset.id = relatedParentId;
        relatedParentElement.dataset.scope = relatedParentType;
        relatedParentElement.textContent = data.entityName || relatedParentType;
        this.messageData['entityType'] = this.translateEntityType(relatedParentType);
        this.messageData['entity'] = relatedParentElement;
        this.messageName = 'userPostInParentReaction';
      }
      let postLabel = this.getLanguage().translateOption('Post', 'type', 'Note');
      if (!this.toUpperCaseFirstLetter()) {
        postLabel = Espo.Utils.lowerCaseFirst(postLabel);
      }
      const postElement = document.createElement('a');
      postElement.href = `#Note/view/${this.noteId}`;
      postElement.dataset.id = this.noteId;
      postElement.dataset.scope = 'Note';
      postElement.textContent = postLabel;
      this.messageData['post'] = postElement;
      this.createMessage();
    }
  }
  _exports.default = UserReactionNotificationItemView;
});

define("views/notification/items/system", ["exports", "views/notification/items/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class SystemNotificationItemView extends _base.default {
    template = 'notification/items/system';
    data() {
      return {
        ...super.data(),
        message: this.model.get('message')
      };
    }
    setup() {
      const data = this.model.get('data') || {};
      this.userId = data.userId;
    }
  }
  var _default = _exports.default = SystemNotificationItemView;
});

define("views/notification/items/message", ["exports", "views/notification/items/base", "dompurify"], function (_exports, _base, _dompurify) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _dompurify = _interopRequireDefault(_dompurify);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class MessageNotificationItemView extends _base.default {
    template = 'notification/items/message';
    data() {
      return {
        ...super.data(),
        style: this.style
      };
    }
    setup() {
      const data = /** @type {Object.<string, *>} */this.model.get('data') || {};
      const messageRaw = this.model.get('message') || data.message || '';
      const message = this.getHelper().transformMarkdownText(messageRaw);
      this.messageTemplate = _dompurify.default.sanitize(message, {}).toString();
      this.userId = data.userId;
      this.style = data.style || 'text-muted';
      this.messageData['entityType'] = this.translateEntityType(data.entityType);
      this.messageData['user'] = $('<a>').attr('href', '#User/view/' + data.userId).attr('data-id', data.userId).attr('data-scope', 'User').text(data.userName);
      this.messageData['entity'] = $('<a>').attr('href', '#' + data.entityType + '/view/' + data.entityId).attr('data-id', data.entityId).attr('data-scope', data.entityType).text(data.entityName);
      this.createMessage();
    }
  }
  var _default = _exports.default = MessageNotificationItemView;
});

define("views/notification/items/entity-removed", ["exports", "views/notification/items/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailRemovedNotificationItemView extends _base.default {
    messageName = 'entityRemoved';
    template = 'notification/items/entity-removed';
    setup() {
      const data = /** @type {Object.<string, *>} */this.model.get('data') || {};
      this.userId = data.userId;
      this.messageData['entityType'] = this.translateEntityType(data.entityType);
      this.messageData['user'] = $('<a>').attr('href', '#User/view/' + data.userId).attr('data-id', data.userId).attr('data-scope', 'User').text(data.userName);
      this.messageData['entity'] = $('<a>').attr('href', '#' + data.entityType + '/view/' + data.entityId).attr('data-id', data.entityId).attr('data-scope', data.entityType).text(data.entityName);
      this.createMessage();
    }
  }
  var _default = _exports.default = EmailRemovedNotificationItemView;
});

define("views/notification/items/email-received", ["exports", "views/notification/items/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailReceivedNotificationItemView extends _base.default {
    messageName = 'emailReceived';
    template = 'notification/items/email-received';
    data() {
      return {
        ...super.data(),
        emailId: this.emailId,
        emailName: this.emailName
      };
    }
    setup() {
      const data = /** @type {Record} */this.model.get('data') || {};
      this.userId = data.userId;
      this.messageData['entityType'] = this.translateEntityType(data.entityType);
      if (data.personEntityId) {
        this.messageData['from'] = $('<a>').attr('href', '#' + data.personEntityType + '/view/' + data.personEntityId).attr('data-id', data.personEntityId).attr('data-scope', data.personEntityType).text(data.personEntityName);
      } else {
        const text = data.fromString || this.translate('empty address');
        this.messageData['from'] = $('<span>').text(text);
      }
      this.emailId = data.emailId;
      this.emailName = data.emailName;
      this.createMessage();
    }
  }
  var _default = _exports.default = EmailReceivedNotificationItemView;
});

define("views/notification/items/email-inbox", ["exports", "views/notification/items/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailInboxNotificationItemView extends _base.default {
    template = 'notification/items/system';
    messageName = 'emailInbox';

    // language=Handlebars
    templateContent = `
        <div class="stream-head-container">
            <div class="pull-left">{{{avatar}}}</div>
            <div class="stream-head-text-container">
                <span
                    class="fas fa-envelope text-muted action icon"
                    style="cursor: pointer;"
                    title="{{translate 'View'}}"
                    data-action="quickView"
                    data-id="{{model.attributes.relatedId}}"
                    data-scope="Email"
                ></span><span class="text-muted message">{{{message}}}</span>
            </div>
        </div>
        <div class="stream-date-container">
            <span class="text-muted small">{{{createdAt}}}</span>
        </div>
    `;
    setup() {
      /** @type {{userId: string, userName: string, emailName: string}} */
      const data = this.model.attributes.data || {};
      this.userId = data.userId;
      this.messageData['entityType'] = this.translateEntityType('Email');
      const entity = document.createElement('a');
      entity.href = `#Email/view/${this.model.attributes.relatedId}`;
      entity.dataset.id = this.model.attributes.relatedId;
      entity.dataset.scope = 'Email';
      entity.innerText = data.emailName;
      const user = document.createElement('a');
      user.href = `#User/view/${data.userId}`;
      user.dataset.id = data.userId;
      user.dataset.scope = 'User';
      user.innerText = data.userName;
      this.messageData['entity'] = entity;
      this.messageData['user'] = user;
      this.createMessage();
    }
  }
  var _default = _exports.default = EmailInboxNotificationItemView;
});

define("views/notification/items/assign", ["exports", "views/notification/items/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class AssignNotificationItemView extends _base.default {
    messageName = 'assign';
    template = 'notification/items/assign';
    setup() {
      const data = this.model.get('data') || {};
      this.userId = data.userId;
      this.messageData['entityType'] = this.translateEntityType(data.entityType);
      this.messageData['entity'] = $('<a>').attr('href', '#' + data.entityType + '/view/' + data.entityId).attr('data-id', data.entityId).attr('data-scope', data.entityType).text(data.entityName);
      this.messageData['user'] = $('<a>').attr('href', '#User/view/' + data.userId).attr('data-id', data.userId).attr('data-scope', 'User').text(data.userName);
      this.createMessage();
    }
  }
  var _default = _exports.default = AssignNotificationItemView;
});

define("views/notification/fields/read", ["exports", "views/fields/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NotificationReadFieldView extends _base.default {
    type = 'read';
    listTemplate = 'notification/fields/read';
    detailTemplate = 'notification/fields/read';
    inlineEditDisabled = true;
    data() {
      return {
        isRead: this.model.get('read')
      };
    }
  }
  var _default = _exports.default = NotificationReadFieldView;
});

define("views/notification/fields/read-with-menu", ["exports", "views/fields/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NotificationReadWithMenuFieldView extends _base.default {
    type = 'read';
    listTemplate = 'notification/fields/read-with-menu';
    detailTemplate = 'notification/fields/read-with-menu';
    inlineEditDisabled = true;
    data() {
      return {
        isRead: this.model.get('read')
      };
    }
  }
  var _default = _exports.default = NotificationReadWithMenuFieldView;
});

define("views/notification/fields/container", ["exports", "views/fields/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NotificationContainerFieldView extends _base.default {
    type = 'notification';
    listTemplate = 'notification/fields/container';
    detailTemplate = 'notification/fields/container';
    types = ['Assign', 'EmailReceived', 'EntityRemoved', 'Message', 'System', 'UserReaction'];
    inlineEditDisabled = true;
    setup() {
      switch (this.model.attributes.type) {
        case 'Note':
          this.processNote(this.model.get('noteData'));
          break;
        case 'MentionInPost':
          this.processMentionInPost(this.model.get('noteData'));
          break;
        default:
          this.process();
      }
    }
    process() {
      let type = this.model.get('type');
      if (!type) {
        return;
      }
      type = Espo.Utils.upperCaseFirst(type.replace(/ /g, ''));
      let viewName = this.getMetadata().get(`clientDefs.Notification.itemViews.${type}`);
      if (!viewName) {
        if (!this.types.includes(type)) {
          return;
        }
        viewName = 'views/notification/items/' + Espo.Utils.camelCaseToHyphen(type);
      }
      this.createView('notification', viewName, {
        model: this.model,
        fullSelector: this.options.containerSelector + ' li[data-id="' + this.model.id + '"]'
      });
    }
    processNote(data) {
      if (!data) {
        return;
      }
      this.wait(true);
      this.getModelFactory().create('Note', model => {
        model.set(data);
        let viewName = this.getMetadata().get(`clientDefs.Note.itemViews.${data.type}`);
        if (!viewName) {
          // @todo Check if type exists.
          viewName = 'views/stream/notes/' + Espo.Utils.camelCaseToHyphen(data.type);
        }
        this.createView('notification', viewName, {
          model: model,
          isUserStream: true,
          fullSelector: `${this.options.containerSelector} li[data-id="${this.model.id}"] .cell[data-name="data"]`,
          onlyContent: true,
          isNotification: true
        });
        this.wait(false);
      });
    }
    processMentionInPost(data) {
      if (!data) {
        return;
      }
      this.wait(true);
      this.getModelFactory().create('Note', model => {
        model.set(data);
        const viewName = 'views/stream/notes/mention-in-post';
        this.createView('notification', viewName, {
          model: model,
          userId: this.model.get('userId'),
          isUserStream: true,
          fullSelector: this.options.containerSelector + ' li[data-id="' + this.model.id + '"]',
          onlyContent: true,
          isNotification: true
        });
        this.wait(false);
      });
    }
  }
  var _default = _exports.default = NotificationContainerFieldView;
});

define("views/note/detail", ["exports", "views/main"], function (_exports, _main) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _main = _interopRequireDefault(_main);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NoteDetailView extends _main.default {
    templateContent = `
        <div class="header page-header">{{{header}}}</div>
        <div class="record list-container list-container-panel block-center">{{{record}}}</div>
    `;

    /**
     * @private
     */
    isDeleted = false;
    setup() {
      this.scope = this.model.entityType;
      this.setupHeader();
      this.setupRecord();
      this.listenToOnce(this.model, 'remove', () => {
        this.clearView('record');
        this.isDeleted = true;
        this.getHeaderView().reRender();
      });
    }

    /**
     * @private
     */
    setupHeader() {
      this.createView('header', 'views/header', {
        selector: '> .header',
        scope: this.scope,
        fontSizeFlexible: true
      });
    }

    /**
     * @private
     */
    setupRecord() {
      this.wait(this.getCollectionFactory().create(this.scope).then(collection => {
        this.collection = collection;
        this.collection.add(this.model);
        this.createView('record', 'views/stream/record/list', {
          selector: '> .record',
          collection: this.collection,
          isUserStream: true
        });
      }));
    }
    getHeader() {
      const parentType = this.model.get('parentType');
      const parentId = this.model.get('parentId');
      const parentName = this.model.get('parentName');
      const type = this.model.get('type');
      const $type = $('<span>').text(this.getLanguage().translateOption(type, 'type', 'Note'));
      if (this.model.get('deleted') || this.isDeleted) {
        $type.css('text-decoration', 'line-through');
      }
      if (parentType && parentId) {
        return this.buildHeaderHtml([$('<a>').attr('href', `#${parentType}`).text(this.translate(parentType, 'scopeNamesPlural')), $('<a>').attr('href', `#${parentType}/view/${parentId}`).text(parentName || parentId), $('<span>').text(this.translate('Stream', 'scopeNames')), $type]);
      }
      return this.buildHeaderHtml([$('<span>').text(this.translate('Stream', 'scopeNames')), $type]);
    }
  }
  var _default = _exports.default = NoteDetailView;
});

define("views/note/record/edit", ["exports", "views/record/edit"], function (_exports, _edit) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _edit.default {
    sideView = null;
    isWide = true;
    setup() {
      super.setup();
      this.controlRequiredFields();
      this.listenTo(this.model, 'change:attachmentsIds', () => {
        this.controlRequiredFields();
      });
    }
    controlRequiredFields() {
      if (!(this.model.get('attachmentsIds') || []).length) {
        this.setFieldRequired('post');
      } else {
        this.setFieldNotRequired('post');
      }
    }
  }
  _exports.default = _default;
});

define("views/note/modals/edit", ["exports", "views/modals/edit"], function (_exports, _edit) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _edit.default {
    fullFormDisabled = true;
    setup() {
      super.setup();
      this.once('ready', () => {
        const recordView = this.getView('edit') || this.getView('record');
        if (!recordView) {
          return;
        }
        const fieldView = recordView.getFieldView('post');
        if (!fieldView) {
          return;
        }
        this.listenTo(fieldView, 'add-files', files => {
          /** @type {import('views/fields/attachment-multiple').default} */
          const attachmentsView = recordView.getFieldView('attachments');
          if (attachmentsView) {
            attachmentsView.uploadFiles(files);
          }
        });
      });
    }
  }
  _exports.default = _default;
});

define("views/note/fields/users", ["exports", "views/fields/link-multiple"], function (_exports, _linkMultiple) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkMultiple = _interopRequireDefault(_linkMultiple);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _linkMultiple.default {
    init() {
      this.messagePermission = this.getAcl().getPermissionLevel('message');
      this.portalPermission = this.getAcl().getPermissionLevel('portal');
      if (this.messagePermission === 'no' && this.portalPermission === 'no') {
        this.readOnly = true;
      }
      super.init();
    }
    getSelectBoolFilterList() {
      if (this.messagePermission === 'team') {
        return ['onlyMyTeam'];
      }
      if (this.portalPermission === 'yes') {
        return null;
      }
    }
    getSelectPrimaryFilterName() {
      if (this.portalPermission === 'yes' && this.messagePermission === 'no') {
        return 'activePortal';
      }
      return 'active';
    }
    getSelectFilterList() {
      if (this.portalPermission === 'yes') {
        if (this.messagePermission === 'no') {
          return ['activePortal'];
        }
        return ['active', 'activePortal'];
      }
      return null;
    }

    /**
     * @inheritDoc
     */
    prepareEditItemElement(id, name) {
      const itemElement = super.prepareEditItemElement(id, name);
      const avatarHtml = this.getHelper().getAvatarHtml(id, 'small', 18, 'avatar-link');
      if (avatarHtml) {
        const img = new DOMParser().parseFromString(avatarHtml, 'text/html').body.childNodes[0];
        itemElement.prepend(img);
      }
      return itemElement;
    }
  }
  _exports.default = _default;
});

define("views/note/fields/type", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _enum.default {
    searchTypeList = ['anyOf', 'noneOf'];
  }
  _exports.default = _default;
});

define("views/note/fields/related", ["exports", "views/fields/link-parent"], function (_exports, _linkParent) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkParent = _interopRequireDefault(_linkParent);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _linkParent.default {
    setup() {
      this.foreignScopeList = Object.keys(this.getMetadata().get('scopes')).filter(scope => {
        const defs = /** @type {Record} */this.getMetadata().get(`scopes.${scope}`) || {};
        if (!defs.entity || !defs.object) {
          return false;
        }
        return this.getAcl().checkScope(scope, 'stream');
      });
      super.setup();
    }
  }
  _exports.default = _default;
});

define("views/note/fields/parent", ["exports", "views/fields/link-parent"], function (_exports, _linkParent) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkParent = _interopRequireDefault(_linkParent);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _linkParent.default {
    setup() {
      this.foreignScopeList = Object.keys(this.getMetadata().get('scopes')).filter(scope => {
        const defs = /** @type {Record} */this.getMetadata().get(`scopes.${scope}`) || {};
        if (!defs.stream || !defs.entity || !defs.object) {
          return false;
        }
        return this.getAcl().checkScope(scope, 'stream');
      });
      super.setup();
    }
  }
  _exports.default = _default;
});

define("views/modals/view-map", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ViewMapModalView extends _modal.default {
    templateContent = `<div class="map-container no-side-margin">{{{map}}}</div>`;
    backdrop = true;
    setup() {
      const field = this.options.field;
      const url = '#AddressMap/view/' + this.model.entityType + '/' + this.model.id + '/' + field;
      const fieldLabel = this.translate(field, 'fields', this.model.entityType);
      this.headerElement = $('<a>').attr('href', '#' + url).text(fieldLabel).get(0);
      const viewName = this.model.getFieldParam(field + 'Map', 'view') || this.getFieldManager().getViewName('map');
      this.createView('map', viewName, {
        model: this.model,
        name: field + 'Map',
        selector: '.map-container',
        height: 'auto'
      });
    }
  }
  var _default = _exports.default = ViewMapModalView;
});

define("views/modals/select-records-with-categories", ["exports", "views/modals/select-records"], function (_exports, _selectRecords) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _selectRecords = _interopRequireDefault(_selectRecords);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class SelectRecordsWithCategoriesModalView extends _selectRecords.default {
    template = 'modals/select-records-with-categories';

    /**
     * @private
     * @type {string}
     */
    categoryField = 'category';

    /**
     * @private
     * @type {string}
     */
    categoryFilterType = 'inCategory';

    /**
     * @private
     * @type {string}
     */
    categoryScope;

    /**
     * @private
     * @type {boolean}
     */
    isExpanded = true;

    /**
     * @protected
     * @type {boolean}
     */
    isCategoryMultiple;
    data() {
      return {
        ...super.data(),
        categoriesDisabled: this.categoriesDisabled
      };
    }
    setup() {
      // noinspection JSUnresolvedReference
      this.scope = this.entityType = this.options.scope || this.scope || this.options.entityType;
      this.categoryScope = this.categoryScope || this.scope + 'Category';
      this.categoryField = this.getMetadata().get(`scopes.${this.categoryScope}.categoryField`) || this.categoryField;
      this.isCategoryMultiple = this.getMetadata().get(`entityDefs.${this.scope}.fields.${this.categoryField}.type`) === 'linkMultiple';
      this.categoriesDisabled = this.categoriesDisabled || this.getMetadata().get(['scopes', this.categoryScope, 'disabled']) || !this.getAcl().checkScope(this.categoryScope);
      super.setup();
      this.addActionHandler('toggleExpandedFromNavigation', () => this.actionToggleExpandedFromNavigation());
    }
    setupList() {
      if (!this.categoriesDisabled) {
        this.setupCategories();
      }
      super.setupList();
    }
    setupCategories() {
      this.getCollectionFactory().create(this.categoryScope, collection => {
        this.treeCollection = collection;
        collection.url = collection.entityType + '/action/listTree';
        collection.data.onlyNotEmpty = true;
        collection.fetch().then(() => this.createCategoriesView());
      });
    }

    /**
     * @protected
     * @return {import('views/record/list-tree').default}
     */
    getCategoriesView() {
      return this.getView('categories');
    }
    createCategoriesView() {
      this.createView('categories', 'views/record/list-tree', {
        collection: this.treeCollection,
        selector: '.categories-container',
        selectable: true,
        readOnly: true,
        showRoot: true,
        buttonsDisabled: true,
        checkboxes: false,
        isExpanded: this.isExpanded
      }, view => {
        if (this.isRendered()) {
          view.render();
        } else {
          this.listenToOnce(this, 'after:render', () => view.render());
        }
        this.listenTo(view, 'select', model => {
          this.currentCategoryId = null;
          this.currentCategoryName = '';
          if (model && model.id) {
            this.currentCategoryId = model.id;
            this.currentCategoryName = model.get('name');
          }
          this.applyCategoryToCollection();
          Espo.Ui.notifyWait();
          this.collection.fetch().then(() => Espo.Ui.notify(false));
        });
      });
    }

    /**
     * @private
     */
    async actionToggleExpandedFromNavigation() {
      this.isExpanded = !this.isExpanded;

      /** @type {HTMLAnchorElement} */
      const a = this.element.querySelector('a[data-role="expandButtonContainer"]');
      if (a) {
        a.classList.add('disabled');
      }
      this.applyCategoryToCollection();
      this.getCategoriesView().isExpanded = this.isExpanded;
      Espo.Ui.notifyWait();
      await this.collection.fetch();
      this.getCategoriesView().reRender().then(() => {});
      Espo.Ui.notify();
    }

    /**
     * @private
     * @todo Move to helper. Together with list view.
     */
    applyCategoryToCollection() {
      this.collection.whereFunction = () => {
        let filter;
        const isExpanded = this.isExpanded;
        if (!isExpanded && !this.hasTextFilter()) {
          if (this.isCategoryMultiple) {
            if (this.currentCategoryId) {
              filter = {
                attribute: this.categoryField,
                type: 'linkedWith',
                value: [this.currentCategoryId]
              };
            } else {
              filter = {
                attribute: this.categoryField,
                type: 'isNotLinked'
              };
            }
          } else {
            if (this.currentCategoryId) {
              filter = {
                attribute: this.categoryField + 'Id',
                type: 'equals',
                value: this.currentCategoryId
              };
            } else {
              filter = {
                attribute: this.categoryField + 'Id',
                type: 'isNull'
              };
            }
          }
        } else {
          if (this.currentCategoryId) {
            filter = {
              attribute: this.categoryField,
              type: this.categoryFilterType,
              value: this.currentCategoryId
            };
          }
        }
        if (filter) {
          return [filter];
        }
      };
    }

    /**
     * @private
     * @return {boolean}
     */
    hasTextFilter() {
      return !!this.collection.data.textFilter || this.collection.where && this.collection.where.find(it => it.type === 'textFilter');
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = SelectRecordsWithCategoriesModalView;
});

define("views/modals/select-category-tree-records", ["exports", "views/modals/select-records", "search-manager"], function (_exports, _selectRecords, _searchManager) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _selectRecords = _interopRequireDefault(_selectRecords);
  _searchManager = _interopRequireDefault(_searchManager);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class SelectCategoryTreeRecordsModalView extends _selectRecords.default {
    setup() {
      this.filters = this.options.filters || {};
      this.boolFilterList = this.options.boolFilterList || {};
      this.primaryFilterName = this.options.primaryFilterName || null;
      if ('multiple' in this.options) {
        this.multiple = this.options.multiple;
      }
      this.createButton = false;
      this.massRelateEnabled = this.options.massRelateEnabled;
      this.buttonList = [{
        name: 'cancel',
        label: 'Cancel'
      }];
      if (this.multiple) {
        this.buttonList.unshift({
          name: 'select',
          style: 'danger',
          label: 'Select',
          onClick: dialog => {
            const listView = this.getRecordView();
            if (listView.allResultIsChecked) {
              const data = {
                massRelate: true,
                where: this.collection.getWhere(),
                searchParams: this.collection.data
              };
              this.trigger('select', data);
              if (this.options.onMassSelect) {
                this.options.onMassSelect(data);
              }
            } else {
              const list = listView.getSelected();
              if (list.length) {
                this.trigger('select', list);
                if (this.options.onSelect) {
                  this.options.onSelect(list);
                }
              }
            }
            dialog.close();
          }
        });
      }

      // noinspection JSUnresolvedReference
      this.scope = this.entityType = this.options.entityType || this.options.scope;
      this.$header = $('<span>');
      this.$header.append($('<span>').text(this.translate('Select') + ' · ' + this.getLanguage().translate(this.entityType, 'scopeNamesPlural')));
      this.$header.prepend(this.getHelper().getScopeColorIconHtml(this.entityType));
      this.waitForView('list');
      this.getCollectionFactory().create(this.entityType, collection => {
        collection.maxSize = this.getConfig().get('recordsPerPageSelect') || 5;
        this.collection = collection;
        const searchManager = new _searchManager.default(collection);
        searchManager.emptyOnReset = true;
        if (this.filters) {
          searchManager.setAdvanced(this.filters);
        }
        if (this.boolFilterList) {
          searchManager.setBool(this.boolFilterList);
        }
        if (this.primaryFilterName) {
          searchManager.setPrimary(this.primaryFilterName);
        }
        collection.where = searchManager.getWhere();
        collection.url = collection.entityType + '/action/listTree';
        const viewName = this.getMetadata().get(`clientDefs.${this.entityType}.recordViews.listSelectCategoryTree`) || 'views/record/list-tree';
        this.listenToOnce(collection, 'sync', () => {
          this.createView('list', viewName, {
            collection: collection,
            fullSelector: this.containerSelector + ' .list-container',
            readOnly: true,
            selectable: true,
            checkboxes: this.multiple,
            massActionsDisabled: true,
            searchManager: searchManager,
            checkAllResultDisabled: true,
            buttonsDisabled: true
          }, listView => {
            listView.once('select', models => {
              if (!Array.isArray(models)) {
                models = [models];
              }
              this.trigger('select', models);
              if (this.options.onSelect) {
                this.options.onSelect(models);
              }
              this.close();
            });
          });
        });
        collection.fetch();
      });
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = SelectCategoryTreeRecordsModalView;
});

define("views/modals/save-filters", ["exports", "views/modal", "model"], function (_exports, _modal, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class SaveFiltersModalView extends _modal.default {
    template = 'modals/save-filters';
    cssName = 'save-filters';
    data() {
      return {
        dashletList: this.dashletList
      };
    }
    setup() {
      this.buttonList = [{
        name: 'save',
        label: 'Save',
        style: 'primary'
      }, {
        name: 'cancel',
        label: 'Cancel'
      }];
      this.headerText = this.translate('Save Filter');
      const model = new _model.default();
      this.createView('name', 'views/fields/varchar', {
        selector: '.field[data-name="name"]',
        defs: {
          name: 'name',
          params: {
            required: true
          }
        },
        mode: 'edit',
        model: model,
        labelText: this.translate('name', 'fields')
      });
    }

    /**
     * @param {string} field
     * @return {module:views/fields/base}
     */
    getFieldView(field) {
      return this.getView(field);
    }
    actionSave() {
      const nameView = this.getFieldView('name');
      nameView.fetchToModel();
      if (nameView.validate()) {
        return;
      }
      this.trigger('save', nameView.model.get('name'));
      return true;
    }
  }
  var _default = _exports.default = SaveFiltersModalView;
});

define("views/modals/resolve-save-conflict", ["exports", "views/modal", "ui/select"], function (_exports, _modal, _select) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _select = _interopRequireDefault(_select);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ResolveSaveConflictModalView extends _modal.default {
    // language=Handlebars
    templateContent = `
        <div class="margin-bottom-3x">
            <p>{{translate 'resolveSaveConflict' category='messages'}}</p>
        </div>

        <div class="panel panel-default no-side-margin">
            <table class="table" style="table-layout: fixed;">
                <thead>
                <tr>
                    <th style="width: 25%">{{translate 'Field'}}</th>
                    <th style="width: 25%">{{translate 'Resolution'}}</th>
                    <th>{{translate 'Value'}}</th>
                </tr>
                </thead>
                <tbody>
                {{#each dataList}}
                    <tr>
                        <td class="cell cell-nowrap">
                    <span>
                        {{translate field category='fields' scope=../entityType}}
                    </span>
                        </td>
                        <td class="cell">
                            <select class="form-control" data-name="resolution" data-field="{{field}}">
                                {{options ../resolutionList resolution field='saveConflictResolution'}}
                            </select>
                        </td>
                        <td class="cell">
                            <div data-name="field" data-field="{{field}}">
                                {{{var viewKey ../this}}}
                            </div>
                        </td>
                    </tr>
                {{/each}}
                </tbody>
            </table>
        </div>
    `;
    backdrop = true;
    resolutionList = ['current', 'actual', 'original'];
    defaultResolution = 'current';

    /**
     * @private
     * @type {string[]}
     */
    fieldList;
    data() {
      const dataList = [];
      this.fieldList.forEach(item => {
        const o = {
          field: item,
          viewKey: item + 'Field',
          resolution: this.defaultResolution
        };
        dataList.push(o);
      });
      return {
        dataList: dataList,
        entityType: this.entityType,
        resolutionList: this.resolutionList
      };
    }
    setup() {
      this.headerText = this.translate('Resolve Conflict');
      this.buttonList = [{
        name: 'apply',
        label: 'Apply',
        style: 'danger'
      }, {
        name: 'cancel',
        label: 'Cancel'
      }];
      this.entityType = this.model.entityType;
      this.originalModel = this.model;
      this.originalAttributes = Espo.Utils.cloneDeep(this.options.originalAttributes);
      this.currentAttributes = Espo.Utils.cloneDeep(this.options.currentAttributes);
      this.actualAttributes = Espo.Utils.cloneDeep(this.options.actualAttributes);
      const attributeList = this.options.attributeList;
      const fieldList = [];
      this.getFieldManager().getEntityTypeFieldList(this.entityType).forEach(field => {
        const fieldAttributeList = this.getFieldManager().getEntityTypeFieldAttributeList(this.entityType, field);
        const intersect = attributeList.filter(value => fieldAttributeList.includes(value));
        if (intersect.length) {
          fieldList.push(field);
        }
      });
      this.fieldList = fieldList;
      this.wait(this.getModelFactory().create(this.entityType).then(model => {
        this.model = model;
        this.fieldList.forEach(field => {
          this.setResolution(field, this.defaultResolution);
        });
        this.fieldList.forEach(field => {
          this.createField(field);
        });
      }));
    }
    setResolution(field, resolution) {
      const attributeList = this.getFieldManager().getEntityTypeFieldAttributeList(this.entityType, field);
      const values = {};
      let source = this.currentAttributes;
      if (resolution === 'actual') {
        source = this.actualAttributes;
      } else if (resolution === 'original') {
        source = this.originalAttributes;
      }
      for (const attribute of attributeList) {
        values[attribute] = source[attribute] || null;
      }
      this.model.set(values);
    }
    createField(field) {
      const type = this.model.getFieldType(field);
      const viewName = this.model.getFieldParam(field, 'view') || this.getFieldManager().getViewName(type);
      this.createView(field + 'Field', viewName, {
        readOnly: true,
        model: this.model,
        name: field,
        selector: `[data-name="field"][data-field="${field}"]`,
        mode: 'list'
      });
    }
    afterRender() {
      this.$el.find('[data-name="resolution"]').on('change', e => {
        const $el = $(e.currentTarget);
        const field = $el.attr('data-field');
        const resolution = $el.val();
        this.setResolution(field, resolution);
      });
      this.fieldList.forEach(field => {
        const selectElement = this.element.querySelector(`select[data-field="${field}"]`);
        _select.default.init(selectElement);
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionApply() {
      const attributes = this.model.attributes;
      this.originalModel.set(attributes);
      this.trigger('resolve');
      this.close();
    }
  }
  var _default = _exports.default = ResolveSaveConflictModalView;
});

define("views/modals/password-change-request", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class PasswordChangeRequestModalView extends _modal.default {
    template = 'modals/password-change-request';
    cssName = 'password-change-request';
    className = 'dialog dialog-centered';
    noFullHeight = true;
    footerAtTheTop = false;
    setup() {
      this.buttonList = [{
        name: 'submit',
        label: 'Submit',
        style: 'danger',
        className: 'btn-s-wide'
      }, {
        name: 'cancel',
        label: 'Close',
        pullLeft: true,
        className: 'btn-s-wide'
      }];
      this.headerText = this.translate('Password Change Request', 'labels', 'User');
      this.once('close remove', () => {
        if (this.$userName) {
          this.$userName.popover('destroy');
        }
        if (this.$emailAddress) {
          this.$emailAddress.popover('destroy');
        }
      });
    }
    afterRender() {
      this.$userName = this.$el.find('input[name="username"]');
      this.$emailAddress = this.$el.find('input[name="emailAddress"]');
    }

    // noinspection JSUnusedGlobalSymbols
    actionSubmit() {
      const $userName = this.$userName;
      const $emailAddress = this.$emailAddress;
      const userName = $userName.val();
      const emailAddress = $emailAddress.val();
      let isValid = true;
      if (userName === '') {
        isValid = false;
        const message = this.getLanguage().translate('userCantBeEmpty', 'messages', 'User');
        this.isPopoverUserNameDestroyed = false;
        $userName.popover({
          container: 'body',
          placement: 'bottom',
          content: message,
          trigger: 'manual'
        }).popover('show');
        const $cellUserName = $userName.closest('.form-group');
        $cellUserName.addClass('has-error');
        $userName.one('mousedown click', () => {
          $cellUserName.removeClass('has-error');
          if (this.isPopoverUserNameDestroyed) {
            return;
          }
          $userName.popover('destroy');
          this.isPopoverUserNameDestroyed = true;
        });
      }
      if (emailAddress === '') {
        isValid = false;
        const message = this.getLanguage().translate('emailAddressCantBeEmpty', 'messages', 'User');
        this.isPopoverEmailAddressDestroyed = false;
        $emailAddress.popover({
          container: 'body',
          placement: 'bottom',
          content: message,
          trigger: 'manual'
        }).popover('show');
        const $cellEmailAddress = $emailAddress.closest('.form-group');
        $cellEmailAddress.addClass('has-error');
        $emailAddress.one('mousedown click', () => {
          $cellEmailAddress.removeClass('has-error');
          if (this.isPopoverEmailAddressDestroyed) {
            return;
          }
          $emailAddress.popover('destroy');
          this.isPopoverEmailAddressDestroyed = true;
        });
      }
      if (!isValid) {
        return;
      }
      const $submit = this.$el.find('button[data-name="submit"]');
      $submit.addClass('disabled');
      Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
      Espo.Ajax.postRequest('User/passwordChangeRequest', {
        userName: userName,
        emailAddress: emailAddress,
        url: this.options.url
      }).then(() => {
        Espo.Ui.notify(false);
        let msg = this.translate('uniqueLinkHasBeenSent', 'messages', 'User');
        msg += ' ' + this.translate('passwordRecoverySentIfMatched', 'messages', 'User');
        this.$el.find('.cell-userName').addClass('hidden');
        this.$el.find('.cell-emailAddress').addClass('hidden');
        $submit.addClass('hidden');
        this.$el.find('.msg-box').removeClass('hidden');
        this.$el.find('.msg-box').html('<span class="text-success">' + msg + '</span>');
      }).catch(xhr => {
        if (xhr.status === 404) {
          Espo.Ui.error(this.translate('userNameEmailAddressNotFound', 'messages', 'User'));
          xhr.errorIsHandled = true;
        }
        if (xhr.status === 403 && xhr.getResponseHeader('X-Status-Reason') === 'Already-Sent') {
          Espo.Ui.error(this.translate('forbidden', 'messages', 'User'), true);
          xhr.errorIsHandled = true;
        }
        $submit.removeClass('disabled');
      });
    }
  }
  var _default = _exports.default = PasswordChangeRequestModalView;
});

define("views/modals/mass-action", ["exports", "views/modal", "model"], function (_exports, _modal, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class MassActionModalView extends _modal.default {
    template = 'modals/mass-action';
    className = 'dialog dialog-record';
    checkInterval = 4000;
    data() {
      return {
        infoText: this.translate('infoText', 'messages', 'MassAction')
      };
    }
    setup() {
      this.action = this.options.action;
      this.id = this.options.id;
      this.status = 'Pending';
      this.headerText = this.translate('Mass Action', 'scopeNames') + ': ' + this.translate(this.action, 'massActions', this.options.scope);
      this.model = new _model.default();
      this.model.name = 'MassAction';
      this.model.setDefs({
        fields: {
          'status': {
            type: 'enum',
            readOnly: true,
            options: ['Pending', 'Running', 'Success', 'Failed'],
            style: {
              'Success': 'success',
              'Failed': 'danger'
            }
          },
          'processedCount': {
            type: 'int',
            readOnly: true
          }
        }
      });
      this.model.set({
        status: this.status,
        processedCount: null
      });
      this.createView('record', 'views/record/edit-for-modal', {
        scope: 'None',
        model: this.model,
        selector: '.record',
        detailLayout: [{
          rows: [[{
            name: 'status',
            labelText: this.translate('status', 'fields', 'MassAction')
          }, {
            name: 'processedCount',
            labelText: this.translate('processedCount', 'fields', 'MassAction')
          }]]
        }]
      });
      this.on('close', () => {
        const status = this.model.get('status');
        if (status !== 'Pending' && status !== 'Running') {
          return;
        }
        Espo.Ajax.postRequest(`MassAction/${this.id}/subscribe`);
      });
      this.checkStatus();
    }
    checkStatus() {
      Espo.Ajax.getRequest(`MassAction/${this.id}/status`).then(response => {
        const status = response.status;
        this.model.set('status', status);
        if (status === 'Pending' || status === 'Running') {
          setTimeout(() => this.checkStatus(), this.checkInterval);
          return;
        }
        this.model.set({
          processedCount: response.processedCount
        });
        if (status === 'Success') {
          this.trigger('success', {
            count: response.processedCount
          });
        }
        if (this.$el) {
          this.$el.find('.info-text').addClass('hidden');
        }
      });
    }
  }
  var _default = _exports.default = MassActionModalView;
});

define("views/modals/last-viewed", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class LastViewedModalView extends _modal.default {
    scope = 'ActionHistoryRecord';
    className = 'dialog dialog-record';
    template = 'modals/last-viewed';
    backdrop = true;
    setup() {
      this.events['click .list .cell > a'] = () => {
        this.close();
      };
      this.$header = $('<a>').attr('href', '#LastViewed').attr('data-action', 'listView').addClass('action').text(this.getLanguage().translate('LastViewed', 'scopeNamesPlural'));
      this.waitForView('list');
      this.getCollectionFactory().create(this.scope, collection => {
        collection.maxSize = this.getConfig().get('recordsPerPage');
        collection.url = 'LastViewed';
        this.collection = collection;
        this.loadList();
        collection.fetch();
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionListView() {
      this.getRouter().navigate('#LastViewed', {
        trigger: true
      });
      this.close();
    }
    loadList() {
      const viewName = this.getMetadata().get('clientDefs.' + this.scope + '.recordViews.listLastViewed') || 'views/record/list';
      this.listenToOnce(this.collection, 'sync', () => {
        this.createView('list', viewName, {
          collection: this.collection,
          fullSelector: this.containerSelector + ' .list-container',
          selectable: false,
          checkboxes: false,
          massActionsDisabled: true,
          rowActionsView: false,
          searchManager: this.searchManager,
          checkAllResultDisabled: true,
          buttonsDisabled: true,
          headerDisabled: true,
          layoutName: 'listForLastViewed',
          layoutAclDisabled: true
        });
      });
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = LastViewedModalView;
});

define("views/modals/kanban-move-over", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class KanbanMoveOverModalView extends _modal.default {
    template = 'modals/kanban-move-over';

    /** @inheritDoc */
    backdrop = true;
    data() {
      return {
        optionDataList: this.optionDataList
      };
    }
    events = {
      /** @this KanbanMoveOverModalView */
      'click [data-action="move"]': function (e) {
        const value = $(e.currentTarget).data('value');
        this.moveTo(value);
      }
    };
    setup() {
      this.scope = this.model.entityType;
      const iconHtml = this.getHelper().getScopeColorIconHtml(this.scope);
      this.statusField = this.options.statusField;
      this.$header = $('<span>');
      this.$header.append($('<span>').text(this.getLanguage().translate(this.scope, 'scopeNames')));
      if (this.model.get('name')) {
        this.$header.append(' <span class="chevron-right"></span> ');
        this.$header.append($('<span>').text(this.model.get('name')));
      }
      this.$header.prepend(iconHtml);
      this.buttonList = [{
        name: 'cancel',
        label: 'Cancel'
      }];
      this.optionDataList = [];
      (this.getMetadata().get(['entityDefs', this.scope, 'fields', this.statusField, 'options']) || []).forEach(item => {
        this.optionDataList.push({
          value: item,
          label: this.getLanguage().translateOption(item, this.statusField, this.scope)
        });
      });
    }

    /**
     * @private
     * @param {string} status
     */
    moveTo(status) {
      const previousStatus = this.model.attributes[this.statusField];
      this.model.save({
        [this.statusField]: status
      }, {
        patch: true,
        isMoveTo: true
      }).then(() => {
        Espo.Ui.success(this.translate('Done'));
      }).catch(() => {
        this.model.setMultiple({
          [this.statusField]: previousStatus
        }, {
          isMoveTo: true
        });
      });
      this.close();
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = KanbanMoveOverModalView;
});

define("views/modals/image-preview", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  let Exif;
  class ImagePreviewModalView extends _modal.default {
    template = 'modals/image-preview';
    cssName = 'image-preview';
    size = '';
    backdrop = true;
    isMaximizable = true;
    transformClassList = ['transform-flip', 'transform-rotate-180', 'transform-flip-and-rotate-180', 'transform-flip-and-rotate-270', 'transform-rotate-90', 'transform-flip-and-rotate-90', 'transform-rotate-270'];

    /**
     * @protected
     * @type {HTMLElement}
     */
    imageContainerElement;

    /**
     * @protected
     * @type {HTMLImageElement}
     */
    imageElement;

    /**
     * @private
     * @type {string}
     */
    imageId;

    /**
     * @private
     * @type {string|null}
     */
    imageName;

    /**
     * @type {{id: string, name?: string|null}[]}
     */
    imageList;
    events = {
      /** @this ImagePreviewModalView */
      'keydown': function (e) {
        if (e.code === 'ArrowLeft') {
          this.switchToPrevious(true);
          return;
        }
        if (e.code === 'ArrowRight') {
          this.switchToNext(true);
        }
      }
    };
    data() {
      return {
        name: this.imageName,
        url: this.getImageUrl(),
        originalUrl: this.getOriginalImageUrl(),
        showOriginalLink: this.size
      };
    }
    setup() {
      this.buttonList = [];
      this.headerHtml = '&nbsp;';
      this.imageId = this.options.id;
      this.imageName = this.options.name;
      this.imageList = this.options.imageList || [];
      this.navigationEnabled = this.imageList.length > 1;
      this.wait(Espo.loader.requirePromise('lib!exif-js').then(Lib => Exif = Lib));

      /** @private */
      this.onImageLoadBind = this.onImageLoad.bind(this);
      /** @private */
      this.onImageClickBind = this.onImageClick.bind(this);
      /** @private */
      this.onWindowResizeBind = this.onWindowResize.bind(this);
    }
    onRemove() {
      window.removeEventListener('resize', this.onWindowResizeBind);
      if (this.imageElement) {
        this.imageElement.removeEventListener('load', this.onImageLoadBind);
        this.imageElement.removeEventListener('click', this.onImageClickBind);
      }
    }

    /**
     * @private
     * @return {string}
     */
    getImageUrl() {
      let url = `${this.getBasePath()}?entryPoint=image&id=${this.imageId}`;
      if (this.size) {
        url += `&size=${this.size}`;
      }
      if (this.getUser().get('portalId')) {
        url += `&portalId=${this.getUser().get('portalId')}`;
      }
      return url;
    }
    getOriginalImageUrl() {
      let url = `${this.getBasePath()}?entryPoint=image&id=${this.imageId}`;
      if (this.getUser().get('portalId')) {
        url += `&portalId=${this.getUser().get('portalId')}`;
      }
      return url;
    }

    /**
     * @private
     */
    onImageLoad() {
      const image = this.imageElement;
      Exif.getData(image, () => {
        // noinspection JSDeprecatedSymbols
        if (window.getComputedStyle(image).imageOrientation === 'from-image') {
          return;
        }
        const orientation = Exif.getTag(image, 'Orientation');
        switch (orientation) {
          case 2:
            image.classList.add('transform-flip');
            break;
          case 3:
            image.classList.add('transform-rotate-180');
            break;
          case 4:
            image.classList.add('transform-rotate-180');
            image.classList.add('transform-flip');
            break;
          case 5:
            image.classList.add('transform-rotate-270');
            image.classList.add('transform-flip');
            break;
          case 6:
            image.classList.add('transform-rotate-90');
            break;
          case 7:
            image.classList.add('transform-rotate-90');
            image.classList.add('transform-flip');
            break;
          case 8:
            image.classList.add('transform-rotate-270');
            break;
        }
      });

      /*if (image.naturalWidth > image.clientWidth) {}*/
    }
    afterRender() {
      if (this.isMultiple()) {
        /** @type {HTMLDivElement|null} */
        const titleElement = this.dialog.getElement().querySelector('.modal-header .modal-title');
        if (titleElement) {
          titleElement.classList.add('text-muted');
          titleElement.style.userSelect = 'none';
        }
        this.dialog.setHeaderText((this.getImageIndex() + 1).toString());
      }
      this.imageContainerElement = this.element.querySelector('.image-container');
      this.imageElement = this.imageContainerElement.querySelector('img');
      this.imageElement.addEventListener('load', this.onImageLoadBind);
      if (this.navigationEnabled) {
        this.imageElement.style.cursor = 'pointer';
      }
      this.imageElement.addEventListener('click', this.onImageClickBind);
      window.removeEventListener('resize', this.onWindowResizeBind);
      window.addEventListener('resize', this.onWindowResizeBind);
      setTimeout(() => this.onWindowResize(), 100);
    }

    /**
     * @private
     */
    onWindowResize() {
      if (!this.imageContainerElement) {
        return;
      }
      const width = this.imageContainerElement.clientWidth;
      this.imageElement.style.maxWidth = width + 'px';
    }

    /**
     * @private
     */
    onImageClick() {
      this.switchToNext();
    }

    /**
     * @private
     * @return {boolean}
     */
    isMultiple() {
      return this.imageList.length > 1;
    }

    /**
     * @private
     * @param {boolean} [noLoop]
     */
    switchToPrevious(noLoop) {
      if (!this.isMultiple()) {
        return;
      }
      let index = this.getImageIndex();
      if (noLoop && index === 0) {
        return;
      }
      if (this.imageElement) {
        this.transformClassList.forEach(item => {
          this.imageElement.classList.remove(item);
        });
      }
      index--;
      if (index < 0) {
        index = this.imageList.length - 1;
      }
      this.imageId = this.imageList[index].id;
      this.imageName = this.imageList[index].name;
      this.reRender();
    }

    /**
     * @private
     * @param {boolean} [noLoop]
     */
    switchToNext(noLoop) {
      if (!this.isMultiple()) {
        return;
      }
      let index = this.getImageIndex();
      if (noLoop && index === this.imageList.length - 1) {
        return;
      }
      if (this.imageElement) {
        this.transformClassList.forEach(item => {
          this.imageElement.classList.remove(item);
        });
      }
      index++;
      if (index > this.imageList.length - 1) {
        index = 0;
      }
      this.imageId = this.imageList[index].id;
      this.imageName = this.imageList[index].name;
      this.reRender();
    }

    /**
     * @private
     * @return {number}
     */
    getImageIndex() {
      let index = -1;
      this.imageList.forEach((item, i) => {
        if (item.id === this.imageId) {
          index = i;
        }
      });
      return index;
    }
    onMaximize() {
      const width = this.imageContainerElement.clientWidth;
      this.imageElement.style.maxWidth = width + 'px';
    }
    onMinimize() {
      const width = this.imageContainerElement.clientWidth;
      this.imageElement.style.maxWidth = width + 'px';
    }
  }
  var _default = _exports.default = ImagePreviewModalView;
});

define("views/modals/image-crop", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ImageCropModalView extends _modal.default {
    template = 'modals/image-crop';
    cssName = 'image-crop';
    events = {
      /** @this ImageCropModalView */
      'click [data-action="zoomIn"]': function () {
        this.$img.cropper('zoom', 0.1);
      },
      /** @this ImageCropModalView */
      'click [data-action="zoomOut"]': function () {
        this.$img.cropper('zoom', -0.1);
      }
    };
    setup() {
      this.buttonList = [{
        name: 'crop',
        label: 'Submit',
        style: 'primary'
      }, {
        name: 'cancel',
        label: 'Cancel'
      }];
      this.wait(Espo.loader.requirePromise('lib!cropper'));
      this.on('remove', () => {
        if (this.$img.length) {
          this.$img.cropper('destroy');
          this.$img.parent().empty();
        }
      });
    }
    afterRender() {
      // noinspection RequiredAttributes,HtmlRequiredAltAttribute
      let $img = this.$img = $(`<img>`).attr('src', this.options.contents).addClass('hidden');
      this.$el.find('.image-container').append($img);
      setTimeout(() => {
        $img.cropper({
          aspectRatio: 1,
          movable: true,
          resizable: true,
          rotatable: false
        });
      }, 50);
    }

    // noinspection JSUnusedGlobalSymbols
    actionCrop() {
      let dataUrl = this.$img.cropper('getDataURL', 'image/jpeg');
      this.trigger('crop', dataUrl);
      this.close();
    }
  }
  var _default = _exports.default = ImageCropModalView;
});

define("views/modals/followers-list", ["exports", "views/modals/related-list", "helpers/record/select-related"], function (_exports, _relatedList, _selectRelated) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _relatedList = _interopRequireDefault(_relatedList);
  _selectRelated = _interopRequireDefault(_selectRelated);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class FollowersListModalView extends _relatedList.default {
    massActionRemoveDisabled = true;
    massActionMassUpdateDisabled = true;
    mandatorySelectAttributeList = ['type'];
    setup() {
      if (!this.getUser().isAdmin() && this.getAcl().getPermissionLevel('followerManagementPermission') === 'no' && this.getAcl().getPermissionLevel('portalPermission') === 'no') {
        this.unlinkDisabled = true;
      }
      super.setup();
    }
    actionSelectRelated() {
      let filter = 'active';
      if (!this.getUser().isAdmin() && this.getAcl().getPermissionLevel('followerManagementPermission') === 'no' && this.getAcl().getPermissionLevel('portalPermission') === 'yes') {
        filter = 'activePortal';
      }
      const helper = new _selectRelated.default(this);
      helper.process(this.model, this.link, {
        primaryFilterName: filter,
        massSelect: false,
        foreignEntityType: 'User',
        viewKey: 'selectFollowers'
      });
    }
  }
  var _default = _exports.default = FollowersListModalView;
});

define("views/modals/edit-dashboard", ["exports", "views/modal", "model"], function (_exports, _modal, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EditDashboardModalView extends _modal.default {
    template = 'modals/edit-dashboard';
    className = 'dialog dialog-record';
    cssName = 'edit-dashboard';
    data() {
      return {
        hasLocked: this.hasLocked
      };
    }
    events = {
      /** @this EditDashboardModalView */
      'click button.add': function (e) {
        const name = $(e.currentTarget).data('name');
        this.getParentDashboardView().addDashlet(name);
        this.close();
      }
    };
    shortcutKeys = {
      'Control+Enter': 'save'
    };

    /**
     * @return {module:views/dashboard}
     */
    getParentDashboardView() {
      return /** @type module:views/dashboard */this.getParentView();
    }
    setup() {
      this.buttonList = [{
        name: 'save',
        label: this.options.fromDashboard ? 'Save' : 'Apply',
        style: 'primary',
        title: 'Ctrl+Enter'
      }, {
        name: 'cancel',
        label: 'Cancel',
        title: 'Esc'
      }];
      const dashboardLayout = this.options.dashboardLayout || [];
      const dashboardTabList = [];
      dashboardLayout.forEach(item => {
        if (item.name) {
          dashboardTabList.push(item.name);
        }
      });
      const model = this.model = new _model.default({}, {
        entityType: 'Preferences'
      });
      model.set('dashboardTabList', dashboardTabList);
      this.hasLocked = 'dashboardLocked' in this.options;
      if (this.hasLocked) {
        model.set('dashboardLocked', this.options.dashboardLocked || false);
      }
      this.createView('dashboardTabList', 'views/preferences/fields/dashboard-tab-list', {
        selector: '.field[data-name="dashboardTabList"]',
        defs: {
          name: 'dashboardTabList',
          params: {
            required: true,
            noEmptyString: true
          }
        },
        mode: 'edit',
        model: model
      });
      if (this.hasLocked) {
        this.createView('dashboardLocked', 'views/fields/bool', {
          selector: '.field[data-name="dashboardLocked"]',
          mode: 'edit',
          model: model,
          defs: {
            name: 'dashboardLocked'
          }
        });
      }
      this.headerText = this.translate('Edit Dashboard');
      this.dashboardLayout = this.options.dashboardLayout;
    }

    /**
     * @param {string} field
     * @return {module:views/fields/base}
     */
    getFieldView(field) {
      return this.getView(field);
    }
    actionSave() {
      const dashboardTabListView = this.getFieldView('dashboardTabList');
      dashboardTabListView.fetchToModel();
      if (this.hasLocked) {
        const dashboardLockedView = this.getFieldView('dashboardLocked');
        dashboardLockedView.fetchToModel();
      }
      if (dashboardTabListView.validate()) {
        return;
      }
      const attributes = {};
      attributes.dashboardTabList = this.model.get('dashboardTabList');
      if (this.hasLocked) {
        attributes.dashboardLocked = this.model.get('dashboardLocked');
      }
      const names = this.model.get('translatedOptions');
      const renameMap = {};
      for (const name in names) {
        if (name !== names[name]) {
          renameMap[name] = names[name];
        }
      }
      attributes.renameMap = renameMap;
      this.trigger('after:save', attributes);
      this.dialog.close();
    }
  }
  var _default = _exports.default = EditDashboardModalView;
});

define("views/modals/duplicate", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class DuplicateModalView extends _modal.default {
    template = 'modals/duplicate';
    cssName = 'duplicate-modal';
    data() {
      return {
        scope: this.scope,
        duplicates: this.duplicates
      };
    }
    setup() {
      let saveLabel = 'Save';
      if (this.model && this.model.isNew()) {
        saveLabel = 'Create';
      }
      this.buttonList = [{
        name: 'save',
        label: saveLabel,
        style: 'danger',
        onClick: dialog => {
          this.trigger('save');
          dialog.close();
        }
      }, {
        name: 'cancel',
        label: 'Cancel'
      }];
      this.scope = this.options.scope;
      this.duplicates = this.options.duplicates;
      if (this.scope) {
        this.setupRecord();
      }
    }
    setupRecord() {
      let promise = new Promise(resolve => {
        this.getHelper().layoutManager.get(this.scope, 'listSmall', layout => {
          layout = Espo.Utils.cloneDeep(layout);
          layout.forEach(item => item.notSortable = true);
          this.getCollectionFactory().create(this.scope).then(collection => {
            collection.add(this.duplicates);
            this.createView('record', 'views/record/list', {
              selector: '.list-container',
              collection: collection,
              listLayout: layout,
              buttonsDisabled: true,
              massActionsDisabled: true,
              rowActionsDisabled: true
            });
            resolve();
          });
        });
      });
      this.wait(promise);
    }
  }
  var _default = _exports.default = DuplicateModalView;
});

define("views/modals/convert-currency", ["exports", "views/modals/mass-convert-currency"], function (_exports, _massConvertCurrency) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _massConvertCurrency = _interopRequireDefault(_massConvertCurrency);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ConvertCurrencyModalView extends _massConvertCurrency.default {
    setup() {
      super.setup();
      this.headerText = this.translate('convertCurrency', 'massActions');
    }
    actionConvert() {
      this.disableButton('convert');
      this.getFieldView('currency').fetchToModel();
      this.getFieldView('currencyRates').fetchToModel();
      const currency = this.model.get('currency');
      const currencyRates = this.model.get('currencyRates');
      Espo.Ajax.postRequest('Action', {
        entityType: this.options.entityType,
        action: 'convertCurrency',
        id: this.options.model.id,
        data: {
          targetCurrency: currency,
          rates: currencyRates,
          fieldList: this.options.fieldList || null
        }
      }).then(attributes => {
        this.trigger('after:update', attributes);
        this.close();
      }).catch(() => {
        this.enableButton('convert');
      });
    }
  }
  var _default = _exports.default = ConvertCurrencyModalView;
});

define("views/modals/compose-email", ["exports", "views/modals/edit", "helpers/misc/mailto", "views/email/modals/schedule-send"], function (_exports, _edit, _mailto, _scheduleSend) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  _mailto = _interopRequireDefault(_mailto);
  _scheduleSend = _interopRequireDefault(_scheduleSend);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ComposeEmailModalView extends _edit.default {
    scope = 'Email';
    layoutName = 'composeSmall';
    saveDisabled = true;
    fullFormDisabled = true;
    isCollapsible = true;
    wasModified = false;
    shortcutKeys = {
      /** @this ComposeEmailModalView */
      'Control+Enter': function (e) {
        if (this.buttonList.findIndex(item => item.name === 'send' && !item.hidden) === -1) {
          return;
        }
        e.stopPropagation();
        e.preventDefault();
        this.actionSend();
      },
      /** @this ComposeEmailModalView */
      'Control+KeyS': function (e) {
        if (this.buttonList.findIndex(item => item.name === 'saveDraft' && !item.hidden) === -1) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        this.actionSaveDraft();
      },
      /** @this ComposeEmailModalView */
      'Escape': function (e) {
        e.stopPropagation();
        e.preventDefault();
        const focusedFieldView = this.getRecordView().getFocusedFieldView();
        if (focusedFieldView) {
          this.model.set(focusedFieldView.fetch());
        }
        if (this.getRecordView().isChanged) {
          this.confirm(this.translate('confirmLeaveOutMessage', 'messages')).then(() => this.actionClose());
          return;
        }
        this.actionClose();
      }
    };
    setup() {
      super.setup();
      this.buttonList.unshift({
        name: 'saveDraft',
        text: this.translate('Save Draft', 'labels', 'Email'),
        title: 'Ctrl+S',
        onClick: () => this.actionSaveDraft()
      });
      this.buttonList.unshift({
        name: 'send',
        text: this.translate('Send', 'labels', 'Email'),
        style: 'primary',
        title: 'Ctrl+Enter',
        onClick: () => this.actionSend()
      });
      this.dropdownItemList.push({
        name: 'scheduleSend',
        text: this.translate('Schedule Send', 'labels', 'Email'),
        onClick: () => this.actionScheduleSend()
      });
      this.$header = $('<a>').attr('role', 'button').attr('tabindex', '0').attr('data-action', 'fullFormDraft').text(this.getLanguage().translate('Compose Email'));
      this.events['click a[data-action="fullFormDraft"]'] = () => this.actionFullFormDraft();
      const helper = new _mailto.default(this.getConfig(), this.getPreferences(), this.getAcl());
      if (helper.toUse()) {
        this.once('after:render', () => this.actionClose());
        this.getRouter().confirmLeaveOut = false;
        const attributes = this.options.attributes || {};
        this.once('after:render', () => document.location.href = helper.composeLink(attributes));
        return;
      }
      this.once('remove', () => {
        this.dialogIsHidden = false;
      });
      this.listenTo(this.model, 'change', (m, o) => {
        if (o.ui) {
          this.wasModified = true;
        }
      });
    }
    createRecordView(model, callback) {
      const viewName = this.getMetadata().get('clientDefs.' + model.entityType + '.recordViews.compose') || 'views/email/record/compose';
      const options = {
        model: model,
        fullSelector: this.containerSelector + ' .edit-container',
        type: 'editSmall',
        layoutName: this.layoutName || 'detailSmall',
        buttonsDisabled: true,
        selectTemplateDisabled: this.options.selectTemplateDisabled,
        removeAttachmentsOnSelectTemplate: this.options.removeAttachmentsOnSelectTemplate,
        signatureDisabled: this.options.signatureDisabled,
        appendSignature: this.options.appendSignature,
        focusForCreate: this.options.focusForCreate,
        exit: () => {}
      };
      this.createView('edit', viewName, options, callback);
    }
    actionSend() {
      const dialog = this.dialog;
      const editView = /** @type {module:views/email/record/compose} */this.getRecordView();
      const model = editView.model;
      const afterSend = () => {
        this.dialogIsHidden = false;
        this.trigger('after:save', model);
        this.trigger('after:send', model);
        dialog.close();
        this.stopListening(editView, 'before:save', beforeSave);
        this.stopListening(editView, 'error:save', errorSave);
        this.remove();
      };
      const beforeSave = () => {
        this.dialogIsHidden = true;
        dialog.hideWithBackdrop();
        editView.setConfirmLeaveOut(false);
        if (!this.forceRemoveIsInitiated) {
          this.initiateForceRemove();
        }
      };
      const errorSave = () => {
        this.dialogIsHidden = false;
        if (this.isRendered()) {
          dialog.show();
        }
        this.stopListening(editView, 'before:save', beforeSave);
        this.stopListening(editView, 'error:save', errorSave);
      };
      this.listenToOnce(editView, 'after:send', afterSend);
      this.disableButton('send');
      this.disableButton('saveDraft');
      this.listenToOnce(editView, 'cancel:save', () => {
        this.enableButton('send');
        this.enableButton('saveDraft');
        this.stopListening(editView, 'after:send', afterSend);
        this.stopListening(editView, 'before:save', beforeSave);
        this.stopListening(editView, 'error:save', errorSave);
      });
      this.listenToOnce(editView, 'before:save', beforeSave);
      this.listenToOnce(editView, 'error:save', errorSave);
      editView.send();
    }

    /**
     * @param {module:views/record/base~saveOptions} [options] Options.
     * @return {Promise}
     */
    actionSaveDraft(options) {
      const editView = /** @type {module:views/email/record/compose} */this.getRecordView();
      const model = editView.model;
      this.disableButton('send');
      this.disableButton('saveDraft');
      const afterSave = () => {
        this.enableButton('send');
        this.enableButton('saveDraft');
        Espo.Ui.success(this.translate('savedAsDraft', 'messages', 'Email'));
        this.trigger('after:save', model);
        this.$el.find('button[data-name="cancel"]').html(this.translate('Close'));
      };
      editView.once('after:save', () => afterSave());
      editView.once('cancel:save', () => {
        this.enableButton('send');
        this.enableButton('saveDraft');
        editView.off('after:save', afterSave);
      });
      return editView.saveDraft(options);
    }
    initiateForceRemove() {
      this.forceRemoveIsInitiated = true;
      const parentView = this.getParentView();
      if (!parentView) {
        return true;
      }
      parentView.once('remove', () => {
        if (!this.dialogIsHidden) {
          return;
        }
        this.remove();
      });
    }
    actionFullFormDraft() {
      this.actionSaveDraft({
        skipNotModifiedWarning: true
      }).then(() => {
        this.getRecordView().setConfirmLeaveOut(false);
        this.getRouter().navigate('#Email/edit/' + this.model.id, {
          trigger: true
        });
        this.close();
      }).catch(reason => {
        if (reason === 'notModified') {
          Espo.Ui.notify(false);
          this.getRouter().navigate('#Email/edit/' + this.model.id, {
            trigger: true
          });
        }
      });
    }
    beforeCollapse() {
      if (this.wasModified) {
        this.actionSaveDraft({
          skipNotModifiedWarning: true
        }).then(() => {
          this.getRecordView().setConfirmLeaveOut(false);
          this.getRouter().removeWindowLeaveOutObject(this);
        });
      }
      return super.beforeCollapse();
    }

    /**
     * @private
     */
    async actionScheduleSend() {
      // Prevents skipping required validation of the 'To' field.
      this.model.set('status', 'Sending');
      if (this.getRecordView().validate()) {
        Espo.Ui.error(this.translate('Not valid'));
        this.model.set('status', 'Draft');
        return;
      }
      this.model.set('status', 'Draft');
      const view = new _scheduleSend.default({
        model: this.model,
        onSave: () => {
          this.trigger('after:save', this.model);
          this.close();
        }
      });
      await this.assignView('dialog', view);
      await view.render();
    }
  }
  var _default = _exports.default = ComposeEmailModalView;
});

define("views/modals/change-password", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ChangePasswordModalView extends _modal.default {
    template = 'modals/change-password';
    cssName = 'change-password';
    className = 'dialog dialog-record';
    setup() {
      this.buttonList = [{
        name: 'change',
        label: 'Change',
        style: 'danger'
      }, {
        name: 'cancel',
        label: 'Cancel'
      }];
      this.headerText = this.translate('Change Password', 'labels', 'User');
      const promise = this.getModelFactory().create('User', user => {
        this.model = user;
        this.createView('currentPassword', 'views/fields/password', {
          model: user,
          mode: 'edit',
          selector: '.field[data-name="currentPassword"]',
          defs: {
            name: 'currentPassword',
            params: {
              required: true
            }
          }
        });
        this.createView('password', 'views/user/fields/password', {
          model: user,
          mode: 'edit',
          selector: '.field[data-name="password"]',
          defs: {
            name: 'password',
            params: {
              required: true
            }
          }
        });
        this.createView('passwordConfirm', 'views/fields/password', {
          model: user,
          mode: 'edit',
          selector: '.field[data-name="passwordConfirm"]',
          defs: {
            name: 'passwordConfirm',
            params: {
              required: true
            }
          }
        });
      });
      this.wait(promise);
    }

    /**
     * @param {string} field
     * @return {module:views/fields/base}
     */
    getFieldView(field) {
      return this.getView(field);
    }

    // noinspection JSUnusedGlobalSymbols
    actionChange() {
      this.getFieldView('currentPassword').fetchToModel();
      this.getFieldView('password').fetchToModel();
      this.getFieldView('passwordConfirm').fetchToModel();
      const notValid = this.getFieldView('currentPassword').validate() || this.getFieldView('password').validate() || this.getFieldView('passwordConfirm').validate();
      if (notValid) {
        return;
      }
      this.$el.find('button[data-name="change"]').addClass('disabled');
      Espo.Ajax.putRequest('UserSecurity/password', {
        currentPassword: this.model.get('currentPassword'),
        password: this.model.get('password')
      }).then(() => {
        Espo.Ui.success(this.translate('passwordChanged', 'messages', 'User'));
        this.trigger('changed');
        this.close();
      }).catch(() => {
        this.$el.find('button[data-name="change"]').removeClass('disabled');
      });
    }
  }
  var _default = _exports.default = ChangePasswordModalView;
});

define("views/modals/auth2fa-required", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class Auth2faRequiredModalView extends _modal.default {
    noCloseButton = true;
    escapeDisabled = true;
    events = {
      'click [data-action="proceed"]': 'actionProceed',
      'click [data-action="logout"]': 'actionLogout'
    };

    // language=Handlebars
    templateContent = `
        <div class="complex-text">{{complexText viewObject.messageText}}</div>
        <div class="button-container btn-group" style="margin-top: 30px">
        <button class="btn btn-primary" data-action="proceed">{{translate 'Proceed'}}</button>
        <button class="btn btn-default" data-action="logout">{{translate 'Log Out'}}</button></div>
    `;
    setup() {
      this.buttonList = [];
      this.headerText = this.translate('auth2FARequiredHeader', 'messages', 'User');
      // noinspection JSUnusedGlobalSymbols
      this.messageText = this.translate('auth2FARequired', 'messages', 'User');
    }
    actionProceed() {
      this.createView('dialog', 'views/user/modals/security', {
        userModel: this.getUser()
      }, view => {
        view.render();
        this.listenToOnce(view, 'done', () => {
          this.clearView('dialog');
          this.close();
        });
      });
    }
    actionLogout() {
      this.getRouter().logout();
    }
  }
  var _default = _exports.default = Auth2faRequiredModalView;
});

define("views/modals/array-field-add", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ArrayFieldAddModalView extends _modal.default {
    template = 'modals/array-field-add';
    cssName = 'add-modal';
    backdrop = true;
    data() {
      return {
        optionDataList: this.getOptionDataList()
      };
    }
    events = {
      /** @this ArrayFieldAddModalView */
      'click .add': function (e) {
        const value = $(e.currentTarget).attr('data-value');
        this.trigger('add', value);
      },
      /** @this ArrayFieldAddModalView */
      'click input[type="checkbox"]': function (e) {
        const value = $(e.currentTarget).attr('data-value');
        if (e.target.checked) {
          this.checkedList.push(value);
        } else {
          const index = this.checkedList.indexOf(value);
          if (index !== -1) {
            this.checkedList.splice(index, 1);
          }
        }
        this.checkedList.length ? this.enableButton('select') : this.disableButton('select');
      },
      /** @this ArrayFieldAddModalView */
      'keyup input[data-name="quick-search"]': function (e) {
        this.processQuickSearch(e.currentTarget.value);
      }
    };
    setup() {
      this.headerText = this.translate('Add Item');
      this.checkedList = [];

      /** @type {Object.<string, string>} */
      this.translations = Espo.Utils.clone(this.options.translatedOptions || {});

      /** @type {string[]} */
      this.optionList = this.options.options || [];
      this.optionList.forEach(item => {
        if (item in this.translations) {
          return;
        }
        this.translations[item] = item;
      });
      this.buttonList = [{
        name: 'select',
        style: 'danger',
        label: 'Select',
        disabled: true,
        onClick: () => {
          this.trigger('add-mass', this.checkedList);
        }
      }, {
        name: 'cancel',
        label: 'Cancel'
      }];
    }
    afterRender() {
      this.$noData = this.$el.find('.no-data');
      setTimeout(() => {
        this.$el.find('input[data-name="quick-search"]').focus();
      }, 100);
    }

    /**
     * @protected
     * @return {{value: string, label: string}[]}
     */
    getOptionDataList() {
      return this.optionList.map(value => ({
        value: value,
        label: value in this.translations ? this.translations[value] : value
      }));
    }
    processQuickSearch(text) {
      text = text.trim();
      const $noData = this.$noData;
      $noData.addClass('hidden');
      if (!text) {
        this.$el.find('ul .list-group-item').removeClass('hidden');
        return;
      }
      const matchedList = [];
      const lowerCaseText = text.toLowerCase();
      this.optionList.forEach(item => {
        const label = this.translations[item].toLowerCase();
        for (const word of label.split(' ')) {
          const matched = word.indexOf(lowerCaseText) === 0;
          if (matched) {
            matchedList.push(item);
            return;
          }
        }
      });
      if (matchedList.length === 0) {
        this.$el.find('ul .list-group-item').addClass('hidden');
        $noData.removeClass('hidden');
        return;
      }
      this.optionList.forEach(item => {
        const $row = this.$el.find(`ul .list-group-item[data-name="${item}"]`);
        if (!~matchedList.indexOf(item)) {
          $row.addClass('hidden');
          return;
        }
        $row.removeClass('hidden');
      });
    }
  }
  var _default = _exports.default = ArrayFieldAddModalView;
});

define("views/modals/add-dashlet", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class AddDashletModalView extends _modal.default {
    template = 'modals/add-dashlet';
    cssName = 'add-dashlet';
    backdrop = true;
    events = {
      /** @this AddDashletModalView */
      'click .add': function (e) {
        const name = $(e.currentTarget).data('name');
        this.trigger('add', name);
        this.close();
      },
      /** @this AddDashletModalView */
      'keyup input[data-name="quick-search"]': function (e) {
        this.processQuickSearch(e.currentTarget.value);
      }
    };
    data() {
      return {
        dashletList: this.dashletList
      };
    }
    setup() {
      this.headerText = this.translate('Add Dashlet');
      const dashletList = Object.keys(this.getMetadata().get('dashlets') || {}).sort((v1, v2) => {
        return this.translate(v1, 'dashlets').localeCompare(this.translate(v2, 'dashlets'));
      });
      this.translations = {};
      this.dashletList = dashletList.filter(item => {
        const aclScope = this.getMetadata().get(['dashlets', item, 'aclScope']) || null;
        const accessDataList = this.getMetadata().get(['dashlets', item, 'accessDataList']) || null;
        if (this.options.parentType === 'Settings') {
          return true;
        }
        if (this.options.parentType === 'Portal') {
          if (accessDataList && accessDataList.find(item => item.inPortalDisabled)) {
            return false;
          }
          return true;
        }
        if (aclScope) {
          if (!this.getAcl().check(aclScope)) {
            return false;
          }
        }
        if (accessDataList) {
          if (!Espo.Utils.checkAccessDataList(accessDataList, this.getAcl(), this.getUser())) {
            return false;
          }
        }
        this.translations[item] = this.translate(item, 'dashlets');
        return true;
      });
      this.dashletList.forEach(item => {
        this.translations[item] = this.translate(item, 'dashlets');
      });
    }
    afterRender() {
      this.$noData = this.$el.find('.no-data');
      setTimeout(() => {
        this.$el.find('input[data-name="quick-search"]').focus();
      }, 100);
    }
    processQuickSearch(text) {
      text = text.trim();
      const $noData = this.$noData;
      $noData.addClass('hidden');
      if (!text) {
        this.$el.find('ul .list-group-item').removeClass('hidden');
        return;
      }
      const matchedList = [];
      const lowerCaseText = text.toLowerCase();
      this.dashletList.forEach(item => {
        if (!(item in this.translations)) {
          return;
        }
        const label = this.translations[item].toLowerCase();
        for (const word of label.split(' ')) {
          const matched = word.indexOf(lowerCaseText) === 0;
          if (matched) {
            matchedList.push(item);
            return;
          }
        }
      });
      if (matchedList.length === 0) {
        this.$el.find('ul .list-group-item').addClass('hidden');
        $noData.removeClass('hidden');
        return;
      }
      this.dashletList.forEach(item => {
        const $row = this.$el.find(`ul .list-group-item[data-name="${item}"]`);
        if (!~matchedList.indexOf(item)) {
          $row.addClass('hidden');
          return;
        }
        $row.removeClass('hidden');
      });
    }
  }
  var _default = _exports.default = AddDashletModalView;
});

define("views/modals/action-history", ["exports", "views/modal", "search-manager"], function (_exports, _modal, _searchManager) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _searchManager = _interopRequireDefault(_searchManager);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ActionHistoryModalView extends _modal.default {
    template = 'modals/action-history';
    scope = 'ActionHistoryRecord';
    className = 'dialog dialog-record';
    backdrop = true;
    setup() {
      super.setup();
      this.buttonList = [{
        name: 'cancel',
        label: 'Close'
      }];
      this.scope = this.entityType = this.options.scope || this.scope;
      this.$header = $('<a>').attr('href', '#ActionHistoryRecord').addClass('action').attr('data-action', 'listView').text(this.getLanguage().translate(this.scope, 'scopeNamesPlural'));
      this.waitForView('list');
      this.getCollectionFactory().create(this.scope, collection => {
        collection.maxSize = this.getConfig().get('recordsPerPage') || 20;
        this.collection = collection;
        this.setupSearch();
        this.setupList();
        collection.fetch();
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionListView() {
      this.getRouter().navigate('#ActionHistoryRecord', {
        trigger: true
      });
      this.close();
    }
    setupSearch() {
      this.searchManager = new _searchManager.default(this.collection);
      this.collection.data.boolFilterList = ['onlyMy'];
      this.collection.where = this.searchManager.getWhere();
      this.createView('search', 'views/record/search', {
        collection: this.collection,
        fullSelector: this.containerSelector + ' .search-container',
        searchManager: this.searchManager,
        disableSavePreset: true,
        textFilterDisabled: true
      });
    }
    setupList() {
      const viewName = this.getMetadata().get(`clientDefs.${this.scope}.recordViews.list`) || 'views/record/list';
      this.listenToOnce(this.collection, 'sync', () => {
        this.createView('list', viewName, {
          collection: this.collection,
          fullSelector: this.containerSelector + ' .list-container',
          selectable: false,
          checkboxes: false,
          massActionsDisabled: true,
          rowActionsView: 'views/record/row-actions/view-only',
          type: 'listSmall',
          searchManager: this.searchManager,
          checkAllResultDisabled: true,
          buttonsDisabled: true
        });
      });
    }
  }
  var _default = _exports.default = ActionHistoryModalView;
});

define("views/lead-capture/opt-in-confirmation-success", ["exports", "view", "model"], function (_exports, _view, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class OptInConfirmationSuccessView extends _view.default {
    template = 'lead-capture/opt-in-confirmation-success';
    setup() {
      const model = new _model.default();
      this.resultData = this.options.resultData;
      if (this.resultData.message) {
        model.set('message', this.resultData.message);
        this.createView('messageField', 'views/fields/text', {
          selector: '.field[data-name="message"]',
          mode: 'detail',
          inlineEditDisabled: true,
          model: model,
          name: 'message'
        });
      }
    }
    data() {
      return {
        resultData: this.options.resultData,
        defaultMessage: this.getLanguage().translate('optInIsConfirmed', 'messages', 'LeadCapture')
      };
    }
  }
  var _default = _exports.default = OptInConfirmationSuccessView;
});

define("views/lead-capture/opt-in-confirmation-expired", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class OptInConfirmationExpiredView extends _view.default {
    template = 'lead-capture/opt-in-confirmation-expired';
    setup() {
      this.resultData = this.options.resultData;
    }
    data() {
      return {
        defaultMessage: this.getLanguage().translate('optInConfirmationExpired', 'messages', 'LeadCapture')
      };
    }
  }
  var _default = _exports.default = OptInConfirmationExpiredView;
});

define("views/lead-capture/record/list", ["exports", "views/record/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _list.default {
    massActionList = ['remove', 'massUpdate', 'export'];
  }
  _exports.default = _default;
});

define("views/lead-capture/record/detail", ["exports", "views/record/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _detail.default {
    setupActionItems() {
      super.setupActionItems();
      this.addDropdownItem({
        label: 'Generate New API Key',
        name: 'generateNewApiKey',
        onClick: () => this.actionGenerateNewApiKey()
      });
      this.addDropdownItem({
        label: 'Generate New Form ID',
        name: 'generateNewFormId',
        onClick: () => this.actionGenerateNewFormId()
      });
    }
    actionGenerateNewApiKey() {
      this.confirm(this.translate('confirmation', 'messages'), () => {
        Espo.Ajax.postRequest('LeadCapture/action/generateNewApiKey', {
          id: this.model.id
        }).then(data => {
          this.model.set(data);
          Espo.Ui.success(this.translate('Done'));
        });
      });
    }
    async actionGenerateNewFormId() {
      await this.confirm(this.translate('confirmation', 'messages'));
      const data = await Espo.Ajax.postRequest('LeadCapture/action/generateNewFormId', {
        id: this.model.id
      });
      this.model.set(data);
      Espo.Ui.success(this.translate('Done'));
    }
  }
  _exports.default = _default;
});

define("views/lead-capture/record/panels/request", ["exports", "views/record/panels/side"], function (_exports, _side) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _side = _interopRequireDefault(_side);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _side.default {
    fieldList = ['exampleRequestUrl', 'exampleRequestMethod', 'exampleRequestHeaders', 'exampleRequestPayload'];
  }
  _exports.default = _default;
});

define("views/lead-capture/record/panels/form", ["exports", "views/record/panels/side"], function (_exports, _side) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _side = _interopRequireDefault(_side);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _side.default {
    fieldList = ['formUrl'];
  }
  _exports.default = _default;
});

define("views/lead-capture/fields/smtp-account", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _enum.default {
    /**
     * @private
     * @type {string}
     */
    dataUrl = 'LeadCapture/action/smtpAccountDataList';
    getAttributeList() {
      return [this.name, 'inboundEmailId'];
    }
    data() {
      const data = super.data();
      data.valueIsSet = this.model.has('inboundEmailId');
      data.isNotEmpty = this.model.has('inboundEmailId');
      ;
      data.value = this.getValueForDisplay();
      data.valueTranslated = data.value != null ? this.translatedOptions[data.value] : undefined;
      return data;
    }
    setupOptions() {
      super.setupOptions();
      this.params.options = [];
      this.translatedOptions = {};
      this.params.options.push('');
      if (!this.loadedOptionList) {
        if (this.model.get('inboundEmailId')) {
          const item = 'inboundEmail:' + this.model.get('inboundEmailId');
          this.params.options.push(item);
          this.translatedOptions[item] = (this.model.get('inboundEmailName') || this.model.get('inboundEmailId')) + ' (' + this.translate('group', 'labels', 'MassEmail') + ')';
        }
      } else {
        this.loadedOptionList.forEach(item => {
          this.params.options.push(item);
          this.translatedOptions[item] = (this.loadedOptionTranslations[item] || item) + ' (' + this.translate('group', 'labels', 'MassEmail') + ')';
        });
      }
      this.translatedOptions[''] = this.getConfig().get('outboundEmailFromAddress') + ' (' + this.translate('system', 'labels', 'MassEmail') + ')';
    }
    getValueForDisplay() {
      if (!this.model.has(this.name)) {
        if (this.model.has('inboundEmailId')) {
          if (this.model.get('inboundEmailId')) {
            return 'inboundEmail:' + this.model.get('inboundEmailId');
          } else {
            return '';
          }
        } else {
          return '';
        }
      }
      return this.model.get(this.name);
    }
    setup() {
      super.setup();
      if (this.getAcl().checkScope('MassEmail', 'create') || this.getAcl().checkScope('MassEmail', 'edit')) {
        Espo.Ajax.getRequest(this.dataUrl).then(dataList => {
          if (!dataList.length) {
            return;
          }
          this.loadedOptionList = [];
          this.loadedOptionTranslations = {};
          this.loadedOptionAddresses = {};
          this.loadedOptionFromNames = {};
          dataList.forEach(item => {
            this.loadedOptionList.push(item.key);
            this.loadedOptionTranslations[item.key] = item.emailAddress;
            this.loadedOptionAddresses[item.key] = item.emailAddress;
            this.loadedOptionFromNames[item.key] = item.fromName || '';
          });
          this.setupOptions();
          this.reRender();
        });
      }
    }
    fetch() {
      const data = {};
      const value = this.$element.val();
      data[this.name] = value;
      if (!value || value === '') {
        data.inboundEmailId = null;
        data.inboundEmailName = null;
      } else {
        const arr = value.split(':');
        if (arr.length > 1) {
          data.inboundEmailId = arr[1];
          data.inboundEmailName = this.translatedOptions[data.inboundEmailId] || data.inboundEmailId;
        }
      }
      return data;
    }
  }
  _exports.default = _default;
});

define("views/lead-capture/fields/phone-number-country", ["exports", "views/fields/enum", "intl-tel-input-globals"], function (_exports, _enum, _intlTelInputGlobals) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  _intlTelInputGlobals = _interopRequireDefault(_intlTelInputGlobals);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection NpmUsedModulesInstalled

  class LeadCapturePhoneNumberCountry extends _enum.default {
    setupOptions() {
      this.params.options = ['', ..._intlTelInputGlobals.default.getCountryData().map(item => item.iso2)];
      this.translatedOptions = _intlTelInputGlobals.default.getCountryData().reduce((map, item) => {
        map[item.iso2] = `${item.iso2.toUpperCase()} +${item.dialCode}`;
        return map;
      }, {});
    }
  }
  var _default = _exports.default = LeadCapturePhoneNumberCountry;
});

define("views/lead-capture/fields/form-theme", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class FormThemeFieldView extends _enum.default {
    setupOptions() {
      const list = Object.keys(this.getMetadata().get('themes') || {}).sort((v1, v2) => {
        if (v2 === 'EspoRtl') {
          return -1;
        }
        return this.translate(v1, 'theme').localeCompare(this.translate(v2, 'theme'));
      });
      this.params.options = ['', ...list];
    }
  }
  _exports.default = FormThemeFieldView;
});

define("views/lead-capture/fields/field-list", ["exports", "views/fields/array"], function (_exports, _array) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _array = _interopRequireDefault(_array);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _array.default {
    // language=Handlebars
    detailTemplateContent = `
        {{#unless isEmpty}}
            {{#each items}}
                <div
                    class="multi-enum-item-container"
                    style="{{#if strikeThrough}} text-decoration: line-through; {{/if}}"
                >{{label}}{{#if required}} *{{/if}}</div>
            {{/each}}
        {{else}}
            {{#if valueIsSet}}<span class="none-value">{{translate 'None'}}</span>
            {{else}}
                <span class="loading-value"></span>
            {{/if}}
        {{/unless}}
    `;

    /**
     * @private
     * @type {string[]}
     */
    webFormNotAllowedFields;
    data() {
      /** @type {string[]|null} */
      const items = this.model.get(this.name);
      if (!items) {
        return super.data();
      }
      const dataItems = items.map(it => {
        return {
          label: this.translatedOptions[it] || it,
          strikeThrough: this.model.attributes.formEnabled && this.webFormNotAllowedFields.includes(it),
          required: this.isFieldRequired(it)
        };
      });
      return {
        ...super.data(),
        items: dataItems
      };
    }
    getAttributeList() {
      return [...super.getAttributeList(), 'formEnabled', 'fieldParams'];
    }
    setup() {
      this.webFormNotAllowedFields = [];
      super.setup();
      this.listenTo(this.model, 'change:formEnabled', (m, v, o) => {
        if (!o.ui || !this.isDetailMode()) {
          return;
        }
        this.reRender();
      });
      this.addActionHandler('toggleRequired', (e, target) => this.toggleRequired(target.dataset.value));
    }
    setupOptions() {
      this.params.options = [];
      this.translatedOptions = {};

      /** @type {Record.<string, Record>} */
      const fields = this.getMetadata().get(['entityDefs', 'Lead', 'fields']) || {};

      /** @type {string[]} */
      const ignoreFieldList = this.getMetadata().get(`entityDefs.LeadCapture.fields.fieldList.ignoreFieldList`) || [];

      /** @type {string[]} */
      const webFormTypeList = this.getMetadata().get(`entityDefs.LeadCapture.fields.fieldList.webFormFieldTypeList`) || [];
      for (const field in fields) {
        const defs = fields[field];
        if (defs.disabled || defs.utility || defs.readOnly) {
          continue;
        }
        if (ignoreFieldList.includes(field)) {
          continue;
        }
        if (!webFormTypeList.includes(defs.type)) {
          this.webFormNotAllowedFields.push(field);
        }
        this.params.options.push(field);
        this.translatedOptions[field] = this.translate(field, 'fields', 'Lead');
      }
    }
    getItemHtml(value) {
      const html = super.getItemHtml(value);
      const div = document.createElement('div');
      div.innerHTML = html;

      /** @type {HTMLElement} */
      const item = div.querySelector('.list-group-item');
      const group = document.createElement('div');
      group.classList.add('btn-group', 'pull-right');
      const button = document.createElement('button');
      button.classList.add('btn', 'btn-link', 'btn-sm', 'dropdown-toggle');
      button.innerHTML = `<span class="caret"></span>`;
      button.dataset.toggle = 'dropdown';
      button.type = 'button';
      const ul = document.createElement('ul');
      ul.classList.add('dropdown-menu', 'pull-right');
      const li = document.createElement('li');
      const a = document.createElement('a');
      a.dataset.value = value;
      a.dataset.action = 'toggleRequired';
      a.role = 'button';
      a.tabIndex = 0;
      if (this.isFieldRequired(value)) {
        a.innerHTML += `<span class="check-icon fas fa-check pull-right"></span>`;
      }
      const textDiv = document.createElement('div');
      textDiv.textContent = this.translate('required', 'fields', 'Admin');
      a.append(textDiv);
      li.append(a);
      ul.append(li);
      group.append(button, ul);
      item.append(group);
      if (this.isFieldRequired(value)) {
        const text = div.querySelector('.text');
        if (text) {
          text.innerHTML += ' *';
        }
      }
      return div.innerHTML;
    }

    /**
     * @param {string} field
     * @return {boolean}
     */
    isFieldRequired(field) {
      const params = this.model.attributes.fieldParams || {};
      const fieldParams = params[field] || {};
      return !!fieldParams.required;
    }

    /**
     * @private
     * @param {string} field
     */
    toggleRequired(field) {
      const params = Espo.Utils.cloneDeep(this.model.attributes.fieldParams || {});
      if (!params[field]) {
        params[field] = {};
      }
      if (!('required' in params[field])) {
        params[field].required = false;
      }
      params[field].required = !params[field].required;
      const newParams = {};

      /** @type {string[]} */
      const fieldList = this.model.attributes.fieldList || [];
      fieldList.forEach(it => newParams[it] = params[it]);
      this.model.set('fieldParams', newParams, {
        ui: true
      });
      this.reRender();
    }
    addValue(value) {
      /** @type {string[]} */
      const items = this.model.get(this.name);
      let isAdded = false;
      if (items && !items.includes(value)) {
        isAdded = true;
      }
      super.addValue(value);
      if (isAdded && this.getMetadata().get(`entityDefs.Lead.fields.${value}.required`) && !this.isFieldRequired(value)) {
        this.toggleRequired(value);
      }
    }
  }
  _exports.default = _default;
});

define("views/last-viewed/list", ["exports", "views/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _list.default {
    searchPanel = false;
    createButton = false;
    setup() {
      super.setup();
      this.collection.url = 'LastViewed';
    }
  }
  _exports.default = _default;
});

define("views/last-viewed/record/list", ["exports", "views/record/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _list.default {
    layoutName = 'listForLastViewed';
    rowActionsDisabled = true;
    massActionsDisabled = true;
    headerDisabled = true;
  }
  _exports.default = _default;
});

define("views/global-search/scope-badge", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class GlobalSearchScopeBadgeView extends _view.default {
    template = 'global-search/scope-badge';
    data() {
      return {
        label: this.translate(this.model.get('_scope'), 'scopeNames')
      };
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = GlobalSearchScopeBadgeView;
});

define("views/global-search/panel", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class GlobalSearchPanel extends _view.default {
    template = 'global-search/panel';
    setup() {
      this.addHandler('click', '[data-action="closePanel"]', () => this.close());
      this.maxSize = this.getConfig().get('globalSearchMaxSize') || 10;
      this.navbarPanelHeightSpace = this.getThemeManager().getParam('navbarPanelHeightSpace') || 100;
      this.navbarPanelBodyMaxHeight = this.getThemeManager().getParam('navbarPanelBodyMaxHeight') || 600;
    }
    onRemove() {
      $(window).off('resize.global-search-height');
      if (this.overflowWasHidden) {
        $('body').css('overflow', 'unset');
        this.overflowWasHidden = false;
      }
    }
    afterRender() {
      this.collection.reset();
      this.collection.maxSize = this.maxSize;
      this.collection.fetch().then(() => this.createRecordView()).then(view => view.render());
      const $window = $(window);
      $window.off('resize.global-search-height');
      $window.on('resize.global-search-height', this.processSizing.bind(this));
      this.processSizing();
    }

    /**
     * @return {Promise<module:views/record/list-expanded>}
     */
    createRecordView() {
      // noinspection JSValidateTypes
      return this.createView('list', 'views/record/list-expanded', {
        selector: '.list-container',
        collection: this.collection,
        listLayout: {
          rows: [[{
            name: 'name',
            view: 'views/global-search/name-field'
          }]],
          right: {
            name: 'read',
            view: 'views/global-search/scope-badge',
            width: '80px'
          }
        }
      });
    }
    processSizing() {
      const $window = $(window);
      const windowHeight = $window.height();
      const windowWidth = $window.width();
      const diffHeight = this.$el.find('.panel-heading').outerHeight();
      const cssParams = {};
      if (windowWidth <= this.getThemeManager().getParam('screenWidthXs')) {
        cssParams.height = windowHeight - diffHeight + 'px';
        cssParams.overflow = 'auto';
        $('body').css('overflow', 'hidden');
        this.overflowWasHidden = true;
      } else {
        cssParams.height = 'unset';
        cssParams.overflow = 'none';
        if (this.overflowWasHidden) {
          $('body').css('overflow', 'unset');
          this.overflowWasHidden = false;
        }
        if (windowHeight - this.navbarPanelBodyMaxHeight < this.navbarPanelHeightSpace) {
          const maxHeight = windowHeight - this.navbarPanelHeightSpace;
          cssParams.maxHeight = maxHeight + 'px';
        }
      }
      this.$el.find('.panel-body').css(cssParams);
    }
    close() {
      this.trigger('close');
    }
  }
  var _default = _exports.default = GlobalSearchPanel;
});

define("views/global-search/name-field", ["exports", "views/fields/base", "helpers/record-modal"], function (_exports, _base, _recordModal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _recordModal = _interopRequireDefault(_recordModal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class GlobalSearchNameFieldView extends _base.default {
    listTemplate = 'global-search/name-field';
    data() {
      const scope = this.model.attributes._scope;
      return {
        scope: scope,
        name: this.model.attributes.name || this.translate('None'),
        id: this.model.id,
        iconHtml: this.getHelper().getScopeColorIconHtml(scope)
      };
    }
    setup() {
      this.addHandler('auxclick', 'a[href]:not([role="button"])', (/** MouseEvent */e) => {
        if (!this.isReadMode()) {
          return;
        }
        const isCombination = e.button === 1 && (e.ctrlKey || e.metaKey);
        if (!isCombination) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        this.quickView();
      });
    }
    quickView() {
      const helper = new _recordModal.default();
      helper.showDetail(this, {
        id: this.model.id,
        entityType: this.model.attributes._scope
      });
    }
  }
  var _default = _exports.default = GlobalSearchNameFieldView;
});

define("views/global-search/global-search", ["exports", "ui/autocomplete", "helpers/site/tabs", "views/site/navbar/item"], function (_exports, _autocomplete, _tabs, _item) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _autocomplete = _interopRequireDefault(_autocomplete);
  _tabs = _interopRequireDefault(_tabs);
  _item = _interopRequireDefault(_item);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/global-search/global-search */

  class GlobalSearchView extends _item.default {
    template = 'global-search/global-search';

    /**
     * @private
     * @type {HTMLElement}
     */
    containerElement;

    /**
     * @private
     * @type {HTMLInputElement}
     */
    inputElement;

    /**
     * @private
     * @type {boolean}
     */
    tabQuickSearch;

    /**
     * @private
     * @type {boolean}
     */
    hasGlobalSearch;

    /**
     * @private
     * @type {TabsHelper}
     */
    tabsHelper;

    /**
     * @private
     * @type {Autocomplete}
     */
    autocomplete;

    /**
     * @private
     * @type {module:views/global-search/global-search~tabData[]}
     */
    tabDataList;
    data() {
      return {
        hasSearchButton: this.hasGlobalSearch
      };
    }
    setup() {
      this.addHandler('keydown', 'input.global-search-input', 'onKeydown');
      this.addHandler('focus', 'input.global-search-input', 'onFocus');
      this.addHandler('click', '[data-action="search"]', () => this.runSearch());
      const promise = this.getCollectionFactory().create('GlobalSearch', collection => {
        this.collection = collection;
        this.collection.url = 'GlobalSearch';
      });
      this.wait(promise);
      this.closeNavbarOnShow = /iPad|iPhone|iPod/.test(navigator.userAgent);
      this.onMouseUpBind = this.onMouseUp.bind(this);
      this.onClickBind = this.onClick.bind(this);
      this.tabQuickSearch = this.getConfig().get('tabQuickSearch') || false;
      this.hasGlobalSearch = (this.getConfig().get('globalSearchEntityList') || []).length > 0;
      this.tabsHelper = new _tabs.default(this.getConfig(), this.getPreferences(), this.getUser(), this.getAcl(), this.getMetadata(), this.getLanguage());
      this.tabDataList = this.getTabDataList();
    }

    /**
     * @param {MouseEvent} e
     * @private
     */
    onFocus(e) {
      const inputElement = /** @type {HTMLInputElement} */e.target;
      inputElement.select();
    }

    /**
     * @param {KeyboardEvent} e
     * @private
     */
    onKeydown(e) {
      if (!this.hasGlobalSearch) {
        return;
      }
      const key = Espo.Utils.getKeyFromKeyEvent(e);
      if (e.key === 'Enter' || key === 'Enter' || key === 'Control+Enter') {
        this.runSearch();
        return;
      }
      if (key === 'Escape') {
        this.closePanel();
      }
    }
    afterRender() {
      this.$input = this.$el.find('input.global-search-input');
      this.inputElement = this.$input.get(0);
      if (this.tabQuickSearch) {
        this.autocomplete = new _autocomplete.default(this.inputElement, {
          minChars: 1,
          lookupFunction: async query => {
            const lower = query.toLowerCase();
            return this.tabDataList.filter(it => {
              if (it.words.find(word => word.startsWith(lower))) {
                return true;
              }
              if (it.lowerLabel.toLowerCase().startsWith(lower)) {
                return true;
              }
              return false;
            }).sort((a, b) => {
              if (a.lowerLabel.startsWith(lower) && !b.lowerLabel.startsWith(lower)) {
                return -1;
              }
              if (!a.lowerLabel.startsWith(lower) && b.lowerLabel.startsWith(lower)) {
                return 1;
              }
              const lengthDiff = a.lowerLabel.length - b.lowerLabel.length;
              if (lengthDiff !== 0) {
                return lengthDiff;
              }
              return a.lowerLabel.localeCompare(b.lowerLabel);
            }).map(it => ({
              value: it.label,
              url: it.url
            }));
          },
          formatResult: /** {value: string, url: string} */item => {
            const a = document.createElement('a');
            a.text = item.value;
            a.href = item.url;
            a.classList.add('text-default');
            return a.outerHTML;
          },
          onSelect: /** {value: string, url: string} */item => {
            window.location.href = item.url;
            this.inputElement.value = '';
          }
        });
        this.once('render remove', () => {
          this.autocomplete.dispose();
          this.autocomplete = undefined;
        });
      }
    }

    /**
     * @private
     */
    runSearch() {
      const text = this.$input.val().trim();
      if (text !== '' && text.length >= 2) {
        this.search(text);
      }
    }

    /**
     * @private
     * @param {string} text
     */
    search(text) {
      this.collection.url = this.collection.urlRoot = 'GlobalSearch?q=' + encodeURIComponent(text);
      this.showPanel();
    }

    /**
     * @param {MouseEvent} e
     * @private
     */
    onMouseUp(e) {
      if (e.button !== 0) {
        return;
      }
      const target = e.target;
      if (!(target instanceof HTMLElement)) {
        return;
      }
      if (this.containerElement === target || this.containerElement.contains(target) || target.classList.contains('modal') || target.closest('.dialog.modal')) {
        return;
      }
      return this.closePanel();
    }

    /**
     * @param {MouseEvent} e
     * @private
     */
    onClick(e) {
      const target = e.target;
      if (!(target instanceof HTMLAnchorElement)) {
        return;
      }
      if (target.dataset.action === 'showMore' || target.classList.contains('global-search-button')) {
        return;
      }
      setTimeout(() => this.closePanel(), 100);
    }

    /**
     * @private
     */
    showPanel() {
      this.closePanel();
      if (this.autocomplete) {
        this.autocomplete.hide();
      }
      if (this.closeNavbarOnShow) {
        this.$el.closest('.navbar-body').removeClass('in');
      }
      const $container = this.$container = $('<div>').attr('id', 'global-search-panel');
      this.containerElement = this.$container.get(0);
      $container.appendTo(this.$el.find('.global-search-panel-container'));
      this.createView('panel', 'views/global-search/panel', {
        fullSelector: '#global-search-panel',
        collection: this.collection
      }, view => {
        view.render();
        this.listenToOnce(view, 'close', this.closePanel);
      });
      document.addEventListener('mouseup', this.onMouseUpBind);
      document.addEventListener('click', this.onClickBind);
    }

    /**
     * @private
     */
    closePanel() {
      const $container = $('#global-search-panel');
      $container.remove();
      if (this.hasView('panel')) {
        this.getView('panel').remove();
      }
      document.removeEventListener('mouseup', this.onMouseUpBind);
      document.removeEventListener('click', this.onClickBind);
    }

    /**
     * @typedef {Object} module:views/global-search/global-search~tabData
     * @property {string} url
     * @property {string} label
     * @property {string} lowerLabel
     * @property {string[]} words
     */

    /**
     * @private
     * @return {module:views/global-search/global-search~tabData[]}
     */
    getTabDataList() {
      /** @type {module:views/global-search/global-search~tabData[]}*/
      let list = [];

      /**
       * @param {string|TabsHelper~item} item
       * @return {module:views/global-search/global-search~tabData}
       */
      const toData = item => {
        const label = this.tabsHelper.getTranslatedTabLabel(item);
        const url = this.tabsHelper.isTabScope(item) ? `#${item}` : item.url;
        return {
          url: url,
          label: label,
          words: label.split(' ').map(it => it.toLowerCase()),
          lowerLabel: label.toLowerCase()
        };
      };

      /**
       * @param {string|TabsHelper~item} item
       * @return {boolean}
       */
      const checkTab = item => {
        return (this.tabsHelper.isTabScope(item) || this.tabsHelper.isTabUrl(item)) && this.tabsHelper.checkTabAccess(item);
      };
      for (const item of this.tabsHelper.getTabList()) {
        if (checkTab(item)) {
          list.push(toData(item));
          continue;
        }
        if (this.tabsHelper.isTabGroup(item) && item.itemList) {
          for (const subItem of item.itemList) {
            if (checkTab(subItem)) {
              list.push(toData(subItem));
            }
          }
        }
      }
      if (this.getUser().isAdmin()) {
        /** @type {
         *     Record<string, {
         *         order?: number,
         *         itemList: {
         *             url: string,
         *             tabQuickSearch: boolean,
         *             label: string,
         *         }[]
         *     }>
         * } panels */
        const panels = this.getMetadata().get(`app.adminPanel`) || {};
        Object.entries(panels).map(it => it[1]).sort((a, b) => a.order - b.order).filter(it => it.itemList) // For bc.
        .forEach(it => {
          it.itemList.filter(it => it.tabQuickSearch && it.label).filter(it => !list.find(subIt => subIt.url === it.url)).forEach(it => {
            const label = this.translate(it.label, 'labels', 'Admin');
            list.push({
              label: this.translate(it.label, 'labels', 'Admin'),
              url: it.url,
              lowerLabel: label.toLowerCase(),
              words: label.split(' ').map(it => it.toLowerCase())
            });
          });
        });
      }
      list = list.filter((it, i) => {
        return list.findIndex(subIt => subIt.url === it.url) === i && list.findIndex(subIt => subIt.lowerLabel === it.lowerLabel) === i;
      });

      /** @type {Record<string, {tab: boolean}>} */
      const scopes = this.getMetadata().get('scopes') || {};
      Object.entries(scopes).filter(_ref => {
        let [scope, it] = _ref;
        return it.tab && checkTab(scope);
      }).forEach(_ref2 => {
        let [scope] = _ref2;
        const data = toData(scope);
        if (list.find(it => it.lowerLabel === data.lowerLabel)) {
          return;
        }
        list.push(data);
      });
      return list.filter((item, index) => list.findIndex(it => it.lowerLabel === item.lowerLabel) === index);
    }
    isAvailable() {
      if (this.tabQuickSearch && !this.getUser().isPortal()) {
        return true;
      }
      let isAvailable = false;

      /** @type {string[]} */
      const entityTypeList = this.getConfig().get('globalSearchEntityList') || [];
      for (const it of entityTypeList) {
        if (this.getAcl().checkScope(it)) {
          isAvailable = true;
          break;
        }
      }
      return isAvailable;
    }
  }
  var _default = _exports.default = GlobalSearchView;
});

define("views/fields/varchar-column", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class VarcharColumnFieldView extends _varchar.default {
    searchTypeList = ['startsWith', 'contains', 'equals', 'endsWith', 'like', 'isEmpty', 'isNotEmpty'];
    fetchSearch() {
      const type = this.fetchSearchType() || 'startsWith';
      if (~['isEmpty', 'isNotEmpty'].indexOf(type)) {
        if (type === 'isEmpty') {
          return {
            typeFront: type,
            where: {
              type: 'or',
              value: [{
                type: 'columnIsNull',
                field: this.name
              }, {
                type: 'columnEquals',
                field: this.name,
                value: ''
              }]
            }
          };
        }
        return {
          typeFront: type,
          where: {
            type: 'and',
            value: [{
              type: 'columnNotEquals',
              field: this.name,
              value: ''
            }, {
              type: 'columnIsNotNull',
              field: this.name,
              value: null
            }]
          }
        };
      }
      let value = this.$element.val().toString().trim();
      value = value.trim();
      if (value) {
        return {
          value: value,
          type: 'column'.Espo.Utils.upperCaseFirst(type),
          data: {
            type: type,
            value: value
          }
        };
      }
      return null;
    }
  }
  var _default = _exports.default = VarcharColumnFieldView;
});

define("views/fields/users", ["exports", "views/fields/link-multiple"], function (_exports, _linkMultiple) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkMultiple = _interopRequireDefault(_linkMultiple);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UsersFieldView extends _linkMultiple.default {
    init() {
      this.assignmentPermission = this.getAcl().getPermissionLevel('assignmentPermission');
      if (this.assignmentPermission === 'no') {
        this.readOnly = true;
      }
      super.init();
    }
    getSelectBoolFilterList() {
      if (this.assignmentPermission === 'team' || this.assignmentPermission === 'no') {
        return ['onlyMyTeam'];
      }
    }
    getSelectPrimaryFilterName() {
      return 'active';
    }
  }
  var _default = _exports.default = UsersFieldView;
});

define("views/fields/teams", ["exports", "views/fields/link-multiple"], function (_exports, _linkMultiple) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkMultiple = _interopRequireDefault(_linkMultiple);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class TeamsFieldView extends _linkMultiple.default {
    init() {
      this.assignmentPermission = this.getAcl().getPermissionLevel('assignmentPermission');
      super.init();
    }
    getSelectBoolFilterList() {
      if (this.assignmentPermission === 'team' || this.assignmentPermission === 'no') {
        return ['onlyMy'];
      }
    }

    /** @inheritDoc */
    getOnEmptyAutocomplete() {
      if (this.autocompleteOnEmpty) {
        return undefined;
      }
      if (this.assignmentPermission === 'no') {
        return undefined;
      }

      /** @type {Record} */
      const names = this.getUser().get('teamsNames') || {};
      const list = this.getUser().getTeamIdList().filter(id => !this.ids.includes(id)).map(id => ({
        id: id,
        name: names[id] || id
      }));
      return Promise.resolve(list);
    }
  }
  var _default = _exports.default = TeamsFieldView;
});

define("views/fields/range-currency", ["exports", "views/fields/range-float", "views/fields/currency", "ui/select"], function (_exports, _rangeFloat, _currency, _select) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _rangeFloat = _interopRequireDefault(_rangeFloat);
  _currency = _interopRequireDefault(_currency);
  _select = _interopRequireDefault(_select);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RangeCurrencyFieldView extends _rangeFloat.default {
    type = 'rangeCurrency';
    editTemplate = 'fields/range-currency/edit';
    data() {
      return {
        currencyField: this.currencyField,
        currencyValue: this.model.get(this.fromCurrencyField) || this.getPreferences().get('defaultCurrency') || this.getConfig().get('defaultCurrency'),
        currencyList: this.currencyList,
        ...super.data()
      };
    }
    setup() {
      super.setup();
      const ucName = Espo.Utils.upperCaseFirst(this.name);
      this.fromCurrencyField = 'from' + ucName + 'Currency';
      this.toCurrencyField = 'to' + ucName + 'Currency';
      this.currencyField = this.name + 'Currency';
      this.currencyList = this.getConfig().get('currencyList') || ['USD'];
      this.decimalPlaces = this.getConfig().get('currencyDecimalPlaces');
    }
    setupAutoNumericOptions() {
      this.autoNumericOptions = {
        digitGroupSeparator: this.thousandSeparator || '',
        decimalCharacter: this.decimalMark,
        modifyValueOnWheel: false,
        selectOnFocus: false,
        decimalPlaces: this.decimalPlaces,
        allowDecimalPadding: true,
        showWarnings: false,
        formulaMode: true
      };
      if (this.decimalPlaces === null) {
        this.autoNumericOptions.decimalPlaces = this.decimalPlacesRawValue;
        this.autoNumericOptions.decimalPlacesRawValue = this.decimalPlacesRawValue;
        this.autoNumericOptions.allowDecimalPadding = false;
      }
    }
    afterRender() {
      super.afterRender();
      if (this.mode === this.MODE_EDIT) {
        this.$currency = this.$el.find('[data-name="' + this.currencyField + '"]');
        _select.default.init(this.$currency);
      }
    }
    formatNumber(value) {
      return _currency.default.prototype.formatNumberDetail.call(this, value);
    }
    getValueForDisplay() {
      let fromValue = this.model.get(this.fromField);
      let toValue = this.model.get(this.toField);
      fromValue = isNaN(fromValue) ? null : fromValue;
      toValue = isNaN(toValue) ? null : toValue;
      const currencyValue = this.model.get(this.fromCurrencyField) || this.model.get(this.toCurrencyField);
      const symbol = this.getMetadata().get(['app', 'currency', 'symbolMap', currencyValue]) || currencyValue;
      if (fromValue !== null && toValue !== null) {
        return this.formatNumber(fromValue) + ' &#8211 ' + this.formatNumber(toValue) + ' ' + symbol + '';
      }
      if (fromValue) {
        return '&#62;&#61; ' + this.formatNumber(fromValue) + ' ' + symbol + '';
      }
      if (toValue) {
        return '&#60;&#61; ' + this.formatNumber(toValue) + ' ' + symbol + '';
      }
      return this.translate('None');
    }
    fetch() {
      const data = super.fetch();
      const currencyValue = this.$currency.val();
      if (data[this.fromField] !== null) {
        data[this.fromCurrencyField] = currencyValue;
      } else {
        data[this.fromCurrencyField] = null;
      }
      if (data[this.toField] !== null) {
        data[this.toCurrencyField] = currencyValue;
      } else {
        data[this.toCurrencyField] = null;
      }
      return data;
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = RangeCurrencyFieldView;
});

define("views/fields/number", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NumberFieldView extends _varchar.default {
    type = 'number';
    validations = [];
    inlineEditDisabled = true;
    readOnly = true;
    data() {
      return {
        ...super.data(),
        textClass: 'numeric-text'
      };
    }

    /** @inheritDoc */
    fetch() {
      return {};
    }
  }
  var _default = _exports.default = NumberFieldView;
});

define("views/fields/map", ["exports", "views/fields/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class MapFieldView extends _base.default {
    type = 'map';
    detailTemplate = 'fields/map/detail';
    listTemplate = 'fields/map/detail';

    /** @type {string} */
    addressField;
    /** @type {string} */
    provider;
    height = 300;
    DEFAULT_PROVIDER = 'Google';

    // noinspection JSCheckFunctionSignatures
    data() {
      const data = super.data();
      data.hasAddress = this.hasAddress();

      // noinspection JSValidateTypes
      return data;
    }
    setup() {
      this.addressField = this.name.slice(0, this.name.length - 3);
      this.provider = this.provider || this.getConfig().get('mapProvider') || this.DEFAULT_PROVIDER;
      this.height = this.options.height || this.params.height || this.height;
      const addressAttributeList = Object.keys(this.getMetadata().get('fields.address.fields') || {}).map(a => this.addressField + Espo.Utils.upperCaseFirst(a));
      this.listenTo(this.model, 'sync', model => {
        let isChanged = false;
        addressAttributeList.forEach(attribute => {
          if (model.hasChanged(attribute)) {
            isChanged = true;
          }
        });
        if (isChanged && this.isRendered()) {
          this.reRender();
        }
      });
      this.listenTo(this.model, 'after:save', () => {
        if (this.isRendered()) {
          this.reRender();
        }
      });
    }
    hasAddress() {
      return !!this.model.get(this.addressField + 'City') || !!this.model.get(this.addressField + 'PostalCode');
    }
    onRemove() {
      $(window).off('resize.' + this.cid);
    }
    afterRender() {
      this.addressData = {
        city: this.model.get(this.addressField + 'City'),
        street: this.model.get(this.addressField + 'Street'),
        postalCode: this.model.get(this.addressField + 'PostalCode'),
        country: this.model.get(this.addressField + 'Country'),
        state: this.model.get(this.addressField + 'State')
      };
      this.$map = this.$el.find('.map');
      if (this.hasAddress()) {
        this.renderMap();
      }
    }
    renderMap() {
      this.processSetHeight(true);
      if (this.height === 'auto') {
        $(window).off('resize.' + this.cid);
        $(window).on('resize.' + this.cid, this.processSetHeight.bind(this));
      }
      const rendererId = this.getMetadata().get(['app', 'mapProviders', this.provider, 'renderer']);
      if (rendererId) {
        Espo.loader.require(rendererId, Renderer => {
          new Renderer(this).render(this.addressData);
        });
        return;
      }
      const methodName = 'afterRender' + this.provider.replace(/\s+/g, '');
      if (typeof this[methodName] === 'function') {
        this[methodName]();
      }
    }
    processSetHeight(init) {
      let height = this.height;
      if (this.height === 'auto') {
        height = this.$el.parent().height();
        if (init && height <= 0) {
          setTimeout(() => this.processSetHeight(true), 50);
          return;
        }
      }
      this.$map.css('height', height + 'px');
    }
  }
  var _default = _exports.default = MapFieldView;
});

define("views/fields/link-one", ["exports", "views/fields/link"], function (_exports, _link) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _link = _interopRequireDefault(_link);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class LinkOneFieldView extends _link.default {
    searchTypeList = ['is', 'isEmpty', 'isNotEmpty', 'isOneOf'];
    fetchSearch() {
      const type = this.$el.find('select.search-type').val();
      const value = this.$el.find('[data-name="' + this.idName + '"]').val();
      if (['isOneOf'].includes(type) && !this.searchData.oneOfIdList.length) {
        return {
          type: 'isNotNull',
          attribute: 'id',
          data: {
            type: type
          }
        };
      }
      if (type === 'isOneOf') {
        if (!value) {
          return false;
        }
        return {
          type: 'linkedWith',
          field: this.name,
          value: this.searchData.oneOfIdList,
          data: {
            type: type,
            oneOfIdList: this.searchData.oneOfIdList,
            oneOfNameHash: this.searchData.oneOfNameHash
          }
        };
      }
      if (type === 'is' || !type) {
        if (!value) {
          return false;
        }
        return {
          type: 'linkedWith',
          field: this.name,
          value: value,
          data: {
            type: type,
            nameValue: this.$el.find('[data-name="' + this.nameName + '"]').val()
          }
        };
      }
      if (type === 'isEmpty') {
        return {
          type: 'isNotLinked',
          data: {
            type: type
          }
        };
      }
      if (type === 'isNotEmpty') {
        return {
          type: 'isLinked',
          data: {
            type: type
          }
        };
      }
    }
  }
  var _default = _exports.default = LinkOneFieldView;
});

define("views/fields/link-multiple-with-status", ["exports", "views/fields/link-multiple"], function (_exports, _linkMultiple) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkMultiple = _interopRequireDefault(_linkMultiple);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class LinkMultipleWithStatusFieldView extends _linkMultiple.default {
    /**
     * @protected
     * @type {string[]}
     */
    canceledStatusList;
    setup() {
      super.setup();
      this.columnsName = this.name + 'Columns';
      this.columns = Espo.Utils.cloneDeep(this.model.get(this.columnsName) || {});
      this.listenTo(this.model, 'change:' + this.columnsName, () => {
        this.columns = Espo.Utils.cloneDeep(this.model.get(this.columnsName) || {});
      });
      this.statusField = this.getMetadata().get(['entityDefs', this.model.entityType, 'fields', this.name, 'columns', 'status']);
      this.styleMap = this.getMetadata().get(['entityDefs', this.foreignScope, 'fields', this.statusField, 'style']) || {};
      this.canceledStatusList = [];
    }
    getAttributeList() {
      const list = super.getAttributeList();
      list.push(this.name + 'Columns');
      return list;
    }
    getDetailLinkHtml(id, name) {
      const status = (this.columns[id] || {}).status;
      if (!status) {
        return super.getDetailLinkHtml(id, name);
      }
      const style = this.styleMap[status];
      const targetStyleList = ['success', 'info', 'danger', 'warning'];
      if (!style || !~targetStyleList.indexOf(style)) {
        return super.getDetailLinkHtml(id, name);
      }
      let iconStyle = '';
      if (style === 'success') {
        iconStyle = 'fas fa-check text-success small';
      } else if (this.canceledStatusList.includes(status)) {
        iconStyle = `fas fa-times text-${style} small`;
      }
      return `<span class="${iconStyle}" style="display: inline-block; min-width: 12px"></span> ` + super.getDetailLinkHtml(id, name);
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = LinkMultipleWithStatusFieldView;
});

define("views/fields/link-multiple-with-columns-with-primary", ["exports", "views/fields/link-multiple-with-columns", "views/fields/link-multiple-with-primary"], function (_exports, _linkMultipleWithColumns, _linkMultipleWithPrimary) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkMultipleWithColumns = _interopRequireDefault(_linkMultipleWithColumns);
  _linkMultipleWithPrimary = _interopRequireDefault(_linkMultipleWithPrimary);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * A link-multiple field with columns and a primary.
   */
  class LinkMultipleWithColumnsWithPrimaryFieldView extends _linkMultipleWithColumns.default {
    /**
     * @protected
     * @type {string}
     */
    primaryLink;
    getAttributeList() {
      const list = super.getAttributeList();
      list.push(this.primaryIdAttribute);
      list.push(this.primaryNameAttribute);
      return list;
    }
    setup() {
      this.primaryLink = this.primaryLink || this.model.getFieldParam(this.name, 'primaryLink');
      this.primaryIdAttribute = this.primaryLink + 'Id';
      this.primaryNameAttribute = this.primaryLink + 'Name';
      super.setup();
      this.events['click [data-action="switchPrimary"]'] = e => {
        const $target = $(e.currentTarget);
        const id = $target.data('id');
        this.switchPrimary(id);
      };
      this.primaryId = this.model.get(this.primaryIdAttribute);
      this.primaryName = this.model.get(this.primaryNameAttribute);
      this.listenTo(this.model, 'change:' + this.primaryIdAttribute, () => {
        this.primaryId = this.model.get(this.primaryIdAttribute);
        this.primaryName = this.model.get(this.primaryNameAttribute);
      });
    }
    setPrimaryId(id) {
      this.primaryId = id;
      this.primaryName = id ? this.nameHash[id] : null;
    }
    switchPrimary(id) {
      const $switch = this.$el.find(`[data-id="${id}"][data-action="switchPrimary"]`);
      if (!$switch.hasClass('active')) {
        this.$el.find('button[data-action="switchPrimary"]').removeClass('active').children().addClass('text-muted');
        $switch.addClass('active').children().removeClass('text-muted');
        this.setPrimaryId(id);
        this.trigger('change');
      }
    }
    renderLinks() {
      if (this.primaryId) {
        this.addLinkHtml(this.primaryId, this.primaryName);
      }
      this.ids.forEach(id => {
        if (id !== this.primaryId) {
          this.addLinkHtml(id, this.nameHash[id]);
        }
      });
    }
    getValueForDisplay() {
      if (this.isDetailMode() || this.isListMode()) {
        const itemList = [];
        if (this.primaryId) {
          itemList.push(this.getDetailLinkHtml(this.primaryId, this.primaryName));
        }
        if (!this.ids.length) {
          return;
        }
        this.ids.forEach(id => {
          if (id !== this.primaryId) {
            itemList.push(this.getDetailLinkHtml(id));
          }
        });
        return itemList.map(item => $('<div>').append(item).addClass('link-multiple-item').get(0).outerHTML).join('');
      }
    }
    deleteLink(id) {
      if (id === this.primaryId) {
        this.setPrimaryId(null);
      }
      super.deleteLink(id);
    }
    deleteLinkHtml(id) {
      super.deleteLinkHtml(id);
      this.managePrimaryButton();
    }
    addLinkHtml(id, name) {
      name = name || id;
      if (this.isSearchMode()) {
        return super.addLinkHtml(id, name);
      }
      if (this.skipRoles) {
        return _linkMultipleWithPrimary.default.prototype.addLinkHtml.call(this, id, name);
      }
      const $el = super.addLinkHtml(id, name);
      const isPrimary = id === this.primaryId;
      const $star = $('<span>').addClass('fas fa-star fa-sm').addClass(!isPrimary ? 'text-muted' : '');
      const $button = $('<button>').attr('type', 'button').addClass('btn btn-link btn-sm pull-right hidden').attr('title', this.translate('Primary')).attr('data-action', 'switchPrimary').attr('data-id', id).append($star);
      $button.insertAfter($el.children().first().children().first());
      this.managePrimaryButton();
      return $el;
    }
    managePrimaryButton() {
      const $primary = this.$el.find('button[data-action="switchPrimary"]');
      if ($primary.length > 1) {
        $primary.removeClass('hidden');
      } else {
        $primary.addClass('hidden');
      }
      if ($primary.filter('.active').length === 0) {
        const $first = $primary.first();
        if ($first.length) {
          $first.addClass('active').children().removeClass('text-muted');
          const id = $first.data('id');
          this.setPrimaryId(id);
          if (id !== this.primaryId) {
            this.trigger('change');
          }
        }
      }
    }
    fetch() {
      const data = super.fetch();
      data[this.primaryIdAttribute] = this.primaryId;
      data[this.primaryNameAttribute] = this.primaryName;
      return data;
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = LinkMultipleWithColumnsWithPrimaryFieldView;
});

define("views/fields/link-multiple-category-tree", ["exports", "views/fields/link-multiple"], function (_exports, _linkMultiple) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkMultiple = _interopRequireDefault(_linkMultiple);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class LinkMultipleCategoryTreeFieldView extends _linkMultiple.default {
    selectRecordsView = 'views/modals/select-category-tree-records';
    autocompleteDisabled = false;
    getUrl(id) {
      return '#' + this.entityType + '/list/categoryId=' + id;
    }
    fetchSearch() {
      const data = super.fetchSearch();
      if (!data) {
        return data;
      }
      data.type = 'inCategory';
      return data;
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = LinkMultipleCategoryTreeFieldView;
});

define("views/fields/link-category-tree", ["exports", "views/fields/link"], function (_exports, _link) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _link = _interopRequireDefault(_link);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class LinkCategoryTreeFieldView extends _link.default {
    selectRecordsView = 'views/modals/select-category-tree-records';
    autocompleteDisabled = false;
    fetchSearch() {
      const data = super.fetchSearch();
      if (!data) {
        return data;
      }
      if (data.typeFront === 'is') {
        data.field = this.name;
        data.type = 'inCategory';
      }
      return data;
    }
    getUrl() {
      const id = this.model.get(this.idName);
      if (!id) {
        return null;
      }
      return '#' + this.entityType + '/list/categoryId=' + id;
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = LinkCategoryTreeFieldView;
});

define("views/fields/json-object", ["exports", "views/fields/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class JsonObjectFieldView extends _base.default {
    type = 'jsonObject';
    listTemplate = 'fields/json-object/detail';
    detailTemplate = 'fields/json-object/detail';
    data() {
      const data = super.data();
      data.valueIsSet = this.model.has(this.name);
      data.isNotEmpty = !!this.model.get(this.name);
      return data;
    }
    getValueForDisplay() {
      const value = this.model.get(this.name);
      if (!value) {
        return null;
      }
      return JSON.stringify(value, null, 2).replace(/(\r\n|\n|\r)/gm, '<br>').replace(/\s/g, '&nbsp;');
    }
  }
  var _default = _exports.default = JsonObjectFieldView;
});

define("views/fields/id", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class IdFieldView extends _varchar.default {
    searchTypeList = ['equals', 'notEquals'];
  }
  var _default = _exports.default = IdFieldView;
});

define("views/fields/formula", ["exports", "views/fields/text"], function (_exports, _text) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _text = _interopRequireDefault(_text);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * @type {{
   *     edit: import('ace-builds').edit,
   *     require: import('ace-builds').require,
   * }}
   */
  let ace;
  class FormulaFieldView extends _text.default {
    detailTemplate = 'fields/formula/detail';
    editTemplate = 'fields/formula/edit';
    height = 308;
    maxLineDetailCount = 80;
    maxLineEditCount = 200;
    insertDisabled = false;
    checkSyntaxDisabled = false;
    smallFont = false;
    events = {
      /** @this FormulaFieldView */
      'click [data-action="addAttribute"]': function () {
        this.addAttribute();
      },
      /** @this FormulaFieldView */
      'click [data-action="addFunction"]': function () {
        this.addFunction();
      },
      /** @this FormulaFieldView */
      'click [data-action="checkSyntax"]': function () {
        this.checkSyntax();
      }
    };
    setup() {
      super.setup();
      this.height = this.options.height || this.params.height || this.height;
      this.smallFont = this.options.smallFont || this.params.smallFont || this.smallFont;
      this.maxLineDetailCount = this.options.maxLineDetailCount || this.params.maxLineDetailCount || this.maxLineDetailCount;
      this.maxLineEditCount = this.options.maxLineEditCount || this.params.maxLineEditCount || this.maxLineEditCount;
      this.targetEntityType = this.options.targetEntityType || this.params.targetEntityType || this.targetEntityType;
      this.insertDisabled = this.insertDisabled || this.options.insertDisabled;
      this.checkSyntaxDisabled = this.checkSyntaxDisabled || this.options.checkSyntaxDisabled;
      this.containerId = 'editor-' + Math.floor(Math.random() * 10000 + 1).toString();
      if (this.mode === this.MODE_EDIT || this.mode === this.MODE_DETAIL) {
        this.wait(this.requireAce());
      }
      this.on('remove', () => {
        if (this.editor) {
          this.editor.destroy();
        }
      });
    }
    requireAce() {
      return Espo.loader.requirePromise('lib!ace').then(lib => {
        ace = /** window.ace */lib;
        const list = [Espo.loader.requirePromise('lib!ace-mode-javascript'), Espo.loader.requirePromise('lib!ace-ext-language_tools')];
        if (this.getThemeManager().getParam('isDark')) {
          list.push(Espo.loader.requirePromise('lib!ace-theme-tomorrow_night'));
        }
        return Promise.all(list);
      });
    }
    data() {
      const data = super.data();
      data.containerId = this.containerId;
      data.targetEntityType = this.targetEntityType;
      data.hasSide = !this.insertDisabled || !this.checkSyntaxDisabled;
      data.hasInsert = !this.insertDisabled;
      data.hasCheckSyntax = !this.checkSyntaxDisabled;
      return data;
    }
    afterRender() {
      super.afterRender();
      this.$editor = this.$el.find('#' + this.containerId);
      if (this.$editor.length && (this.mode === this.MODE_EDIT || this.mode === this.MODE_DETAIL || this.mode === this.MODE_LIST)) {
        const fontSize = this.smallFont ? 'var(--font-size-small)' : 'var(--font-size-base)';
        const lineHeight = this.smallFont ? 'var(--line-height-small)' : 'var(--line-height-computed)';
        this.$editor.css('fontSize', fontSize);
        if (this.mode === this.MODE_EDIT) {
          const height = (this.height * this.getThemeManager().getFontSizeFactor()).toString();
          this.$editor.css('minHeight', `${height}px`);
        }
        const editor = this.editor = ace.edit(this.containerId);
        editor.setOptions({
          fontFamily: 'var(--font-family-monospace)'
        });
        editor.setFontSize(fontSize);
        editor.container.style.lineHeight = lineHeight;
        editor.renderer.updateFontSize();
        editor.setOptions({
          maxLines: this.mode === this.MODE_EDIT ? this.maxLineEditCount : this.maxLineDetailCount
        });
        if (this.getThemeManager().getParam('isDark')) {
          editor.setOptions({
            theme: 'ace/theme/tomorrow_night'
          });
        }
        if (this.isEditMode()) {
          // noinspection JSCheckFunctionSignatures
          editor.getSession().on('change', () => {
            this.trigger('change', {
              ui: true
            });
          });
          editor.getSession().setUseWrapMode(true);
        }
        if (this.isReadMode()) {
          editor.setReadOnly(true);
          // noinspection JSUnresolvedReference
          editor.renderer.$cursorLayer.element.style.display = "none";
          editor.renderer.setShowGutter(false);
        }
        editor.setShowPrintMargin(false);
        editor.getSession().setUseWorker(false);
        editor.commands.removeCommand('find');
        editor.setHighlightActiveLine(false);
        const JavaScriptMode = ace.require('ace/mode/javascript').Mode;
        editor.session.setMode(new JavaScriptMode());
        if (!this.insertDisabled && !this.isReadMode()) {
          this.initAutocomplete();
        }
      }
    }
    fetch() {
      const data = {};
      let value = this.editor.getValue();
      if (value === '') {
        value = null;
      }
      data[this.name] = value;
      return data;
    }
    addAttribute() {
      this.createView('dialog', 'views/admin/formula/modals/add-attribute', {
        scope: this.targetEntityType
      }, view => {
        view.render();
        this.listenToOnce(view, 'add', attribute => {
          this.editor.insert(attribute);
          this.clearView('dialog');
        });
      });
    }
    addFunction() {
      this.createView('dialog', 'views/admin/formula/modals/add-function', {
        scope: this.targetEntityType,
        functionDataList: this.getFunctionDataList()
      }, view => {
        view.render();
        this.listenToOnce(view, 'add', string => {
          this.editor.insert(string);
          this.clearView('dialog');
        });
      });
    }

    /**
     * @private
     * @return {{
     *     name: string,
     *     insertText?: string,
     *     returnType?: string,
     * }[]}
     */
    getFunctionDataList() {
      let list = [...this.getMetadata().get(['app', 'formula', 'functionList'], [])].filter(item => item.insertText);
      if (this.options.additionalFunctionDataList) {
        list = list.concat(this.options.additionalFunctionDataList);
      }
      const allowedFunctionList = /** @type string[] */this.options.allowedFunctionList;
      if (allowedFunctionList) {
        list = list.filter(/** {name: string} */item => {
          for (const func of allowedFunctionList) {
            if (func.endsWith('\\') && item.name.startsWith(func)) {
              return true;
            }
            if (item.name === func) {
              return true;
            }
          }
          return false;
        });
      }
      if (!this.targetEntityType) {
        list = list.filter(item => {
          if (item.name.indexOf('entity\\') === 0) {
            return false;
          }
          return true;
        });
      }
      return list;
    }
    initAutocomplete() {
      const functionItemList = this.getFunctionDataList().filter(item => item.insertText);
      const attributeList = this.getFormulaAttributeList();
      ace.require('ace/ext/language_tools');
      this.editor.setOptions({
        enableBasicAutocompletion: true,
        enableLiveAutocompletion: true
      });

      // noinspection JSUnusedGlobalSymbols
      const completer = {
        identifierRegexps: [/[\\a-zA-Z0-9{}\[\].$'"]/],
        getCompletions: function (editor, session, pos, prefix, callback) {
          const matchedFunctionItemList = functionItemList.filter(originalItem => {
            const text = originalItem.name.toString().toLowerCase();
            if (text.indexOf(prefix) === 0) {
              return true;
            }
            const parts = text.split('\\');
            if (parts[parts.length - 1].indexOf(prefix) === 0) {
              return true;
            }
            if (parts.length > 2 && parts[parts.length - 2].indexOf(prefix) === 0) {
              return true;
            }
            return false;
          });
          let itemList = matchedFunctionItemList.map(item => {
            return {
              caption: item.name + '()',
              value: item.insertText,
              meta: item.returnType || null,
              completer: {
                insertMatch: (editor, data) => {
                  editor.completer.insertMatch({
                    value: data.value
                  });
                  const index = data.value.indexOf('(');
                  if (!~index) {
                    return;
                  }
                  if (~data.value.indexOf('()')) {
                    return;
                  }
                  const pos = editor.selection.getCursor();

                  // noinspection JSCheckFunctionSignatures
                  editor.gotoLine(pos.row + 1, pos.column - data.value.length + index + 1);
                }
              }
            };
          });
          const matchedAttributeList = attributeList.filter(item => {
            if (item.indexOf(prefix) === 0) {
              return true;
            }
            return false;
          });
          const itemAttributeList = matchedAttributeList.map(item => {
            return {
              name: item,
              value: item,
              meta: 'attribute'
            };
          });

          // noinspection JSCheckFunctionSignatures
          itemList = itemList.concat(itemAttributeList);
          callback(null, itemList);
        }
      };
      this.editor.completers = [completer];
    }
    getFormulaAttributeList() {
      if (!this.targetEntityType) {
        return [];
      }
      const attributeList = this.getFieldManager().getEntityTypeAttributeList(this.targetEntityType).concat(['id']).sort();
      const links = this.getMetadata().get(['entityDefs', this.targetEntityType, 'links']) || {};
      const linkList = [];
      Object.keys(links).forEach(link => {
        const type = links[link].type;
        if (!type) {
          return;
        }
        if (~['belongsToParent', 'hasOne', 'belongsTo'].indexOf(type)) {
          linkList.push(link);
        }
      });
      linkList.sort();
      linkList.forEach(link => {
        const scope = links[link].entity;
        if (!scope) {
          return;
        }
        if (links[link].disabled) {
          return;
        }
        const linkAttributeList = this.getFieldManager().getEntityTypeAttributeList(scope).sort();
        linkAttributeList.forEach(item => {
          attributeList.push(link + '.' + item);
        });
      });
      return attributeList;
    }
    checkSyntax() {
      const expression = this.editor.getValue();
      if (!expression) {
        Espo.Ui.success(this.translate('checkSyntaxSuccess', 'messages', 'Formula'));
        return;
      }
      Espo.Ajax.postRequest('Formula/action/checkSyntax', {
        expression: expression
      }).then(/** Record */response => {
        if (response.isSuccess) {
          Espo.Ui.success(this.translate('checkSyntaxSuccess', 'messages', 'Formula'));
          return;
        }
        let message = this.translate('checkSyntaxError', 'messages', 'Formula');
        if (response.message) {
          message += ' ' + response.message;
        }
        Espo.Ui.error(message);
      });
    }
  }
  var _default = _exports.default = FormulaFieldView;
});

define("views/fields/foreign", ["exports", "views/fields/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignFieldView extends _base.default {
    type = 'foreign';
  }
  var _default = _exports.default = ForeignFieldView;
});

define("views/fields/foreign-varchar", ["exports", "views/fields/varchar", "helpers/misc/foreign-field"], function (_exports, _varchar, _foreignField) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  _foreignField = _interopRequireDefault(_foreignField);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignVarcharFieldView extends _varchar.default {
    type = 'foreign';
    setup() {
      const helper = new _foreignField.default(this);
      const foreignParams = helper.getForeignParams();
      for (const param in foreignParams) {
        this.params[param] = foreignParams[param];
      }
      super.setup();
    }
  }
  var _default = _exports.default = ForeignVarcharFieldView;
});

define("views/fields/foreign-url", ["exports", "views/fields/url", "helpers/misc/foreign-field"], function (_exports, _url, _foreignField) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _url = _interopRequireDefault(_url);
  _foreignField = _interopRequireDefault(_foreignField);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignUrlFieldView extends _url.default {
    type = 'foreign';
    readOnly = true;
    setup() {
      const helper = new _foreignField.default(this);
      const foreignParams = helper.getForeignParams();
      for (const param in foreignParams) {
        this.params[param] = foreignParams[param];
      }
      super.setup();
    }
  }
  var _default = _exports.default = ForeignUrlFieldView;
});

define("views/fields/foreign-url-multiple", ["exports", "views/fields/url-multiple", "helpers/misc/foreign-field"], function (_exports, _urlMultiple, _foreignField) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _urlMultiple = _interopRequireDefault(_urlMultiple);
  _foreignField = _interopRequireDefault(_foreignField);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignUrlMultipleFieldView extends _urlMultiple.default {
    type = 'foreign';
    readOnly = true;
    setup() {
      const helper = new _foreignField.default(this);
      const foreignParams = helper.getForeignParams();
      for (const param in foreignParams) {
        this.params[param] = foreignParams[param];
      }
      super.setup();
    }
  }
  var _default = _exports.default = ForeignUrlMultipleFieldView;
});

define("views/fields/foreign-text", ["exports", "views/fields/text", "helpers/misc/foreign-field"], function (_exports, _text, _foreignField) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _text = _interopRequireDefault(_text);
  _foreignField = _interopRequireDefault(_foreignField);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignTextFieldView extends _text.default {
    type = 'foreign';
    setup() {
      const helper = new _foreignField.default(this);
      const foreignParams = helper.getForeignParams();
      for (const param in foreignParams) {
        this.params[param] = foreignParams[param];
      }
      super.setup();
    }
  }
  var _default = _exports.default = ForeignTextFieldView;
});

define("views/fields/foreign-phone", ["exports", "views/fields/phone"], function (_exports, _phone) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _phone = _interopRequireDefault(_phone);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignPhoneFieldView extends _phone.default {
    type = 'foreign';
    readOnly = true;
    setup() {
      super.setup();

      // Numeric search does not work for foreign.
      this.isNumeric = false;
    }
  }
  var _default = _exports.default = ForeignPhoneFieldView;
});

define("views/fields/foreign-multi-enum", ["exports", "views/fields/multi-enum", "views/fields/foreign-array"], function (_exports, _multiEnum, _foreignArray) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _multiEnum = _interopRequireDefault(_multiEnum);
  _foreignArray = _interopRequireDefault(_foreignArray);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignMultiEnumFieldView extends _multiEnum.default {
    type = 'foreign';
    setupOptions() {
      _foreignArray.default.prototype.setupOptions.call(this);
    }
  }
  var _default = _exports.default = ForeignMultiEnumFieldView;
});

define("views/fields/foreign-int", ["exports", "views/fields/int", "helpers/misc/foreign-field"], function (_exports, _int, _foreignField) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _int = _interopRequireDefault(_int);
  _foreignField = _interopRequireDefault(_foreignField);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignIntFieldView extends _int.default {
    type = 'foreign';
    setup() {
      const helper = new _foreignField.default(this);
      const foreignParams = helper.getForeignParams();
      for (const param in foreignParams) {
        this.params[param] = foreignParams[param];
      }
      this.disableFormatting = foreignParams.disableFormatting;
      super.setup();
    }
  }
  var _default = _exports.default = ForeignIntFieldView;
});

define("views/fields/foreign-float", ["exports", "views/fields/float", "helpers/misc/foreign-field"], function (_exports, _float, _foreignField) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _float = _interopRequireDefault(_float);
  _foreignField = _interopRequireDefault(_foreignField);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignFloatFieldView extends _float.default {
    type = 'foreign';
    setup() {
      const helper = new _foreignField.default(this);
      const foreignParams = helper.getForeignParams();
      for (const param in foreignParams) {
        this.params[param] = foreignParams[param];
      }
      super.setup();
    }
  }
  var _default = _exports.default = ForeignFloatFieldView;
});

define("views/fields/foreign-enum", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignEnumFieldView extends _enum.default {
    type = 'foreign';
    setupOptions() {
      this.params.options = [];
      const field = this.params.field;
      const link = this.params.link;
      if (!field || !link) {
        return;
      }
      const scope = this.getMetadata().get(['entityDefs', this.model.entityType, 'links', link, 'entity']);
      if (!scope) {
        return;
      }
      const fieldDefs = this.getMetadata().get(['entityDefs', scope, 'fields', field]);
      if (!fieldDefs) {
        return;
      }
      let {
        optionsPath,
        translation,
        options,
        isSorted,
        displayAsLabel,
        style
      } = fieldDefs;
      options = optionsPath ? this.getMetadata().get(optionsPath) : options;
      this.params.options = Espo.Utils.clone(options) || [];
      this.params.translation = translation;
      this.params.isSorted = isSorted || false;
      this.params.displayAsLabel = displayAsLabel || false;
      this.styleMap = style || {};
      this.translatedOptions = Object.fromEntries(this.params.options.map(item => [item, this.getLanguage().translateOption(item, field, scope)]));
    }
  }
  var _default = _exports.default = ForeignEnumFieldView;
});

define("views/fields/foreign-email", ["exports", "views/fields/email"], function (_exports, _email) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _email = _interopRequireDefault(_email);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignEmailFieldView extends _email.default {
    type = 'foreign';
    readOnly = true;
  }
  var _default = _exports.default = ForeignEmailFieldView;
});

define("views/fields/foreign-datetime", ["exports", "views/fields/datetime", "helpers/misc/foreign-field"], function (_exports, _datetime, _foreignField) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _datetime = _interopRequireDefault(_datetime);
  _foreignField = _interopRequireDefault(_foreignField);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignDatetimeFieldView extends _datetime.default {
    type = 'foreign';
    setup() {
      const helper = new _foreignField.default(this);
      const foreignParams = helper.getForeignParams();
      for (const param in foreignParams) {
        this.params[param] = foreignParams[param];
      }
      super.setup();
    }
  }
  var _default = _exports.default = ForeignDatetimeFieldView;
});

define("views/fields/foreign-date", ["exports", "views/fields/date", "helpers/misc/foreign-field"], function (_exports, _date, _foreignField) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _date = _interopRequireDefault(_date);
  _foreignField = _interopRequireDefault(_foreignField);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignDateFieldView extends _date.default {
    type = 'foreign';
    setup() {
      const helper = new _foreignField.default(this);
      const foreignParams = helper.getForeignParams();
      for (const param in foreignParams) {
        this.params[param] = foreignParams[param];
      }
      super.setup();
    }
  }
  var _default = _exports.default = ForeignDateFieldView;
});

define("views/fields/foreign-currency-converted", ["exports", "views/fields/currency-converted"], function (_exports, _currencyConverted) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _currencyConverted = _interopRequireDefault(_currencyConverted);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignCurrencyConvertedFieldView extends _currencyConverted.default {
    type = 'foreign';
  }
  var _default = _exports.default = ForeignCurrencyConvertedFieldView;
});

define("views/fields/foreign-checklist", ["exports", "views/fields/checklist"], function (_exports, _checklist) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _checklist = _interopRequireDefault(_checklist);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignChecklistFieldView extends _checklist.default {
    type = 'foreign';
    setupOptions() {
      this.params.options = [];
      if (!this.params.field || !this.params.link) {
        return;
      }
      const scope = this.getMetadata().get(['entityDefs', this.model.entityType, 'links', this.params.link, 'entity']);
      if (!scope) {
        return;
      }
      this.params.isSorted = this.getMetadata().get(['entityDefs', scope, 'fields', this.params.field, 'isSorted']) || false;
      this.params.options = this.getMetadata().get(['entityDefs', scope, 'fields', this.params.field, 'options']) || [];
      this.translatedOptions = {};
      this.params.options.forEach(item => {
        this.translatedOptions[item] = this.getLanguage().translateOption(item, this.params.field, scope);
      });
    }
  }
  var _default = _exports.default = ForeignChecklistFieldView;
});

define("views/fields/foreign-bool", ["exports", "views/fields/bool", "helpers/misc/foreign-field"], function (_exports, _bool, _foreignField) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _bool = _interopRequireDefault(_bool);
  _foreignField = _interopRequireDefault(_foreignField);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ForeignBoolFieldView extends _bool.default {
    type = 'foreign';
    setup() {
      const helper = new _foreignField.default(this);
      const foreignParams = helper.getForeignParams();
      for (const param in foreignParams) {
        this.params[param] = foreignParams[param];
      }
      super.setup();
    }
  }
  var _default = _exports.default = ForeignBoolFieldView;
});

define("views/fields/followers", ["exports", "views/fields/link-multiple"], function (_exports, _linkMultiple) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkMultiple = _interopRequireDefault(_linkMultiple);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class FollowersFieldView extends _linkMultiple.default {
    foreignScope = 'User';
    portionSize = 6;
    linkClass = 'text-default';
    setup() {
      super.setup();
      this.addActionHandler('showMoreFollowers', (e, target) => {
        this.showMoreFollowers();
        $(target).remove();
      });
      this.portionSize = this.getConfig().get('recordFollowersLoadLimit') || this.portionSize;
      this.limit = this.portionSize;
      this.listenTo(this.model, 'change:isFollowed', () => {
        const idList = this.model.get(this.idsName) || [];
        if (this.model.get('isFollowed')) {
          if (!idList.includes(this.getUser().id)) {
            idList.unshift(this.getUser().id);
            const nameMap = this.model.get(this.nameHashName) || {};
            nameMap[this.getUser().id] = this.getUser().get('name');
            this.model.trigger('change:' + this.idsName);
            this.reRender();
          }
          return;
        }
        const index = idList.indexOf(this.getUser().id);
        if (~index) {
          idList.splice(index, 1);
          this.model.trigger('change:' + this.idsName);
          this.reRender();
        }
      });
    }

    /*reloadFollowers() {
        this.getCollectionFactory().create('User', collection => {
            collection.url = this.model.entityType + '/' + this.model.id + '/followers';
            collection.offset = 0;
            collection.maxSize = this.limit;
              this.listenToOnce(collection, 'sync', () => {
                let idList = [];
                let nameMap = {};
                  collection.forEach(user => {
                    idList.push(user.id);
                    nameMap[user.id] = user.get('name');
                });
                  this.model.set(this.idsName, idList);
                this.model.set(this.nameHashName, nameMap);
                  this.reRender();
            });
              collection.fetch();
        });
    }*/

    showMoreFollowers() {
      this.getCollectionFactory().create('User', collection => {
        collection.url = this.model.entityType + '/' + this.model.id + '/followers';
        collection.offset = this.ids.length || 0;
        collection.maxSize = this.portionSize;
        collection.data.select = ['id', 'name'].join(',');
        collection.orderBy = null;
        collection.order = null;
        this.listenToOnce(collection, 'sync', () => {
          const idList = this.model.get(this.idsName) || [];
          const nameMap = this.model.get(this.nameHashName) || {};
          collection.forEach(user => {
            idList.push(user.id);
            nameMap[user.id] = user.get('name');
          });
          this.limit += this.portionSize;
          this.model.trigger('change:' + this.idsName);
          this.reRender();
        });
        collection.fetch();
      });
    }
    getValueForDisplay() {
      if (this.mode === this.MODE_DETAIL || this.mode === this.MODE_LIST) {
        const list = [];
        this.ids.forEach(id => {
          list.push(this.getDetailLinkHtml(id));
        });
        let str = null;
        if (list.length) {
          str = '' + list.join(', ') + '';
        }
        if (list.length >= this.limit) {
          str += ', <a role="button" data-action="showMoreFollowers">...</a>';
        }
        return str;
      }
    }
  }
  var _default = _exports.default = FollowersFieldView;
});

define("views/fields/enum-styled", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EnumStyledFieldView extends _enum.default {}

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = EnumStyledFieldView;
});

define("views/fields/enum-float", ["exports", "views/fields/enum-int"], function (_exports, _enumInt) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enumInt = _interopRequireDefault(_enumInt);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EnumFloatFieldView extends _enumInt.default {
    type = 'enumFloat';
    fetch() {
      const raw = this.$element.val();
      if (raw === '') {
        return {
          [this.name]: null
        };
      }
      const value = parseFloat(raw);
      const data = {};
      data[this.name] = value;
      return data;
    }
    parseItemForSearch(item) {
      return parseFloat(item);
    }
  }
  var _default = _exports.default = EnumFloatFieldView;
});

define("views/fields/enum-column", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EnumColumnFieldView extends _enum.default {
    searchTypeList = ['anyOf', 'noneOf'];
    fetchSearch() {
      const type = this.fetchSearchType();
      let list = this.$element.val().split(':,:');
      if (list.length === 1 && list[0] === '') {
        list = [];
      }
      list.forEach((item, i) => {
        list[i] = this.parseItemForSearch(item);
      });
      if (type === 'anyOf') {
        if (list.length === 0) {
          return {
            data: {
              type: 'anyOf',
              valueList: list
            }
          };
        }
        return {
          type: 'columnIn',
          value: list,
          data: {
            type: 'anyOf',
            valueList: list
          }
        };
      } else if (type === 'noneOf') {
        if (list.length === 0) {
          return {
            data: {
              type: 'noneOf',
              valueList: list
            }
          };
        }
        return {
          type: 'or',
          value: [{
            type: 'columnIsNull',
            attribute: this.name
          }, {
            type: 'columnNotIn',
            value: list,
            attribute: this.name
          }],
          data: {
            type: 'noneOf',
            valueList: list
          }
        };
      }
      return null;
    }
  }
  var _default = _exports.default = EnumColumnFieldView;
});

define("views/fields/entity-type-list", ["exports", "views/fields/multi-enum"], function (_exports, _multiEnum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _multiEnum = _interopRequireDefault(_multiEnum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EntityTypeListFieldView extends _multiEnum.default {
    checkAvailability(entityType) {
      const defs = this.scopesMetadataDefs[entityType] || {};
      if (defs.entity && defs.object) {
        return true;
      }
    }
    setupOptions() {
      const scopes = this.scopesMetadataDefs = this.getMetadata().get('scopes');
      this.params.options = Object.keys(scopes).filter(scope => {
        if (this.checkAvailability(scope)) {
          return true;
        }
      }).sort((v1, v2) => {
        return this.translate(v1, 'scopeNames').localeCompare(this.translate(v2, 'scopeNames'));
      });
    }
    setup() {
      if (!this.params.translation) {
        this.params.translation = 'Global.scopeNames';
      }
      this.setupOptions();
      super.setup();
    }
  }
  var _default = _exports.default = EntityTypeListFieldView;
});

define("views/fields/email-address", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailAddressFieldView extends _varchar.default {
    editTemplate = 'fields/email-address/edit';
    validations = ['required', 'emailAddress'];
    emailAddressRe = new RegExp(/^[-!#$%&'*+/=?^_`{|}~A-Za-z0-9]+(?:\.[-!#$%&'*+/=?^_`{|}~A-Za-z0-9]+)*/.source + /@([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?\.)+[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9]/.source);

    // noinspection JSUnusedGlobalSymbols
    validateEmailAddress() {
      const value = this.model.get(this.name);
      if (!value) {
        return false;
      }
      if (value !== '' && !this.emailAddressRe.test(value)) {
        const msg = this.translate('fieldShouldBeEmail', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg);
        return true;
      }
      return false;
    }
  }
  var _default = _exports.default = EmailAddressFieldView;
});

define("views/fields/duration", ["exports", "views/fields/enum", "ui/select", "moment"], function (_exports, _enum, _select, _moment) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  _select = _interopRequireDefault(_select);
  _moment = _interopRequireDefault(_moment);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class DurationFieldView extends _enum.default {
    type = 'duration';
    listTemplate = 'fields/base/detail';
    detailTemplate = 'fields/varchar/detail';
    editTemplate = 'fields/duration/edit';

    /**
     * @private
     * @type {boolean}
     */
    _justFocused = false;
    data() {
      const valueIsSet = this.model.has(this.startField) && this.model.has(this.endField);
      return {
        valueIsSet: valueIsSet,
        durationOptions: this.durationOptions,
        ...super.data()
      };
    }

    /**
     * @private
     */
    calculateSeconds() {
      this.seconds = 0;
      const start = this.model.get(this.startField);
      let end = this.model.get(this.endField);
      if (this.isEditMode() || this.isDetailMode()) {
        if (this.model.isNew()) {
          this.seconds = this.model.getFieldParam(this.name, 'default') || 0;
        }
      }
      if (this.model.attributes.isAllDay && this.hasAllDay) {
        const startDate = this.model.attributes[this.startDateField];
        const endDate = this.model.attributes[this.endDateField];
        if (startDate && endDate) {
          this.seconds = (0, _moment.default)(endDate).add(1, 'days').unix() - (0, _moment.default)(startDate).unix();
          return;
        }
      }
      if (start && end) {
        this.seconds = (0, _moment.default)(this.model.get(this.endField)).unix() - (0, _moment.default)(this.model.get(this.startField)).unix();
        return;
      }
      if (start) {
        end = this._getDateEnd();
        this.model.set(this.endField, end, {
          silent: true
        });
      }
    }
    init() {
      super.init();
      this.listenTo(this, 'render', () => {
        this.calculateSeconds();
        this.durationOptions = '';
        this.getOptions().forEach(duration => {
          const option = document.createElement('option');
          option.value = duration.toString();
          option.text = this.stringifyDuration(duration);
          if (duration === this.seconds) {
            option.setAttribute('selected', 'selected');
          }
          this.durationOptions += option.outerHTML;
        });
        this.stringValue = this.stringifyDuration(this.seconds);
      });
    }

    /**
     * @return {Number[]}
     */
    getOptions() {
      const options = Espo.Utils.clone(this.model.getFieldParam(this.name, 'options') ?? []);
      if (!this.model.attributes.isAllDay && options.indexOf(this.seconds) === -1) {
        options.push(this.seconds);
      }
      options.sort((a, b) => a - b);
      return options;
    }
    setup() {
      this.startField = this.model.getFieldParam(this.name, 'start');
      this.endField = this.model.getFieldParam(this.name, 'end');
      this.startDateField = this.startField + 'Date';
      this.endDateField = this.endField + 'Date';
      this.hasAllDay = this.model.getFieldType(this.startField) === 'datetimeOptional';
      if (!this.startField || !this.endField) {
        throw new Error(`Bad definition for field '${this.name}'.`);
      }
      this.calculateSeconds();
      this.blockDateEndChangeListener = false;
      this.listenTo(this.model, `change:${this.endField}`, (m, v, o) => {
        if (this.blockDateEndChangeListener) {
          return;
        }
        const start = this.model.get(this.startField);
        const end = this.model.get(this.endField);
        if (!end || !start) {
          return;
        }
        this.seconds = _moment.default.utc(end).unix() - _moment.default.utc(start).unix();
        if (o.updatedByDuration) {
          return;
        }
        this.updateDuration();
      });
      this.listenTo(this.model, `change:${this.startField}`, (m, v, o) => {
        if (o.ui) {
          const isAllDay = this.model.attributes[this.startDateField];
          if (isAllDay && this.hasAllDay) {
            const remainder = this.seconds % (3600 * 24);
            if (remainder !== 0) {
              this.seconds = this.seconds - remainder + 3600 * 24;
            }
          }
          this.blockDateEndChangeListener = true;
          setTimeout(() => this.blockDateEndChangeListener = false, 100);
          this.updateDateEnd(this.startField);
          setTimeout(() => this.updateDuration(), 110);
          return;
        }
        if (!this.isEditMode() && o.xhr) {
          return;
        }
        this.updateDateEnd();
      });
    }
    getValueForDisplay() {
      return this.stringValue;
    }

    /**
     * @private
     * @param {number} secondsTotal
     * @return {string}
     */
    stringifyDuration(secondsTotal) {
      if (!secondsTotal) {
        return '0';
      }
      if (secondsTotal < 60) {
        return '0';
      }
      let d = secondsTotal;
      const days = Math.floor(d / 86400);
      d = d % 86400;
      const hours = Math.floor(d / 3600);
      d = d % 3600;
      const minutes = Math.floor(d / 60);
      const parts = [];
      if (days) {
        parts.push(days + '' + this.getLanguage().translate('d', 'durationUnits'));
      }
      if (hours) {
        parts.push(hours + '' + this.getLanguage().translate('h', 'durationUnits'));
      }
      if (minutes) {
        parts.push(minutes + '' + this.getLanguage().translate('m', 'durationUnits'));
      }
      return parts.join(' ');
    }
    focusOnInlineEdit() {
      _select.default.focus(this.$duration);
    }
    afterRender() {
      const parentView = this.getParentView();
      if (parentView && 'getView' in parentView) {
        this.endFieldView = parentView.getView(this.endField);
      }
      if (this.isEditMode()) {
        this.$duration = this.$el.find('.main-element');
        this.$duration.on('change', () => {
          this.seconds = parseInt(this.$duration.val());
          this.updateDateEnd();
        });
        const start = this.model.get(this.startField);
        const end = this.model.get(this.endField);
        const seconds = this.$duration.val();
        if (!end && start && seconds) {
          if (this.endFieldView) {
            if (this.endFieldView.isRendered()) {
              this.updateDateEnd();
            } else {
              this.endFieldView.once('after:render', () => {
                this.updateDateEnd();
              });
            }
          }
        }
        _select.default.init(this.$duration, {
          sortBy: '$score',
          sortDirection: 'desc',
          /**
           * @param {string} search
           * @param {{value: string}} item
           * @return {number}
           */
          score: (search, item) => {
            const num = parseInt(item.value);
            const searchNum = parseInt(search);
            if (isNaN(searchNum)) {
              return 0;
            }
            const numOpposite = Number.MAX_SAFE_INTEGER - num;
            if (searchNum === 0 && num === 0) {
              return numOpposite;
            }
            if (searchNum * 60 === num) {
              return numOpposite;
            }
            if (searchNum * 60 * 60 === num) {
              return numOpposite;
            }
            if (searchNum * 60 * 60 * 24 === num) {
              return numOpposite;
            }
            return 0;
          },
          load: (item, callback) => {
            const num = parseInt(item);
            if (isNaN(num) || num <= 0) {
              return;
            }
            if (num > 59) {
              return;
            }
            const list = [];
            const mSeconds = num * 60;
            list.push({
              value: mSeconds.toString(),
              text: this.stringifyDuration(mSeconds)
            });
            if (num <= 9) {
              const hSeconds = num * 3600;
              list.push({
                value: hSeconds.toString(),
                text: this.stringifyDuration(hSeconds)
              });
            }
            callback(list);
          },
          onFocus: () => {
            this._justFocused = true;
            setTimeout(() => this._justFocused = false, 150);
          }
        });
      }
    }

    /**
     * @private
     * @return {string}
     */
    _getDateEndDate() {
      const seconds = this.seconds;
      const start = this.model.attributes[this.startDateField];
      if (!start) {
        return undefined;
      }
      if (!seconds) {
        return start;
      }
      const endUnix = _moment.default.utc(start).unix() + seconds;
      return _moment.default.unix(endUnix).utc().add(-1, 'day').format(this.getDateTime().internalDateFormat);
    }

    /**
     * @private
     * @return {string}
     */
    _getDateEnd() {
      const seconds = this.seconds;
      const start = this.model.get(this.startField);
      if (!start) {
        return undefined;
      }
      let endUnix;
      let end;
      if (seconds) {
        endUnix = _moment.default.utc(start).unix() + seconds;
        end = _moment.default.unix(endUnix).utc().format(this.getDateTime().internalDateTimeFormat);
      } else {
        end = start;
      }
      return end;
    }

    /**
     * @private
     * @param {string} [fromField]
     */
    updateDateEnd(fromField) {
      if (this.model.attributes.isAllDay && this.hasAllDay) {
        const end = this._getDateEndDate();
        setTimeout(() => {
          this.model.set(this.endDateField, end, {
            updatedByDuration: true,
            fromField: fromField
          });
        }, 1);
        return;
      }
      const end = this._getDateEnd();

      // Smaller timeouts produce a js error in timepicker.
      setTimeout(() => {
        this.model.set(this.endField, end, {
          updatedByDuration: true,
          fromField: fromField
        });
        if (this.hasAllDay) {
          this.model.set(this.endDateField, null, {
            fromField: fromField
          });
        }
      }, 100);
    }

    /**
     * @private
     */
    updateDuration() {
      const seconds = this.seconds;
      if (this.isEditMode() && this.$duration && this.$duration.length && !this._justFocused) {
        const options = this.getOptions().map(value => {
          return {
            value: value.toString(),
            text: this.stringifyDuration(value)
          };
        });
        _select.default.setValue(this.$duration, '');
        _select.default.setOptions(this.$duration, options);
        _select.default.setValue(this.$duration, seconds.toString());
        return;
      }
      this.reRender();
    }
    fetch() {
      // noinspection JSValidateTypes
      return {};
    }
  }
  var _default = _exports.default = DurationFieldView;
});

define("views/fields/datetime-optional", ["exports", "views/fields/datetime", "moment"], function (_exports, _datetime, _moment) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _datetime = _interopRequireDefault(_datetime);
  _moment = _interopRequireDefault(_moment);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/datetime-optional */

  /**
   * A date-time or date.
   *
   * @extends DatetimeFieldView<module:views/fields/datetime-optional~params>
   */
  class DatetimeOptionalFieldView extends _datetime.default {
    /**
     * @typedef {Object} module:views/fields/datetime-optional~options
     * @property {
     *     module:views/fields/varchar~params &
     *     module:views/fields/base~params &
     *     Record
     * } [params] Parameters.
     */

    /**
     * @typedef {Object} module:views/fields/datetime-optional~params
     * @property {boolean} [required] Required.
     * @property {boolean} [useNumericFormat] Use numeric format.
     * @property {boolean} [hasSeconds] Display seconds.
     * @property {number} [minuteStep] A minute step.
     * @property {string} [after] Validate to be after another date field.
     * @property {string} [before] Validate to be before another date field.
     */

    /**
     * @param {
     *     module:views/fields/datetime-optional~options &
     *     module:views/fields/base~options
     * } options Options.
     */
    constructor(options) {
      super(options);
    }
    type = 'datetimeOptional';
    setup() {
      super.setup();
      this.noneOption = this.translate('None');
      this.nameDate = this.name + 'Date';
    }
    isDate() {
      const dateValue = this.model.get(this.nameDate);
      if (dateValue && dateValue !== '') {
        return true;
      }
      return false;
    }
    data() {
      const data = super.data();
      if (this.isDate()) {
        const dateValue = this.model.get(this.nameDate);
        data.date = this.getDateTime().toDisplayDate(dateValue);
        data.time = this.noneOption;
      }
      return data;
    }
    getDateStringValue() {
      if (this.isDate()) {
        const dateValue = this.model.get(this.nameDate);
        return this.stringifyDateValue(dateValue);
      }
      return super.getDateStringValue();
    }
    setDefaultTime() {
      this.$time.val(this.noneOption);
    }
    initTimepicker() {
      const $time = this.$time;
      const modalBodyElement = this.element.closest('.modal-body');
      const o = {
        step: this.params.minuteStep || 30,
        scrollDefaultNow: true,
        timeFormat: this.timeFormatMap[this.getDateTime().timeFormat],
        noneOption: [{
          label: this.noneOption,
          value: this.noneOption
        }],
        appendTo: modalBodyElement ? $(modalBodyElement) : 'body'
      };
      if (this.emptyTimeInInlineEditDisabled && this.isInlineEditMode() || this.noneOptionIsHidden) {
        delete o.noneOption;
      }
      $time.timepicker(o);
      $time.parent().find('button.time-picker-btn').on('click', () => {
        $time.timepicker('show');
      });
    }
    fetch() {
      const data = {};
      const date = this.$date.val();
      const time = this.$time.val();
      let value = null;
      if (time !== this.noneOption && time !== '') {
        if (date !== '' && time !== '') {
          value = this.parse(date + ' ' + time);
        }
        data[this.name] = value;
        data[this.nameDate] = null;
        return data;
      }
      if (date !== '') {
        data[this.nameDate] = this.getDateTime().fromDisplayDate(date);
        let dateTimeValue = data[this.nameDate] + ' 00:00:00';
        dateTimeValue = _moment.default.tz(dateTimeValue, this.getConfig().get('timeZone') || 'UTC').add(this.isEnd ? 1 : 0, 'days').utc().format(this.getDateTime().internalDateTimeFullFormat);
        data[this.name] = dateTimeValue;
        return data;
      }
      data[this.nameDate] = null;
      data[this.name] = null;
      return data;
    }
    validateAfter() {
      const field = this.params.after;
      if (!field) {
        return;
      }
      const fieldDate = field + 'Date';
      const value = this.model.get(this.name) || this.model.get(this.nameDate);
      const otherValue = this.model.get(field) || this.model.get(fieldDate);
      if (!(value && otherValue)) {
        return;
      }
      const isNotValid = this.validateAfterAllowSameDay && this.model.get(this.nameDate) ? (0, _moment.default)(value).unix() < (0, _moment.default)(otherValue).unix() : (0, _moment.default)(value).unix() <= (0, _moment.default)(otherValue).unix();
      if (isNotValid) {
        const msg = this.translate('fieldShouldAfter', 'messages').replace('{field}', this.getLabelText()).replace('{otherField}', this.translate(field, 'fields', this.entityType));
        this.showValidationMessage(msg);
        return true;
      }
    }
    validateBefore() {
      const field = this.params.before;
      if (!field) {
        return;
      }
      const fieldDate = field + 'Date';
      const value = this.model.get(this.name) || this.model.get(this.nameDate);
      const otherValue = this.model.get(field) || this.model.get(fieldDate);
      if (!(value && otherValue)) {
        return;
      }
      if ((0, _moment.default)(value).unix() >= (0, _moment.default)(otherValue).unix()) {
        const msg = this.translate('fieldShouldBefore', 'messages').replace('{field}', this.getLabelText()).replace('{otherField}', this.translate(field, 'fields', this.entityType));
        this.showValidationMessage(msg);
        return true;
      }
    }
    validateRequired() {
      if (!this.isRequired()) {
        return;
      }
      if (this.model.get(this.name) === null && this.model.get(this.nameDate) === null) {
        const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
        this.showValidationMessage(msg);
        return true;
      }
    }

    /**
     * @protected
     * @return {string|undefined}
     */
    getStartDateForDatePicker() {
      if (!this.isEditMode() || !this.params.after) {
        return undefined;
      }

      /** @type {string} */
      const date = this.model.attributes[this.params.after + 'Date'];
      if (date) {
        return this.getDateTime().toDisplayDate(date);
      }
      return super.getStartDateForDatePicker();
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = DatetimeOptionalFieldView;
});

define("views/fields/currency-list", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class CurrencyListFieldView extends _enum.default {
    setupOptions() {
      this.params.options = [];
      (this.getConfig().get('currencyList') || []).forEach(item => {
        this.params.options.push(item);
      });
    }
  }
  var _default = _exports.default = CurrencyListFieldView;
});

define("views/fields/complex-expression", ["exports", "views/fields/text"], function (_exports, _text) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _text = _interopRequireDefault(_text);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * @type {{
   *     edit: import('ace-builds').edit,
   *     require: import('ace-builds').require,
   * }}
   */
  let ace;
  class ComplexExpressionFieldView extends _text.default {
    detailTemplate = 'fields/formula/detail';
    editTemplate = 'fields/formula/edit';
    height = 46;
    maxLineDetailCount = 80;
    maxLineEditCount = 200;
    smallFont = false;
    events = {
      /** @this ComplexExpressionFieldView */
      'click [data-action="addAttribute"]': function () {
        this.addAttribute();
      },
      /** @this ComplexExpressionFieldView */
      'click [data-action="addFunction"]': function () {
        this.addFunction();
      }
    };
    setup() {
      super.setup();
      this.height = this.options.height || this.params.height || this.height;
      this.smallFont = this.options.smallFont || this.params.smallFont || this.smallFont;
      this.maxLineDetailCount = this.options.maxLineDetailCount || this.params.maxLineDetailCount || this.maxLineDetailCount;
      this.maxLineEditCount = this.options.maxLineEditCount || this.params.maxLineEditCount || this.maxLineEditCount;
      this.targetEntityType = this.options.targetEntityType || this.params.targetEntityType || this.targetEntityType;
      this.containerId = 'editor-' + Math.floor(Math.random() * 10000 + 1).toString();
      if (this.mode === this.MODE_EDIT || this.mode === this.MODE_DETAIL) {
        this.wait(this.requireAce());
      }
      this.on('remove', () => {
        if (this.editor) {
          this.editor.destroy();
        }
      });
    }
    requireAce() {
      return Espo.loader.requirePromise('lib!ace').then(lib => {
        ace = lib;
        const list = [Espo.loader.requirePromise('lib!ace-ext-language_tools')];
        if (this.getThemeManager().getParam('isDark')) {
          list.push(Espo.loader.requirePromise('lib!ace-theme-tomorrow_night'));
        }
        return Promise.all(list);
      });
    }
    data() {
      const data = super.data();
      data.containerId = this.containerId;
      data.targetEntityType = this.targetEntityType;
      data.hasInsert = true;
      return data;
    }
    afterRender() {
      super.afterRender();
      this.$editor = this.$el.find('#' + this.containerId);
      if (this.$editor.length && (this.mode === this.MODE_EDIT || this.mode === this.MODE_DETAIL || this.mode === this.MODE_LIST)) {
        const fontSize = this.smallFont ? 'var(--font-size-small)' : 'var(--font-size-base)';
        const lineHeight = this.smallFont ? 'var(--line-height-small)' : 'var(--line-height-computed)';
        this.$editor.css('fontSize', fontSize);
        if (this.mode === this.MODE_EDIT) {
          this.$editor.css('minHeight', this.height + 'px');
        }
        const editor = this.editor = ace.edit(this.containerId);
        editor.setOptions({
          fontFamily: 'var(--font-family-monospace)'
        });
        editor.setFontSize(fontSize);
        editor.container.style.lineHeight = lineHeight;
        editor.renderer.updateFontSize();
        editor.setOptions({
          maxLines: this.mode === this.MODE_EDIT ? this.maxLineEditCount : this.maxLineDetailCount
        });
        if (this.getThemeManager().getParam('isDark')) {
          editor.setOptions({
            theme: 'ace/theme/tomorrow_night'
          });
        }
        if (this.isEditMode()) {
          // noinspection JSCheckFunctionSignatures
          editor.getSession().on('change', () => {
            this.trigger('change', {
              ui: true
            });
          });
          editor.getSession().setUseWrapMode(true);
        }
        if (this.isReadMode()) {
          editor.setReadOnly(true);
          // noinspection JSUnresolvedReference
          editor.renderer.$cursorLayer.element.style.display = 'none';
          editor.renderer.setShowGutter(false);
        }
        editor.setShowPrintMargin(false);
        editor.getSession().setUseWorker(false);
        editor.commands.removeCommand('find');
        editor.setHighlightActiveLine(false);

        //let JavaScriptMode = ace.require('ace/mode/javascript').Mode;
        //editor.session.setMode(new JavaScriptMode());

        if (!this.isReadMode()) {
          this.initAutocomplete();
        }
      }
    }
    fetch() {
      const data = {};
      data[this.name] = this.editor.getValue();
      return data;
    }
    getFunctionDataList() {
      return this.getMetadata().get(['app', 'complexExpression', 'functionList']) || [];
    }
    initAutocomplete() {
      const functionItemList = this.getFunctionDataList().filter(item => item.insertText);
      const attributeList = this.getFormulaAttributeList();
      ace.require('ace/ext/language_tools');
      this.editor.setOptions({
        enableBasicAutocompletion: true,
        enableLiveAutocompletion: true
      });

      // noinspection JSUnusedGlobalSymbols
      const completer = {
        identifierRegexps: [/[\\a-zA-Z0-9{}\[\].$'"]/],
        getCompletions: function (editor, session, pos, prefix, callback) {
          const matchedFunctionItemList = functionItemList.filter(originalItem => {
            const text = originalItem.name.toLowerCase();
            if (text.indexOf(prefix.toLowerCase()) === 0) {
              return true;
            }
            return false;
          });
          let itemList = matchedFunctionItemList.map(item => {
            return {
              caption: item.name + '()',
              value: item.insertText,
              meta: item.returnType || null
            };
          });
          const matchedAttributeList = attributeList.filter(item => {
            if (item.indexOf(prefix) === 0) {
              return true;
            }
            return false;
          });
          const itemAttributeList = matchedAttributeList.map(item => {
            return {
              name: item,
              value: item,
              meta: 'attribute'
            };
          });
          itemList = itemList.concat(itemAttributeList);
          callback(null, itemList);
        }
      };
      this.editor.completers = [completer];
    }
    getFormulaAttributeList() {
      if (!this.targetEntityType) {
        return [];
      }
      const attributeList = this.getFieldManager().getEntityTypeAttributeList(this.targetEntityType, {
        ignoreTypeList: ['map', 'linkMultiple', 'attachmentMultiple'],
        onlyAvailable: true
      }).sort();
      attributeList.unshift('id');

      // @todo Skip not storable attributes.

      const links = this.getMetadata().get(['entityDefs', this.targetEntityType, 'links']) || {};
      const linkList = [];
      Object.keys(links).forEach(link => {
        const type = links[link].type;
        if (!type) {
          return;
        }
        if (['hasMany', 'hasOne', 'belongsTo'].includes(type)) {
          linkList.push(link);
        }
      });
      linkList.sort();
      linkList.forEach(link => {
        /** @type {Record} */
        const defs = links[link];
        const scope = defs.entity;
        if (!scope) {
          return;
        }
        if (defs.disabled || defs.utility) {
          return;
        }
        attributeList.push(`${link}.id`);
        const linkAttributeList = this.getFieldManager().getEntityTypeAttributeList(scope, {
          ignoreTypeList: ['map', 'linkMultiple', 'attachmentMultiple'],
          onlyAvailable: true
        }).sort();
        linkAttributeList.forEach(item => attributeList.push(link + '.' + item));
      });
      return attributeList;
    }
    addAttribute() {
      this.createView('dialog', 'views/admin/formula/modals/add-attribute', {
        scope: this.targetEntityType,
        attributeList: this.getFormulaAttributeList()
      }, view => {
        view.render();
        this.listenToOnce(view, 'add', attribute => {
          this.editor.insert(attribute);
          this.clearView('dialog');
        });
      });
    }
    addFunction() {
      this.createView('dialog', 'views/admin/complex-expression/modals/add-function', {
        scope: this.targetEntityType,
        functionDataList: this.getFunctionDataList()
      }, view => {
        view.render();
        this.listenToOnce(view, 'add', string => {
          this.editor.insert(string);
          this.clearView('dialog');
        });
      });
    }
  }
  var _default = _exports.default = ComplexExpressionFieldView;
});

define("views/fields/complex-created", ["exports", "views/fields/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ComplexCreatedFieldView extends _base.default {
    // language=Handlebars
    detailTemplateContent = `{{#if hasAt}}<span data-name="{{baseName}}At" class="field">{{{atField}}}</span>{{/if}}
        {{#if hasBoth}}<span class="text-muted middle-dot"></span>{{/if}}
        {{#if hasBy}}<span data-name="{{baseName}}By" class="field">{{{byField}}}</span>{{/if}}`;
    baseName = 'created';
    getAttributeList() {
      return [this.fieldAt, this.fieldBy];
    }
    init() {
      this.baseName = this.options.baseName || this.baseName;
      this.fieldAt = this.baseName + 'At';
      this.fieldBy = this.baseName + 'By';
      super.init();
    }
    setup() {
      super.setup();
      this.createField('at');
      this.createField('by');
    }

    // noinspection JSCheckFunctionSignatures
    data() {
      const hasBy = this.model.has(this.fieldBy + 'Id');
      const hasAt = this.model.has(this.fieldAt);
      return {
        baseName: this.baseName,
        hasBy: hasBy,
        hasAt: hasAt,
        hasBoth: hasAt && hasBy,
        ...super.data()
      };
    }
    createField(part) {
      const field = this.baseName + Espo.Utils.upperCaseFirst(part);
      const type = this.model.getFieldType(field) || 'base';
      const viewName = this.model.getFieldParam(field, 'view') || this.getFieldManager().getViewName(type);
      this.createView(part + 'Field', viewName, {
        name: field,
        model: this.model,
        mode: this.MODE_DETAIL,
        readOnly: true,
        readOnlyLocked: true,
        selector: '[data-name="' + field + '"]'
      });
    }
    fetch() {
      return {};
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = ComplexCreatedFieldView;
});

define("views/fields/collaborators", ["exports", "views/fields/link-multiple"], function (_exports, _linkMultiple) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkMultiple = _interopRequireDefault(_linkMultiple);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class CollaboratorsFieldView extends _linkMultiple.default {
    linkClass = 'text-default';
    init() {
      this.assignmentPermission = this.getAcl().getPermissionLevel('assignmentPermission');
      if (this.assignmentPermission === 'no') {
        this.readOnly = true;
      }
      super.init();
    }
    getSelectBoolFilterList() {
      if (this.assignmentPermission === 'team') {
        return ['onlyMyTeam'];
      }
    }
    getSelectPrimaryFilterName() {
      return 'active';
    }
    getDetailLinkHtml(id, name) {
      const html = super.getDetailLinkHtml(id);
      const avatarHtml = this.isDetailMode() ? this.getHelper().getAvatarHtml(id, 'small', 18, 'avatar-link') : '';
      if (!avatarHtml) {
        return html;
      }
      return `${avatarHtml}${html}`;
    }

    /** @inheritDoc */
    getOnEmptyAutocomplete() {
      if (this.params.autocompleteOnEmpty) {
        return undefined;
      }
      if (this.ids && this.ids.includes(this.getUser().id)) {
        return Promise.resolve([]);
      }
      return Promise.resolve([{
        id: this.getUser().id,
        name: this.getUser().attributes.name
      }]);
    }

    /**
     * @inheritDoc
     */
    prepareEditItemElement(id, name) {
      const itemElement = super.prepareEditItemElement(id, name);
      const avatarHtml = this.getHelper().getAvatarHtml(id, 'small', 18, 'avatar-link');
      if (avatarHtml) {
        const img = new DOMParser().parseFromString(avatarHtml, 'text/html').body.childNodes[0];
        itemElement.prepend(img);
      }
      return itemElement;
    }
  }
  _exports.default = CollaboratorsFieldView;
});

define("views/fields/barcode", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  let JsBarcode;
  let QRCode;
  class BarcodeFieldView extends _varchar.default {
    type = 'barcode';
    listTemplate = 'fields/barcode/detail';
    detailTemplate = 'fields/barcode/detail';
    setup() {
      this.validations.push('valid');
      let maxLength = 255;

      // noinspection SpellCheckingInspection
      switch (this.params.codeType) {
        case 'EAN2':
          maxLength = 2;
          break;
        case 'EAN5':
          maxLength = 5;
          break;
        case 'EAN8':
          maxLength = 8;
          break;
        case 'EAN13':
          maxLength = 13;
          break;
        case 'UPC':
          maxLength = 12;
          break;
        case 'UPCE':
          maxLength = 11;
          break;
        case 'ITF14':
          maxLength = 14;
          break;
        case 'pharmacode':
          maxLength = 6;
          break;
      }
      this.params.maxLength = maxLength;

      // noinspection SpellCheckingInspection
      if (this.params.codeType !== 'QRcode') {
        this.isSvg = true;
        this.wait(Espo.loader.requirePromise('lib!jsbarcode').then(lib => JsBarcode = lib));
      } else {
        this.wait(Espo.loader.requirePromise('lib!qrcodejs').then(lib => QRCode = lib));
      }
      super.setup();
      $(window).on('resize.' + this.cid, () => {
        if (!this.isRendered()) {
          return;
        }
        this.controlWidth();
      });
      this.listenTo(this.recordHelper, 'panel-show', () => this.controlWidth());
    }
    data() {
      const data = super.data();
      data.isSvg = this.isSvg;

      // noinspection JSValidateTypes
      return data;
    }
    onRemove() {
      $(window).off('resize.' + this.cid);
    }
    afterRender() {
      super.afterRender();
      if (this.isListMode() || this.isDetailMode()) {
        const value = this.model.get(this.name);
        if (value) {
          // noinspection SpellCheckingInspection
          if (this.params.codeType === 'QRcode') {
            this.initQrcode(value);
          } else {
            const $barcode = $(this.getSelector() + ' .barcode');
            if ($barcode.length) {
              this.initBarcode(value);
            } else {
              // SVG may be not available yet (in webkit).
              setTimeout(() => {
                this.initBarcode(value);
                this.controlWidth();
              }, 100);
            }
          }
        }
        this.controlWidth();
      }
    }
    initQrcode(value) {
      let size = 128;
      if (value.length > 192) {
        size *= 2;
      }
      if (this.isListMode()) {
        size = 64;
      }
      const containerWidth = this.$el.width();
      if (containerWidth < size && containerWidth) {
        size = containerWidth;
      }
      const $barcode = this.$el.find('.barcode');
      const init = level => {
        const options = {
          text: value,
          width: size,
          height: size,
          colorDark: '#000000',
          colorLight: '#ffffff',
          correctLevel: level || QRCode.CorrectLevel.H
        };
        new QRCode($barcode.get(0), options);
      };
      try {
        init();
      } catch (e) {
        try {
          $barcode.empty();
          init(QRCode.CorrectLevel.L);
        } catch (e) {
          console.error(this.name + ': ' + e.message);
        }
      }
    }
    initBarcode(value) {
      const factor = this.getThemeManager().getFontSizeFactor();
      try {
        JsBarcode(this.getSelector() + ' .barcode', value, {
          format: this.params.codeType,
          height: 50 * factor,
          fontSize: 14 * factor,
          margin: 0,
          lastChar: this.params.lastChar
        });
      } catch (e) {
        console.error(this.name, e);
      }
    }
    controlWidth() {
      this.$el.find('.barcode').css('max-width', this.$el.width() + 'px');
    }

    // noinspection JSUnusedGlobalSymbols
    validateValid() {
      if (this.params.codeType === 'QRcode') {
        return;
      }
      const value = this.model.get(this.name);
      if (!value) {
        return;
      }
      let isValid;
      try {
        JsBarcode({}, value, {
          format: this.params.codeType,
          lastChar: this.params.lastChar,
          valid: valid => isValid = valid
        });
      } catch (e) {
        return true;
      }
      if (isValid) {
        return;
      }
      const msg = this.translate('barcodeInvalid', 'messages').replace('{field}', this.getLabelText()).replace('{type}', this.params.codeType);
      this.showValidationMessage(msg);
      return true;
    }
  }
  var _default = _exports.default = BarcodeFieldView;
});

define("views/fields/autoincrement", ["exports", "views/fields/int"], function (_exports, _int) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _int = _interopRequireDefault(_int);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class AutoincrementFieldView extends _int.default {
    type = 'autoincrement';
    validations = [];
    inlineEditDisabled = true;
    readOnly = true;
    disableFormatting = true;
    parse(value) {
      value = value !== '' ? value : null;
      if (value !== null) {
        value = value.indexOf('.') !== -1 || value.indexOf(',') !== -1 ? NaN : parseInt(value);
      }
      return value;
    }
    fetch() {
      return {};
    }
  }
  var _default = _exports.default = AutoincrementFieldView;
});

define("views/fields/assigned-users", ["exports", "views/fields/link-multiple"], function (_exports, _linkMultiple) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkMultiple = _interopRequireDefault(_linkMultiple);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class AssignedUsersFieldView extends _linkMultiple.default {
    linkClass = 'text-default';
    init() {
      this.assignmentPermission = this.getAcl().getPermissionLevel('assignmentPermission');
      if (this.assignmentPermission === 'no') {
        this.readOnly = true;
      }
      super.init();
    }
    getSelectBoolFilterList() {
      if (this.assignmentPermission === 'team') {
        return ['onlyMyTeam'];
      }
    }
    getSelectPrimaryFilterName() {
      return 'active';
    }
    getDetailLinkHtml(id, name) {
      const html = super.getDetailLinkHtml(id);
      const avatarHtml = this.isDetailMode() || this.isListMode() ? this.getHelper().getAvatarHtml(id, 'small', 18, 'avatar-link') : '';
      if (!avatarHtml) {
        return html;
      }
      return avatarHtml + html;
    }

    /** @inheritDoc */
    getOnEmptyAutocomplete() {
      if (this.params.autocompleteOnEmpty) {
        return undefined;
      }
      if (this.ids && this.ids.includes(this.getUser().id)) {
        return Promise.resolve([]);
      }
      return Promise.resolve([{
        id: this.getUser().id,
        name: this.getUser().get('name')
      }]);
    }

    /**
     * @inheritDoc
     */
    prepareEditItemElement(id, name) {
      const itemElement = super.prepareEditItemElement(id, name);
      const avatarHtml = this.getHelper().getAvatarHtml(id, 'small', 18, 'avatar-link');
      if (avatarHtml) {
        const img = new DOMParser().parseFromString(avatarHtml, 'text/html').body.childNodes[0];
        itemElement.prepend(img);
      }
      return itemElement;
    }
  }
  var _default = _exports.default = AssignedUsersFieldView;
});

define("views/fields/assigned-user", ["exports", "views/fields/user-with-avatar"], function (_exports, _userWithAvatar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _userWithAvatar = _interopRequireDefault(_userWithAvatar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class AssignedUserFieldView extends _userWithAvatar.default {
    init() {
      this.assignmentPermission = this.getAcl().getPermissionLevel('assignmentPermission');
      if (this.assignmentPermission === 'no') {
        this.setReadOnly(true);
      }
      super.init();
    }
    getSelectBoolFilterList() {
      if (this.assignmentPermission === 'team') {
        return ['onlyMyTeam'];
      }
    }
    getSelectPrimaryFilterName() {
      return 'active';
    }

    /** @inheritDoc */
    getOnEmptyAutocomplete() {
      if (this.params.autocompleteOnEmpty) {
        return undefined;
      }
      return Promise.resolve([{
        id: this.getUser().id,
        name: this.getUser().get('name')
      }]);
    }
  }
  var _default = _exports.default = AssignedUserFieldView;
});

define("views/fields/array-int", ["exports", "views/fields/array"], function (_exports, _array) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _array = _interopRequireDefault(_array);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ArrayIntFieldView extends _array.default {
    type = 'arrayInt';
    fetchFromDom() {
      let selected = [];
      this.$el.find('.list-group .list-group-item').each((i, el) => {
        let value = $(el).data('value');
        if (typeof value === 'string' || value instanceof String) {
          value = parseInt($(el).data('value'));
        }
        selected.push(value);
      });
      this.selected = selected;
    }
    addValue(value) {
      value = parseInt(value);
      if (isNaN(value)) {
        return;
      }
      super.addValue(value);
    }
    removeValue(value) {
      value = parseInt(value);
      if (isNaN(value)) {
        return;
      }
      const valueInternal = CSS.escape(value.toString());
      this.$list.children('[data-value="' + valueInternal + '"]').remove();
      const index = this.selected.indexOf(value);
      this.selected.splice(index, 1);
      this.trigger('change');
    }
  }
  var _default = _exports.default = ArrayIntFieldView;
});

define("views/fields/address", ["exports", "views/fields/base", "views/fields/varchar", "ui/autocomplete"], function (_exports, _base, _varchar, _autocomplete) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _varchar = _interopRequireDefault(_varchar);
  _autocomplete = _interopRequireDefault(_autocomplete);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/fields/address */

  /**
   * An address field.
   */
  class AddressFieldView extends _base.default {
    type = 'address';
    listTemplate = 'fields/address/detail';
    detailTemplate = 'fields/address/detail';
    editTemplate = 'fields/address/edit';
    // noinspection JSUnusedGlobalSymbols
    editTemplate1 = 'fields/address/edit-1';
    // noinspection JSUnusedGlobalSymbols
    editTemplate2 = 'fields/address/edit-2';
    // noinspection JSUnusedGlobalSymbols
    editTemplate3 = 'fields/address/edit-3';
    // noinspection JSUnusedGlobalSymbols
    editTemplate4 = 'fields/address/edit-4';
    searchTemplate = 'fields/address/search';
    listLinkTemplate = 'fields/address/list-link';
    postalCodeField;
    streetField;
    cityField;
    stateField;
    countryField;

    /**
     * @inheritDoc
     * @type {Array<(function (): boolean)|string>}
     */
    validations = ['required', 'pattern'];

    /** @inheritDoc */
    events = {
      /** @this AddressFieldView */
      'click [data-action="viewMap"]': function (e) {
        e.preventDefault();
        e.stopPropagation();
        this.viewMapAction();
      }
    };

    // noinspection JSCheckFunctionSignatures
    data() {
      const data = super.data();
      data.ucName = Espo.Utils.upperCaseFirst(this.name);
      this.addressPartList.forEach(item => {
        data[item + 'Value'] = this.model.get(this[item + 'Field']);
      });
      if (this.isReadMode()) {
        data.formattedAddress = this.getFormattedAddress();
        data.isNone = data.formattedAddress === null;
        if (data.formattedAddress === -1) {
          data.formattedAddress = null;
          data.isLoading = true;
        }
        if (this.params.viewMap && this.canBeDisplayedOnMap()) {
          data.viewMap = true;
          data.viewMapLink = '#AddressMap/view/' + this.model.entityType + '/' + this.model.id + '/' + this.name;
        }
      }
      if (this.isEditMode()) {
        data.stateMaxLength = this.stateMaxLength;
        data.streetMaxLength = this.streetMaxLength;
        data.postalCodeMaxLength = this.postalCodeMaxLength;
        data.cityMaxLength = this.cityMaxLength;
        data.countryMaxLength = this.countryMaxLength;
      }

      // noinspection JSValidateTypes
      return data;
    }
    setupSearch() {
      this.searchData.value = this.getSearchParamsData().value || this.searchParams.additionalValue;
    }
    canBeDisplayedOnMap() {
      return !!this.model.get(this.name + 'City') || !!this.model.get(this.name + 'PostalCode');
    }
    getFormattedAddress() {
      let isNotEmpty = false;
      let isSet = false;
      this.addressAttributeList.forEach(attribute => {
        isNotEmpty = isNotEmpty || this.model.get(attribute);
        isSet = isSet || this.model.has(attribute);
      });
      const isEmpty = !isNotEmpty;
      if (isEmpty) {
        if (this.mode === this.MODE_LIST) {
          return '';
        }
        if (!isSet) {
          return -1;
        }
        return null;
      }
      const methodName = 'getFormattedAddress' + this.getAddressFormat().toString();
      if (methodName in this) {
        return this[methodName]();
      }
    }

    // noinspection JSUnusedGlobalSymbols
    getFormattedAddress1() {
      const postalCodeValue = this.model.get(this.postalCodeField);
      const streetValue = this.model.get(this.streetField);
      const cityValue = this.model.get(this.cityField);
      const stateValue = this.model.get(this.stateField);
      const countryValue = this.model.get(this.countryField);
      let html = '';
      if (streetValue) {
        html += streetValue;
      }
      if (cityValue || stateValue || postalCodeValue) {
        if (html !== '') {
          html += '\n';
        }
        if (cityValue) {
          html += cityValue;
        }
        if (stateValue) {
          if (cityValue) {
            html += ', ';
          }
          html += stateValue;
        }
        if (postalCodeValue) {
          if (cityValue || stateValue) {
            html += ' ';
          }
          html += postalCodeValue;
        }
      }
      if (countryValue) {
        if (html !== '') {
          html += '\n';
        }
        html += countryValue;
      }
      return html;
    }

    // noinspection JSUnusedGlobalSymbols
    getFormattedAddress2() {
      const postalCodeValue = this.model.get(this.postalCodeField);
      const streetValue = this.model.get(this.streetField);
      const cityValue = this.model.get(this.cityField);
      const stateValue = this.model.get(this.stateField);
      const countryValue = this.model.get(this.countryField);
      let html = '';
      if (streetValue) {
        html += streetValue;
      }
      if (cityValue || postalCodeValue) {
        if (html !== '') {
          html += '\n';
        }
        if (postalCodeValue) {
          html += postalCodeValue;
          if (cityValue) {
            html += ' ';
          }
        }
        if (cityValue) {
          html += cityValue;
        }
      }
      if (stateValue || countryValue) {
        if (html !== '') {
          html += '\n';
        }
        if (stateValue) {
          html += stateValue;
          if (countryValue) {
            html += ' ';
          }
        }
        if (countryValue) {
          html += countryValue;
        }
      }
      return html;
    }

    // noinspection JSUnusedGlobalSymbols
    getFormattedAddress3() {
      const postalCodeValue = this.model.get(this.postalCodeField);
      const streetValue = this.model.get(this.streetField);
      const cityValue = this.model.get(this.cityField);
      const stateValue = this.model.get(this.stateField);
      const countryValue = this.model.get(this.countryField);
      let html = '';
      if (countryValue) {
        html += countryValue;
      }
      if (cityValue || stateValue || postalCodeValue) {
        if (html !== '') {
          html += '\n';
        }
        if (postalCodeValue) {
          html += postalCodeValue;
        }
        if (stateValue) {
          if (postalCodeValue) {
            html += ' ';
          }
          html += stateValue;
        }
        if (cityValue) {
          if (postalCodeValue || stateValue) {
            html += ' ';
          }
          html += cityValue;
        }
      }
      if (streetValue) {
        if (html !== '') {
          html += '\n';
        }
        html += streetValue;
      }
      return html;
    }

    // noinspection JSUnusedGlobalSymbols
    getFormattedAddress4() {
      const postalCodeValue = this.model.get(this.postalCodeField);
      const streetValue = this.model.get(this.streetField);
      const cityValue = this.model.get(this.cityField);
      const stateValue = this.model.get(this.stateField);
      const countryValue = this.model.get(this.countryField);
      let html = '';
      if (streetValue) {
        html += streetValue;
      }
      if (cityValue) {
        if (html !== '') {
          html += '\n';
        }
        html += cityValue;
      }
      if (countryValue || stateValue || postalCodeValue) {
        if (html !== '') {
          html += '\n';
        }
        if (countryValue) {
          html += countryValue;
        }
        if (stateValue) {
          if (countryValue) {
            html += ' - ';
          }
          html += stateValue;
        }
        if (postalCodeValue) {
          if (countryValue || stateValue) {
            html += ' ';
          }
          html += postalCodeValue;
        }
      }
      return html;
    }
    _getTemplateName() {
      if (this.mode === this.MODE_EDIT) {
        const prop = 'editTemplate' + this.getAddressFormat().toString();
        if (prop in this) {
          return this[prop];
        }
      }

      // @todo
      return super._getTemplateName();
    }
    getAddressFormat() {
      return this.getConfig().get('addressFormat') || 1;
    }
    afterRender() {
      if (this.mode === this.MODE_EDIT) {
        this.$street = this.$el.find(`[data-name="${this.streetField}"]`);
        this.$postalCode = this.$el.find(`[data-name="${this.postalCodeField}"]`);
        this.$state = this.$el.find(`[data-name="${this.stateField}"]`);
        this.$city = this.$el.find(`[data-name="${this.cityField}"]`);
        this.$country = this.$el.find(`[data-name="${this.countryField}"]`);
        this.$street.on('change', () => this.trigger('change'));
        this.$postalCode.on('change', () => this.trigger('change'));
        this.$state.on('change', () => this.trigger('change'));
        this.$city.on('change', () => this.trigger('change'));
        this.$country.on('change', () => this.trigger('change'));
        const countryList = this.getCountryList();
        const cityList = this.getConfig().get('addressCityList') || [];
        const stateList = this.getConfig().get('addressStateList') || [];
        if (countryList.length) {
          const autocomplete = new _autocomplete.default(this.$country.get(0), {
            name: this.name + 'Country',
            triggerSelectOnValidInput: true,
            autoSelectFirst: true,
            handleFocusMode: 1,
            focusOnSelect: true,
            lookup: countryList,
            lookupFunction: this.getCountryAutocompleteLookupFunction(countryList),
            onSelect: () => this.trigger('change')
          });
          this.once('render remove', () => autocomplete.dispose());
        }
        if (cityList.length) {
          const autocomplete = new _autocomplete.default(this.$city.get(0), {
            name: this.name + 'City',
            triggerSelectOnValidInput: true,
            autoSelectFirst: true,
            handleFocusMode: 1,
            focusOnSelect: true,
            lookup: cityList,
            onSelect: () => this.trigger('change')
          });
          this.once('render remove', () => autocomplete.dispose());
        }
        if (stateList.length) {
          const autocomplete = new _autocomplete.default(this.$state.get(0), {
            name: this.name + 'State',
            triggerSelectOnValidInput: true,
            autoSelectFirst: true,
            handleFocusMode: 1,
            focusOnSelect: true,
            lookup: stateList,
            onSelect: () => this.trigger('change')
          });
          this.once('render remove', () => autocomplete.dispose());
        }
        this.controlStreetTextareaHeight();
        this.$street.on('input', () => this.controlStreetTextareaHeight());
      }
    }
    controlStreetTextareaHeight(lastHeight) {
      const scrollHeight = this.$street.prop('scrollHeight');
      const clientHeight = this.$street.prop('clientHeight');
      if (typeof lastHeight === 'undefined' && clientHeight === 0) {
        setTimeout(this.controlStreetTextareaHeight.bind(this), 10);
        return;
      }
      if (clientHeight === lastHeight) return;
      if (scrollHeight > clientHeight + 1) {
        const rows = this.$street.prop('rows');
        this.$street.attr('rows', rows + 1);
        this.controlStreetTextareaHeight(clientHeight);
      }
      if (this.$street.val().length === 0) {
        this.$street.attr('rows', 1);
      }
    }
    setup() {
      super.setup();
      const actualAttributePartList = this.getMetadata().get(['fields', this.type, 'actualFields']) || ['street', 'city', 'state', 'country', 'postalCode'];
      this.addressAttributeList = [];
      this.addressPartList = [];
      actualAttributePartList.forEach(item => {
        const attribute = this.name + Espo.Utils.upperCaseFirst(item);
        this.addressAttributeList.push(attribute);
        this.addressPartList.push(item);
        this[item + 'Field'] = attribute;
        this[item + 'MaxLength'] = this.getMetadata().get(['entityDefs', this.entityType, 'fields', attribute, 'maxLength']);
      });
    }
    validateRequired() {
      const validate = name => {
        if (this.model.isRequired(name)) {
          if (this.model.get(name) === '') {
            const msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.translate(name, 'fields', this.entityType));
            this.showValidationMessage(msg, '[data-name="' + name + '"]');
            return true;
          }
        }
      };
      let result = false;
      result = validate(this.postalCodeField) || result;
      result = validate(this.streetField) || result;
      result = validate(this.stateField) || result;
      result = validate(this.cityField) || result;
      result = validate(this.countryField) || result;
      return result;
    }
    isRequired() {
      return this.model.getFieldParam(this.postalCodeField, 'required') || this.model.getFieldParam(this.streetField, 'required') || this.model.getFieldParam(this.stateField, 'required') || this.model.getFieldParam(this.cityField, 'required') || this.model.getFieldParam(this.countryField, 'required');
    }

    // noinspection JSUnusedGlobalSymbols
    validatePattern() {
      const fieldList = [this.postalCodeField, this.stateField, this.cityField, this.countryField];
      let result = false;
      for (const field of fieldList) {
        result = _varchar.default.prototype.fieldValidatePattern.call(this, field) || result;
      }
      return result;
    }
    fetch() {
      const data = {};
      data[this.postalCodeField] = this.$postalCode.val().toString().trim();
      data[this.streetField] = this.$street.val().toString().trim();
      data[this.stateField] = this.$state.val().toString().trim();
      data[this.cityField] = this.$city.val().toString().trim();
      data[this.countryField] = this.$country.val().toString().trim();
      const attributeList = [this.postalCodeField, this.streetField, this.stateField, this.cityField, this.countryField];
      attributeList.forEach(attribute => {
        if (data[attribute] === '') {
          data[attribute] = null;
        }
      });
      return data;
    }
    fetchSearch() {
      const value = this.$el.find('input.main-element').val().toString().trim();
      if (!value) {
        return null;
      }
      return {
        type: 'or',
        value: [{
          type: 'like',
          field: this.postalCodeField,
          value: value + '%'
        }, {
          type: 'like',
          field: this.streetField,
          value: value + '%'
        }, {
          type: 'like',
          field: this.cityField,
          value: value + '%'
        }, {
          type: 'like',
          field: this.stateField,
          value: value + '%'
        }, {
          type: 'like',
          field: this.countryField,
          value: value + '%'
        }],
        data: {
          value: value
        }
      };
    }
    viewMapAction() {
      this.createView('mapDialog', 'views/modals/view-map', {
        model: this.model,
        field: this.name
      }, view => view.render());
    }

    /**
     * @private
     * @return {string[]}
     */
    getCountryList() {
      const list = (this.getHelper().getAppParam('addressCountryData') || {}).list || [];
      if (list.length) {
        return list;
      }
      return [];
    }

    /**
     * @private
     * @param {string[]} fullList
     * @return {function(string): Promise|undefined}
     */
    getCountryAutocompleteLookupFunction(fullList) {
      // noinspection JSUnresolvedReference
      const list = (this.getHelper().getAppParam('addressCountryData') || {}).preferredList || [];
      if (!list.length) {
        return undefined;
      }
      return query => {
        if (query.length === 0) {
          const result = list.map(item => ({
            value: item
          }));
          return Promise.resolve(result);
        }
        const queryLowerCase = query.toLowerCase();
        const result = fullList.filter(item => {
          if (item.toLowerCase().indexOf(queryLowerCase) === 0) {
            return item.length !== queryLowerCase.length;
          }
        }).map(item => ({
          value: item
        }));
        return Promise.resolve(result);
      };
    }
  }
  var _default = _exports.default = AddressFieldView;
});

define("views/fields/address-state", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class AddressStateFieldView extends _varchar.default {
    setupOptions() {
      let stateList = this.getConfig().get('addressStateList') || [];
      if (stateList.length) {
        this.params.options = Espo.Utils.clone(stateList);
      }
    }
  }
  var _default = _exports.default = AddressStateFieldView;
});

define("views/fields/address-country", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class AddressCountryFieldView extends _varchar.default {
    setupOptions() {
      const countryList = this.getCountryList();
      if (countryList.length) {
        this.params.options = Espo.Utils.clone(countryList);
      }
    }

    /**
     * @private
     * @return {string[]}
     */
    getCountryList() {
      const list = (this.getHelper().getAppParam('addressCountryData') || {}).list || [];
      if (list.length) {
        return list;
      }
      return [];
    }
    getAutocompleteLookupFunction() {
      // noinspection JSUnresolvedReference
      const list = (this.getHelper().getAppParam('addressCountryData') || {}).preferredList || [];
      if (!list.length) {
        return undefined;
      }
      const fullList = this.params.options || [];
      return query => {
        if (query.length === 0) {
          const result = list.map(item => ({
            value: item
          }));
          return Promise.resolve(result);
        }
        const queryLowerCase = query.toLowerCase();
        const result = fullList.filter(item => {
          if (item.toLowerCase().indexOf(queryLowerCase) === 0) {
            return item.length !== queryLowerCase.length;
          }
        }).map(item => ({
          value: item
        }));
        return Promise.resolve(result);
      };
    }
  }
  var _default = _exports.default = AddressCountryFieldView;
});

define("views/fields/address-city", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class AddressCityFieldView extends _varchar.default {
    setupOptions() {
      let cityList = this.getConfig().get('addressCityList') || [];
      if (cityList.length) {
        this.params.options = Espo.Utils.clone(cityList);
      }
    }
  }
  var _default = _exports.default = AddressCityFieldView;
});

define("views/export/record/record", ["exports", "views/record/edit-for-modal"], function (_exports, _editForModal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _editForModal = _interopRequireDefault(_editForModal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ExportRecordView extends _editForModal.default {
    formatList = null;

    /**
     * @type {Object.<string, string[]>},
     */
    customParams = null;
    setupBeforeFinal() {
      this.formatList = this.options.formatList;
      this.scope = this.options.scope;
      const fieldsData = this.getExportFieldsData();
      this.setupExportFieldDefs(fieldsData);
      this.setupExportLayout(fieldsData);
      this.setupExportDynamicLogic();
      this.controlFormatField();
      this.listenTo(this.model, 'change:format', () => this.controlFormatField());
      this.controlAllFields();
      this.listenTo(this.model, 'change:exportAllFields', () => this.controlAllFields());
      super.setupBeforeFinal();
    }
    setupExportFieldDefs(fieldsData) {
      const fieldDefs = {
        format: {
          type: 'enum',
          options: this.formatList
        },
        fieldList: {
          type: 'multiEnum',
          options: fieldsData.list,
          required: true
        },
        exportAllFields: {
          type: 'bool'
        }
      };
      this.customParams = {};
      this.formatList.forEach(format => {
        const fields = this.getFormatParamsDefs(format).fields || {};
        this.customParams[format] = [];
        for (const name in fields) {
          const newName = this.modifyParamName(format, name);
          this.customParams[format].push(name);
          fieldDefs[newName] = Espo.Utils.cloneDeep(fields[name]);
        }
      });
      this.model.setDefs({
        fields: fieldDefs
      });
    }
    setupExportLayout(fieldsData) {
      this.detailLayout = [];
      const mainPanel = {
        rows: [[{
          name: 'format'
        }, false], [{
          name: 'exportAllFields'
        }, false], [{
          name: 'fieldList',
          options: {
            translatedOptions: fieldsData.translations
          }
        }]]
      };
      this.detailLayout.push(mainPanel);
      this.formatList.forEach(format => {
        const rows = this.getFormatParamsDefs(format).layout || [];
        rows.forEach(row => {
          row.forEach(item => {
            item.name = this.modifyParamName(format, item.name);
          });
        });
        this.detailLayout.push({
          name: format,
          rows: rows
        });
      });
    }
    setupExportDynamicLogic() {
      this.dynamicLogicDefs = {
        fields: {}
      };
      this.formatList.forEach(format => {
        const defs = this.getFormatParamsDefs(format).dynamicLogic || {};
        this.customParams[format].forEach(param => {
          const logic = defs[param] || {};
          if (!logic.visible) {
            logic.visible = {};
          }
          if (!logic.visible.conditionGroup) {
            logic.visible.conditionGroup = [];
          }
          logic.visible.conditionGroup.push({
            type: 'equals',
            attribute: 'format',
            value: format
          });
          const newName = this.modifyParamName(format, param);
          this.dynamicLogicDefs.fields[newName] = logic;
        });
      });
    }

    /**
     * @param {string} format
     * @return {string[]}
     */
    getFormatParamList(format) {
      return Object.keys(this.getFormatParamsDefs(format).fields || {});
    }

    /**
     * @private
     * @return {Object.<string, *>}
     */
    getFormatParamsDefs(format) {
      const defs = this.getMetadata().get(['app', 'export', 'formatDefs', format]) || {};
      return Espo.Utils.cloneDeep(defs.params || {});
    }

    /**
     * @param {string} format
     * @param {string} name
     * @return {string}
     */
    modifyParamName(format, name) {
      return format + Espo.Utils.upperCaseFirst(name);
    }

    /**
     * @return {{
     *   translations: Object.<string, string>,
     *   list: string[]
     * }}
     */
    getExportFieldsData() {
      let fieldList = this.getFieldManager().getEntityTypeFieldList(this.scope);
      const forbiddenFieldList = this.getAcl().getScopeForbiddenFieldList(this.scope);
      fieldList = fieldList.filter(item => {
        return !~forbiddenFieldList.indexOf(item);
      });
      fieldList = fieldList.filter(item => {
        /** @type {Record} */
        const defs = this.getMetadata().get(['entityDefs', this.scope, 'fields', item]) || {};
        if (defs.disabled || defs.exportDisabled || defs.type === 'map' || defs.utility) {
          return false;
        }
        return true;
      });
      this.getLanguage().sortFieldList(this.scope, fieldList);
      fieldList.unshift('id');
      const fieldListTranslations = {};
      fieldList.forEach(item => {
        fieldListTranslations[item] = this.getLanguage().translate(item, 'fields', this.scope);
      });
      const setFieldList = this.model.get('fieldList') || [];
      setFieldList.forEach(item => {
        if (~fieldList.indexOf(item)) {
          return;
        }
        if (!~item.indexOf('_')) {
          return;
        }
        const arr = item.split('_');
        fieldList.push(item);
        const foreignScope = this.getMetadata().get(['entityDefs', this.scope, 'links', arr[0], 'entity']);
        if (!foreignScope) {
          return;
        }
        fieldListTranslations[item] = this.getLanguage().translate(arr[0], 'links', this.scope) + '.' + this.getLanguage().translate(arr[1], 'fields', foreignScope);
      });
      return {
        list: fieldList,
        translations: fieldListTranslations
      };
    }
    controlAllFields() {
      if (!this.model.get('exportAllFields')) {
        this.showField('fieldList');
        return;
      }
      this.hideField('fieldList');
    }
    controlFormatField() {
      const format = this.model.get('format');
      this.formatList.filter(item => item !== format).forEach(format => {
        this.hidePanel(format);
      });
      this.formatList.filter(item => item === format).forEach(format => {
        this.customParams[format].length ? this.showPanel(format) : this.hidePanel(format);
      });
    }
  }
  var _default = _exports.default = ExportRecordView;
});

define("views/export/modals/idle", ["exports", "views/modal", "model"], function (_exports, _modal, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ExportIdleModalView extends _modal.default {
    template = 'export/modals/idle';
    className = 'dialog dialog-record';
    checkInterval = 4000;
    data() {
      return {
        infoText: this.translate('infoText', 'messages', 'Export')
      };
    }
    setup() {
      this.addActionHandler('download', () => this.actionDownload());
      this.action = this.options.action;
      this.id = this.options.id;
      this.status = 'Pending';
      this.headerText = this.translate('Export');
      this.model = new _model.default();
      this.model.name = 'Export';
      this.model.setDefs({
        fields: {
          'status': {
            type: 'enum',
            readOnly: true,
            options: ['Pending', 'Running', 'Success', 'Failed'],
            style: {
              'Success': 'success',
              'Failed': 'danger'
            }
          },
          'attachmentId': {
            type: 'varchar'
          }
        }
      });
      this.model.set({
        status: this.status,
        processedCount: null
      });
      this.createView('record', 'views/record/edit-for-modal', {
        scope: 'None',
        model: this.model,
        selector: '.record',
        detailLayout: [{
          rows: [[{
            name: 'status',
            labelText: this.translate('status', 'fields', 'Export')
          }]]
        }]
      });
      this.on('close', () => {
        const status = this.model.get('status');
        if (status !== 'Pending' && status !== 'Running') {
          return;
        }
        Espo.Ajax.postRequest(`Export/${this.id}/subscribe`);
      });
      this.checkStatus();
    }

    /**
     * @private
     */
    checkStatus() {
      Espo.Ajax.getRequest(`Export/${this.id}/status`).then(response => {
        const status = response.status;
        this.model.set('status', status);
        if (status === 'Pending' || status === 'Running') {
          setTimeout(() => this.checkStatus(), this.checkInterval);
          return;
        }
        this.model.set({
          attachmentId: response.attachmentId
        });
        if (status === 'Success') {
          this.trigger('success', {
            attachmentId: response.attachmentId
          });
          this.showDownload();
        }
        if (this.$el) {
          this.$el.find('.info-text').addClass('hidden');
        }
      });
    }

    /**
     * @private
     */
    showDownload() {
      this.$el.find('.download-container').removeClass('hidden');
      const $download = this.$el.find('[data-action="download"]');
      $download.removeClass('hidden');
    }

    /**
     * @private
     */
    actionDownload() {
      this.trigger('download', this.model.get('attachmentId'));
      this.close();
    }
  }
  var _default = _exports.default = ExportIdleModalView;
});

define("views/export/modals/export", ["exports", "views/modal", "model"], function (_exports, _modal, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ExportModalView extends _modal.default {
    cssName = 'export-modal';
    className = 'dialog dialog-record';
    template = 'export/modals/export';
    shortcutKeys = {
      'Control+Enter': 'export'
    };
    setup() {
      this.buttonList = [{
        name: 'export',
        label: 'Export',
        style: 'danger',
        title: 'Ctrl+Enter'
      }, {
        name: 'cancel',
        label: 'Cancel'
      }];
      this.model = new _model.default();
      this.model.name = 'Export';
      this.scope = this.options.scope;
      if (this.options.fieldList) {
        const fieldList = this.options.fieldList.filter(field => {
          /** @type {Record} */
          const defs = this.getMetadata().get(`entityDefs.${this.scope}.fields.${field}`) || {};
          return !defs.exportDisabled && !defs.utility;
        });
        this.model.set('fieldList', fieldList);
        this.model.set('exportAllFields', false);
      } else {
        this.model.set('exportAllFields', true);
      }
      const formatList = this.getMetadata().get(['scopes', this.scope, 'exportFormatList']) || this.getMetadata().get('app.export.formatList');
      this.model.set('format', formatList[0]);
      this.createView('record', 'views/export/record/record', {
        scope: this.scope,
        model: this.model,
        selector: '.record',
        formatList: formatList
      });
    }

    /**
     * @return {import('views/record/edit').default}
     */
    getRecordView() {
      return this.getView('record');
    }

    // noinspection JSUnusedGlobalSymbols
    actionExport() {
      const recordView = this.getRecordView();
      const data = recordView.fetch();
      this.model.set(data);
      if (recordView.validate()) {
        return;
      }
      const returnData = {
        exportAllFields: data.exportAllFields,
        format: data.format
      };
      if (!data.exportAllFields) {
        const attributeList = [];
        data.fieldList.forEach(item => {
          if (item === 'id') {
            attributeList.push('id');
            return;
          }
          const type = this.getMetadata().get(['entityDefs', this.scope, 'fields', item, 'type']);
          if (type) {
            this.getFieldManager().getAttributeList(type, item).forEach(attribute => {
              attributeList.push(attribute);
            });
          }
          if (~item.indexOf('_')) {
            attributeList.push(item);
          }
        });
        returnData.attributeList = attributeList;
        returnData.fieldList = data.fieldList;
      }
      returnData.params = {};
      recordView.getFormatParamList(data.format).forEach(param => {
        const name = recordView.modifyParamName(data.format, param);
        const fieldView = recordView.getFieldView(name);
        if (!fieldView || fieldView.disabled) {
          return;
        }
        this.getFieldManager().getActualAttributeList(fieldView.type, param).forEach(subParam => {
          const name = recordView.modifyParamName(data.format, subParam);
          returnData.params[subParam] = data[name];
        });
      });
      this.trigger('proceed', returnData);
      this.close();
    }
  }
  var _default = _exports.default = ExportModalView;
});

define("views/event/fields/name-for-history", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _varchar.default {
    listLinkTemplate = 'event/fields/name-for-history/list-link';
    data() {
      const data = super.data();
      const status = this.model.get('status');
      const canceledStatusList = this.getMetadata().get(['scopes', this.model.entityType, 'canceledStatusList']) || [];
      data.strikethrough = canceledStatusList.includes(status);
      return data;
    }
  }
  _exports.default = _default;
});

define("views/email-template/list", ["exports", "views/list-with-categories"], function (_exports, _listWithCategories) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _listWithCategories = _interopRequireDefault(_listWithCategories);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _listWithCategories.default {
    quickCreate = false;
  }
  _exports.default = _default;
});

define("views/email-template/record/edit", ["exports", "views/record/edit", "views/email-template/record/detail"], function (_exports, _edit, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _edit.default {
    saveAndContinueEditingAction = true;
    setup() {
      super.setup();
      _detail.default.prototype.listenToInsertField.call(this);
    }
  }
  _exports.default = _default;
});

define("views/email-template/record/edit-quick", ["exports", "views/record/edit", "views/email-template/record/detail"], function (_exports, _edit, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _edit.default {
    isWide = true;
    sideView = false;
    setup() {
      super.setup();
      _detail.default.prototype.listenToInsertField.call(this);
    }
  }
  _exports.default = _default;
});

define("views/email-template/record/panels/information", ["exports", "views/record/panels/side"], function (_exports, _side) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _side = _interopRequireDefault(_side);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _side.default {
    templateContent = `{{{infoText}}}`;
    data() {
      const list2 = this.getMetadata().get(['clientDefs', 'EmailTemplate', 'placeholderList']) || [];
      const defs = this.getMetadata().get('app.emailTemplate.placeholders') || {};
      const list1 = Object.keys(defs).sort((a, b) => {
        const o1 = defs[a].order || 0;
        const o2 = defs[b].order || 0;
        return o1 - o2;
      });
      const placeholderList = [...list1, ...list2];
      if (!placeholderList.length) {
        return {
          infoText: ''
        };
      }
      const $header = $('<h4>').text(this.translate('Available placeholders', 'labels', 'EmailTemplate') + ':');
      const $liList = placeholderList.map(item => {
        return $('<li>').append($('<code>').text('{' + item + '}'), ' &#8211; ', $('<span>').text(this.translate(item, 'placeholderTexts', 'EmailTemplate')));
      });
      const $ul = $('<ul>').append($liList);
      const $text = $('<span>').addClass('complex-text').append($header, $ul);
      return {
        infoText: $text[0].outerHTML
      };
    }
  }
  _exports.default = _default;
});

define("views/email-template/fields/insert-field", ["exports", "views/fields/base", "ui/select"], function (_exports, _base, _select) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _select = _interopRequireDefault(_select);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _base.default {
    inlineEditDisabled = true;
    detailTemplate = 'email-template/fields/insert-field/detail';
    editTemplate = 'email-template/fields/insert-field/edit';
    data() {
      return {};
    }
    setup() {
      this.addActionHandler('insert', () => {
        const entityType = this.$entityType.val();
        const field = this.$field.val();
        if (!field) {
          return;
        }
        this.insert(entityType, field);
      });
      if (this.mode !== this.MODE_LIST) {
        const defs = this.getMetadata().get('scopes');
        const entityList = Object.keys(defs).filter(scope => {
          if (scope === 'Email') {
            return;
          }
          if (!this.getAcl().checkScope(scope)) {
            return;
          }
          return defs[scope].entity && defs[scope].object;
        });
        this.translatedOptions = {};
        const entityPlaceholders = {};
        entityList.forEach(scope => {
          this.translatedOptions[scope] = {};
          entityPlaceholders[scope] = this.getScopeAttributeList(scope);
          entityPlaceholders[scope].forEach(item => {
            this.translatedOptions[scope][item] = this.translatePlaceholder(scope, item);
          });

          /** @type {Record<string, Record>} */
          const links = this.getMetadata().get(`entityDefs.${scope}.links`) || {};
          const linkList = Object.keys(links).sort((v1, v2) => {
            return this.translate(v1, 'links', scope).localeCompare(this.translate(v2, 'links', scope));
          });
          linkList.forEach(link => {
            const type = links[link].type;
            if (type !== 'belongsTo') {
              return;
            }
            const foreignScope = links[link].entity;
            if (!foreignScope) {
              return;
            }
            if (links[link].disabled || links[link].utility) {
              return;
            }
            if (this.getMetadata().get(['entityAcl', scope, 'links', link, 'onlyAdmin']) || this.getMetadata().get(['entityAcl', scope, 'links', link, 'forbidden']) || this.getMetadata().get(['entityAcl', scope, 'links', link, 'internal'])) {
              return;
            }
            const attributeList = this.getScopeAttributeList(foreignScope, true);
            attributeList.forEach(item => {
              entityPlaceholders[scope].push(`${link}.${item}`);
              this.translatedOptions[scope][`${link}.${item}`] = this.translatePlaceholder(scope, `${link}.${item}`);
            });
          });
        });
        entityPlaceholders['Person'] = ['name', 'firstName', 'lastName', 'salutationName', 'emailAddress', 'assignedUserName'];
        this.translatedOptions['Person'] = {};
        this.entityList = entityList;
        this.entityFields = entityPlaceholders;
      }
    }

    /**
     * @private
     * @param {string} scope
     * @param {boolean} [isForeign]
     * @return {string[]}
     */
    getScopeAttributeList(scope) {
      let isForeign = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
      let fieldList = this.getFieldManager().getEntityTypeFieldList(scope);
      let list = [];
      fieldList = fieldList.sort((v1, v2) => {
        return this.translate(v1, 'fields', scope).localeCompare(this.translate(v2, 'fields', scope));
      });
      fieldList.forEach(field => {
        const fieldType = this.getMetadata().get(['entityDefs', scope, 'fields', field, 'type']);

        /** @type {Record} */
        const aclDefs = this.getMetadata().get(['entityAcl', scope, 'fields', field]) || {};
        /** @type {Record} */
        const fieldDefs = this.getMetadata().get(['entityDefs', scope, 'fields', field]) || {};
        if (aclDefs.onlyAdmin || aclDefs.forbidden || aclDefs.internal || fieldDefs.disabled || fieldDefs.utility || fieldDefs.directAccessDisabled && (isForeign || !fieldDefs.loaderClassName) || fieldDefs.templatePlaceholderDisabled) {
          return false;
        }
        if (fieldType === 'map') {
          return;
        }
        if (fieldType === 'linkMultiple') {
          return;
        }
        if (fieldType === 'attachmentMultiple') {
          return;
        }
        if (this.getMetadata().get(['entityAcl', scope, 'fields', field, 'onlyAdmin']) || this.getMetadata().get(['entityAcl', scope, 'fields', field, 'forbidden']) || this.getMetadata().get(['entityAcl', scope, 'fields', field, 'internal'])) {
          return;
        }
        const fieldAttributeList = this.getFieldManager().getAttributeList(fieldType, field);
        fieldAttributeList.forEach(attribute => {
          if (list.includes(attribute)) {
            return;
          }
          list.push(attribute);
        });
      });
      const forbiddenList = this.getAcl().getScopeForbiddenAttributeList(scope);
      list = list.filter(item => {
        if (~forbiddenList.indexOf(item)) {
          return;
        }
        return true;
      });
      list.push('id');
      if (this.getMetadata().get('entityDefs.' + scope + '.fields.name.type') === 'personName') {
        list.unshift('name');
      }
      return list;
    }

    /**
     * @private
     * @param {string} entityType
     * @param {string} item
     * @return {string}
     */
    translatePlaceholder(entityType, item) {
      let field = item;
      let scope = entityType;
      let isForeign = false;
      let link;
      if (item.includes('.')) {
        isForeign = true;
        field = item.split('.')[1];
        link = item.split('.')[0];
        scope = this.getMetadata().get(`entityDefs.${entityType}.links.${link}.entity`);
      }
      let label = this.translate(field, 'fields', scope);
      if (field.indexOf('Id') === field.length - 2) {
        const baseField = field.substr(0, field.length - 2);
        if (this.getMetadata().get(['entityDefs', scope, 'fields', baseField])) {
          label = this.translate(baseField, 'fields', scope) + ' (' + this.translate('id', 'fields') + ')';
        }
      } else if (field.indexOf('Name') === field.length - 4) {
        const baseField = field.substr(0, field.length - 4);
        if (this.getMetadata().get(['entityDefs', scope, 'fields', baseField])) {
          label = this.translate(baseField, 'fields', scope) + ' (' + this.translate('name', 'fields') + ')';
        }
      } else if (field.indexOf('Type') === field.length - 4) {
        const baseField = field.substr(0, field.length - 4);
        if (this.getMetadata().get(['entityDefs', scope, 'fields', baseField])) {
          label = this.translate(baseField, 'fields', scope) + ' (' + this.translate('type', 'fields') + ')';
        }
      }
      if (field.indexOf('Ids') === field.length - 3) {
        const baseField = field.substr(0, field.length - 3);
        if (this.getMetadata().get(['entityDefs', scope, 'fields', baseField])) {
          label = this.translate(baseField, 'fields', scope) + ' (' + this.translate('ids', 'fields') + ')';
        }
      } else if (field.indexOf('Names') === field.length - 5) {
        const baseField = field.substr(0, field.length - 5);
        if (this.getMetadata().get(['entityDefs', scope, 'fields', baseField])) {
          label = this.translate(baseField, 'fields', scope) + ' (' + this.translate('names', 'fields') + ')';
        }
      } else if (field.indexOf('Types') === field.length - 5) {
        const baseField = field.substr(0, field.length - 5);
        if (this.getMetadata().get(['entityDefs', scope, 'fields', baseField])) {
          label = this.translate(baseField, 'fields', scope) + ' (' + this.translate('types', 'fields') + ')';
        }
      }
      if (isForeign) {
        label = this.translate(link, 'links', entityType) + ' . ' + label;
      }
      return label;
    }
    afterRender() {
      super.afterRender();
      if (this.mode === this.MODE_EDIT) {
        const entityTranslation = {};
        this.entityList.forEach(scope => {
          entityTranslation[scope] = this.translate(scope, 'scopeNames');
        });
        this.entityList.sort((a, b) => {
          return a.localeCompare(b);
        });
        const $entityType = this.$entityType = this.$el.find('[data-name="entityType"]');
        this.$field = this.$el.find('[data-name="field"]');
        $entityType.on('change', () => {
          this.changeEntityType();
        });
        $entityType.append($('<option>').val('Person').text(this.translate('Person')));
        this.entityList.forEach(scope => {
          $entityType.append($('<option>').val(scope).text(entityTranslation[scope]));
        });
        _select.default.init(this.$field);
        this.changeEntityType();
        _select.default.init(this.$entityType);
      }
    }

    /**
     * @private
     */
    changeEntityType() {
      const entityType = this.$entityType.val();
      const fieldList = this.entityFields[entityType];
      _select.default.setValue(this.$field, '');
      _select.default.setOptions(this.$field, fieldList.map(field => {
        return {
          value: field,
          label: this.translateItem(entityType, field)
        };
      }));
    }

    /**
     * @private
     * @param {string} entityType
     * @param {string} item
     * @return {string}
     */
    translateItem(entityType, item) {
      if (this.translatedOptions[entityType][item]) {
        return this.translatedOptions[entityType][item];
      }
      return this.translate(item, 'fields');
    }

    /**
     * @private
     * @param {string} entityType
     * @param {string} field
     */
    insert(entityType, field) {
      this.model.trigger('insert-field', {
        entityType: entityType,
        field: field
      });
    }
  }
  _exports.default = _default;
});

define("views/email-template/fields/body", ["exports", "views/fields/wysiwyg"], function (_exports, _wysiwyg) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _wysiwyg = _interopRequireDefault(_wysiwyg);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailTemplateBodyFieldView extends _wysiwyg.default {
    // To allow the iterate attribute.
    htmlPurificationForEditDisabled = true;
    hasBodyPlainField = true;
  }
  var _default = _exports.default = EmailTemplateBodyFieldView;
});

define("views/email-folder/list", ["exports", "views/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _list.default {
    quickCreate = true;
    setup() {
      super.setup();
      this.collection.data = {
        boolFilterList: ['onlyMy']
      };
    }
  }
  _exports.default = _default;
});

define("views/email-folder/list-side", ["exports", "view"], function (_exports, _view) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _view = _interopRequireDefault(_view);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _view.default {
    template = 'email-folder/list-side';
    FOLDER_ALL = 'all';
    FOLDER_INBOX = 'inbox';
    FOLDER_DRAFTS = 'drafts';
    data() {
      const data = {};
      data.selectedFolderId = this.selectedFolderId;
      data.showEditLink = this.options.showEditLink;
      data.scope = this.scope;
      return data;
    }
    actionSelectFolder(id) {
      this.$el.find('li.selected').removeClass('selected');
      this.selectFolder(id);
      this.$el.find(`li[data-id="${id}"]`).addClass('selected');
    }
    setup() {
      this.addActionHandler('selectFolder', (e, target) => {
        e.preventDefault();
        this.actionSelectFolder(target.dataset.id);
      });
      this.scope = 'EmailFolder';
      this.selectedFolderId = this.options.selectedFolderId || this.FOLDER_ALL;
      this.emailCollection = this.options.emailCollection;
      this.loadNotReadCounts();
      this.listenTo(this.emailCollection, 'sync', this.loadNotReadCounts);
      this.listenTo(this.emailCollection, 'folders-update', this.loadNotReadCounts);
      this.listenTo(this.emailCollection, 'all-marked-read', () => {
        this.countsData = this.countsData || {};
        for (const id in this.countsData) {
          if (id === this.FOLDER_DRAFTS) {
            continue;
          }
          this.countsData[id] = 0;
        }
        this.renderCounts();
      });
      this.listenTo(this.emailCollection, 'draft-sent', () => {
        this.decreaseNotReadCount(this.FOLDER_DRAFTS);
        this.renderCounts();
      });
      this.listenTo(this.emailCollection, 'change:isRead', model => {
        if (this.countsIsBeingLoaded) {
          return;
        }
        this.manageCountsDataAfterModelChanged(model);
      });
      this.listenTo(this.emailCollection, 'model-removing', id => {
        const model = this.emailCollection.get(id);
        if (!model) {
          return;
        }
        if (this.countsIsBeingLoaded) {
          return;
        }
        this.manageModelRemoving(model);
      });
      this.listenTo(this.emailCollection, 'moving-to-trash', (id, model) => {
        model = this.emailCollection.get(id) || model;
        if (!model) {
          return;
        }
        if (this.countsIsBeingLoaded) {
          return;
        }
        this.manageModelRemoving(model);
      });
      this.listenTo(this.emailCollection, 'retrieving-from-trash', (id, model) => {
        model = this.emailCollection.get(id) || model;
        if (!model) {
          return;
        }
        if (this.countsIsBeingLoaded) {
          return;
        }
        this.manageModelRetrieving(model);
      });
    }
    manageModelRemoving(model) {
      if (model.get('status') === 'Draft') {
        this.decreaseNotReadCount(this.FOLDER_DRAFTS);
        this.renderCounts();
        return;
      }
      if (!model.get('isUsers')) {
        return;
      }
      if (model.get('isRead')) {
        return;
      }
      let folderId = model.get('groupFolderId') ? 'group:' + model.get('groupFolderId') : model.get('folderId') || this.FOLDER_INBOX;
      this.decreaseNotReadCount(folderId);
      this.renderCounts();
    }
    manageModelRetrieving(model) {
      if (!model.get('isUsers')) {
        return;
      }
      if (model.get('isRead')) {
        return;
      }
      const folderId = model.get('groupFolderId') ? 'group:' + model.get('groupFolderId') : model.get('folderId') || this.FOLDER_INBOX;
      this.increaseNotReadCount(folderId);
      this.renderCounts();
    }
    manageCountsDataAfterModelChanged(model) {
      if (!model.get('isUsers')) {
        return;
      }
      const folderId = model.get('groupFolderId') ? 'group:' + model.get('groupFolderId') : model.get('folderId') || this.FOLDER_INBOX;
      !model.get('isRead') ? this.increaseNotReadCount(folderId) : this.decreaseNotReadCount(folderId);
      this.renderCounts();
    }
    increaseNotReadCount(folderId) {
      this.countsData = this.countsData || {};
      this.countsData[folderId] = this.countsData[folderId] || 0;
      this.countsData[folderId]++;
    }
    decreaseNotReadCount(folderId) {
      this.countsData = this.countsData || {};
      this.countsData[folderId] = this.countsData[folderId] || 0;
      if (this.countsData[folderId]) {
        this.countsData[folderId]--;
      }
    }
    selectFolder(id) {
      this.emailCollection.reset();
      this.emailCollection.abortLastFetch();
      this.selectedFolderId = id;
      this.trigger('select', id);
    }
    afterRender() {
      if (this.countsData) {
        this.renderCounts();
      }
    }
    loadNotReadCounts() {
      if (this.countsIsBeingLoaded) {
        return;
      }
      this.countsIsBeingLoaded = true;
      Espo.Ajax.getRequest('Email/inbox/notReadCounts').then(data => {
        this.countsData = data;
        if (this.isRendered()) {
          this.renderCounts();
          this.countsIsBeingLoaded = false;
          return;
        }
        this.once('after:render', () => {
          this.renderCounts();
          this.countsIsBeingLoaded = false;
        });
      });
    }
    renderCounts() {
      const data = this.countsData;
      for (const id in data) {
        let value = '';
        if (data[id]) {
          value = data[id].toString();
        }
        this.$el.find(`li a.count[data-id="${id}"]`).text(value);
      }
    }
  }
  _exports.default = _default;
});

define("views/email-folder/record/list", ["exports", "views/record/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _list.default {
    massUpdateDisabled = true;
    mergeDisabled = true;
    exportDisabled = true;
    removeDisabled = true;
    rowActionsView = 'views/email-folder/record/row-actions/default';

    // noinspection JSUnusedGlobalSymbols
    actionMoveUp(data) {
      const model = this.collection.get(data.id);
      if (!model) {
        return;
      }
      const index = this.collection.indexOf(model);
      if (index === 0) {
        return;
      }
      Espo.Ajax.postRequest('EmailFolder/action/moveUp', {
        id: model.id
      }).then(() => {
        this.collection.fetch();
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionMoveDown(data) {
      const model = this.collection.get(data.id);
      if (!model) {
        return;
      }
      const index = this.collection.indexOf(model);
      if (index === this.collection.length - 1 && this.collection.length === this.collection.total) {
        return;
      }
      Espo.Ajax.postRequest('EmailFolder/action/moveDown', {
        id: model.id
      }).then(() => {
        this.collection.fetch();
      });
    }
  }
  _exports.default = _default;
});

define("views/email-folder/record/edit-small", ["exports", "views/record/edit"], function (_exports, _edit) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _edit.default {
    afterSave() {
      this.getBaseController().clearScopeStoredMainView('Email');
      super.afterSave();
    }
  }
  _exports.default = _default;
});

define("views/email-folder/record/row-actions/default", ["exports", "views/record/row-actions/default"], function (_exports, _default2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _default2 = _interopRequireDefault(_default2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _default2.default {
    getActionList() {
      const list = super.getActionList();
      if (this.options.acl.edit) {
        list.unshift({
          action: 'moveDown',
          label: 'Move Down',
          data: {
            id: this.model.id
          }
        });
        list.unshift({
          action: 'moveUp',
          label: 'Move Up',
          data: {
            id: this.model.id
          }
        });
      }
      return list;
    }
  }
  _exports.default = _default;
});

define("views/email-folder/modals/select-folder", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _modal.default {
    template = 'email-folder/modals/select-folder';
    cssName = 'select-folder';
    backdrop = true;

    /** @const */
    FOLDER_ALL = 'all';
    /** @const */
    FOLDER_INBOX = 'inbox';
    /** @const */
    FOLDER_IMPORTANT = 'important';
    /** @const */
    FOLDER_SENT = 'sent';
    /** @const */
    FOLDER_DRAFTS = 'drafts';
    /** @const */
    FOLDER_TRASH = 'trash';
    /** @const */
    FOLDER_ARCHIVE = 'archive';
    data() {
      return {
        folderDataList: this.folderDataList
      };
    }

    /**
     * @private
     * @type {string|undefined}
     */
    currentFolderId;
    setup() {
      this.addActionHandler('selectFolder', (e, target) => {
        const id = target.dataset.id;
        const name = target.dataset.name;
        this.trigger('select', id, name);
        this.close();
      });
      this.headerText = this.options.headerText || '';
      this.isGroup = this.options.isGroup || false;
      this.noArchive = this.options.noArchive || false;
      this.currentFolderId = this.options.currentFolderId;
      if (this.headerText === '') {
        this.buttonList.push({
          name: 'cancel',
          label: 'Cancel'
        });
      }
      Espo.Ui.notifyWait();
      this.wait(Espo.Ajax.getRequest('EmailFolder/action/listAll').then(/** {list: {id: string, name: string}[]} */data => {
        Espo.Ui.notify(false);
        const builtInFolders = [this.FOLDER_INBOX, this.FOLDER_IMPORTANT, this.FOLDER_SENT, this.FOLDER_DRAFTS, this.FOLDER_TRASH, this.FOLDER_ARCHIVE];
        const iconMap = {
          [this.FOLDER_ALL]: 'far fa-hdd',
          [this.FOLDER_TRASH]: 'far fa-trash-alt',
          [this.FOLDER_SENT]: 'far fa-paper-plane',
          [this.FOLDER_INBOX]: 'fas fa-inbox',
          [this.FOLDER_ARCHIVE]: 'far fa-caret-square-down'
        };
        this.folderDataList = data.list.filter(item => {
          if (this.isGroup && !item.id.startsWith('group:')) {
            return false;
          }
          return !builtInFolders.includes(item.id);
        }).map(item => {
          const isGroup = item.id.startsWith('group:');
          return {
            disabled: item.id === this.currentFolderId,
            id: item.id,
            name: item.name,
            isGroup: isGroup,
            iconClass: isGroup ? 'far fa-circle' : 'far fa-folder'
          };
        });
        this.folderDataList.unshift({
          id: 'inbox',
          name: this.isGroup ? this.translate('all', 'presetFilters', 'Email') : this.translate('inbox', 'presetFilters', 'Email'),
          iconClass: this.isGroup ? iconMap[this.FOLDER_ALL] : iconMap[this.FOLDER_INBOX]
        });
        if (!this.noArchive) {
          this.folderDataList.push({
            id: this.FOLDER_ARCHIVE,
            name: this.translate('archive', 'presetFilters', 'Email'),
            iconClass: iconMap[this.FOLDER_ARCHIVE],
            disabled: this.currentFolderId === this.FOLDER_ARCHIVE
          });
        }
      }));
    }
  }
  _exports.default = _default;
});

define("views/email-filter/record/list", ["exports", "views/record/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _list.default {
    massActionList = ['remove', 'export'];
  }
  _exports.default = _default;
});

define("views/email-filter/modals/edit", ["exports", "views/modals/edit"], function (_exports, _edit) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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" wo
   * rd.
   ************************************************************************/

  class _default extends _edit.default {
    fullFormDisabled = true;
  }
  _exports.default = _default;
});

define("views/email-filter/fields/parent", ["exports", "views/fields/link-parent"], function (_exports, _linkParent) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkParent = _interopRequireDefault(_linkParent);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _linkParent.default {
    getSelectPrimaryFilterName() {
      const map = {
        'User': 'active'
      };
      if (!this.foreignScope) {
        return;
      }
      return map[this.foreignScope];
    }
  }
  _exports.default = _default;
});

define("views/email-filter/fields/email-folder", ["exports", "views/fields/link"], function (_exports, _link) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _link = _interopRequireDefault(_link);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _link.default {
    createDisabled = true;
    autocompleteDisabled = true;
    getSelectFilters() {
      if (this.getUser().isAdmin() && this.model.get('parentType') === 'User' && this.model.get('parentId')) {
        return {
          assignedUser: {
            type: 'equals',
            attribute: 'assignedUserId',
            value: this.model.get('parentId'),
            data: {
              nameValue: this.model.get('parentName')
            }
          }
        };
      }
    }
  }
  _exports.default = _default;
});

define("views/email/list", ["exports", "views/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailListView extends _list.default {
    createButton = false;
    template = 'email/list';
    folderId = null;
    folderScope = 'EmailFolder';
    selectedFolderId = null;
    defaultFolderId = 'inbox';
    keepCurrentRootUrl = true;
    stickableTop = null;

    /** @const */
    FOLDER_ALL = 'all';
    /** @const */
    FOLDER_INBOX = 'inbox';
    /** @const */
    FOLDER_IMPORTANT = 'important';
    /** @const */
    FOLDER_SENT = 'sent';
    /** @const */
    FOLDER_DRAFTS = 'drafts';
    /** @const */
    FOLDER_TRASH = 'trash';
    /** @const */
    FOLDER_ARCHIVE = 'archive';
    noDropFolderIdList = ['sent', 'drafts'];

    /** @inheritDoc */
    createListRecordView(fetch) {
      return super.createListRecordView(fetch).then(view => {
        this.listenTo(view, 'after:render', () => this.initDraggable(null));
        this.listenTo(view, 'after:show-more', fromIndex => this.initDraggable(fromIndex));
      });
    }

    /**
     * @private
     */
    initDroppable() {
      // noinspection JSUnresolvedReference
      this.$el.find('.folders-container .folder-list > .droppable').droppable({
        accept: '.list-row',
        tolerance: 'pointer',
        over: e => {
          if (!this.isDroppable(e)) {
            return;
          }
          const $target = $(e.target);
          $target.removeClass('success');
          $target.addClass('active');
          $target.find('a').css('pointer-events', 'none');
        },
        out: e => {
          if (!this.isDroppable(e)) {
            return;
          }
          const $target = $(e.target);
          $target.removeClass('active');
          $target.find('a').css('pointer-events', '');
        },
        drop: (e, ui) => {
          if (!this.isDroppable(e)) {
            return;
          }
          const $target = $(e.target);
          const $helper = $(ui.helper);
          $target.find('a').css('pointer-events', '');
          const folderId = $target.attr('data-id');
          let id = $helper.attr('data-id');
          id = id === '' ? true : id;
          this.onDrop(folderId, id);
          $target.removeClass('active');
          $target.addClass('success');
          setTimeout(() => {
            $target.removeClass('success');
          }, 1000);
        }
      });
    }

    /**
     * @private
     * @param {?Number} fromIndex
     */
    initDraggable(fromIndex) {
      fromIndex = fromIndex || 0;
      const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
      if (isTouchDevice) {
        return;
      }
      const $container = this.$el.find('.list-container > .list');
      const recordView = this.getEmailRecordView();
      this.collection.models.slice(fromIndex).forEach(m => {
        const $row = $container.find(`.list-row[data-id="${m.id}"]`).first();

        // noinspection JSUnresolvedReference,JSUnusedGlobalSymbols
        $row.draggable({
          cancel: 'input,textarea,button,select,option,.dropdown-menu',
          helper: () => {
            let text = this.translate('Moving to Folder', 'labels', 'Email');
            if (recordView.isIdChecked(m.id) && !recordView.allResultIsChecked && recordView.checkedList.length > 1) {
              text += ' · ' + recordView.checkedList.length;
            }
            let draggedId = m.id;
            if (recordView.isIdChecked(m.id) && !recordView.allResultIsChecked) {
              draggedId = '';
            }
            return $('<div>').attr('data-id', draggedId).css('cursor', 'grabbing').addClass('draggable-helper').text(text);
          },
          distance: 8,
          containment: this.$el,
          appendTo: 'body',
          cursor: 'grabbing',
          cursorAt: {
            top: 0,
            left: 0
          },
          drag: () => {
            if (recordView.allResultIsChecked) {
              return false;
            }
          },
          start: e => {
            if (recordView.allResultIsChecked) {
              return;
            }
            const $target = $(e.target);
            $target.closest('tr').addClass('active');
          },
          stop: () => {
            if (!recordView.isIdChecked(m.id)) {
              $container.find(`.list-row[data-id="${m.id}"]`).first().removeClass('active');
            }
          }
        });
      });
    }
    isDroppable(e) {
      const $target = $(e.target);
      const folderId = $target.attr('data-id');
      if (this.selectedFolderId === this.FOLDER_DRAFTS) {
        return false;
      }
      if (this.selectedFolderId === this.FOLDER_SENT && folderId === this.FOLDER_INBOX) {
        return false;
      }
      if (this.selectedFolderId === this.FOLDER_ALL) {
        if (folderId.indexOf('group:') === 0) {
          return true;
        }
        return false;
      }
      if (folderId === this.FOLDER_ALL) {
        if (this.selectedFolderId.indexOf('group:') === 0) {
          return true;
        }
        return false;
      }
      if (this.selectedFolderId === this.FOLDER_DRAFTS) {
        if (folderId.indexOf('group:') === 0) {
          return true;
        }
        if (folderId === this.FOLDER_TRASH || folderId === this.FOLDER_ARCHIVE) {
          return false;
        }
        return true;
      }
      if (this.selectedFolderId.indexOf('group:') === 0) {
        if ([this.FOLDER_ALL, this.FOLDER_ARCHIVE, this.FOLDER_TRASH].includes(folderId) || folderId.startsWith('group:')) {
          return true;
        }
        return false;
      }
      return true;
    }
    setup() {
      super.setup();
      this.addMenuItem('dropdown', false);
      if (this.getAcl().checkScope('EmailAccountScope')) {
        this.addMenuItem('dropdown', {
          name: 'reply',
          label: 'Email Accounts',
          link: '#EmailAccount/list/userId=' + this.getUser().id + '&userName=' + encodeURIComponent(this.getUser().get('name'))
        });
      }
      if (!this.getAcl().checkScope('Import')) {
        this.hideHeaderActionItem('archiveEmail');
      }
      if (this.getUser().isAdmin()) {
        this.addMenuItem('dropdown', {
          link: '#InboundEmail',
          label: 'Inbound Emails'
        });
      }
      this.foldersDisabled = this.foldersDisabled || this.getConfig().get('emailFoldersDisabled') || this.getMetadata().get(['scopes', this.folderScope, 'disabled']) || !this.getAcl().checkScope(this.folderScope);
      const params = this.options.params || {};
      this.selectedFolderId = params.folder || this.defaultFolderId;
      if (this.foldersDisabled) {
        this.selectedFolderId = null;
      }
      this.applyFolder();
      this.initEmailShortcuts();
      this.on('remove', () => {
        $(window).off('resize.email-folders');
        $(window).off('scroll.email-folders');
      });
    }
    data() {
      const data = {};
      data.foldersDisabled = this.foldersDisabled;
      return data;
    }

    /** @inheritDoc */
    createSearchView() {
      /** @type {Promise<module:view>} */
      const promise = super.createSearchView();
      promise.then(view => {
        this.listenTo(view, 'update-ui', () => {
          this.stickableTop = null;
          setTimeout(() => {
            $(window).trigger('scroll');

            // If search fields are not yet rendered, the value may be wrong.
            this.stickableTop = null;
          }, 100);
        });
      });
      return promise;
    }
    initEmailShortcuts() {
      this.shortcutKeys['Control+Delete'] = e => {
        if (!this.hasSelectedRecords()) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        this.getEmailRecordView().massActionMoveToTrash();
      };
      this.shortcutKeys['Control+Backspace'] = e => {
        if (!this.hasSelectedRecords()) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        this.getEmailRecordView().massActionMoveToArchive();
      };
      this.shortcutKeys['Control+KeyI'] = e => {
        if (!this.hasSelectedRecords()) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        this.getEmailRecordView().toggleMassMarkAsImportant();
      };
      this.shortcutKeys['Control+KeyM'] = e => {
        if (!this.hasSelectedRecords()) {
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        this.getEmailRecordView().massActionMoveToFolder();
      };
      this.shortcutKeys['Control+KeyQ'] = e => {
        e.preventDefault();
        e.stopPropagation();
        if (this.hasSelectedRecords()) {
          this.getEmailRecordView().massActionMarkAsRead();
          return;
        }
        this.getEmailRecordView().actionMarkAllAsRead();
      };
    }
    hasSelectedRecords() {
      const recordView = this.getEmailRecordView();
      return recordView.checkedList && recordView.checkedList.length && !recordView.allResultIsChecked;
    }

    /** @inheritDoc */
    setupReuse(params) {
      this.applyRoutingParams(params);
      this.initDroppable();
      this.initStickableFolders();
      const recordView = /** @type {import('views/email/record/list').default} */this.getRecordView();
      recordView.removeQueuedRecord();
    }

    /**
     * @param {Object.<string,*>} [data]
     */
    actionComposeEmail(data) {
      data = data || {};
      Espo.Ui.notifyWait();
      const viewName = this.getMetadata().get('clientDefs.Email.modalViews.compose') || 'views/modals/compose-email';
      const options = {
        attributes: {
          status: 'Draft'
        },
        focusForCreate: data.focusForCreate
      };
      this.createView('quickCreate', viewName, options, view => {
        view.render();
        view.notify(false);
        this.listenToOnce(view, 'after:save', () => {
          this.collection.fetch();
        });
      });
    }
    afterRender() {
      super.afterRender();
      if (!this.foldersDisabled && !this.hasView('folders')) {
        this.loadFolders();
      }
    }

    /**
     * @private
     * @param {function(import('collection').default)} callback
     */
    getFolderCollection(callback) {
      this.getCollectionFactory().create(this.folderScope, collection => {
        collection.url = 'EmailFolder/action/listAll';
        collection.maxSize = 200;
        this.listenToOnce(collection, 'sync', () => {
          callback.call(this, collection);
        });
        collection.fetch();
      });
    }
    loadFolders() {
      let xhr = null;
      const auxFolderList = [this.FOLDER_TRASH, this.FOLDER_DRAFTS, this.FOLDER_ALL, this.FOLDER_INBOX, this.FOLDER_IMPORTANT, this.FOLDER_SENT, this.FOLDER_ARCHIVE];
      const iconMap = {
        [this.FOLDER_TRASH]: 'far fa-trash-alt',
        [this.FOLDER_SENT]: 'far fa-paper-plane',
        [this.FOLDER_INBOX]: 'fas fa-inbox',
        [this.FOLDER_ARCHIVE]: 'far fa-caret-square-down',
        [this.FOLDER_DRAFTS]: 'far fa-file',
        [this.FOLDER_IMPORTANT]: 'far fa-star'
      };
      this.getFolderCollection(collection => {
        collection.forEach((model, i) => {
          if (this.noDropFolderIdList.indexOf(model.id) === -1) {
            model.droppable = true;
          }
          if (model.id === this.FOLDER_INBOX) {
            model.groupStart = true;
          } else if (model.id === this.FOLDER_ARCHIVE || model.id === this.FOLDER_TRASH && !collection.models.find(m => m.id === this.FOLDER_ARCHIVE)) {
            model.groupStart = true;
          } else if (model.id.indexOf('group:') === 0 && collection.models.findIndex(m => m.id.indexOf('group:') === 0) === i) {
            model.groupStart = true;
          }
          model.iconClass = iconMap[model.id];
          if (model.id.indexOf('group:') === 0) {
            model.title = this.translate('groupFolder', 'fields', 'Email');
            model.iconClass = 'far fa-circle';
          } else if (auxFolderList.indexOf(model.id) === -1) {
            model.title = this.translate('folder', 'fields', 'Email');
            model.iconClass = 'far fa-folder';
          }
        });
        this.createView('folders', 'views/email-folder/list-side', {
          collection: collection,
          emailCollection: this.collection,
          selector: '.folders-container',
          showEditLink: this.getAcl().check(this.folderScope, 'edit'),
          selectedFolderId: this.selectedFolderId
        }, view => {
          view.render().then(() => this.initDroppable()).then(() => this.initStickableFolders());
          this.listenTo(view, 'select', id => {
            this.selectedFolderId = id;
            this.applyFolder();
            if (xhr && xhr.readyState < 4) {
              xhr.abort();
            }
            Espo.Ui.notifyWait();
            this.collection.offset = 0;
            xhr = this.collection.fetch().then(() => Espo.Ui.notify(false));
            if (id !== this.defaultFolderId) {
              this.getRouter().navigate(`#Email/list/folder=${id}`);
            } else {
              this.getRouter().navigate('#Email');
            }
            this.updateLastUrl();
          });
        });
      });
    }
    applyFolder() {
      this.rootData.selectedFolderId = this.selectedFolderId;
      this.collection.trigger('select-folder');
      if (!this.selectedFolderId) {
        this.collection.whereFunction = null;
        return;
      }
      this.collection.whereFunction = () => {
        return [{
          type: 'inFolder',
          attribute: 'folderId',
          value: this.selectedFolderId
        }];
      };
    }

    /**
     * @protected
     * @return {import('views/email-folder/list-side').default}
     */
    getFoldersView() {
      // noinspection JSValidateTypes
      return this.getView('folders');
    }
    applyRoutingParams(params) {
      let id;
      if ('folder' in params) {
        id = params.folder || 'inbox';
      } else {
        return;
      }
      if (!params.isReturnThroughLink && id !== this.selectedFolderId) {
        const foldersView = this.getFoldersView();
        if (foldersView) {
          foldersView.actionSelectFolder(id);
          foldersView.reRender();
          $(window).scrollTop(0);
        }
      }
    }
    onDrop(folderId, id) {
      const recordView = this.getEmailRecordView();
      if (folderId === this.FOLDER_IMPORTANT) {
        setTimeout(() => {
          id === true ? recordView.massActionMarkAsImportant() : recordView.actionMarkAsImportant({
            id: id
          });
        }, 10);
        return;
      }
      if (this.selectedFolderId === this.FOLDER_TRASH) {
        if (folderId === this.FOLDER_TRASH) {
          return;
        }
        id === true ? recordView.massRetrieveFromTrashMoveToFolder(folderId) : recordView.actionRetrieveFromTrashMoveToFolder({
          id: id,
          folderId: folderId
        });
        return;
      }
      if (folderId === this.FOLDER_TRASH) {
        id === true ? recordView.massActionMoveToTrash() : recordView.actionMoveToTrash({
          id: id
        });
        return;
      }
      if (this.selectedFolderId.indexOf('group:') === 0 && folderId === this.FOLDER_ALL) {
        folderId = this.FOLDER_INBOX;
      }
      id === true ? recordView.massMoveToFolder(folderId) : recordView.actionMoveToFolder({
        id: id,
        folderId: folderId
      });
    }

    /**
     * @protected
     * @return {module:views/email/record/list}
     */
    getEmailRecordView() {
      return /** @type {module:views/email/record/list} */this.getRecordView();
    }

    /**
     * @private
     */
    initStickableFolders() {
      const $window = $(window);
      const $list = this.$el.find('.list-container');
      const $container = this.$el.find('.folders-container');
      const $left = this.$el.find('.left-container').first();
      const screenWidthXs = this.getThemeManager().getParam('screenWidthXs');
      const isSmallScreen = $(window.document).width() < screenWidthXs;
      const factor = this.getThemeManager().getFontSizeFactor();
      const offset = this.getThemeManager().getParam('navbarHeight') * factor + (this.getThemeManager().getParam('buttonsContainerHeight') || 47) * factor;
      const bottomSpaceHeight = parseInt(window.getComputedStyle($('#content').get(0)).paddingBottom, 10);
      const getOffsetTop = (/** JQuery */$element) => {
        let element = /** @type {HTMLElement} */$element.get(0);
        let value = 0;
        while (element) {
          value += !isNaN(element.offsetTop) ? element.offsetTop : 0;
          element = element.offsetParent;
        }
        if (isSmallScreen) {
          return value;
        }
        return value - offset;
      };
      this.stickableTop = getOffsetTop($list);
      const control = () => {
        let start = this.stickableTop;
        if (start === null) {
          start = this.stickableTop = getOffsetTop($list);
        }
        const scrollTop = $window.scrollTop();
        if (scrollTop <= start || isSmallScreen) {
          $container.removeClass('sticked').width('').scrollTop(0);
          $container.css({
            maxHeight: ''
          });
          return;
        }
        if (scrollTop > start) {
          const scroll = $window.scrollTop() - start;
          $container.addClass('sticked').width($left.outerWidth(true)).scrollTop(scroll);
          const topStickPosition = parseInt(window.getComputedStyle($container.get(0)).top);
          const maxHeight = $window.height() - topStickPosition - bottomSpaceHeight;
          $container.css({
            maxHeight: maxHeight
          });
        }
      };
      $window.on('resize.email-folders', () => control());
      $window.on('scroll.email-folders', () => control());
    }

    /**
     * @protected
     * @param {KeyboardEvent} e
     */
    handleShortcutKeyCtrlSpace(e) {
      if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT') {
        return;
      }
      if (!this.getAcl().checkScope(this.scope, 'create')) {
        return;
      }
      e.preventDefault();
      e.stopPropagation();
      this.actionComposeEmail({
        focusForCreate: true
      });
    }
  }
  var _default = _exports.default = EmailListView;
});

define("views/email/record/list-related", ["exports", "views/record/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _list.default {
    massActionList = ['remove', 'massUpdate'];
  }
  _exports.default = _default;
});

define("views/email/record/list-expanded", ["exports", "views/record/list-expanded", "views/email/record/list"], function (_exports, _listExpanded, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _listExpanded = _interopRequireDefault(_listExpanded);
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _listExpanded.default {
    // noinspection JSUnusedGlobalSymbols
    actionMarkAsImportant(data) {
      _list.default.prototype.actionMarkAsImportant.call(this, data);
    }

    // noinspection JSUnusedGlobalSymbols
    actionMarkAsNotImportant(data) {
      _list.default.prototype.actionMarkAsNotImportant.call(this, data);
    }

    // noinspection JSUnusedGlobalSymbols
    actionMoveToTrash(data) {
      _list.default.prototype.actionMoveToTrash.call(this, data);
    }
  }
  _exports.default = _default;
});

define("views/email/record/edit-quick", ["exports", "views/email/record/edit"], function (_exports, _edit) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _edit.default {
    isWide = true;
    sideView = false;
  }
  _exports.default = _default;
});

define("views/email/record/detail-side", ["exports", "views/record/detail-side"], function (_exports, _detailSide) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detailSide = _interopRequireDefault(_detailSide);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _detailSide.default {}
  _exports.default = _default;
});

define("views/email/record/detail-quick", ["exports", "views/email/record/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _detail.default {
    isWide = true;
    sideView = false;
  }
  _exports.default = _default;
});

define("views/email/record/compose", ["exports", "views/record/edit", "views/email/record/detail"], function (_exports, _edit, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _edit = _interopRequireDefault(_edit);
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 views/email/record/compose */

  class EmailComposeRecordView extends _edit.default {
    isWide = true;
    sideView = false;

    /**
     * @private
     * @type {string|null}
     */
    initialBody;

    /**
     * @private
     * @type {boolean|null}
     */
    initialIsHtml;
    setupBeforeFinal() {
      super.setupBeforeFinal();
      this.initialBody = this.model.attributes.body;
      this.initialIsHtml = this.model.attributes.isHtml;
      if (!this.model.attributes.isHtml && this.getPreferences().get('emailReplyForceHtml')) {
        this.initialBody = (this.initialBody || '').replace(/\n/g, '<br>') || null;
        this.initialIsHtml = true;
      }
      let body = this.initialBody;
      if (!this.options.signatureDisabled && this.hasSignature()) {
        let addSignatureMethod = 'prependSignature';
        if (this.options.appendSignature) {
          addSignatureMethod = 'appendSignature';
        }
        body = this[addSignatureMethod](body || '', this.initialIsHtml) || null;
      }
      this.model.set('body', body, {
        silent: true
      });
      this.model.set('isHtml', this.initialIsHtml, {
        silent: true
      });
    }
    setup() {
      super.setup();
      this.isBodyChanged = false;
      this.listenTo(this.model, 'change:body', () => {
        this.isBodyChanged = true;
      });
      if (!this.options.removeAttachmentsOnSelectTemplate) {
        this.initialAttachmentsIds = this.model.get('attachmentsIds') || [];
        this.initialAttachmentsNames = this.model.get('attachmentsNames') || {};
      }
      this.initInsertTemplate();
      if (this.options.selectTemplateDisabled) {
        this.hideField('selectTemplate');
      }
    }
    initInsertTemplate() {
      this.listenTo(this.model, 'insert-template', data => {
        const body = this.model.get('body') || '';
        let bodyPlain = body.replace(/<br\s*\/?>/mg, '');
        bodyPlain = bodyPlain.replace(/<\/p\s*\/?>/mg, '');
        bodyPlain = bodyPlain.replace(/ /g, '');
        bodyPlain = bodyPlain.replace(/\n/g, '');
        const $div = $('<div>').html(bodyPlain);
        bodyPlain = $div.text();
        if (bodyPlain !== '' && this.isBodyChanged) {
          this.confirm({
            message: this.translate('confirmInsertTemplate', 'messages', 'Email'),
            confirmText: this.translate('Yes')
          }).then(() => this.insertTemplate(data));
          return;
        }
        this.insertTemplate(data);
      });
    }
    insertTemplate(data) {
      let body = data.body;
      if (this.hasSignature()) {
        body = this.appendSignature(body || '', data.isHtml);
      }
      if (this.initialBody && !this.isBodyChanged) {
        let initialBody = this.initialBody;
        if (data.isHtml !== this.initialIsHtml) {
          if (data.isHtml) {
            initialBody = this.plainToHtml(initialBody);
          } else {
            initialBody = this.htmlToPlain(initialBody);
          }
        }
        body += initialBody;
      }
      this.model.set('isHtml', data.isHtml);
      if (data.subject) {
        this.model.set('name', data.subject);
      }
      this.model.set('body', '');
      this.model.set('body', body);
      if (!this.options.removeAttachmentsOnSelectTemplate) {
        this.initialAttachmentsIds.forEach(id => {
          if (data.attachmentsIds) {
            data.attachmentsIds.push(id);
          }
          if (data.attachmentsNames) {
            data.attachmentsNames[id] = this.initialAttachmentsNames[id] || id;
          }
        });
      }
      this.model.set({
        attachmentsIds: data.attachmentsIds,
        attachmentsNames: data.attachmentsNames
      });
      this.isBodyChanged = false;
    }
    prependSignature(body, isHtml) {
      if (isHtml) {
        let signature = this.getSignature();
        if (body) {
          signature += '';
        }
        return '<p><br></p>' + signature + body;
      }
      let signature = this.getPlainTextSignature();
      if (body) {
        signature += '\n';
      }
      return '\n\n' + signature + body;
    }
    appendSignature(body, isHtml) {
      if (isHtml) {
        const signature = this.getSignature();
        return body + '' + signature;
      }
      const signature = this.getPlainTextSignature();
      return body + '\n\n' + signature;
    }
    hasSignature() {
      return !!this.getPreferences().get('signature');
    }
    getSignature() {
      return this.getPreferences().get('signature') || '';
    }
    getPlainTextSignature() {
      let value = this.getSignature().replace(/<br\s*\/?>/mg, '\n');
      value = $('<div>').html(value).text();
      return value;
    }
    afterSave() {
      super.afterSave();
      if (this.isSending && this.model.get('status') === 'Sent') {
        Espo.Ui.success(this.translate('emailSent', 'messages', 'Email'));
      }
    }
    send() {
      _detail.default.prototype.send.call(this);
    }

    /**
     * @param {module:views/record/base~saveOptions} [options] Options.
     * @return {Promise}
     */
    saveDraft(options) {
      const model = this.model;
      model.set('status', 'Draft');
      const subjectView = this.getFieldView('subject');
      if (subjectView) {
        subjectView.fetchToModel();
        if (!model.get('name')) {
          model.set('name', this.translate('No Subject', 'labels', 'Email'));
        }
      }
      return this.save(options);
    }
    htmlToPlain(text) {
      text = text || '';
      let value = text.replace(/<br\s*\/?>/mg, '\n');
      value = value.replace(/<\/p\s*\/?>/mg, '\n\n');
      const $div = $('<div>').html(value);
      $div.find('style').remove();
      $div.find('link[ref="stylesheet"]').remove();
      value = $div.text();
      return value;
    }
    plainToHtml(html) {
      html = html || '';
      return html.replace(/\n/g, '<br>');
    }

    // noinspection JSUnusedGlobalSymbols
    errorHandlerSendingFail(data) {
      _detail.default.prototype.errorHandlerSendingFail.call(this, data);
    }
    focusForCreate() {
      if (!this.model.get('to')) {
        this.$el.find('.field[data-name="to"] input').focus();
        return;
      }
      if (!this.model.get('subject')) {
        this.$el.find('.field[data-name="subject"] input').focus();
        return;
      }
      if (this.model.get('isHtml')) {
        const $div = this.$el.find('.field[data-name="body"] .note-editable');
        if (!$div.length) {
          return;
        }
        $div.focus();
        return;
      }
      this.$el.find('.field[data-name="body"] textarea').prop('selectionEnd', 0).focus();
    }
  }
  var _default = _exports.default = EmailComposeRecordView;
});

define("views/email/record/row-actions/default", ["exports", "views/record/row-actions/default"], function (_exports, _default2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _default2 = _interopRequireDefault(_default2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailDefaultRowActionView extends _default2.default {
    setup() {
      super.setup();
      this.listenTo(this.model, 'change:isImportant change:inTrash change:groupStatusFolder', () => {
        setTimeout(() => this.reRender(), 10);
      });
    }
    getActionList() {
      /** @type {module:views/record/list~rowAction[]} */
      let list = [{
        action: 'quickView',
        label: 'View',
        data: {
          id: this.model.id
        },
        groupIndex: 0
      }];
      if (this.model.get('createdById') === this.getUser().id && this.model.get('status') === 'Draft' && !this.model.attributes.inTrash) {
        list.push({
          action: 'send',
          label: 'Send',
          data: {
            id: this.model.id
          }
        });
      }
      if (this.options.acl.edit) {
        list = list.concat([{
          action: 'quickEdit',
          label: 'Edit',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        }]);
      }
      if (this.model.get('isUsers')) {
        if (!this.model.get('isImportant')) {
          if (!this.model.get('inTrash')) {
            list.push({
              action: 'markAsImportant',
              label: 'Mark as Important',
              data: {
                id: this.model.id
              },
              groupIndex: 1
            });
          }
        } else {
          list.push({
            action: 'markAsNotImportant',
            label: 'Unmark Importance',
            data: {
              id: this.model.id
            },
            groupIndex: 1
          });
        }
      }
      if (this.model.attributes.isUsers && !this.model.attributes.isRead) {
        list.push({
          action: 'markAsRead',
          label: 'Mark Read',
          data: {
            id: this.model.id
          },
          groupIndex: 1
        });
      }
      if (this.model.attributes.isUsers && this.model.attributes.status !== 'Draft' || this.model.attributes.groupFolderId) {
        const inTrash = this.model.attributes.groupFolderId ? this.model.attributes.groupStatusFolder === 'Trash' : this.model.attributes.inTrash;
        const inArchive = this.model.attributes.groupFolderId ? this.model.attributes.groupStatusFolder === 'Archive' : this.model.attributes.inArchive;
        if (!inTrash) {
          list.push({
            action: 'moveToTrash',
            label: 'Move to Trash',
            data: {
              id: this.model.id
            },
            groupIndex: 2
          });
        } else {
          list.push({
            action: 'retrieveFromTrash',
            label: 'Retrieve from Trash',
            data: {
              id: this.model.id
            },
            groupIndex: 2
          });
        }
        if (!inArchive) {
          list.push({
            action: 'moveToArchive',
            text: this.getLanguage().translatePath('Email.actions.moveToArchive'),
            data: {
              id: this.model.id
            },
            groupIndex: 2
          });
        }
        list.push({
          action: 'moveToFolder',
          label: 'Move to Folder',
          data: {
            id: this.model.id
          },
          groupIndex: 2
        });
      } else if (!this.model.attributes.isUsers && !this.model.attributes.groupFolderId && this.model.attributes.status === 'Archived') {
        list.push({
          action: 'moveToFolder',
          label: 'Move to Folder',
          data: {
            id: this.model.id
          },
          groupIndex: 2
        });
      }
      if (this.options.acl.delete) {
        list.push({
          action: 'quickRemove',
          label: 'Remove',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        });
      }
      return list;
    }
  }
  var _default = _exports.default = EmailDefaultRowActionView;
});

define("views/email/record/row-actions/dashlet", ["exports", "views/record/row-actions/default"], function (_exports, _default2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _default2 = _interopRequireDefault(_default2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _default2.default {
    setup() {
      super.setup();
      this.listenTo(this.model, 'change:isImportant', () => {
        setTimeout(() => this.reRender(), 10);
      });
    }
    getActionList() {
      let list = [{
        action: 'quickView',
        label: 'View',
        data: {
          id: this.model.id
        },
        groupIndex: 0
      }];
      if (this.options.acl.edit) {
        list = list.concat([{
          action: 'quickEdit',
          label: 'Edit',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        }]);
      }
      if (this.model.get('isUsers') && this.model.get('status') !== 'Draft') {
        if (!this.model.get('inTrash')) {
          list.push({
            action: 'moveToTrash',
            label: 'Move to Trash',
            data: {
              id: this.model.id
            },
            groupIndex: 1
          });
        } else {
          list.push({
            action: 'retrieveFromTrash',
            label: 'Retrieve from Trash',
            data: {
              id: this.model.id
            },
            groupIndex: 1
          });
        }
      }
      if (this.getAcl().checkModel(this.model, 'delete')) {
        list.push({
          action: 'quickRemove',
          label: 'Remove',
          data: {
            id: this.model.id
          },
          groupIndex: 0
        });
      }
      if (this.model.get('isUsers')) {
        if (!this.model.get('isImportant')) {
          list.push({
            action: 'markAsImportant',
            label: 'Mark as Important',
            data: {
              id: this.model.id
            },
            groupIndex: 1
          });
        } else {
          list.push({
            action: 'markAsNotImportant',
            label: 'Unmark Importance',
            data: {
              id: this.model.id
            },
            groupIndex: 1
          });
        }
      }
      return list;
    }
  }
  _exports.default = _default;
});

define("views/email/record/panels/event", ["exports", "views/record/panels/side"], function (_exports, _side) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _side = _interopRequireDefault(_side);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _side.default {
    setupFields() {
      super.setupFields();
      this.fieldList.push({
        name: 'icsEventDateStart',
        readOnly: true,
        labelText: this.translate('dateStart', 'fields', 'Meeting')
      });
      this.fieldList.push({
        name: 'createdEvent',
        readOnly: true
      });
      this.fieldList.push({
        name: 'createEvent',
        readOnly: true,
        noLabel: true
      });
      this.controlEventField();
      this.listenTo(this.model, 'change:icsEventData', this.controlEventField, this);
      this.listenTo(this.model, 'change:createdEventId', this.controlEventField, this);
    }

    /**
     * @private
     */
    controlEventField() {
      if (!this.model.get('icsEventData')) {
        this.recordViewObject.hideField('createEvent');
        this.recordViewObject.showField('createdEvent');
        return;
      }
      const eventData = this.model.get('icsEventData');
      if (eventData.createdEvent) {
        this.recordViewObject.hideField('createEvent');
        this.recordViewObject.showField('createdEvent');
        return;
      }
      if (!this.model.get('createdEventId')) {
        this.recordViewObject.hideField('createdEvent');
        this.recordViewObject.showField('createEvent');
        return;
      }
      this.recordViewObject.hideField('createEvent');
      this.recordViewObject.showField('createdEvent');
    }
  }
  _exports.default = _default;
});

define("views/email/record/panels/default-side", ["exports", "views/record/panels/default-side"], function (_exports, _defaultSide) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _defaultSide = _interopRequireDefault(_defaultSide);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _defaultSide.default {
    setupFields() {
      super.setupFields();
      this.fieldList.push({
        name: 'hasAttachment',
        view: 'views/email/fields/has-attachment',
        noLabel: true
      });
      this.controlHasAttachmentField();
      this.listenTo(this.model, 'change:hasAttachment', () => this.controlHasAttachmentField());
    }

    /**
     * @private
     */
    controlHasAttachmentField() {
      if (this.model.get('hasAttachment')) {
        this.recordViewObject.showField('hasAttachment');
        return;
      }
      this.recordViewObject.hideField('hasAttachment');
    }
  }
  _exports.default = _default;
});

define("views/email/modals/insert-field", ["exports", "views/modal", "helpers/misc/field-language"], function (_exports, _modal, _fieldLanguage) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _fieldLanguage = _interopRequireDefault(_fieldLanguage);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _modal.default {
    backdrop = true;

    // language=Handlebars
    templateContent = `
        {{#each viewObject.dataList}}
            <div class="margin-bottom">
            <h5>{{label}}: {{translate entityType category='scopeNames'}}</h5>
            </div>
            <ul class="list-group no-side-margin">
                {{#each dataList}}
                    <li class="list-group-item clearfix">
                        <a role="button"
                            data-action="insert" class="text-bold" data-name="{{name}}" data-type="{{../type}}">
                            {{label}}
                        </a>

                        <div class="pull-right"
                            style="width: 50%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;">
                            {{valuePreview}}
                        </div>
                    </li>
                {{/each}}
            </ul>
        {{/each}}

        {{#unless viewObject.dataList.length}}
            {{translate 'No Data'}}
        {{/unless}}
    `;
    setup() {
      super.setup();
      this.headerText = this.translate('Insert Field', 'labels', 'Email');
      this.fieldLanguage = new _fieldLanguage.default(this.getMetadata(), this.getLanguage());
      this.wait(Espo.Ajax.getRequest('Email/insertFieldData', {
        parentId: this.options.parentId,
        parentType: this.options.parentType,
        to: this.options.to
      }).then(fetchedData => {
        this.fetchedData = fetchedData;
        this.prepareData();
      }));
      this.addActionHandler('insert', (e, target) => {
        const name = target.dataset.name;
        const type = target.dataset.type;
        this.insert(type, name);
      });
    }
    prepareData() {
      this.dataList = [];
      const fetchedData = this.fetchedData;
      const typeList = ['parent', 'to'];
      typeList.forEach(type => {
        if (!fetchedData[type]) {
          return;
        }
        const entityType = fetchedData[type].entityType;
        const id = fetchedData[type].id;
        for (const it of this.dataList) {
          if (it.id === id && it.entityType === entityType) {
            return;
          }
        }
        const dataList = this.prepareDisplayValueList(fetchedData[type].entityType, fetchedData[type].values);
        if (!dataList.length) {
          return;
        }
        this.dataList.push({
          type: type,
          entityType: entityType,
          id: id,
          name: fetchedData[type].name,
          dataList: dataList,
          label: this.translate(type, 'fields', 'Email')
        });
      });
    }
    prepareDisplayValueList(scope, values) {
      const list = [];
      let attributeList = Object.keys(values);
      const labels = {};
      attributeList.forEach(item => {
        labels[item] = this.fieldLanguage.translateAttribute(scope, item);
      });
      attributeList = attributeList.sort((v1, v2) => {
        return labels[v1].localeCompare(labels[v2]);
      });
      const ignoreAttributeList = ['id', 'modifiedAt', 'modifiedByName'];
      const fm = this.getFieldManager();
      fm.getEntityTypeFieldList(scope).forEach(field => {
        const type = this.getMetadata().get(['entityDefs', scope, 'fields', field, 'type']);
        if (['link', 'linkOne', 'image', 'filed', 'linkParent'].includes(type)) {
          ignoreAttributeList.push(field + 'Id');
        }
        if (type === 'linkParent') {
          ignoreAttributeList.push(field + 'Type');
        }
      });
      attributeList.forEach(item => {
        if (~ignoreAttributeList.indexOf(item)) {
          return;
        }
        let value = values[item];
        if (value === null || value === '') {
          return;
        }
        if (typeof value == 'boolean') {
          return;
        }
        if (Array.isArray(value)) {
          for (const v in value) {
            if (typeof v !== 'string') {
              return;
            }
          }
          value = value.split(', ');
        }
        value = this.getHelper().sanitizeHtml(value);
        const valuePreview = value.replace(/<br( \/)?>/gm, ' ');

        // noinspection RegExpUnnecessaryNonCapturingGroup
        value = value.replace(/(?:\r\n|\r|\n)/g, '');
        value = value.replace(/<br( \/)?>/gm, '\n');
        list.push({
          name: item,
          label: labels[item],
          value: value,
          valuePreview: valuePreview
        });
      });
      return list;
    }

    /**
     * @private
     * @param {string} type
     * @param {string} name
     */
    insert(type, name) {
      for (const g of this.dataList) {
        if (g.type !== type) {
          continue;
        }
        for (const i of g.dataList) {
          if (i.name !== name) {
            continue;
          }
          this.trigger('insert', i.value);
          break;
        }
        break;
      }
      this.close();
    }
  }
  _exports.default = _default;
});

define("views/email/modals/detail", ["exports", "views/modals/detail", "views/email/detail"], function (_exports, _detail, _detail2) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  _detail2 = _interopRequireDefault(_detail2);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _detail.default {
    setup() {
      super.setup();
      this.addButton({
        name: 'reply',
        label: 'Reply',
        hidden: this.model && this.model.get('status') === 'Draft',
        style: 'danger',
        position: 'right'
      }, true);
      if (this.model) {
        this.listenToOnce(this.model, 'sync', () => {
          setTimeout(() => {
            this.model.set('isRead', true);
          }, 50);
        });
      }
    }
    controlRecordButtonsVisibility() {
      super.controlRecordButtonsVisibility();
      if (this.model.get('status') === 'Draft' || !this.getAcl().check('Email', 'create')) {
        this.hideActionItem('reply');
        return;
      }
      this.showActionItem('reply');
    }

    // noinspection JSUnusedGlobalSymbols
    actionReply(data, e) {
      _detail2.default.prototype.actionReply.call(this, {}, e, this.getPreferences().get('emailReplyToAllByDefault'));
    }
  }
  _exports.default = _default;
});

define("views/email/modals/body-plain", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _modal.default {
    backdrop = true;
    templateContent = `
        <div class="panel no-side-margin">
            <div class="panel-body">
                <div class="field" data-name="body-plain">{{{bodyPlain}}}</div>
            </div>

        </div>
    `;
    setup() {
      super.setup();
      this.buttonList.push({
        'name': 'cancel',
        'label': 'Close'
      });
      this.headerText = this.model.get('name');
      this.createView('bodyPlain', 'views/fields/text', {
        selector: '.field[data-name="bodyPlain"]',
        model: this.model,
        defs: {
          name: 'bodyPlain',
          params: {
            readOnly: true,
            inlineEditDisabled: true,
            displayRawText: true
          }
        }
      });
    }
  }
  _exports.default = _default;
});

define("views/email/modals/attachments", ["exports", "views/modal"], function (_exports, _modal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _modal.default {
    backdrop = true;
    templateContent = `<div class="record no-side-margin">{{{record}}}</div>`;
    setup() {
      super.setup();
      this.headerText = this.translate('attachments', 'fields', 'Email');
      this.createView('record', 'views/record/detail', {
        model: this.model,
        selector: '.record',
        readOnly: true,
        sideView: null,
        buttonsDisabled: true,
        detailLayout: [{
          rows: [[{
            name: 'attachments',
            noLabel: true
          }, false]]
        }]
      });
      if (!this.model.has('attachmentsIds')) {
        this.wait(this.model.fetch());
      }
    }
  }
  _exports.default = _default;
});

define("views/email/fields/subject", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailSubjectFieldView extends _varchar.default {
    listLinkTemplate = 'email/fields/subject/list-link';
    data() {
      const data = super.data();
      data.isRead = this.model.get('sentById') === this.getUser().id || this.model.get('isRead');
      data.isImportant = this.model.has('isImportant') && this.model.get('isImportant');
      data.hasAttachment = this.model.has('hasAttachment') && this.model.get('hasAttachment');
      data.isReplied = this.model.has('isReplied') && this.model.get('isReplied');
      data.inTrash = this.model.attributes.groupFolderId ? this.model.attributes.groupStatusFolder === 'Trash' : this.model.attributes.inTrash;
      data.inArchive = this.model.attributes.groupFolderId ? this.model.attributes.groupStatusFolder === 'Archive' : this.model.attributes.inArchive;
      data.style = null;
      if (data.isImportant) {
        data.style = 'warning';
      } else if (data.inTrash) {
        data.style = 'muted';
      } else if (data.inArchive) {
        data.style = 'info';
      }
      if (!data.isRead && !this.model.has('isRead')) {
        data.isRead = true;
      }
      if (!data.isNotEmpty) {
        if (this.model.get('name') !== null && this.model.get('name') !== '' && this.model.has('name')) {
          data.isNotEmpty = true;
        }
      }
      return data;
    }
    getValueForDisplay() {
      return this.model.get('name');
    }
    getAttributeList() {
      return ['name', 'subject', 'isRead', 'isImportant', 'hasAttachment', 'inTrash', 'groupStatusFolder'];
    }
    setup() {
      super.setup();
      this.events['click [data-action="showAttachments"]'] = e => {
        e.stopPropagation();
        this.showAttachments();
      };
      this.listenTo(this.model, 'change:isRead change:isImportant change:groupStatusFolder', () => {
        if (this.mode === this.MODE_LIST || this.mode === this.MODE_LIST_LINK) {
          this.reRender();
        }
      });
    }
    fetch() {
      const data = super.fetch();
      data.name = data.subject;
      return data;
    }
    showAttachments() {
      Espo.Ui.notifyWait();
      this.createView('dialog', 'views/email/modals/attachments', {
        model: this.model
      }).then(view => {
        view.render();
        Espo.Ui.notify(false);
      });
    }
  }
  var _default = _exports.default = EmailSubjectFieldView;
});

define("views/email/fields/select-template", ["exports", "views/fields/link"], function (_exports, _link) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _link = _interopRequireDefault(_link);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _link.default {
    editTemplate = 'email/fields/select-template/edit';
    foreignScope = 'EmailTemplate';
    setup() {
      super.setup();
      this.on('change', () => {
        const id = this.model.get(this.idName);
        if (id) {
          this.loadTemplate(id);
        }
      });
    }
    getSelectPrimaryFilterName() {
      return 'actual';
    }
    loadTemplate(id) {
      let to = this.model.get('to') || '';
      let emailAddress = null;
      to = to.trim();
      if (to) {
        emailAddress = to.split(';')[0].trim();
      }
      Espo.Ajax.postRequest(`EmailTemplate/${id}/prepare`, {
        emailAddress: emailAddress,
        parentType: this.model.get('parentType'),
        parentId: this.model.get('parentId'),
        relatedType: this.model.get('relatedType'),
        relatedId: this.model.get('relatedId')
      }).then(data => {
        this.model.trigger('insert-template', data);
        this.emptyField();
      }).catch(() => {
        this.emptyField();
      });
    }

    /**
     * @private
     */
    emptyField() {
      this.model.set(this.idName, null);
      this.model.set(this.nameName, null);
    }
  }
  _exports.default = _default;
});

define("views/email/fields/replies", ["exports", "views/fields/link-multiple"], function (_exports, _linkMultiple) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkMultiple = _interopRequireDefault(_linkMultiple);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _linkMultiple.default {
    getAttributeList() {
      const attributeList = super.getAttributeList();
      attributeList.push(this.name + 'Columns');
      return attributeList;
    }
    getDetailLinkHtml(id) {
      const html = super.getDetailLinkHtml(id);
      const columns = this.model.get(this.name + 'Columns') || {};
      const status = (columns[id] || {})['status'];
      return $('<div>').append($('<span>').addClass('fas fa-arrow-right fa-sm link-multiple-item-icon').addClass(status === 'Draft' ? 'text-warning' : 'text-success')).append(html).html();
    }
  }
  _exports.default = _default;
});

define("views/email/fields/replied", ["exports", "views/fields/link"], function (_exports, _link) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _link = _interopRequireDefault(_link);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _link.default {
    afterRender() {
      super.afterRender();
      if (this.mode === this.MODE_DETAIL) {
        const $a = this.$el.find('a');
        if ($a.get(0)) {
          $('<span class="fas fa-arrow-left fa-sm link-field-icon text-soft"></span>').insertBefore($a);
        }
      }
    }
  }
  _exports.default = _default;
});

define("views/email/fields/person-string-data-for-expanded", ["exports", "views/email/fields/person-string-data"], function (_exports, _personStringData) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _personStringData = _interopRequireDefault(_personStringData);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class _default extends _personStringData.default {
    listTemplate = 'email/fields/person-string-data/list-for-expanded';
  }
  _exports.default = _default;
});

define("views/email/fields/icon", ["exports", "views/fields/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _base.default {
    listTemplate = 'email/fields/icon/detail';
    detailTemplate = 'email/fields/icon/detail';
  }
  _exports.default = _default;
});

define("views/email/fields/has-attachment", ["exports", "views/fields/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _base.default {
    listTemplate = 'email/fields/has-attachment/detail';
    detailTemplate = 'email/fields/has-attachment/detail';
    data() {
      const data = super.data();
      data.isSmall = this.mode === this.MODE_LIST;
      return data;
    }
    setup() {
      super.setup();
      this.addActionHandler('show', e => {
        e.stopPropagation();
        this.show();
      });
    }

    /**
     * @private
     */
    show() {
      Espo.Ui.notifyWait();
      this.createView('dialog', 'views/email/modals/attachments', {
        model: this.model
      }).then(view => {
        view.render();
        Espo.Ui.notify(false);
      });
    }
  }
  _exports.default = _default;
});

define("views/email/fields/from-email-address", ["exports", "views/fields/link"], function (_exports, _link) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _link = _interopRequireDefault(_link);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _link.default {
    listTemplate = 'email/fields/from-email-address/detail';
    detailTemplate = 'email/fields/from-email-address/detail';
  }
  _exports.default = _default;
});

define("views/email/fields/folder-string", ["exports", "views/fields/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailFolderStringFieldView extends _base.default {
    // language=Handlebars
    detailTemplateContent = `
        {{#if valueIsSet}}
            {{#if value}}
                {{#if isList}}
                    {{#each value}}
                        <div class="multi-enum-item-container">{{this}}</div>
                    {{/each}}
                {{else}}
                    {{value}}
                {{/if}}

            {{else}}
                <span class="none-value">{{translate 'None'}}</span>
            {{/if}}
        {{else}}
            <span class="loading-value"></span>
        {{/if}}
    `;

    // noinspection JSCheckFunctionSignatures
    data() {
      if (!this.model.has('folderId')) {
        return {
          valueIsSet: false
        };
      }
      const value = this.getFolderString();
      return {
        valueIsSet: true,
        value: this.getFolderString(),
        isList: Array.isArray(value)
      };
    }
    getAttributeList() {
      return ['isUsers', 'folderId', 'folderName', 'groupFolderId', 'groupFolderName', 'inArchive', 'inTrash', 'isUsersSent', 'groupStatusFolder'];
    }

    /**
     * @return {string|string[]}
     */
    getFolderString() {
      if (this.model.attributes.groupFolderName) {
        let string = this.translate('group', 'strings', 'Email') + ' · ' + this.model.attributes.groupFolderName;
        if (this.model.attributes.groupStatusFolder === 'Archive') {
          string += ' · ' + this.translate('archive', 'presetFilters', 'Email');
        } else if (this.model.attributes.groupStatusFolder === 'Trash') {
          string += ' · ' + this.translate('trash', 'presetFilters', 'Email');
        }
        if (this.model.attributes.isUsersSent) {
          return [string, this.translate('sent', 'presetFilters', 'Email')];
        }
        return string;
      }
      let string;
      if (this.model.attributes.inTrash) {
        string = this.translate('trash', 'presetFilters', 'Email');
      }
      if (this.model.attributes.inArchive) {
        string = this.translate('archive', 'presetFilters', 'Email');
      }
      if (this.model.attributes.folderName && this.model.attributes.folderId) {
        string = this.model.attributes.folderName;
      }
      if (string && this.model.attributes.isUsersSent) {
        return [string, this.translate('sent', 'presetFilters', 'Email')];
      }
      if (this.model.attributes.isUsersSent) {
        return this.translate('sent', 'presetFilters', 'Email');
      }
      if (this.model.attributes.createdById === this.getUser().id && this.model.attributes.status === 'Draft') {
        return this.translate('drafts', 'presetFilters', 'Email');
      }
      if (this.model.attributes.isUsers) {
        return this.translate('inbox', 'presetFilters', 'Email');
      }
      return undefined;
    }
  }
  var _default = _exports.default = EmailFolderStringFieldView;
});

define("views/email/fields/email-address-varchar", ["exports", "views/fields/base", "views/email/fields/from-address-varchar", "views/email/fields/email-address", "ui/autocomplete"], function (_exports, _base, _fromAddressVarchar, _emailAddress, _autocomplete) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _fromAddressVarchar = _interopRequireDefault(_fromAddressVarchar);
  _emailAddress = _interopRequireDefault(_emailAddress);
  _autocomplete = _interopRequireDefault(_autocomplete);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailAddressVarcharFieldView extends _base.default {
    detailTemplate = 'email/fields/email-address-varchar/detail';
    editTemplate = 'email/fields/email-address-varchar/edit';
    emailAddressRegExp = new RegExp(/^[-!#$%&'*+/=?^_`{|}~A-Za-z0-9]+(?:\.[-!#$%&'*+/=?^_`{|}~A-Za-z0-9]+)*/.source + /@([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?\.)+[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9]/.source);

    // noinspection JSCheckFunctionSignatures
    data() {
      const data = super.data();
      data.valueIsSet = this.model.has(this.name);
      data.maxLength = 254;
      data.hasSelectAddress = this.hasSelectAddress;

      // noinspection JSValidateTypes
      return data;
    }
    events = {
      /** @this EmailAddressVarcharFieldView */
      'click a[data-action="clearAddress"]': function (e) {
        const address = $(e.currentTarget).data('address').toString();
        this.deleteAddress(address);
      },
      /** @this EmailAddressVarcharFieldView */
      'keyup input': function (e) {
        if (!this.isEditMode()) {
          return;
        }
        const key = Espo.Utils.getKeyFromKeyEvent(e);
        if (key === 'Comma' || key === 'Semicolon' || key === 'Enter') {
          const $input = $(e.currentTarget);
          const address = this.obtainEmailAddressFromString($input.val());
          if (address) {
            this.addAddress(address, '');
            $input.val('');
          }
        }
      },
      /** @this EmailAddressVarcharFieldView */
      'change input': function (e) {
        if (!this.isEditMode()) {
          return;
        }
        const $input = $(e.currentTarget);
        const address = this.obtainEmailAddressFromString($input.val());
        if (address) {
          this.addAddress(address, '');
          $input.val('');
        }
      },
      /** @this EmailAddressVarcharFieldView */
      'click [data-action="createContact"]': function (e) {
        const address = $(e.currentTarget).data('address');
        _fromAddressVarchar.default.prototype.createPerson.call(this, 'Contact', address);
      },
      /** @this EmailAddressVarcharFieldView */
      'click [data-action="createLead"]': function (e) {
        const address = $(e.currentTarget).data('address');
        _fromAddressVarchar.default.prototype.createPerson.call(this, 'Lead', address);
      },
      /** @this EmailAddressVarcharFieldView */
      'click [data-action="addToContact"]': function (e) {
        const address = $(e.currentTarget).data('address');
        _fromAddressVarchar.default.prototype.addToPerson.call(this, 'Contact', address);
      },
      /** @this EmailAddressVarcharFieldView */
      'click [data-action="addToLead"]': function (e) {
        const address = $(e.currentTarget).data('address');
        _fromAddressVarchar.default.prototype.addToPerson.call(this, 'Lead', address);
      },
      /** @this EmailAddressVarcharFieldView */
      'auxclick a[href][data-scope][data-id]': function (e) {
        const isCombination = e.button === 1 && (e.ctrlKey || e.metaKey);
        if (!isCombination) {
          return;
        }
        const $target = $(e.currentTarget);
        const id = $target.attr('data-id');
        const scope = $target.attr('data-scope');
        e.preventDefault();
        e.stopPropagation();
        _fromAddressVarchar.default.prototype.quickView.call(this, {
          id: id,
          scope: scope
        });
      }
    };

    /**
     *
     * @param {string} input
     * @return {string}
     */
    obtainEmailAddressFromString(input) {
      input = input.replace(',', '').replace(';', '').trim();
      const address = input.split(' ').find(it => it.includes('@'));
      if (!address) {
        return undefined;
      }
      if (!this.checkEmailAddressInString(address)) {
        return undefined;
      }
      return address;
    }
    getAutocompleteMaxCount() {
      if (this.autocompleteMaxCount) {
        return this.autocompleteMaxCount;
      }
      return this.getConfig().get('recordsPerPage');
    }
    getAttributeList() {
      const list = super.getAttributeList();
      list.push('nameHash');
      list.push('typeHash');
      list.push('idHash');
      list.push('accountId');
      list.push(this.name + 'EmailAddressesNames');
      list.push(this.name + 'EmailAddressesIds');
      return list;
    }
    setup() {
      this.setupSelectAddress();
      this.on('render', () => this.initAddressList());
    }

    /**
     * @private
     */
    setupSelectAddress() {
      const list = /** @type {string[]} */this.getConfig().get('emailAddressSelectEntityTypeList') || [];
      this.selectAddressEntityTypeList = list.filter(it => this.getAcl().checkScope(it));
      this.hasSelectAddress = this.selectAddressEntityTypeList.length !== 0;
      this.addActionHandler('selectAddress', () => {
        const entityType = this.selectAddressEntityTypeList[0];
        this.processSelectEntityType(entityType);
      });
    }

    /**
     * @param {string} entityType
     */
    async processSelectEntityType(entityType) {
      const viewName = this.getMetadata().get(['clientDefs', entityType, 'modalViews', 'select']) || 'views/modals/select-records';
      const headerText = this.translate('Select') + ' · ' + this.translate(this.name, 'fields', 'Email');
      const filters = {
        emailAddress: {
          type: 'isNotNull',
          data: {
            type: 'isNotEmpty'
          }
        },
        emailAddressIsInvalid: {
          type: 'isFalse',
          data: {
            type: 'isFalse'
          }
        }
      };
      if (entityType === 'Contact' && (this.model.attributes.parentId && this.model.attributes.parentType === 'Account' || this.model.attributes.accountId)) {
        const accountId = this.model.attributes.accountId || this.model.attributes.parentId;
        const accountName = this.model.attributes.accountId ? this.model.attributes.accountName : this.model.attributes.parentName;
        filters.accounts = {
          field: 'accounts',
          type: 'linkedWith',
          value: [accountId],
          data: {
            nameHash: {
              [accountId]: accountName
            }
          }
        };
      }

      /** @type {module:views/modals/select-records~Options} */
      const options = {
        entityType: entityType,
        multiple: true,
        createButton: false,
        mandatorySelectAttributeList: ['emailAddress'],
        headerText: headerText,
        filters: filters,
        onSelect: models => {
          models.filter(model => model.attributes.emailAddress).forEach(model => {
            const address = model.attributes.emailAddress;
            if (this.addressList.includes(address)) {
              return;
            }
            this.addressList.push(address);
            this.nameHash[address] = model.attributes.name;
            this.idHash[address] = model.id;
            this.typeHash[address] = model.entityType;
            this.addAddressHtml(address, model.attributes.name);
          });
          this.trigger('change');
        }
      };
      const view = /** @type {import('views/modals/select-records').default} */
      await this.createView('modal', viewName, options);
      this.selectAddressEntityTypeList.forEach(itemEntityType => {
        view.addButton({
          name: 'selectEntityType' + itemEntityType,
          style: 'text',
          position: 'right',
          label: this.translate(itemEntityType, 'scopeNamesPlural'),
          className: itemEntityType === entityType ? 'active btn-xs-wide' : 'btn-xs-wide',
          disabled: itemEntityType === entityType,
          onClick: () => {
            this.clearView('modal');
            this.processSelectEntityType(itemEntityType);
          }
        }, false, true);
      });
      await view.render();
    }
    initAddressList() {
      this.nameHash = {};
      this.addressList = (this.model.get(this.name) || '').split(';').filter(item => {
        return item !== '';
      }).map(item => {
        return item.trim();
      });
      this.idHash = this.idHash || {};
      this.typeHash = this.typeHash || {};
      this.nameHash = this.nameHash || {};
      _.extend(this.typeHash, this.model.get('typeHash') || {});
      _.extend(this.nameHash, this.model.get('nameHash') || {});
      _.extend(this.idHash, this.model.get('idHash') || {});
      this.nameHash = _.clone(this.nameHash);
      this.typeHash = _.clone(this.typeHash);
      this.idHash = _.clone(this.idHash);
    }
    afterRender() {
      super.afterRender();
      if (this.isEditMode()) {
        this.$input = this.$element = this.$el.find('input');
        this.addressList.forEach(item => {
          this.addAddressHtml(item, this.nameHash[item] || '');
        });

        /** @type {module:ajax.AjaxPromise & Promise<any>} */
        let lastAjaxPromise;
        const autocomplete = new _autocomplete.default(this.$input.get(0), {
          name: this.name,
          autoSelectFirst: true,
          triggerSelectOnValidInput: false,
          focusOnSelect: true,
          minChars: 1,
          forceHide: true,
          onSelect: item => {
            this.addAddress(item.emailAddress, item.entityName, item.entityType, item.entityId);
            this.$input.val('');
          },
          formatResult: item => {
            return this.getHelper().escapeString(item.name) + ' &#60;' + this.getHelper().escapeString(item.id) + '&#62;';
          },
          lookupFunction: query => {
            if (lastAjaxPromise && lastAjaxPromise.getReadyState() < 4) {
              lastAjaxPromise.abort();
            }
            lastAjaxPromise = Espo.Ajax.getRequest('EmailAddress/search', {
              q: query,
              maxSize: this.getAutocompleteMaxCount(),
              onlyActual: true
            });
            return lastAjaxPromise.then(/** Record[] */response => {
              return response.map(item => {
                return {
                  id: item.emailAddress,
                  name: item.entityName,
                  emailAddress: item.emailAddress,
                  entityId: item.entityId,
                  entityName: item.entityName,
                  entityType: item.entityType,
                  data: item.emailAddress,
                  value: item.emailAddress
                };
              });
            });
          }
        });
        this.once('render remove', () => autocomplete.dispose());
      }
      if (this.mode === 'search' && this.getAcl().check('Email', 'create')) {
        _emailAddress.default.prototype.initSearchAutocomplete.call(this);
      }
      if (this.mode === 'search') {
        this.$input.on('input', () => {
          this.trigger('change');
        });
      }
    }
    checkEmailAddressInString(string) {
      const arr = string.match(this.emailAddressRegExp);
      if (!arr || !arr.length) {
        return;
      }
      return true;
    }
    addAddress(address, name, type, id) {
      if (this.justAddedAddress) {
        this.deleteAddress(this.justAddedAddress);
      }
      this.justAddedAddress = address;
      setTimeout(() => {
        this.justAddedAddress = null;
      }, 100);
      address = address.trim();
      if (!type) {
        const arr = address.match(this.emailAddressRegExp);
        if (!arr || !arr.length) {
          return;
        }
        address = arr[0];
      }
      if (!~this.addressList.indexOf(address)) {
        this.addressList.push(address);
        this.nameHash[address] = name;
        if (type) {
          this.typeHash[address] = type;
        }
        if (id) {
          this.idHash[address] = id;
        }
        this.addAddressHtml(address, name);
        this.trigger('change');
      }
    }

    /**
     * @private
     * @param {string} address
     * @param {string} name
     */
    addAddressHtml(address, name) {
      const $container = this.$el.find('.link-container');
      const type = this.typeHash[address];
      const id = this.idHash[address];
      let avatarHtml = '';
      const $text = $('<span>');
      if (name) {
        if (type === 'User' && id) {
          avatarHtml = this.getHelper().getAvatarHtml(id, 'small', 18, 'avatar-link');
        }
        $text.append($('<span>').text(name), ' ', $('<span>').addClass('text-muted middle-dot'), ' ');
      }
      $text.append($('<span>').text(address));
      const $div = $('<div>').attr('data-address', address).addClass('list-group-item').append(avatarHtml, $('<a>').attr('data-address', address).attr('role', 'button').attr('tabindex', '0').attr('data-action', 'clearAddress').addClass('pull-right').append($('<span>').addClass('fas fa-times')), $text);
      $container.append($div);
    }
    deleteAddress(address) {
      this.deleteAddressHtml(address);
      const index = this.addressList.indexOf(address);
      if (index > -1) {
        this.addressList.splice(index, 1);
      }
      delete this.nameHash[address];
      this.trigger('change');
    }
    deleteAddressHtml(address) {
      this.$el.find('.list-group-item[data-address="' + address + '"]').remove();
    }
    fetch() {
      const data = {};
      data[this.name] = this.addressList.join(';');
      return data;
    }
    fetchSearch() {
      const value = this.$element.val().trim();
      if (value) {
        return {
          type: 'equals',
          value: value
        };
      }
      return null;
    }
    getValueForDisplay() {
      if (this.isDetailMode()) {
        const names = [];
        this.addressList.forEach(address => {
          names.push(this.getDetailAddressHtml(address));
        });
        return names.join('');
      }
    }
    getDetailAddressHtml(address) {
      if (!address) {
        return '';
      }
      const name = this.nameHash[address] || null;
      const entityType = this.typeHash[address] || null;
      const id = this.idHash[address] || null;
      if (id) {
        let avatarHtml = '';
        if (entityType === 'User') {
          avatarHtml = this.getHelper().getAvatarHtml(id, 'small', 18, 'avatar-link');
        }
        return $('<div class="email-address-detail-item">').append(avatarHtml, $('<a>').attr('href', `#${entityType}/view/${id}`).attr('data-scope', entityType).attr('data-id', id).text(name), ' <span class="text-muted middle-dot"></span> ', $('<span>').text(address)).get(0).outerHTML;
      }
      const $div = $('<div>');
      if (name) {
        $div.append($('<span>').addClass('email-address-line').text(name).append(' <span class="text-muted middle-dot"></span> ').append($('<span>').text(address)));
      } else {
        $div.append($('<span>').addClass('email-address-line').text(address));
      }
      if (this.getAcl().check('Contact', 'create') || this.getAcl().check('Lead', 'create')) {
        $div.prepend(_fromAddressVarchar.default.prototype.getCreateHtml.call(this, address));
      }
      return $div.get(0).outerHTML;
    }
    validateRequired() {
      if (this.model.get('status') === 'Draft') {
        return false;
      }
      return super.validateRequired();
    }
  }
  var _default = _exports.default = EmailAddressVarcharFieldView;
});

define("views/email/fields/date-sent", ["exports", "views/fields/datetime-short"], function (_exports, _datetimeShort) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _datetimeShort = _interopRequireDefault(_datetimeShort);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _datetimeShort.default {
    shortInDetailMode = false;
  }
  _exports.default = _default;
});

define("views/email/fields/created-event", ["exports", "views/fields/link-parent"], function (_exports, _linkParent) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkParent = _interopRequireDefault(_linkParent);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _linkParent.default {
    data() {
      const data = super.data();

      /** @type {Record} */
      const icsEventData = this.model.get('icsEventData') || {};
      if (this.isReadMode() && !data.idValue && icsEventData.createdEvent) {
        data.idValue = icsEventData.createdEvent.id;
        data.typeValue = icsEventData.createdEvent.entityType;
        data.nameValue = icsEventData.createdEvent.name;
      }
      return data;
    }
    getAttributeList() {
      const list = super.getAttributeList();
      list.push('icsEventData');
      return list;
    }
  }
  _exports.default = _default;
});

define("views/email/fields/create-event", ["exports", "views/fields/base", "helpers/record-modal"], function (_exports, _base, _recordModal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _recordModal = _interopRequireDefault(_recordModal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _base.default {
    detailTemplate = 'email/fields/create-event/detail';
    eventEntityType = 'Meeting';
    getAttributeList() {
      return ['icsEventData', 'createdEventId'];
    }
    setup() {
      super.setup();
      this.addActionHandler('createEvent', () => this.createEvent());
    }
    createEvent() {
      const eventData = this.model.get('icsEventData') || {};
      const attributes = Espo.Utils.cloneDeep(eventData.valueMap || {});
      attributes.parentId = this.model.get('parentId');
      attributes.parentType = this.model.get('parentType');
      attributes.parentName = this.model.get('parentName');
      this.addFromAddressToAttributes(attributes);
      const helper = new _recordModal.default();
      helper.showCreate(this, {
        entityType: this.eventEntityType,
        attributes: attributes,
        afterSave: async () => {
          await this.model.fetch();
          Espo.Ui.success(this.translate('Done'));
        }
      });
    }

    /**
     * @private
     * @param {Record} attributes
     */
    addFromAddressToAttributes(attributes) {
      const fromAddress = this.model.get('from');
      const idHash = this.model.get('idHash') || {};
      const typeHash = this.model.get('typeHash') || {};
      const nameHash = this.model.get('nameHash') || {};
      let fromId = null;
      let fromType = null;
      let fromName = null;
      if (!fromAddress) {
        return;
      }
      fromId = idHash[fromAddress] || null;
      fromType = typeHash[fromAddress] || null;
      fromName = nameHash[fromAddress] || null;
      const attendeeLink = this.getAttendeeLink(fromType);
      if (!attendeeLink) {
        return;
      }
      attributes[attendeeLink + 'Ids'] = attributes[attendeeLink + 'Ids'] || [];
      attributes[attendeeLink + 'Names'] = attributes[attendeeLink + 'Names'] || {};
      if (~attributes[attendeeLink + 'Ids'].indexOf(fromId)) {
        return;
      }
      attributes[attendeeLink + 'Ids'].push(fromId);
      attributes[attendeeLink + 'Names'][fromId] = fromName;
    }

    /**
     * @private
     * @param {string} entityType
     * @return {null|string}
     */
    getAttendeeLink(entityType) {
      if (entityType === 'User') {
        return 'users';
      }
      if (entityType === 'Contact') {
        return 'contacts';
      }
      if (entityType === 'Lead') {
        return 'leads';
      }
      return null;
    }
  }
  _exports.default = _default;
});

define("views/email/fields/compose-from-address", ["exports", "views/fields/base", "ui/select"], function (_exports, _base, _select) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _select = _interopRequireDefault(_select);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _base.default {
    detailTemplate = 'email/fields/email-address-varchar/detail';
    editTemplate = 'email/fields/compose-from-address/edit';
    data() {
      let noSmtpMessage = this.translate('noSmtpSetup', 'messages', 'Email');
      const linkHtml = $('<a>').attr('href', '#EmailAccount').text(this.translate('EmailAccount', 'scopeNamesPlural')).get(0).outerHTML;
      noSmtpMessage = noSmtpMessage.replace('{link}', linkHtml);
      return {
        list: this.list,
        noSmtpMessage: noSmtpMessage,
        ...super.data()
      };
    }
    setup() {
      super.setup();
      this.nameHash = {
        ...(this.model.get('nameHash') || {})
      };
      this.typeHash = this.model.get('typeHash') || {};
      this.idHash = this.model.get('idHash') || {};
      this.list = this.getUser().get('emailAddressList') || [];
    }
    afterRenderEdit() {
      if (this.$element.length) {
        _select.default.init(this.$element);
      }
    }
    getValueForDisplay() {
      if (this.isDetailMode()) {
        const address = this.model.get(this.name);
        return this.getDetailAddressHtml(address);
      }
      return super.getValueForDisplay();
    }
    getDetailAddressHtml(address) {
      if (!address) {
        return '';
      }
      const name = this.nameHash[address] || null;
      const entityType = this.typeHash[address] || null;
      const id = this.idHash[address] || null;
      if (id && name) {
        return $('<div>').append($('<a>').attr('href', `#${entityType}/view/${id}`).attr('data-scope', entityType).attr('data-id', id).text(name), ' ', $('<span>').addClass('text-muted chevron-right'), ' ', $('<span>').text(address)).get(0).outerHTML;
      }
      const $div = $('<div>');
      if (name) {
        $div.append($('<span>').addClass('email-address-line').text(name).append(' ', $('<span>').addClass('text-muted chevron-right'), ' ', $('<span>').text(address)));
        return $div.get(0).outerHTML;
      }
      $div.append($('<span>').addClass('email-address-line').text(address));
      return $div.get(0).outerHTML;
    }
  }
  _exports.default = _default;
});

define("views/dashlets/stream", ["exports", "views/dashlets/abstract/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class StreamDashletView extends _base.default {
    name = 'Stream';
    templateContent = '<div class="list-container">{{{list}}}</div>';
    actionRefresh() {
      this.refreshInternal();
    }
    autoRefresh() {
      this.refreshInternal({
        skipNotify: true
      });
    }
    /**
     * @private
     * @param {{skipNotify?: boolean}} [options]
     * @return {Promise<void>}
     */
    async refreshInternal() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      if (!this.getRecordView()) {
        return;
      }
      if (!options.skipNotify) {
        Espo.Ui.notifyWait();
      }
      await this.getRecordView().showNewRecords();
      if (!options.skipNotify) {
        Espo.Ui.notify();
      }
    }
    afterRender() {
      this.getCollectionFactory().create('Note', collection => {
        this.collection = collection;
        collection.url = 'Stream';
        collection.maxSize = this.getOption('displayRecords');
        if (this.getOption('skipOwn')) {
          collection.data.skipOwn = true;
        }
        collection.fetch().then(() => {
          this.createView('list', 'views/stream/record/list', {
            selector: '> .list-container',
            collection: collection,
            isUserStream: true,
            noEdit: false
          }, view => {
            view.render();
          });
        });
      });
    }

    /**
     * @return {module:views/stream/record/list}
     */
    getRecordView() {
      return this.getView('list');
    }
    setupActionList() {
      this.actionList.unshift({
        name: 'viewList',
        text: this.translate('View'),
        iconHtml: '<span class="fas fa-align-justify"></span>',
        url: '#Stream'
      });
      if (!this.getUser().isPortal()) {
        this.actionList.unshift({
          name: 'create',
          text: this.translate('Create Post', 'labels'),
          iconHtml: '<span class="fas fa-plus"></span>'
        });
      }
    }

    // noinspection JSUnusedGlobalSymbols
    actionCreate() {
      this.createView('dialog', 'views/stream/modals/create-post', {}, view => {
        view.render();
        this.listenToOnce(view, 'after:save', () => {
          view.close();
          this.actionRefresh();
        });
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionViewList() {
      this.getRouter().navigate('#Stream', {
        trigger: true
      });
    }
  }
  var _default = _exports.default = StreamDashletView;
});

define("views/dashlets/records", ["exports", "views/dashlets/abstract/record-list"], function (_exports, _recordList) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _recordList = _interopRequireDefault(_recordList);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RecordsDashletView extends _recordList.default {
    name = 'Records';
    rowActionsView = 'views/record/row-actions/view-and-edit';
    listView = 'views/email/record/list-expanded';
    init() {
      super.init();
      this.scope = this.getOption('entityType');
    }
    getSearchData() {
      const data = {
        primary: /** @type string */this.getOption('primaryFilter')
      };
      if (data.primary === 'all') {
        delete data.primary;
      }
      const bool = {};
      (this.getOption('boolFilterList') || []).forEach(item => {
        bool[item] = true;
      });
      data.bool = bool;
      return data;
    }
    setupActionList() {
      const scope = this.getOption('entityType');
      if (scope && this.getAcl().checkScope(scope, 'create')) {
        this.actionList.unshift({
          name: 'create',
          text: this.translate('Create ' + scope, 'labels', scope),
          iconHtml: '<span class="fas fa-plus"></span>',
          url: '#' + scope + '/create'
        });
      }
    }
  }
  var _default = _exports.default = RecordsDashletView;
});

define("views/dashlets/memo", ["exports", "views/dashlets/abstract/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class MemoDashletView extends _base.default {
    name = 'Memo';
    templateContent = `
        {{#if text}}
        <div class="complex-text complex-text-memo">{{complexText text}}</div>
        {{/if}}
    `;
    data() {
      return {
        text: this.getOption('text')
      };
    }
    afterAdding() {
      this.getContainerView().actionOptions();
    }
  }
  var _default = _exports.default = MemoDashletView;
});

define("views/dashlets/iframe", ["exports", "views/dashlets/abstract/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class IframeDashletView extends _base.default {
    name = 'Iframe';

    /**
     * @private
     * @type {boolean}
     */
    sandboxDisabled = false;

    // language=Handlebars
    templateContent = `
        <iframe
            style="margin: 0; border: 0;"
            {{#unless viewObject.sandboxDisabled}}
                sandbox="allow-scripts"
            {{/unless}}
        ></iframe>
    `;
    setup() {
      const url = this.getOption('url');

      /** @type {string[]} */
      const excludeDomains = this.getConfig().get('iframeSandboxExcludeDomainList') || [];
      if (url) {
        for (const domain of excludeDomains) {
          try {
            const urlObject = new URL(url);
            if (urlObject.hostname === domain) {
              this.sandboxDisabled = true;
              break;
            }
          } catch (e) {
            console.warn(`Invalid URL ${url}.`);
          }
        }
      }
    }
    afterRender() {
      const $iframe = this.$el.find('iframe');
      const url = this.getOption('url');
      if (url) {
        $iframe.attr('src', url);
      }
      this.$el.addClass('no-padding');
      this.$el.css('overflow', 'hidden');
      const height = this.$el.height();
      $iframe.css('height', height);
      $iframe.css('width', '100%');
    }
    afterAdding() {
      this.getContainerView().actionOptions();
    }
  }
  var _default = _exports.default = IframeDashletView;
});

define("views/dashlets/emails", ["exports", "views/dashlets/abstract/record-list"], function (_exports, _recordList) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _recordList = _interopRequireDefault(_recordList);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailsDashletView extends _recordList.default {
    name = 'Emails';
    scope = 'Emails';
    rowActionsView = 'views/email/record/row-actions/dashlet';
    listView = 'views/email/record/list-expanded';
    setupActionList() {
      if (this.getAcl().checkScope(this.scope, 'create')) {
        this.actionList.unshift({
          name: 'compose',
          text: this.translate('Compose Email', 'labels', this.scope),
          iconHtml: '<span class="fas fa-plus"></span>'
        });
      }
    }

    // noinspection JSUnusedGlobalSymbols
    actionCompose() {
      const attributes = this.getCreateAttributes() || {};
      Espo.Ui.notifyWait();
      const viewName = this.getMetadata().get('clientDefs.' + this.scope + '.modalViews.compose') || 'views/modals/compose-email';
      this.createView('modal', viewName, {
        scope: this.scope,
        attributes: attributes
      }, view => {
        view.render();
        Espo.Ui.notify(false);
        this.listenToOnce(view, 'after:save', () => {
          this.actionRefresh();
        });
      });
    }

    /**
     * @return {module:search-manager~data}
     */
    getSearchData() {
      return {
        'advanced': [{
          'attribute': 'folderId',
          'type': 'inFolder',
          'value': this.getOption('folder') || 'inbox'
        }]
      };
    }
  }
  var _default = _exports.default = EmailsDashletView;
});

define("views/dashlets/options/record-list", ["exports", "views/dashlets/options/base"], function (_exports, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RecordListDashletOptionsModalView extends _base.default {
    /**
     * @protected
     * @type {boolean}
     */
    hasCollaborators;
    setup() {
      const entityType = this.getMetadata().get(`dashlets.${this.name}.entityType`);
      this.hasCollaborators = entityType && !!this.getMetadata().get(`scopes.${entityType}.collaborators`);
      super.setup();
      if (!this.hasCollaborators) {
        this.getRecordView().hideField('includeShared');
      }
    }
  }
  _exports.default = RecordListDashletOptionsModalView;
});

define("views/dashlets/fields/records/sort-direction", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class _default extends _enum.default {
    setup() {
      super.setup();
      this.listenTo(this.model, 'change:entityType', () => {
        this.setupOptions();
        this.reRender();
      });
    }
  }
  _exports.default = _default;
});

define("views/dashlets/fields/records/sort-by", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _enum.default {
    setup() {
      super.setup();
      this.listenTo(this.model, 'change:entityType', () => {
        this.setupOptions();
        this.reRender();
      });
    }
    setupOptions() {
      const entityType = this.model.get('entityType');
      const scope = entityType;
      if (!entityType) {
        this.params.options = [];
        return;
      }

      /** @type {Record<string, Record>} */
      const fieldDefs = this.getMetadata().get(`entityDefs.${scope}.fields`) || {};
      const orderableFieldList = Object.keys(fieldDefs).filter(item => {
        if (fieldDefs[item].orderDisabled || fieldDefs[item].utility) {
          return false;
        }
        return true;
      }).sort((v1, v2) => {
        return this.translate(v1, 'fields', scope).localeCompare(this.translate(v2, 'fields', scope));
      });
      const translatedOptions = {};
      orderableFieldList.forEach(item => {
        translatedOptions[item] = this.translate(item, 'fields', scope);
      });
      this.params.options = orderableFieldList;
      this.translatedOptions = translatedOptions;
    }
  }
  _exports.default = _default;
});

define("views/dashlets/fields/records/primary-filter", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _enum.default {
    setup() {
      super.setup();
      this.listenTo(this.model, 'change:entityType', () => {
        this.setupOptions();
        this.reRender();
      });
    }
    setupOptions() {
      const entityType = this.model.get('entityType');
      if (!entityType) {
        this.params.options = [];
        return;
      }
      const filterList = this.getMetadata().get(['clientDefs', entityType, 'filterList']) || [];
      this.params.options = [];
      filterList.forEach(item => {
        if (typeof item === 'object' && item.name) {
          if (item.accessDataList && !Espo.Utils.checkAccessDataList(item.accessDataList, this.getAcl(), this.getUser(), null, true)) {
            return false;
          }
          this.params.options.push(item.name);
          return;
        }
        this.params.options.push(item);
      });
      this.params.options.unshift('all');
      if (this.getMetadata().get(`scopes.${entityType}.stars`)) {
        this.params.options.push('starred');
      }
      this.translatedOptions = {};
      this.params.options.forEach(item => {
        this.translatedOptions[item] = this.translate(item, 'presetFilters', entityType);
      });
    }
  }
  _exports.default = _default;
});

define("views/dashlets/fields/records/expanded-layout", ["exports", "views/fields/base", "ui/multi-select", "views/dashlets/fields/records/expanded-layout/modals/edit-item"], function (_exports, _base, _multiSelect, _editItem) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _base = _interopRequireDefault(_base);
  _multiSelect = _interopRequireDefault(_multiSelect);
  _editItem = _interopRequireDefault(_editItem);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ExpandedLayoutDashletFieldView extends _base.default {
    // language=Handlebars
    editTemplateContent = `
        <div class="layout-container">
            {{#each rowDataList}}
                <div data-role="layoutRow">
                    <div
                        style="display: inline-block; width: calc(100% - var(--40px));"
                    >
                        <input
                            type="text"
                            value="{{value}}"
                            data-index="{{index}}"
                        >
                    </div>
                    {{#if hasEdit}}
                        <div class="btn-group pull-right">
                            <button
                                class="btn btn-text dropdown-toggle"
                                data-toggle="dropdown"
                            ><span class="caret"></span></button>
                            <ul class="dropdown-menu pull-right">
                                <li class="dropdown-header">{{translate 'Edit'}}</li>
                                {{#each itemList}}
                                    <li>
                                        <a
                                            role="button"
                                            tabindex="0"
                                            data-action="editItem"
                                            data-name="{{name}}"
                                        >{{label}}</a>
                                    </li>
                                {{/each}}
                            </ul>
                        </div>
                    {{/if}}
                </div>
            {{/each}}
        </div>
    `;
    delimiter = ':,:';

    /**
     * @private
     * @type {string}
     */
    targetEntityType;
    data() {
      const rowList = this.getRowList();
      const dataList = [...rowList, []].map((it, i) => ({
        index: i,
        value: it.map(subIt => subIt.name).join(this.delimiter),
        hasEdit: i < rowList.length,
        itemList: it.map(subIt => ({
          name: subIt.name,
          label: this.translate(subIt.name, 'fields', this.targetEntityType)
        }))
      }));
      return {
        rowDataList: dataList
      };
    }
    setup() {
      this.addHandler('change', 'div[data-role="layoutRow"] input', () => {
        setTimeout(() => {
          this.trigger('change');
          this.reRender();
        }, 1);
      });
      this.addActionHandler('editItem', (event, target) => this.editItem(target.dataset.name));
      this.targetEntityType = this.model.get('entityType') || this.getMetadata().get(['dashlets', this.dataObject.dashletName, 'entityType']);
    }

    /**
     * @private
     * @return {Array.<{name: string, link?: boolean, soft?: boolean, small?: boolean}>[]}
     */
    getRowList() {
      return Espo.Utils.cloneDeep((this.model.get(this.name) || {}).rows || []);
    }
    afterRenderEdit() {
      const rowList = Espo.Utils.cloneDeep(this.getRowList());
      rowList.push([]);
      const fieldDataList = this.getFieldDataList();
      rowList.forEach((row, i) => {
        const usedOtherList = [];
        const usedList = [];
        rowList.forEach((it, j) => {
          usedList.push(...it.map(it => it.name));
          if (j === i) {
            return;
          }
          usedOtherList.push(...it.map(it => it.name));
        });
        const preparedList = fieldDataList.filter(it => !usedOtherList.includes(it.value)).map(it => {
          if (!usedList.includes(it.value)) {
            return it;
          }
          const itemData = this.getItemData(it.value) || {};
          if (itemData.soft) {
            it.style = 'soft';
          }
          if (itemData.small) {
            it.small = true;
          }
          return it;
        });
        const inputElement = this.element.querySelector(`input[data-index="${i.toString()}"]`);

        /** @type {module:ui/multi-select~Options} */
        const multiSelectOptions = {
          items: preparedList,
          delimiter: this.delimiter,
          matchAnyWord: this.matchAnyWord,
          draggable: true
        };
        _multiSelect.default.init(inputElement, multiSelectOptions);
      });
    }

    /**
     * @private
     * @return {{value: string, text: string}[]}
     */
    getFieldDataList() {
      const scope = this.model.get('entityType') || this.getMetadata().get(['dashlets', this.dataObject.dashletName, 'entityType']);
      if (!scope) {
        return [];
      }
      const fields = this.getMetadata().get(['entityDefs', scope, 'fields']) || {};
      const forbiddenFieldList = this.getAcl().getScopeForbiddenFieldList(scope);
      const fieldList = Object.keys(fields).sort((v1, v2) => {
        return this.translate(v1, 'fields', scope).localeCompare(this.translate(v2, 'fields', scope));
      }).filter(item => {
        const defs = /** @type {Record} */fields[item];
        if (defs.disabled || defs.listLayoutDisabled || defs.utility) {
          return false;
        }
        const layoutAvailabilityList = defs.layoutAvailabilityList;
        if (layoutAvailabilityList && !layoutAvailabilityList.includes('list')) {
          return false;
        }
        const layoutIgnoreList = defs.layoutIgnoreList || [];
        if (layoutIgnoreList.includes('list')) {
          return false;
        }
        if (forbiddenFieldList.indexOf(item) !== -1) {
          return false;
        }
        return true;
      });
      const dataList = [];
      fieldList.forEach(item => {
        dataList.push({
          value: item,
          text: this.translate(item, 'fields', scope)
        });
      });
      return dataList;
    }

    /**
     * @private
     * @param {string} name
     */
    async editItem(name) {
      const inputData = this.getItemData(name);
      const view = new _editItem.default({
        label: this.translate(name, 'fields', this.targetEntityType),
        data: inputData,
        onApply: data => this.applyItem(name, data)
      });
      await this.assignView('modal', view);
      await view.render();
    }

    /**
     * @private
     * @param {string} name
     * @return {{
     *     soft: boolean,
     *     small: boolean,
     * }}
     */
    getItemData(name) {
      /**
       * @type {{
       *     soft: boolean,
       *     small: boolean,
       * }}
       */
      let inputData;
      for (const row of this.getRowList()) {
        for (const item of row) {
          if (item.name === name) {
            inputData = {
              soft: item.soft || false,
              small: item.small || false
            };
          }
        }
      }
      return inputData;
    }
    fetch() {
      const value = {
        rows: []
      };

      /** @type {Record.<string, Record>} */
      const params = {};
      for (const row of this.getRowList()) {
        for (const item of row) {
          params[item.name] = item;
        }
      }
      this.element.querySelectorAll('input').forEach(/** HTMLInputElement*/inputElement => {
        const row = [];
        let list = inputElement.value.split(this.delimiter);
        if (list.length === 1 && list[0] === '') {
          list = [];
        }
        if (list.length === 0) {
          return;
        }
        list.forEach(name => {
          const item = {
            name: name
          };
          if (name === 'name') {
            item.link = true;
          }
          if (params[name]) {
            item.soft = params[name].soft || false;
            item.small = params[name].small || false;
          }
          row.push(item);
        });
        value.rows.push(row);
      });
      return {
        [this.name]: value
      };
    }

    /**
     * @private
     * @param {string} name
     * @param {{soft: boolean, small: boolean}} data
     */
    applyItem(name, data) {
      const rowList = this.getRowList();
      for (const row of rowList) {
        for (const item of row) {
          if (item.name === name) {
            item.soft = data.soft;
            item.small = data.small;
          }
        }
      }
      this.model.set(this.name, {
        rows: rowList
      }, {
        ui: true
      });
      this.reRender();
    }
  }
  var _default = _exports.default = ExpandedLayoutDashletFieldView;
});

define("views/dashlets/fields/records/entity-type", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _enum.default {
    setup() {
      super.setup();
      this.on('change', () => {
        const o = {
          primaryFilter: null,
          boolFilterList: [],
          title: this.translate('Records', 'dashlets'),
          sortBy: null,
          sortDirection: 'asc'
        };
        o.expandedLayout = {
          rows: []
        };
        const entityType = this.model.get('entityType');
        if (entityType) {
          o.title = this.translate(entityType, 'scopeNamesPlural');
          o.sortBy = this.getMetadata().get(['entityDefs', entityType, 'collection', 'orderBy']);
          const order = this.getMetadata().get(['entityDefs', entityType, 'collection', 'order']);
          if (order) {
            o.sortDirection = order;
          } else {
            o.sortDirection = 'asc';
          }
          o.expandedLayout = {
            rows: [[{
              name: "name",
              link: true,
              scope: entityType
            }]]
          };
        }
        this.model.set(o);
      });
    }
    setupOptions() {
      this.params.options = Object.keys(this.getMetadata().get('scopes')).filter(scope => {
        if (this.getMetadata().get(`scopes.${scope}.disabled`)) {
          return;
        }
        if (!this.getAcl().checkScope(scope, 'read')) {
          return;
        }
        if (!this.getMetadata().get(['scopes', scope, 'entity'])) {
          return;
        }
        if (!this.getMetadata().get(['scopes', scope, 'object'])) {
          return;
        }
        return true;
      }).sort((v1, v2) => {
        return this.translate(v1, 'scopeNames').localeCompare(this.translate(v2, 'scopeNames'));
      });
      this.params.options.unshift('');
    }
  }
  _exports.default = _default;
});

define("views/dashlets/fields/records/bool-filter-list", ["exports", "views/fields/multi-enum"], function (_exports, _multiEnum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _multiEnum = _interopRequireDefault(_multiEnum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _multiEnum.default {
    setup() {
      super.setup();
      this.listenTo(this.model, 'change:entityType', () => {
        this.setupOptions();
        this.reRender();
      });
    }
    setupOptions() {
      const entityType = this.model.get('entityType');
      if (!entityType) {
        this.params.options = [];
        return;
      }
      const filterList = this.getMetadata().get(['clientDefs', entityType, 'boolFilterList']) || [];
      this.params.options = [];
      filterList.forEach(item => {
        if (typeof item === 'object' && item.name) {
          if (item.accessDataList && !Espo.Utils.checkAccessDataList(item.accessDataList, this.getAcl(), this.getUser(), null, true)) {
            return false;
          }
          this.params.options.push(item.name);
          return;
        }
        this.params.options.push(item);
      });
      if (this.getMetadata().get(['scopes', entityType, 'stream']) && this.getAcl().checkScope(entityType, 'stream')) {
        this.params.options.push('followed');
      }
      if (this.getMetadata().get(`scopes.${entityType}.collaborators`)) {
        this.params.options.push('shared');
      }
      this.translatedOptions = {};
      this.params.options.forEach(item => {
        this.translatedOptions[item] = this.translate(item, 'boolFilters', entityType);
      });
    }
  }
  _exports.default = _default;
});

define("views/dashlets/fields/emails/folder", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailFolderDashletFieldView extends _enum.default {
    /** @type {{id: string, name: string}[]} */
    folderDataList;
    setup() {
      super.setup();
      let userId = this.dataObject.userId ?? this.getUser().id;
      this.wait(Espo.Ajax.getRequest('EmailFolder/action/listAll', {
        userId: userId
      }).then(data => this.folderDataList = data.list).then(() => this.setupOptions()));
      this.setupOptions();
    }
    setupOptions() {
      if (!this.folderDataList) {
        return;
      }
      this.params.options = this.folderDataList.map(item => item.id).filter(item => item !== 'inbox' && item !== 'trash');
      this.params.options.unshift('');
      this.translatedOptions = {
        '': this.translate('inbox', 'presetFilters', 'Email')
      };
      this.folderDataList.forEach(item => {
        this.translatedOptions[item.id] = item.name;
      });
    }
  }
  var _default = _exports.default = EmailFolderDashletFieldView;
});

define("views/dashboard-template/detail", ["exports", "views/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _detail.default {
    // noinspection JSUnusedGlobalSymbols
    actionDeployToUsers() {
      this.createView('dialog', 'views/dashboard-template/modals/deploy-to-users', {
        model: this.model
      }, view => {
        view.render();
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionDeployToTeam() {
      this.createView('dialog', 'views/dashboard-template/modals/deploy-to-team', {
        model: this.model
      }, view => {
        view.render();
      });
    }
  }
  _exports.default = _default;
});

define("views/dashboard-template/record/list", ["exports", "views/record/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _list.default {
    massActionList = ['remove', 'export'];
  }
  _exports.default = _default;
});

define("views/dashboard-template/modals/deploy-to-users", ["exports", "views/modal", "model"], function (_exports, _modal, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _modal.default {
    className = 'dialog dialog-record';
    templateContent = `<div class="record">{{{record}}}</div>`;
    setup() {
      this.buttonList = [{
        name: 'deploy',
        text: this.translate('Deploy for Users', 'labels', 'DashboardTemplate'),
        style: 'danger',
        onClick: () => this.actionDeploy()
      }, {
        name: 'cancel',
        label: 'Cancel'
      }];
      this.headerText = this.model.get('name');
      this.formModel = new _model.default();
      this.formModel.name = 'None';
      this.formModel.setDefs({
        fields: {
          'users': {
            type: 'linkMultiple',
            view: 'views/fields/users',
            entity: 'User',
            required: true
          },
          'append': {
            type: 'bool'
          }
        }
      });
      this.createView('record', 'views/record/edit-for-modal', {
        scope: 'None',
        model: this.formModel,
        selector: '.record',
        detailLayout: [{
          rows: [[{
            name: 'users',
            labelText: this.translate('users', 'links')
          }, {
            name: 'append',
            labelText: this.translate('append', 'fields', 'DashboardTemplate')
          }]]
        }]
      });
    }

    /**
     * @private
     * @return {import('views/record/edit').default}
     */
    getRecordView() {
      return this.getView('record');
    }

    /**
     * @private
     */
    actionDeploy() {
      if (this.getRecordView().processFetch()) {
        Espo.Ajax.postRequest('DashboardTemplate/action/deployToUsers', {
          id: this.model.id,
          userIdList: this.formModel.get('usersIds'),
          append: this.formModel.get('append')
        }).then(() => {
          Espo.Ui.success(this.translate('Done'));
          this.close();
        });
      }
    }
  }
  _exports.default = _default;
});

define("views/dashboard-template/modals/deploy-to-team", ["exports", "views/modal", "model"], function (_exports, _modal, _model) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _modal = _interopRequireDefault(_modal);
  _model = _interopRequireDefault(_model);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _modal.default {
    className = 'dialog dialog-record';
    templateContent = `<div class="record">{{{record}}}</div>`;
    setup() {
      this.buttonList = [{
        name: 'deploy',
        text: this.translate('Deploy for Team', 'labels', 'DashboardTemplate'),
        style: 'danger',
        onClick: () => this.actionDeploy()
      }, {
        name: 'cancel',
        label: 'Cancel'
      }];
      this.headerText = this.model.get('name');
      this.formModel = new _model.default();
      this.formModel.name = 'None';
      this.formModel.setDefs({
        fields: {
          'team': {
            type: 'link',
            entity: 'Team',
            required: true
          },
          'append': {
            type: 'bool'
          }
        }
      });
      this.createView('record', 'views/record/edit-for-modal', {
        scope: 'None',
        model: this.formModel,
        selector: '.record',
        detailLayout: [{
          rows: [[{
            name: 'team',
            labelText: this.translate('team', 'links')
          }, {
            name: 'append',
            labelText: this.translate('append', 'fields', 'DashboardTemplate')
          }]]
        }]
      });
    }

    /**
     * @private
     * @return {import('views/record/edit').default}
     */
    getRecordView() {
      return this.getView('record');
    }

    /**
     * @private
     */
    actionDeploy() {
      if (this.getRecordView().processFetch()) {
        Espo.Ajax.postRequest('DashboardTemplate/action/deployToTeam', {
          id: this.model.id,
          teamId: this.formModel.get('teamId'),
          append: this.formModel.get('append')
        }).then(() => {
          Espo.Ui.success(this.translate('Done'));
          this.close();
        });
      }
    }
  }
  _exports.default = _default;
});

define("views/attachment/record/list", ["exports", "views/record/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _list.default {
    rowActionsView = 'views/record/row-actions/view-and-remove';
    massActionList = ['remove'];
  }
  _exports.default = _default;
});

define("views/attachment/record/detail", ["exports", "views/record/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _detail.default {
    readOnly = true;
  }
  _exports.default = _default;
});

define("views/attachment/modals/detail", ["exports", "views/modals/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _detail.default {
    editDisabled = true;
  }
  _exports.default = _default;
});

define("views/attachment/fields/parent", ["exports", "views/fields/link-parent"], function (_exports, _linkParent) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkParent = _interopRequireDefault(_linkParent);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _linkParent.default {
    ignoreScopeList = ['Preferences', 'ExternalAccount', 'Notification', 'Note', 'ArrayValue', 'Attachment'];
    displayEntityType = true;
    setup() {
      super.setup();
      this.foreignScopeList = this.getMetadata().getScopeEntityList().filter(item => {
        if (!this.getUser().isAdmin() && !this.getAcl().checkScopeHasAcl(item)) {
          return false;
        }
        if (this.ignoreScopeList.includes(item)) {
          return false;
        }
        if (!this.getAcl().checkScope(item)) {
          return false;
        }
        return true;
      });
      this.getLanguage().sortEntityList(this.foreignScopeList);
      this.foreignScope = this.model.get(this.typeName) || this.foreignScopeList[0];
    }
  }
  _exports.default = _default;
});

define("views/attachment/fields/name", ["exports", "views/fields/varchar"], function (_exports, _varchar) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _varchar = _interopRequireDefault(_varchar);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _varchar.default {
    detailTemplate = 'attachment/fields/name/detail';
    data() {
      const data = super.data();
      let url = this.getBasePath() + '?entryPoint=download&id=' + this.model.id;
      if (this.getUser().get('portalId')) {
        url += '&portalId=' + this.getUser().get('portalId');
      }
      data.url = url;
      return data;
    }
  }
  _exports.default = _default;
});

define("views/attachment/fields/name-for-stream", ["exports", "model", "views/fields/file", "views/fields/base"], function (_exports, _model, _file, _base) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _model = _interopRequireDefault(_model);
  _file = _interopRequireDefault(_file);
  _base = _interopRequireDefault(_base);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _base.default {
    // language=Handlebars
    listTemplateContent = `
        <span>{{{subField}}}</span>
    `;
    prepare() {
      const model = new _model.default({
        fileId: this.model.id,
        fileName: this.model.attributes.name,
        fileType: this.model.attributes.type
      });
      const view = new _file.default({
        name: 'file',
        model: model,
        params: {
          showPreview: true,
          listPreviewSize: 'small'
        },
        mode: 'list'
      });
      return this.assignView('subField', view);
    }
  }
  _exports.default = _default;
});

define("views/address-map/view", ["exports", "views/main"], function (_exports, _main) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _main = _interopRequireDefault(_main);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class AddressMapView extends _main.default {
    templateContent = `
        <div class="header page-header">{{{header}}}</div>
        <div class="map-container">{{{map}}}</div>
    `;
    setup() {
      this.scope = this.model.entityType;
      this.createView('header', 'views/header', {
        model: this.model,
        fullSelector: '#main > .header',
        scope: this.model.entityType,
        fontSizeFlexible: true
      });
    }
    afterRender() {
      const field = this.options.field;
      const viewName = this.model.getFieldParam(field + 'Map', 'view') || this.getFieldManager().getViewName('map');
      this.createView('map', viewName, {
        model: this.model,
        name: field + 'Map',
        selector: '.map-container',
        height: this.getHelper().calculateContentContainerHeight(this.$el.find('.map-container'))
      }, view => {
        view.render();
      });
    }
    getHeader() {
      let name = this.model.get('name');
      if (!name) {
        name = this.model.id;
      }
      const recordUrl = `#${this.model.entityType}/view/${this.model.id}`;
      const scopeLabel = this.getLanguage().translate(this.model.entityType, 'scopeNamesPlural');
      const fieldLabel = this.translate(this.options.field, 'fields', this.model.entityType);
      const rootUrl = this.options.rootUrl || this.options.params.rootUrl || '#' + this.model.entityType;
      const $name = $('<a>').attr('href', recordUrl).append($('<span>').addClass('font-size-flexible title').text(name));
      if (this.model.get('deleted')) {
        $name.css('text-decoration', 'line-through');
      }
      const $root = $('<span>').append($('<a>').attr('href', rootUrl).addClass('action').attr('data-action', 'navigateToRoot').text(scopeLabel));
      const headerIconHtml = this.getHeaderIconHtml();
      if (headerIconHtml) {
        $root.prepend(headerIconHtml);
      }
      const $field = $('<span>').text(fieldLabel);
      return this.buildHeaderHtml([$root, $name, $field]);
    }
  }
  var _default = _exports.default = AddressMapView;
});

define("views/action-history-record/record/list", ["exports", "views/record/list"], function (_exports, _list) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _list = _interopRequireDefault(_list);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _list.default {
    rowActionsView = 'views/record/row-actions/view-and-remove';
    massActionList = ['remove', 'export'];
  }
  _exports.default = _default;
});

define("views/action-history-record/modals/detail", ["exports", "views/modals/detail"], function (_exports, _detail) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _detail = _interopRequireDefault(_detail);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _detail.default {
    fullFormDisabled = true;
    editDisabled = true;
    sideDisabled = true;
  }
  _exports.default = _default;
});

define("views/action-history-record/fields/target", ["exports", "views/fields/link-parent"], function (_exports, _linkParent) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _linkParent = _interopRequireDefault(_linkParent);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ActionHistoryRecordTargetFieldView extends _linkParent.default {
    displayScopeColorInListMode = true;
    ignoreScopeList = ['Preferences', 'ExternalAccount', 'Notification', 'Note', 'ArrayValue'];
    setup() {
      super.setup();
      this.foreignScopeList = this.getMetadata().getScopeEntityList().filter(item => {
        if (!this.getUser().isAdmin() && !this.getAcl().checkScopeHasAcl(item)) {
          return false;
        }
        if (this.ignoreScopeList.includes(item)) {
          return false;
        }
        if (!this.getAcl().checkScope(item)) {
          return false;
        }
        return true;
      });
      this.getLanguage().sortEntityList(this.foreignScopeList);
      this.foreignScope = this.model.get(this.typeName) || this.foreignScopeList[0];
    }
  }
  var _default = _exports.default = ActionHistoryRecordTargetFieldView;
});

define("views/action-history-record/fields/target-type", ["exports", "views/fields/enum"], function (_exports, _enum) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _enum = _interopRequireDefault(_enum);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _enum.default {
    setupOptions() {
      super.setupOptions();
      this.params.options = this.getMetadata().getScopeEntityList();
    }
  }
  _exports.default = _default;
});

define("helpers/misc/list-select-attributes", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 helpers/misc/stored-text-search */

  class _default {
    /**
     * @param {module:storage} storage
     * @param {string} scope
     * @param {Number} [maxCount]
     */
    constructor(scope, storage, maxCount) {
      this.scope = scope;
      this.storage = storage;
      this.key = 'textSearches';
      this.maxCount = maxCount || 100;
      /** @type {string[]|null} */
      this.list = null;
    }

    /**
     * Match.
     *
     * @param {string} text
     * @param {Number} [limit]
     * @return {string[]}
     */
    match(text, limit) {
      text = text.toLowerCase().trim();
      const list = this.get();
      const matchedList = [];
      for (const item of list) {
        if (item.toLowerCase().startsWith(text)) {
          matchedList.push(item);
        }
        if (limit !== undefined && matchedList.length === limit) {
          break;
        }
      }
      return matchedList;
    }

    /**
     * Get stored text filters.
     *
     * @private
     * @return {string[]}
     */
    get() {
      if (this.list === null) {
        this.list = this.getFromStorage();
      }
      return this.list;
    }

    /**
     * @private
     * @return {string[]}
     */
    getFromStorage() {
      /** @var {string[]} */
      return this.storage.get(this.key, this.scope) || [];
    }

    /**
     * Store a text filter.
     *
     * @param {string} text
     */
    store(text) {
      text = text.trim();
      let list = this.getFromStorage();
      const index = list.indexOf(text);
      if (index !== -1) {
        list.splice(index, 1);
      }
      list.unshift(text);
      if (list.length > this.maxCount) {
        list = list.slice(0, this.maxCount);
      }
      this.list = list;
      this.storage.set(this.key, this.scope, list);
    }

    /**
     * Remove a text filter.
     *
     * @param {string} text
     */
    remove(text) {
      text = text.trim();
      const list = this.getFromStorage();
      const index = list.indexOf(text);
      if (index === -1) {
        return;
      }
      list.splice(index, 1);
      this.list = list;
      this.storage.set(this.key, this.scope, list);
    }
  }
  _exports.default = _default;
});

define("helpers/misc/foreign-field-params", ["exports", "di", "metadata"], function (_exports, _di, _metadata) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _metadata = _interopRequireDefault(_metadata);
  let _init_metadata, _init_extra_metadata;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
  /**
   * @since 9.1.1
   * @internal For future use.
   */
  class _Class {
    static #_ = [_init_metadata, _init_extra_metadata] = _applyDecs(this, [], [[(0, _di.inject)(_metadata.default), 0, "metadata"]]).e;
    constructor() {
      _init_extra_metadata(this);
    }
    /**
     * @type {Metadata}
     * @private
     */
    metadata = _init_metadata(this);

    /**
     * Get foreign field params.
     *
     * @param {string} entityType
     * @param {string} field
     * @return {Record|null}
     */
    get(entityType, field) {
      /** @type {Record|null} */
      const params = this.metadata.get(`entityDefs.${entityType}.fields.${field}`);
      if (!params) {
        return null;
      }
      const foreignField = params.field;
      const link = params.link;
      const foreignEntityType = this.metadata.get(`entityDefs.${entityType}.links.${link}.entity`);
      if (!foreignEntityType) {
        return null;
      }
      return this.metadata.get(`entityDefs.${foreignEntityType}.links.${foreignField}`);
    }
  }
  _exports.default = _Class;
});

define("helpers/misc/authentication-provider", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 module:helpers/misc/authentication-provider */

  class _default {
    /**
     * @param {module:views/record/detail} view A view.
     */
    constructor(view) {
      /**
       * @private
       * @type {module:views/record/detail}
       */
      this.view = view;
      this.metadata = view.getMetadata();

      /**
       * @private
       * @type {module:model}
       */
      this.model = view.model;

      /** @var {Object.<string, Object.<string, *>>} defs */
      const defs = view.getMetadata().get(['authenticationMethods']) || {};

      /**
       * @private
       * @type {string[]}
       */
      this.methodList = Object.keys(defs).filter(item => {
        /** @var {Object.<string, *>} */
        const data = defs[item].provider || {};
        return data.isAvailable;
      });

      /** @private */
      this.authFields = {};

      /** @private */
      this.dynamicLogicDefs = {
        fields: {},
        panels: {}
      };
    }

    /**
     * @param {function(): void} callback
     */
    setupPanelsVisibility(callback) {
      this.handlePanelsVisibility(callback);
      this.view.listenTo(this.model, 'change:method', () => this.handlePanelsVisibility(callback));
    }

    /**
     * @private
     * @param {string} method
     * @param {string} param
     * @return {*}
     */
    getFromMetadata(method, param) {
      return this.metadata.get(['authenticationMethods', method, 'provider', param]) || this.metadata.get(['authenticationMethods', method, 'settings', param]);
    }

    /**
     * @return {Object}
     */
    setupMethods() {
      this.methodList.forEach(method => this.setupMethod(method));
      return this.dynamicLogicDefs;
    }

    /**
     * @private
     */
    setupMethod(method) {
      /** @var {string[]} */
      let fieldList = this.getFromMetadata(method, 'fieldList') || [];
      fieldList = fieldList.filter(item => this.model.hasField(item));
      this.authFields[method] = fieldList;
      const mDynamicLogicFieldsDefs = (this.getFromMetadata(method, 'dynamicLogic') || {}).fields || {};
      for (const f in mDynamicLogicFieldsDefs) {
        if (!fieldList.includes(f)) {
          continue;
        }
        const defs = this.modifyDynamicLogic(mDynamicLogicFieldsDefs[f]);
        this.dynamicLogicDefs.fields[f] = Espo.Utils.cloneDeep(defs);
      }
    }

    /**
     * @private
     */
    modifyDynamicLogic(defs) {
      defs = Espo.Utils.clone(defs);
      if (Array.isArray(defs)) {
        return defs.map(item => this.modifyDynamicLogic(item));
      }
      if (typeof defs === 'object') {
        const o = {};
        for (const property in defs) {
          let value = defs[property];
          if (property === 'attribute' && value === 'authenticationMethod') {
            value = 'method';
          }
          o[property] = this.modifyDynamicLogic(value);
        }
        return o;
      }
      return defs;
    }
    modifyDetailLayout(layout) {
      this.methodList.forEach(method => {
        let mLayout = this.getFromMetadata(method, 'layout');
        if (!mLayout) {
          return;
        }
        mLayout = Espo.Utils.cloneDeep(mLayout);
        mLayout.name = method;
        this.prepareLayout(mLayout, method);
        layout.push(mLayout);
      });
    }
    prepareLayout(layout, method) {
      layout.rows.forEach(row => {
        row.filter(item => !item.noLabel && !item.labelText && item.name).forEach(item => {
          if (item === null) {
            return;
          }
          const labelText = this.view.translate(item.name, 'fields', 'Settings');
          item.options = item.options || {};
          if (labelText && labelText.toLowerCase().indexOf(method.toLowerCase() + ' ') === 0) {
            item.labelText = labelText.substring(method.length + 1);
          }
          item.options.tooltipText = this.view.translate(item.name, 'tooltips', 'Settings');
        });
      });
      layout.rows = layout.rows.map(row => {
        row = row.map(cell => {
          if (cell && cell.name && !this.model.hasField(cell.name)) {
            return false;
          }
          return cell;
        });
        return row;
      });
    }

    /**
     * @private
     * @param {function(): void} callback
     */
    handlePanelsVisibility(callback) {
      const authenticationMethod = this.model.get('method');
      this.methodList.forEach(method => {
        const fieldList = this.authFields[method] || [];
        if (method !== authenticationMethod) {
          this.view.hidePanel(method);
          fieldList.forEach(field => {
            this.view.hideField(field);
          });
          return;
        }
        this.view.showPanel(method);
        fieldList.forEach(field => this.view.showField(field));
        callback();
      });
    }
  }
  _exports.default = _default;
});

define("handlers/working-time-range", ["exports", "bullbone"], function (_exports, _bullbone) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * @mixes Bull.Events
   */
  class WorkingTimeRangeHandler {
    constructor(view) {
      /** @type {module:views/record/edit} */
      this.view = view;
    }
    process() {
      this.listenTo(this.view.model, 'change:dateStart', (model, value, o) => {
        if (!o.ui || model.get('dateEnd')) {
          return;
        }
        setTimeout(() => model.set('dateEnd', value), 50);
      });
    }
  }
  Object.assign(WorkingTimeRangeHandler.prototype, _bullbone.Events);
  var _default = _exports.default = WorkingTimeRangeHandler;
});

define("handlers/navbar-menu", ["exports", "action-handler"], function (_exports, _actionHandler) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _actionHandler = _interopRequireDefault(_actionHandler);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NavbarMenuHandler extends _actionHandler.default {
    logout() {
      this.view.getRouter().logout();
    }

    // noinspection JSUnusedGlobalSymbols
    lastViewed() {
      Espo.Ui.notifyWait();
      this.view.createView('dialog', 'views/modals/last-viewed', {}, view => {
        view.render();
        Espo.Ui.notify(false);
      });
    }
  }
  var _default = _exports.default = NavbarMenuHandler;
});

define("handlers/import", ["exports", "action-handler"], function (_exports, _actionHandler) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _actionHandler = _interopRequireDefault(_actionHandler);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ImportHandler extends _actionHandler.default {
    // noinspection JSUnusedGlobalSymbols
    errorExport() {
      Espo.Ajax.postRequest(`Import/${this.view.model.id}/exportErrors`).then(data => {
        if (!data.attachmentId) {
          const message = this.view.translate('noErrors', 'messages', 'Import');
          Espo.Ui.warning(message);
          return;
        }
        window.location = this.view.getBasePath() + '?entryPoint=download&id=' + data.attachmentId;
      });
    }
  }
  var _default = _exports.default = ImportHandler;
});

define("handlers/email-filter", ["exports", "dynamic-handler"], function (_exports, _dynamicHandler) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _dynamicHandler = _interopRequireDefault(_dynamicHandler);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailFilterHandler extends _dynamicHandler.default {
    init() {
      if (this.model.isNew()) {
        if (!this.recordView.getUser().isAdmin()) {
          this.recordView.hideField('isGlobal');
        }
      }
      if (!this.model.isNew() && !this.recordView.getUser().isAdmin() && !this.model.get('isGlobal')) {
        this.recordView.hideField('isGlobal');
      }
      if (this.model.isNew() && !this.model.get('parentId')) {
        this.model.set('parentType', 'User');
        this.model.set('parentId', this.recordView.getUser().id);
        this.model.set('parentName', this.recordView.getUser().get('name'));
        if (!this.recordView.getUser().isAdmin()) {
          this.recordView.setFieldReadOnly('parent');
        }
      } else if (this.model.get('parentType') && !this.recordView.options.duplicateSourceId) {
        this.recordView.setFieldReadOnly('parent');
        this.recordView.setFieldReadOnly('isGlobal');
      }
      this.recordView.listenTo(this.model, 'change:isGlobal', (model, value, o) => {
        if (!o.ui) {
          return;
        }
        if (value) {
          this.model.set({
            action: 'Skip',
            parentName: null,
            parentType: null,
            parentId: null,
            emailFolderId: null,
            groupEmailFolderId: null,
            markAsRead: false
          });
        }
      });
      this.recordView.listenTo(this.model, 'change:parentType', (model, value, o) => {
        if (!o.ui) {
          return;
        }

        // Avoiding side effects.
        setTimeout(() => {
          if (value !== 'User') {
            this.model.set('markAsRead', false);
          }
          if (value === 'EmailAccount') {
            this.model.set('action', 'Skip');
            this.model.set('emailFolderId', null);
            this.model.set('groupEmailFolderId', null);
            this.model.set('markAsRead', false);
            return;
          }
          if (value !== 'InboundEmail') {
            if (this.model.get('action') === 'Move to Group Folder') {
              this.model.set('action', 'Skip');
            }
            this.model.set('groupEmailFolderId', null);
            return;
          }
          if (value !== 'User') {
            if (this.model.get('action') === 'Move to Folder') {
              this.model.set('action', 'Skip');
            }
            this.model.set('groupFolderId', null);
          }
        }, 40);
      });
    }
  }
  var _default = _exports.default = EmailFilterHandler;
});

define("handlers/create-related", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * Prepares attributes for a related record that is being created.
   *
   * @abstract
   */
  class CreateRelatedHandler {
    /**
     * @param {module:view-helper} viewHelper
     */
    constructor(viewHelper) {
      // noinspection JSUnusedGlobalSymbols
      this.viewHelper = viewHelper;
    }

    /**
     * Get attributes for a new record.
     *
     * @abstract
     * @param {module:model} model A model.
     * @return {Promise<Object.<string, *>>} Attributes.
     */
    getAttributes(model) {
      return Promise.resolve({});
    }
  }
  var _default = _exports.default = CreateRelatedHandler;
});

define("handlers/user/select-contact", ["exports", "handlers/select-related"], function (_exports, _selectRelated) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _selectRelated = _interopRequireDefault(_selectRelated);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _selectRelated.default {
    /**
     * @param {import('model').default} model
     */
    async getFilters(model) {
      /** @type {string[]|null} */
      const accountIds = model.attributes.accountsIds;
      if (accountIds && accountIds.length) {
        return {
          advanced: {
            accounts: {
              field: 'accounts',
              type: 'linkedWith',
              value: accountIds,
              data: {
                nameHash: model.attributes.accountsNames || {}
              }
            }
          }
        };
      }
      return {};
    }
  }
  _exports.default = _default;
});

define("handlers/user/change-team-position-row-action", ["exports", "handlers/row-action", "views/user/modals/select-position"], function (_exports, _rowAction, _selectPosition) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _rowAction = _interopRequireDefault(_rowAction);
  _selectPosition = _interopRequireDefault(_selectPosition);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class ChangeUserTeamPositionRowActionHandler extends _rowAction.default {
    async process(model, action) {
      if (!model.collection || !model.collection.parentModel) {
        console.error(`Team model cannot be obtained.`);
        return;
      }
      const team = model.collection.parentModel;

      /** @type {string[]} */
      const positionList = team.attributes.positionList || [];
      const position = model.attributes.teamRole;
      const view = new _selectPosition.default({
        position: position,
        positionList: positionList,
        name: model.attributes.name,
        onApply: position => {
          this.savePosition(team.id, model, position);
        }
      });
      await this.view.assignView('dialog', view);
      await view.render();
    }
    isAvailable(model, action) {
      if (!model.collection || !model.collection.parentModel) {
        return false;
      }
      if (!this.view.getAcl().checkModel(model, 'edit')) {
        return false;
      }
      if (!this.view.getUser().isAdmin()) {
        return false;
      }
      return true;
    }

    /**
     * @private
     * @param {string} teamId
     * @param {import('model').default} model
     * @param {string|null} position
     */
    async savePosition(teamId, model, position) {
      Espo.Ui.notifyWait();
      await Espo.Ajax.putRequest(`Team/${teamId}/userPosition`, {
        id: model.id,
        position: position
      });
      model.setMultiple({
        teamRole: position
      });
      Espo.Ui.success(this.view.translate('Saved'));
    }
  }
  _exports.default = ChangeUserTeamPositionRowActionHandler;
});

define("handlers/select-related/same-account", ["exports", "handlers/select-related"], function (_exports, _selectRelated) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _selectRelated = _interopRequireDefault(_selectRelated);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class SameAccountSelectRelatedHandler extends _selectRelated.default {
    /**
     * @param {module:model} model
     * @return {Promise<module:handlers/select-related~filters>}
     */
    getFilters(model) {
      const advanced = {};
      let accountId = null;
      let accountName = null;
      if (model.get('accountId')) {
        accountId = model.get('accountId');
        accountName = model.get('accountName');
      }
      if (!accountId && model.get('parentType') === 'Account' && model.get('parentId')) {
        accountId = model.get('parentId');
        accountName = model.get('parentName');
      }
      if (accountId) {
        advanced.account = {
          attribute: 'accountId',
          type: 'equals',
          value: accountId,
          data: {
            type: 'is',
            nameValue: accountName
          }
        };
      }
      return Promise.resolve({
        advanced: advanced
      });
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = SameAccountSelectRelatedHandler;
});

define("handlers/select-related/same-account-many", ["exports", "handlers/select-related"], function (_exports, _selectRelated) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _selectRelated = _interopRequireDefault(_selectRelated);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class SameAccountManySelectRelatedHandler extends _selectRelated.default {
    /**
     * @param {module:model} model
     * @return {Promise<module:handlers/select-related~filters>}
     */
    getFilters(model) {
      const advanced = {};
      let accountId = null;
      let accountName = null;
      if (model.get('accountId')) {
        accountId = model.get('accountId');
        accountName = model.get('accountName');
      }
      if (!accountId && model.get('parentType') === 'Account' && model.get('parentId')) {
        accountId = model.get('parentId');
        accountName = model.get('parentName');
      }
      if (accountId) {
        const nameHash = {};
        nameHash[accountId] = accountName;
        advanced.accounts = {
          field: 'accounts',
          type: 'linkedWith',
          value: [accountId],
          data: {
            nameHash: nameHash
          }
        };
      }
      return Promise.resolve({
        advanced: advanced
      });
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = SameAccountManySelectRelatedHandler;
});

define("handlers/record/view-user-access", ["exports", "views/modals/related-list"], function (_exports, _relatedList) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _relatedList = _interopRequireDefault(_relatedList);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default {
    toShow;
    constructor(/** import('views/record/detail').default */view) {
      this.view = view;
      this.metadata = /** @type {module:metadata} */view.getMetadata();
      this.entityType = this.view.entityType;
      this.model = this.view.model;
      const level = this.view.getAcl().getPermissionLevel('user');
      this.toShow = (level === 'all' || level === 'team') && (this.metadata.get(`scopes.${this.entityType}.object`) || this.metadata.get(`scopes.${this.entityType}.acl`));
      this.view.getAcl().checkScope('User');
    }
    isAvailable() {
      return this.toShow;
    }
    async show() {
      const actionList = this.getActionList();

      /** @type {Record[]} */
      const listLayout = [{
        name: 'name',
        link: true,
        view: 'views/user/fields/name'
      }];

      //const width = Math.round((100.0 - 40) / (actionList.length + 1));

      actionList.forEach(action => {
        listLayout.push({
          name: 'recordAccessLevel' + action,
          customLabel: this.view.translate(action, 'recordActions'),
          view: 'views/user/fields/record-access-level',
          notSortable: true,
          width: 16
        });
      });
      const view = new _relatedList.default({
        model: this.model,
        link: 'usersAccess',
        entityType: 'User',
        title: this.view.translate('View User Access'),
        url: `${this.entityType}/${this.model.id}/usersAccess`,
        createDisabled: true,
        selectDisabled: true,
        massActionsDisabled: true,
        maxSize: this.view.getConfig().get('recordsPerPageSmall'),
        rowActionsView: null,
        listLayout: listLayout,
        filter: 'active'
      });
      await this.view.assignView('dialog', view);
      await view.render();
    }

    /**
     * @private
     * @return {string[]}
     */
    getActionList() {
      /** @type {string[]} */
      let actionList = this.metadata.get(`scopes.${this.entityType}.aclActionList`);
      if (!actionList) {
        actionList = ['read', 'edit', 'delete'];
        if (this.metadata.get(`scopes.${this.entityType}.stream`)) {
          actionList.push('stream');
        }
      }
      return actionList.filter(it => it !== 'create');
    }
  }
  _exports.default = _default;
});

define("handlers/record/view-audit-log", ["exports", "views/stream/modals/view-audit-log"], function (_exports, _viewAuditLog) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _viewAuditLog = _interopRequireDefault(_viewAuditLog);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ViewAuditLogHandler {
    constructor(/** import('views/record/detail').default */view) {
      this.view = view;
      this.metadata = /** @type {module:metadata} */view.getMetadata();
      this.entityType = this.view.entityType;
      this.model = /** @type {module:model} */this.view.model;
      this.hasAudited = this.metadata.get(`scopes.${this.entityType}.stream`) && this.metadata.get(`scopes.${this.entityType}.statusField`) || this.model.getFieldList().find(field => this.model.getFieldParam(field, 'audited')) !== undefined;
      if (this.entityType === 'User' && !this.view.getUser().isAdmin()) {
        this.hasAudited = false;
      }
      if (this.view.getUser().isPortal()) {
        this.hasAudited = false;
      }
      if (this.view.getAcl().getPermissionLevel('audit') !== 'yes') {
        this.hasAudited = false;
      }
    }
    isAvailable() {
      return this.hasAudited;
    }
    show() {
      const view = new _viewAuditLog.default({
        model: this.model
      });
      this.view.assignView('dialog', view).then(() => {
        view.render();
      });
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = ViewAuditLogHandler;
});

define("handlers/note/record-detail-setup", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default {
    /**
     * @param {import('views/record/detail').default} view
     */
    constructor(view) {
      this.view = view;
    }
    process() {
      this.controlFields();
      this.view.listenTo(this.view.model, 'sync', () => this.controlFields());
    }
    controlFields() {
      const attachmentsIds = this.view.model.attributes.attachmentsIds;
      if (!attachmentsIds || !attachmentsIds.length) {
        this.view.hideField('attachments');
        return;
      }
      this.view.showField('attachments');
    }
  }
  _exports.default = _default;
});

define("handlers/model/defaults-preparator", ["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * @abstract
   */
  class DefaultsPreparator {
    /**
     * @param {import('view-helper').default} viewHelper
     */
    constructor(viewHelper) {
      /** @protected */
      this.viewHelper = viewHelper;
    }

    // noinspection JSUnusedLocalSymbols
    /**
     * Prepare default attributes.
     *
     * @param {import('model').default} model A model.
     * @return {Promise<Record>}
     */
    prepare(model) {
      return Promise.resolve({});
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = DefaultsPreparator;
});

define("handlers/map/google-maps-renderer", ["exports", "handlers/map/renderer"], function (_exports, _renderer) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _renderer = _interopRequireDefault(_renderer);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class GoogleMapsRenderer extends _renderer.default {
    /**
     * @param {module:handlers/map/renderer~addressData} addressData
     */
    render(addressData) {
      if ('google' in window && window.google.maps) {
        this.initMapGoogle(addressData);
        return;
      }

      // noinspection SpellCheckingInspection
      if (typeof window.mapapiloaded === 'function') {
        // noinspection SpellCheckingInspection
        const mapapiloaded = window.mapapiloaded;

        // noinspection SpellCheckingInspection
        window.mapapiloaded = () => {
          this.initMapGoogle(addressData);
          mapapiloaded();
        };
        return;
      }

      // noinspection SpellCheckingInspection
      window.mapapiloaded = () => this.initMapGoogle(addressData);
      let src = 'https://maps.googleapis.com/maps/api/js?callback=mapapiloaded&loading=async&v=weekly&libraries=marker';
      const apiKey = this.view.getConfig().get('googleMapsApiKey');
      if (apiKey) {
        src += '&key=' + apiKey;
      }
      const scriptElement = document.createElement('script');
      scriptElement.setAttribute('defer', 'defer');
      scriptElement.src = src;
      document.head.appendChild(scriptElement);
    }

    /**
     * @param {module:handlers/map/renderer~addressData} addressData
     */
    initMapGoogle(addressData) {
      // noinspection JSUnresolvedReference
      const geocoder = new google.maps.Geocoder();
      let map;
      const mapId = this.view.getConfig().get('googleMapsMapId') || 'DEMO_MAP_ID';
      try {
        // noinspection SpellCheckingInspection,JSUnresolvedReference
        map = new google.maps.Map(this.view.$el.find('.map').get(0), {
          zoom: 15,
          center: {
            lat: 0,
            lng: 0
          },
          scrollwheel: false,
          mapId: mapId
        });
      } catch (e) {
        console.error(e.message);
        return;
      }
      let address = '';
      if (addressData.street) {
        address += addressData.street;
      }
      if (addressData.city) {
        if (address !== '') {
          address += ', ';
        }
        address += addressData.city;
      }
      if (addressData.state) {
        if (address !== '') {
          address += ', ';
        }
        address += addressData.state;
      }
      if (addressData.postalCode) {
        if (addressData.state || addressData.city) {
          address += ' ';
        } else {
          if (address) {
            address += ', ';
          }
        }
        address += addressData.postalCode;
      }
      if (addressData.country) {
        if (address !== '') {
          address += ', ';
        }
        address += addressData.country;
      }

      // noinspection JSUnresolvedReference
      geocoder.geocode({
        'address': address
      }, (results, status) => {
        // noinspection JSUnresolvedReference
        if (status === google.maps.GeocoderStatus.OK) {
          // noinspection JSUnresolvedReference
          map.setCenter(results[0].geometry.location);

          // noinspection JSUnresolvedReference
          new google.maps.marker.AdvancedMarkerElement({
            map: map,
            position: results[0].geometry.location
          });
        }
      });
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = GoogleMapsRenderer;
});

define("handlers/login/oidc", ["exports", "handlers/login", "js-base64"], function (_exports, _login, _jsBase) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _login = _interopRequireDefault(_login);
  _jsBase = _interopRequireDefault(_jsBase);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class OidcLoginHandler extends _login.default {
    /** @inheritDoc */
    process() {
      const proxy = window.open('about:blank', 'ConnectWithOAuth', 'location=0,status=0,width=800,height=800');
      Espo.Ui.notifyWait();
      return new Promise((resolve, reject) => {
        Espo.Ajax.getRequest('Oidc/authorizationData').then(data => {
          Espo.Ui.notify(false);
          this.processWithData(data, proxy).then(info => {
            const code = info.code;
            const nonce = info.nonce;
            const authString = _jsBase.default.encode('**oidc:' + code);
            const headers = {
              'Espo-Authorization': authString,
              'Authorization': 'Basic ' + authString,
              'X-Oidc-Authorization-Nonce': nonce
            };
            resolve(headers);
          }).catch(() => {
            proxy.close();
            reject();
          });
        }).catch(() => {
          Espo.Ui.notify(false);
          proxy.close();
          reject();
        });
      });
    }

    /**
     * @private
     * @param {{
     *  endpoint: string,
     *  clientId: string,
     *  redirectUri: string,
     *  scopes: string[],
     *  claims: ?string,
     *  prompt: 'login'|'consent'|'select_account',
     *  maxAge: ?Number,
     * }} data
     * @param {WindowProxy} proxy
     * @return {Promise<{code: string, nonce: string}>}
     */
    processWithData(data, proxy) {
      const state = (Math.random() + 1).toString(36).substring(4);
      const nonce = (Math.random() + 1).toString(36).substring(4);
      const params = {
        client_id: data.clientId,
        redirect_uri: data.redirectUri,
        response_type: 'code',
        scope: data.scopes.join(' '),
        state: state,
        nonce: nonce,
        prompt: data.prompt
      };
      if (data.maxAge || data.maxAge === 0) {
        params.max_age = data.maxAge;
      }
      if (data.claims) {
        params.claims = data.claims;
      }
      const partList = Object.entries(params).map(_ref => {
        let [key, value] = _ref;
        return key + '=' + encodeURIComponent(value);
      });
      const url = data.endpoint + '?' + partList.join('&');
      return this.processWindow(url, state, nonce, proxy);
    }

    /**
     * @private
     * @param {string} url
     * @param {string} state
     * @param {string} nonce
     * @param {WindowProxy} proxy
     * @return {Promise<{code: string, nonce: string}>}
     */
    processWindow(url, state, nonce, proxy) {
      proxy.location.href = url;
      return new Promise((resolve, reject) => {
        const fail = () => {
          window.clearInterval(interval);
          if (!proxy.closed) {
            proxy.close();
          }
          reject();
        };
        const interval = window.setInterval(() => {
          if (proxy.closed) {
            fail();
            return;
          }
          let url;
          try {
            url = proxy.location.href;
          } catch (e) {
            return;
          }
          if (!url) {
            return;
          }
          const parsedData = this.parseWindowUrl(url);
          if (!parsedData) {
            fail();
            Espo.Ui.error('Could not parse URL', true);
            return;
          }
          if ((parsedData.error || parsedData.code) && parsedData.state !== state) {
            fail();
            Espo.Ui.error('State mismatch', true);
            return;
          }
          if (parsedData.error) {
            fail();
            Espo.Ui.error(parsedData.errorDescription || this.loginView.translate('Error'), true);
            return;
          }
          if (parsedData.code) {
            window.clearInterval(interval);
            proxy.close();
            resolve({
              code: parsedData.code,
              nonce: nonce
            });
          }
        }, 300);
      });
    }

    /**
     * @param {string} url
     * @return {?{
     *     code: ?string,
     *     state: ?string,
     *     error: ?string,
     *     errorDescription: ?string,
     * }}
     */
    parseWindowUrl(url) {
      try {
        const params = new URL(url).searchParams;
        return {
          code: params.get('code'),
          state: params.get('state'),
          error: params.get('error'),
          errorDescription: params.get('errorDescription')
        };
      } catch (e) {
        return null;
      }
    }
  }
  var _default = _exports.default = OidcLoginHandler;
});

define("handlers/email/select-user", ["exports", "handlers/select-related"], function (_exports, _selectRelated) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _selectRelated = _interopRequireDefault(_selectRelated);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class _default extends _selectRelated.default {
    /**
     * @param {import('model').default} model
     * @return {Promise<module:handlers/select-related~filters>}
     */
    async getFilters(model) {
      const acl = this.viewHelper.acl;
      const permission = acl.getPermissionLevel('assignment');

      /** @type {string[]} */
      const boolFilterList = [];
      if (permission === 'team') {
        boolFilterList.push('onlyMyTeam');
      } else if (permission === 'own') {
        boolFilterList.push('onlyMe');
      }
      return {
        bool: boolFilterList
      };
    }
  }
  _exports.default = _default;
});

define("handlers/email/list-actions", ["exports", "action-handler", "views/email/modals/import-eml"], function (_exports, _actionHandler, _importEml) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _actionHandler = _interopRequireDefault(_actionHandler);
  _importEml = _interopRequireDefault(_importEml);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailListActionsHandler extends _actionHandler.default {
    // noinspection JSUnusedGlobalSymbols
    async importEml() {
      const view = new _importEml.default();
      await this.view.assignView('dialog', view);
      await view.render();
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @return {boolean}
     */
    checkImportEml() {
      const acl = this.view.getAcl();
      return acl.checkScope('Email', 'create') && acl.checkScope('Import');
    }
  }
  var _default = _exports.default = EmailListActionsHandler;
});

define("handlers/admin/address-country/populate-defaults", ["exports", "action-handler"], function (_exports, _actionHandler) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _actionHandler = _interopRequireDefault(_actionHandler);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class PopulateDefaultsHandler extends _actionHandler.default {
    async populate() {
      const confirmMessage = this.view.translate('confirmPopulateDefaults', 'messages', 'AddressCountry');
      await this.view.confirm(confirmMessage);
      Espo.Ui.notifyWait();
      await Espo.Ajax.postRequest('AddressCountry/action/populateDefaults');
      await this.view.collection.fetch();
      Espo.Ui.success(this.view.translate('Done'));
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = PopulateDefaultsHandler;
});

define("controllers/user", ["exports", "controllers/record"], function (_exports, _record) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _record = _interopRequireDefault(_record);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserController extends _record.default {
    getCollection(usePreviouslyFetched) {
      return super.getCollection().then(collection => {
        collection.data.userType = 'internal';
        return collection;
      });
    }

    /**
     * @protected
     * @param {Object} options
     * @param {module:models/user} model
     * @param {string} view
     */
    createViewView(options, model, view) {
      if (model.get('deleted')) {
        view = 'views/deleted-detail';
        super.createViewView(options, model, view);
        return;
      }
      if (model.isPortal()) {
        this.getRouter().dispatch('PortalUser', 'view', {
          id: model.id,
          model: model
        });
        return;
      }
      if (model.isApi()) {
        this.getRouter().dispatch('ApiUser', 'view', {
          id: model.id,
          model: model
        });
        return;
      }
      super.createViewView(options, model, view);
    }
  }
  var _default = _exports.default = UserController;
});

define("controllers/team", ["exports", "controllers/record"], function (_exports, _record) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _record = _interopRequireDefault(_record);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class TeamController extends _record.default {
    checkAccess(action) {
      if (action === 'read') {
        return true;
      }
      if (this.getUser().isAdmin()) {
        return true;
      }
      return false;
    }
  }
  var _default = _exports.default = TeamController;
});

define("controllers/stream", ["exports", "controller"], function (_exports, _controller) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _controller = _interopRequireDefault(_controller);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class StreamController extends _controller.default {
    defaultAction = 'index';

    // noinspection JSUnusedGlobalSymbols
    actionIndex() {
      const key = 'index';
      const isReturn = this.getRouter().backProcessed;
      if (!isReturn) {
        this.clearStoredMainView(key);
      }
      this.main('views/stream', {
        displayTitle: true
      }, undefined, {
        key: key,
        useStored: isReturn
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionPosts() {
      const key = 'index';
      const isReturn = this.getRouter().backProcessed;
      if (!isReturn) {
        this.clearStoredMainView(key);
      }
      this.main('views/stream', {
        displayTitle: true,
        filter: 'posts'
      }, undefined, {
        key: key,
        useStored: isReturn
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionUpdates() {
      const key = 'index';
      const isReturn = this.getRouter().backProcessed;
      if (!isReturn) {
        this.clearStoredMainView(key);
      }
      this.main('views/stream', {
        displayTitle: true,
        filter: 'updates'
      }, undefined, {
        key: key,
        useStored: isReturn
      });
    }
  }
  var _default = _exports.default = StreamController;
});

define("controllers/record-tree", ["exports", "controllers/record"], function (_exports, _record) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _record = _interopRequireDefault(_record);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class RecordTreeController extends _record.default {
    defaultAction = 'listTree';
    beforeView(options) {
      super.beforeView(options);
      options = options || {};
      if (options.model) {
        options.model.unset('childCollection');
        options.model.unset('childList');
      }
    }

    // noinspection JSUnusedGlobalSymbols
    beforeListTree() {
      this.handleCheckAccess('read');
    }

    // noinspection JSUnusedGlobalSymbols
    actionListTree() {
      this.getCollection().then(collection => {
        collection.url = collection.entityType + '/action/listTree';
        this.main(this.getViewName('listTree'), {
          scope: this.name,
          collection: collection
        });
      });
    }
  }
  var _default = _exports.default = RecordTreeController;
});

define("controllers/preferences", ["exports", "controllers/record", "models/preferences"], function (_exports, _record, _preferences) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _record = _interopRequireDefault(_record);
  _preferences = _interopRequireDefault(_preferences);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class PreferencesController extends _record.default {
    defaultAction = 'own';
    getModel(callback, context) {
      const model = new _preferences.default({}, {
        defs: this.getMetadata().get('entityDefs.Preferences') || {}
      });
      model.setSettings(this.getConfig());
      if (callback) {
        callback.call(this, model);
      }
      return new Promise(resolve => {
        resolve(model);
      });
    }
    checkAccess(action) {
      return true;
    }

    // noinspection JSUnusedGlobalSymbols
    actionOwn() {
      this.actionEdit({
        id: this.getUser().id
      });
    }
    actionList(options) {}
  }
  var _default = _exports.default = PreferencesController;
});

define("controllers/portal-user", ["exports", "controllers/record"], function (_exports, _record) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _record = _interopRequireDefault(_record);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class PortalUserController extends _record.default {
    entityType = 'User';
    getCollection(usePreviouslyFetched) {
      return super.getCollection().then(collection => {
        collection.data.userType = 'portal';
        return collection;
      });
    }

    /**
     * @protected
     * @param {Object} options
     * @param {module:models/user} model
     * @param {string} view
     */
    createViewView(options, model, view) {
      if (!model.isPortal()) {
        if (model.isApi()) {
          this.getRouter().dispatch('ApiUser', 'view', {
            id: model.id,
            model: model
          });
          return;
        }
        this.getRouter().dispatch('User', 'view', {
          id: model.id,
          model: model
        });
        return;
      }
      super.createViewView(options, model, view);
    }
    actionCreate(options) {
      options = options || {};
      options.attributes = options.attributes || {};
      options.attributes.type = 'portal';
      super.actionCreate(options);
    }
    checkAccess(action) {
      if (this.getAcl().getPermissionLevel('portalPermission') === 'yes') {
        return true;
      }
      return false;
    }
  }
  var _default = _exports.default = PortalUserController;
});

define("controllers/password-change-request", ["exports", "controller"], function (_exports, _controller) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _controller = _interopRequireDefault(_controller);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class PasswordChangeRequestController extends _controller.default {
    // noinspection JSUnusedGlobalSymbols
    actionPasswordChange(options) {
      options = options || {};
      if (!options.id) {
        throw new Error();
      }
      this.entire('views/user/password-change-request', {
        requestId: options.id,
        strengthParams: options.strengthParams,
        notFound: options.notFound
      }, view => {
        view.render();
      });
    }
  }
  var _default = _exports.default = PasswordChangeRequestController;
});

define("controllers/notification", ["exports", "controller"], function (_exports, _controller) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _controller = _interopRequireDefault(_controller);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NotificationController extends _controller.default {
    defaultAction = 'index';

    // noinspection JSUnusedGlobalSymbols
    actionIndex() {
      this.main('views/notification/list', {}, view => {
        view.render();
      });
    }
  }
  var _default = _exports.default = NotificationController;
});

define("controllers/note", ["exports", "controller"], function (_exports, _controller) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _controller = _interopRequireDefault(_controller);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NoteController extends _controller.default {
    // noinspection JSUnusedGlobalSymbols
    /**
     * @param {Record} options
     */
    actionView(options) {
      const id = options.id;
      if (!id) {
        throw new Espo.Exceptions.NotFound();
      }
      const viewName = this.getMetadata().get(['clientDefs', this.name, 'views', 'detail']) || 'views/note/detail';
      let model;
      this.showLoadingNotification();
      this.modelFactory.create('Note').then(m => {
        model = m;
        model.id = id;
        return model.fetch({
          main: true
        });
      }).then(() => {
        this.hideLoadingNotification();
        this.main(viewName, {
          model: model
        });
      });
    }
  }
  var _default = _exports.default = NoteController;
});

define("controllers/login-as", ["exports", "controller"], function (_exports, _controller) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _controller = _interopRequireDefault(_controller);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class LoginAsController extends _controller.default {
    // noinspection JSUnusedGlobalSymbols
    /**
     * @param {Record} options
     */
    actionLogin(options) {
      const anotherUser = options.anotherUser;
      const username = options.username;
      if (!anotherUser) {
        throw new Error("No anotherUser.");
      }
      this.baseController.login({
        anotherUser: anotherUser,
        username: username
      });
      this.listenToOnce(this.baseController, 'login', () => {
        this.baseController.once('router-set', () => {
          const url = window.location.href.split('?')[0];
          window.location.replace(url);
        });
      });
    }
  }
  var _default = _exports.default = LoginAsController;
});

define("controllers/lead-capture-opt-in-confirmation", ["exports", "controller"], function (_exports, _controller) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _controller = _interopRequireDefault(_controller);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class LeadCaptureOptInConfirmationController extends _controller.default {
    // noinspection JSUnusedGlobalSymbols
    actionOptInConfirmationSuccess(data) {
      const viewName = this.getMetadata().get(['clientDefs', 'LeadCapture', 'optInConfirmationSuccessView']) || 'views/lead-capture/opt-in-confirmation-success';
      this.entire(viewName, {
        resultData: data
      }, view => {
        view.render();
      });
    }

    // noinspection JSUnusedGlobalSymbols
    actionOptInConfirmationExpired(data) {
      const viewName = this.getMetadata().get(['clientDefs', 'LeadCapture', 'optInConfirmationExpiredView']) || 'views/lead-capture/opt-in-confirmation-expired';
      this.entire(viewName, {
        resultData: data
      }, view => {
        view.render();
      });
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = LeadCaptureOptInConfirmationController;
});

define("controllers/lead-capture-form", ["exports", "controller", "views/lead-capture/form"], function (_exports, _controller, _form) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _controller = _interopRequireDefault(_controller);
  _form = _interopRequireDefault(_form);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  // noinspection JSUnusedGlobalSymbols
  class LeadCaptureFormController extends _controller.default {
    // noinspection JSUnusedGlobalSymbols
    actionShow(data) {
      this.prepareContainer();
      const view = new _form.default({
        formData: data
      });
      view.setSelector('body > .content');
      this.viewFactory.prepare(view, () => view.render());
    }

    /**
     * @private
     */
    prepareContainer() {
      // Prevents recaptcha removal.

      const existingContainer = document.body.querySelector('.container');
      existingContainer.remove();
      const container = document.createElement('div');
      container.classList.add('container', 'content');
      document.body.prepend(container);
    }
  }
  _exports.default = LeadCaptureFormController;
});

define("controllers/layout-set", ["exports", "controllers/record"], function (_exports, _record) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _record = _interopRequireDefault(_record);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class LayoutSetController extends _record.default {
    // noinspection JSUnusedGlobalSymbols
    /**
     * @param {Record} options
     */
    actionEditLayouts(options) {
      const id = options.id;
      if (!id) {
        throw new Error("ID not passed.");
      }
      this.main('views/layout-set/layouts', {
        layoutSetId: id,
        scope: options.scope,
        type: options.type
      });
    }
  }
  var _default = _exports.default = LayoutSetController;
});

define("controllers/last-viewed", ["exports", "controllers/record"], function (_exports, _record) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _record = _interopRequireDefault(_record);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class LastViewedController extends _record.default {
    entityType = 'ActionHistoryRecord';
    checkAccess(action) {
      return this.getAcl().check(this.entityType, action);
    }
  }

  // noinspection JSUnusedGlobalSymbols
  var _default = _exports.default = LastViewedController;
});

define("controllers/inbound-email", ["exports", "controllers/record"], function (_exports, _record) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _record = _interopRequireDefault(_record);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class InboundEmailController extends _record.default {
    checkAccess(action) {
      if (this.getUser().isAdmin()) {
        return true;
      }
      return false;
    }
  }
  var _default = _exports.default = InboundEmailController;
});

define("controllers/import", ["exports", "controllers/record"], function (_exports, _record) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _record = _interopRequireDefault(_record);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ImportController extends _record.default {
    defaultAction = 'index';
    storedData;
    checkAccessGlobal() {
      if (this.getAcl().checkScope('Import')) {
        return true;
      }
      return false;
    }
    checkAccess(action) {
      if (this.getAcl().checkScope('Import')) {
        return true;
      }
      return false;
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @param {{
     *     step?: int|string,
     *     fromAdmin?: boolean,
     *     formData?: Object
     * }} o
     */
    actionIndex(o) {
      o = o || {};
      let step = null;
      if (o.step) {
        step = parseInt(step);
      }
      let formData = null;
      let fileContents = null;
      if (o.formData) {
        this.storedData = undefined;
      }
      if (this.storedData) {
        formData = this.storedData.formData;
        fileContents = this.storedData.fileContents;
      }
      if (!formData) {
        step = null;
      }
      formData = formData || o.formData;
      this.main('views/import/index', {
        step: step,
        formData: formData,
        fileContents: fileContents,
        fromAdmin: o.fromAdmin
      }, /** module:views/import/index */view => {
        this.listenTo(view, 'change', () => {
          this.storedData = {
            formData: view.formData,
            fileContents: view.fileContents
          };
        });
        this.listenTo(view, 'done', () => {
          this.storedData = undefined;
        });
        view.render();
      });
    }
  }
  var _default = _exports.default = ImportController;
});

define("controllers/home", ["exports", "controller"], function (_exports, _controller) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _controller = _interopRequireDefault(_controller);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class HomeController extends _controller.default {
    // noinspection JSUnusedGlobalSymbols
    actionIndex() {
      this.main('views/home', null);
    }
  }
  var _default = _exports.default = HomeController;
});

define("controllers/global-stream", ["exports", "controller", "views/global-stream"], function (_exports, _controller, _globalStream) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _controller = _interopRequireDefault(_controller);
  _globalStream = _interopRequireDefault(_globalStream);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class GlobalStreamController extends _controller.default {
    // noinspection JSUnusedGlobalSymbols
    actionIndex() {
      const key = 'index';
      const isReturn = this.getRouter().backProcessed;
      if (!isReturn) {
        this.clearStoredMainView(key);
      }
      const view = new _globalStream.default();
      this.main(view, undefined, undefined, {
        key: key,
        useStored: isReturn
      });
    }
  }
  var _default = _exports.default = GlobalStreamController;
});

define("controllers/external-account", ["exports", "controller"], function (_exports, _controller) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _controller = _interopRequireDefault(_controller);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ExternalAccountController extends _controller.default {
    defaultAction = 'list';
    actionList() {
      this.collectionFactory.create('ExternalAccount', collection => {
        collection.once('sync', () => {
          this.main('ExternalAccount.Index', {
            collection: collection
          });
        });
        collection.fetch();
      });
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @param {{id: string}} options
     */
    actionEdit(options) {
      const id = options.id;
      this.collectionFactory.create('ExternalAccount', collection => {
        collection.once('sync', () => {
          this.main('ExternalAccount.Index', {
            collection: collection,
            id: id
          });
        });
        collection.fetch();
      });
    }
  }
  var _default = _exports.default = ExternalAccountController;
});

define("controllers/email", ["exports", "controllers/record"], function (_exports, _record) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _record = _interopRequireDefault(_record);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailController extends _record.default {
    prepareModelView(model, options) {
      super.prepareModelView(model, options);
      this.listenToOnce(model, 'after:send', () => {
        const key = 'list';
        const stored = this.getStoredMainView(key);
        if (stored) {
          this.clearStoredMainView(key);
        }
      });
    }
  }
  var _default = _exports.default = EmailController;
});

define("controllers/email-filter", ["exports", "controllers/record"], function (_exports, _record) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _record = _interopRequireDefault(_record);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailFilterController extends _record.default {
    actionCreate(options) {
      const attributes = {
        ...options.attributes
      };
      if (options.from) {
        attributes.from = options.from;
        attributes.name = options.from;
        options.attributes = attributes;
      }
      super.actionCreate(options);
    }
  }
  var _default = _exports.default = EmailFilterController;
});

define("controllers/dashboard", ["exports", "controller"], function (_exports, _controller) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _controller = _interopRequireDefault(_controller);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class DashboardController extends _controller.default {
    defaultAction = 'index';

    // noinspection JSUnusedGlobalSymbols
    actionIndex() {
      this.main('views/dashboard', {
        displayTitle: true
      }, view => {
        view.render();
      });
    }
  }
  var _default = _exports.default = DashboardController;
});

define("controllers/api-user", ["exports", "controllers/record"], function (_exports, _record) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _record = _interopRequireDefault(_record);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ApiUserController extends _record.default {
    entityType = 'User';
    getCollection(usePreviouslyFetched) {
      return super.getCollection().then(collection => {
        collection.data.userType = 'api';
        return collection;
      });
    }

    /**
     * @protected
     * @param {Object} options
     * @param {module:models/user} model
     * @param {string} view
     */
    createViewView(options, model, view) {
      if (!model.isApi()) {
        if (model.isPortal()) {
          this.getRouter().dispatch('PortalUser', 'view', {
            id: model.id,
            model: model
          });
          return;
        }
        this.getRouter().dispatch('User', 'view', {
          id: model.id,
          model: model
        });
        return;
      }
      super.createViewView(options, model, view);
    }
    actionCreate(options) {
      options = options || {};
      options.attributes = options.attributes || {};
      options.attributes.type = 'api';
      super.actionCreate(options);
    }
  }
  var _default = _exports.default = ApiUserController;
});

define("controllers/address-map", ["exports", "controller"], function (_exports, _controller) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _controller = _interopRequireDefault(_controller);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class AddressMapController extends _controller.default {
    defaultAction = 'index';

    // noinspection JSUnusedGlobalSymbols
    actionIndex() {
      this.error404();
    }

    // noinspection JSUnusedGlobalSymbols
    /**
     * @param {Object} o
     */
    actionView(o) {
      this.modelFactory.create(o.entityType).then(model => {
        model.id = o.id;
        model.fetch().then(() => {
          const viewName = this.getMetadata().get(['AddressMap', 'view']) || 'views/address-map/view';
          this.main(viewName, {
            model: model,
            field: o.field
          });
        });
      });
    }
  }
  var _default = _exports.default = AddressMapController;
});

define("controllers/about", ["exports", "controller"], function (_exports, _controller) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _controller = _interopRequireDefault(_controller);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class AboutController extends _controller.default {
    defaultAction = 'about';

    // noinspection JSUnusedGlobalSymbols
    actionAbout() {
      this.main('About', {}, view => {
        view.render();
      });
    }
  }
  var _default = _exports.default = AboutController;
});

define("collections/tree", ["exports", "collection"], function (_exports, _collection) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _collection = _interopRequireDefault(_collection);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 collections/tree */

  class TreeCollection extends _collection.default {
    /**
     * @return {TreeCollection}
     */
    createSeed() {
      const seed = new this.constructor();
      seed.url = this.url;
      seed.model = this.model;
      seed.name = this.name;
      seed.entityType = this.entityType;
      seed.defs = this.defs;
      return seed;
    }
    prepareAttributes(response, options) {
      const list = super.prepareAttributes(response, options);
      const seed = this.clone();
      seed.reset();
      this.path = response.path;
      /**
       * @type {{
       *     name: string,
       *     upperId?: string,
       *     upperName?: string,
       * }|null}
       */
      this.categoryData = response.data || null;
      const prepare = (list, depth) => {
        list.forEach(data => {
          data.depth = depth;
          const childCollection = this.createSeed();
          childCollection.parentId = data.id;
          if (data.childList) {
            if (data.childList.length) {
              prepare(data.childList, depth + 1);
              childCollection.set(data.childList);
              data.childCollection = childCollection;
              return;
            }
            data.childCollection = childCollection;
            return;
          }
          if (data.childList === null) {
            data.childCollection = null;
            return;
          }
          data.childCollection = childCollection;
        });
      };
      prepare(list, 0);
      return list;
    }
    fetch(options) {
      options = options || {};
      options.data = options.data || {};
      if (this.parentId) {
        options.data.parentId = this.parentId;
      }
      return super.fetch(options);
    }
    clone() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      options = {
        ...options
      };

      // Prevents recurring clone.
      options.withModels = false;
      return super.clone(options);
    }
  }
  var _default = _exports.default = TreeCollection;
});

define("collections/note", ["exports", "collection"], function (_exports, _collection) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _collection = _interopRequireDefault(_collection);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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 collections/note */

  class NoteCollection extends _collection.default {
    paginationByNumber = false;

    /**
     * @private
     * @type {string|null}
     */
    reactionsCheckDate = null;

    /**
     * @type {Record[]}
     */
    pinnedList;

    /**
     * @type {number}
     */
    reactionsCheckMaxSize = 0;

    /** @inheritDoc */
    prepareAttributes(response, params) {
      const total = this.total;
      const list = super.prepareAttributes(response, params);
      if (params.data && params.data.after) {
        this.total = total >= 0 && response.total >= 0 ? total + response.total : total;
      }
      if (response.pinnedList) {
        this.pinnedList = Espo.Utils.cloneDeep(response.pinnedList);
      }
      this.reactionsCheckDate = response.reactionsCheckDate;

      /** @type {Record[]} */
      const updatedReactions = response.updatedReactions;
      if (updatedReactions) {
        updatedReactions.forEach(item => {
          const model = this.get(item.id);
          if (!model) {
            return;
          }
          model.set(item);
        });
      }
      return list;
    }

    /**
     * Fetch new records.
     *
     * @param {Object} [options] Options.
     * @returns {Promise}
     */
    fetchNew(options) {
      options = options || {};
      options.data = options.data || {};
      options.fetchNew = true;
      options.noRebuild = true;
      options.lengthBeforeFetch = this.length;
      if (this.length) {
        options.data.after = this.models[0].get('createdAt');
        options.remove = false;
        options.at = 0;
        options.maxSize = null;
        if (this.reactionsCheckMaxSize) {
          options.data.reactionsAfter = this.reactionsCheckDate || options.data.after;
          options.data.reactionsCheckNoteIds = this.models.filter(model => model.attributes.type === 'Post').map(model => model.id).slice(0, this.reactionsCheckMaxSize).join(',');
        }
      }
      return this.fetch(options);
    }
    fetch(options) {
      options = {
        ...options
      };
      if (this.paginationByNumber && options.more) {
        options.more = false;
        options.data = options.data || {};
        const lastModel = this.models.at(this.length - 1);
        if (lastModel) {
          options.data.beforeNumber = lastModel.get('number');
        }
      }
      return super.fetch(options);
    }
  }
  var _default = _exports.default = NoteCollection;
});

define("acl-portal/preferences", ["exports", "acl-portal"], function (_exports, _aclPortal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _aclPortal = _interopRequireDefault(_aclPortal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class PreferencesAclPortal extends _aclPortal.default {
    checkIsOwner(model) {
      if (this.getUser().id === model.id) {
        return true;
      }
      return false;
    }
  }
  var _default = _exports.default = PreferencesAclPortal;
});

define("acl-portal/notification", ["exports", "acl-portal"], function (_exports, _aclPortal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _aclPortal = _interopRequireDefault(_aclPortal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NotificationAclPortal extends _aclPortal.default {
    checkIsOwner(model) {
      if (this.getUser().id === model.get('userId')) {
        return true;
      }
      return false;
    }
  }
  var _default = _exports.default = NotificationAclPortal;
});

define("acl-portal/email", ["exports", "acl-portal"], function (_exports, _aclPortal) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _aclPortal = _interopRequireDefault(_aclPortal);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailAclPortal extends _aclPortal.default {
    // noinspection JSUnusedGlobalSymbols
    checkModelRead(model, data, precise) {
      const result = this.checkModel(model, data, 'read', precise);
      if (result) {
        return true;
      }
      if (data === false) {
        return false;
      }
      const d = data || {};
      if (d.read === 'no') {
        return false;
      }
      if (model.has('usersIds')) {
        if (~(model.get('usersIds') || []).indexOf(this.getUser().id)) {
          return true;
        }
      } else if (precise) {
        return null;
      }
      return result;
    }
  }
  var _default = _exports.default = EmailAclPortal;
});

define("acl/user", ["exports", "acl"], function (_exports, _acl) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _acl = _interopRequireDefault(_acl);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class UserAcl extends _acl.default {
    // noinspection JSUnusedGlobalSymbols
    checkModelRead(model, data, precise) {
      if (model.isPortal() && this.getPermissionLevel('portalPermission') === 'yes') {
        return true;
      }
      return this.checkModel(model, data, 'read', precise);
    }
    checkIsOwner(model) {
      return this.getUser().id === model.id;
    }
  }
  var _default = _exports.default = UserAcl;
});

define("acl/team", ["exports", "acl"], function (_exports, _acl) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _acl = _interopRequireDefault(_acl);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class TeamAcl extends _acl.default {
    checkInTeam(model) {
      const userTeamIdList = this.getUser().getTeamIdList();
      return userTeamIdList.indexOf(model.id) !== -1;
    }
  }
  var _default = _exports.default = TeamAcl;
});

define("acl/preferences", ["exports", "acl"], function (_exports, _acl) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _acl = _interopRequireDefault(_acl);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class PreferencesAcl extends _acl.default {
    checkIsOwner(model) {
      if (this.getUser().id === model.id) {
        return true;
      }
      return false;
    }
  }
  var _default = _exports.default = PreferencesAcl;
});

define("acl/notification", ["exports", "acl"], function (_exports, _acl) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _acl = _interopRequireDefault(_acl);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class NotificationAcl extends _acl.default {
    checkIsOwner(model) {
      if (this.getUser().id === model.get('userId')) {
        return true;
      }
      return false;
    }
  }
  var _default = _exports.default = NotificationAcl;
});

define("acl/import", ["exports", "acl"], function (_exports, _acl) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _acl = _interopRequireDefault(_acl);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class ImportAcl extends _acl.default {
    checkScope(data, action, precise, entityAccessData) {
      return !!data;
    }

    // noinspection JSUnusedGlobalSymbols,JSUnusedLocalSymbols
    checkModelRead(model, data, precise) {
      return true;
    }
    checkIsOwner(model) {
      if (this.getUser().id === model.get('createdById')) {
        return true;
      }
      return false;
    }
    checkModelDelete(model, data, precise) {
      return true;
    }
  }
  var _default = _exports.default = ImportAcl;
});

define("acl/foreign", ["exports", "acl"], function (_exports, _acl) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _acl = _interopRequireDefault(_acl);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  /**
   * To be used for entities for which access is determined by access to a foreign record.
   */
  class ForeignAcl extends _acl.default {
    checkIsOwner(model) {
      return true;
    }
    checkInTeam(model) {
      return true;
    }
  }
  var _default = _exports.default = ForeignAcl;
});

define("acl/email", ["exports", "acl"], function (_exports, _acl) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  _acl = _interopRequireDefault(_acl);
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  /************************************************************************
   * This file is part of EspoCRM.
   *
   * EspoCRM – Open Source CRM application.
   * Copyright (C) 2014-2025 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
   * 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 <https://www.gnu.org/licenses/>.
   *
   * 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.
   ************************************************************************/

  class EmailAcl extends _acl.default {
    // noinspection JSUnusedGlobalSymbols
    checkModelRead(model, data, precise) {
      const result = this.checkModel(model, data, 'read', precise);
      if (result) {
        return true;
      }
      if (data === false) {
        return false;
      }
      const d = data || {};
      if (d.read === 'no') {
        return false;
      }
      if (model.has('usersIds')) {
        if (~(model.get('usersIds') || []).indexOf(this.getUser().id)) {
          return true;
        }
      } else if (precise) {
        return null;
      }
      return result;
    }
    checkIsOwner(model) {
      if (this.getUser().id === model.get('assignedUserId') || this.getUser().id === model.get('createdById')) {
        return true;
      }
      if (!model.has('assignedUsersIds')) {
        return null;
      }
      if (~(model.get('assignedUsersIds') || []).indexOf(this.getUser().id)) {
        return true;
      }
      return false;
    }

    // noinspection JSUnusedGlobalSymbols
    checkModelEdit(model, data, precise) {
      if (model.get('status') === 'Draft' && model.get('createdById') === this.getUser().id) {
        return true;
      }
      return this.checkModel(model, data, 'edit', precise);
    }
    checkModelDelete(model, data, precise) {
      const result = this.checkModel(model, data, 'delete', precise);
      if (result) {
        return true;
      }
      if (data === false) {
        return false;
      }
      const d = data || {};
      if (d.read === 'no') {
        return false;
      }
      if (model.get('createdById') === this.getUser().id) {
        if (model.get('status') !== 'Sent' && model.get('status') !== 'Archived') {
          return true;
        }
      }
      return result;
    }
  }
  var _default = _exports.default = EmailAcl;
});

Espo.loader.mapBundleFile('admin', 'client/lib/espo-admin.js');
Espo.loader.mapBundleFile('extra', 'client/lib/espo-extra.js');
Espo.loader.mapBundleFile('crm', 'client/lib/espo-crm.js');
Espo.loader.mapBundleDependencies('chart', ["flotr2","espo-funnel-chart"]);
Espo.loader.mapBundleFile('chart', 'client/lib/espo-chart.js');
Espo.loader.mapBundleDependencies('calendar', ["fullcalendar"]);
Espo.loader.mapBundleFile('calendar', 'client/lib/espo-calendar.js');
Espo.loader.mapBundleDependencies('timeline', ["vis-data","vis-timeline"]);
Espo.loader.mapBundleFile('timeline', 'client/lib/espo-timeline.js');
Espo.loader.addBundleMapping({"views/admin/layouts/base":"admin","views/admin/layouts/rows":"admin","views/admin/layouts/side-panels-detail":"admin","views/admin/dynamic-logic/conditions-string/item-base":"admin","views/admin/link-manager/modals/edit-params":"admin","views/admin/layouts/grid":"admin","views/admin/layouts/default-page":"admin","views/admin/layouts/bottom-panels-detail":"admin","views/admin/layouts/modals/create":"admin","views/admin/field-manager/detail-fields/attributes":"admin","views/admin/dynamic-logic/conditions-string/item-operator-only-base":"admin","views/admin/dynamic-logic/conditions/field-types/base":"admin","views/settings/edit":"admin","views/settings/record/edit":"admin","views/settings/fields/quick-create-list":"admin","views/role/record/table":"admin","views/role/record/list":"admin","views/role/record/edit":"admin","views/role/record/detail":"admin","views/inbound-email/record/detail":"admin","views/admin/index":"admin","views/admin/link-manager/index":"admin","views/admin/layouts/list":"admin","views/admin/layouts/index":"admin","views/admin/layouts/detail":"admin","views/admin/layouts/bottom-panels-edit":"admin","views/admin/integrations/edit":"admin","views/admin/field-manager/modals/view-details":"admin","views/admin/field-manager/fields/options":"admin","views/admin/entity-manager/record/edit-formula":"admin","views/admin/entity-manager/modals/export":"admin","views/admin/entity-manager/fields/primary-filters":"admin","views/admin/entity-manager/fields/acl-contact-link":"admin","views/admin/dynamic-logic/conditions-string/item-operator-only-date":"admin","views/admin/dynamic-logic/conditions-string/group-base":"admin","views/admin/dynamic-logic/conditions/group-base":"admin","views/admin/dynamic-logic/conditions/field-types/link-multiple":"admin","views/email-account/fields/test-send":"admin","views/email-account/fields/test-connection":"admin","views/email-account/fields/folders":"admin","views/email-account/fields/folder":"admin","views/templates/event/record/detail":"admin","views/settings/modals/tab-list-field-add":"admin","views/settings/modals/edit-tab-url":"admin","views/settings/modals/edit-tab-group":"admin","views/settings/modals/edit-tab-divider":"admin","views/settings/fields/time-format":"admin","views/settings/fields/thousand-separator":"admin","views/settings/fields/tab-url":"admin","views/settings/fields/stream-email-with-content-entity-type-list":"admin","views/settings/fields/stream-email-notifications-entity-list":"admin","views/settings/fields/sms-provider":"admin","views/settings/fields/phone-number-preferred-country-list":"admin","views/settings/fields/pdf-engine":"admin","views/settings/fields/outbound-email-from-address":"admin","views/settings/fields/oidc-teams":"admin","views/settings/fields/oidc-redirect-uri":"admin","views/settings/fields/language":"admin","views/settings/fields/history-entity-list":"admin","views/settings/fields/group-tab-list":"admin","views/settings/fields/global-search-entity-list":"admin","views/settings/fields/fiscal-year-shift":"admin","views/settings/fields/email-address-lookup-entity-type-list":"admin","views/settings/fields/default-currency":"admin","views/settings/fields/date-format":"admin","views/settings/fields/dashboard-layout":"admin","views/settings/fields/currency-rates":"admin","views/settings/fields/currency-list":"admin","views/settings/fields/calendar-entity-list":"admin","views/settings/fields/busy-ranges-entity-list":"admin","views/settings/fields/available-reactions":"admin","views/settings/fields/authentication-method":"admin","views/settings/fields/auth-two-fa-method-list":"admin","views/settings/fields/assignment-notifications-entity-list":"admin","views/settings/fields/assignment-email-notifications-entity-list":"admin","views/settings/fields/address-preview":"admin","views/settings/fields/activities-entity-list":"admin","views/scheduled-job/list":"admin","views/scheduled-job/record/list":"admin","views/scheduled-job/record/detail":"admin","views/scheduled-job/record/panels/log":"admin","views/scheduled-job/fields/scheduling":"admin","views/scheduled-job/fields/job":"admin","views/role/list":"admin","views/role/record/detail-side":"admin","views/role/record/panels/side":"admin","views/role/modals/add-field":"admin","views/role/fields/permission":"admin","views/portal-role/list":"admin","views/portal-role/record/table":"admin","views/portal-role/record/list":"admin","views/portal-role/record/edit":"admin","views/portal-role/record/detail":"admin","views/portal/record/list":"admin","views/portal/fields/tab-list":"admin","views/portal/fields/quick-create-list":"admin","views/portal/fields/custom-id":"admin","views/lead-capture-log-record/modals/detail":"admin","views/layout-set/layouts":"admin","views/layout-set/record/list":"admin","views/layout-set/fields/layout-list":"admin","views/layout-set/fields/edit":"admin","views/inbound-email/record/list":"admin","views/inbound-email/record/edit":"admin","views/inbound-email/fields/test-send":"admin","views/inbound-email/fields/test-connection":"admin","views/inbound-email/fields/target-user-position":"admin","views/inbound-email/fields/name":"admin","views/inbound-email/fields/folders":"admin","views/inbound-email/fields/folder":"admin","views/inbound-email/fields/email-address":"admin","views/extension/record/row-actions":"admin","views/extension/record/list":"admin","views/authentication-provider/record/edit":"admin","views/authentication-provider/record/detail":"admin","views/authentication-provider/fields/method":"admin","views/api-user/list":"admin","views/admin/user-interface":"admin","views/admin/sms":"admin","views/admin/settings":"admin","views/admin/outbound-emails":"admin","views/admin/notifications":"admin","views/admin/jobs-settings":"admin","views/admin/inbound-emails":"admin","views/admin/currency":"admin","views/admin/authentication":"admin","views/admin/upgrade/ready":"admin","views/admin/upgrade/index":"admin","views/admin/upgrade/done":"admin","views/admin/template-manager/index":"admin","views/admin/template-manager/edit":"admin","views/admin/template-manager/fields/body":"admin","views/admin/system-requirements/index":"admin","views/admin/panels/notifications":"admin","views/admin/link-manager/modals/edit":"admin","views/admin/link-manager/fields/foreign-link-entity-type-list":"admin","views/admin/layouts/side-panels-edit":"admin","views/admin/layouts/side-panels-edit-small":"admin","views/admin/layouts/side-panels-detail-small":"admin","views/admin/layouts/mass-update":"admin","views/admin/layouts/list-small":"admin","views/admin/layouts/kanban":"admin","views/admin/layouts/filters":"admin","views/admin/layouts/detail-small":"admin","views/admin/layouts/detail-convert":"admin","views/admin/layouts/default-side-panel":"admin","views/admin/layouts/bottom-panels-edit-small":"admin","views/admin/layouts/bottom-panels-detail-small":"admin","views/admin/layouts/record/edit-attributes":"admin","views/admin/layouts/modals/panel-attributes":"admin","views/admin/layouts/modals/edit-attributes":"admin","views/admin/layouts/fields/width-complex":"admin","views/admin/label-manager/index":"admin","views/admin/label-manager/edit":"admin","views/admin/label-manager/category":"admin","views/admin/job/list":"admin","views/admin/job/record/list":"admin","views/admin/job/record/detail-small":"admin","views/admin/job/modals/detail":"admin","views/admin/job/fields/name":"admin","views/admin/integrations/oauth2":"admin","views/admin/integrations/index":"admin","views/admin/integrations/google-maps":"admin","views/admin/formula-sandbox/index":"admin","views/admin/formula-sandbox/record/edit":"admin","views/admin/formula/modals/add-function":"admin","views/admin/formula/modals/add-attribute":"admin","views/admin/formula/fields/attribute":"admin","views/admin/field-manager/list":"admin","views/admin/field-manager/index":"admin","views/admin/field-manager/header":"admin","views/admin/field-manager/edit":"admin","views/admin/field-manager/modals/add-field":"admin","views/admin/field-manager/fields/source-list":"admin","views/admin/field-manager/fields/pattern":"admin","views/admin/field-manager/fields/options-with-style":"admin","views/admin/field-manager/fields/options-reference":"admin","views/admin/field-manager/fields/not-actual-options":"admin","views/admin/field-manager/fields/entity-list":"admin","views/admin/field-manager/fields/dynamic-logic-options":"admin","views/admin/field-manager/fields/dynamic-logic-conditions":"admin","views/admin/field-manager/fields/currency-default":"admin","views/admin/field-manager/fields/text/attachment-field":"admin","views/admin/field-manager/fields/phone/default":"admin","views/admin/field-manager/fields/options/default":"admin","views/admin/field-manager/fields/options/default-multi":"admin","views/admin/field-manager/fields/link-multiple/default":"admin","views/admin/field-manager/fields/link/default":"admin","views/admin/field-manager/fields/int/max":"admin","views/admin/field-manager/fields/foreign/link":"admin","views/admin/field-manager/fields/foreign/field":"admin","views/admin/field-manager/fields/date/default":"admin","views/admin/field-manager/fields/date/after-before":"admin","views/admin/extensions/ready":"admin","views/admin/extensions/index":"admin","views/admin/extensions/done":"admin","views/admin/entity-manager/scope":"admin","views/admin/entity-manager/index":"admin","views/admin/entity-manager/formula":"admin","views/admin/entity-manager/edit":"admin","views/admin/entity-manager/record/edit":"admin","views/admin/entity-manager/modals/select-icon":"admin","views/admin/entity-manager/modals/select-formula":"admin","views/admin/entity-manager/fields/icon-class":"admin","views/admin/entity-manager/fields/duplicate-check-field-list":"admin","views/admin/entity-manager/fields/acl-account-link":"admin","views/admin/dynamic-logic/modals/edit":"admin","views/admin/dynamic-logic/modals/add-field":"admin","views/admin/dynamic-logic/fields/user-id":"admin","views/admin/dynamic-logic/fields/field":"admin","views/admin/dynamic-logic/conditions-string/item-value-varchar":"admin","views/admin/dynamic-logic/conditions-string/item-value-link":"admin","views/admin/dynamic-logic/conditions-string/item-value-enum":"admin","views/admin/dynamic-logic/conditions-string/item-multiple-values-base":"admin","views/admin/dynamic-logic/conditions-string/item-is-today":"admin","views/admin/dynamic-logic/conditions-string/item-in-past":"admin","views/admin/dynamic-logic/conditions-string/item-in-future":"admin","views/admin/dynamic-logic/conditions-string/group-not":"admin","views/admin/dynamic-logic/conditions/or":"admin","views/admin/dynamic-logic/conditions/not":"admin","views/admin/dynamic-logic/conditions/and":"admin","views/admin/dynamic-logic/conditions/field-types/multi-enum":"admin","views/admin/dynamic-logic/conditions/field-types/link":"admin","views/admin/dynamic-logic/conditions/field-types/link-parent":"admin","views/admin/dynamic-logic/conditions/field-types/enum":"admin","views/admin/dynamic-logic/conditions/field-types/date":"admin","views/admin/dynamic-logic/conditions/field-types/current-user":"admin","views/admin/dynamic-logic/conditions/field-types/current-user-teams":"admin","views/admin/complex-expression/modals/add-function":"admin","views/admin/authentication/fields/test-connection":"admin","views/admin/auth-token/list":"admin","views/admin/auth-token/record/list":"admin","views/admin/auth-token/record/detail":"admin","views/admin/auth-token/record/detail-small":"admin","views/admin/auth-token/record/row-actions/default":"admin","views/admin/auth-token/modals/detail":"admin","views/admin/auth-log-record/list":"admin","views/admin/auth-log-record/record/list":"admin","views/admin/auth-log-record/record/detail":"admin","views/admin/auth-log-record/record/detail-small":"admin","views/admin/auth-log-record/modals/detail":"admin","views/admin/auth-log-record/fields/authentication-method":"admin","views/admin/app-secret/fields/value":"admin","views/admin/app-log-record/record/list":"admin","controllers/role":"admin","controllers/portal-role":"admin","controllers/admin":"admin","views/import/record/panels/imported":"extra","views/email-account/record/detail":"extra","views/personal-data/record/record":"extra","views/personal-data/modals/personal-data":"extra","views/outbound-email/modals/test-send":"extra","views/import-error/fields/validation-failures":"extra","views/import-error/fields/line-number":"extra","views/import/step2":"extra","views/import/step1":"extra","views/import/list":"extra","views/import/index":"extra","views/import/detail":"extra","views/import/record/list":"extra","views/import/record/detail":"extra","views/import/record/row-actions/duplicates":"extra","views/import/record/panels/updated":"extra","views/import/record/panels/duplicates":"extra","views/group-email-folder/list":"extra","views/group-email-folder/record/list":"extra","views/group-email-folder/record/edit-small":"extra","views/group-email-folder/record/row-actions/default":"extra","views/external-account/oauth2":"extra","views/external-account/index":"extra","views/email-account/list":"extra","views/email-account/record/list":"extra","views/email-account/record/edit":"extra","views/email-account/modals/select-folder":"extra","views/email-account/fields/email-folder":"extra","views/email-account/fields/email-address":"extra","modules/crm/views/meeting/fields/attendees":"crm","modules/crm/views/calendar/fields/teams":"crm","modules/crm/knowledge-base-helper":"crm","modules/crm/views/task/record/list":"crm","modules/crm/views/record/panels/tasks":"crm","modules/crm/views/record/panels/activities":"crm","modules/crm/views/meeting/detail":"crm","modules/crm/views/meeting/record/list":"crm","modules/crm/views/mass-email/record/edit":"crm","modules/crm/views/mass-email/modals/send-test":"crm","modules/crm/views/dashlets/options/chart":"crm","modules/crm/views/contact/record/detail":"crm","modules/crm/views/call/record/list":"crm","modules/crm/views/call/fields/contacts":"crm","modules/crm/views/calendar/modals/edit-view":"crm","modules/crm/views/calendar/fields/users":"crm","modules/crm/acl/meeting":"crm","modules/crm/views/user/record/panels/tasks":"crm","modules/crm/views/task/list":"crm","modules/crm/views/task/detail":"crm","modules/crm/views/task/record/list-expanded":"crm","modules/crm/views/task/record/detail":"crm","modules/crm/views/task/record/row-actions/default":"crm","modules/crm/views/task/record/row-actions/dashlet":"crm","modules/crm/views/task/modals/detail":"crm","modules/crm/views/task/fields/tasks":"crm","modules/crm/views/task/fields/priority-for-dashlet":"crm","modules/crm/views/task/fields/is-overdue":"crm","modules/crm/views/task/fields/date-end":"crm","modules/crm/views/target-list/record/detail":"crm","modules/crm/views/target-list/record/row-actions/opted-out":"crm","modules/crm/views/target-list/record/row-actions/default":"crm","modules/crm/views/target-list/record/panels/relationship":"crm","modules/crm/views/target-list/record/panels/opted-out":"crm","modules/crm/views/target-list/fields/target-status":"crm","modules/crm/views/target-list/fields/including-action-list":"crm","modules/crm/views/stream/notes/event-confirmation":"crm","modules/crm/views/record/list-activities-dashlet":"crm","modules/crm/views/record/row-actions/tasks":"crm","modules/crm/views/record/row-actions/relationship-target":"crm","modules/crm/views/record/row-actions/history":"crm","modules/crm/views/record/row-actions/activities":"crm","modules/crm/views/record/row-actions/activities-dashlet":"crm","modules/crm/views/record/panels/target-lists":"crm","modules/crm/views/record/panels/history":"crm","modules/crm/views/opportunity/detail":"crm","modules/crm/views/opportunity/record/list":"crm","modules/crm/views/opportunity/record/kanban":"crm","modules/crm/views/opportunity/record/edit":"crm","modules/crm/views/opportunity/record/edit-small":"crm","modules/crm/views/opportunity/record/panels/activities":"crm","modules/crm/views/opportunity/fields/stage":"crm","modules/crm/views/opportunity/fields/lead-source":"crm","modules/crm/views/opportunity/fields/last-stage":"crm","modules/crm/views/opportunity/fields/contacts":"crm","modules/crm/views/opportunity/fields/contact-role":"crm","modules/crm/views/opportunity/admin/field-manager/fields/probability-map":"crm","modules/crm/views/notification/items/event-attendee":"crm","modules/crm/views/meeting/popup-notification":"crm","modules/crm/views/meeting/record/list-expanded":"crm","modules/crm/views/meeting/record/edit-small":"crm","modules/crm/views/meeting/record/detail":"crm","modules/crm/views/meeting/record/row-actions/default":"crm","modules/crm/views/meeting/record/row-actions/dashlet":"crm","modules/crm/views/meeting/record/panels/scheduler":"crm","modules/crm/views/meeting/record/panels/attendees":"crm","modules/crm/views/meeting/modals/send-invitations":"crm","modules/crm/views/meeting/modals/send-cancellation":"crm","modules/crm/views/meeting/modals/detail":"crm","modules/crm/views/meeting/modals/acceptance-status":"crm","modules/crm/views/meeting/fields/users":"crm","modules/crm/views/meeting/fields/reminders":"crm","modules/crm/views/meeting/fields/date-start":"crm","modules/crm/views/meeting/fields/date-end":"crm","modules/crm/views/meeting/fields/contacts":"crm","modules/crm/views/meeting/fields/acceptance-status":"crm","modules/crm/views/mass-email/detail":"crm","modules/crm/views/mass-email/record/list-for-campaign":"crm","modules/crm/views/mass-email/record/edit-small":"crm","modules/crm/views/mass-email/record/detail":"crm","modules/crm/views/mass-email/record/detail-bottom":"crm","modules/crm/views/mass-email/record/row-actions/for-campaign":"crm","modules/crm/views/mass-email/fields/smtp-account":"crm","modules/crm/views/mass-email/fields/from-address":"crm","modules/crm/views/mass-email/fields/email-template":"crm","modules/crm/views/lead/detail":"crm","modules/crm/views/lead/convert":"crm","modules/crm/views/lead/record/detail":"crm","modules/crm/views/lead/record/detail-side":"crm","modules/crm/views/lead/record/panels/converted-to":"crm","modules/crm/views/lead/fields/industry":"crm","modules/crm/views/lead/fields/created-opportunity":"crm","modules/crm/views/lead/fields/created-contact":"crm","modules/crm/views/lead/fields/acceptance-status":"crm","modules/crm/views/knowledge-base-article/list":"crm","modules/crm/views/knowledge-base-article/record/list":"crm","modules/crm/views/knowledge-base-article/record/edit":"crm","modules/crm/views/knowledge-base-article/record/edit-quick":"crm","modules/crm/views/knowledge-base-article/record/detail":"crm","modules/crm/views/knowledge-base-article/record/detail-quick":"crm","modules/crm/views/knowledge-base-article/modals/select-records":"crm","modules/crm/views/knowledge-base-article/fields/status":"crm","modules/crm/views/knowledge-base-article/fields/language":"crm","modules/crm/views/fields/ico":"crm","modules/crm/views/event-confirmation/confirmation":"crm","modules/crm/views/email-queue-item/list":"crm","modules/crm/views/email-queue-item/record/list":"crm","modules/crm/views/document/list":"crm","modules/crm/views/document/modals/select-records":"crm","modules/crm/views/document/fields/name":"crm","modules/crm/views/document/fields/file":"crm","modules/crm/views/dashlets/tasks":"crm","modules/crm/views/dashlets/meetings":"crm","modules/crm/views/dashlets/calls":"crm","modules/crm/views/dashlets/calendar":"crm","modules/crm/views/dashlets/activities":"crm","modules/crm/views/dashlets/options/sales-pipeline":"crm","modules/crm/views/dashlets/options/calendar":"crm","modules/crm/views/dashlets/options/activities":"crm","modules/crm/views/dashlets/options/sales-pipeline/fields/team":"crm","modules/crm/views/contact/detail":"crm","modules/crm/views/contact/record/detail-small":"crm","modules/crm/views/contact/modals/select-for-portal-user":"crm","modules/crm/views/contact/fields/title":"crm","modules/crm/views/contact/fields/opportunity-role":"crm","modules/crm/views/contact/fields/name-for-account":"crm","modules/crm/views/contact/fields/accounts":"crm","modules/crm/views/contact/fields/account":"crm","modules/crm/views/contact/fields/account-role":"crm","modules/crm/views/case/record/detail":"crm","modules/crm/views/case/record/panels/activities":"crm","modules/crm/views/campaign-tracking-url/record/edit":"crm","modules/crm/views/campaign-tracking-url/record/edit-small":"crm","modules/crm/views/campaign-log-record/fields/data":"crm","modules/crm/views/campaign/unsubscribe":"crm","modules/crm/views/campaign/tracking-url":"crm","modules/crm/views/campaign/detail":"crm","modules/crm/views/campaign/record/detail":"crm","modules/crm/views/campaign/record/panels/campaign-stats":"crm","modules/crm/views/campaign/record/panels/campaign-log-records":"crm","modules/crm/views/campaign/modals/mail-merge-pdf":"crm","modules/crm/views/campaign/fields/template":"crm","modules/crm/views/campaign/fields/int-with-percentage":"crm","modules/crm/views/call/detail":"crm","modules/crm/views/call/record/list-expanded":"crm","modules/crm/views/call/record/edit-small":"crm","modules/crm/views/call/record/detail":"crm","modules/crm/views/call/record/row-actions/default":"crm","modules/crm/views/call/record/row-actions/dashlet":"crm","modules/crm/views/call/fields/leads":"crm","modules/crm/views/call/fields/date-start":"crm","modules/crm/views/calendar/mode-buttons":"crm","modules/crm/views/calendar/calendar-page":"crm","modules/crm/views/calendar/modals/shared-options":"crm","modules/crm/views/calendar/modals/edit":"crm","modules/crm/views/admin/entity-manager/fields/status-list":"crm","modules/crm/views/activities/list":"crm","modules/crm/views/account/detail":"crm","modules/crm/views/account/fields/shipping-address":"crm","modules/crm/view-setup-handlers/document/record-list-drag-n-drop":"crm","modules/crm/handlers/task/reminders-handler":"crm","modules/crm/handlers/task/menu":"crm","modules/crm/handlers/task/detail-actions":"crm","modules/crm/handlers/opportunity/defaults-preparator":"crm","modules/crm/handlers/opportunity/contacts-create":"crm","modules/crm/handlers/knowledge-base-article/send-in-email":"crm","modules/crm/handlers/knowledge-base-article/move":"crm","modules/crm/handlers/event/reminders-handler":"crm","modules/crm/handlers/case/detail-actions":"crm","modules/crm/handlers/campaign/mass-emails-create":"crm","modules/crm/controllers/unsubscribe":"crm","modules/crm/controllers/tracking-url":"crm","modules/crm/controllers/task":"crm","modules/crm/controllers/lead":"crm","modules/crm/controllers/event-confirmation":"crm","modules/crm/controllers/calendar":"crm","modules/crm/controllers/activities":"crm","modules/crm/acl-portal/document":"crm","modules/crm/acl-portal/contact":"crm","modules/crm/acl-portal/account":"crm","modules/crm/acl/mass-email":"crm","modules/crm/acl/campaign-tracking-url":"crm","modules/crm/acl/call":"crm","modules/crm/views/dashlets/abstract/chart":"chart","modules/crm/views/dashlets/sales-pipeline":"chart","modules/crm/views/dashlets/sales-by-month":"chart","modules/crm/views/dashlets/opportunities-by-stage":"chart","modules/crm/views/dashlets/opportunities-by-lead-source":"chart","modules/crm/views/calendar/calendar":"calendar","modules/crm/views/scheduler/scheduler":"timeline","modules/crm/views/calendar/timeline":"timeline"});

Espo.layoutTemplates = {"record":"<% var hasHiddenPanel = false; %>\r\n\r\n<% _.each(layout, function (panel, columnNumber) { %>\r\n    <% hasHiddenPanel = panel.hidden || hasHiddenPanel; %>\r\n    <div\r\n        class=\"panel panel-<%= panel.style %><%= panel.label ? ' headered' : '' %><%= panel.tabNumber ? ' tab-hidden' : '' %><% if (panel.name) { %>{{#if hiddenPanels.<%= panel.name %>}} hidden{{/if}}<% } %>\"\r\n        <% if (panel.name) print('data-name=\"'+panel.name+'\"') %>\r\n        <% if (panel.style) print('data-style=\"'+panel.style+'\"') %>\r\n        data-tab=\"<%= panel.tabNumber %>\"\r\n    >\r\n        <% if (panel.label) { %>\r\n        <div class=\"panel-heading\"><h4 class=\"panel-title\"><%= panel.label %></h4></div>\r\n        <% } %>\r\n        <div class=\"panel-body panel-body-form\">\r\n\r\n        <% if (panel.noteText) { %>\r\n        <div class=\"alert alert-<%= panel.noteStyle %>\"><%= panel.noteText %></div>\r\n        <% } %>\r\n\r\n        <% var rows = panel.rows || [] %>\r\n        <% var columns = panel.columns || [] %>\r\n\r\n        <% _.each(rows, function (row, rowNumber) { %>\r\n            <div class=\"row\">\r\n            <% var columnCount = row.length; %>\r\n            <% _.each(row, function (cell, cellNumber) { %>\r\n\r\n                <%\r\n                    var spanClassBase;\r\n                    if (columnCount === 1) {\r\n                        spanClassBase = 'col-sm-12';\r\n                    } else if (columnCount === 2) {\r\n                        spanClassBase = 'col-sm-6';\r\n                    } else if (columnCount === 3) {\r\n                        spanClassBase = 'col-sm-4';\r\n                    } else if (columnCount === 4) {\r\n                        spanClassBase = 'col-md-3 col-sm-6';\r\n                    } else {\r\n                        spanClass = 'col-sm-12';\r\n                    }\r\n                %>\r\n                <% if (cell != false) { %>\r\n                    <%\r\n                        var spanClass;\r\n                        if (columnCount === 1 || cell.fullWidth) {\r\n                            spanClass = 'col-sm-12';\r\n                        } else if (columnCount === 2) {\r\n                            if (cell.span === 2) {\r\n                                spanClass = 'col-sm-12';\r\n                            } else {\r\n                                spanClass = 'col-sm-6';\r\n                            }\r\n                        } else if (columnCount === 3) {\r\n                            if (cell.span === 2) {\r\n                                spanClass = 'col-sm-8';\r\n                            } else if (cell.span === 3) {\r\n                                spanClass = 'col-sm-12';\r\n                            } else {\r\n                                spanClass = 'col-sm-4';\r\n                            }\r\n                        } else if (columnCount === 4) {\r\n                            if (cell.span === 2) {\r\n                                spanClass = 'col-sm-6';\r\n                            } else if (cell.span === 3) {\r\n                                spanClass = 'col-sm-9';\r\n                            } else if (cell.span === 4) {\r\n                                spanClass = 'col-sm-12';\r\n                            } else {\r\n                                spanClass = 'col-md-3 col-sm-6';\r\n                            }\r\n                        } else {\r\n                            spanClass = 'col-sm-12';\r\n                        }\r\n                    %>\r\n                    <div\r\n                        class=\"cell <%= spanClass %> form-group<% if (cell.field) { %>{{#if hiddenFields.<%= cell.field %>}} hidden-cell{{/if}}<% } %>\"\r\n                        data-name=\"<%= cell.field %>\"\r\n                        tabindex=\"-1\"\r\n                    >\r\n                        <% if (!cell.noLabel) { %><label class=\"control-label<% if (cell.field) { %>{{#if hiddenFields.<%= cell.field %>}} hidden{{/if}}<% } %>\" data-name=\"<%= cell.field %>\"><span class=\"label-text\"><%\r\n                            if ('customLabel' in cell) {\r\n                                print (cell.customLabel);\r\n                            } else {\r\n                                var label = cell.label || cell.field;\r\n                                print (\"{{translate \\\"\"+label+\"\\\" scope=\\\"\"+model.name+\"\\\" category='fields'}}\");\r\n                            }\r\n                        %></span></label><% } %>\r\n                        <div class=\"field<% if (cell.field) { %>{{#if hiddenFields.<%= cell.field %>}} hidden{{/if}}<% } %>\" data-name=\"<%= cell.field %>\"><%\r\n                            if ('customCode' in cell) {\r\n                                print (cell.customCode);\r\n                            } else {\r\n                                print (\"{{{this.\"+cell.name+\"}}}\");\r\n                            }\r\n                        %></div>\r\n                    </div>\r\n                <% } else { %>\r\n                    <div class=\"<%= spanClassBase %>\"></div>\r\n                <% } %>\r\n            <% }); %>\r\n            </div>\r\n        <% }); %>\r\n\r\n        <%\r\n            var columnCount = columns.length;\r\n            if (columnCount) {\r\n                %>\r\n            <div class=\"row\">\r\n                <%\r\n            }\r\n        %>\r\n        <% _.each(columns, function (column, columnNumber) { %>\r\n            <%\r\n                var spanClass;\r\n                if (!columnCount) return;\r\n\r\n                if (columnCount === 1 || column.fullWidth) {\r\n                    spanClass = 'col-sm-12';\r\n                } else if (columnCount === 2) {\r\n                    if (column.span === 2) {\r\n                        spanClass = 'col-sm-12';\r\n                    } else {\r\n                        spanClass = 'col-sm-6';\r\n                    }\r\n                } else if (columnCount === 3) {\r\n                    if (column.span === 2) {\r\n                        spanClass = 'col-sm-8';\r\n                    } else if (column.span === 3) {\r\n                        spanClass = 'col-sm-12';\r\n                    } else {\r\n                        spanClass = 'col-sm-4';\r\n                    }\r\n                } else if (columnCount === 4) {\r\n                    if (column.span === 2) {\r\n                        spanClass = 'col-sm-6';\r\n                    } else if (column.span === 3) {\r\n                        spanClass = 'col-sm-9';\r\n                    } else if (column.span === 4) {\r\n                        spanClass = 'col-sm-12';\r\n                    } else {\r\n                        spanClass = 'col-md-3 col-sm-6';\r\n                    }\r\n                } else {\r\n                    spanClass = 'col-sm-12';\r\n                }\r\n            %>\r\n            <div class=\"column <%= spanClass %>\">\r\n                <% _.each(column, function (cell, cellNumber) { %>\r\n                    <div class=\"cell form-group<% if (cell.field) { %>{{#if hiddenFields.<%= cell.field %>}} hidden-cell{{/if}}<% } %>\" data-name=\"<%= cell.field %>\">\r\n                        <% if (!cell.noLabel) { %><label class=\"control-label<% if (cell.field) { %>{{#if hiddenFields.<%= cell.field %>}} hidden{{/if}}<% } %>\" data-name=\"<%= cell.field %>\"><span class=\"label-text\"><%\r\n                            if ('customLabel' in cell) {\r\n                                print (cell.customLabel);\r\n                            } else {\r\n                                print (\"{{translate \\\"\"+cell.field+\"\\\" scope=\\\"\"+model.name+\"\\\" category='fields'}}\");\r\n                            }\r\n                        %></span></label><% } %>\r\n                        <div class=\"field<% if (cell.field) { %>{{#if hiddenFields.<%= cell.field %>}} hidden{{/if}}<% } %>\" data-name=\"<%= cell.field %>\"><%\r\n                            if ('customCode' in cell) {\r\n                                print (cell.customCode);\r\n                            } else {\r\n                                print (\"{{{this.\"+cell.name+\"}}}\");\r\n                            }\r\n                        %></div>\r\n                    </div>\r\n                <% }); %>\r\n            </div>\r\n        <% }); %>\r\n        <%\r\n            if (columnCount) {\r\n                %>\r\n            </div>\r\n                <%\r\n            }\r\n        %>\r\n        </div>\r\n    </div>\r\n<% }); %>\r\n\r\n<%\r\nif (hasHiddenPanel) {\r\n%>\r\n<div class=\"panel panel-default panels-show-more-delimiter\" data-name=\"showMoreDelimiter\" data-tab=\"0\">\r\n    <a role=\"button\" tabindex=\"0\" data-action=\"showMoreDetailPanels\" title=\"{{translate 'Show more'}}\">\r\n        <span class=\"fas fa-ellipsis-h fa-lg\"></span>\r\n    </a>\r\n</div>\r\n<%\r\n}\r\n%>\r\n","list-row":"<% _.each(layout, function (defs, key) { %>\r\n    <%\r\n        let width = null;\r\n\r\n        if (defs.options && defs.options.defs && defs.options.defs.width !== undefined) {\r\n            width = (defs.options.defs.width + '%') || null;\r\n        }\r\n\r\n        if (defs.options && defs.options.defs && defs.options.defs.widthPx !== undefined) {\r\n            width = defs.options.defs.widthPx || null;\r\n        }\r\n\r\n        let align = false;\r\n\r\n        if (defs.options && defs.options.defs) {\r\n            align = defs.options.defs.align || false;\r\n        }\r\n    %>\r\n    <td\r\n        class=\"cell\"\r\n        data-name=\"<%= defs.columnName %>\"\r\n        <% if (width || align) { %>\r\n        style=\"<% if (width) print('width: ' + width); %>;<% if (align) print(' text-align: ' + align);%>\"\r\n        <% } %>\r\n    >\r\n    <%\r\n            var tag = 'tag' in defs ? defs.tag : false;\r\n            if (tag) {\r\n                print( '<' + tag);\r\n                if ('id' in defs) {\r\n                    print(' id=\"'+defs.id+'\"');\r\n                }\r\n                if ('class' in defs) {\r\n                    print(' class=\"'+defs.class+'\"');\r\n                };\r\n                print('>');\r\n            }\r\n        %>{{{this.<%= defs.name %>}}}<%\r\n            if (tag) {\r\n                print( '</' + tag + '>');\r\n            }\r\n    %>\r\n    </td>\r\n<% }); %>\r\n","list-row-expanded":"<% if (layout.right) { %>\r\n<div class=\"pull-right right cell\" data-name=\"buttons\">\r\n    {{{<%= layout.right.name %>}}}\r\n</div>\r\n<% } %>\r\n<% _.each(layout.rows, function (row, key) { %><div class=\"expanded-row\"><% _.each(row, function (defs, key) { %><span class=\"cell<%= defs.align ? ' pull-right' : '' %><%= defs.small ? ' small' : '' %><%= defs.soft ? ' text-soft' : '' %>\" data-name=\"<%= defs.field %>\"><%\r\n                var tag = 'tag' in defs ? defs.tag : false;\r\n                if (tag) {\r\n                    print( '<' + tag);\r\n                    if ('id' in defs) {\r\n                        print(' id=\"'+defs.id+'\"');\r\n                    }\r\n                    if ('class' in defs) {\r\n                        print(' class=\"'+defs.class+'\"');\r\n                    };\r\n                    print('>');\r\n                }\r\n            %>{{{this.<%= defs.name %>}}}<%\r\n                if (tag) {\r\n                    print( '</' + tag + '>');\r\n                }\r\n        %></span><% }); %></div>\r\n<% }); %>\r\n","default":"<% _.each(layout, function (defs, key) {\r\n        var tag = 'tag' in defs ? defs.tag : 'div';\r\n        print( '<' + tag);\r\n        if ('id' in defs) {\r\n            print(' id=\"'+defs.id+'\"');\r\n        }\r\n        if ('class' in defs) {\r\n            print(' class=\"'+defs.class+'\"');\r\n        }\r\n        print('>');\r\n    %>\r\n        {{{this.<%= defs.name %>}}}\r\n        <%= '</' + tag + '>' %>\r\n<% }); %>\r\n","columns-2":"<% _.each(layout, function (row, rowNumber) { %>\r\n    <div class=\"col-sm-6\">\r\n    <% _.each(row, function (defs, key) { %>\r\n        <%\r\n            var tag = 'tag' in defs ? defs.tag : 'div';\r\n            print( '<' + tag);\r\n            if ('id' in defs) {\r\n                print(' id=\"'+defs.id+'\"');\r\n            }\r\n            print(' class=\"');\r\n            if ('class' in defs) {\r\n                print(defs.class);\r\n            };\r\n            print('\"');\r\n            print('>');\r\n        %>\r\n            {{{this.<%= defs.name %>}}}\r\n            <%= '</' + tag + '>' %>\r\n    <% }); %>\r\n    </div>\r\n<% }); %>\r\n"};
