(self["webpackChunktine20_tinebase"] = self["webpackChunktine20_tinebase"] || []).push([[9542],{

/***/ 67268:
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

/* pkg: Felamimail FAT Client (js/Felamimail-FAT.js)*/
__webpack_require__(26309);
__webpack_require__(49471);
__webpack_require__(67792);
__webpack_require__(54758);
__webpack_require__(27166);
__webpack_require__(47694);
__webpack_require__(53539);
__webpack_require__(63312);
__webpack_require__(80316);
__webpack_require__(68845);
__webpack_require__(71182);
__webpack_require__(12600);
__webpack_require__(57671);
__webpack_require__(89697);
__webpack_require__(48139);
__webpack_require__(1130);
__webpack_require__(3220);
__webpack_require__(48588);
__webpack_require__(88738);
__webpack_require__(72054);
__webpack_require__(82090);
__webpack_require__(9030);
__webpack_require__(69170);
__webpack_require__(97710);
__webpack_require__(62980);
__webpack_require__(14516);
__webpack_require__(54411);
__webpack_require__(86286);
__webpack_require__(31163);
__webpack_require__(12707);
__webpack_require__(8045);
__webpack_require__(85177);
/* pkg: Felamimail FAT Client (css/Felamimail-FAT.css)*/
__webpack_require__(58940);


/***/ }),

/***/ 54411:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Addressbook
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2013 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.ns('Tine.Addressbook');

/**
 * Contact grid panel
 * 
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.ContactGridPanel
 * @extends     Tine.widgets.grid.GridPanel
 * 
 * <p>Contact Grid Panel</p>
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Addressbook.ContactGridPanel
 */
Tine.Addressbook.ContactGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
  /**
   * record class
   * @cfg {Tine.Addressbook.Model.Contact} recordClass
   */
  recordClass: 'Addressbook.Model.Contact',
  /**
   * grid specific
   * @private
   */
  defaultSortInfo: {
    field: 'n_fileas',
    direction: 'ASC'
  },
  gridConfig: {
    autoExpandColumn: 'n_fileas',
    enableDragDrop: true,
    ddGroup: 'containerDDGroup'
  },
  copyEditAction: true,
  felamimail: false,
  multipleEdit: true,
  duplicateResolvable: true,
  /**
   * @cfg {Bool} hasDetailsPanel 
   */
  hasDetailsPanel: true,
  /**
   * inits this cmp
   * @private
   */
  initComponent: function () {
    // this.recordProxy = Tine.Addressbook.contactBackend;

    // check if felamimail is installed and user has run right and wants to use felamimail in adb
    if (Tine.Felamimail && Tine.Tinebase.common.hasRight('run', 'Felamimail') && Tine.Felamimail.registry.get('preferences').get('useInAdb')) {
      this.felamimail = Tine.Felamimail.registry.get('preferences').get('useInAdb') == 1;
    }
    this.gridConfig.cm = this.getColumnModel();
    if (this.hasDetailsPanel) {
      this.detailsPanel = this.getDetailsPanel();
    }
    Tine.Addressbook.ContactGridPanel.superclass.initComponent.call(this);
  },
  /**
   * responsive content Renderer
   *
   * @param {String} folderId
   * @param {Object} metadata
   * @param {Folder|Account} record
   * @return {String}
   */
  responsiveRenderer: function (folderId, metadata, record) {
    const block = document.createElement('div');
    block.className = 'responsive-title';
    const iconEl = document.createElement('img');
    iconEl.src = record.data.jpegphoto || '';
    iconEl.className = 'contact-image';

    // nameEl
    const nameEl = document.createElement('div');
    nameEl.innerText = record.data.n_fileas;
    nameEl.setAttribute('ext:qtip', Ext.util.Format.htmlEncode(record.data.n_fileas));

    // companyEl
    const companyEl = document.createElement('div');
    companyEl.innerText = record.data.org_name;
    companyEl.setAttribute('ext:qtip', Ext.util.Format.htmlEncode(record.data.org_name));
    const row1 = document.createElement('div');
    const row1Left = document.createElement('div');
    const row1Right = document.createElement('div');
    row1.className = 'responsive-grid-row ';
    row1Left.className = 'responsive-grid-row-left';
    row1Right.className = 'responsive-grid-row-right';
    nameEl.className = 'responsive-grid-text-medium';
    companyEl.className = 'responsive-grid-text-small';
    row1Left.appendChild(iconEl);
    row1Right.appendChild(nameEl);
    row1Right.appendChild(companyEl);
    row1.appendChild(row1Left);
    row1.appendChild(row1Right);
    block.appendChild(row1);
    return block.outerHTML;
  },
  /**
   * returns column model
   * 
   * @return Ext.grid.ColumnModel
   * @private
   */
  getColumnModel: function () {
    return new Ext.grid.ColumnModel({
      defaults: {
        sortable: true,
        hidden: true,
        resizable: true
      },
      columns: this.getColumns()
    });
  },
  /**
   * returns array with columns
   * 
   * @return {Array}
   */
  getColumns: function () {
    return Tine.Addressbook.ContactGridPanel.getBaseColumns(this.app.i18n).concat(this.getModlogColumns().concat(this.getCustomfieldColumns()));
  },
  /**
   * @private
   */
  initActions: function () {
    this.actions_import = new Ext.Action({
      //requiredGrant: 'addGrant',
      text: this.app.i18n._('Import contacts'),
      disabled: false,
      handler: this.onImport,
      iconCls: 'action_import',
      scope: this,
      allowMultiple: true
    });

    // register actions in updater
    this.actionUpdater.addActions([this.actions_import]);
    Tine.Addressbook.ContactGridPanel.superclass.initActions.call(this);
  },
  /**
   * get default / selected addressbook container
   *
   * @returns {Object|Tine.Tinebase.Model.Container}
   */
  getDefaultContainer: function () {
    return this.app.getMainScreen().getWestPanel().getContainerTreePanel().getDefaultContainer('defaultAddressbook');
  },
  /**
   * returns details panel
   * 
   * @private
   * @return {Tine.Addressbook.ContactGridDetailsPanel}
   */
  getDetailsPanel: function () {
    return new Tine.Addressbook.ContactGridDetailsPanel({
      recordClass: this.recordClass,
      gridpanel: this,
      il8n: this.app.i18n,
      felamimail: this.felamimail
    });
  }
});

// Static Methods

/**
 * tid renderer
 * 
 * @private
 * @return {String} HTML
 */
Tine.Addressbook.ContactGridPanel.contactTypeRenderer = function (data, cell, record) {
  var _record$data;
  const i18n = Tine.Tinebase.appMgr.get('Addressbook').i18n;
  const hasAccount = record.get && record.get('account_id') || record.account_id;
  const isEmailAccount = ((_record$data = record.data) === null || _record$data === void 0 ? void 0 : _record$data.type) === 'email_account';
  const typeRenderer = hasAccount ? 'renderer_typeAccountIcon' : isEmailAccount ? 'renderer_typeEmailAccountIcon' : 'renderer_typeContactIcon';
  const cssClass = 'tine-grid-row-action-icon ' + typeRenderer;
  const qtipText = Tine.Tinebase.common.doubleEncode(hasAccount ? i18n._('Contact of a user account') : isEmailAccount ? i18n._('Email Account Contact') : i18n._('Contact'));
  return '<div ext:qtip="' + qtipText + '" style="background-position:0px;" class="' + cssClass + '">&#160</div>';
};
Tine.Addressbook.ContactGridPanel.displayNameRenderer = function (data) {
  var i18n = Tine.Tinebase.appMgr.get('Addressbook').i18n;
  return data ? Tine.Tinebase.EncodingHelper.encode(data) : '<div class="renderer_displayNameRenderer_noName">' + i18n._('No name') + '</div>';
};
Tine.Addressbook.ContactGridPanel.countryRenderer = function (data) {
  data = Locale.getTranslationData('CountryList', data);
  return Ext.util.Format.htmlEncode(data);
};

/**
 * Column renderer adb preferred_address field
 * @param value
 * @return {*}
 */
Tine.Addressbook.ContactGridPanel.preferredAddressRenderer = function (value) {
  var i18n = Tine.Tinebase.appMgr.get('Addressbook').i18n;
  switch (value) {
    case '0':
      return i18n._('Business');
    case '1':
      return i18n._('Private');
    default:
      return i18n._('Not set');
  }
};
Tine.Addressbook.ContactGridPanel.languageRenderer = function (value) {
  var allLanguages = Locale.getTranslationList('Language');
  return allLanguages[value];
};

/**
 * Statically constructs the columns used to represent a contact. Reused by ListMemberGridPanel + ListMemberRoleGridPanel
 */
Tine.Addressbook.ContactGridPanel.getBaseColumns = function (i18n) {
  var columns = [{
    id: 'type',
    header: i18n._('Type'),
    tooltip: i18n._('Type'),
    dataIndex: 'type',
    width: 20,
    renderer: Tine.Addressbook.ContactGridPanel.contactTypeRenderer.createDelegate(this),
    hidden: false
  }, {
    id: 'jpegphoto',
    header: i18n._('Contact Image'),
    tooltip: i18n._('Contact Image'),
    dataIndex: 'jpegphoto',
    width: 20,
    sortable: false,
    resizable: false,
    renderer: Tine.widgets.grid.imageRenderer,
    hidden: false
  }, {
    id: 'attachments',
    header: '<div class="action_attach tine-grid-row-action-icon"></div>',
    tooltip: window.i18n._('Attachments'),
    dataIndex: 'attachments',
    width: 20,
    sortable: false,
    resizable: false,
    renderer: Tine.widgets.grid.attachmentRenderer,
    hidden: false
  }, {
    id: 'tags',
    header: i18n._('Tags'),
    dataIndex: 'tags',
    width: 50,
    renderer: Tine.Tinebase.common.tagsRenderer,
    sortable: false,
    hidden: false
  }, {
    id: 'salutation',
    header: i18n._('Salutation'),
    dataIndex: 'salutation',
    renderer: Tine.Tinebase.widgets.keyfield.Renderer.get('Addressbook', 'contactSalutation')
  }, {
    id: 'container_id',
    dataIndex: 'container_id',
    header: Tine.Addressbook.Model.Contact.getContainerName(),
    width: 150,
    renderer: Tine.Tinebase.common.containerRenderer
  }, {
    id: 'n_prefix',
    header: i18n._('Title'),
    dataIndex: 'n_prefix',
    width: 80
  }, {
    id: 'n_middle',
    header: i18n._('Middle Name'),
    dataIndex: 'n_middle',
    width: 80
  }, {
    id: 'n_family',
    header: i18n._('Last Name'),
    dataIndex: 'n_family'
  }, {
    id: 'n_given',
    header: i18n._('First Name'),
    dataIndex: 'n_given',
    width: 80
  }, {
    id: 'n_fn',
    header: i18n._('Full Name'),
    dataIndex: 'n_fn',
    renderer: Tine.Addressbook.ContactGridPanel.displayNameRenderer
  }, {
    id: 'n_fileas',
    header: i18n._('Display Name'),
    dataIndex: 'n_fileas',
    hidden: false,
    renderer: Tine.Addressbook.ContactGridPanel.displayNameRenderer
  }, {
    id: 'org_name',
    header: i18n._('Company / Organisation'),
    dataIndex: 'org_name',
    width: 120,
    hidden: false
  }, {
    id: 'org_unit',
    header: i18n._('Unit'),
    dataIndex: 'org_unit'
  }, {
    id: 'title',
    header: i18n._('Job Title'),
    dataIndex: 'title'
  },
  //            { id: 'role', header: i18n._('Job Role'), dataIndex: 'role' },
  //            { id: 'room', header: i18n._('Room'), dataIndex: 'room' },
  {
    id: 'adr_one_street',
    header: i18n._('Street'),
    dataIndex: 'adr_one_street'
  }, {
    id: 'adr_one_locality',
    header: i18n._('City'),
    dataIndex: 'adr_one_locality',
    width: 150,
    hidden: false
  }, {
    id: 'adr_one_region',
    header: i18n._('Region'),
    dataIndex: 'adr_one_region'
  }, {
    id: 'adr_one_postalcode',
    header: i18n._('Postalcode'),
    dataIndex: 'adr_one_postalcode'
  }, {
    id: 'adr_one_countryname',
    header: i18n._('Country'),
    dataIndex: 'adr_one_countryname',
    renderer: Tine.Addressbook.ContactGridPanel.countryRenderer
  }, {
    id: 'adr_two_street',
    header: i18n._('Street (private)'),
    dataIndex: 'adr_two_street'
  }, {
    id: 'adr_two_locality',
    header: i18n._('City (private)'),
    dataIndex: 'adr_two_locality'
  }, {
    id: 'adr_two_region',
    header: i18n._('Region (private)'),
    dataIndex: 'adr_two_region'
  }, {
    id: 'adr_two_postalcode',
    header: i18n._('Postalcode (private)'),
    dataIndex: 'adr_two_postalcode'
  }, {
    id: 'adr_two_countryname',
    header: i18n._('Country (private)'),
    dataIndex: 'adr_two_countryname',
    renderer: Tine.Addressbook.ContactGridPanel.countryRenderer
  }, {
    id: 'preferred_address',
    header: i18n._('Preferred Address'),
    dataIndex: 'preferred_address',
    renderer: Tine.Addressbook.ContactGridPanel.preferredAddressRenderer
  }, {
    id: 'email',
    header: i18n._('Email'),
    dataIndex: 'email',
    width: 150,
    hidden: false
  }, {
    id: 'tel_work',
    header: i18n._('Phone'),
    dataIndex: 'tel_work',
    hidden: false
  }, {
    id: 'tel_cell',
    header: i18n._('Mobile'),
    dataIndex: 'tel_cell',
    hidden: false
  }, {
    id: 'tel_fax',
    header: i18n._('Fax'),
    dataIndex: 'tel_fax'
  }, {
    id: 'tel_car',
    header: i18n._('Car phone'),
    dataIndex: 'tel_car'
  }, {
    id: 'tel_pager',
    header: i18n._('Pager'),
    dataIndex: 'tel_pager'
  }, {
    id: 'tel_home',
    header: i18n._('Phone (private)'),
    dataIndex: 'tel_home'
  }, {
    id: 'tel_fax_home',
    header: i18n._('Fax (private)'),
    dataIndex: 'tel_fax_home'
  }, {
    id: 'tel_cell_private',
    header: i18n._('Mobile (private)'),
    dataIndex: 'tel_cell_private'
  }, {
    id: 'email_home',
    header: i18n._('Email (private)'),
    dataIndex: 'email_home'
  }, {
    id: 'url',
    header: i18n._('Web'),
    dataIndex: 'url'
  }, {
    id: 'url_home',
    header: i18n._('URL (private)'),
    dataIndex: 'url_home'
  }, {
    id: 'language',
    header: i18n._('Language'),
    dataIndex: 'language',
    renderer: Tine.Addressbook.ContactGridPanel.languageRenderer
  }, {
    id: 'note',
    header: i18n._('Note'),
    dataIndex: 'note'
  }, {
    id: 'tz',
    header: i18n._('Timezone'),
    dataIndex: 'tz'
  }, {
    id: 'geo',
    header: i18n._('Geo'),
    dataIndex: 'geo'
  }, {
    id: 'bday',
    header: i18n._('Birthday'),
    dataIndex: 'bday',
    renderer: Tine.Tinebase.common.dateRenderer
  }, {
    id: 'color',
    header: i18n._('Color'),
    dataIndex: 'color'
  }];
  if (Tine.Tinebase.appMgr.get('Addressbook').featureEnabled('featureIndustry')) {
    columns.push({
      id: 'industry',
      header: i18n._('Industry'),
      dataIndex: 'industry',
      renderer: Tine.Tinebase.common.foreignRecordRenderer
    });
  }
  if (Tine.Tinebase.appMgr.get('Addressbook').featureEnabled('featureShortName')) {
    columns.push({
      id: 'n_short',
      header: i18n._('Short Name'),
      dataIndex: 'n_short',
      width: 50
    });
  }
  return columns;
};

/***/ }),

/***/ 9030:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _renderers__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(43677);
/* harmony import */ var _Tinebase_js_widgets_form_RecordEditFieldTriggerPlugin__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28198);
/*
 * Tine 2.0
 * contacts combo box and store
 * 
 * @package     Addressbook
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */


Ext.ns('Tine.Addressbook');

/**
 * contact selection combo box
 * 
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.SearchCombo
 * @extends     Ext.form.ComboBox
 * 
 * <p>Contact Search Combobox</p>
 * <p><pre>
 * TODO         make this a twin trigger field with 'clear' button?
 * TODO         add switch to filter for expired/enabled/disabled user accounts
 * </pre></p>
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schuele <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Addressbook.SearchCombo
 * 
 * TODO         add     forceSelection: true ?
 */

Tine.Addressbook.ContactSearchCombo = Ext.extend(Tine.Tinebase.widgets.form.RecordPickerComboBox, {
  /**
   * @cfg {Boolean} userOnly
   */
  userOnly: false,
  /**
   * @cfg {Boolean} noEditPlugin
   */
  noEditPlugin: false,
  /**
   * @cfg {Boolean} addPathFilter
   */
  addPathFilter: true,
  /**
   * use account objects/records in get/setValue
   * 
   * @cfg {Boolean} legacy
   * @legacy
   * 
   * TODO remove this later
   */
  useAccountRecord: false,
  allowBlank: true,
  itemSelector: 'div.search-item',
  minListWidth: 350,
  //private
  initComponent: function () {
    this.app = Tine.Tinebase.appMgr.get('Addressbook');
    if (this.recordClass === null) {
      this.recordClass = Tine.Addressbook.Model.Contact;
      // this.recordProxy = Tine.Addressbook.contactBackend;
    }

    this.emptyText = this.emptyText || (this.readOnly || this.disabled ? '' : this.userOnly ? this.app.i18n._('Search for users ...') : this.app.i18n._('Search for Contacts ...'));
    this.useEditPlugin = this.hasOwnProperty('useEditPlugin') ? this.useEditPlugin : !this.useAccountRecord && !this.noEditPlugin;
    this.initTemplate();
    Tine.Addressbook.SearchCombo.superclass.initComponent.call(this);
  },
  /**
   * is called in accountMode to reset the value
   * @param value
   */
  processValue: function (value) {
    if (this.useAccountRecord) {
      if (value == '') {
        this.accountId = null;
        this.selectedRecord = null;
      }
    }
    return Tine.Addressbook.SearchCombo.superclass.processValue.call(this, value);
  },
  /**
   * use beforequery to set query filter
   * 
   * @param {Event} qevent
   */
  onBeforeQuery: function (qevent) {
    Tine.Addressbook.SearchCombo.superclass.onBeforeQuery.apply(this, arguments);
    const filter = this.store.baseParams.filter;
    if (this.addPathFilter) {
      const queryFilter = _.find(filter, {
        field: 'query'
      });
      const pathFilter = {
        field: 'path',
        operator: 'contains',
        value: queryFilter.value
      };
      _.remove(filter, queryFilter);
      filter.push({
        condition: "OR",
        filters: [queryFilter, pathFilter]
      });
    }
    if (this.userOnly) {
      filter.push({
        field: 'type',
        operator: 'equals',
        value: 'user'
      });
    }
  },
  /**
   * init template
   * @private
   */
  initTemplate: function () {
    // Custom rendering Template
    if (!this.tpl) {
      this.tpl = new Ext.XTemplate('<tpl for="."><div class="search-item addressbook-search-combo x-combo-list-item">', '<table>', '<tr>', '<td style="min-width: 20px;">{[Tine.Addressbook.ContactGridPanel.contactTypeRenderer(null, null, values)]}</td>', '<td width="30%"><b>{[Tine.Addressbook.ContactGridPanel.displayNameRenderer(values.n_fileas)]}</b><br/>' + '{[Tine.Tinebase.EncodingHelper.encode(values.org_name)]}<br/>{[this.encode(values.org_unit)]}</td>', '<td width="25%">{[this.preferredAddressRender(null, null, values)]}<br/>' + '{[this.encode(values.email)]}</td>', '<td width="25%">{[Tine.Tinebase.EncodingHelper.encode(values.tel_work)]}<br/>{[Tine.Tinebase.EncodingHelper.encode(values.tel_cell)]}</td>', '<td width="50px">', '<img width="45px" height="39px" src="{jpegphoto}" />', '</td>', '</tr>', '</table>', '{[Tine.widgets.path.pathsRenderer(values.paths, this.getLastQuery())]}', '</div></tpl>', {
        getLastQuery: this.getLastQuery.createDelegate(this),
        preferredAddressRender: _renderers__WEBPACK_IMPORTED_MODULE_0__.preferredAddressRender
      });
    }
  },
  getValue: function () {
    if (this.useAccountRecord) {
      if (this.selectedRecord) {
        return this.selectedRecord.get('account_id') || this.selectedRecord.get('accountId');
      } else {
        return this.accountId;
      }
    } else {
      return Tine.Addressbook.SearchCombo.superclass.getValue.call(this);
    }
  },
  setValue: function (value) {
    if (this.useAccountRecord) {
      if (value) {
        if (value.accountId) {
          // account object
          this.selectedRecord = this.selectedAccount = Tine.Tinebase.data.Record.setFromJson(value, Tine.Tinebase.Model.User);
          this.accountId = value.accountId;
          value = value.accountDisplayName;
        } else if (typeof value.get == 'function') {
          // account record
          this.selectedRecord = this.selectedAccount = value;
          this.accountId = value.get('id');
          value = value.get('name');
        }
      } else {
        this.accountId = null;
        this.selectedRecord = null;
      }
    }
    return Tine.Addressbook.SearchCombo.superclass.setValue.call(this, value);
  }
});

// legacy
Tine.Addressbook.SearchCombo = Tine.Addressbook.ContactSearchCombo;
Ext.reg('addressbookcontactpicker', Tine.Addressbook.ContactSearchCombo);
Tine.widgets.form.RecordPickerManager.register('Addressbook', 'Contact', Tine.Addressbook.ContactSearchCombo);

/***/ }),

/***/ 43677:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "addressRenderer": () => (/* binding */ addressRenderer),
/* harmony export */   "avatarRenderer": () => (/* binding */ avatarRenderer),
/* harmony export */   "countryRenderer": () => (/* binding */ countryRenderer),
/* harmony export */   "imageRenderer": () => (/* binding */ imageRenderer),
/* harmony export */   "mailAddressRenderer": () => (/* binding */ mailAddressRenderer),
/* harmony export */   "preferredAddressRender": () => (/* binding */ preferredAddressRender),
/* harmony export */   "urlRenderer": () => (/* binding */ urlRenderer)
/* harmony export */ });
/*
 * Tine 2.0
 *
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Christian Feitl <c.feitl@metaways.de>
 * @copyright   Copyright (c) 2022 Metaways Infosystems GmbH (http://www.metaways.de)
 */

/**
 * Render given MailAddresss
 *
 * @namespace   Tine.Addressbook
 * @author      Michael Spahn <m.spahn@metaways.de>
 * @singleton
 */
const mailAddressRenderer = function (email) {
  if (!email) {
    return '';
  }
  email = Tine.Tinebase.EncodingHelper.encode(email);
  var link = this.felamimail === true ? '#' : 'mailto:' + email;
  var id = Ext.id() + ':' + email;
  return '<a href="' + link + '" class="tinebase-email-link" id="' + id + '">' + Ext.util.Format.ellipsis(email, 18) + '</a>';
};
Tine.widgets.grid.RendererManager.register('Addressbook', 'Addressbook_Model_Contact', 'email', mailAddressRenderer, 'displayPanel');

/**
 * Render country name by it's iso code
 *
 * @namespace   Tine.Addressbook
 * @author      Michael Spahn <m.spahn@metaways.de>
 * @singleton
 */
const countryRenderer = function (v) {
  return Locale.getTranslationData('CountryList', v);
};
Tine.widgets.grid.RendererManager.register('Addressbook', 'Addressbook_Model_Contact', 'country', countryRenderer, 'displayPanel');

/**
 * Render given addresss
 *
 * You need to pass the record and a config to use this renderer.
 * The config contains a mapping, which fields from the record should have which place in the template.
 * Undefined fields won't be rendered, this keeps it well reusable for all address like purposes.
 *
 * @namespace   Tine.Addressbook
 * @author      Michael Spahn <m.spahn@metaways.de>
 * @singleton
 */
const addressRenderer = function (v, metadata, record, store, a, b, config) {
  var template = new Ext.XTemplate('<tpl for="." class="address">' + '<tpl if="street">{street} <br /></tpl>' + '<tpl if="street2">{street2} <br /></tpl>' + '<tpl if="postalcode">{postalcode}</tpl> <tpl if="locality">{locality}</tpl><br />' + '<tpl if="region">{region} <br /></tpl>' + '<tpl if="country">{country}</tpl>' + '</tpl>');
  template.compile();
  if (!record) {
    return template.applyTemplate({
      street: '',
      street2: '',
      postalcode: '',
      locality: '',
      region: '',
      country: ''
    });
  }
  var local = Ext.apply({}, config);
  var keys = Object.keys(local);

  // According to config, resolve the given fields from record
  keys.forEach(function (key) {
    var value = null;
    if (!record.data) {
      value = window.lodash.get(record, local[key]);
    } else {
      value = window.lodash.get(record.data, local[key]);
    }
    local[key] = Tine.Tinebase.EncodingHelper.encode(value);

    // Country code to country name
    // @todo: Wouldn't it be cool, if this could be managed by the modelconfig as well?
    if (key === 'country') {
      var countryRenderer = Tine.widgets.grid.RendererManager.get("Addressbook", "Addressbook_Model_Contact", "country", "displayPanel");
      local[key] = countryRenderer(local[key]);
    }
  });
  return template.applyTemplate(local);
};
Tine.widgets.grid.RendererManager.register('Addressbook', 'Addressbook_Model_Contact', 'addressblock', addressRenderer, 'displayPanel');

/**
 * Render preferred addresss
 *
 * give the preferred address from contact. (street, postalcode and locality)
 * @namespace   Tine.Addressbook
 * @author      Christian Feitl <c.feitl@metaways.de>
 */
const preferredAddressRender = function (v, metadata, record) {
  let preferredAddress = !!+_.get(record.data, 'preferred_address') || !!+_.get(record, 'preferred_address'),
    adr_street = preferredAddress ? 'adr_two_street' : 'adr_one_street',
    adr_postalcode = preferredAddress ? 'adr_two_postalcode' : 'adr_one_postalcode',
    adr_locality = preferredAddress ? 'adr_two_locality' : 'adr_one_locality',
    contact = record.data ? record.data : record,
    result = '';
  _.each([adr_street, adr_postalcode, adr_locality], function (value) {
    if (contact[value] !== null) {
      result += Ext.util.Format.htmlEncode(_.get(contact, value, ' ')) + ' ';
    }
  });
  return result;
};
Tine.widgets.grid.RendererManager.register('Addressbook', 'Addressbook_Model_Contact', 'addressblock', addressRenderer, 'displayPanel');

/**
 * Render given image
 *
 * @namespace   Tine.Addressbook
 * @author      Michael Spahn <m.spahn@metaways.de>
 * @singleton
 */
const imageRenderer = function (jpegphoto, metadata, record) {
  function getImageUrl(url, width, height, contact) {
    var _ = window.lodash,
      mtime = _.get(contact, 'data.last_modified_time') || _.get(contact, 'data.creation_time') || null;
    if (url.match(/&/)) {
      url = Ext.ux.util.ImageURL.prototype.parseURL(url);
      url.width = width;
      url.height = height;
      url.ratiomode = 0;
      url.mtime = Ext.isDate(mtime) ? mtime.getTime() : new Date().getTime();
    }
    return url;
  }
  var url = getImageUrl(jpegphoto, 90, 113, record);
  return '<img src="' + url + '" class="' + (String(url).match(/\.svg/) ? '' : 'dark-reverse') + '" />';
};
Tine.widgets.grid.RendererManager.register('Addressbook', 'Addressbook_Model_Contact', 'image', imageRenderer, 'displayPanel');

/**
 * Render given URL as html
 *
 * @namespace   Tine.Addressbook
 * @author      Michael Spahn <m.spahn@metaways.de>
 * @singleton
 */
const urlRenderer = function (url) {
  return '<a href=' + Tine.Tinebase.EncodingHelper.encode(url, 'href') + ' target="_blank">' + Tine.Tinebase.EncodingHelper.encode(url, 'shorttext') + '</a>';
};
Tine.widgets.grid.RendererManager.register('Addressbook', 'Addressbook_Model_Contact', 'url', urlRenderer, 'displayPanel');
const avatarRenderer = function (n_short, metadata, record) {
  const fullName = record ? record.get('n_fileas') : n_short;
  let shortName = record ? record.get('n_short') : n_short;
  if (!shortName && record) {
    let names = _.compact([record.get('n_family'), record.get('n_middle'), record.get('n_given')]);
    if (!names.length && fullName) {
      names = fullName.split(' ');
    }
    if (!names.length) {
      names = _.compact([record.get('accountLastName'), record.get('accountFirstName')]);
    }
    if (names.length > 1) {
      shortName = _.map(names, n => {
        return n.substring(0, 1).toUpperCase();
      }).join('');
    } else {
      shortName = String(names[0]).substring(0, 2);
    }
  }
  const colorSchema = Tine.Calendar.colorMgr.getSchema(record && record.get('color') ? record.get('color') : Tine.Calendar.ColorManager.stringToColour(shortName).substring(1, 6));
  return shortName ? `<span class="adb-avatar-wrap" ext:qtip="${fullName}" style="background-color: ${colorSchema.color}; color: ${colorSchema.text}">${shortName}</span>` : '';
};


/***/ }),

/***/ 82090:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var util_waitFor_es6__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(50696);
/*
 * Tine 2.0
 *
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2009-2020 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.namespace('Tine.Felamimail');
__webpack_require__(38062);
__webpack_require__(30092);


/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.AccountEditDialog
 * @extends     Tine.widgets.dialog.EditDialog
 *
 * <p>Account Edit Dialog</p>
 * <p>
 * </p>
 *
 * @author      Philipp Schuele <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2009 Metaways Infosystems GmbH (http://www.metaways.de)
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 *
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Felamimail.AccountEditDialog
 *
 */
Tine.Felamimail.AccountEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
  /**
   * @private
   */
  windowNamePrefix: 'AccountEditWindow_',
  appName: 'Felamimail',
  recordClass: Tine.Felamimail.Model.Account,
  recordProxy: Tine.Felamimail.accountBackend,
  loadRecord: false,
  tbarItems: [],
  evalGrants: false,
  asAdminModule: false,
  /**
   * needed to prevent events in form fields (i.e. migration_approved checkbox)
   * @type boolean
   */
  preventCheckboxEvents: true,
  initComponent: function () {
    if (this.asAdminModule) {
      this.recordProxy = new Tine.Tinebase.data.RecordProxy({
        appName: 'Admin',
        modelName: 'EmailAccount',
        recordClass: Tine.Felamimail.Model.Account,
        idProperty: 'id'
      });
    }
    this.hasEditAccountRight = this.asAdminModule ? true : Tine.Tinebase.common.hasRight('manage_accounts', 'Felamimail');
    Tine.Felamimail.AccountEditDialog.superclass.initComponent.call(this);
  },
  /**
   * overwrite update toolbars function (we don't have record grants yet)
   * @private
   */
  updateToolbars: function () {},
  /**
   * executed after record got updated from proxy
   *
   * -> only allow to change some fields if it is a system account
   */
  onRecordLoad: function () {
    Tine.Felamimail.AccountEditDialog.superclass.onRecordLoad.call(this);
    if (!this.copyRecord && !this.record.id && this.window) {
      this.window.setTitle(this.app.i18n._('Add New Account'));
      if (this.asAdminModule) {
        this.record.set('type', 'shared');
      }
    } else {
      this.grantsGrid.setValue(this.record.get('grants'));
    }
    this.loadEmailQuotas();
    this.disableSieveTabs();
    this.disableFormFields();
    this.loadEmailAliasesAndForwards();
  },
  // finally load the record into the form
  onAfterRecordLoad: function () {
    Tine.Felamimail.AccountEditDialog.superclass.onAfterRecordLoad.call(this);
    this.preventCheckboxEvents = false;
    this.loadDefaultAddressbook();
    this.loadSieve();
  },
  /**
   * executed when record gets updated from form
   */
  onRecordUpdate: function (callback, scope) {
    Tine.Felamimail.AccountEditDialog.superclass.onRecordUpdate.apply(this, arguments);
    const hasRight = this.checkAccountEditRight(this.record);
    this.action_saveAndClose.setDisabled(!hasRight);
    this.record.set('grants', this.grantsGrid.getValue());
    this.updateContactAddressbook();
    this.updateEmailQuotas();
    this.updateEmailAliasesAndForwards();
    if (this.isSystemAccount()) {
      this.updateSieve();
    }
  },
  disableFormFields: function () {
    // if account type == system disable most of the input fields
    this.getForm().items.each(function (item) {
      let disabled = false;
      // only enable some fields
      switch (item.name) {
        case 'user_id':
          this.disableUserCombo(item);
          break;
        case 'migration_approved':
          disabled = this.record.get('type') === 'shared' || this.record.get('type') === 'adblist';
          item.setDisabled(disabled);
          break;
        case 'signatures':
        case 'signature_position':
        case 'from':
        case 'display_format':
        case 'compose_format':
        case 'preserve_format':
        case 'organization':
        case 'reply_to':
        case 'save_sent_mail_copy':
        case 'sent_folder':
        case 'trash_folder':
        case 'drafts_folder':
        case 'templates_folder':
          // fields can be edited in any mode
          item.setDisabled(false);
          break;
        case 'type':
          item.setDisabled(!this.asAdminModule);
          break;
        case 'password':
          this.disablePasswordField(item);
          break;
        case 'revealpwd':
          item.setDisabled(false);
          break;
        case 'user':
          disabled = !this.hasEditAccountRight || !(!this.record.get('type') || this.record.get('type') === 'userInternal' || this.record.get('type') === 'user');
          item.setDisabled(disabled);
          break;
        case 'smtp_user':
        case 'smtp_password':
          item.setDisabled(!this.hasEditAccountRight || this.isSystemAccount() || this.asAdminModule && this.record.get('type') === 'user');
          break;
        case 'host':
        case 'port':
        case 'ssl':
        case 'smtp_hostname':
        case 'smtp_port':
        case 'smtp_ssl':
        case 'smtp_auth':
        case 'sieve_hostname':
        case 'sieve_port':
        case 'sieve_ssl':
          // always disabled for system accounts
          item.setDisabled(this.isSystemAccount() || !this.hasEditAccountRight);
          break;
        case 'sieve_notification_email':
        case 'sieve_notification_move':
        case 'sieve_notification_move_folder':
        case 'enabled':
          // always disabled for non-system accounts
          item.setDisabled(!this.isSystemAccount());
          break;
        case 'emailMailSize':
        case 'emailSieveSize':
          item.setDisabled(true);
          break;
        case 'emailMailQuota':
          item.setDisabled(!this.emailImapUser.hasOwnProperty('emailMailQuota') || !this.hasEditAccountRight);
          break;
        case 'emailSieveQuota':
          item.setDisabled(!this.emailImapUser.hasOwnProperty('emailSieveQuota') || !this.hasEditAccountRight);
          break;
        case 'container_id':
          item.setDisabled(this.record.get('visibility') === 'hidden');
          break;
        case 'visibility':
          item.setDisabled(this.record.get('type') === 'system');
          break;
        case 'save_sent_mail_to_source':
          item.setDisabled(this.record.get('message_sent_copy_behavior') === 'skip');
          break;
        case 'name':
          item.setDisabled(!this.asAdminModule && (this.record.get('type') === 'shared' || this.record.get('type') === 'adblist' || !this.hasEditAccountRight));
          break;
        default:
          item.setDisabled(!this.asAdminModule && (this.isSystemAccount() || !this.hasEditAccountRight));
      }
    }, this);
    this.grantsGrid.setDisabled(!(this.record.get('type') === 'shared' && this.asAdminModule));
  },
  disableUserCombo: function (item) {
    if (!this.asAdminModule) {
      item.hide();
    } else {
      let disabled = this.record.get('type') === 'shared' || this.record.get('type') === 'adblist';
      item.setDisabled(disabled);
      if (disabled) {
        item.setValue('');
      }
    }
  },
  disablePasswordField: function (item) {
    if (this.record.get('type') && this.record.get('type') === 'user') {
      item.setDisabled(false);
    } else {
      item.setDisabled(!(!this.record.get('type') || this.record.get('type') === 'shared'));
    }
  },
  disableSieveTabs() {
    if (this.asAdminModule) {
      if (!Tine.Admin.registry.get('masterSieveAccess')) {
        this.vacationPanel.setDisabled(true);
        this.rulesGridPanel.setDisabled(true);
      }
    } else {
      if (!this.isSystemAccount()) {
        this.vacationPanel.setDisabled(true);
        this.rulesGridPanel.setDisabled(true);
      }
    }
  },
  checkAccountEditRight(account) {
    if (this.asAdminModule) {
      return Tine.Tinebase.common.hasRight('manage_emailaccounts', 'Admin');
    } else {
      var _account$data;
      if (((_account$data = account.data) === null || _account$data === void 0 ? void 0 : _account$data.type) === 'shared') {
        var _account$data2, _account$data2$accoun;
        return (_account$data2 = account.data) === null || _account$data2 === void 0 ? void 0 : (_account$data2$accoun = _account$data2.account_grants) === null || _account$data2$accoun === void 0 ? void 0 : _account$data2$accoun.editGrant;
      }
      return true;
    }
  },
  isSystemAccount: function () {
    return this.record.get('type') === 'system' || this.record.get('type') === 'shared' || this.record.get('type') === 'userInternal' || this.record.get('type') === 'adblist';
  },
  isExternalUserAccount: function () {
    return this.record.get('type') === 'user';
  },
  /**
   * returns dialog
   *
   * NOTE: when this method gets called, all initialization is done.
   * @private
   */
  getFormItems: function () {
    const me = this;
    this.grantsGrid = new Tine.widgets.account.PickerGridPanel({
      selectType: 'both',
      title: this.app.i18n._('Permissions'),
      store: this.getGrantsStore(),
      hasAccountPrefix: true,
      configColumns: this.getGrantsColumns(),
      selectTypeDefault: 'group',
      height: 250,
      disabled: !this.asAdminModule,
      recordClass: Tine.Tinebase.Model.Grant,
      checkState: function () {
        const disabled = !(me.record.get('type') === 'shared' && me.asAdminModule);
        me.grantsGrid.setDisabled(disabled);
      }
    });
    this.rulesGridPanel = new Tine.Felamimail.sieve.RulesGridPanel({
      title: this.app.i18n._('Filter Rules'),
      account: this.record ? this.record : null,
      recordProxy: this.ruleRecordProxy,
      initialLoadAfterRender: false,
      disabled: !this.isSystemAccount()
    });
    this.vacationPanel = new Tine.Felamimail.sieve.VacationPanel({
      title: this.app.i18n._('Vacation'),
      account: this.record,
      editDialog: this,
      disabled: !this.isSystemAccount()
    });
    this.saveInAdbFields = [];
    this.emailImapUser = [];
    this.aliasesGrid = [];
    this.forwardsGrid = [];
    const additionConfig = {
      scope: this,
      record: this.record
    };
    if (this.asAdminModule) {
      var _this$record$data;
      this.saveInAdbFields = Tine.Admin.UserEditDialog.prototype.getSaveInAddessbookFields(this, this.record.get('type') === 'system');
      this.emailImapUser = ((_this$record$data = this.record.data) === null || _this$record$data === void 0 ? void 0 : _this$record$data.email_imap_user) || [];
      this.aliasesGrid = Tine.Admin.UserEditDialog.prototype.initAliasesGrid(additionConfig);
      this.forwardsGrid = Tine.Admin.UserEditDialog.prototype.initForwardsGrid(additionConfig);
    }
    this.sieveNotifyGrid = Tine.Felamimail.sieve.NotificationDialog.prototype.initSieveEmailNotifyGrid(additionConfig);
    var commonFormDefaults = {
      xtype: 'textfield',
      anchor: '100%',
      labelSeparator: '',
      maxLength: 256,
      columnWidth: 1
    };
    return {
      xtype: 'tabpanel',
      name: 'accountEditPanel',
      deferredRender: false,
      border: false,
      activeTab: 0,
      items: [{
        title: this.app.i18n._('Account'),
        autoScroll: true,
        border: false,
        frame: true,
        layout: 'border',
        items: [{
          region: 'north',
          xtype: 'columnform',
          formDefaults: commonFormDefaults,
          height: 400,
          items: [[{
            fieldLabel: this.app.i18n._('Account Name'),
            name: 'name',
            allowBlank: this.asAdminModule
          }], [new Tine.Tinebase.widgets.keyfield.ComboBox({
            app: 'Felamimail',
            keyFieldName: 'mailAccountType',
            fieldLabel: this.app.i18n._('Type'),
            name: 'type',
            hidden: !this.asAdminModule,
            listeners: {
              scope: this,
              select: this.onSelectType,
              blur: function () {
                this.disableFormFields();
              }
            }
          })], [{
            fieldLabel: this.app.i18n._('Migration Approved'),
            name: 'migration_approved',
            hidden: !this.asAdminModule,
            xtype: 'checkbox',
            listeners: {
              check: function (checkbox, checked) {
                if (!this.preventCheckboxEvents && checked) {
                  checkbox.setValue(0);
                  Ext.MessageBox.show({
                    title: this.app.i18n._('Approve Migration'),
                    msg: this.app.i18n._('Do you want to approve the migration of this account?'),
                    buttons: Ext.MessageBox.YESNO,
                    fn: btn => {
                      if (btn === 'yes') {
                        this.preventCheckboxEvents = true;
                        this.record.set('migration_approved', true);
                        checkbox.setValue(1);
                        this.preventCheckboxEvents = false;
                      }
                    },
                    icon: Ext.MessageBox.QUESTION
                  });
                }
              },
              scope: this
            },
            checkState: function () {
              const disabled = me.record.get('type') === 'shared' || me.record.get('type') === 'adblist';
              this.setDisabled(disabled);
            }
          }], [this.userAccountPicker = Tine.widgets.form.RecordPickerManager.get('Addressbook', 'Contact', {
            userOnly: true,
            fieldLabel: this.app.i18n._('User'),
            useAccountRecord: true,
            name: 'user_id',
            allowBlank: true
            // TODO user selection for system accounts should fill in the values!
          })], [{
            fieldLabel: this.app.i18n._('User Email'),
            name: 'email',
            allowBlank: this.asAdminModule,
            vtype: 'email'
          }], [{
            fieldLabel: this.app.i18n._('User Name (From)'),
            name: 'from'
          }], [{
            fieldLabel: this.app.i18n._('Organization'),
            name: 'organization'
          }], this.saveInAdbFields, [{
            fieldLabel: this.app.i18n._('Signature position'),
            name: 'signature_position',
            typeAhead: false,
            triggerAction: 'all',
            lazyRender: true,
            editable: false,
            mode: 'local',
            forceSelection: true,
            value: 'below',
            xtype: 'combo',
            store: [['above', this.app.i18n._('Above the quote')], ['below', this.app.i18n._('Below the quote')]]
          }]]
        }, new Tine.Felamimail.SignatureGridPanel({
          region: 'center',
          editDialog: this
        })]
      }, {
        title: this.app.i18n._('IMAP'),
        autoScroll: true,
        border: false,
        frame: true,
        xtype: 'columnform',
        formDefaults: commonFormDefaults,
        items: [[{
          fieldLabel: this.app.i18n._('Host'),
          name: 'host',
          allowBlank: this.asAdminModule,
          checkState: function () {
            this.setDisabled(me.isSystemAccount());
          }
        }], [{
          fieldLabel: this.app.i18n._('Port (Default: 143 / SSL: 993)'),
          name: 'port',
          allowBlank: this.asAdminModule,
          maxLength: 5,
          value: 143,
          xtype: 'numberfield',
          checkState: function () {
            this.setDisabled(me.isSystemAccount());
          }
        }], [{
          fieldLabel: this.app.i18n._('Secure Connection'),
          name: 'ssl',
          typeAhead: false,
          triggerAction: 'all',
          lazyRender: true,
          editable: false,
          mode: 'local',
          forceSelection: true,
          value: 'tls',
          xtype: 'combo',
          store: [['none', this.app.i18n._('None')], ['tls', this.app.i18n._('TLS')], ['ssl', this.app.i18n._('SSL')]],
          checkState: function () {
            this.setDisabled(me.isSystemAccount());
          }
        }], [{
          fieldLabel: this.app.i18n._('Username'),
          name: 'user',
          allowBlank: this.asAdminModule,
          checkState: function () {
            const disabled = !(!me.record.get('type') || me.record.get('type') === 'userInternal' || me.record.get('type') === 'user');
            this.setDisabled(disabled);
          }
        }], [{
          fieldLabel: this.app.i18n._('Password'),
          name: 'password',
          record: this.record,
          emptyText: 'password',
          xtype: 'tw-passwordTriggerField',
          clipboard: false,
          revealPasswordFn: async () => {
            return new Promise((fulfill, reject) => {
              if (this.record.id === 0) {
                return reject('none existing account');
              }
              Ext.Msg.confirm(this.app.i18n._('Reveal Password?'), this.app.i18n._('You are about to reveal the password. This action will be logged. Proceed?'), async button => {
                if (button === 'yes') {
                  const result = await Tine.Admin.revealEmailAccountPassword(this.record.id ?? '');
                  fulfill(result['password']);
                } else {
                  reject('canceled');
                }
              });
            });
          },
          listeners: {
            scope: this,
            keyup: field => {
              if ('' !== field.getValue()) {
                this.imapButton.setDisabled(false);
                this.smtpButton.setDisabled(false);
              }
            }
          }
        }], [this.imapButton = new Ext.Button({
          text: this.app.i18n._('Test IMAP Connection'),
          handler: () => {
            this.testConnection('IMAP', true, true);
          },
          disabled: true
        })], [{
          fieldLabel: 'Imap Quota',
          emptyText: 'no quota set',
          name: 'emailMailQuota',
          xtype: 'extuxbytesfield',
          disabled: true,
          hidden: !this.asAdminModule || !this.emailImapUser.hasOwnProperty('emailMailQuota')
        }], [{
          fieldLabel: 'Current Mailbox size',
          name: 'emailMailSize',
          xtype: 'extuxbytesfield',
          disabled: true,
          hidden: !this.asAdminModule || !this.emailImapUser.hasOwnProperty('emailMailSize')
        }], [{
          fieldLabel: 'Sieve Quota',
          emptyText: 'no quota set',
          name: 'emailSieveQuota',
          xtype: 'extuxbytesfield',
          disabled: true,
          hidden: !this.asAdminModule || !this.emailImapUser.hasOwnProperty('emailSieveQuota')
        }], [{
          fieldLabel: 'Current Sieve size',
          name: 'emailSieveSize',
          xtype: 'extuxbytesfield',
          disabled: true,
          hidden: !this.asAdminModule || !this.emailImapUser.hasOwnProperty('emailSieveSize')
        }]]
      }, {
        title: this.app.i18n._('SMTP'),
        autoScroll: true,
        border: false,
        frame: true,
        xtype: 'columnform',
        formDefaults: commonFormDefaults,
        items: [[{
          fieldLabel: this.app.i18n._('Host'),
          name: 'smtp_hostname',
          checkState: function () {
            this.setDisabled(me.isSystemAccount());
          }
        }], [{
          fieldLabel: this.app.i18n._('Port (Default: 25)'),
          name: 'smtp_port',
          maxLength: 5,
          xtype: 'numberfield',
          value: 25,
          allowBlank: this.asAdminModule,
          checkState: function () {
            this.setDisabled(me.isSystemAccount());
          }
        }], [{
          fieldLabel: this.app.i18n._('Secure Connection'),
          name: 'smtp_ssl',
          typeAhead: false,
          triggerAction: 'all',
          lazyRender: true,
          editable: false,
          mode: 'local',
          value: 'tls',
          xtype: 'combo',
          store: [['none', this.app.i18n._('None')], ['tls', this.app.i18n._('TLS')], ['ssl', this.app.i18n._('SSL')]],
          checkState: function () {
            this.setDisabled(me.isSystemAccount());
          }
        }], [{
          fieldLabel: this.app.i18n._('Authentication'),
          name: 'smtp_auth',
          typeAhead: false,
          triggerAction: 'all',
          lazyRender: true,
          editable: false,
          mode: 'local',
          xtype: 'combo',
          value: 'login',
          store: [['none', this.app.i18n._('None')], ['login', this.app.i18n._('Login')], ['plain', this.app.i18n._('Plain')]],
          checkState: function () {
            this.setDisabled(me.isSystemAccount());
          }
        }], [{
          fieldLabel: this.app.i18n._('Username (optional)'),
          name: 'smtp_user',
          checkState: function () {
            const disabled = me.isSystemAccount() || me.asAdminModule && me.record.get('type') === 'user';
            this.setDisabled(disabled);
          }
        }], [{
          fieldLabel: this.app.i18n._('Password (optional)'),
          name: 'smtp_password',
          emptyText: 'password',
          xtype: 'tw-passwordTriggerField',
          clipboard: false,
          listeners: {
            scope: this,
            keyup: field => {
              if ('' !== field.getValue()) {
                this.imapButton.setDisabled(false);
                this.smtpButton.setDisabled(false);
              }
            }
          },
          checkState: function () {
            const disabled = me.isSystemAccount() || me.asAdminModule && me.record.get('type') === 'user';
            this.setDisabled(disabled);
          }
        }], [this.smtpButton = new Ext.Button({
          text: this.app.i18n._('Test SMTP Connection'),
          height: 40,
          handler: () => {
            this.testConnection('SMTP', true, true);
          },
          disabled: true
        })], [this.aliasesGrid, this.forwardsGrid]]
      }, {
        title: this.app.i18n._('Sieve'),
        autoScroll: true,
        border: false,
        frame: true,
        xtype: 'columnform',
        formDefaults: commonFormDefaults,
        items: [[{
          fieldLabel: this.app.i18n._('Host'),
          name: 'sieve_hostname',
          maxLength: 64,
          checkState: function () {
            this.setDisabled(me.isSystemAccount());
          }
        }], [{
          fieldLabel: this.app.i18n._('Port (Default: 2000)'),
          name: 'sieve_port',
          maxLength: 5,
          value: 2000,
          xtype: 'numberfield',
          checkState: function () {
            this.setDisabled(me.isSystemAccount());
          }
        }], [{
          fieldLabel: this.app.i18n._('Secure Connection'),
          name: 'sieve_ssl',
          typeAhead: false,
          triggerAction: 'all',
          lazyRender: true,
          editable: false,
          mode: 'local',
          value: 'tls',
          xtype: 'combo',
          store: [['none', this.app.i18n._('None')], ['tls', this.app.i18n._('TLS')]],
          checkState: function () {
            this.setDisabled(me.isSystemAccount());
          }
        }], [this.sieveNotifyGrid], [{
          fieldLabel: this.app.i18n._('Auto-move notifications'),
          name: 'sieve_notification_move',
          xtype: 'widget-keyfieldcombo',
          keyFieldName: 'sieveNotificationMoveStatus',
          app: 'Felamimail',
          hidden: !Tine.Tinebase.appMgr.get('Felamimail').featureEnabled('accountMoveNotifications'),
          checkState: function () {
            this.setDisabled(!me.isSystemAccount());
          }
        }], [{
          fieldLabel: this.app.i18n._('Auto-move notifications folder'),
          name: 'sieve_notification_move_folder',
          maxLength: 255,
          hidden: !Tine.Tinebase.appMgr.get('Felamimail').featureEnabled('accountMoveNotifications'),
          checkState: function () {
            this.setDisabled(!me.isSystemAccount());
          }
        }], [new Ext.Button({
          text: this.app.i18n._('Explore Sieve script'),
          handler: async () => {
            await this.showSieveScriptWindow();
          },
          hidden: !this.asAdminModule
        }), new Ext.Button({
          text: this.app.i18n._('Edit Sieve custom script'),
          handler: async () => {
            await this.showEditSieveScriptWindow();
          },
          hidden: !this.checkAccountEditRight(this.record)
        })]]
      }, {
        title: this.app.i18n._('Other Settings'),
        autoScroll: true,
        border: false,
        frame: true,
        xtype: 'columnform',
        formDefaults: commonFormDefaults,
        items: [[{
          xtype: 'fieldset',
          title: this.app.i18n.gettext('Behavior of sent mails'),
          items: [[{
            hideLabel: true,
            xtype: 'checkbox',
            boxLabel: this.app.i18n._('Save copy of sent mail on mail server.'),
            name: 'save_sent_mail_copy',
            checked: me.record.get('message_sent_copy_behavior') !== 'skip',
            listeners: {
              'check': (checkbox, value) => {
                const mode = value ? 'sent' : 'skip';
                me.record.set('message_sent_copy_behavior', mode);
                this.getForm().findField('sent_folder').setDisabled(!value);
                this.getForm().findField('save_sent_mail_to_source').setDisabled(!value);
              }
            }
          }, {
            hideLabel: true,
            xtype: 'checkbox',
            name: 'save_sent_mail_to_source',
            boxLabel: this.app.i18n._('Folder of the source mail if it is no system folder.'),
            checked: me.record.get('message_sent_copy_behavior') === 'source',
            listeners: {
              'check': (checkbox, value) => {
                const mode = value ? 'source' : 'sent';
                me.record.set('message_sent_copy_behavior', mode);
              }
            }
          }]]
        }], [{
          fieldLabel: this.app.i18n._('Sent Folder Name'),
          name: 'sent_folder',
          xtype: 'felamimailfolderselect',
          account: this.record,
          maxLength: 64,
          value: 'Sent'
        }], [{
          fieldLabel: this.app.i18n._('Trash Folder Name'),
          name: 'trash_folder',
          xtype: 'felamimailfolderselect',
          account: this.record,
          maxLength: 64,
          value: 'Trash'
        }], [{
          fieldLabel: this.app.i18n._('Drafts Folder Name'),
          name: 'drafts_folder',
          xtype: 'felamimailfolderselect',
          account: this.record,
          maxLength: 64,
          value: 'Drafts'
        }], [{
          fieldLabel: this.app.i18n._('Templates Folder Name'),
          name: 'templates_folder',
          xtype: 'felamimailfolderselect',
          account: this.record,
          maxLength: 64,
          value: 'Templates'
        }], [{
          fieldLabel: this.app.i18n._('Display Format'),
          name: 'display_format',
          typeAhead: false,
          triggerAction: 'all',
          lazyRender: true,
          editable: false,
          mode: 'local',
          forceSelection: true,
          value: 'html',
          xtype: 'combo',
          store: [['html', this.app.i18n._('HTML')], ['plain', this.app.i18n._('Plain Text')],
          //TODO: content_type should not be experimental anymore
          ['content_type', this.app.i18n._('Depending on content type (experimental)')]]
        }], [{
          fieldLabel: this.app.i18n._('Compose Format'),
          name: 'compose_format',
          typeAhead: false,
          triggerAction: 'all',
          lazyRender: true,
          editable: false,
          mode: 'local',
          forceSelection: true,
          value: 'html',
          xtype: 'combo',
          store: [['html', this.app.i18n._('HTML')], ['plain', this.app.i18n._('Plain Text')]]
        }], [{
          fieldLabel: this.app.i18n._('Preserve Format'),
          name: 'preserve_format',
          typeAhead: false,
          triggerAction: 'all',
          lazyRender: true,
          editable: false,
          mode: 'local',
          forceSelection: true,
          value: '0',
          xtype: 'combo',
          store: [[0, this.app.i18n._('No')], [1, this.app.i18n._('Yes')]]
        }], [{
          fieldLabel: this.app.i18n._('Reply-To Email'),
          name: 'reply_to',
          vtype: 'email'
        }]]
      }, new Tine.widgets.activities.ActivitiesTabPanel({
        app: this.appName,
        record_id: this.record.id,
        record_model: this.appName + '_Model_' + this.recordClass.getMeta('modelName')
      }), this.rulesGridPanel, this.vacationPanel, this.grantsGrid]
    };
  },
  onSelectType: function (combo, record) {
    let newValue = combo.getValue();
    let currentValue = this.record.get('type');
    if (this.record.id) {
      if (!Tine.Tinebase.configManager.get('emailUserIdInXprops')) {
        this.onTypeChangeError(combo, this.app.i18n._('It is not possible to convert accounts because the config option EMAIL_USER_ID_IN_XPROPS is disabled.'));
      }
      if (newValue === 'shared' && ['system', 'userInternal'].indexOf(currentValue) !== false) {
        // check migration_approved
        if (!this.record.get('migration_approved')) {
          this.onTypeChangeError(combo, this.app.i18n._('Migration has not been approved.'));
          return false;
        }
        this.showPasswordDialog();
      } else if (newValue === 'userInternal' && ['shared'].indexOf(currentValue) !== false) {
        // this is valid
      } else {
        this.onTypeChangeError(combo, this.app.i18n._('It is not possible to convert the account to this type.'));
        return false;
      }
    } else if (newValue === 'system') {
      this.onTypeChangeError(combo, this.app.i18n._('System accounts cannot be created manually.'));
      return false;
    } else if (newValue === 'adblist') {
      this.onTypeChangeError(combo, this.app.i18n._('Mailinglist accounts cannot be created manually.'));
      return false;
    }

    // apply to record for disableFormFields()
    this.record.set('type', newValue);
    this.disableFormFields();
  },
  onTypeChangeError: function (combo, errorText) {
    combo.setValue(this.record.get('type'));
    Ext.MessageBox.alert(this.app.i18n._('Account type change not possible'), errorText);
  },
  /**
   * generic request exception handler
   *
   * @param {Object} exception
   */
  onRequestFailed: function (exception) {
    this.saving = false;
    this.hideLoadMask();

    // if code == 925 (Felamimail_Exception_PasswordMissing)
    // -> open new Tine.Tinebase.widgets.dialog.PasswordDialog to set imap password field
    if (exception.code === 925) {
      this.showPasswordDialog(true);
    } else {
      Tine.Felamimail.handleRequestException(exception);
    }
  },
  showPasswordDialog: function (apply) {
    var me = this,
      dialog = new Tine.Tinebase.widgets.dialog.PasswordDialog({
        windowTitle: this.app.i18n._('E-Mail account needs a password')
      });
    dialog.openWindow();

    // password entered
    dialog.on('apply', function (password) {
      me.getForm().findField('password').setValue(password);
      if (apply) {
        me.onApplyChanges(true);
      }
    });
  },
  /**
   * Show window for script reading
   */
  showSieveScriptWindow: async function () {
    const script = this.asAdminModule ? await Tine.Admin.getSieveScript(this.record.data.id) : await Tine.Felamimail.getSieveScript(this.record.data.id);
    const windowTitle = this.app.i18n._('Explore Sieve script');
    const dialog = new Tine.Tinebase.dialog.Dialog({
      items: [{
        cls: 'x-ux-display-background-border',
        xtype: 'ux.displaytextarea',
        type: 'code/folding/mixed',
        height: 300,
        value: script,
        listeners: {
          render: async cmp => {
            // wait ace editor
            await (0,util_waitFor_es6__WEBPACK_IMPORTED_MODULE_0__/* ["default"] */ .Z)(() => {
              return cmp.el.child('.ace_content');
            });
            cmp.el.setStyle({
              'overflow': null
            });
          }
        }
      }],
      initComponent: function () {
        this.fbar = ['->', {
          text: i18n._('Ok'),
          minWidth: 70,
          ref: '../buttonApply',
          scope: this,
          handler: this.onButtonApply,
          iconCls: 'action_saveAndClose'
        }];
        Tine.Tinebase.dialog.Dialog.superclass.initComponent.call(this);
      },
      openWindow: function (config) {
        if (this.window) {
          return this.window;
        }
        config = config || {};
        this.window = Tine.WindowFactory.getWindow(Ext.apply({
          resizable: false,
          title: windowTitle,
          closeAction: 'close',
          modal: true,
          width: 550,
          height: 400,
          items: [this],
          fbar: ['->']
        }, config));
        return this.window;
      }
    });
    dialog.openWindow();
  },
  /**
   * Show custom sieve script window
   */
  showEditSieveScriptWindow: async function () {
    const accountId = this.record.data.id;
    const asAdminModule = this.asAdminModule;
    const result = asAdminModule ? await Tine.Admin.getSieveCustomScript(accountId) : await Tine.Felamimail.getSieveCustomScript(accountId);
    const windowTitle = this.app.i18n._('Edit Sieve custom script');
    const script = result === null || result === void 0 ? void 0 : result.script;
    const dialog = new Tine.Tinebase.dialog.Dialog({
      items: [{
        xtype: 'tw-acefield',
        mode: 'sieve',
        fieldLabel: 'custom script',
        id: 'sieve_custom_script',
        allowBlank: true,
        height: 200,
        value: script
      }],
      initComponent: function () {
        this.fbar = ['->', {
          text: i18n._('Cancel'),
          minWidth: 70,
          ref: '../buttonApply',
          scope: this,
          handler: async () => {
            this.onButtonApply();
          },
          iconCls: 'action_cancel'
        }, {
          text: i18n._('Ok'),
          minWidth: 70,
          ref: '../buttonApply',
          scope: this,
          handler: async () => {
            try {
              const script = dialog.getForm().findField('sieve_custom_script').getValue();
              const sieveCustomScript = asAdminModule ? await Tine.Admin.saveSieveCustomScript(accountId, script) : await Tine.Felamimail.saveSieveCustomScript(accountId, script);
              this.onButtonApply();
            } catch (e) {
              Ext.MessageBox.alert(i18n._('Errors'), e.message);
            }
          },
          iconCls: 'action_saveAndClose'
        }];
        Tine.Tinebase.dialog.Dialog.superclass.initComponent.call(this);
      },
      openWindow: function (config) {
        if (this.window) return this.window;
        config = config || {};
        this.window = Tine.WindowFactory.getWindow(Ext.apply({
          resizable: false,
          title: windowTitle,
          closeAction: 'close',
          modal: true,
          width: 550,
          height: 400,
          items: [this],
          fbar: ['->']
        }, config));
        return this.window;
      }
    });
    dialog.openWindow();
  },
  getGrantsColumns: function () {
    return [new Ext.ux.grid.CheckColumn({
      header: this.app.i18n._('Use'),
      dataIndex: 'readGrant',
      tooltip: this.app.i18n._('The grant use the shared email account'),
      width: 60
    }), new Ext.ux.grid.CheckColumn({
      header: this.app.i18n._('Edit'),
      tooltip: this.app.i18n._('The grant edit the shared email account'),
      dataIndex: 'editGrant',
      width: 60
    }), new Ext.ux.grid.CheckColumn({
      header: this.app.i18n._('Send Mails'),
      tooltip: this.app.i18n._('The grant to send mails via the email account'),
      dataIndex: 'addGrant',
      width: 60
    })];
  },
  /**
   * get grants store
   *
   * @return Ext.data.JsonStore
   */
  getGrantsStore: function () {
    if (!this.grantsStore) {
      this.grantsStore = new Ext.data.JsonStore({
        root: 'results',
        totalProperty: 'totalcount',
        // use account_id here because that simplifies the adding of new records with the search comboboxes
        id: 'account_id',
        fields: Tine.Tinebase.Model.Grant
      });
    }
    return this.grantsStore;
  },
  /**
   * generic apply changes handler
   */
  onApplyChanges: async function (closeWindow) {
    let result = true;
    let errorMessage = '';
    const password = this.getForm().findField('password').getValue();

    //only test connection for external user account
    if (this.isExternalUserAccount() && '' !== password) {
      await _.reduce(['IMAP', 'SMTP'], (pre, server) => {
        return pre.then(async () => {
          await this.testConnection(server, false).catch(e => {
            result = false;
            errorMessage = errorMessage + '</b><br>' + e.message;
          });
        });
      }, Promise.resolve());
    }
    if (!result) {
      Ext.MessageBox.show({
        title: this.app.i18n._('Warning'),
        msg: this.app.i18n._('Server connection failed. Do you still want to save this setting and exit?') + '<br>' + errorMessage,
        buttons: Ext.MessageBox.YESNO,
        fn: btn => {
          if (btn === 'yes') {
            Tine.Felamimail.AccountEditDialog.superclass.onApplyChanges.call(this, closeWindow);
          }
        },
        icon: Ext.MessageBox.WARNING
      });
    } else {
      if (this.isSystemAccount()) {
        await this.updateSieve();
      }
      Tine.Felamimail.AccountEditDialog.superclass.onApplyChanges.call(this, closeWindow);
    }
  },
  /**
   * test IMAP or SMTP connection
   *
   */
  testConnection: async function (server) {
    let showDialog = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
    let forceConnect = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    let message = this.app.i18n._('Connection succeed');
    let fields = {
      'SMTP': {
        'smtp_hostname': '',
        'smtp_port': '',
        'smtp_ssl': '',
        'smtp_auth': '',
        'smtp_user': '',
        'smtp_password': '',
        'user': '',
        //use Imap username if not set
        'password': '' //use Imap password if not set
      },

      'IMAP': {
        'host': '',
        'port': '',
        'ssl': '',
        'user': '',
        'password': ''
      }
    };
    _.each(fields[server], (val, key) => {
      val = this.getForm().findField(key).getValue();
      fields[server][key] = val ? val : '';
    }, this);
    try {
      //show load mask
      if (!this['connectLoadMask' + server]) {
        this['connectLoadMask' + server] = new Ext.LoadMask(this.getEl(), {
          msg: String.format(this.app.i18n._('Connecting to {0} server...'), server)
        });
      }
      this['connectLoadMask' + server].show();

      //test connection
      await Tine.Felamimail[server === 'SMTP' ? 'testSmtpSettings' : 'testIMapSettings'](this.record.id, fields[server], forceConnect);
    } catch (e) {
      message = this.app.i18n._hidden(_.get(e, 'message', this.app.i18n._('Connection failed')));
      return Promise.reject(new Error(message));
    } finally {
      this['connectLoadMask' + server].hide();
      if (showDialog) {
        Ext.MessageBox.show({
          title: this.app.formatMessage('{server} Connection Status', {
            server: server
          }),
          msg: message,
          buttons: Ext.Msg.OK,
          icon: Ext.MessageBox.INFO
        });
      }
    }
    return message;
  },
  /**
   * is form valid?
   *
   * @return {Boolean}
   */
  isValid: function () {
    var result = Tine.Felamimail.AccountEditDialog.superclass.isValid.call(this);
    var from = this.getForm().findField('from').getValue();
    if (String(from).includes(',')) {
      this.getForm().markInvalid([{
        id: 'from',
        msg: this.app.i18n._("User Name (From) cannot contain a comma.")
      }]);
      return false;
    }
    return result;
  },
  /**
   * load vacation record
   *
   */
  loadSieve: function () {
    var _this$record$data2, _this$record$data3, _this$record$data4;
    if (!this.isSystemAccount()) {
      return;
    }
    let me = this;
    me.vacationRecord = Tine.Tinebase.data.Record.setFromJson((_this$record$data2 = this.record.data) === null || _this$record$data2 === void 0 ? void 0 : _this$record$data2.sieve_vacation, Tine.Felamimail.Model.Vacation);

    // mime type is always multipart/alternative
    me.vacationRecord.set('mime', 'multipart/alternative');
    if (me.record && me.record.get('signature')) {
      me.vacationRecord.set('signature', me.record.get('signature'));
    }
    me.getForm().loadRecord(me.vacationRecord);
    me.ruleRecords = ((_this$record$data3 = this.record.data) === null || _this$record$data3 === void 0 ? void 0 : _this$record$data3.sieve_rules) === '' ? [] : (_this$record$data4 = this.record.data) === null || _this$record$data4 === void 0 ? void 0 : _this$record$data4.sieve_rules;
    Ext.each(me.ruleRecords, function (item) {
      const record = Tine.Tinebase.data.Record.setFromJson(item, Tine.Felamimail.Model.Rule);
      me.rulesGridPanel.store.addSorted(record);
    });
    me.getForm().loadRecord(me.ruleRecords);

    // load sieve notification emails
    const data = this.record.data.sieve_notification_email.split(',');
    this.sieveNotifyGrid.setStoreFromArray(data.map(e => {
      return {
        'email': e
      };
    }));
  },
  /**
   * load email quotas
   *
   */
  loadEmailQuotas: function () {
    if (this.asAdminModule) {
      var _this$record$data5;
      const emailImapUser = ((_this$record$data5 = this.record.data) === null || _this$record$data5 === void 0 ? void 0 : _this$record$data5.email_imap_user) || [];
      if (emailImapUser.hasOwnProperty('emailMailQuota')) {
        this.getForm().findField('emailMailQuota').setValue(emailImapUser.emailMailQuota);
        this.getForm().findField('emailMailSize').setValue(emailImapUser.emailMailSize);
      }
      if (emailImapUser.hasOwnProperty('emailSieveQuota')) {
        this.getForm().findField('emailSieveQuota').setValue(emailImapUser.emailSieveQuota);
        this.getForm().findField('emailSieveSize').setValue(emailImapUser.emailSieveSize);
      }
    }
  },
  loadEmailAliasesAndForwards: function () {
    var _this$record$data6;
    if (Tine.Tinebase.registry.get('manageSmtpEmailUser') && (_this$record$data6 = this.record.data) !== null && _this$record$data6 !== void 0 && _this$record$data6.email_smtp_user) {
      var _this$record$data7, _this$record$data7$em, _this$record$data8, _this$record$data8$em;
      if ((_this$record$data7 = this.record.data) !== null && _this$record$data7 !== void 0 && (_this$record$data7$em = _this$record$data7.email_smtp_user) !== null && _this$record$data7$em !== void 0 && _this$record$data7$em.emailAliases) {
        this.aliasesGrid.setStoreFromArray(this.record.data.email_smtp_user.emailAliases);
      }
      if ((_this$record$data8 = this.record.data) !== null && _this$record$data8 !== void 0 && (_this$record$data8$em = _this$record$data8.email_smtp_user) !== null && _this$record$data8$em !== void 0 && _this$record$data8$em.emailForwards) {
        this.forwardsGrid.setStoreFromArray(this.record.data.email_smtp_user.emailForwards);
      }
    }
  },
  updateEmailAliasesAndForwards: function () {
    // get aliases / forwards
    if (this.asAdminModule && Tine.Tinebase.registry.get('manageSmtpEmailUser')) {
      var _this$record$data9, _this$record$data9$em, _this$record$data10, _this$record$data10$e;
      // forcing blur of quickadd grids
      this.aliasesGrid.doBlur();
      this.forwardsGrid.doBlur();
      if ((_this$record$data9 = this.record.data) !== null && _this$record$data9 !== void 0 && (_this$record$data9$em = _this$record$data9.email_smtp_user) !== null && _this$record$data9$em !== void 0 && _this$record$data9$em.emailAliases) {
        this.record.data.email_smtp_user.emailAliases = this.aliasesGrid.getFromStoreAsArray();
      }
      if ((_this$record$data10 = this.record.data) !== null && _this$record$data10 !== void 0 && (_this$record$data10$e = _this$record$data10.email_smtp_user) !== null && _this$record$data10$e !== void 0 && _this$record$data10$e.emailForwards) {
        this.record.data.email_smtp_user.emailForwards = this.forwardsGrid.getFromStoreAsArray();
      }
      Tine.log.debug('Tine.Felamimail.AccountEditDialog::onRecordUpdate() -> setting aliases and forwards in e-mail record');
      Tine.log.debug(this.record);
    }
  },
  /**
   * update vacation record
   *
   */
  updateSieve: async function () {
    if (this.record.id && this.vacationRecord && this.vacationPanel) {
      let form = this.getForm();
      const contactIds = [];
      form.updateRecord(this.vacationRecord);
      Ext.each(['contact_id1', 'contact_id2'], function (field) {
        if (form.findField(field) && form.findField(field).getValue() !== '') {
          contactIds.push(form.findField(field).getValue());
        }
      }, this);
      let template = form.findField('template_id') ? form.findField('template_id').getValue() : '';
      this.vacationRecord.set('contact_ids', contactIds);
      this.vacationRecord.set('template_id', template);
      if (template !== '') {
        try {
          const response = await Tine.Felamimail.getVacationMessage(this.vacationRecord.data);
          this.vacationPanel.reasonEditor.setValue(response === null || response === void 0 ? void 0 : response.message);
        } catch (e) {
          Tine.Felamimail.handleRequestException(e);
        }
      }
    }
    let rules = [];
    this.rulesGridPanel.store.each(function (record) {
      rules.push(record.data);
    });
    this.record.set('sieve_rules', rules);
    this.record.set('sieve_vacation', this.vacationRecord);

    // update sieve notification emails
    const notifyEmails = this.sieveNotifyGrid.getFromStoreAsArray();
    this.record.set('sieve_notification_email', notifyEmails.filter(e => e === null || e === void 0 ? void 0 : e.email).map(e => e === null || e === void 0 ? void 0 : e.email).join());
  },
  /**
   * update email quotas
   *
   */
  updateEmailQuotas: function () {
    if (this.asAdminModule) {
      if (!this.emailImapUser || !this.record.data.email_imap_user) {
        return;
      }
      if (this.emailImapUser.hasOwnProperty('emailMailQuota')) {
        this.record.data.email_imap_user.emailMailQuota = this.getForm().findField('emailMailQuota').getValue();
      }
      if (this.emailImapUser.hasOwnProperty('emailSieveQuota')) {
        this.record.data.email_imap_user.emailSieveQuota = this.getForm().findField('emailSieveQuota').getValue();
      }
    }
  },
  /**
   * update email_account type contact addressbook
   *
   */
  updateContactAddressbook: function () {
    var _this$record$data11;
    if (this.record.get('type') === 'system') {
      return;
    }
    const container = this.getForm().findField('container_id');
    if (((_this$record$data11 = this.record.data) === null || _this$record$data11 === void 0 ? void 0 : _this$record$data11.visibility) === 'displayed' && container) {
      var _this$record$data12, _this$record$data12$c;
      if (!((_this$record$data12 = this.record.data) !== null && _this$record$data12 !== void 0 && (_this$record$data12$c = _this$record$data12.contact_id) !== null && _this$record$data12$c !== void 0 && _this$record$data12$c.container_id)) {
        this.record.data.contact_id = {
          'container_id': container.getValue()
        };
      } else {
        this.record.data.contact_id.container_id = container.getValue();
      }
    }
  },
  /**
   * load deafault addressbook from contact
   *
   */
  loadDefaultAddressbook: function () {
    var _this$record$data13, _this$record$data13$c;
    const item = this.getForm().findField('container_id');
    if (this.record.get('type') === 'system' || !item) {
      return;
    }
    const id = ((_this$record$data13 = this.record.data) === null || _this$record$data13 === void 0 ? void 0 : (_this$record$data13$c = _this$record$data13.contact_id) === null || _this$record$data13$c === void 0 ? void 0 : _this$record$data13$c.container_id) ?? Tine.Admin.registry.get('defaultInternalAddressbook');
    item.setValue(id);
  }
});

/**
 * Felamimail Account Edit Popup
 *
 * @param   {Object} config
 * @return  {Ext.ux.Window}
 */
Tine.Felamimail.AccountEditDialog.openWindow = function (config) {
  var window = Tine.WindowFactory.getWindow({
    width: 580,
    height: 600,
    name: Tine.Felamimail.AccountEditDialog.prototype.windowNamePrefix + Ext.id(),
    contentPanelConstructor: 'Tine.Felamimail.AccountEditDialog',
    contentPanelConstructorConfig: config
  });
  return window;
};

/***/ }),

/***/ 67792:
/***/ (() => {

/*
 * Tine 2.0
 *
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Michael Spahn <m.spahn@metaways.de>
 * @copyright   Copyright (c) 2017 Metaways Infosystems GmbH (http://www.metaways.de)
 */

Ext.ns('Tine.Felamimail');

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.AttachmentUploadGrid
 * @extends     Ext.grid.GridPanel
 *
 * @author      Michael Spahn <m.spahn@metaways.de>
 * @copyright   Copyright (c) 2017 Metaways Infosystems GmbH (http://www.metaways.de)
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 */
Tine.Felamimail.AttachmentUploadGrid = Ext.extend(Tine.widgets.grid.FileUploadGrid, {
  /**
   * Store with all valid attachment types
   */
  attachmentTypeStore: null,
  app: null,
  clicksToEdit: 1,
  currentRecord: null,
  initComponent: function () {
    this.app = this.app || Tine.Tinebase.appMgr.get('Felamimail');
    this.attachmentTypeStore = new Ext.data.JsonStore({
      fields: ['id', 'name'],
      data: this.getAttachmentMethods()
    });
    Tine.Felamimail.AttachmentUploadGrid.superclass.initComponent.call(this);
    this.on('beforeedit', this.onBeforeEdit.createDelegate(this));
    this.store.on('add', this.onStoreAddRecords, this);
    if (this.action_rename) {
      _.set(this, 'action_rename.initialConfig.actionUpdater', this.renameActionUpdater);
    }
  },
  onStoreAddRecords: function (store, rs, idx) {
    _.each(rs, r => {
      _.set(r, 'data.attachment_type', 'attachment');
    });
  },
  renameActionUpdater: function (action, grants, records, isFilterSelect, filteredContainers) {
    const isTempfile = !!_.get(records, '[0].data.tempFile');
    const enabled = !!isTempfile;
    action.setDisabled(!enabled);
    action.baseAction.setDisabled(!enabled);
  },
  onBeforeEdit: function (e) {
    var record = e.record;
    this.currentRecord = record;
  },
  getAttachmentMethods: function () {
    var methods = [{
      id: 'attachment',
      name: this.app.i18n._('Attachment')
    }];
    if (!Tine.Tinebase.appMgr.isEnabled('Filemanager')) {
      return methods;
    }
    if (!Tine.Tinebase.appMgr.get('Felamimail').featureEnabled('onlyPwDownloadLink')) {
      methods = methods.concat([{
        id: 'download_public_fm',
        name: this.app.i18n._('Filemanager (Download link)')
      }]);
    }
    methods = methods.concat([{
      id: 'download_protected_fm',
      name: this.app.i18n._('Filemanager (Download link, password)')
    }, {
      id: 'systemlink_fm',
      name: this.app.i18n._('Filemanager (Systemlink)')
    }]);
    return methods;
  },
  /**
   * Override columns
   */
  getColumns: function () {
    var me = this;
    var combo = new Ext.form.ComboBox({
      blurOnSelect: true,
      expandOnFocus: true,
      listWidth: 250,
      minListWidth: 250,
      mode: 'local',
      value: 'attachment',
      displayField: 'name',
      valueField: 'id',
      store: me.attachmentTypeStore,
      disableKeyFilter: true,
      queryMode: 'local'
    });
    combo.doQuery = function (q, forceAll, uploadGrid) {
      this.store.clearFilter();
      this.store.filterBy(function (record, id) {
        const isFilemanagerNode = !_.has(uploadGrid.currentRecord, 'data.input'); // check if upload or fm node

        // attach fm nodes only with download grant
        if (isFilemanagerNode && !_.get(uploadGrid.currentRecord, 'data.account_grants.downloadGrant', true) && id === 'attachment') {
          return false;
        }

        // only fm files can be system links
        if (!isFilemanagerNode && id === 'systemlink_fm') {
          return false;
        }

        // if no grants, then its not from fm
        if (!isFilemanagerNode && id.startsWith('download_')) {
          return false;
        }
        return true;
      }.createDelegate(this, [uploadGrid.currentRecord], true));
      this.onLoad();
    }.createDelegate(combo, [this], true);
    return [{
      id: 'attachment_type',
      dataIndex: 'attachment_type',
      sortable: true,
      width: 250,
      header: this.app.i18n._('Attachment Type'),
      tooltip: this.app.i18n._('Click icon to change'),
      listeners: {},
      value: 'attachment',
      renderer: function (value) {
        if (!value) {
          return null;
        }
        var record = me.attachmentTypeStore.getById(value);
        if (!record) {
          return null;
        }
        return Tine.Tinebase.common.cellEditorHintRenderer(record.get('name'));
      },
      editor: combo
    }, {
      resizable: true,
      id: 'name',
      dataIndex: 'name',
      flex: 1,
      header: i18n._('name'),
      renderer: Ext.ux.PercentRendererWithName
    }, {
      resizable: true,
      id: 'size',
      dataIndex: 'size',
      width: 70,
      header: i18n._('size'),
      renderer: Ext.util.Format.fileSize
    }, {
      resizable: true,
      id: 'type',
      dataIndex: 'type',
      width: 70,
      header: i18n._('type')
    }];
  }
});

/***/ }),

/***/ 14516:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.namespace('Tine.Felamimail');

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.ComposeEditor
 * @extends     Ext.form.HtmlEditor
 * 
 * <p>Compose HTML Editor</p>
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Felamimail.ComposeEditor
 */
Tine.Felamimail.ComposeEditor = Ext.extend(Ext.form.HtmlEditor, {
  cls: 'felamimail-message-body-html',
  name: 'body_html',
  allowBlank: true,
  defaultFont: Tine.Felamimail.registry.get('preferences').get('defaultfont') || 'tahoma',
  getDocMarkup: function () {
    var markup = '<html>' + '<head>' + '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">' + '<title></title>' + '<style type="text/css">'
    // standard css reset
    + "html,body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,p,blockquote,th,td{margin:0;padding:0;}img,body,html{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}ol,ul {list-style:none;}caption,th {text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;}q:before,q:after{content:'';}"
    // small forms
    + "html,body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,p,blockquote,th,td{font-size: small;}"
    // lists
    + "ul {list-style:circle outside; margin-left: 20px;}" + "ol {list-style:decimal outside; margin-left: 20px;}" + "strong {font-weight: bold; font-style: inherit;}" + "em {font-style: italic; font-weight: inherit;}"
    // fmail special
    + '.felamimail-body-blockquote {' + 'margin: 5px 10px 0 3px;' + 'padding-left: 10px;' + 'border-left: 2px solid #000088;' + '} ' + '.dark-mode img {' + '   filter: invert(1) hue-rotate(180deg);' + '} ' + '</style>' + '</head>' + '<body style="padding: 5px 0px 0px 5px; margin: 0px" class="' + String(Ext.getBody().dom.classList.value).trim() + '">' + '</body></html>';
    return markup;
  },
  /**
   * @private
   */
  initComponent: function () {
    this.plugins = [new Ext.ux.form.HtmlEditor.IndentOutdent(), new Ext.ux.form.HtmlEditor.RemoveFormat(), new Ext.ux.form.HtmlEditor.EndBlockquote(), new Ext.ux.form.HtmlEditor.SpecialKeys(), new Ext.ux.form.HtmlEditor.SelectImage(), new Ext.ux.file.BrowsePlugin({
      multiple: true,
      scope: this,
      handler: this.onFileSelect,
      enableFileDialog: false
    }), new Ext.ux.form.HtmlEditor.PlainText()];
    Tine.Felamimail.ComposeEditor.superclass.initComponent.call(this);
  },
  // *Fix* Overridding the onRender method, in order to
  // unset the height and width property, so that the
  // layout manager won't consider this field to be of
  // fixed dimension, thus ignoring the flex option
  onRender: function () {
    Tine.Felamimail.ComposeEditor.superclass.onRender.apply(this, arguments);
    delete this.height;
    delete this.width;
  },
  async onFileSelect(fileSelector, e) {
    const files = await fileSelector.getFileList();
    await Ext.ux.form.HtmlEditor.MidasCommand.prototype.onImageSelected(files[0], this);
  }
});
Ext.namespace('Ext.ux.form.HtmlEditor');

/**
 * @class Ext.ux.form.HtmlEditor.EndBlockquote
 * @extends Ext.util.Observable
 * 
 * plugin for htmleditor that ends blockquotes on ENTER
 * tested with chrome, sarari, FF4+
 * fallsback for old (non IE) browser which works for easy structures
 * does not work with IE yet
 * 
 * TODO move this to ux dir
 */
Ext.ux.form.HtmlEditor.EndBlockquote = Ext.extend(Ext.util.Observable, {
  // private
  init: function (cmp) {
    this.cmp = cmp;
    this.cmp.on('initialize', this.onInit, this);
  },
  // private
  onInit: function () {
    if (Ext.isFunction(this.cmp.win.getSelection().modify)) {
      Ext.EventManager.on(this.cmp.getDoc(), {
        'keyup': this.endBlockquoteHTML5,
        scope: this
      });
    } else {
      Ext.EventManager.on(this.cmp.getDoc(), {
        'keydown': this.endBlockquoteHTML4,
        scope: this
      });
    }
  },
  /**
   * on keyup 
   * 
   * @param {Event} e
   */
  endBlockquoteIE: function (e) {
    if (e.getKey() == e.ENTER) {
      e.stopEvent();
      e.preventDefault();
      var s = this.cmp.win.document.selection,
        r = s.createRange(),
        doc = this.cmp.getDoc(),
        anchor = r.parentElement(),
        level = this.getBlockquoteLevel(anchor),
        scrollTop = doc.body.scrollTop;
      if (level > 0) {
        r.moveStart('word', -1);
        r.moveEnd('textedit');
        var fragment = r.htmlText;
        r.execCommand('Delete');
        var newText = doc.createElement('p'),
          newTextMark = '###newTextHere###' + Ext.id(),
          fragmentMark = '###fragmentHere###' + Ext.id();
        newText.innerHTML = newTextMark + fragmentMark;
        doc.body.appendChild(newText);
        r.expand('textedit');
        r.findText(fragmentMark);
        r.select();
        r.pasteHTML(fragment);
        r.expand('textedit');
        r.findText(newTextMark);
        r.select();
        r.execCommand('Delete');

        // reset scroller
        doc.body.scrollTop = scrollTop;
        this.cmp.syncValue();
        this.cmp.deferFocus();
      }
    }
    return;
  },
  /**
   * on keyup 
   * 
   * @param {Event} e
   */
  endBlockquoteHTML5: function (e) {
    // Chrome, Safari, FF4+
    if (e.getKey() == e.ENTER) {
      var s = this.cmp.win.getSelection(),
        r = s.getRangeAt(0),
        doc = this.cmp.getDoc(),
        level = this.getBlockquoteLevel(s.anchorNode),
        scrollTop = doc.body.scrollTop;
      if (level > 0) {
        // cut from cursor to end of the document
        if (s.anchorNode.nodeName == '#text') {
          r.setStartBefore(s.anchorNode.parentNode);
        }
        s.modify("move", "backward", "character");
        r.setEndAfter(doc.body.lastChild);
        var fragmet = r.extractContents();

        // insert paragraph for new text and move cursor in
        // NOTE: we need at least one character in the newText to move cursor in
        var newText = doc.createElement('p');
        newText.innerHTML = '&nbsp;';
        doc.body.appendChild(newText);
        s.modify("move", "forward", "character");

        // paste rest of document 
        doc.body.appendChild(fragmet);

        // reset scroller
        doc.body.scrollTop = scrollTop;
      }
    }
  },
  /**
   * on keydown 
   * 
   * @param {Event} e
   */
  endBlockquoteHTML4: function (e) {
    if (e.getKey() == e.ENTER) {
      var s = this.cmp.win.getSelection(),
        r = s.getRangeAt(0),
        doc = this.cmp.getDoc(),
        level = this.getBlockquoteLevel(s.anchorNode);
      if (level > 0) {
        e.stopEvent();
        e.preventDefault();
        this.cmp.win.focus();
        if (level == 1) {
          this.cmp.insertAtCursor('<br /><blockquote class="felamimail-body-blockquote"><br />');
          this.cmp.execCmd('outdent');
          this.cmp.execCmd('outdent');
        } else if (level > 1) {
          for (var i = 0; i < level; i++) {
            this.cmp.insertAtCursor('<br /><blockquote class="felamimail-body-blockquote">');
            this.cmp.execCmd('outdent');
            this.cmp.execCmd('outdent');
          }
          var br = doc.createElement('br');
          r.insertNode(br);
        }
        this.cmp.deferFocus();
      }
    }
  },
  /**
   * get blockquote level helper
   * 
   * @param {DOMNode} node
   * @return {Integer}
   */
  getBlockquoteLevel: function (node) {
    var result = 0;
    while (node.nodeName == '#text' || node.tagName.toLowerCase() != 'body') {
      if (node.tagName && node.tagName.toLowerCase() == 'blockquote') {
        result++;
      }
      node = node.parentNode ? node.parentNode : node.parentElement;
    }
    return result;
  }
});

/**
 * @class Ext.ux.form.HtmlEditor.SpecialKeys
 * @extends Ext.util.Observable
 * 
 * plugin for htmleditor that fires events for special keys (like CTRL-ENTER and SHIFT-TAB)
 * 
 * TODO move this to ux dir
 */
Ext.ux.form.HtmlEditor.SpecialKeys = Ext.extend(Ext.util.Observable, {
  // private
  init: function (cmp) {
    this.cmp = cmp;
    this.cmp.on('initialize', this.onInit, this);
  },
  // private
  onInit: function () {
    Ext.EventManager.on(this.cmp.getDoc(), {
      'keydown': this.onKeydown,
      scope: this
    });
  },
  /**
   * on keydown 
   * 
   * @param {Event} e
   * 
   * TODO try to prevent TAB key event from inserting a TAB in the editor 
   */
  onKeydown: function (e) {
    this.cmp.fireEvent('keydown', this.cmp, e);
  }
});

/***/ }),

/***/ 86286:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.ns('Tine.Felamimail');

/**
 * Contact grid panel
 * 
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.ContactGridPanel
 * @extends     Tine.Addressbook.ContactGridPanel
 * 
 * <p>Contact Grid Panel</p>
 * <p>
 * </p>
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Felamimail.ContactGridPanel
 */
Tine.Felamimail.ContactGridPanel = Ext.extend(Tine.Addressbook.ContactGridPanel, {
  hasDetailsPanel: false,
  hasFavoritesPanel: false,
  hasQuickSearchFilterToolbarPlugin: false,
  stateId: 'FelamimailContactGrid',
  gridConfig: {
    autoExpandColumn: 'n_fileas',
    enableDragDrop: false
  },
  /**
   * the message record with recipients
   * @type Tine.Felamimail.Model.Message
   */
  messageRecord: null,
  /**
   * felamimail app, needed for translations
   * @type Tine.Felamimail.Application
   */
  felamimailApp: null,
  /**
   * inits this cmp
   * @private
   */
  initComponent: function () {
    this.addEvents(
    /**
     * @event addcontacts
     * Fired when contacts are added
     */
    'addcontacts');
    this.app = Tine.Tinebase.appMgr.get('Addressbook');
    this.felamimailApp = Tine.Tinebase.appMgr.get('Felamimail');
    Tine.Felamimail.ContactGridPanel.superclass.initComponent.call(this);
    this.grid.disableResponsiveLayout = true;
    this.grid.on('rowdblclick', this.onRowDblClick, this);
    this.grid.on('cellclick', this.onCellClick, this);
    this.store.on('load', this.onContactStoreLoad, this);
  },
  /**
   * initialises the filter panel 
   * 
   * @param {Object} config
   */
  initFilterPanel: function (config) {
    this.defaultFilters = [{
      field: 'email_query',
      operator: 'contains',
      value: '@'
    }];
    Tine.Felamimail.ContactGridPanel.superclass.initFilterPanel.call(this, {
      useQuickFilter: false
    });
  },
  /**
   * returns array with columns
   * 
   * @return {Array}
   */
  getColumns: function () {
    var columns = Tine.Felamimail.ContactGridPanel.superclass.getColumns.call(this);

    // hide all columns except name/company/email/email_home (?)
    Ext.each(columns, function (column) {
      if (['n_fileas', 'org_name', 'email'].indexOf(column.dataIndex) === -1) {
        column.hidden = true;
      }
    });
    this.radioTpl = new Ext.XTemplate('<input', ' name="' + this.id + '_{id}"', ' value="{type}"', ' type="radio"', ' autocomplete="off"', ' class="x-form-radio x-form-field"', ' {checked}', '>');
    Ext.each(['To', 'Cc', 'Bcc', 'None'], function (type) {
      // i18n._('None')
      columns.push({
        header: this.felamimailApp.i18n._(type),
        dataIndex: Ext.util.Format.lowercase(type),
        width: 50,
        hidden: false,
        renderer: this.typeRadioRenderer.createDelegate(this, [type], 0)
      });
    }, this);
    return columns;
  },
  /**
   * render type radio buttons in grid
   * 
   * @param {String} type
   * @param {String} value
   * @param {Object} metaData
   * @param {Object} record
   * @param {Number} rowIndex
   * @param {Number} colIndex
   * @param {Store} store
   * @return {String}
   */
  typeRadioRenderer: function (type, value, metaData, record, rowIndex, colIndex, store) {
    if (!record.hasEmail()) {
      return '';
    }
    var lowerType = Ext.util.Format.lowercase(type);
    return this.radioTpl.apply({
      id: record.id,
      type: lowerType,
      checked: lowerType === 'none' ? 'checked' : ''
    });
  },
  /**
   * called after a new set of Records has been loaded
   *
   * @param  {Ext.data.Store} this.store
   * @return {Void}
   * @param store
   * @param records
   * @param options
   */
  onContactStoreLoad: function (store, records, options) {
    if (Ext.isObject(this.messageRecord)) {
      _.each(records, contact => {
        const token = Tine.Felamimail.GridPanelHook.prototype.getRecipientTokenFromContact(contact);
        if (token) {
          ['to', 'cc', 'bcc'].forEach(type => {
            if (Ext.isArray(this.messageRecord.data[type]) && _.find(this.messageRecord.data[type], {
              email: token.email
            })) {
              this.setTypeRadio(contact, type);
            }
          });
        }
      });
    }
  },
  /**
   * cell click handler -> update recipients in record
   * 
   * @param {Grid} grid
   * @param {Number} row
   * @param {Number} col
   * @param {Event} e
   */
  onCellClick: function (grid, row, col, e) {
    const contact = this.store.getAt(row);
    const typeToSet = this.grid.getColumnModel().getDataIndex(col);
    if (['to', 'cc', 'bcc', 'none'].indexOf(typeToSet) === -1) {
      // some other column has been clicked
      return;
    }
    if (!contact.hasEmail() && typeToSet !== 'none') {
      this.setTypeRadio(contact, 'none');
    } else {
      this.setTypeRadio(contact, typeToSet);
      this.updateRecipients(contact, typeToSet);
    }
  },
  /**
   * update recipient
   * 
   * @param {Tine.Addressbook.Model.Contact} contact
   * @param {String} typeToSet
   */
  updateRecipients: function (contact, typeToSet) {
    let found = false;
    const token = Tine.Felamimail.GridPanelHook.prototype.getRecipientTokenFromContact(contact);
    if (!token) {
      return;
    }
    _.each(['to', 'cc', 'bcc'], async type => {
      const existToken = _.find(this.messageRecord.data[type], {
        email: token.email
      });
      if (Ext.isArray(this.messageRecord.data[type]) && existToken) {
        if (type !== typeToSet) {
          this.messageRecord.data[type].remove(existToken);
        } else {
          found = true;
        }
      }
    });
    if (!found && typeToSet !== 'none') {
      Tine.log.debug('Tine.Felamimail.ContactGridPanel::updateRecipients() - adding email ' + token.email + ' to ' + typeToSet);
      this.messageRecord.data[typeToSet].push(token);
    }
  },
  /**
   * update type radio buttons dom
   * 
   * @param {Array} records of type Tine.Addressbook.Model.Contact
   * @param {String} type
   */
  setTypeRadio: function (records, type) {
    const rs = [].concat(records);
    Ext.each(rs, function (r) {
      if (r.hasEmail() || type === 'none') {
        Ext.select('input[name=' + this.id + '_' + r.id + ']', this.grid.el).each(function (el) {
          el.dom.checked = type === el.dom.value;
        });
        this.updateRecipients(r, type);
      }
    }, this);
  },
  /**
   * Return CSS class to apply to rows depending upon email set or not
   * 
   * @param {Tine.Addressbook.Model.Contact} record
   * @param {Integer} index
   * @return {String}
   */
  getViewRowClass: function (record, index) {
    var className = '';
    if (!record.hasEmail()) {
      className = 'felamimail-no-email';
    }
    return className;
  },
  /**
   * @private
   */
  initActions: function () {
    this.actions_addAsTo = new Ext.Action({
      requiredGrant: 'readGrant',
      text: this.felamimailApp.i18n._('Add as "To"'),
      disabled: true,
      iconCls: 'action_add',
      actionUpdater: this.updateRecipientActions,
      handler: this.onAddContact.createDelegate(this, ['to']),
      allowMultiple: true,
      scope: this
    });
    this.actions_addAsCc = new Ext.Action({
      requiredGrant: 'readGrant',
      text: this.felamimailApp.i18n._('Add as "Cc"'),
      disabled: true,
      iconCls: 'action_add',
      actionUpdater: this.updateRecipientActions,
      handler: this.onAddContact.createDelegate(this, ['cc']),
      allowMultiple: true,
      scope: this
    });
    this.actions_addAsBcc = new Ext.Action({
      requiredGrant: 'readGrant',
      text: this.felamimailApp.i18n._('Add as "Bcc"'),
      disabled: true,
      iconCls: 'action_add',
      actionUpdater: this.updateRecipientActions,
      handler: this.onAddContact.createDelegate(this, ['bcc']),
      allowMultiple: true,
      scope: this
    });
    this.actions_setToNone = new Ext.Action({
      requiredGrant: 'readGrant',
      text: this.felamimailApp.i18n._('Remove from recipients'),
      disabled: true,
      iconCls: 'action_delete',
      actionUpdater: this.updateRecipientActions,
      handler: this.onAddContact.createDelegate(this, ['none']),
      allowMultiple: true,
      scope: this
    });

    //register actions in updater
    this.actionUpdater.addActions([this.actions_addAsTo, this.actions_addAsCc, this.actions_addAsBcc, this.actions_setToNone]);
  },
  /**
   * updates context menu
   * 
   * @param {Ext.Action} action
   * @param {Object} grants grants sum of grants
   * @param {Object} records
   */
  updateRecipientActions: function (action, grants, records) {
    if (records.length > 0) {
      var emptyEmails = true;
      for (var i = 0; i < records.length; i++) {
        if (records[i].hasEmail()) {
          emptyEmails = false;
          break;
        }
      }
      action.setDisabled(emptyEmails);
    } else {
      action.setDisabled(true);
    }
  },
  /**
   * on add contact -> fires addcontacts event and passes rows + type
   * 
   * @param {String} type
   */
  onAddContact: function (type) {
    var sm = this.grid.getSelectionModel(),
      selectedRows = sm.getSelections();
    this.setTypeRadio(selectedRows, type);

    // search contacts if all pages are selected (filter select)
    if (sm.isFilterSelect) {
      this['AddressLoadMask'] = new Ext.LoadMask(Ext.getBody(), {
        msg: this.felamimailApp.i18n._('Loading Mail Addresses')
      });
      this['AddressLoadMask'].show();
      var contact = null;
      Tine.Addressbook.searchContacts(sm.getSelectionFilter(), null, function (response) {
        Ext.each(response.results, function (contactData) {
          contact = new Tine.Addressbook.Model.Contact(contactData);
          this.updateRecipients(contact, type);
        }, this);
        this['AddressLoadMask'].hide();
      }.createDelegate(this));
    }
  },
  /**
   * row doubleclick handler
   * 
   * @param {} grid
   * @param {} row
   * @param {} e
   */
  onRowDblClick: function (grid, row, e) {
    this.onAddContact('to');
  },
  /**
   * returns rows context menu
   * 
   * @return {Ext.menu.Menu}
   */
  getContextMenu: function () {
    if (!this.contextMenu) {
      var items = [this.actions_addAsTo, this.actions_addAsCc, this.actions_addAsBcc, this.actions_setToNone];
      this.contextMenu = new Ext.menu.Menu({
        items: items,
        plugins: [{
          ptype: 'ux.itemregistry',
          key: 'Tinebase-MainContextMenu'
        }]
      });
    }
    return this.contextMenu;
  }
});
Ext.reg('felamimailcontactgrid', Tine.Felamimail.ContactGridPanel);

/***/ }),

/***/ 69170:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2009-2018 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.namespace('Tine.Felamimail');

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.ContactSearchCombo
 * @extends     Tine.Addressbook.SearchCombo
 * 
 * <p>Email Search ComboBox</p>
 * <p></p>
 * <pre></pre>
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Felamimail.ContactSearchCombo
 */
Tine.Felamimail.ContactSearchCombo = Ext.extend(Tine.Addressbook.SearchCombo, {
  /**
   * @cfg {Boolean} forceSelection
   */
  forceSelection: false,
  /**
   * @private
   */
  valueIsList: false,
  /**
   * no path filter for emails!
   */
  addPathFilter: false,
  recordEditPluginConfig: false,
  additionalFilterSpec: {},
  /**
   * @private
   */
  initComponent: function () {
    // Search Lists and Contacts
    this.recordClass = Tine.Addressbook.Model.EmailAddress;
    this.recordProxy = Tine.Addressbook.emailAddressBackend;
    this.tpl = new Ext.XTemplate('<tpl for="."><div class="search-item">', '{[this.getIcon(values)]}', '<span style="padding-left: 5px;">', '{[this.encode(values)]}', '</span>', '</div></tpl>', {
      encode: this.renderEmailAddress.createDelegate(this),
      getIcon: this.renderAddressIconCls.createDelegate(this)
    });
    Tine.Felamimail.ContactSearchCombo.superclass.initComponent.call(this);
    this.store.on('load', this.onStoreLoad, this);
  },
  /**
   * use beforequery to set query filter
   *
   * @param {Event} qevent
   */
  onBeforeQuery: function (qevent) {
    Tine.Felamimail.ContactSearchCombo.superclass.onBeforeQuery.apply(this, arguments);
    const filter = this.store.baseParams.filter;
    const queryFilter = _.find(filter, {
      field: 'query'
    });
    _.remove(filter, queryFilter);
    filter.push({
      field: 'name_email_query',
      operator: 'contains',
      value: queryFilter.value
    });
  },
  doQuery: function (q, forceAll) {
    // always load store otherwise the recipients will not be updated
    this.store.load({
      params: this.getParams(q)
    });
    Tine.Felamimail.ContactSearchCombo.superclass.doQuery.apply(this, arguments);
  },
  /**
   * override default onSelect
   * - set email/name as value
   * 
   * @param {} record
   * @private
   */
  onSelect: function (record, index) {
    this.selectedRecord = record;
    if (this.selectedRecord) {
      this.setRawValue('');
    }
    this.value = this.getValue();
    this.collapse();
    this.fireEvent('select', this, record, index);
  },
  /**
   * always return raw value
   * 
   * @return String
   */
  getValue: function () {
    return this.getRawValue();
  },
  /**
   * always set valueIsList to false
   *
   * @param value
   */
  setValue: function (value) {
    Tine.Felamimail.ContactSearchCombo.superclass.setValue.call(this, value);
  },
  /**
   * on load handler of combo store
   * -> add additional record if contact has multiple email addresses
   * 
   * @param {} store
   * @param {} records
   * @param {} options
   */
  onStoreLoad: function (store, records, options) {
    this.removeInvalidRecords(store);
  },
  renderAddressIconCls: function (token) {
    const i18n = Tine.Tinebase.appMgr.get('Addressbook').i18n;
    let data = {
      tip: 'E-Mail',
      iconClass: 'EmailAccount'
    };
    switch (token === null || token === void 0 ? void 0 : token.type) {
      case 'user':
        data = {
          tip: 'Contact of a user account',
          iconClass: 'Account'
        };
        break;
      case 'mailingListMember':
        data = {
          tip: 'Mailing List Member',
          iconClass: 'Account'
        };
        break;
      case 'responsible':
        data = {
          tip: 'E-Mail',
          iconClass: 'Contact'
        };
        break;
      case 'mailingList':
        data = {
          tip: 'Mailing List',
          iconClass: 'MailingList'
        };
        break;
      case 'email_account':
        data = {
          tip: 'E-Mail',
          iconClass: 'EmailAccount'
        };
        break;
      case 'email_home':
        data = {
          tip: 'Email (private)',
          iconClass: 'Private'
        };
        break;
      case 'group':
        data = {
          tip: 'System Group',
          iconClass: 'Group'
        };
        break;
      case 'list':
        data = {
          tip: 'Group',
          iconClass: 'List'
        };
        break;
      case 'groupMember':
      case 'listMember':
        data = {
          tip: 'Group Member',
          iconClass: 'GroupMember'
        };
        break;
      case 'contact':
        data = {
          tip: 'Contact',
          iconClass: 'Contact'
        };
        break;
      default:
        if ((token === null || token === void 0 ? void 0 : token.contact_record) !== '') data = {
          tip: 'Contact',
          iconClass: 'Contact'
        };
        break;
    }
    if ((token === null || token === void 0 ? void 0 : token.email_type) === 'email_home') data = {
      tip: 'Email (private)',
      iconClass: 'Private'
    };
    return '<div class="tine-combo-icon renderer AddressbookIconCls renderer_type' + data.iconClass + 'Icon" ext:qtip="' + Ext.util.Format.htmlEncode(i18n._(data.tip)) + '"/></div>';
  },
  renderEmailAddress(token) {
    const renderEmail = token.email !== '' && token.name !== '' ? ` < ${token.email} >` : token.email;
    const note = token !== null && token !== void 0 && token.note && token.note !== '' ? ` ( ${token.note} )` : '';
    return Ext.util.Format.htmlEncode(token.name) + '<b>' + Ext.util.Format.htmlEncode(renderEmail) + '</b>' + Ext.util.Format.htmlEncode(note);
  },
  removeInvalidRecords(store) {
    store.each(record => {
      const idx = store.indexOf(record);
      if (!this.validate(record)) {
        store.removeAt(idx);
        return;
      }
      const duplicates = store.queryBy(contact => {
        return record.id !== contact.id && Tine.Felamimail.getEmailStringFromContact(record) === Tine.Felamimail.getEmailStringFromContact(contact);
      });
      if (duplicates.getCount() > 0) {
        Tine.log.debug(`remove duplicate email from ${record.data.type}: ${record.data.email}`);
        store.removeAt(idx);
      }
    });
  },
  validate(record) {
    return true;
  }
});
Ext.reg('felamimailcontactcombo', Tine.Felamimail.ContactSearchCombo);

/***/ }),

/***/ 62980:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var util_waitFor_es6__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(50696);
/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2009-2013 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.ns('Tine.Felamimail');

__webpack_require__(42398);
__webpack_require__(3096);
__webpack_require__(50728);
__webpack_require__(79886);
__webpack_require__(51496);

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.Application
 * @extends     Tine.Tinebase.Application
 * 
 * <p>Felamimail application obj</p>
 * <p>
 * </p>
 * 
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * 
 * @param       {Object} config
 * 
 * @constructor
 * Create a new  Tine.Felamimail.Application
 */
Tine.Felamimail.Application = Ext.extend(Tine.Tinebase.Application, {
  /**
   * auto hook text i18n._('New Mail')
   */
  addButtonText: 'New Mail',
  /**
   * @property checkMailsDelayedTask
   * @type Ext.util.DelayedTask
   */
  checkMailsDelayedTask: null,
  /**
   * @property defaultAccount
   * @type Tine.Felamimail.Model.Account
   */
  defaultAccount: null,
  /**
   * @type Ext.data.JsonStore
   */
  folderStore: null,
  /**
   * @type Ext.data.JsonStore
   */
  accountStore: null,
  /**
   * @property updateInterval user defined update interval (milliseconds)
   * @type Number
   */
  updateInterval: null,
  /**
   * transaction id of current update message cache request
   * @type Number
   */
  updateMessageCacheTransactionId: null,
  getFolderStatusTransactionInProgress: false,
  /**
   * unreadcount in default account inbox
   * @type Number
   */
  unreadcountInDefaultInbox: 0,
  routes: {
    'MailTo/:params': 'mailto'
  },
  /**
   * returns title (Email)
   * 
   * @return {String}
   */
  getTitle: function () {
    return this.i18n._('Email');
  },
  /**
   * start delayed task to init folder store / updateFolderStore
   */
  init: function () {
    Tine.log.info('initialising app');
    this.checkMailsDelayedTask = new Ext.util.DelayedTask(this.checkMails, this);
    this.updateInterval = parseInt(Tine.Felamimail.registry.get('preferences').get('updateInterval')) * 60000;
    Tine.log.debug('user defined update interval is "' + this.updateInterval / 1000 + '" seconds');
    this.defaultAccount = Tine.Felamimail.registry.get('preferences').get('defaultEmailAccount');
    Tine.log.debug('default account is "' + this.defaultAccount);
    Tine.Tinebase.appMgr.isInitialised('Felamimail').then(_.bind(async function () {
      this.initAccountModel();
      this.initGridPanelHooks();
      await this.initAccountStore();
      if (window.isMainWindow) {
        if (Tine.Tinebase.appMgr.getActive() != this && this.updateInterval) {
          var delayTime = this.updateInterval / 20;
          Tine.log.debug('start preloading mails in "' + delayTime / 1000 + '" seconds');
          this.checkMailsDelayedTask.delay(delayTime);
        }
        _.delay(_.bind(this.fillAttachmentCache, this), 15000);
        this.showActiveVacation();
        this.registerProtocolHandler();
        this.registerQuickLookPanel();
      }
    }, this));
  },
  // regularly fill attachmentCache
  fillAttachmentCache: async function () {
    const nowTs = new Date().getTime();
    const lastRunTs = Ext.state.Manager.get('fmail-acache-lr', 0);
    const seconds = lastRunTs ? Math.floor((nowTs - lastRunTs) / 1000) : 24 * 3600;
    if (!this.fillAttachmentCacheRequest) {
      try {
        const accountIds = _.map(_.get(this.getAccountStore(), 'data.items'), 'data.id');
        this.fillAttachmentCacheRequest = Tine.Felamimail.fillAttachmentCache(accountIds, seconds);
        await this.fillAttachmentCacheRequest;
        this.fillAttachmentCacheRequest = null;
        Ext.state.Manager.set('fmail-acache-lr', nowTs);
      } catch (e) {}
    }

    // run every 5 minutes
    return _.delay(_.bind(this.fillAttachmentCache, this), 300000);
  },
  /**
   * show notification with active vacation information
   */
  showActiveVacation: function () {
    var accountsWithActiveVacation = this.getAccountStore().query('sieve_vacation_active', true);
    accountsWithActiveVacation.each(function (item) {
      Ext.ux.Notification.show(this.i18n._('Active Vacation Message'), String.format(this.i18n._('Email account "{0}" has an active vacation message.'), item.get('name')));
    }, this);
  },
  /**
   * TODO can this be done in a more elegant way?
   */
  initAccountModel: function () {
    /**
     * @type Object
     */
    Tine.Felamimail.Model.Account.prototype.lastIMAPException = null;

    /**
     * get the last IMAP exception
     *
     * @return {Object}
     */
    Tine.Felamimail.Model.Account.prototype.getLastIMAPException = function () {
      return this.lastIMAPException;
    };

    /**
     * returns sendfolder id
     */
    Tine.Felamimail.Model.Account.prototype.getSendFolderId = function () {
      return this.getSpecialFolderId('sent_folder');
    };

    /**
     * returns trashfolder id
     */
    Tine.Felamimail.Model.Account.prototype.getTrashFolderId = function () {
      return this.getSpecialFolderId('trash_folder');
    };

    /**
     * returns draftsfolder id
     */
    Tine.Felamimail.Model.Account.prototype.getDraftFolderId = function () {
      return this.getSpecialFolderId('drafts_folder');
    };

    /**
     * returns special folder id
     *
     * @param {String} nameProperty
     */
    Tine.Felamimail.Model.Account.prototype.getSpecialFolderId = function (nameProperty) {
      var _mainWindow$Tine;
      const mainWindow = Ext.ux.PopupWindowMgr.getMainWindow();
      if (!mainWindow) return null;
      const appmgr = (_mainWindow$Tine = mainWindow.Tine) === null || _mainWindow$Tine === void 0 ? void 0 : _mainWindow$Tine.Tinebase.appMgr;
      if (!appmgr) return null;
      const app = appmgr.get('Felamimail');
      if (!app) return null;
      const folderName = this.get(nameProperty);
      const accountId = this.id;
      const folder = folderName ? app.getFolderStore().queryBy(function (record) {
        return record.get('account_id') === accountId && record.get('globalname') === folderName;
      }, this).first() : null;
      return folder ? folder.id : null;
    };

    /**
     * set or clear IMAP exception and update imap_state
     *
     * @param {Object} exception
     */
    Tine.Felamimail.Model.Account.prototype.setLastIMAPException = function (exception) {
      this.lastIMAPException = exception;
      this.set('imap_status', exception ? 'failure' : 'success');
      this.commit();
    };
  },
  /**
   * initialize grid panel hooks
   */
  initGridPanelHooks: function () {
    var adbHook = new Tine.Felamimail.GridPanelHook({
      app: this,
      foreignAppName: 'Addressbook',
      modelName: 'Contact'
    });
    var adbHook = new Tine.Felamimail.GridPanelHook({
      app: this,
      foreignAppName: 'Addressbook',
      modelName: 'List'
    });
    var crmHook = new Tine.Felamimail.GridPanelHook({
      app: this,
      foreignAppName: 'Crm',
      contactInRelation: true,
      relationType: 'CUSTOMER',
      modelName: 'Lead',
      subjectField: 'lead_name'
    });
    var calHook = new Tine.Felamimail.GridPanelHook({
      app: this,
      foreignAppName: 'Calendar',
      modelName: 'Event',
      subjectFn: function (record) {
        var _ = window.lodash;
        return _.get(record, 'data.poll.name', _.get(record, 'data.summary', ''), '');
      },
      bodyFn: function (record) {
        if (record && record.hasPoll()) {
          return String.format(this.app.i18n._('Poll URL: {0}'), record.getPollUrl());
        } else {
          return '';
        }
      },
      massMailingFlagFn: function (record) {
        return record && record.hasPoll();
      },
      addMailFromRecord: async function (mailAddresses, record) {
        const recipients = [];
        Ext.each(record.get('attendee'), function (attender) {
          Tine.log.debug('Tine.Felamimail.Application::initGridPanelHooks/addMailFromRecord() - Calendar attender:');
          Tine.log.debug(attender);
          if (attender.user_type === 'user' || attender.user_type === 'groupmember') {
            attender.user_id.type = 'user';
            recipients.push(attender.user_id);
          }
        }, this);
        await this.addRecipientTokenFromContacts(mailAddresses, recipients);
      }
    });
  },
  registerProtocolHandler: function () {
    var text = String.format(this.i18n._('{0} as default mailer'), Tine.title),
      enabled = true; //Tine.Tinebase.configManager.get('registerMailToHandler', 'Felamimail');
    Tine.Felamimail.registerProtocolHandlerAction.setText(text);
    if (!(enabled && Ext.isFunction(navigator.registerProtocolHandler))) {
      Tine.Felamimail.registerProtocolHandlerAction.setHidden(true);
    }
  },
  /**
   * initialize Filemanager email QuickLook
   *
   * @returns {boolean}
   */
  registerQuickLookPanel: function () {
    if (!Tine.Tinebase.common.hasRight('run', 'Filemanager')) {
      // needs Filemanager
      return false;
    }
    Tine.Filemanager.QuickLookRegistry.registerContentType('message/rfc822', 'felamimaildetailspanel');
    Tine.Filemanager.QuickLookRegistry.registerExtension('eml', 'felamimaildetailspanel');
    Tine.Filemanager.QuickLookRegistry.registerExtension('msg', 'felamimaildetailspanel');
    return true;
  },
  /**
   * check mails
   * 
   * if no folder is given, we find next folder to update ourself
   * 
   * @param {Tine.Felamimail.Model.Folder} [folder]
   * @param {Function} [callback]
   */
  checkMails: function (folder, callback) {
    this.checkMailsDelayedTask.cancel();
    if (!this.getFolderStore().getCount() && this.defaultAccount) {
      this.fetchSubfolders('/' + this.defaultAccount);
      return;
    }
    Tine.log.info('checking mails' + (folder ? ' for folder ' + folder.get('localname') : '') + ' now: ' + new Date());
    if (!this.getMainScreen()) {
      return;
    }
    // if no folder is given, see if there is a folder to check in the folderstore
    if (!folder) {
      folder = this.getNextFolderToUpdate();
    }

    // for testing purposes
    //console.log('update disabled');
    //return;

    if (folder) {
      var executionTime = folder.isCurrentSelection() ? 10 : Math.min(this.updateInterval / 1000, 120);
      if (this.updateMessageCacheTransactionId && Tine.Felamimail.folderBackend.isLoading(this.updateMessageCacheTransactionId)) {
        var currentRequestFolder = this.folderStore.query('cache_status', 'pending').first(),
          expectedResponseIn = Math.floor((this.updateMessageCacheTransactionExpectedResponse.getTime() - new Date().getTime()) / 1000);
        if (currentRequestFolder && (currentRequestFolder !== folder || expectedResponseIn > executionTime)) {
          Tine.log.debug('aborting current update message request (expected response in ' + expectedResponseIn + ' seconds)');
          Tine.Felamimail.folderBackend.abort(this.updateMessageCacheTransactionId);
          currentRequestFolder.set('cache_status', 'incomplete');
          currentRequestFolder.commit();
        } else {
          Tine.log.debug('a request updating message cache for folder "' + folder.get('localname') + '" is in progress -> wait for request to return');
          return;
        }
      }
      Tine.log.debug('Updating message cache for folder "' + folder.get('localname') + '" of account ' + folder.get('account_id'));
      Tine.log.debug('Max execution time: ' + executionTime + ' seconds');
      this.updateMessageCacheTransactionExpectedResponse = new Date().add(Date.SECOND, executionTime);
      folder.set('cache_status', 'pending');
      folder.commit();
      this.updateMessageCacheTransactionId = Tine.Felamimail.folderBackend.updateMessageCache(folder.id, executionTime, {
        scope: this,
        callback: callback,
        failure: this.onBackgroundRequestFail,
        success: function (folder) {
          var account = this.getAccountStore().getById(folder.get('account_id'));
          if (account) {
            account.setLastIMAPException(null);
          }
          this.getFolderStore().updateFolder(folder);
          if (folder.get('cache_status') === 'updating') {
            Tine.log.debug('updating message cache for folder "' + folder.get('localname') + '" is in progress on the server (folder is locked)');
            return this.checkMailsDelayedTask.delay(this.updateInterval);
          }
          this.checkMailsDelayedTask.delay(0);
        }
      });
    } else {
      var allFoldersFetched = this.fetchSubfolders();
      if (allFoldersFetched) {
        Tine.log.info('nothing more to do -> will check mails again in "' + this.updateInterval / 1000 + '" seconds');
        if (this.updateInterval > 0) {
          this.checkMailsDelayedTask.delay(this.updateInterval);
        }
      } else {
        this.checkMailsDelayedTask.delay(20000);
      }
    }
  },
  /**
   * fetch subfolders by parent path 
   * - if parentPath param is empty, it loops all accounts and account folders to find the next folders to fetch
   * 
   * @param {String} parentPath
   * @return {Boolean} true if all folders of all accounts have been fetched
   */
  fetchSubfolders: function (parentPath) {
    var folderStore = this.getFolderStore(),
      accountStore = this.getAccountStore(),
      doQuery = true,
      allFoldersFetched = false;
    if (!parentPath) {
      // find first account that has unfetched folders
      var index = accountStore.findExact('all_folders_fetched', false),
        account = accountStore.getAt(index);
      if (account) {
        // determine the next level of folders that is not fetched
        parentPath = '/' + account.id;
        var recordsOfAccount = folderStore.query('account_id', account.id);
        if (recordsOfAccount.getCount() > 0) {
          // loop account folders and find the next folder path that hasn't been queried and has children
          var path,
            found = false;
          recordsOfAccount.each(function (record) {
            path = parentPath + '/' + record.id;
            if (!folderStore.isLoadedOrLoading('parent_path', path) && (!account.get('has_children_support') || record.get('has_children'))) {
              parentPath = path;
              found = true;
              Tine.log.debug('fetching next level of subfolders for ' + record.get('globalname'));
              return false;
            }
            return true;
          }, this);
          if (!found) {
            Tine.log.debug('all folders of account ' + account.get('name') + ' have been fetched ...');
            account.set('all_folders_fetched', true);
            account.commit();
            return false;
          }
        } else {
          Tine.log.debug('fetching first level of folders for account ' + account.get('name'));
        }
      } else {
        Tine.log.debug('all folders of all accounts have been fetched ...');
        return true;
      }
    } else {
      Tine.log.debug('no folders in store yet, fetching first level ...');
    }
    if (!folderStore.queriesPending || folderStore.queriesPending.length == 0) {
      folderStore.asyncQuery('parent_path', parentPath, subfolder => {
        if (subfolder.getCount()) {
          this.checkMails();
        }
      }, [], this, folderStore);
    } else {
      this.checkMailsDelayedTask.delay(0);
    }
    return false;
  },
  /**
   * get folder store
   * 
   * @return {Tine.Felamimail.FolderStore}
   */
  getFolderStore: function () {
    if (!this.folderStore) {
      Tine.log.debug('creating folder store');
      this.folderStore = new Tine.Felamimail.FolderStore({
        listeners: {
          scope: this,
          update: this.onUpdateFolder
        }
      });
    }
    return this.folderStore;
  },
  /**
   * gets next folder which needs to be checked for mails
   * 
   * @return {Model.Folder/null}
   */
  getNextFolderToUpdate: function () {
    var currNode = this.getMainScreen().getTreePanel().getSelectionModel().getSelectedNode(),
      currFolder = currNode ? this.getFolderStore().getById(currNode.id) : null;

    // current selection has highest prio!
    if (currFolder && currFolder.needsUpdate(this.updateInterval)) {
      return currFolder;
    }

    // check if inboxes need updates
    var inboxes = this.folderStore.queryBy(function (folder) {
      return folder.isInbox() && folder.needsUpdate(this.updateInterval);
    }, this);
    if (inboxes.getCount() > 0) {
      return inboxes.first();
    }

    // check for incompletes
    var incompletes = this.folderStore.queryBy(function (folder) {
      return ['complete', 'updating', 'disconnect'].indexOf(folder.get('cache_status')) === -1 && folder.get('is_selectable');
    }, this);
    if (incompletes.getCount() > 0) {
      Tine.log.debug('Got ' + incompletes.getCount() + ' incomplete folders.');
      var firstIncomplete = incompletes.first();
      Tine.log.debug('First ' + firstIncomplete.get('cache_status') + ' folder to check: ' + firstIncomplete.get('globalname'));
      return firstIncomplete;
    }

    // check for outdated
    if (!this.getFolderStatusTransactionInProgress) {
      this.getStatusOfOutdatedFolders();
    } else {
      Tine.log.debug('getFolderStatus() already running ... wait a little more.');
    }

    // nothing to update
    return null;
  },
  /**
   * collects outdated folders and calls getFolderStatus on server to fetch all folders that need to be updated
   */
  getStatusOfOutdatedFolders: function () {
    var outdated = this.folderStore.queryBy(function (folder) {
      if (!folder.get('is_selectable')) {
        return false;
      }
      var timestamp = folder.get('client_access_time');
      if (!Ext.isDate(timestamp)) {
        return true;
      }
      // update inboxes more often than other folders
      if (folder.isInbox() && timestamp.getElapsed() > this.updateInterval) {
        return true;
      } else if (timestamp.getElapsed() > this.updateInterval * 5) {
        return true;
      }
      return false;
    }, this);
    if (outdated.getCount() > 0) {
      Tine.log.debug('Still got ' + outdated.getCount() + ' outdated folders to update');

      // call Felamimail.getFolderStatus() with ids of outdated folders -> update folder store on success
      // get only max 50 folders at once
      var rangeOfFolders = outdated.getCount() > 50 ? outdated.getRange(0, 49) : outdated.getRange(),
        ids = [],
        now = new Date();
      Ext.each(rangeOfFolders, function (folder) {
        folder.set('client_access_time', now);
        ids.push(folder.id);
      });
      var filter = [{
        field: 'id',
        operator: 'in',
        value: ids
      }];
      Tine.log.debug('Requesting folder status of ' + rangeOfFolders.length + ' folders ...');
      Tine.Felamimail.getFolderStatus(filter, this.onGetFolderStatusSuccess.createDelegate(this));
      this.getFolderStatusTransactionInProgress = true;
    }
  },
  /**
   * get folder status returned -> set folders that need an update to pending status
   * 
   * @param {Array} response
   */
  onGetFolderStatusSuccess: function (response) {
    this.getFolderStatusTransactionInProgress = false;
    Tine.log.debug('Tine.Felamimail.Application::onGetFolderStatusSuccess() -> Folder status update successful.');
    Tine.log.debug(response);
    if (response && response.length > 0) {
      Tine.log.debug('Tine.Felamimail.Application::onGetFolderStatusSuccess() -> Got ' + response.length + ' folders that need an update.');
      Ext.each(response, function (folder) {
        var folderToUpdate = this.folderStore.getById(folder.id);
        folderToUpdate.set('cache_status', 'pending');
      }, this);
      this.checkMailsDelayedTask.delay(1000);
    } else {
      Tine.log.debug('Tine.Felamimail.Application::onGetFolderStatusSuccess() -> No folders for update found.');
    }
  },
  /**
   * executed when updateMessageCache requests fail
   * 
   * NOTE: We show the credential error dlg and this only for the first error
   * 
   * @param {Object} exception
   */
  onBackgroundRequestFail: function (exception) {
    var currentRequestFolder = this.folderStore.query('cache_status', 'pending').first();
    var accountId = currentRequestFolder.get('account_id'),
      account = accountId ? this.getAccountStore().getById(accountId) : null,
      imapStatus = account ? account.get('imap_status') : null,
      grid = this.getMainScreen().getCenterPanel(),
      manualRefresh = grid && grid.manualRefresh;
    if (manualRefresh) {
      grid.manualRefresh = false;
      grid.pagingToolbar.refresh.enable();
    }
    if (exception.code == 913) {
      // folder not found -> remove folder from store and tree panel
      var treePanel = this.getMainScreen().getTreePanel(),
        node = treePanel.getNodeById(currentRequestFolder.id);
      if (node) {
        node.remove();
      }
      this.getFolderStore().remove(currentRequestFolder);
    } else if (account && (manualRefresh ||
    //  do not show exclamation mark for timeouts and connection losses
    exception.code !== 520 && exception.code !== 510)) {
      account.setLastIMAPException(exception);
      this.getFolderStore().each(function (folder) {
        if (folder.get('account_id') === accountId) {
          folder.set('cache_status', 'disconnect');
          folder.commit();
        }
      }, this);
      if (exception.code == 912 && imapStatus !== 'failure' && Tine.Tinebase.appMgr.getActive() === this) {
        Tine.Felamimail.folderBackend.handleRequestException(exception);
      }
    }
    Tine.log.info((manualRefresh ? 'Manual' : 'Background') + ' update failed (' + exception.message + ') for folder ' + currentRequestFolder.get('globalname') + ' -> will check mails again in "' + this.updateInterval / 1000 + '" seconds');
    Tine.log.debug(exception);
    this.checkMailsDelayedTask.delay(this.updateInterval);
  },
  /**
   * executed right before this app gets activated
   */
  onBeforeActivate: function () {
    Tine.log.info('activating felamimail now');
    // abort preloading/old actions and force fresh fetch
    this.checkMailsDelayedTask.delay(0);
  },
  /**
   * on update folder
   * 
   * @param {Tine.Felamimail.FolderStore} store
   * @param {Tine.Felamimail.Model.Folder} record
   * @param {String} operation
   */
  onUpdateFolder: function (store, record, operation) {
    if (operation === Ext.data.Record.EDIT) {
      if (record.isModified('cache_status')) {
        Tine.log.info('Tine.Felamimail.Application::onUpdateFolder(): Folder "' + record.get('localname') + '" updated with cache_status: ' + record.get('cache_status'));

        // as soon as we get a folder with status != complete we need to trigger checkmail soon!
        if (['complete', 'pending'].indexOf(record.get('cache_status')) === -1) {
          this.checkMailsDelayedTask.delay(1000);
        }

        // this should not be shown for messages that have been marked "unread" by another client
        if (record.isInbox() && record.isModified('cache_unreadcount') && record.isModified('cache_totalcount')) {
          this.showNewMessageNotification(record);
        }
        if (record.isModified('imap_lastmodseq')) {
          Tine.log.info('Tine.Felamimail.Application::onUpdateFolder(): flags have changed');
          // TODO this needs to be reviewed: grid is loaded way to often, selection is lost. maybe we need to have the updated flags here or the messages with updated flags
          // TODO maybe server needs to be reviewed, too as flags should not be synced if the tine client herself changes the flags
          // TODO maybe we need cache_lastmodseq, too
          //this.getMainScreen().getCenterPanel().getStore().reload();
        }
      }

      if (record.isInbox()) {
        if (this.isDefaultAccountId(record.get('account_id'))) {
          if (record.isModified('cache_unreadcount') || record.get('cache_unreadcount') != this.unreadcountInDefaultInbox) {
            this.setTitleWithUnreadcount(record.get('cache_unreadcount'));
          }
        }
        if (record.isModified('quota_usage') || record.isModified('quota_limit')) {
          this.onUpdateFolderQuota(record);
        }
      }
    }
  },
  /**
   * checks default account id
   * 
   * @param {String} accountId
   * @return {Boolean}
   */
  isDefaultAccountId: function (accountId) {
    return accountId == Tine.Felamimail.registry.get('preferences').get('defaultEmailAccount');
  },
  /**
   * show notification for new messages
   * 
   * @param {Tine.Felamimail.Model.Folder} record
   */
  showNewMessageNotification: function (record) {
    var recents = record.get('cache_unreadcount') - record.modified.cache_unreadcount,
      account = this.getAccountStore().getById(record.get('account_id'));
    if (recents > 0) {
      Tine.log.info('Show notification: ' + recents + ' new mails.');
      var title = this.i18n._('New mails'),
        message = String.format(this.i18n._('You got {0} new mail(s) in folder {1} ({2}).'), recents, record.get('localname'), account.get('name'));
      if (record.isCurrentSelection()) {
        // need to defer the notification because the new messages are not shown yet 
        // -> improve this with a callback fn or something like that / unread count should be updated when the messages become visible, too
        Ext.ux.Notification.show.defer(3500, this, [title, message]);
      } else {
        Ext.ux.Notification.show(title, message);
      }
    }
  },
  /**
   * write number of unread messages in all accounts into title
   * 
   * @param {Number} unreadcount
   */
  setTitleWithUnreadcount: function (unreadcount) {
    if (!window.isMainWindow) {
      return;
    }
    this.unreadcountInDefaultInbox = unreadcount;
    if (this.unreadcountInDefaultInbox < 0) {
      this.unreadcountInDefaultInbox = 0;
    }
    Tine.log.info('Updating title with new unreadcount: ' + this.unreadcountInDefaultInbox);
    const currentTitle = document.title;
    const unreadString = this.unreadcountInDefaultInbox ? '(' + this.unreadcountInDefaultInbox + ') ' : '';
    if (currentTitle.match(/^\([0-9]+\) /)) {
      document.title = document.title.replace(/^\([0-9]+\) /, unreadString);
    } else {
      document.title = unreadString + currentTitle;
    }
  },
  /**
   * folder quota is updated
   * 
   * @param {Tine.Felamimail.Model.Folder} record
   */
  onUpdateFolderQuota: function (record) {
    if (record.get('quota_usage')) {
      Tine.log.info('Folder "' + record.get('localname') + '" updated with quota values: ' + record.get('quota_usage') + ' / ' + record.get('quota_limit'));
      this.getMainScreen().getCenterPanel().updateQuotaBar(record);
    }
  },
  /**
   * get active account
   * @return {Tine.Felamimail.Model.Account}
   */
  getActiveAccount: function () {
    var account = null;
    var treePanel = this.getMainScreen().getTreePanel();
    if (treePanel && treePanel.rendered) {
      account = treePanel.getActiveAccount();
    }
    if (!account) {
      account = this.getAccountStore().getById(Tine.Felamimail.registry.get('preferences').get('defaultEmailAccount'));
    }
    if (!account) {
      // try to get first account in store
      account = this.getAccountStore().getAt(0);
    }
    return account;
  },
  initAccountStore: async function () {
    return new Promise(_.bind((fulfill, reject) => {
      Tine.log.debug('creating account store');
      this.accountStore = new Ext.data.JsonStore({
        fields: Tine.Felamimail.Model.Account,
        id: 'id',
        root: 'results',
        totalProperty: 'totalcount',
        proxy: Tine.Felamimail.accountBackend,
        reader: Tine.Felamimail.accountBackend.getReader(),
        listeners: {
          load: _.bind(() => {
            fulfill(this.accountStore);
          }, this)
        }
      });
      this.accountStore.load();
      this.postalSubscriptions = [];
      this.postalSubscriptions.push(postal.subscribe({
        channel: "recordchange",
        topic: "Felamimail.Account.*",
        callback: _.bind(this.onAccountRecordChange, this)
      }));
      this.postalSubscriptions.push(postal.subscribe({
        channel: "recordchange",
        topic: "Admin.EmailAccount.*",
        callback: _.bind(this.onAccountRecordChange, this)
      }));
    }, this));
  },
  /**
   * get account store
   *
   * @return {Ext.data.JsonStore}
   */
  getAccountStore: function () {
    return this.accountStore;
  },
  /**
   * update account store after account record change
   *
   * @param {} account
   * @param {} e
   */
  onAccountRecordChange: function (account, e) {
    let accountRecord = Tine.Tinebase.data.Record.setFromJson(account, Tine.Felamimail.Model.Account);
    let existingAccount = this.accountStore.getById(account.id);

    // filter out other user accounts / edit from admin module
    if (_.get(account, 'user_id.accountId') === Tine.Tinebase.registry.get('currentAccount').accountId) {
      if (e.topic.match(/\.create/)) {
        this.accountStore.add([accountRecord]);
      } else if (e.topic.match(/\.update/) && existingAccount) {
        existingAccount.update(accountRecord);
        existingAccount.commit();
      } else if (e.topic.match(/\.delete/) && existingAccount) {
        this.accountStore.remove(existingAccount);
      }
      this.getMainScreen().getCenterPanel().action_write.setDisabled(!this.getActiveAccount());
    } else if (e.topic.match(/\.update/) && existingAccount) {
      // handle shared account updates in felamimail
      existingAccount.update(accountRecord);
      existingAccount.commit();
    }
  },
  /**
   * gets default signature of given account
   *
   * @param {Tine.Felamimail.Model.Account} account
   * @return {Tine.Felamimail.Model.Signature}
   */
  getDefaultSignature: function (account) {
    account = _.isString(account) ? this.getAccountStore().getById(account) : account;
    let signatures = _.get(account, 'data.signatures', []);
    let signature = _.find(signatures, s => {
      return !!+_.get(s, 'is_default');
    }) || signatures[0];
    return signature ? Tine.Tinebase.data.Record.setFromJson(signature, Tine.Felamimail.Model.Signature) : null;
  },
  /**
   * show felamimail credentials dialog
   *
   * @param {Tine.Felamimail.Model.Account} account
   * @param {String} username [optional]
   */
  showCredentialsDialog: function (account, username) {
    Tine.Felamimail.credentialsDialog = Tine.widgets.dialog.CredentialsDialog.openWindow({
      windowTitle: String.format(this.i18n._('IMAP Credentials for {0}'), account.get('name')),
      appName: 'Felamimail',
      credentialsId: account.id,
      i18nRecordName: this.i18n._('Credentials'),
      recordClass: Tine.Tinebase.Model.Credentials,
      record: new Tine.Tinebase.Model.Credentials({
        id: account.id,
        username: username ? username : ''
      }),
      listeners: {
        scope: this,
        'update': function (data) {
          var folderStore = this.getFolderStore();
          if (folderStore.queriesPending.length > 0) {
            // reload all folders of account and try to select inbox
            var accountId = folderStore.queriesPending[0].substring(16, 56),
              account = this.getAccountStore().getById(accountId),
              accountNode = this.getMainScreen().getTreePanel().getNodeById(accountId);
            folderStore.resetQueryAndRemoveRecords('parent_path', '/' + accountId);
            account.set('all_folders_fetched', true);
            account.commit();
            accountNode.loading = false;
            accountNode.reload(function (callback) {
              Ext.each(accountNode.childNodes, function (node) {
                if (Ext.util.Format.lowercase(node.attributes.localname) == 'inbox') {
                  node.select();
                  return false;
                }
              }, this);
            });
          } else {
            this.checkMailsDelayedTask.delay(0);
          }
        }
      }
    });
  },
  /**
   * compose mail via mailto link
   *
   * @param paramString from mailto link
   */
  mailto: function (paramString) {
    var decodedParamString = decodeURIComponent(paramString).replace(/mailto:/, ''),
      parts = decodedParamString.split(/\?|&/),
      params = {};
    Ext.each(parts, function (part) {
      var pair = part.split('='),
        param = pair[1] ? pair[0] : 'to',
        value = decodeURIComponent(pair[1] ? pair[1] : pair[0]);
      value = param.match(/(body)|(subject)/) ? value : value.split(',');
      param = param == 'body' ? 'msgBody' : param;
      params[param] = value.length > 1 ? value : value[0];
    });
    (0,util_waitFor_es6__WEBPACK_IMPORTED_MODULE_0__/* ["default"] */ .Z)(() => {
      return !!Tine.Tinebase.appMgr.get('Felamimail').getActiveAccount();
    }, 10000).then(() => {
      var activeAccount = Tine.Tinebase.appMgr.get('Felamimail').getActiveAccount();
      params['accountId'] = activeAccount ? activeAccount.id : null;

      //@TODO: remove old url from popstate. its ugly!
      Tine.Tinebase.MainScreenPanel.show(this);
      Tine.Felamimail.MessageEditDialog.openWindow(params);
    });
  }
});
Tine.Felamimail.registerProtocolHandlerAction = new Ext.Action({
  iconCls: 'FelamimailIconCls',
  handler: function () {
    var url = Tine.Tinebase.common.getUrl() + '#Felamimail/MailTo/%s';
    navigator.registerProtocolHandler('mailto', url, Ext.util.Format.stripTags(Tine.title));
  }
});
Ext.ux.ItemRegistry.registerItem('Tine.Tinebase.MainMenu.userActions', Tine.Felamimail.registerProtocolHandlerAction);

/**
 * @namespace Tine.Felamimail
 * @class Tine.Felamimail.MainScreen
 * @extends Tine.widgets.MainScreen
 * 
 * MainScreen Definition
 */
Tine.Felamimail.MainScreen = Ext.extend(Tine.widgets.MainScreen, {
  /**
   * adapter fn to get folder tree panel
   * 
   * @return {Ext.tree.TreePanel}
   */
  getTreePanel: function () {
    return this.getWestPanel().getContainerTreePanel();
  }
});

/**
 * get flags store
 *
 * @param {Boolean} reload
 * @return {Ext.data.JsonStore}
 */
Tine.Felamimail.loadFlagsStore = function (reload) {
  var store = Ext.StoreMgr.get('FelamimailFlagsStore');
  if (!store) {
    // create store (get from initial registry data)
    store = new Ext.data.JsonStore({
      fields: Tine.Felamimail.Model.Flag,
      data: Tine.Felamimail.registry.get('supportedFlags'),
      autoLoad: true,
      id: 'id',
      root: 'results',
      totalProperty: 'totalcount'
    });
    Ext.StoreMgr.add('FelamimailFlagsStore', store);
  }
  return store;
};

/**
 * gets default signature text of given account
 *
 * @param {String|Tine.Felamimail.Model.Account} account
 * @param signatureRecord
 * @return {String}
 */
Tine.Felamimail.getSignature = function () {
  let account = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
  let signatureRecord = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
  const app = Tine.Tinebase.appMgr.get('Felamimail');
  let signatureText = '';
  account = _.isString(account) ? app.getAccountStore().getById(account) : account;
  account = account || app.getActiveAccount();
  if (account) {
    Tine.log.info('Tine.Felamimail.getSignature() - Fetch signature of account ' + account.id + ' (' + account.name + ')');
    signatureRecord = signatureRecord || app.getDefaultSignature(account);
    if (signatureRecord && signatureRecord.id !== 'none') {
      // NOTE: signature is always in html, nl2br here would cause duplicate linebreaks!
      signatureText = _.get(signatureRecord, 'data.signature', '');
    }
  }
  return signatureText;
};

/**
 * get email string (n_fileas <email@host.tld>) from contact
 * 
 * @param {Tine.Addressbook.Model.Contact} contact
 * @return {String}
 */
Tine.Felamimail.getEmailStringFromContact = function (contact) {
  var result = contact.get('n_fileas') + ' <';
  result += contact.getPreferredEmail();
  result += '>';
  return result;
};

/**
 * generic exception handler for felamimail (used by folder and message backends and updateMessageCache)
 * 
 * TODO move all 902 exception handling here!
 * TODO invent requery on 902 with cred. dialog
 * 
 * @param {Tine.Exception|Object} exception
 */
Tine.Felamimail.handleRequestException = function (exception) {
  if (!exception.code && exception.responseText) {
    // we need to decode the exception first
    var response = Ext.util.JSON.decode(exception.responseText);
    exception = response.data || response;
  }
  Tine.log.warn('Request exception :');
  Tine.log.warn(exception);
  var app = Tine.Tinebase.appMgr.get('Felamimail');
  switch (exception.code) {
    case 910: // Felamimail_Exception_IMAP
    case 911:
      // Felamimail_Exception_IMAPServiceUnavailable
      Ext.Msg.show({
        title: app.i18n._('IMAP Error'),
        msg: exception.message ? exception.message : app.i18n._('No connection to IMAP server.'),
        icon: Ext.MessageBox.ERROR,
        buttons: Ext.Msg.OK
      });
      break;
    case 912:
      // Felamimail_Exception_IMAPInvalidCredentials
      var accountId = exception.account && exception.account.id ? exception.account.id : '',
        account = accountId ? app.getAccountStore().getById(accountId) : null,
        imapStatus = account ? account.get('imap_status') : null;
      if (account) {
        account.set('all_folders_fetched', true);
        account.commit();
        if (account.get('type') == 'system') {
          // just show message box for system accounts
          Ext.Msg.show({
            title: app.i18n._('IMAP Credentials Error'),
            msg: app.i18n._('Your email credentials are wrong. Please contact your administrator'),
            icon: Ext.MessageBox.ERROR,
            buttons: Ext.Msg.OK
          });
        } else {
          app.showCredentialsDialog(account, exception.username);
        }
      } else {
        exception.code = 910;
        return this.handleRequestException(exception);
      }
      break;
    case 913:
      // Felamimail_Exception_IMAPFolderNotFound
      Ext.Msg.show({
        title: app.i18n._('IMAP Error'),
        msg: app.i18n._('One of your folders was deleted or renamed by another client. Please update the folder list of this account.'),
        icon: Ext.MessageBox.ERROR,
        buttons: Ext.Msg.OK
      });
      // TODO reload account root node
      break;
    case 914:
      // Felamimail_Exception_IMAPMessageNotFound
      Tine.log.notice('Message was deleted by another client.');

      // remove message from store and select next message
      var requestParams = Ext.util.JSON.decode(exception.request).params,
        centerPanel = app.getMainScreen().getCenterPanel(),
        msg = centerPanel.getStore().getById(requestParams.id);
      if (msg) {
        var sm = centerPanel.getGrid().getSelectionModel(),
          selectedMsgs = sm.getSelectionsCollection(),
          nextMessage = centerPanel.getNextMessage(selectedMsgs);
        centerPanel.getStore().remove(msg);
        if (nextMessage) {
          sm.selectRecords([nextMessage]);
        }
      }
      break;
    case 920:
      // Felamimail_Exception_SMTP
      Ext.Msg.show({
        title: app.i18n._('SMTP Error'),
        msg: exception.message ? exception.message : app.i18n._('No connection to SMTP server.'),
        icon: Ext.MessageBox.ERROR,
        buttons: Ext.Msg.OK
      });
      break;
    case 930:
      // Felamimail_Exception_Sieve
      Ext.Msg.show({
        title: app.i18n._('Sieve Error'),
        msg: exception.message ? exception.message : app.i18n._('No connection to Sieve server.'),
        icon: Ext.MessageBox.ERROR,
        buttons: Ext.Msg.OK
      });
      break;
    case 931:
      // Felamimail_Exception_SievePutScriptFail
      Ext.Msg.show({
        title: app.i18n._('Save Sieve Script Error'),
        msg: app.i18n._('Could not save script on Sieve server.') + (exception.message ? ' (' + exception.message + ')' : ''),
        icon: Ext.MessageBox.ERROR,
        buttons: Ext.Msg.OK
      });
      break;
    default:
      Tine.Tinebase.ExceptionHandler.handleRequestException(exception);
      break;
  }
};

/***/ }),

/***/ 8045:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010-2012 Metaways Infosystems GmbH (http://www.metaways.de)
 * 
 */
Ext.ns('Tine.Felamimail');

/**
 * @namespace   Tine.widgets.container
 * @class       Tine.Felamimail.FolderFilterModel
 * @extends     Tine.widgets.grid.FilterModel
 * 
 * @author      Philipp Schuele <p.schuele@metaways.de>
 */
Tine.Felamimail.FolderFilterModel = Ext.extend(Tine.widgets.grid.PickerFilter, {
  /**
   * @cfg 
   */
  operators: ['in', 'notin'],
  field: 'path',
  /**
   * @private
   */
  initComponent: function () {
    this.label = Tine.Felamimail.Model.Folder.getMeta('modelName');
    this.multiselectFieldConfig = {
      labelField: 'path',
      selectionWidget: new Tine.Felamimail.FolderSelectTriggerField({
        allAccounts: true
      }),
      recordClass: Tine.Felamimail.Model.Folder,
      valueStore: this.app.getFolderStore(),
      /**
       * functions
       */
      labelRenderer: Tine.Felamimail.GridPanel.prototype.accountAndFolderRenderer.createDelegate(this),
      initSelectionWidget: function () {
        this.selectionWidget.onSelectFolder = this.addRecord.createDelegate(this);
      },
      isSelectionVisible: function () {
        return this.selectionWidget.selectPanel && !this.selectionWidget.selectPanel.isDestroyed;
      },
      getRecordText: function (value) {
        const path = Ext.isString(value) ? value : value.path ? value.path : '/' + value.id;
        const index = this.valueStore.findExact('path', path);
        let record = this.valueStore.getAt(index);
        let text = path;
        if (!record) {
          // try account
          const accountId = path.substring(1, 40);
          record = this.app.getAccountStore().getById(accountId);
        }
        if (record) {
          this.currentValue.push(path);
          // always copy/clone record because it can't exist in 2 different stores
          this.store.add(record.copy());
          text = this.labelRenderer(record.id, {}, record);
        } else {
          this.currentValue.push(text);
        }
        return text;
      }
    };
    Tine.Felamimail.FolderFilterModel.superclass.initComponent.call(this);
  }
});
Tine.widgets.grid.FilterToolbar.FILTERS['tine.felamimail.folder.filtermodel'] = Tine.Felamimail.FolderFilterModel;

/***/ }),

/***/ 27166:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010-2012 Metaways Infosystems GmbH (http://www.metaways.de)
 */
Ext.ns('Tine.Felamimail');

/**
 * folder select trigger field
 * 
 * @namespace   Tine.widgets.container
 * @class       Tine.Felamimail.FolderSelectTriggerField
 * @extends     Ext.form.ComboBox
 * @author      Philipp Schuele <p.schuele@metaways.de>
 * 
 */
Tine.Felamimail.FolderSelectTriggerField = Ext.extend(Ext.form.TriggerField, {
  triggerClass: 'x-form-search-trigger',
  account: null,
  allAccounts: false,
  /**
   * onTriggerClick
   * open ext window with (folder-)select panel that fires event on select
   * 
   * @param e
   */
  onTriggerClick: function (e) {
    if (!this.disabled && this.account && this.account.id !== 0 || this.allAccounts) {
      this.selectPanel = Tine.Felamimail.FolderSelectPanel.openWindow({
        account: this.account,
        allAccounts: this.allAccounts,
        listeners: {
          // NOTE: scope has to be first item in listeners! @see Ext.ux.WindowFactory
          scope: this,
          'folderselect': this.onSelectFolder
        }
      });
    }
  },
  /**
   * select folder event listener
   * 
   * @param {Ext.tree.AsyncTreeNode} node
   */
  onSelectFolder: function (node) {
    this.selectPanel.close();
    this.setValue(node.attributes.globalname);
    this.el.focus();
  }
});
Ext.reg('felamimailfolderselect', Tine.Felamimail.FolderSelectTriggerField);

/***/ }),

/***/ 47694:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schuele <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.namespace('Tine.Felamimail');

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.FolderSelectPanel
 * @extends     Ext.Panel
 * 
 * <p>Account/Folder Tree Panel</p>
 * <p>Tree of Accounts with folders</p>
 * <pre>
 * TODO         show error if no account(s) available
 * TODO         make it possible to preselect folder
 * TODO         use it for folder subscriptions
 * </pre>
 * 
 * @author      Philipp Schuele <p.schuele@metaways.de>
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Felamimail.FolderSelectPanel
 */
Tine.Felamimail.FolderSelectPanel = Ext.extend(Ext.Panel, {
  /**
   * Panel config
   * @private
   */
  border: true,
  autoScroll: true,
  bodyStyle: 'background-color:white',
  selectedNode: null,
  /**
   * init
   * @private
   */
  initComponent: function () {
    this.addEvents(
    /**
     * @event folderselect
     * Fired when folder is selected
     */
    'folderselect');
    this.app = Tine.Tinebase.appMgr.get('Felamimail');
    if (!this.allAccounts) {
      this.account = this.account || this.app.getActiveAccount();
    }
    this.initActions();
    this.initFolderTree();
    Tine.Felamimail.FolderSelectPanel.superclass.initComponent.call(this);
  },
  /**
   * init actions
   */
  initActions: function () {
    this.action_cancel = new Ext.Action({
      text: i18n._('Cancel'),
      minWidth: 70,
      scope: this,
      handler: this.onCancel,
      iconCls: 'action_cancel'
    });
    this.action_ok = new Ext.Action({
      disabled: true,
      text: i18n._('Ok'),
      iconCls: 'action_saveAndClose',
      minWidth: 70,
      handler: this.onOk,
      scope: this
    });
    this.fbar = ['->', this.action_cancel, this.action_ok];
  },
  /**
   * init folder tree
   */
  initFolderTree: function () {
    if (this.allAccounts) {
      this.root = new Ext.tree.TreeNode({
        text: 'default',
        draggable: false,
        allowDrop: false,
        expanded: true,
        leaf: false,
        id: 'root'
      });
      var mainApp = Ext.ux.PopupWindowMgr.getMainWindow().Tine.Tinebase.appMgr.get('Felamimail');
      mainApp.getAccountStore().each(function (record) {
        // TODO generalize this
        var node = new Ext.tree.AsyncTreeNode({
          id: record.data.id,
          path: '/' + record.data.id,
          record: record,
          globalname: '',
          draggable: false,
          allowDrop: false,
          expanded: false,
          text: Ext.util.Format.htmlEncode(record.get('name')),
          qtip: Ext.util.Format.htmlEncode(record.get('host')),
          leaf: false,
          cls: 'felamimail-node-account',
          delimiter: record.get('delimiter'),
          ns_personal: record.get('ns_personal'),
          account_id: record.data.id
        });
        this.root.appendChild(node);
      }, this);
    } else {
      this.root = new Ext.tree.AsyncTreeNode({
        text: this.account.get('name'),
        draggable: false,
        allowDrop: false,
        expanded: true,
        leaf: false,
        cls: 'felamimail-node-account',
        id: this.account.id,
        path: '/' + this.account.id
      });
    }
    this.folderTree = new Ext.tree.TreePanel({
      id: 'felamimail-foldertree',
      border: false,
      rootVisible: !this.allAccounts,
      store: this.store || this.app.getFolderStore(),
      // TODO use another loader/store for subscriptions
      loader: this.loader || new Tine.Felamimail.TreeLoader({
        folderStore: this.store,
        app: this.app
      }),
      root: this.root
    });
    this.folderTree.on('dblclick', this.onTreeNodeDblClick, this);
    this.folderTree.on('click', this.onTreeNodeClick, this);
    this.folderTree.on('beforeclick', this.onTreeNodeBeforeClick, this);
    this.items = [this.folderTree];
  },
  /**
   * @private
   */
  afterRender: function () {
    Tine.Felamimail.FolderSelectPanel.superclass.afterRender.call(this);
    var title = !this.allAccounts ? String.format(this.app.i18n._('Folders of account {0}'), this.account.get('name')) : this.app.i18n._('Folders of all accounts');
    this.window.setTitle(title);
  },
  /**
   * on folder select handler
   * 
   * @param {Ext.tree.AsyncTreeNode} node
   * @private
   */
  onTreeNodeDblClick: function (node) {
    var _this$selectedNode$at;
    this.selectedNode = node;
    if ((_this$selectedNode$at = this.selectedNode.attributes) !== null && _this$selectedNode$at !== void 0 && _this$selectedNode$at.folderNode) {
      this.onOk();
    }
    return false;
  },
  /**
   * @private
   */
  onTreeNodeClick: function (node) {
    var _this$selectedNode$at2;
    this.selectedNode = node;
    const isFolderNode = (_this$selectedNode$at2 = this.selectedNode.attributes) === null || _this$selectedNode$at2 === void 0 ? void 0 : _this$selectedNode$at2.folderNode;
    this.action_ok.setDisabled(!isFolderNode);
  },
  /**
   * @private
   */
  onTreeNodeBeforeClick: function (node) {
    this.selectedNode = node;
    this.selectedNode.attributes.singleClickExpand = true;
  },
  /**
   * @private
   */
  onCancel: function () {
    this.purgeListeners();
    this.window.close();
  },
  /**
   * @private
   */
  onOk: function () {
    if (this.selectedNode) {
      this.fireEvent('folderselect', this.selectedNode);
    }
  }
});

/**
 * Felamimail FolderSelectPanel Popup
 * 
 * @param   {Object} config
 * @return  {Ext.ux.Window}
 */
Tine.Felamimail.FolderSelectPanel.openWindow = function (config) {
  var window = Tine.WindowFactory.getWindow({
    width: 200,
    height: 300,
    modal: true,
    name: Tine.Felamimail.FolderSelectPanel.prototype.windowNamePrefix + Ext.id(),
    contentPanelConstructor: 'Tine.Felamimail.FolderSelectPanel',
    contentPanelConstructorConfig: config
  });
  return window;
};

/***/ }),

/***/ 54758:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2009-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.namespace('Tine.Felamimail');

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.FolderStore
 * @extends     Ext.data.Store
 * 
 * <p>Felamimail folder store</p>
 * <p>
 * </p>
 * 
 * @author      Philipp Schuele <p.schuele@metaways.de>
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * 
 * @param       {Object} config
 * 
 * @constructor
 * Create a new  Tine.Felamimail.FolderStore
 */
Tine.Felamimail.FolderStore = function (config) {
  config = config || {};
  Ext.apply(this, config);
  this.reader = Tine.Felamimail.folderBackend.getReader();
  this.queriesPending = [];
  this.queriesDone = [];
  Tine.Felamimail.FolderStore.superclass.constructor.call(this);
  this.on('load', this.onStoreLoad, this);
  this.on('add', this.onStoreAdd, this);
  this.on('remove', this.onStoreRemove, this);
  this.on('loadexception', this.onStoreLoadException, this);
};
Ext.extend(Tine.Felamimail.FolderStore, Ext.data.Store, {
  fields: Tine.Felamimail.Model.Folder,
  proxy: Tine.Felamimail.folderBackend,
  /**
   * @property queriesDone
   * @type Array
   */
  queriesDone: null,
  /**
   * @property queriesPending
   * @type Array
   */
  queriesPending: null,
  /**
   * async query
   */
  asyncQuery: function (field, value, callback, args, scope, store) {
    var result = null,
      key = store.getKey(field, value);
    Tine.log.info('Tine.Felamimail.FolderStore.asyncQuery: ' + key);
    if (store.queriesDone.indexOf(key) >= 0) {
      Tine.log.debug('result already loaded -> directly query store');
      // we need regexp here because query returns all records with path that begins with the value string otherwise
      var valueReg = new RegExp(window.lodash.escapeRegExp(value) + '$');
      result = store.query(field, valueReg);
      args.push(result);
      callback.apply(scope, args);
    } else if (store.queriesPending.indexOf(key) >= 0) {
      Tine.log.debug('result not in store yet, but async query already running -> wait a bit');
      this.asyncQuery.defer(2500, this, [field, value, callback, args, scope, store]);
    } else {
      Tine.log.debug('result is requested the first time -> fetch from server');
      var accountId = value.match(/^\/([a-z0-9]*)/i)[1],
        folderIdMatch = value.match(/[a-z0-9]+\/([a-z0-9]*)$/i),
        folderId = folderIdMatch ? folderIdMatch[1] : null,
        folder = folderId ? store.getById(folderId) : null;
      if (folderId && !folder) {
        Tine.log.warn('folder ' + folderId + ' not found -> performing no query at all');
        callback.apply(scope, args);
        return;
      }
      store.queriesPending.push(key);
      store.load({
        path: value,
        params: {
          filter: [{
            field: 'account_id',
            operator: 'equals',
            value: accountId
          }, {
            field: 'globalname',
            operator: 'equals',
            value: folder !== null ? folder.get('globalname') : ''
          }]
        },
        callback: function () {
          store.queriesDone.push(key);
          store.queriesPending.remove(key);

          // query store again (it should have the new folders now) and call callback function to add nodes
          result = store.query(field, value);
          args.push(result);
          callback.apply(scope, args);
        },
        add: true
      });
    }
  },
  /**
   * on store load exception
   * 
   * @param {Tine.Tinebase.data.RecordProxy} proxy
   * @param {String} type
   * @param {Object} error
   * @param {Object} options
   * 
   * TODO remove loading class / remove from queriesDone?
   */
  onStoreLoadException: function (proxy, type, error, options) {
    //var node = options.params.path
    //node.getUI().removeClass("x-tree-node-loading");

    Tine.Felamimail.handleRequestException(error);
  },
  /**
   * check if query has already loaded or is loading
   * 
   * @param {String} field
   * @param {String} value
   * @return {boolean}
   */
  isLoadedOrLoading: function (field, value) {
    var key = this.getKey(field, value),
      result = false;
    result = this.queriesDone.indexOf(key) >= 0 || this.queriesPending.indexOf(key) >= 0;
    return result;
  },
  /**
   * get key to store query 
   * 
   * @param  {string} field
   * @param  {mixed} value
   * @return {string}
   */
  getKey: function (field, value) {
    return field + ' -> ' + value;
  },
  /**
   * load event handler
   * 
   * @param {Tine.Felamimail.FolderStore} store
   * @param {Tine.Felamimail.Model.Folder} records
   * @param {Object} options
   */
  onStoreLoad: function (store, records, options) {
    this.computePaths(records, options.path);
  },
  /**
   * add event handler
   * 
   * @param {Tine.Felamimail.FolderStore} store
   * @param {Tine.Felamimail.Model.Folder} records
   * @param {Integer} index
   */
  onStoreAdd: function (store, records, index) {
    this.computePaths(records, null);
  },
  /**
   * add event handler
   *
   * @param {Tine.Felamimail.FolderStore} store
   * @param {Tine.Felamimail.Model.Folder} record
   * @param {Integer} index
   */
  onStoreRemove: function (store, record, index) {
    this.resetQueryAndRemoveRecords('parent_path', record.get('path'));
  },
  /**
   * compute paths for folder records
   *
   * @param {Tine.Felamimail.Model.Folder} records
   * @param givenParentPath
   */
  computePaths: function (records, givenParentPath) {
    let parentPath;
    Ext.each(records, function (record) {
      const parent = this.getParent(record);
      if (givenParentPath === null) {
        parentPath = parent ? parent.get('path') : '/' + record.get('account_id');
      } else {
        parentPath = parent ? parent.get('path') : givenParentPath;
      }
      const path = parentPath + '/' + record.id;
      if (record.get('parent_path') !== parentPath || record.get('path') !== path) {
        record.beginEdit();
        record.set('parent_path', parentPath);
        record.set('path', path);
        record.endEdit();
      }
    }, this);
  },
  /**
   * recursive resets the query and removes all records that match it
   * 
   * @param {String} field
   * @param {String} value
   */
  resetQueryAndRemoveRecords: function (field, value) {
    this.queriesPending.remove(this.getKey(field, value));
    const toRemove = this.query(field, value);
    toRemove.items.forEach(record => {
      const parentKey = this.getKey(field, record.get(field));
      const currentKey = this.getKey(field, record.get('path'));
      if (this.queriesDone.indexOf(parentKey) > -1) {
        this.queriesDone.remove(parentKey);
      } else {
        if (this.queriesDone.indexOf(currentKey) > -1) {
          this.queriesDone.remove(currentKey);
        }
      }
      this.remove(record);
    });
  },
  /**
   * update folder in this store
   * 
   * NOTE: parent_path and path are computed onLoad and must be preserved
   * 
   * @param {Array/Tine.Felamimail.Model.Folder} update
   * @return {Tine.Felamimail.Model.Folder}
   */
  updateFolder: function (update) {
    if (Ext.isArray(update)) {
      Ext.each(update, function (u) {
        this.updateFolder.call(this, u);
      }, this);
      return;
    }
    var folder = this.getById(update.id);
    if (folder) {
      folder.beginEdit();
      Ext.each(Tine.Felamimail.Model.Folder.getFieldNames(), function (f) {
        if (!f.match('path')) {
          folder.set(f, update.get(f));
        }
      }, this);
      folder.endEdit();
      folder.commit();
      return folder;
    }
  },
  /**
   * get parent folder
   * 
   * @param {Tine.Felamimail.Model.Folder} folder
   * @return {Tine.Felamimail.Model.Folder|null}
   */
  getParent: function (folder) {
    var result = this.queryBy(function (record, id) {
      if (record.get('account_id') == folder.get('account_id') && record.get('globalname') == folder.get('parent')) {
        return true;
      }
    });
    return result.first() || null;
  }
});

/***/ }),

/***/ 1130:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2009-2018 Metaways Infosystems GmbH (http://www.metaways.de)
 */

Ext.ns('Tine.Felamimail');

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.GridDetailsPanel
 * @extends     Tine.widgets.grid.DetailsPanel
 * 
 * <p>Message Grid Details Panel</p>
 * <p>the details panel (shows message content)</p>
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Felamimail.GridDetailsPanel
 */
Tine.Felamimail.GridDetailsPanel = Ext.extend(Tine.widgets.grid.DetailsPanel, {
  /**
   * config
   * @private
   */
  defaultHeight: 350,
  currentId: null,
  record: null,
  app: null,
  i18n: null,
  cls: 'x-ux-display',
  fetchBodyTransactionId: null,
  /**
   * init
   * @private
   */
  initComponent: function () {
    this.initDefaultTemplate();
    // this.initTopToolbar();

    Tine.Felamimail.GridDetailsPanel.superclass.initComponent.call(this);
  },
  /**
   * use default Tpl for default and multi view
   */
  initDefaultTemplate: function () {
    this.defaultTpl = new Ext.XTemplate('<div class="preview-panel-felamimail">', '<div class="preview-panel-felamimail-preparedPart"></div>', '<div class="preview-panel-felamimail-body">{[values ? values.msg : ""]}</div>', '</div>');
  },
  /**
   * init bottom toolbar (needed for event invitations atm)
   * 
   * TODO add buttons (show header, add to addressbook, create filter, show images ...) here
   */
  //    initTopToolbar: function() {
  //        this.tbar = new Ext.Toolbar({
  //            hidden: true,
  //            items: []
  //        });
  //    },

  /**
   * get panel for single record details
   * 
   * @return {Ext.Panel}
   */
  getSingleRecordPanel: function () {
    if (!this.singleRecordPanel) {
      this.singleRecordPanel = new Tine.Felamimail.MailDetailsPanel({
        hasTopToolbar: false,
        grid: this.grid
      });
    }
    return this.singleRecordPanel;
  },
  /**
   * (on) update details
   * 
   * @param {Tine.Felamimail.Model.Message} record
   * @private
   */
  updateDetails: function (record) {
    if (record.id === this.currentId) {
      // nothing to do
    } else if (!record.bodyIsFetched()) {
      this.waitForContent(record);
    } else if (record === this.record) {
      this.setTemplateContent(record);
    }
  },
  /**
   * wait for body content
   * 
   * @param {Tine.Felamimail.Model.Message} record
   *
   * @todo move to Tine.Felamimail.MailDetailsPanel?
   */
  waitForContent: function (record) {
    if (!this.grid || this.grid.getSelectionModel().getCount() == 1) {
      this.refetchBody(record, {
        success: this.updateDetails.createDelegate(this, [record]),
        failure: function (exception) {
          Tine.log.debug(exception);
          this.getLoadMask().hide();
          if (exception.code == 404) {
            this.defaultTpl.overwrite(this.singleRecordPanel.getTemplateBody(), {
              msg: this.app.i18n._('Message not available.')
            });
          } else {
            Tine.Felamimail.messageBackend.handleRequestException(exception);
          }
        },
        scope: this
      });
      this.defaultTpl.overwrite(this.singleRecordPanel.getTemplateBody(), {
        msg: ''
      });
      this.getLoadMask().show();
    } else {
      this.getLoadMask().hide();
    }
  },
  /**
   * refetch message body
   * 
   * @param {Tine.Felamimail.Model.Message} record
   * @param {Function} callback
   */
  refetchBody: function (record, callback) {
    // cancel old request first
    if (this.fetchBodyTransactionId && !Tine.Felamimail.messageBackend.isLoading(this.fetchBodyTransactionId)) {
      Tine.log.debug('Tine.Felamimail.GridDetailsPanel::refetchBody -> cancelling current fetchBody request.');
      Tine.Felamimail.messageBackend.abort(this.fetchBodyTransactionId);
    }
    Tine.log.debug('Tine.Felamimail.GridDetailsPanel::refetchBody -> calling fetchBody');
    this.fetchBodyTransactionId = Tine.Felamimail.messageBackend.fetchBody(record, 'configured', callback);
  },
  /**
   * overwrite template with content
   * 
   * @param {Tine.Felamimail.Model.Message} record
   */
  setTemplateContent: function (record) {
    this.currentId = record.id;
    this.getLoadMask().hide();
    this.singleRecordPanel.loadRecord(record);
    this.getEl().down('div').down('div').scrollTo('top', 0, false);
    if (this.record.get('preparedParts') && this.record.get('preparedParts').length > 0) {
      Tine.log.debug('Tine.Felamimail.GridDetailsPanel::setTemplateContent about to handle preparedParts');
      this.handlePreparedParts(record);
    }
  },
  /**
   * handle invitation messages (show top + bottom panels)
   * 
   * @param {Tine.Felamimail.Model.Message} record
   */
  handlePreparedParts: function (record) {
    var firstPreparedPart = this.record.get('preparedParts')[0],
      mimeType = String(firstPreparedPart.contentType).split(/[ ;]/)[0],
      mainType = Tine.Felamimail.MimeDisplayManager.getMainType(mimeType);
    if (!mainType) {
      Tine.log.info('Tine.Felamimail.GridDetailsPanel::handlePreparedParts nothing found to handle ' + mimeType);
      return;
    }
    var bodyEl = this.singleRecordPanel.getMessageRecordPanel().getEl().query('div[class=preview-panel-felamimail-preparedPart]')[0],
      detailsPanel = Tine.Felamimail.MimeDisplayManager.create(mainType, {
        detailsPanel: this,
        preparedPart: firstPreparedPart
      });
    Ext.fly(bodyEl).update('');
    detailsPanel.render(bodyEl);
  }
});

/***/ }),

/***/ 48588:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";
// ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);

;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/applyDecoratedDescriptor.js
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
  var desc = {};
  Object.keys(descriptor).forEach(function (key) {
    desc[key] = descriptor[key];
  });
  desc.enumerable = !!desc.enumerable;
  desc.configurable = !!desc.configurable;
  if ('value' in desc || desc.initializer) {
    desc.writable = true;
  }
  desc = decorators.slice().reverse().reduce(function (desc, decorator) {
    return decorator(target, property, desc) || desc;
  }, desc);
  if (context && desc.initializer !== void 0) {
    desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
    desc.initializer = undefined;
  }
  if (desc.initializer === void 0) {
    Object.defineProperty(target, property, desc);
    desc = null;
  }
  return desc;
}
// EXTERNAL MODULE: ./node_modules/keydown/src/lib/array.from.js
var array_from = __webpack_require__(26911);
;// CONCATENATED MODULE: ./node_modules/keydown/src/lib/keys.js
// TODO: Need better, more complete, and more methodical key definitions

const Keys = {
  backspace: 8,
  del:       46,
  delete:    46,
  tab:       9,
  enter:     13,
  'return':  13,
  esc:       27,
  space:     32,
  pageUp:    33,
  pageDown:  34,
  end:       35,
  home:      36,
  left:      37,
  up:        38,
  right:     39,
  down:      40,
  ';':       186,
  '=':       187,
  ',':       188,
  '-':       189,
  '.':       190,
  '/':       191,
  '`':       192,
  '[':       219,
  '\\':      220,
  ']':       221
};

// Add uppercase versions of keys above for backwards compatibility
Object.keys( Keys ).forEach( key => Keys[ key.toUpperCase() ] = Keys[ key ] );

'0123456789'.split( '' ).forEach( ( num, index ) => Keys[ num ] = index + 48 );

'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split( '' ).forEach( ( letter, index ) => {
  Keys[letter] = index + 65;
  Keys[letter.toLowerCase()] = index + 65;
});

// fn keys
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].forEach( ( item, index ) => Keys[ `f${ index }` ] = 111 + index );

const modifiers = {
  control: 'ctrl',
  ctrl:    'ctrl',
  shift:   'shift',
  meta:    'meta',
  cmd:     'meta',
  command: 'meta',
  option:  'alt',
  alt:     'alt'
};

const ALL_KEYS = Symbol('ALL_KEYS')

const ALL_PRINTABLE_KEYS = Symbol('ALL_PRINTABLE_KEYS')

/* harmony default export */ const keys = (Keys);

;// CONCATENATED MODULE: ./node_modules/keydown/src/lib/match_keys.js


const PRINTABLE_CHARACTERS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~!@#$%^&*()-_+=[]\\{}|;\':",./<>?£'

const modKeys = Object.keys( modifiers );

function match_keys_matchKeys( { keySet, event } ) {
  const { key, modifiers = [] } = keySet;
  let keysMatch

  keysMatch = key === ALL_KEYS;

  if (key === ALL_PRINTABLE_KEYS) {
    if (event.key) {
      // Modern browsers implement `key`, so if `key` is length 1, we have a match. e.g. 'a' for the
      // a key, or '2' for the 2 key. All other non-printable characters have names, e.g. 'Enter' or 'Backspace'.
      keysMatch = event.key.length === 1
    } else {
      // For browsers that do no support `event.key`, we test against a list of characters
      const pressedChar = String.fromCharCode(event.charCode)
      keysMatch = PRINTABLE_CHARACTERS.indexOf(pressedChar) >= 0
    }
  }

  if ( key === event.which ) {
    const evtModKeys = modKeys.filter( modKey => event[ `${modKey}Key` ] ).sort();
    keysMatch = (
      modifiers.length === evtModKeys.length &&
      modifiers.every( ( modKey, index ) => evtModKeys[ index ] === modKey )
    );
  }

  return keysMatch;
}

/* harmony default export */ const match_keys = (match_keys_matchKeys);

;// CONCATENATED MODULE: ./node_modules/keydown/src/lib/parse_keys.js


function parse_keys_parseKeys( keysArray ) {
  return keysArray.map( key => {
    let keySet = { key };
    if ( typeof key === 'string' ) {
      const keyString = key.toLowerCase().trim();
      const matches = keyString.split( /\s?\+\s?/ );
      keySet = matches.length === 1 ?
        { key: keys[ keyString ] } :
        {
          key: keys[ matches.pop() ],
          modifiers: matches.map( modKey => modifiers[modKey] ).sort()
        };
    }
    return keySet;
  });
}

/* harmony default export */ const parse_keys = (parse_keys_parseKeys);

;// CONCATENATED MODULE: ./node_modules/keydown/src/lib/uuid.js
// Counter being incremented. JS is single-threaded, so it'll Just Work™.
let __counter = 1;

/**
 * Returns a process-wide unique identifier.
 */
function uuid() {
  return `uid-${__counter++}`;
}

;// CONCATENATED MODULE: ./node_modules/keydown/src/store.js
/**
 * @module store
 *
 */




/**
 * private
 *
 */

// dict for class prototypes => bindings
const _handlers = new Map();

// all mounted instances that have keybindings
const _instances = new Set();

// for testing
function _resetStore() {
  _handlers.clear();
  _instances.clear();
}

/**
 * activate
 *
 * @access public
 * @param {object} instance Instantiated class that extended React.Component, to be focused to receive keydown events
 */
function activate( instances ) {
  const instancesArray = [].concat( instances );

  // if no components were found as ancestors of the event target,
  // effectively deactivate keydown handling by capping the set of instances
  // with `null`.
  if ( !instancesArray.length ) {
    _instances.add( null );
  } else {
    _instances.delete( null );

    // deleting and then adding the instance(s) has the effect of sorting the set
    // according to instance activation (ascending)
    instancesArray.forEach( instance => {
      _instances.delete( instance );
      _instances.add( instance );
    });
  }
};

/**
 * deleteInstance
 *
 * @access public
 * @param {object} target Instantiated class that extended React.Component
 * @return {boolean} The value set.has( target ) would have returned prior to deletion
 */
function deleteInstance( target ) {
  _instances.delete( target );
};

function findBindingForEvent( event ) {
  if ( !_instances.has( null ) ) {
    const keyMatchesEvent = keySet => match_keys( { keySet, event } );

    // loop through instances in reverse activation order so that most
    // recently activated instance gets first dibs on event
    for ( const instance of [ ..._instances ].reverse() ) {
      const bindings = getBinding( instance.constructor.prototype );
      for ( const [ keySets, fn ] of bindings ) {
        if ( keySets.some( keyMatchesEvent ) ) {
          // return when matching keybinding is found - i.e. only one
          // keybound component can respond to a given key code. to get around this,
          // scope a common ancestor component class with @keydown and use
          // @keydownScoped to bind the duplicate keys in your child components
          // (or just inspect nextProps.keydown.event).
          return { fn, instance };
        }
      }
    }
  }
  return null;
};

/**
 * getBinding
 *
 * @access public
 * @param {object} target Class used as key in dict of key bindings
 * @return {object} The object containing bindings for the given class
 */
function getBinding( { __reactKeydownUUID } ) {
  return _handlers.get( __reactKeydownUUID );
};

/**
 * getInstances
 *
 * @access public
 * @return {set} All stored instances (all mounted component instances with keybindings)
 */
function getInstances() {
  return _instances;
};

/**
 * isEmpty
 *
 * @access public
 * @return {number} Size of the set of all stored instances
 */
function isEmpty() {
  return !_instances.size;
};

/**
 * setBinding
 *
 * @access public
 * @param {object} args All arguments necessary to set the binding
 * @param {array} args.keys Key codes that should trigger the fn
 * @param {function} args.fn The callback to be triggered when given keys are pressed
 * @param {object} args.target The decorated class
 */
function setBinding( { keys, fn, target } ) {
  const keySets = parse_keys( keys );

  const { __reactKeydownUUID } = target;
  if ( !__reactKeydownUUID ) {
    target.__reactKeydownUUID = uuid();
    _handlers.set( target.__reactKeydownUUID, new Map( [ [ keySets, fn ] ] ) );
  } else {
    _handlers.get( __reactKeydownUUID ).set( keySets, fn );
  }
};

;// CONCATENATED MODULE: ./node_modules/keydown/src/lib/dom_helpers.js
/**
 * @module domHelpers
 *
 */
// import ReactDOM from 'react-dom';

const focusableSelector = 'a[href], button, input, object, select, textarea, [tabindex]';

const ReactDOM = {
  findDOMNode ( instance ) {
    return instance ? instance.getEl().dom : null;
  }
}
/**
 * bindFocusables: Find any focusable child elements of the component instance and
 * add an onFocus handler to focus our keydown handlers on the parent component
 * when user keys applies focus to the element.
 *
 * NOTE: One limitation of this right now is that if you tab out of the
 * component, _focusedInstance will still be set until next click or mount or
 * controlled focus.
 *
 * @access public
 * @param {object} instance The key-bound component instance
 * @param {callback} activateOnFocus The fn to fire when element is focused
 */
function bindFocusables( instance, activateOnFocus ) {
  if ( document.querySelectorAll ) {
    try {
      const node = ReactDOM.findDOMNode( instance );
      if ( node ) {
        const focusables = node.querySelectorAll( focusableSelector );
        if ( focusables.length ) {
          const onFocus = element => {
            const onFocusPrev = element.onfocus;
            return function( event ) {
              activateOnFocus( instance );
              if ( onFocusPrev ) onFocusPrev.call( element, event );
            };
          };
          Array.prototype.slice.call( focusables ).forEach( element => (
            element.onfocus = onFocus( element )
          ) );
        }
      }
    } catch ( error ) {
      // noop, mostly suppressing error here https://github.com/glortho/react-keydown/issues/76
    }
  }
}

/**
 * findContainerNodes: Called by our click handler to find instances with nodes
 * that are equal to or that contain the click target. Any that pass this test
 * will be recipients of the next keydown event.
 *
 * @access public
 * @param {object} target The click event.target DOM element
 * @return {function} Reducer function
 */
function findContainerNodes( target ) {
  return ( memo, instance ) => {
    try {
      const node = ReactDOM.findDOMNode( instance );
      if ( node && ( node === target || node.contains( target ) ) ) {
        memo.push( { instance, node } );
      }
    } finally {
      return memo;
    }
  };
}

/**
 * sortByDOMPosition: Called by our click handler to sort a list of instances
 * according to least -> most nested. This is so that if multiple keybound
 * instances have nodes that are ancestors of the click target, they will be
 * sorted to let the instance closest to the click target get first dibs on the
 * next key down event.
 */
function sortByDOMPosition( a, b ) {
  return a.node.compareDocumentPosition( b.node ) === 10 ? 1 : -1;
}

/* harmony default export */ const dom_helpers = ({ bindFocusables, findContainerNodes, sortByDOMPosition });

;// CONCATENATED MODULE: ./node_modules/keydown/src/lib/listeners.js
/**
 * @module Listeners
 *
 */

// flag for whether click listener has been bound to document
let _clicksBound = false;

// flag for whether keydown listener has been bound to document
let _keysBound = false;

const Listeners = {
  /**
   * _bindKeys
   *
   * @access public
   */
  bindKeys( callback ) {
    if ( !_keysBound ) {
      document.addEventListener( 'keydown', callback );
      _keysBound = true;
    }
  },

  /**
   * unbindKeys
   *
   * @access public
   */
  unbindKeys( callback ) {
    if ( _keysBound ) {
      document.removeEventListener( 'keydown', callback );
      _keysBound = false;
    }
  },

  /**
   * bindClicks
   *
   * @access public
   */
  bindClicks( callback ) {
    if ( !_clicksBound ) {
      document.addEventListener( 'click', callback, true );
      _clicksBound = true;
    }
  },

  /**
   * unbindClicks
   *
   * @access public
   */
  unbindClicks( callback ) {
    if ( _clicksBound ) {
      document.removeEventListener( 'click', callback, true );
      _clicksBound = false;
    }
  }
};

/* harmony default export */ const listeners = (Listeners);

;// CONCATENATED MODULE: ./node_modules/keydown/src/event_handlers.js
/* eslint-disable no-use-before-define */
/**
 * @module eventHandlers
 *
 */




/**
 * private
 *
 */

/**
 * _onClick
 *
 * @access private
 * @param {object} event The click event object
 * @param {object} event.target The DOM node from the click event
 */
function _onClick( { target } ) {
  activate(
    [ ...getInstances() ]
      .reduce( dom_helpers.findContainerNodes( target ), [] )
      .sort( dom_helpers.sortByDOMPosition )
      .map( item => item.instance )
  );
}

/**
 * _onKeyDown: The keydown event callback
 *
 * @access private
 * @param {object} event The keydown event object
 * @param {number} event.which The key code (which) received from the keydown event
 */
function _onKeyDown( event, forceConsider = false ) {
  if ( forceConsider || _shouldConsider( event ) ) {
    const { fn, instance } = findBindingForEvent( event ) || {};
    if ( fn ) {
      event.preventDefault();
      fn.call( instance, event );
      return true;
    }
  }
  return false;
}

/**
 * _shouldConsider: Conditions for proceeding with key event handling
 *
 * @access private
 * @param {object} event The keydown event object
 * @param {object} event.target The node origin of the event
 * @return {boolean} Whether to continue procesing the keydown event
 */
function _shouldConsider( { ctrlKey, target } ) {
  return (
    ctrlKey || (
      !~[ 'INPUT', 'SELECT', 'TEXTAREA' ].indexOf( target.tagName ) && (
        !target.getAttribute ||
        target.getAttribute( 'role' ) !== 'textbox'
      )
    )
  );
}

/**
 * public
 *
 */

/**
 * onMount
 *
 * @access public
 */
function onMount( instance ) {
  activate( instance );
  listeners.bindKeys( _onKeyDown );
  listeners.bindClicks( _onClick );
  dom_helpers.bindFocusables( instance, activate );
}

/**
 * onUnmount
 *
 * @access public
 */
function onUnmount( instance ) {
  deleteInstance( instance );
  if ( isEmpty() ) {
    listeners.unbindClicks( _onClick );
    listeners.unbindKeys( _onKeyDown );
  }
}



;// CONCATENATED MODULE: ./node_modules/keydown/src/decorators/class_decorator.js
/**
 * @module componentWrapper
 *
 */
// import React from 'react';





/**
 * componentWrapper
 *
 * @access public
 * @param {object} WrappedComponent React component class to be wrapped
 * @param {array} [keys] The key(s) bound to the class
 * @return {object} The higher-order function that wraps the decorated class
 */
function componentWrapper( WrappedComponent, keys = ALL_KEYS ) {

  class KeyBoardHelper { //extends React.Component {

    constructor( props ) {
      // super( props );
      this.state = {
        event: null
      };
    }

    componentDidMount() {
      onMount( this );
    }

    componentWillUnmount() {
      onUnmount( this );
    }

    handleKeyDown( event ) {
      // to simulate a keypress, set the event and then clear it in the callback
      this.setState( { event }, () => this.setState( { event: null } ) );
    }

    render() {
      // return <WrappedComponent {...this.props} keydown={this.state} />;
    }
  }

  setBinding( { keys: [].concat( keys ), fn: KeyBoardHelper.prototype.handleKeyDown, target: KeyBoardHelper.prototype } );

  return KeyBoardHelper;
}

/* harmony default export */ const class_decorator = (componentWrapper);

;// CONCATENATED MODULE: ./node_modules/keydown/src/decorators/method_decorator.js
/**
 * @module methodWrapper
 *
 */



/**
 * _isReactKeyDown
 *
 * @access private
 * @param {object} event The possibly synthetic event passed as an argument with
 * the method invocation.
 * @return {boolean}
 */
function _isReactKeyDown( event ) {
   return event &&
    typeof event === 'object' &&
    event.nativeEvent instanceof window.KeyboardEvent &&
    event.type === 'keydown';
}

/**
 * methodWrapper
 *
 * @access public
 * @param {object} args All arguments necessary for wrapping method
 * @param {object} args.target The decorated class
 * @param {object} args.descriptor Method descriptor
 * @param {array} args.keys The array of keys bound to the given method
 * @return {object} The method descriptor
 */
function methodWrapper( { target, descriptor, keys } ) {

  const fn = descriptor.value;

  // if we haven't already created a binding for this class (via another
  // decorated method), wrap these lifecycle methods.
  if ( !getBinding( target ) ) {

    const { componentDidMount, componentWillUnmount } = target;

    target.componentDidMount = function() {
      onMount( this );
      if ( componentDidMount ) return componentDidMount.call( this );
    };

    target.componentWillUnmount = function() {
      onUnmount( this );
      if ( componentWillUnmount ) return componentWillUnmount.call( this );
    };
  }

  // add this binding of keys and method to the target's bindings
  setBinding( { keys, target, fn } );

  descriptor.value = function( ...args ) {
    const [ maybeEvent ] = args;
    if ( _isReactKeyDown( maybeEvent ) ) {
      // proxy method in order to use @keydown as filter for keydown events coming
      // from an actual onKeyDown binding (as identified by react's addition of
      // 'nativeEvent' + type === 'keydown')
      if ( !maybeEvent.ctrlKey ) {
        // we already whitelist shortcuts with ctrl modifiers so if we were to
        // fire it again here the method would trigger twice. see https://github.com/glortho/react-keydown/issues/38
        return _onKeyDown( maybeEvent, true );
      }
    } else if ( !maybeEvent || !( maybeEvent instanceof window.KeyboardEvent ) || maybeEvent.type !== 'keydown' ) {
      // if our first argument is a keydown event it is being handled by our
      // binding system. if it's anything else, just pass through.
      return fn.call( this, ...args );
    }
  }

  return descriptor;
}

/* harmony default export */ const method_decorator = (methodWrapper);

;// CONCATENATED MODULE: ./node_modules/keydown/src/decorators/method_decorator_scoped.js
/**
 * @module methodWrapperScoped
 *
 */



/**
 * methodWrapperScoped
 *
 * @access public
 * @param {object} args All args necessary for decorating the method
 * @param {object} args.target The decorated method's class object
 * @param {object} args.descriptor The method's descriptor object
 * @param {array} args.keys The key codes bound to the decorated method
 * @return {object} The method's descriptor object
 */
function method_decorator_scoped_methodWrapperScoped( { target, descriptor, keys } ) {
  const { componentWillReceiveProps } = target;
  const fn = descriptor.value;
  if ( !keys ) {
    console.warn( `${fn}: keydownScoped requires one or more keys` );
  } else {
    const keySets = parseKeys( keys );

    /**
     * _shouldTrigger
     *
     * @access private
     * @param {object} thisProps Exsting props from the wrapped component
     * @param {object} thisProps.keydown The namespaced state from the higher-order
     * component (class_decorator)
     * @param {object} nextProps The incoming props from the wrapped component
     * @param {object} nextProps.keydown The namescaped state from the higher-order
     * component (class_decorator)
     * @param {array} keys The keys bound to the decorated method
     * @return {boolean} Whether all tests have passed
     */
    function _shouldTrigger (keydownThis, keydownNext) {
      if (!(keydownNext && keydownNext.event && !keydownThis.event)) return false;

      return ( keySets.some( keySet => matchKeys( { keySet, event: keydownNext.event } ) ) )
    }

    // wrap the component's lifecycle method to intercept key codes coming down
    // from the wrapped/scoped component up the view hierarchy. if new keydown
    // event has arrived and the key codes match what was specified in the
    // decorator, call the wrapped method.
    target.componentWillReceiveProps = function( nextProps, ...args ) {
      const { keydown: keydownNext } = nextProps;
      const { keydown: keydownThis } = this.props;

      if ( _shouldTrigger( keydownThis, keydownNext ) ) {
        return fn.call( this, keydownNext.event );
      }

      if ( componentWillReceiveProps ) return componentWillReceiveProps.call( this, nextProps, ...args );
    };
  }

  return descriptor;
}

/* harmony default export */ const method_decorator_scoped = ((/* unused pure expression or super */ null && (method_decorator_scoped_methodWrapperScoped)));



;// CONCATENATED MODULE: ./node_modules/keydown/src/decorators/index.js
/**
 * @module decorators
 *
 */




/**
 * noopDecorator
 *
 * @access private
 * @return {undefined} Returns `undefined` so that the original undecorated instance/method is used
 */
function noopDecorator () {
  return undefined
}

/**
 * _decorator
 *
 * @access private
 * @param {Function} methodFn The method wrapper to delegate to, based on whether user has specified a scoped decorator or not
 * @param {Array} ...args Remainder of arguments passed in
 * @return {Function} The decorated class or method
 */
function _decorator( methodFn, ...args ) {
  // check the first argument to see if it's a user-supplied keycode or array
  // of keycodes, or if it's the wrapped class or method
  const testArg = args[0];
  const isArray = Array.isArray( testArg );

  // if the test argument is not an object or function, it is user-supplied
  // keycodes. else there are no arguments and it's just the wrapped class
  if ( isArray || ~[ 'string', 'number', 'symbol' ].indexOf( typeof testArg ) ) {
    const keys = isArray ? testArg : args;

    // return the decorator function, which on the next call will look for
    // the presence of a method name to determine if this is a wrapped method
    // or component
    return ( target, methodName, descriptor ) => {
      return methodName ?
        methodFn( { target, descriptor: Object.assign(descriptor, {value: target[methodName]}), keys } ) :
        class_decorator( target, keys );
    };
  } else {
    const WrappedComponent = args[0];
    const methodName = args[1];

    // method decorators without keycode (which) arguments are not allowed.
    if ( WrappedComponent && !methodName ) {
      return class_decorator( ...args );
    } else {
      console.warn( `${methodName}: Method decorators must have keycode arguments, so the decorator for this method will not do anything` );
      return noopDecorator
    }
  }
}

/**
 * keydownScoped
 *
 * Method decorator that will look for changes to its targeted component's
 * `keydown` props to decide when to trigger, rather than responding directly
 * to keydown events. This lets you specify a @keydown decorated class higher
 * up in the view hierarchy for larger scoping of keydown events, or for
 * programmatically sending keydown events as props into the components in order
 * to trigger decorated methods with matching keys.
 *
 * @access public
 * @param {Array} ...args  All (or no) arguments passed in from decoration
 * @return {Function} The decorated class or method
 */
function keydownScoped( ...args ) {
  return _decorator( methodWrapperScoped, ...args );
}

/**
 * keydown
 *
 * The main decorator and default export, handles both classes and methods.
 *
 * @access public
 * @param {Array} ...args  All (or no) arguments passed in from decoration
 * @return {Function} The decorated class or method
 */
function keydown( ...args ) {
  return _decorator( method_decorator, ...args );
}

/* harmony default export */ const decorators = (keydown);



;// CONCATENATED MODULE: ./node_modules/keydown/src/index.js
// polyfill array.from (mainly for IE)


// @keydown and @keydownScoped


// setBinding - only useful if you're not going to use decorators


// Keys - use this to find key codes for strings. for example: Keys.j, Keys.enter


;// CONCATENATED MODULE: ../../Felamimail/js/GridPanel.js

var _dec, _dec2, _dec3, _dec4, _dec5, _dec6, _dec7, _dec8, _dec9, _dec10, _obj, _init, _init2;
/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2021 Metaways Infosystems GmbH (http://www.metaways.de)
 */

Ext.namespace('Tine.Felamimail');
__webpack_require__(46274);


/**
 * Message grid panel
 * 
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.GridPanel
 * @extends     Tine.widgets.grid.GridPanel
 * 
 * <p>Message Grid Panel</p>
 * <p><pre>
 * </pre></p>
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Felamimail.GridPanel
 */
Tine.Felamimail.GridPanel = Ext.extend(Tine.widgets.grid.GridPanel, (_dec = decorators(['ctrl+n', 'ctrl+m']), _dec2 = decorators('ctrl+l'), _dec3 = decorators('ctrl+r'), _dec4 = decorators('ctrl+shift+r'), _dec5 = decorators(['a', 'ctrl+s']), _dec6 = decorators(['ctrl+o', 'enter']), _dec7 = decorators('1'), _dec8 = decorators('m'), _dec9 = decorators('j'), _dec10 = decorators('shift+j'), (_obj = {
  /**
   * record class
   * @cfg {Tine.Felamimail.Model.Message} recordClass
   */
  recordClass: Tine.Felamimail.Model.Message,
  /**
   * message detail panel
   * 
   * @type Tine.Felamimail.GridDetailsPanel
   * @property detailsPanel
   */
  detailsPanel: null,
  /**
   * default panel region
   *
   * @property detailsPanelRegion
   */
  detailsPanelRegion: 'south',
  /**
   * transaction id of current delete message request
   * @type Number
   */
  deleteTransactionId: null,
  /**
   * this is true if messages are moved/deleted
   * 
   * @type Boolean
   */
  movingOrDeleting: false,
  manualRefresh: false,
  /**
   * @private model cfg
   */
  evalGrants: false,
  filterSelectionDelete: true,
  // autoRefresh is done via onUpdateFolderStore
  autoRefreshInterval: false,
  // needed for refresh after file messages
  listenMessageBus: true,
  // NOTE: initial loading is done by tree selection
  initialLoadAfterRender: false,
  /**
   * @private grid cfg
   */
  defaultSortInfo: {
    field: 'sent',
    direction: 'DESC'
  },
  gridConfig: {
    autoExpandColumn: 'subject',
    // drag n dropfrom
    enableDragDrop: true,
    ddGroup: 'mailToTreeDDGroup'
  },
  // we don't want to update the preview panel on context menu
  updateDetailsPanelOnCtxMenu: false,
  /**
   * needed to apply second grid state for send folders
   */
  sendFolderGridStateId: null,
  sentFolderSelected: false,
  /**
   * Return CSS class to apply to rows depending upon flags
   * - checks Flagged, Deleted and Seen
   * 
   * @param {Tine.Felamimail.Model.Message} record
   * @param {Integer} index
   * @return {String}
   */
  getViewRowClass: function (record, index) {
    var className = '';
    if (record.hasFlag('\\Flagged')) {
      className += 'flag_flagged ';
    }
    if (record.hasFlag('\\Deleted')) {
      className += 'flag_deleted ';
    }
    if (!record.hasFlag('\\Seen')) {
      className += 'flag_unread ';
    }
    if (record.get('is_spam_suspicions')) {
      className += 'is_spam_suspicions ';
    }
    return className;
  },
  /**
   * init message grid
   * @private
   */
  initComponent: function () {
    this.app = Tine.Tinebase.appMgr.get('Felamimail');
    this.i18nEmptyText = this.app.i18n._('No Messages found.');
    this.recordProxy = Tine.Felamimail.messageBackend;
    this.gridConfig.cm = new Ext.grid.ColumnModel({
      defaults: {
        resizable: true
      },
      columns: this.getColumns()
    });
    this.gridConfig.columns = this.getColumns();
    this.initDetailsPanel();
    this.pagingConfig = {
      doRefresh: this.doRefresh.createDelegate(this)
    };
    if (!this.readOnly) {
      this.plugins.push({
        ptype: 'ux.browseplugin',
        multiple: true,
        scope: this,
        enableFileDialog: false,
        handler: this.onFilesSelect.createDelegate(this)
      });
    }
    Tine.Felamimail.GridPanel.superclass.initComponent.call(this);
    this.grid.getSelectionModel().on('rowselect', this.onRowSelection, this);
    this.app.getFolderStore().on('update', this.onUpdateFolderStore, this);
    this.contextMenu.on('beforeshow', this.onDisplaySpamActions, this);
    this.initPagingToolbar();
    this.sendFolderGridStateId = this.gridConfig.stateId + '-SendFolder';
  },
  /**
   * add quota bar to paging toolbar
   */
  initPagingToolbar: function () {
    Ext.QuickTips.init();
    this.quotaBar = new Ext.Component({
      displayPriority: 50,
      style: {
        margin: '3px 10px',
        width: '100px',
        height: '16px'
      }
    });
    this.pagingToolbar.insert(12, new Ext.Toolbar.Separator());
    this.pagingToolbar.insert(13, this.quotaBar);
  },
  /**
   * cleanup on destruction
   */
  onDestroy: function () {
    this.app.getFolderStore().un('update', this.onUpdateFolderStore, this);
  },
  /**
   * folder store gets updated -> refresh grid if new messages arrived or messages have been removed
   * 
   * @param {Tine.Felamimail.FolderStore} store
   * @param {Tine.Felamimail.Model.Folder} record
   * @param {String} operation
   */
  onUpdateFolderStore: function (store, record, operation) {
    if (operation === Ext.data.Record.EDIT && record.isModified('cache_totalcount')) {
      var tree = this.app.getMainScreen().getTreePanel(),
        selectedNodes = tree ? tree.getSelectionModel().getSelectedNodes() : [];

      // only refresh if 1 or no messages are selected
      if (this.getGrid().getSelectionModel().getCount() <= 1) {
        var refresh = false;
        for (var i = 0; i < selectedNodes.length; i++) {
          if (selectedNodes[i].id == record.id) {
            refresh = true;
            break;
          }
        }

        // check if folder is in filter or allinboxes are selected and updated folder is an inbox
        if (!refresh) {
          var filters = this.filterToolbar.getValue();
          filters = filters.filters ? filter.filters : filters;
          for (var i = 0; i < filters.length; i++) {
            if (filters[i].field == 'path' && filters[i].operator == 'in') {
              if (filters[i].value.indexOf(record.get('path')) !== -1 || filters[i].value.indexOf('/allinboxes') !== -1 && record.isInbox()) {
                refresh = true;
                break;
              }
            }
          }
        }
        if (refresh && this.noDeleteRequestInProgress()) {
          Tine.log.debug('Refresh grid because of folder update.');
          this.loadGridData({
            removeStrategy: 'keepBuffered',
            autoRefresh: true
          });
        }
      }
    }
  },
  /**
   * init actions with actionToolbar, contextMenu and actionUpdater
   * 
   * @private
   */
  initActions: function () {
    // init felamimail specific actions
    this.action_spam = new Ext.Action({
      requiredGrant: 'editGrant',
      actionType: 'edit',
      text: this.app.i18n._('It is SPAM'),
      handler: this.processSpamStrategy.bind(this, 'spam'),
      iconCls: 'felamimail-action-spam',
      allowMultiple: true,
      hidden: !this.app.featureEnabled('featureSpamSuspicionStrategy')
    });
    this.action_ham = new Ext.Action({
      requiredGrant: 'editGrant',
      actionType: 'edit',
      text: this.app.i18n._('It is not SPAM'),
      handler: this.processSpamStrategy.bind(this, 'ham'),
      iconCls: 'felamimail-action-ham',
      allowMultiple: true,
      hidden: !this.app.featureEnabled('featureSpamSuspicionStrategy')
    });
    this.action_write = new Ext.Action({
      requiredGrant: 'addGrant',
      actionType: 'add',
      text: this.app.i18n._('Compose'),
      handler: this.onMessageCompose.createDelegate(this),
      // TODO reactivate when account becomes available as sometimes this stays deactivated
      disabled: !this.app.getActiveAccount(),
      iconCls: this.app.appName + 'IconCls'
    });
    this.action_reply = new Ext.Action({
      requiredGrant: 'readGrant',
      actionType: 'reply',
      text: this.app.i18n._('Reply'),
      handler: this.onMessageReplyTo.createDelegate(this, [false]),
      iconCls: 'action_email_reply',
      disabled: true
    });
    this.action_replyAll = new Ext.Action({
      requiredGrant: 'readGrant',
      actionType: 'replyAll',
      text: this.app.i18n._('Reply To All'),
      handler: this.onMessageReplyTo.createDelegate(this, [true]),
      iconCls: 'action_email_replyAll',
      disabled: true
    });
    this.action_forward = new Ext.Action({
      requiredGrant: 'readGrant',
      actionType: 'forward',
      text: this.app.i18n._('Forward'),
      handler: this.onMessageForward.createDelegate(this),
      iconCls: 'action_email_forward',
      disabled: true
    });
    this.action_flag = new Ext.Action({
      requiredGrant: 'readGrant',
      text: this.app.i18n._('Toggle highlighting'),
      handler: this.toggleMessageFlagged,
      scope: this,
      iconCls: 'action_email_flag',
      allowMultiple: true,
      disabled: true
    });
    this.action_markUnread = new Ext.Action({
      requiredGrant: 'readGrant',
      text: this.app.i18n._('Mark read/unread'),
      handler: this.toggleMessageUnread,
      scope: this,
      iconCls: 'action_mark_read',
      allowMultiple: true,
      disabled: true
    });
    this.action_moveRecord = new Ext.Action({
      requiredGrant: 'editGrant',
      allowMultiple: true,
      text: this.app.i18n._('Move'),
      disabled: true,
      actionType: 'edit',
      handler: this.onMoveRecords,
      scope: this,
      iconCls: 'action_move'
    });
    this.action_copyRecord = new Ext.Action({
      requiredGrant: 'editGrant',
      allowMultiple: true,
      text: this.app.i18n._('Copy'),
      disabled: true,
      actionType: 'edit',
      handler: this.onCopyRecords,
      scope: this,
      iconCls: 'action_editcopy'
    });
    this.action_fileRecord = new Tine.Felamimail.MessageFileAction({});
    this.action_importRecords = new Ext.Action({
      requiredGrant: 'addGrant',
      actionType: 'add',
      text: this.app.i18n._('Import Messages'),
      handler: this.onFilesSelect,
      disabled: true,
      scope: this,
      plugins: [{
        ptype: 'ux.browseplugin',
        multiple: true,
        enableFileDrop: false,
        disable: true
      }],
      iconCls: 'action_import',
      actionUpdater: function (action, grants, records, isFilterSelect, filteredContainers) {
        action.setDisabled(!this.getCurrentFolderFromTree());
      }.createDelegate(this)
    });
    this.action_addAccount = new Ext.Action({
      text: this.app.i18n._('Add Account'),
      handler: this.onAddAccount,
      iconCls: 'action_add',
      scope: this,
      disabled: !Tine.Tinebase.common.hasRight('add_accounts', 'Felamimail')
    });
    this.action_printPreview = new Ext.Action({
      requiredGrant: 'readGrant',
      text: this.app.i18n._('Print Preview'),
      handler: this.onPrintPreview.createDelegate(this, []),
      disabled: true,
      hidden: Ext.supportsPopupWindows,
      iconCls: 'action_printPreview',
      scope: this
    });
    this.action_print = new Ext.Action({
      requiredGrant: 'readGrant',
      text: this.app.i18n._('Print Message'),
      handler: this.onPrint.createDelegate(this, []),
      disabled: true,
      iconCls: 'action_print',
      scope: this,
      menu: {
        items: [this.action_printPreview]
      }
    });
    this.action_addTask = new Ext.Action({
      requiredGrant: 'readGrant',
      text: Tine.Tinebase.appMgr.isEnabled('Tasks') ? Tine.Tinebase.appMgr.get('Tasks').i18n._('Create New Task') : '',
      handler: this.onCreateTask,
      iconCls: 'action_addTask',
      scope: this,
      disabled: true,
      allowMultiple: false,
      hidden: !Tine.Tinebase.appMgr.isEnabled('Tasks')
    });

    // initial tagging actions from parent
    Tine.Felamimail.GridPanel.superclass.initActions.call(this);
    this.action_deleteRecord.setText(this.app.i18n._('Delete'));
    this.action_deleteRecord.initialConfig.translationObject = null;
    this.actionUpdater.addActions([this.action_deleteRecord, this.action_reply, this.action_replyAll, this.action_forward, this.action_fileRecord, this.action_importRecords, this.action_flag, this.action_markUnread, this.action_addAccount, this.action_print, this.action_printPreview, this.action_copyRecord, this.action_moveRecord, this.action_addTask, this.action_spam, this.action_ham, this.action_tagsMassAttach, this.action_tagsMassDetach]);
    this.contextMenu = new Ext.menu.Menu({
      plugins: [{
        ptype: 'ux.itemregistry',
        key: 'Tinebase-MainContextMenu'
      }],
      items: [this.action_reply, this.action_replyAll, this.action_forward, this.action_flag, this.action_markUnread, this.action_copyRecord, this.action_moveRecord, this.action_deleteRecord, this.action_fileRecord, this.action_addTask, this.action_spam, this.action_ham, this.action_tagsMassAttach, this.action_tagsMassDetach]
    });
  },
  // we don't delete messages, we move them to trash
  disableDeleteActionCheckServiceMap() {},
  /**
   * upload new file and add to store
   *
   * @param {ux.BrowsePlugin} fileSelector
   * @param {} e
   */
  onFilesSelect: async function (fileSelector, event) {
    const folder = this.getCurrentFolderFromTree();
    if (!folder) {
      return Ext.Msg.alert(this.app.i18n._('No target folder selected'), this.app.i18n._('You need to select a target folder for the messages.'));
    }
    const exts = _.uniq(_.map(fileSelector.files, file => {
      return file.name.split('.').pop();
    }));
    if (exts.length !== 1 || exts[0] !== 'eml') {
      return Ext.Msg.alert(this.app.i18n._('Wrong File Type'), this.app.i18n._('Files of type eml allowed only.'));
    }
    this.importMask = new Ext.LoadMask(this.grid.getEl(), {
      msg: i18n._('Importing Messages...')
    });
    this.importMask.show();
    const pms = _.map(fileSelector.files, file => {
      const upload = new Ext.ux.file.Upload({
        file: file
      });
      upload.upload();
      return upload.promise.then(upload => {
        const tempFile = upload.fileRecord.get('tempFile');
        return Tine.Felamimail.importMessage(folder.id, tempFile.id);
      });
    });
    await Promise.allSettled(pms);
    this.doRefresh();
    this.importMask.hide();
  },
  /**
   * initializes the filterPanel, overwrites the superclass method
   */
  initFilterPanel: function () {
    this.defaultFilters = [{
      field: 'query',
      operator: 'contains',
      value: ''
    }];
    this.filterToolbar = this.getFilterToolbar();
    this.filterToolbar.criteriaIgnores = [{
      field: 'query',
      operator: 'contains',
      value: ''
    }, {
      field: 'id'
    }, {
      field: 'path'
    }];
    this.plugins = this.plugins || [];
    this.plugins.push(this.filterToolbar);
  },
  /**
   * the details panel (shows message content)
   * 
   * @private
   */
  initDetailsPanel: function () {
    this.detailsPanel = new Tine.Felamimail.GridDetailsPanel({
      gridpanel: this,
      grid: this,
      app: this.app,
      i18n: this.app.i18n
    });
  },
  /**
   * get action toolbar
   * 
   * @return {Ext.Toolbar}
   */
  getActionToolbar: function () {
    if (!this.actionToolbar) {
      this.actionToolbar = new Ext.Toolbar({
        defaults: {
          height: 55
        },
        items: [{
          xtype: 'buttongroup',
          layout: 'toolbar',
          buttonAlign: 'left',
          columns: 6,
          items: [Ext.apply(new Ext.SplitButton(this.action_write), {
            scale: 'medium',
            rowspan: 2,
            iconAlign: 'top',
            arrowAlign: 'right',
            menu: new Ext.menu.Menu({
              items: [],
              plugins: [{
                ptype: 'ux.itemregistry',
                key: 'Tine.widgets.grid.GridPanel.addButton'
              }, {
                ptype: 'ux.itemregistry',
                key: 'Tinebase-MainContextMenu'
              }]
            })
          }), Ext.apply(new Ext.Button(this.action_deleteRecord), {
            scale: 'medium',
            rowspan: 2,
            iconAlign: 'top'
          }), Ext.apply(new Ext.Button(this.action_reply), {
            scale: 'medium',
            rowspan: 2,
            iconAlign: 'top'
          }), Ext.apply(new Ext.Button(this.action_replyAll), {
            scale: 'medium',
            rowspan: 2,
            iconAlign: 'top'
          }), Ext.apply(new Ext.Button(this.action_forward), {
            scale: 'medium',
            rowspan: 2,
            iconAlign: 'top'
          }), {
            xtype: 'buttongroup',
            buttonAlign: 'left',
            columns: 3,
            frame: false,
            items: [this.action_print, this.action_markUnread, this.action_addAccount, this.action_fileRecord, this.action_flag, this.action_importRecords]
          }]
        }, this.getActionToolbarItems()]
      });
      this.actionToolbar.on('resize', this.onActionToolbarResize, this, {
        buffer: 250
      });
      this.actionToolbar.on('show', this.onActionToolbarResize, this);
      if (this.filterToolbar && typeof this.filterToolbar.getQuickFilterField == 'function') {
        this.actionToolbar.add('->', this.filterToolbar.getQuickFilterField());
      }
    }
    return this.actionToolbar;
  },
  /**
   * returns cm
   * 
   * @private
   */
  getColumns: function () {
    return [{
      id: 'id',
      header: this.app.i18n._("Id"),
      width: 100,
      sortable: true,
      dataIndex: 'id',
      hidden: true
    }, {
      id: 'content_type',
      header: '<div class="action_attach tine-grid-row-action-icon"></div>',
      tooltip: this.app.i18n._("Attachments"),
      width: 12,
      sortable: true,
      dataIndex: 'has_attachment',
      renderer: this.attachmentRenderer
    }, {
      id: 'flags',
      header: this.app.i18n._("Flags"),
      width: 24,
      sortable: true,
      dataIndex: 'flags',
      align: 'center',
      renderer: this.flagRenderer
    }, {
      id: 'tags',
      header: this.app.i18n._("Tags"),
      width: 24,
      sortable: false,
      dataIndex: 'tags',
      align: 'center',
      renderer: Tine.Tinebase.common.tagsRenderer
    }, {
      id: 'subject',
      header: this.app.i18n._("Subject"),
      width: 300,
      sortable: true,
      dataIndex: 'subject'
    }, {
      id: 'from_email',
      header: this.app.i18n._("From (Email)"),
      width: 100,
      sortable: true,
      dataIndex: 'from_email'
    }, {
      id: 'from_name',
      header: this.app.i18n._("From (Name)"),
      width: 100,
      sortable: true,
      dataIndex: 'from_name'
    }, {
      id: 'sender',
      header: this.app.i18n._("Sender"),
      width: 100,
      sortable: true,
      dataIndex: 'sender',
      hidden: true
    }, {
      id: 'to',
      header: this.app.i18n._("To"),
      width: 150,
      sortable: true,
      dataIndex: 'to',
      hidden: true,
      renderer: Tine.Tinebase.common.emailRenderer
    }, {
      id: 'sent',
      header: this.app.i18n._("Sent"),
      width: 100,
      sortable: true,
      dataIndex: 'sent',
      renderer: Tine.Tinebase.common.dateTimeRenderer
    }, {
      id: 'received',
      header: this.app.i18n._("Received"),
      width: 100,
      sortable: true,
      dataIndex: 'received',
      hidden: true,
      renderer: Tine.Tinebase.common.dateTimeRenderer
    }, {
      id: 'folder_id',
      header: this.app.i18n._("Folder"),
      width: 100,
      sortable: true,
      dataIndex: 'folder_id',
      hidden: true,
      renderer: this.accountAndFolderRenderer.createDelegate(this)
    }, {
      id: 'size',
      header: this.app.i18n._("Size"),
      width: 80,
      sortable: true,
      dataIndex: 'size',
      hidden: true,
      renderer: Ext.util.Format.fileSize
    }];
  },
  /**
   * attachment column renderer
   * 
   * @param {String} value
   * @return {String}
   * @private
   */
  attachmentRenderer: function (value, metadata, record) {
    let result = '';
    if (value == 1) {
      result = '<div class="action_attach tine-grid-row-action-icon" />';
    }
    return result;
  },
  /**
   * get flag icon
   *
   * @private
   *
   * TODO  use spacer if first flag(s) is/are not set?
   * @param value
   * @param metadata
   * @param record
   * @param value
   * @param metadata
   * @param record
   */
  flagRenderer: function (value, metadata, record) {
    let result = '';
    const i18n = Tine.Tinebase.appMgr.get('Felamimail').i18n;
    const icons = record.getFlagIcons();
    Ext.each(icons, function (icon) {
      result += '<img class="FelamimailFlagIcon ' + (icon.cls || "") + '" src="' + icon.src + '" ext:qtip="' + Ext.util.Format.htmlEncode(icon.qtip) + '">';
    }, this);
    let fileLocations = record.get('fileLocations');
    if (_.isArray(fileLocations) && fileLocations.length) {
      const fileLocationText = Tine.Felamimail.MessageFileAction.getFileLocationText(fileLocations, '<br>');
      result += fileLocationText ? '<img class="FelamimailFlagIcon MessageFileIcon" src="images/icon-set/icon_download.svg" ' + 'ext:qtitle="' + Ext.util.Format.htmlEncode(i18n._('Filed as:')) + '"' + 'ext:qtip="' + Ext.util.Format.htmlEncode(fileLocationText) + '"' + '>' : '';
    }
    return result;
  },
  /**
   * returns account and folder globalname
   * 
   * @param {String} folderId
   * @param {Object} metadata
   * @param {Folder|Account} record
   * @return {String}
   */
  accountAndFolderRenderer: function (folderId, metadata, record) {
    var folderStore = this.app.getFolderStore(),
      account = this.app.getAccountStore().getById(record.get('account_id')),
      result = account ? account.get('name') : record.get('account_id'),
      folder = folderStore.getById(folderId);
    if (!folder) {
      folder = folderStore.getById(record.id);
      if (!folder) {
        // only account
        return result ? result : record.get('name');
      }
    }
    result += '/';
    if (folder) {
      result += folder.get('globalname');
    }
    return result;
  },
  /**
   * responsive content Renderer
   *
   * @param {String} folderId
   * @param {Object} metadata
   * @param {Folder|Account} record
   * @return {String}
   */
  responsiveRenderer: function (folderId, metadata, record) {
    var _record$data, _record$data2;
    const block = document.createElement('div');
    const flagIcons = record.getFlagIcons();
    const flagIconEls = flagIcons.map(icon => {
      const iconEl = document.createElement('img');
      iconEl.id = icon.name;
      iconEl.className = 'felamimail-message-icon ' + ((icon === null || icon === void 0 ? void 0 : icon.cls) || "");
      iconEl.src = (icon === null || icon === void 0 ? void 0 : icon.src) || '';
      iconEl.setAttribute('ext:qtip', Ext.util.Format.htmlEncode((icon === null || icon === void 0 ? void 0 : icon.qtip) || ''));
      if (icon !== null && icon !== void 0 && icon.visibility) iconEl.style.visibility = icon.visibility;
      return iconEl;
    });

    // unread icon
    let unreadIconEl = flagIconEls.find(item => item.id === 'seen');
    if (!unreadIconEl) {
      unreadIconEl = document.createElement('img');
      unreadIconEl.id = 'empty';
      unreadIconEl.className = 'felamimail-message-icon ';
      unreadIconEl.src = 'images/icon-set/empty.svg';
    }

    // sender
    const senderEl = document.createElement('div');
    senderEl.innerHTML = Ext.util.Format.htmlEncode(record.data.from_name ?? record.data.from_email);
    senderEl.setAttribute('ext:qtip', Ext.util.Format.htmlEncode(record.data.from_email));

    // recipient
    const recipientEl = document.createElement('div');
    const recipients = record.data.to.map(to => {
      return (to === null || to === void 0 ? void 0 : to.n_fileas) || (to === null || to === void 0 ? void 0 : to.email) || to;
    });
    const extra = recipients.length > 2 ? ' ...' : '';
    recipientEl.innerHTML = Ext.util.Format.htmlEncode(recipients.slice(0, 2).join(' & ') + extra);

    // attachment
    const attachmentEl = document.createElement('div');
    attachmentEl.innerHTML = '<div class="felamimail-message-icon action_attach tine-grid-row-action-icon"/>';
    attachmentEl.style.visibility = record !== null && record !== void 0 && (_record$data = record.data) !== null && _record$data !== void 0 && _record$data.has_attachment ? 'visible' : 'hidden';

    // receivedDate
    const receivedDateEl = document.createElement('div');
    const date = ((_record$data2 = record.data) === null || _record$data2 === void 0 ? void 0 : _record$data2.received) ?? '';
    let formattedDate = date;
    let qtip = date;
    if (Ext.isFunction(date.format)) {
      const isToday = date.format('Y-m-d') === new Date().format('Y-m-d');
      const isThisWeek = date.between(new Date().add(Date.DAY, -7), new Date());
      const isThisYear = date.format('Y') === new Date().format('Y');
      formattedDate = date.format('l').substr(0, 2) + Ext.util.Format.date(date, ' d/m');
      if (isThisWeek) formattedDate = date.format('l').substr(0, 2) + Ext.util.Format.date(date, ' d/m H:i');
      if (isToday) formattedDate = Ext.util.Format.date(date, 'H:i');
      if (!isThisYear) formattedDate = Ext.util.Format.date(date, 'd.m.Y');
      qtip = date.format('H:i:s d/m/y');
    }
    receivedDateEl.innerHTML = formattedDate;
    receivedDateEl.setAttribute('ext:qtip', Ext.util.Format.htmlEncode(qtip));

    // subject
    const subjectEl = document.createElement('div');
    subjectEl.innerHTML = Ext.util.Format.htmlEncode(record.data.subject);
    subjectEl.setAttribute('ext:qtip', Ext.util.Format.htmlEncode(record.data.subject));

    // flags
    const displayIcons = ['answered', 'passed', 'spam', 'encrypted', 'tine20'];
    const flags = flagIconEls.filter(iconEl => displayIcons.includes(iconEl.id));

    // tags
    const tagsEl = document.createElement('div');
    tagsEl.innerHTML = Tine.Tinebase.common.tagsRenderer(record.data.tags);
    const row1 = document.createElement('div');
    const row1Left = document.createElement('div');
    const row1Right = document.createElement('div');
    row1.className = 'felamimail-message-title';
    row1Left.className = 'felamimail-message-title-row';
    row1Right.className = 'felamimail-message-title-row';
    senderEl.className = 'felamimail-message-title-text-small';
    recipientEl.className = 'felamimail-message-title-text-small';
    row1Left.appendChild(unreadIconEl);
    if (this.sentFolderSelected) {
      row1Left.appendChild(recipientEl);
    } else {
      row1Left.appendChild(senderEl);
    }
    row1Right.appendChild(receivedDateEl);
    const row2 = document.createElement('div');
    const row2Left = document.createElement('div');
    const row2Right = document.createElement('div');
    row2.className = 'felamimail-message-title';
    row2Left.className = 'felamimail-message-title-row';
    row2Right.className = 'felamimail-message-title-row';
    row2Right.style.minWidth = '50px';
    subjectEl.className = 'felamimail-message-title-text-medium';
    row2Left.appendChild(attachmentEl);
    row2Left.appendChild(subjectEl);
    row2Right.appendChild(tagsEl);
    flags.forEach(flagEl => row2Right.appendChild(flagEl));
    row1.appendChild(row1Left);
    row1.appendChild(row1Right);
    row2.appendChild(row2Left);
    row2.appendChild(row2Right);
    block.appendChild(row1);
    block.appendChild(row2);
    return block.outerHTML;
  },
  /**
   * executed when user clicks refresh btn
   */
  doRefresh: function () {
    var folder = this.getCurrentFolderFromTree(),
      refresh = this.pagingToolbar.refresh;

    // refresh is explicit
    this.editBuffer = [];
    this.manualRefresh = true;
    if (folder) {
      refresh.disable();
      Tine.log.info('User forced mail check for folder "' + folder.get('localname') + '"');
      this.app.checkMails(folder, function () {
        refresh.enable();
        this.manualRefresh = false;
      });
    } else {
      this.filterToolbar.onFilterChange();
    }
  },
  /**
   * get currently selected folder from tree
   * @return {Tine.Felamimail.Model.Folder}
   */
  getCurrentFolderFromTree: function () {
    var tree = this.app.getMainScreen().getTreePanel(),
      node = tree ? tree.getSelectionModel().getSelectedNode() : null,
      folder = node ? this.app.getFolderStore().getById(node.id) : null;
    return folder;
  },
  /**
   * delete messages handler:
   * delete messages from trash/drafts folder, move messages to trash from other folders
   * 
   * @return {Boolean}
   */
  onDeleteRecords: function () {
    const account = this.app.getActiveAccount();
    const trashId = account ? account.getTrashFolderId() : null;
    const trash = trashId ? this.app.getFolderStore().getById(trashId) : null;
    const draftsFolderId = account.getSpecialFolderId('drafts_folder');
    const draftsFolder = draftsFolderId ? this.app.getFolderStore().getById(draftsFolderId) : null;
    let moveMessages;
    if (draftsFolder && draftsFolder.isCurrentSelection()) {
      moveMessages = false;
    } else {
      const trashConfigured = account.get('trash_folder');
      moveMessages = trash && !trash.isCurrentSelection() || !trash && trashConfigured;
    }
    return moveMessages ? this.moveSelectedMessages(trash, true, false) : this.deleteSelectedMessages();
  },
  /**
   * move messages handler
   *
   * @return {void}
   */
  onMoveRecords: function () {
    var selectPanel = Tine.Felamimail.FolderSelectPanel.openWindow({
      allAccounts: true,
      listeners: {
        scope: this,
        folderselect: function (node) {
          var folder = new Tine.Felamimail.Model.Folder(node.attributes, node.attributes.id);
          this.moveSelectedMessages(folder, false, false);
          selectPanel.close();
        }
      }
    });
  },
  /**
   * copy messages handler
   *
   * @return {void}
   */
  onCopyRecords: function () {
    var selectPanel = Tine.Felamimail.FolderSelectPanel.openWindow({
      allAccounts: true,
      listeners: {
        scope: this,
        folderselect: function (node) {
          var folder = new Tine.Felamimail.Model.Folder(node.attributes, node.attributes.id);
          this.moveSelectedMessages(folder, false, true);
          selectPanel.close();
        }
      }
    });
  },
  /**
   * file selected messages to Filemanager
   */
  onFileRecords: function () {
    var filePicker = new Tine.Filemanager.FilePickerDialog({
      windowTitle: this.app.i18n._('Select Message File Location'),
      singleSelect: true,
      requiredGrants: ['addGrant'],
      constraint: 'folder'
    });
    filePicker.on('selected', function (node) {
      this.fileRecords('Filemanager', node[0].path);
    }, this);
    filePicker.openWindow();
  },
  /**
   * file messages
   *
   * @param appName
   * @param path
   */
  fileRecords: function (appName, path) {
    var sm = this.getGrid().getSelectionModel(),
      filter = sm.getSelectionFilter();
    this.fileMessagesLoadMask = new Ext.LoadMask(Ext.getBody(), {
      msg: this.app.i18n._('Filing Messages')
    });
    this.fileMessagesLoadMask.show();
    Ext.Ajax.request({
      params: {
        method: 'Felamimail.fileMessages',
        filterData: filter,
        targetApp: appName,
        targetPath: path
      },
      timeout: 3600000,
      // 1 hour
      scope: this,
      success: function (result, request) {
        this.afterFileRecords(result, request);
      },
      failure: function (response, request) {
        var responseText = Ext.util.JSON.decode(response.responseText),
          exception = responseText.data;
        this.afterFileRecords(response, request, exception);
      }
    });
  },
  /**
   * show feedback when message filing has been (un)successful
   *
   * TODO reload grid when request returns?
   */
  afterFileRecords: function (result, request, error) {
    Tine.log.info('Tine.Felamimail.GridPanel::afterFileRecords');
    Tine.log.debug(result);
    this.fileMessagesLoadMask.hide();
    if (error) {
      Ext.Msg.show({
        title: this.app.i18n._('Error Filing Message'),
        msg: error.message ? error.message : this.app.i18n._('Could not file message.'),
        icon: Ext.MessageBox.ERROR,
        buttons: Ext.Msg.OK
      });
    }
  },
  /**
   * permanently delete selected messages
   */
  deleteSelectedMessages: function () {
    this.moveOrDeleteMessages(null);
  },
  /**
   * move selected messages to given folder
   * 
   * @param {Tine.Felamimail.Model.Folder} folder
   * @param {Boolean} toTrash
   * @param {Boolean} keepOriginalMessages
   */
  moveSelectedMessages: function (folder, toTrash, keepOriginalMessages) {
    if (folder && folder.isCurrentSelection()) {
      // nothing to do ;-)
      return;
    }
    this.moveOrDeleteMessages(folder, toTrash, keepOriginalMessages);
  },
  /**
   * move (folder !== null) or delete selected messages 
   * 
   * @param {Tine.Felamimail.Model.Folder} folder
   * @param {Boolean} toTrash
   * @param {Boolean} keepOriginalMessages
   */
  moveOrDeleteMessages: function (folder, toTrash, keepOriginalMessages) {
    // this is needed to prevent grid reloads while messages are moved or deleted
    this.movingOrDeleting = true;
    var sm = this.getGrid().getSelectionModel(),
      filter = sm.getSelectionFilter(),
      msgsIds = [];
    if (sm.isFilterSelect) {
      var msgs = this.getStore(),
        nextRecord = null;
    } else {
      var msgs = sm.getSelectionsCollection(),
        nextRecord = this.getNextMessage(msgs);
    }
    var increaseUnreadCountInTargetFolder = 0;
    msgs.each(function (msg) {
      var isSeen = msg.hasFlag('\\Seen'),
        currFolder = this.app.getFolderStore().getById(msg.get('folder_id')),
        diff = isSeen ? 0 : 1;
      if (currFolder) {
        currFolder.set('cache_unreadcount', currFolder.get('cache_unreadcount') - diff);
        currFolder.set('cache_totalcount', currFolder.get('cache_totalcount') - 1);
        currFolder.set('cache_status', 'pending');
        currFolder.commit();
      }
      increaseUnreadCountInTargetFolder += diff;
      msgsIds.push(msg.id);
      if (!keepOriginalMessages) {
        this.getStore().remove(msg);
      }
    }, this);
    if (folder) {
      // update unread count of target folder (only when moving)
      folder.set('cache_unreadcount', folder.get('cache_unreadcount') + increaseUnreadCountInTargetFolder);
      folder.set('cache_status', 'pending');
      folder.commit();
    }
    if (!keepOriginalMessages) {
      this.deleteQueue = this.deleteQueue.concat(msgsIds);
    }
    if (nextRecord !== null) {
      sm.selectRecords([nextRecord]);
    }
    this.app.checkMailsDelayedTask.delay(1000);
    var callbackFn = this.onAfterDelete.createDelegate(this, [msgsIds]);
    if (folder !== null || toTrash) {
      // move
      var targetFolderId = toTrash ? '_trash_' : folder.id;
      this.deleteTransactionId = Tine.Felamimail.messageBackend.moveMessages(filter, targetFolderId, keepOriginalMessages, {
        callback: callbackFn
      });
    } else {
      // delete
      this.deleteTransactionId = Tine.Felamimail.messageBackend.addFlags(filter, '\\Deleted', {
        callback: callbackFn
      });
    }
  },
  /**
   * get next message in grid
   * 
   * @param msgs
   * @return Tine.Felamimail.Model.Message
   */
  getNextMessage: function (msgs) {
    var nextRecord = null;
    if (msgs.getCount() >= 1 && this.getStore().getCount() > 1) {
      if (msgs.getCount() === 1) {
        // select next message (or previous if it was the last or BACKSPACE)
        var lastIdx = this.getStore().indexOf(msgs.last()),
          direction = Ext.EventObject.getKey() == Ext.EventObject.BACKSPACE ? -1 : +1;
        nextRecord = this.getStore().getAt(lastIdx + 1 * direction);
        if (!nextRecord) {
          nextRecord = this.getStore().getAt(lastIdx + -1 * direction);
        }
      }
      if (msgs.getCount() > 1) {
        const sm = this.getGrid().getSelectionModel();
        msgs.each(function (msg) {
          let idx = this.getStore().indexOfId(msg.id);
          nextRecord = this.getStore().getAt(idx + 1);

          // return the first unselected row as nextRecord
          if (!sm.isSelected(idx + 1)) {
            return false;
          }

          //select the previous row before the first selected one, if user select till the last row
          if (idx === this.getStore().getCount() - 1) {
            idx = this.getStore().indexOfId(msgs.items[0].id);
            nextRecord = this.getStore().getAt(idx - 1) || null;
            return false;
          }
        }, this);
      }
    }
    return nextRecord;
  },
  /**
   * executed after a msg compose
   * 
   * @param {String} composedMsg
   * @param {String} action
   * @param {Array}  [affectedMsgs]  messages affected 
   * @param {String} [mode]
   */
  onAfterCompose: function (composedMsg, action, affectedMsgs, mode) {
    Tine.log.debug('Tine.Felamimail.GridPanel::onAfterCompose / arguments:');
    Tine.log.debug(arguments);

    // mark send folders cache status incomplete
    composedMsg = Ext.isString(composedMsg) ? new this.recordClass(Ext.decode(composedMsg)) : composedMsg;

    // NOTE: if affected messages is decoded, we need to fetch the originals out of our store
    if (Ext.isString(affectedMsgs)) {
      var msgs = [],
        store = this.getStore();
      Ext.each(Ext.decode(affectedMsgs), function (msgData) {
        var msg = store.getById(msgData.id);
        if (msg) {
          msgs.push(msg);
        }
      }, this);
      affectedMsgs = msgs;
    }
    var composerAccount = this.app.getAccountStore().getById(composedMsg.get('account_id')),
      sendFolderId = composerAccount ? composerAccount.getSendFolderId() : null,
      sendFolder = sendFolderId ? this.app.getFolderStore().getById(sendFolderId) : null;
    if (sendFolder) {
      sendFolder.set('cache_status', 'incomplete');
    }
    if (Ext.isArray(affectedMsgs)) {
      Ext.each(affectedMsgs, function (msg) {
        if (['reply', 'forward'].indexOf(action) !== -1) {
          msg.addFlag(action === 'reply' ? '\\Answered' : 'Passed');
        } else if (action === 'senddraft') {
          this.deleteTransactionId = Tine.Felamimail.messageBackend.addFlags(msg.id, '\\Deleted', {
            callback: this.onAfterDelete.createDelegate(this, [[msg.id]])
          });
        }
      }, this);
    }
  },
  /**
   * executed after msg delete
   * 
   * @param {Array} [ids]
   */
  onAfterDelete: function (ids) {
    this.deleteQueue = this.deleteQueue.diff(ids);
    this.editBuffer = this.editBuffer.diff(ids);
    this.movingOrDeleting = false;
    Tine.log.debug('Tine.Felamimail.GridPanel::onAfterDelete() -> Loading grid data after delete.');
    this.loadGridData({
      removeStrategy: 'keepBuffered',
      autoRefresh: true
    });
  },
  /**
   * check if delete/move action is running atm
   * 
   * @return {Boolean}
   */
  noDeleteRequestInProgress: function () {
    return !this.movingOrDeleting && (!this.deleteTransactionId || !Tine.Felamimail.messageBackend.isLoading(this.deleteTransactionId));
  },
  onMessageCompose: function () {
    var activeAccount = Tine.Tinebase.appMgr.get('Felamimail').getActiveAccount();
    var win = Tine.Felamimail.MessageEditDialog.openWindow({
      accountId: activeAccount ? activeAccount.id : null,
      listeners: {
        'update': this.onAfterCompose.createDelegate(this, ['compose', []], 1)
      }
    });
  },
  onMessageForward: function () {
    var sm = this.getGrid().getSelectionModel(),
      msgs = sm.getSelections(),
      msgsData = [];
    Ext.each(msgs, function (msg) {
      msgsData.push(msg.data);
    }, this);
    if (sm.getCount() > 0) {
      var win = Tine.Felamimail.MessageEditDialog.openWindow({
        forwardMsgs: Ext.encode(msgsData),
        listeners: {
          'update': this.onAfterCompose.createDelegate(this, ['forward', msgs], 1)
        }
      });
    }
  },
  replyMessage() {
    return this.getGrid().getSelectionModel().getSelected() ? this.onMessageReplyTo(false) : false;
  },
  replyMessageAll() {
    return this.getGrid().getSelectionModel().getSelected() ? this.onMessageReplyTo(true) : false;
  },
  fileMessage() {
    return this.getGrid().getSelectionModel().getSelected() ? this.action_fileRecord.handler() : false;
  },
  /**
   * reply message handler
   * 
   * @param {bool} toAll
   */
  onMessageReplyTo: function (toAll) {
    var sm = this.getGrid().getSelectionModel(),
      msg = sm.getSelected();
    if (!msg) return;
    Tine.Felamimail.MessageEditDialog.openWindow({
      replyTo: Ext.encode(msg.data),
      replyToAll: toAll,
      listeners: {
        'update': this.onAfterCompose.createDelegate(this, ['reply', [msg]], 1)
      }
    });
  },
  /**
   * called when a row gets selected
   * 
   * @param {SelectionModel} sm
   * @param {Number} rowIndex
   * @param {Tine.Felamimail.Model.Message} record
   * @param {Number} retryCount
   * 
   * TODO find a better way to check if body is fetched, this does not work correctly if a message is removed
   *       and the next one is selected automatically
   */
  onRowSelection: function (sm, rowIndex, record, retryCount) {
    if (sm.getCount() == 1 && (!retryCount || retryCount < 5) && !record.bodyIsFetched()) {
      Tine.log.debug('Tine.Felamimail.GridPanel::onRowSelection() -> Deferring onRowSelection');
      retryCount = retryCount ? retryCount++ : 1;
      return this.onRowSelection.defer(250, this, [sm, rowIndex, record, retryCount + 1]);
    }
    if (sm.getCount() == 1 && sm.isIdSelected(record.id) && !record.hasFlag('\\Seen')) {
      Tine.log.debug('Tine.Felamimail.GridPanel::onRowSelection() -> Selected unread message');
      Tine.log.debug(record);
      if (Tine.Felamimail.registry.get('preferences').get('markEmailRead') === 1) {
        setTimeout(() => {
          record.addFlag('\\Seen');
          record.mtime = new Date().getTime();
          Tine.Felamimail.messageBackend.addFlags(record.id, '\\Seen');
          this.app.getMainScreen().getTreePanel().decrementCurrentUnreadCount();
        }, 2000);
      }
      if (record.get('headers')['disposition-notification-to']) {
        Ext.Msg.confirm(this.app.i18n._('Send Reading Confirmation'), this.app.i18n._('Do you want to send a reading confirmation message?'), function (btn) {
          if (btn == 'yes') {
            Tine.Felamimail.sendReadingConfirmation(record.id);
          }
        }, this);
      }
    }
  },
  /**
   * open first file location when file icon is clicked
   */
  onRowClick: function (grid, row, e) {
    if (e.getTarget('.MessageFileIcon')) {
      let record = this.getStore().getAt(row);
      let fileLocation = record.get('fileLocations')[0];
      Tine.Felamimail.MessageFileAction.locationClickHandler(fileLocation.model, fileLocation.record_id);
      e.stopEvent();
    } else if (e.getTarget('.action_attach')) {
      if (Tine.Tinebase.appMgr.isEnabled('Filemanager')) {
        const me = this;
        Tine.Filemanager.QuickLookPanel.openWindow({
          record: this.getStore().getAt(row),
          initialApp: this.app,
          sm: grid.getSelectionModel(),
          handleAttachments: Tine.Felamimail.MailDetailsPanel.prototype.quicklookHandleAttachments
        });
      }
    } else {
      Tine.Felamimail.GridPanel.superclass.onRowClick.apply(this, arguments);
    }
  },
  openMessage() {
    return this.getGrid().getSelectionModel().getSelected() ? this.onRowDblClick(this.grid) : false;
  },
  /**
   * row doubleclick handler
   * 
   * - opens message edit dialog (if draft/template)
   * - opens message display dialog (everything else)
   * 
   * @param {Tine.Felamimail.GridPanel} grid
   */
  onRowDblClick: function (grid) {
    var record = this.grid.getSelectionModel().getSelected(),
      folder = this.app.getFolderStore().getById(record.get('folder_id')),
      account = this.app.getAccountStore().getById(folder.get('account_id')),
      action = folder.get('globalname') == account.get('drafts_folder') ? 'senddraft' : folder.get('globalname') == account.get('templates_folder') ? 'sendtemplate' : null,
      win;

    // check folder to determine if mail should be opened in compose dlg
    if (action !== null) {
      win = Tine.Felamimail.MessageEditDialog.openWindow({
        draftOrTemplate: Ext.encode(record.data),
        listeners: {
          scope: this,
          'update': this.onAfterCompose.createDelegate(this, [action, [record]], 1)
        }
      });
    } else {
      win = Tine.Felamimail.MessageDisplayDialog.openWindow({
        record: Ext.encode(record.data),
        listeners: {
          scope: this,
          'update': this.onAfterCompose.createDelegate(this, ['compose', []], 1),
          'remove': this.onRemoveInDisplayDialog
        }
      });
    }
  },
  /**
   * message got removed in display dialog
   * 
   * @param {} msgData
   */
  onRemoveInDisplayDialog: function (msgData) {
    var msg = this.getStore().getById(Ext.decode(msgData).id),
      folderId = msg ? msg.get('folder_id') : null,
      folder = folderId ? this.app.getFolderStore().getById(folderId) : null,
      accountId = folder ? folder.get('account_id') : null,
      account = accountId ? this.app.getAccountStore().getById(accountId) : null;
    this.getStore().remove(msg);
    this.onAfterDelete(null);
  },
  /**
   * called when the store gets updated
   * 
   * NOTE: we only allow updateing flags BUT the actual updating is done 
   *       directly from the UI fn's to support IMAP optimised bulk actions
   */
  onStoreUpdate: function (store, record, operation) {
    if (operation === Ext.data.Record.EDIT && record.isModified('flags')) {
      record.commit();
    }
  },
  toggleMessageFlagged() {
    return this.getGrid().getSelectionModel().getSelected() ? this.onToggleFlag('\\Flagged') : false;
  },
  toggleMessageUnread() {
    return this.getGrid().getSelectionModel().getSelected() ? this.onToggleFlag('\\Seen') : false;
  },
  /**
   * toggle flagged status of mail(s)
   * - Flagged/Seen
   *
   * @param {String} flag
   * @param {Boolean} flagged
   */
  onToggleFlag: function (flag, flagged) {
    var sm = this.getGrid().getSelectionModel(),
      filter = sm.getSelectionFilter(),
      msgs = sm.isFilterSelect ? this.getStore() : sm.getSelectionsCollection(),
      flagCount = 0;

    // switch all msgs to one state -> toogle most of them
    msgs.each(function (msg) {
      flagCount += msg.hasFlag(flag) ? 1 : 0;
    });
    var action = flagCount >= Math.round(msgs.getCount() / 2) ? 'clear' : 'add';
    action = _.isBoolean(flagged) ? flagged ? 'add' : 'clear' : action;
    Tine.log.info('Tine.Felamimail.GridPanel::onToggleFlag - Toggle flag for ' + msgs.getCount() + ' message(s): ' + flag);

    // mark messages in UI and add to edit buffer
    msgs.each(function (msg) {
      // update unreadcount
      if (flag === '\\Seen') {
        var isSeen = msg.hasFlag('\\Seen'),
          folder = this.app.getFolderStore().getById(msg.get('folder_id')),
          diff = action === 'clear' && isSeen ? 1 : action === 'add' && !isSeen ? -1 : 0;
        if (folder) {
          folder.set('cache_unreadcount', folder.get('cache_unreadcount') + diff);
          if (sm.isFilterSelect && sm.getCount() > 50 && folder.get('cache_status') !== 'pending') {
            Tine.log.debug('Tine.Felamimail.GridPanel::onToggleFlag - Set cache status to pending for folder ' + folder.get('globalname'));
            folder.set('cache_status', 'pending');
          }
          folder.commit();
        }
      }
      msg[action + 'Flag'](flag);
      this.addToEditBuffer(msg);
    }, this);
    if (sm.isFilterSelect && sm.getCount() > 50) {
      Tine.log.debug('Tine.Felamimail.GridPanel::moveOrDeleteMessages - Update message cache for "pending" folders');
      this.app.checkMailsDelayedTask.delay(1000);
    }

    // do request
    Tine.Felamimail.messageBackend[action + 'Flags'](filter, flag);
  },
  /**
   * called before store queries for data
   */
  onStoreBeforeload: function (store, options) {
    this.supr().onStoreBeforeload.call(this, store, options);
    if (!Ext.isEmpty(this.deleteQueue)) {
      options.params.filter.push({
        field: 'id',
        operator: 'notin',
        value: this.deleteQueue
      });
    }

    // make sure, our handler is called just before the request is sent
    this.onStoreBeforeLoadFolderChange(store, options);
  },
  onStoreBeforeLoadFolderChange: function (store, options) {
    this.updateGridState();
    this.updateDefaultfilter(options.params, this.sentFolderSelected);
  },
  resolveSendFolderPath: function (params) {
    const pathFilter = _.find(this.latestFilter, {
      field: 'path'
    });
    const operator = _.get(pathFilter, 'operator', '');
    const value = operator.match(/equals|in/) ? _.get(pathFilter, 'value', null) : null;
    const pathFilterValue = value && _.isArray(value) ? value[0] : value;
    const isSendFolderPath = this.isSendFolderPath(pathFilterValue);
    if (isSendFolderPath && !this.sentFolderSelected) {
      this.sentFolderSelected = true;
    } else if (!isSendFolderPath && this.sentFolderSelected) {
      this.sentFolderSelected = false;
    }
  },
  isSendFolderPath: function (pathFilterValue) {
    const pathParts = _.isString(pathFilterValue) ? pathFilterValue.split('/') : null;
    if (!pathParts || pathParts.length < 3) {
      return false;
    }
    const composerAccount = this.app.getAccountStore().getById(pathParts[1]);
    const sendFolderIds = composerAccount ? [composerAccount.getSendFolderId(), composerAccount.getSpecialFolderId('templates_folder'), composerAccount.getSpecialFolderId('drafts_folder')] : null;
    return -1 !== sendFolderIds.indexOf(pathParts[2]);
  },
  /**
   * TODO: make quick filter search "to" in be , and remove all the filter switch in fe
   * add custom filter before or after store load
   *
   * @param params
   * @param isSentFolder
   *
   */
  updateDefaultfilter: function (params, isSentFolder) {
    var _params$filter, _params$filter$, _params$filter$$filte, _params$filter$$filte2;
    return; // not longer needed as the query filter now contains 'to' as well
    let targetFilters = params === null || params === void 0 ? void 0 : (_params$filter = params.filter) === null || _params$filter === void 0 ? void 0 : (_params$filter$ = _params$filter[0]) === null || _params$filter$ === void 0 ? void 0 : (_params$filter$$filte = _params$filter$.filters) === null || _params$filter$$filte === void 0 ? void 0 : (_params$filter$$filte2 = _params$filter$$filte[0]) === null || _params$filter$$filte2 === void 0 ? void 0 : _params$filter$$filte2.filters;
    if (!targetFilters) return;
    const defaultFilterField = isSentFolder ? 'to_list' : 'query';
    const existingDefaultFilter = _.find(targetFilters, {
      field: defaultFilterField
    });
    if (!existingDefaultFilter) {
      targetFilters.push({
        field: defaultFilterField,
        operator: 'contains',
        value: ''
      });
    }
    params.filter[0].filters[0].filters = targetFilters;
  },
  /**
   * // if send, draft, template folders are selected, do the following:
   * // - hide from email + name columns from grid
   * // - show to column in grid
   * // - save this state
   * // - if grid state is changed by user, do not change columns by user
   *
   * // if switched from send, draft, template to "normal" folder
   * // - switch to default state
   */
  updateGridState: function () {
    var _stateClonedResolved;
    this.resolveSendFolderPath();
    let stateId = this.sentFolderSelected ? this.sendFolderGridStateId : this.gridConfig.stateId;
    const isEastLayout = this.detailsPanelRegion === 'east';
    if (isEastLayout && !stateId.includes('_DetailsPanel_East')) stateId = stateId + '_DetailsPanel_East';
    const isStateIdChanged = this.grid.stateId !== stateId;
    if (!Ext.state.Manager.get(stateId)) this.grid.saveState();
    this.grid.stateId = stateId;
    const stateIdDefault = isEastLayout ? stateId : this.gridConfig.stateId;
    const stateStored = Ext.state.Manager.get(stateIdDefault);
    const stateCurrent = this.grid.getState();
    let stateCloned = stateStored;
    if (!isStateIdChanged || !stateStored) stateCloned = stateCurrent;
    let stateClonedResolved = JSON.parse(JSON.stringify(stateCloned));
    if ((_stateClonedResolved = stateClonedResolved) !== null && _stateClonedResolved !== void 0 && _stateClonedResolved.columns && isEastLayout) {
      const stateClonedResolvedVisibleColumns = stateClonedResolved.columns.filter(col => {
        return !col.hidden;
      });
      if (stateClonedResolvedVisibleColumns.length === 0) {
        const restoreStateId = stateId.replace('_DetailsPanel_East', '');
        const restoreState = Ext.state.Manager.get(restoreStateId);
        const restoreStateVisibleColumns = restoreState.columns.filter(col => {
          return !col.hidden;
        });
        if (restoreStateVisibleColumns === 0) {
          stateClonedResolved.columns.forEach(c => {
            c.hidden = c.id === 'responsive';
          });
        } else {
          stateClonedResolved.columns.forEach((c, idx) => {
            c.hidden = restoreState.columns[idx].hidden ?? false;
          });
        }
      }
    }
    if (stateId.includes(this.sendFolderGridStateId)) {
      let refState = Ext.state.Manager.get(stateId);
      if (refState) stateClonedResolved = JSON.parse(JSON.stringify(refState));

      // - hide from email + name columns from grid
      // - show to column in grid
      const customHideCols = {
        'from_email': true,
        'from_name': true,
        'to': false
      };
      //overwrite custom states
      _.each(customHideCols, (isHidden, colId) => {
        const idx = _.findIndex(stateClonedResolved.columns, {
          id: colId
        });
        isHidden = !refState ? isHidden : _.get(_.find(refState.columns, {
          id: colId
        }), 'hidden', false);
        if (idx > -1 && isHidden !== _.get(stateClonedResolved.columns[idx], 'hidden', false)) {
          if (isHidden) {
            stateClonedResolved.columns[idx].hidden = true;
          } else {
            delete stateClonedResolved.columns[idx].hidden;
          }
        }
      });
    }
    if (!isStateIdChanged) {
      stateClonedResolved.sort = this.store.getSortState();
    }
    this.grid.applyState(stateClonedResolved);
    // save state
    this.grid.saveState();
    if (isStateIdChanged) this.getView().refresh(true);
  },
  /**
   *  called after a new set of Records has been loaded
   *  
   * @param  {Ext.data.Store} this.store
   * @param  {Array}          loaded records
   * @param  {Array}          load options
   * @return {Void}
   */
  onStoreLoad: function (store, records, options) {
    this.supr().onStoreLoad.apply(this, arguments);
    Tine.log.debug('Tine.Felamimail.GridPanel::onStoreLoad(): store loaded new records.');
    let folder = this.getCurrentFolderFromTree();
    if (folder && folder.get('imap_totalcount') !== folder.get('cache_totalcount')) {
      Tine.log.debug('Tine.Felamimail.GridPanel::onStoreLoad() - Count mismatch: got ' + folder.get('imap_totalcount') + ' records for folder ' + folder.get('globalname'));
      Tine.log.debug(folder);
      folder.set('cache_status', 'pending');
      folder.commit();
      this.app.checkMailsDelayedTask.delay(1000);
    }
    this.latestFilter = _.get(options.params, 'filter[0].filters[0].filters', {});
    this.updateDefaultfilter(options.params, this.sentFolderSelected);
    this.updateQuotaBar();
    this.updateGridState();
  },
  /**
   * update quotaBar / only do it if we have a path filter with a single account id
   * 
   * @param {Record} accountInbox
   */
  updateQuotaBar: function (accountInbox) {
    var accountId = this.extractAccountIdFromFilter();
    if (accountId === null) {
      Tine.log.debug('No or multiple account ids in filter. Resetting quota bar.');
      this.quotaBar.hide();
      return;
    }
    if (!accountInbox) {
      var accountInbox = this.app.getFolderStore().queryBy(function (folder) {
        return folder.isInbox() && folder.get('account_id') == accountId;
      }, this).first();
    }
    if (accountInbox && parseInt(accountInbox.get('quota_limit'), 10) && accountId == accountInbox.get('account_id')) {
      Tine.log.debug('Showing quota info.');
      var limit = parseInt(accountInbox.get('quota_limit'), 10) / 1024,
        usage = parseInt(accountInbox.get('quota_usage'), 10) * 1024;
      this.quotaBar.show();
      this.quotaBar.update(Tine.widgets.grid.QuotaRenderer(usage, limit, /*use SoftQuota*/false));
    } else {
      Tine.log.debug('No account inbox found or no quota info found.');
      this.quotaBar.hide();
    }
  },
  /**
   * get account id from filter (only returns the id if a single account id was found)
   * 
   * @param {Array} filter
   * @return {String}
   */
  extractAccountIdFromFilter: function (filter) {
    if (!filter) {
      filter = this.filterToolbar.getValue();
    }

    // use first OR panel in case of filterPanel
    Ext.each(filter, function (filterData) {
      if (filterData.condition && filterData.condition == 'OR') {
        filter = filterData.filters[0].filters;
        return false;
      }
    }, this);

    // condition from filterPanel
    while (filter.filters || Ext.isArray(filter) && filter.length > 0 && filter[0].filters) {
      filter = filter.filters ? filter.filters : filter[0].filters;
    }
    var accountId = null,
      filterAccountId = null,
      accountIdMatch = null;
    for (var i = 0; i < filter.length; i++) {
      if (filter[i].field == 'path' && filter[i].operator == 'in') {
        for (var j = 0; j < filter[i].value.length; j++) {
          accountIdMatch = filter[i].value[j].match(/^\/([a-z0-9]*)/i);
          if (accountIdMatch) {
            filterAccountId = accountIdMatch[1];
            if (accountId && accountId != filterAccountId) {
              // multiple different account ids found!
              return null;
            } else {
              accountId = filterAccountId;
            }
          }
        }
      }
    }
    return accountId;
  },
  /**
   * add new account button
   * 
   * @param {Button} button
   * @param {Event} event
   */
  onAddAccount: function (button, event) {
    // it is only allowed to create user (external) accounts here
    var newAccount = new Tine.Felamimail.Model.Account({
      type: 'user'
    });
    // this is a little bit clunky but seems to be required to prevent record loading in AccountEditDialog
    newAccount.id = null;

    // make sure accountStore is initialised
    this.app.getAccountStore();
    var popupWindow = Tine.Felamimail.AccountEditDialog.openWindow({
      record: newAccount
    });
  },
  /**
   * create task handler
   *
   * @param {Button} button
   * @param {Event} event
   */
  onCreateTask: function (button, event) {
    const sm = this.getGrid().getSelectionModel();
    const msgs = sm.isFilterSelect ? this.getStore() : sm.getSelectionsCollection();
    if (msgs.length !== 1) {
      return;
    }
    const msg = msgs.items[0];
    if (!(msg !== null && msg !== void 0 && msg.data)) {
      return;
    }
    this.setIconClass('x-btn-wait');
    const popupWindow = Tine.Tasks.TaskEditDialog.openWindow({
      contentPanelConstructorInterceptor: async config => {
        const isTaskEnabled = Tine.Tinebase.appMgr.isEnabled('Tasks');
        const waitingText = isTaskEnabled ? Tine.Tinebase.appMgr.get('Tasks').i18n._('Creating new Task...') : 'Creating new Task...';
        const mask = await config.setWaitText(waitingText);
        const messageData = msg.data;
        const body = Tine.Tinebase.common.html2text((messageData === null || messageData === void 0 ? void 0 : messageData.body) || '');
        const subject = messageData !== null && messageData !== void 0 && messageData.subject ? messageData.subject : isTaskEnabled ? Tine.Tinebase.appMgr.get('Tasks').i18n._('New Task') : 'New Task';
        config.record = Tine.Tinebase.data.Record.setFromJson(await Tine.Tasks.saveTask(Ext.apply(Tine.Tasks.Model.Task.getDefaultData(), {
          summary: subject,
          description: body
        })), Tine.Tasks.Model.Task);

        // a quick hack to prevent duplicated popupwindow
        config.window.manager.unregister(config.window);
        config.window.id = config.window.name = Tine.Tasks.TaskEditDialog.prototype.windowNamePrefix + config.record.id;
        config.window.manager.register(config.window);
        const messageFilter = [{
          field: 'id',
          operator: 'in',
          value: [msg.data.id]
        }];
        const locations = [{
          type: 'attachment',
          model: 'Tasks_Model_Task',
          record_id: config.record.data,
          record_title: config.record.getTitle()
        }];
        await Tine.Felamimail.fileMessages(messageFilter, locations).catch(error => {
          win.Ext.Msg.show({
            title: this.app.formatMessage('Error'),
            msg: error.message,
            buttons: Ext.MessageBox.OK,
            icon: Ext.MessageBox.ERROR
          });
        });
        config.listeners = {
          single: true,
          load: function () {
            mask.hide();
          }
        };
      }
    });
  },
  /**
   * print handler
   * 
   * @todo move this to Ext.ux.Printer as iframe driver
   * @param {Tine.Felamimail.GridDetailsPanel} details panel [optional]
   */
  onPrint: function (detailsPanel) {
    var id = Ext.id(),
      doc = document,
      frame = doc.createElement('iframe');
    Ext.fly(frame).set({
      id: id,
      name: id,
      style: {
        position: 'absolute',
        width: '210mm',
        height: '297mm',
        top: '-10000px',
        left: '-10000px'
      }
    });
    doc.body.appendChild(frame);
    Ext.fly(frame).set({
      src: Ext.SSL_SECURE_URL
    });
    var doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document,
      content = this.getDetailsPanelContentForPrinting(detailsPanel || this.detailsPanel);
    doc.open();
    doc.write(content);
    doc.close();
    frame.contentWindow.focus();
    frame.contentWindow.print();
  },
  /**
   * get detail panel content
   * 
   * @param {Tine.Felamimail.GridDetailsPanel} details panel
   * @return {String}
   */
  getDetailsPanelContentForPrinting: function (detailsPanel) {
    // TODO somehow we have two <div class="preview-panel-felamimail"> -> we need to fix that and get the first element found
    var detailsPanels = detailsPanel.getEl().query('.preview-panel-felamimail');
    var detailsPanelContent = detailsPanels.length > 1 ? detailsPanels[1].innerHTML : detailsPanels[0].innerHTML;
    var buffer = '<html><head>';
    buffer += '<title>' + this.app.i18n._('Print Preview') + '</title>';
    buffer += '</head><body>';
    buffer += detailsPanelContent;
    buffer += '</body></html>';
    return buffer;
  },
  /**
   * print preview handler
   * 
   * @param {Tine.Felamimail.GridDetailsPanel} details panel [optional]
   */
  onPrintPreview: function (detailsPanel) {
    var content = this.getDetailsPanelContentForPrinting(detailsPanel || this.detailsPanel);
    var win = window.open('about:blank', this.app.i18n._('Print Preview'), 'width=500,height=500,scrollbars=yes,toolbar=yes,status=yes,menubar=yes');
    win.document.open();
    win.document.write(content);
    win.document.close();
    win.focus();
  },
  /**
   * format headers
   * 
   * @param {Object} headers
   * @param {Bool} ellipsis
   * @param {Bool} onlyImportant
   * @return {String}
   */
  formatHeaders: function (headers, ellipsis, onlyImportant, plain) {
    let result = '';
    let header = '';
    for (header in headers) {
      if (headers.hasOwnProperty(header) && (!onlyImportant || header == 'from' || header == 'to' || header == 'cc' || header == 'subject' || header == 'date')) {
        result += (plain ? header + ': ' : '<b>' + header + ':</b> ') + Ext.util.Format.htmlEncode(ellipsis ? Ext.util.Format.ellipsis(headers[header], 40) : headers[header]) + (plain ? '\n' : '<br/>');
      }
    }
    return result;
  },
  markMessagesSpam() {
    return this.getGrid().getSelectionModel().getSelected() ? this.processSpamStrategy('spam') : false;
  },
  markMessagesHam() {
    return this.getGrid().getSelectionModel().getSelected() ? this.processSpamStrategy('ham') : false;
  },
  /**
   * process spam strategy and refresh grid panel
   *
   * @param option
   */
  processSpamStrategy: async function (option) {
    const sm = this.getGrid().getSelectionModel();
    const msgs = sm.isFilterSelect ? this.getStore() : sm.getSelectionsCollection();
    const nextRecord = sm.isFilterSelect ? null : this.getNextMessage(msgs);
    const account = this.app.getActiveAccount();
    const msgsIds = [];
    try {
      const promises = [];
      let increaseUnreadCountInTargetFolder = 0;
      this.movingOrDeleting = true;
      msgs.each(function (msg) {
        const currFolder = this.app.getFolderStore().getById(msg.get('folder_id'));
        if (currFolder) {
          if ('spam' === option) {
            this.getStore().remove(msg);
            this.deleteQueue.push(msg.id);
            msgsIds.push(msg.id);

            //spam strategy will execute move message , ham will remain in current folder
            const isSeen = msg.hasFlag('\\Seen');
            const diff = isSeen ? 0 : 1;
            currFolder.set('cache_unreadcount', currFolder.get('cache_unreadcount') - diff);
            currFolder.set('cache_totalcount', currFolder.get('cache_totalcount') - 1);
            currFolder.set('cache_status', 'pending');
            currFolder.commit();
            increaseUnreadCountInTargetFolder += diff;
          }
          if ('ham' === option) {
            let subject = msg.get('subject').replace(/SPAM\? \(.+\) \*\*\* /, '');
            msg.set('subject', subject);
            msg.set('is_spam_suspicions', false);
            msg.commit();
            this.addToEditBuffer(msg);
          }
        }
        promises.push(Tine.Felamimail.processSpam(msg, option));
      }, this);

      // update unread count of trash folder (only needed for spam Strategy)
      if ('spam' === option) {
        const trashFolderId = account ? account.getTrashFolderId() : null;
        const targetFolder = trashFolderId ? this.app.getFolderStore().getById(trashFolderId) : null;
        if (targetFolder) {
          targetFolder.set('cache_unreadcount', targetFolder.get('cache_unreadcount') + increaseUnreadCountInTargetFolder);
          targetFolder.set('cache_status', 'pending');
          targetFolder.commit();
        }
        if (nextRecord) {
          sm.selectRecords([nextRecord]);
        }
      }
      if ('ham' === option) {
        sm.selectRecords(msgs.items, true);
      }
      await Promise.allSettled(promises).then(() => {
        if ('spam' === option) {
          this.onAfterDelete(msgsIds);
        }
        this.doRefresh();
      });
    } catch (e) {
      this.doRefresh();
    }
  },
  /**
   * - hide spam actions if the current folder is trash/junk folder
   * - disable spam actions if the message has no spam flag
   */
  onDisplaySpamActions: function () {
    if (!this.app.featureEnabled('featureSpamSuspicionStrategy')) {
      return;
    }
    const folder = this.getCurrentFolderFromTree();
    const account = folder ? this.app.getAccountStore().getById(folder.get('account_id')) : null;
    this.action_spam.show();
    this.action_ham.show();
    if (!account) {
      this.action_spam.hide();
      this.action_ham.hide();
    }
  }
}, (_applyDecoratedDescriptor(_obj, "onMessageCompose", [_dec], (_init = Object.getOwnPropertyDescriptor(_obj, "onMessageCompose"), _init = _init ? _init.value : undefined, {
  enumerable: true,
  configurable: true,
  writable: true,
  initializer: function () {
    return _init;
  }
}), _obj), _applyDecoratedDescriptor(_obj, "onMessageForward", [_dec2], (_init2 = Object.getOwnPropertyDescriptor(_obj, "onMessageForward"), _init2 = _init2 ? _init2.value : undefined, {
  enumerable: true,
  configurable: true,
  writable: true,
  initializer: function () {
    return _init2;
  }
}), _obj), _applyDecoratedDescriptor(_obj, "replyMessage", [_dec3], Object.getOwnPropertyDescriptor(_obj, "replyMessage"), _obj), _applyDecoratedDescriptor(_obj, "replyMessageAll", [_dec4], Object.getOwnPropertyDescriptor(_obj, "replyMessageAll"), _obj), _applyDecoratedDescriptor(_obj, "fileMessage", [_dec5], Object.getOwnPropertyDescriptor(_obj, "fileMessage"), _obj), _applyDecoratedDescriptor(_obj, "openMessage", [_dec6], Object.getOwnPropertyDescriptor(_obj, "openMessage"), _obj), _applyDecoratedDescriptor(_obj, "toggleMessageFlagged", [_dec7], Object.getOwnPropertyDescriptor(_obj, "toggleMessageFlagged"), _obj), _applyDecoratedDescriptor(_obj, "toggleMessageUnread", [_dec8], Object.getOwnPropertyDescriptor(_obj, "toggleMessageUnread"), _obj), _applyDecoratedDescriptor(_obj, "markMessagesSpam", [_dec9], Object.getOwnPropertyDescriptor(_obj, "markMessagesSpam"), _obj), _applyDecoratedDescriptor(_obj, "markMessagesHam", [_dec10], Object.getOwnPropertyDescriptor(_obj, "markMessagesHam"), _obj)), _obj)));

/***/ }),

/***/ 49471:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2011-2012 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.ns('Tine.Felamimail');

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.GridPanelHook
 * 
 * <p>Felamimail Gridpanel Hook</p>
 * <p>
 * </p>
 * 
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * 
 * @constructor
 */
Tine.Felamimail.GridPanelHook = function (config) {
  Ext.apply(this, config);
  Tine.log.info('Tine.Felamimail.GridPanelHook::Initialising Felamimail ' + this.foreignAppName + ' hooks.');

  // NOTE: due to the action updater this action is bound the the adb grid only!
  this.composeMailAction = new Ext.Action({
    actionType: 'add',
    text: this.app.i18n._('Compose email'),
    iconCls: this.app.getIconCls(),
    disabled: true,
    scope: this,
    actionUpdater: this.updateAction,
    handler: this.onComposeEmailTO,
    allowMultiple: true,
    listeners: {
      scope: this,
      render: this.onRender
    },
    menu: {
      items: [this.composeMailActionTO = new Ext.Action({
        actionType: 'add',
        text: this.app.i18n._('To'),
        iconCls: this.app.getIconCls(),
        disabled: true,
        scope: this,
        actionUpdater: this.updateAction,
        handler: this.onComposeEmailTO,
        listeners: {
          scope: this,
          render: this.onRender
        }
      }), this.composeMailActionCC = new Ext.Action({
        actionType: 'add',
        text: this.app.i18n._('CC'),
        iconCls: this.app.getIconCls(),
        disabled: true,
        scope: this,
        actionUpdater: this.updateAction,
        handler: this.onComposeEmailCC,
        listeners: {
          scope: this,
          render: this.onRender
        }
      }), this.composeMailActionBCC = new Ext.Action({
        actionType: 'add',
        text: this.app.i18n._('BCC'),
        iconCls: this.app.getIconCls(),
        disabled: true,
        scope: this,
        actionUpdater: this.updateAction,
        handler: this.onComposeEmailBCC,
        listeners: {
          scope: this,
          render: this.onRender
        }
      }), this.composeMailActionMass = new Ext.Action({
        actionType: 'add',
        text: this.app.i18n._('Mass Mailing'),
        iconCls: this.app.getIconCls(),
        disabled: true,
        scope: this,
        actionUpdater: this.updateAction,
        handler: this.onComposeEmailMass,
        listeners: {
          scope: this,
          render: this.onRender
        }
      })]
    }
  });
  this.composeMailBtn = Ext.apply(new Ext.Button(this.composeMailActionTO), {
    text: this.app.i18n._('Compose email'),
    scale: 'medium',
    rowspan: 2,
    iconAlign: 'top'
  });

  // register in toolbar + contextmenu
  Ext.ux.ItemRegistry.registerItem(this.foreignAppName + '-' + this.modelName + '-GridPanel-ActionToolbar-leftbtngrp', this.composeMailBtn, 30);
  Ext.ux.ItemRegistry.registerItem(this.foreignAppName + '-' + this.modelName + '-GridPanel-ContextMenu', this.composeMailAction, 80);
};
Ext.apply(Tine.Felamimail.GridPanelHook.prototype, {
  /**
   * @property app
   * @type Tine.Felamimail.Application
   * @private
   */
  app: null,
  /**
   * foreign application name
   * @type String
   */
  foreignAppName: null,
  /**
   * @property composeMailActionTO
   * @type Tine.widgets.ActionUpdater
   * @private
   */
  composeMailActionTO: null,
  /**
   * @property composeMailActionCC
   * @type Tine.widgets.ActionUpdater
   * @private
   */
  composeMailActionCC: null,
  /**
   * @property composeMailActionBCC
   * @type Tine.widgets.ActionUpdater
   * @private
   */
  composeMailActionBCC: null,
  /**
   * @property composeMailActionMass
   * @type Tine.widgets.ActionUpdater
   * @private
   */
  composeMailActionMass: null,
  /**
   * @property composeMailBtn
   * @type Ext.Button
   * @private
   */
  composeMailBtn: null,
  /**
   * @property gridPanel
   * @type Tine.Addressbook.gridPanel
   * @private
   */
  gridPanel: null,
  contactInRelation: false,
  relationType: null,
  addMailFromRecord: null,
  mailAddresses: null,
  // TODO move to a messageData object
  subject: null,
  body: null,
  massMailingFlag: false,
  subjectField: null,
  subjectFn: null,
  bodyFn: null,
  massMailingFlagFn: null,
  records: [],
  /**
   * get addressbook contact grid panel
   */
  getGridPanel: function () {
    if (!this.gridPanel) {
      this.gridPanel = Tine.Tinebase.appMgr.get(this.foreignAppName).getMainScreen().getCenterPanel();
    }
    return this.gridPanel;
  },
  /**
   * return mail addresses of given contacts
   *
   * @param records
   */
  getMailAddresses: async function () {
    let records = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
    const mailAddresses = [];
    const promises = [];
    _.each(records, record => {
      if (this.contactInRelation && record.get('relations')) {
        Ext.each(record.get('relations'), function (relation) {
          if (relation.type === this.relationType) {
            promises.push(this.addRecipientTokenFromContacts(mailAddresses, [relation.related_record]));
          }
        }, this);
      } else if (Ext.isFunction(this.addMailFromRecord)) {
        // addMailFromRecord can be defined in config
        promises.push(this.addMailFromRecord(mailAddresses, record));
      } else {
        promises.push(this.addRecipientTokenFromContacts(mailAddresses, [record]));
      }
    }, this);
    await Promise.allSettled(promises).then(() => {
      if (mailAddresses.length > 0) {
        this.mailAddresses = mailAddresses;
      }
    });
    return this.mailAddresses;
  },
  /**
   * add mail address from addressbook (if available) and add it to mailAddresses array
   *
   * @param {Array} mailAddresses
   * @param contacts
   */
  addRecipientTokenFromContacts: function (mailAddresses, contacts) {
    return new Promise(async (resolve, reject) => {
      if (!contacts || contacts.length === 0) {
        resolve();
      }
      await _.reduce(contacts, async (prev, contact) => {
        return prev.then(async () => {
          contact = Ext.isFunction(contact.beginEdit) ? contact : new Tine.Addressbook.Model.Contact(contact);
          const memberIds = contact.get("members");
          if (memberIds && memberIds.length > 0) {
            let {
              results: memberContacts
            } = await Tine.Addressbook.searchContacts([{
              field: 'id',
              operator: 'in',
              value: _.compact(_.uniq(memberIds))
            }]);
            await this.addRecipientTokenFromContacts(mailAddresses, memberContacts);
          } else {
            const token = this.getRecipientTokenFromContact(contact);
            if (token) {
              const existToken = _.find(mailAddresses, {
                email: token.email
              });
              if (!existToken) {
                mailAddresses.push(token);
              }
            }
          }
        });
      }, Promise.resolve());
      resolve();
    });
  },
  getRecipientTokenFromContact: function (contact) {
    // no exact matches are necessary - use the same regex as in \Tinebase_Mail::EMAIL_ADDRESS_CONTAINED_REGEXP
    const emailRegEx = /([a-z0-9_\+-\.&]+@[a-z0-9-\.]+\.[a-z]{2,63})/i;
    const email = typeof contact.getPreferredEmail == 'function' ? contact.getPreferredEmail() : null;
    const emailType = contact.get('email') === email ? 'email' : contact.get('email_home') === email ? 'email_home' : 'email';
    let token = null;
    if (email && email.match(emailRegEx)) {
      token = {
        'email': email,
        'email_type': emailType,
        'type': contact.get('type'),
        'n_fileas': contact.get('n_fileas'),
        'name': contact.get('n_fn'),
        'contact_record': contact
      };
    }
    return token;
  },
  /**
   * compose an email to selected contacts
   *
   * @param {Button} btn
   * @param to
   */
  onComposeEmail: async function (btn, to) {
    const sm = this.getGridPanel().grid ? this.getGridPanel().grid.getSelectionModel() : null;
    const records = sm && sm.isFilterSelect ? sm.getSelections() : this.records;
    var popupWindow = Tine.Felamimail.MessageEditDialog.openWindow({
      contentPanelConstructorInterceptor: async config => {
        const waitingText = this.app.i18n._('Loading Recipients...');
        const mask = await config.setWaitText(waitingText);
        const mailAddresses = await this.getMailAddresses(records);
        const record = new Tine.Felamimail.Model.Message({
          subject: this.subject ? this.subject : '',
          body: this.body,
          massMailingFlag: this.massMailingFlag
        }, 0);
        switch (to) {
          case "TO":
            record.set('to', mailAddresses);
            break;
          case "CC":
            record.set('cc', mailAddresses);
            break;
          case "BCC":
            record.set('bcc', mailAddresses);
            break;
          case "mass":
            to = 'bcc';
            record.set('massMailingFlag', true);
            record.set('bcc', mailAddresses);
            break;
        }
        config.record = record;
        config.listeners = {
          single: true,
          load: function () {
            mask.hide();
          }
        };
      },
      selectionFilter: sm && sm.isFilterSelect ? Ext.encode({
        to: to,
        filter: sm.getSelectionFilter()
      }) : null
    });
  },
  /**
   * compose an email to selected contacts
   *
   * @param {Button} btn
   */
  onComposeEmailTO: function (btn) {
    this.onComposeEmail(btn, "TO");
  },
  /**
   * compose an email to selected contacts
   *
   * @param {Button} btn
   */
  onComposeEmailCC: function (btn) {
    this.onComposeEmail(btn, "CC");
  },
  /**
   * compose an email to selected contacts
   *
   * @param {Button} btn
   */
  onComposeEmailBCC: function (btn) {
    this.onComposeEmail(btn, "BCC");
  },
  /**
   * compose mass maiiling to selected contacts
   *
   * @param {Button} btn
   */
  onComposeEmailMass: function (btn) {
    this.onComposeEmail(btn, "mass");
  },
  /**
   * add to action updater the first time we render
   */
  onRender: function () {
    var actionUpdater = this.getGridPanel().actionUpdater,
      registeredActions = actionUpdater.actions;
    if (registeredActions.indexOf(this.composeMailAction) < 0) {
      actionUpdater.addActions([this.composeMailAction]);
    }
    if (registeredActions.indexOf(this.composeMailActionTO) < 0) {
      actionUpdater.addActions([this.composeMailActionTO]);
    }
    if (registeredActions.indexOf(this.composeMailActionCC) < 0) {
      actionUpdater.addActions([this.composeMailActionCC]);
    }
    if (registeredActions.indexOf(this.composeMailActionBCC) < 0) {
      actionUpdater.addActions([this.composeMailActionBCC]);
    }
  },
  /**
   * updates compose button
   * 
   * @param {Ext.Action} action
   * @param {Object} grants grants sum of grants
   * @param {Object} records
   */
  updateAction: function (action, grants, records) {
    var _action$initialConfig, _action$initialConfig2, _action$initialConfig3;
    this.mailAddresses = [];
    this.records = records.length ? records : (_action$initialConfig = action.initialConfig) !== null && _action$initialConfig !== void 0 && (_action$initialConfig2 = _action$initialConfig.selections) !== null && _action$initialConfig2 !== void 0 && _action$initialConfig2.length ? action.initialConfig.selections : [];
    if (action.text === this.app.i18n._('Compose email') && this.records.length > 0) {
      let hasEmailAddress = false;
      _.each(this.records, record => {
        if (typeof record.getPreferredEmail == 'function' && record.getPreferredEmail() !== '' || record.get('members') && record.get('members').length > 0 || record.get('attendee') && record.get('attendee').length > 0) {
          hasEmailAddress = true;
        }
      });
      this.composeMailAction.setDisabled(!hasEmailAddress);
    }
    if (action !== null && action !== void 0 && (_action$initialConfig3 = action.initialConfig) !== null && _action$initialConfig3 !== void 0 && _action$initialConfig3.text) {
      const isComposeItem = _.find(['To', 'CC', 'BCC', 'Mass Mailing'], name => {
        return action.initialConfig.text === this.app.i18n._(name);
      });
      if (isComposeItem) {
        var _action$initialConfig4, _action$initialConfig5;
        const isFilterSelect = (_action$initialConfig4 = action.initialConfig) === null || _action$initialConfig4 === void 0 ? void 0 : (_action$initialConfig5 = _action$initialConfig4.selectionModel) === null || _action$initialConfig5 === void 0 ? void 0 : _action$initialConfig5.isFilterSelect;
        action.setDisabled(isFilterSelect ? action.initialConfig.text !== this.app.i18n._('Mass Mailing') : false);
      }
    }
    if (this.subjectField && records.length > 0) {
      this.subject = records[0].get(this.subjectField);
    } else if (Ext.isFunction(this.subjectFn)) {
      this.subject = this.subjectFn(records[0]);
    }
    if (Ext.isFunction(this.bodyFn)) {
      this.body = this.bodyFn(records[0]);
    }
    if (Ext.isFunction(this.massMailingFlagFn)) {
      this.massMailingFlag = this.massMailingFlagFn(records[0]);
    }
  }
});

/***/ }),

/***/ 42398:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";
// ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);

// EXTERNAL MODULE: ./util/waitFor.es6.js
var waitFor_es6 = __webpack_require__(50696);
// EXTERNAL MODULE: ./widgets/form/RecordEditFieldTriggerPlugin.js
var RecordEditFieldTriggerPlugin = __webpack_require__(28198);
;// CONCATENATED MODULE: ../../Felamimail/js/AttachmentFileAction.js
/*
 * Tine 2.0
 *
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiß <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2022 Metaways Infosystems GmbH (http://www.metaways.de)
 */



/**
 * quicklook panel file action for attachments
 */
Tine.Tinebase.appMgr.isInitialised('Felamimail').then(async () => {
  const app = Tine.Tinebase.appMgr.get('Felamimail');
  Ext.ux.ItemRegistry.registerItem('Tine-Filemanager-QuicklookPanel', getFileAttachmentAction(async (locations, action, attachments) => {
    const record = _.get(action, 'ownerCt.ownerCt.selection[0]', _.get(action, 'selection[0]'));
    if (locations === 'download') {
      Ext.ux.file.Download.start({
        url: Tine.Filemanager.Model.Node.getDownloadUrl(record)
      });
    } else {
      const srcLocation = {
        type: 'attachment',
        model: 'Felamimail_Model_AttachmentCache',
        record_id: record.get('path').split('/')[3],
        file_name: record.get('name')
      };
      await Tine.Tinebase.copyNodes([srcLocation], locations[0], true);
      return 1;
    }
  }, {
    hidden: true,
    actionUpdater: (action, grants, records, isFilterSelect) => {
      var _action$menu;
      (_action$menu = action.menu) === null || _action$menu === void 0 ? void 0 : _action$menu.items.each(item => {
        item.selection = records;
      });
      const isMailAttachment = String(_.get(records, '[0].data.path')).match(/^\/records\/Felamimail_Model_AttachmentCache/);
      action.baseAction.setHidden(!isMailAttachment);
    }
  }), 50);
});

/**
 * @param {Function} fileFn
 * @param config
 * @returns {Ext.Action}
 */
const getFileAttachmentAction = (fileFn, config) => {
  var _config$record, _config$record2;
  const app = Tine.Tinebase.appMgr.get('Felamimail');
  return new Ext.Action(Object.assign({
    text: app.i18n._('Save As'),
    iconCls: 'action_filemanager_save_all',
    menu: [{
      text: app.i18n._('File (in Filemanager) ...'),
      hidden: !Tine.Tinebase.common.hasRight('run', 'Filemanager'),
      disabled: (_config$record = config.record) === null || _config$record === void 0 ? void 0 : _config$record.get('from_node'),
      // not implemented @see \Felamimail_Controller_Message_File::fileAttachments
      handler: (action, e) => {
        // details panel vs. messageDisplayDialog
        const attachments = config.attachments || action.selection;

        // we don't set constrain here because user should be able to select both folder and file as target
        const filePickerDialog = new Tine.Filemanager.FilePickerDialog({
          mode: 'target',
          singleSelect: true,
          requiredGrants: ['addGrant'],
          files: attachments,
          initialPath: Tine.Tinebase.container.getMyFileNodePath() + '/'
        });
        filePickerDialog.on('selected', async nodes => {
          if (attachments.length === 1) {
            attachments[0].filename = filePickerDialog.filePicker.fileName;
          }
          const type = _.get(nodes, '[0].type');
          let path = _.get(nodes, '[0].path');
          let recordId = _.get(nodes[0], 'nodeRecord.data', nodes[0]);

          //attachment data only accept folder node as location
          if (type === 'file') {
            var _nodes$;
            path = Tine.Filemanager.Model.Node.dirname(path);
            recordId = ((_nodes$ = nodes[0]) === null || _nodes$ === void 0 ? void 0 : _nodes$.parent_id) ?? _.get(nodes[0], 'recordId');
          }
          const attachmentCount = await fileFn([{
            type: 'fm_node',
            model: 'Filemanager_Model_Node',
            fm_path: path,
            record_id: recordId,
            file_name: attachments.length === 1 ? filePickerDialog.filePicker.fileName : null
          }], action, attachments);
          const msg = app.formatMessage('{attachmentCount, plural, one {Attachment was saved} other {# Attachments where saved}}', {
            attachmentCount
          });
          Ext.ux.MessageBox.msg(app.formatMessage('Success'), msg);
        });
        filePickerDialog.openWindow();
      }
    }, {
      text: app.i18n._('Attachment (of Record)'),
      disabled: (_config$record2 = config.record) === null || _config$record2 === void 0 ? void 0 : _config$record2.get('from_node'),
      // not implemented @see \Felamimail_Controller_Message_File::fileAttachments
      listeners: {
        render: cmp => {
          cmp.menu.add(_.reduce(Tine.Tinebase.data.RecordMgr.items, (menu, model) => {
            if (model.hasField('attachments') && model.getMeta('appName') !== 'Felamimail') {
              menu.push({
                text: model.getRecordName() + ' ...',
                iconCls: model.getIconCls(),
                handler: async (action, e) => {
                  let attachmentRecordsData = [];
                  if (config !== null && config !== void 0 && config.attachments) {
                    await _.reduce(config === null || config === void 0 ? void 0 : config.attachments, async (prev, attachment, id) => {
                      return prev.then(async () => {
                        const cachePromises = await Promise.all(attachment.promises);
                        const validResponse = cachePromises.find(cachePromise => {
                          return cachePromise.cache !== null;
                        });
                        if (validResponse) return attachmentRecordsData.push(validResponse.cache.data);
                      });
                    }, Promise.resolve());
                  }
                  var pickerDialog = Tine.WindowFactory.getWindow({
                    layout: 'fit',
                    width: 250,
                    height: 100,
                    padding: '5px',
                    modal: true,
                    title: app.i18n._('Save as Record Attachment'),
                    items: new Tine.Tinebase.dialog.Dialog({
                      listeners: {
                        apply: async fileTarget => {
                          const attachmentCount = await fileFn([fileTarget], action);
                          const msg = app.formatMessage('{attachmentCount, plural, one {Attachment was saved} other {# Attachments where saved}}', {
                            attachmentCount
                          });
                          Ext.ux.MessageBox.msg(app.formatMessage('Success'), msg);
                        }
                      },
                      getEventData: function (eventName) {
                        if (eventName === 'apply') {
                          var attachRecord = this.getForm().findField('attachRecord').selectedRecord;
                          return {
                            type: 'attachment',
                            model: model.getPhpClassName(),
                            record_id: attachRecord.data
                          };
                        }
                      },
                      items: Tine.widgets.form.RecordPickerManager.get(model.getMeta('appName'), model.getMeta('modelName'), {
                        fieldLabel: model.getRecordName(),
                        name: 'attachRecord',
                        useEditPlugin: false,
                        plugins: [new RecordEditFieldTriggerPlugin/* default */.Z({
                          editDialogMode: 'remote',
                          attachments: attachmentRecordsData
                        })]
                      })
                    })
                  });
                }
              });
            }
            return menu;
          }, []));
        }
      },
      menu: []
    }, {
      xtype: 'menuseparator',
      hidden: !Tine.Tinebase.configManager.get('downloadsAllowed')
    }, {
      text: app.i18n._('Download'),
      iconCls: 'action_download',
      hidden: !Tine.Tinebase.configManager.get('downloadsAllowed'),
      handler: async (action, e) => {
        await fileFn('download', action);
      }
    }]
  }, config));
};
/* harmony default export */ const AttachmentFileAction = (getFileAttachmentAction);
;// CONCATENATED MODULE: ../../Felamimail/js/MailDetailsPanel.js
/*
 * Tine 2.0
 *
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2017-2021 Metaways Infosystems GmbH (http://www.metaways.de)
 */


Ext.ns('Tine.Felamimail');


/**
 * @param config
 * @constructor
 */
Tine.Felamimail.MailDetailsPanel = function (config) {
  Ext.apply(this, config);
  Tine.Felamimail.MailDetailsPanel.superclass.constructor.call(this);
};

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.MailDetailsPanel
 * @extends     Ext.Panel
 *
 * TODO         replace telephone numbers in emails with 'call contact' link
 * TODO         make only text body scrollable (headers should be always visible)
 * TODO         show image attachments inline
 * TODO         add 'download all' button
 * TODO         'from' to contact: check for duplicates
 */
Ext.extend(Tine.Felamimail.MailDetailsPanel, Ext.Panel, {
  /**
   * layout stuff
   */
  layout: 'vbox',
  layoutConfig: {
    align: 'stretch'
  },
  border: false,
  record: null,
  app: null,
  i18n: null,
  // if this is given, we load the record from a node
  nodeRecord: null,
  // if this is true, add top toolbar with actions to open mail in message display dialog
  hasTopToolbar: true,
  initComponent: function () {
    this.app = Tine.Tinebase.appMgr.get('Felamimail');
    this.i18n = this.app.i18n;
    this.messageRecordPanel = new Ext.Panel({
      border: false,
      autoScroll: true,
      flex: 1
    });
    this.items = [this.messageRecordPanel];
    this.initTemplate();
    if (this.hasTopToolbar) {
      this.initTopToolbar();
    }
    Tine.Felamimail.MailDetailsPanel.superclass.initComponent.call(this);
  },
  /**
   * init top toolbar for opening mails in fmail
   */
  initTopToolbar: function () {
    this.action_openInFmail = new Ext.Action({
      text: this.app.i18n._('Open in Felamimail'),
      minWidth: 70,
      scope: this,
      handler: this.onOpenInFmail,
      iconCls: this.app.appName + 'IconCls'
    });
    this.tbar = new Ext.Toolbar({
      items: ['->', this.action_openInFmail]
    });
  },
  /**
   * open in Felamimail MessageDisplayDialog
   */
  onOpenInFmail: function () {
    if (this.nodeRecord) {
      // prepare message for forwarding in Tine.Felamimail.MessageEditDialog.handleAttachmentsOfExistingMessage
      this.record.set('from_node', this.nodeRecord.data);
    }
    Tine.Felamimail.MessageDisplayDialog.openWindow({
      record: this.record,
      // remove delete + save actions as this makes no sense if opened from another app
      hasDeleteAction: false,
      hasDownloadAction: false
    });
  },
  /**
   * add on click event after render
   * @private
   */
  afterRender: async function () {
    Tine.Felamimail.MailDetailsPanel.superclass.afterRender.apply(this, arguments);
    this.body.on('click', this.onClick, this);
    if (this.nodeRecord) {
      await this.loadRecord();
    }
  },
  getTemplateBody: function () {
    return this.messageRecordPanel.body;
  },
  getMessageRecordPanel: function () {
    return this.messageRecordPanel;
  },
  /**
   * fills this fields with the corresponding message data
   *
   * @param {Tine.Tinebase.data.Record|Object} record
   */
  loadRecord: async function (record) {
    if (record) {
      this.record = record;
      this.tpl.overwrite(this.messageRecordPanel.body, record.data);
      this.doLayout();

      // prefill attachmentCache, all attachments cache got saved the first time, to filemanager_attachmentcache
      const attachments = this.resolveAttachmentCache('Felamimail_Model_Message', this.record);
      this.record.set('attachments', attachments, this.record);
    } else if (this.nodeRecord) {
      await Tine.Felamimail.getMessageFromNode(this.nodeRecord.id).then(async response => {
        // this.record.id is not the original messageId, we can not get attachment from this record
        this.record = Tine.Felamimail.messageBackend.recordReader({
          responseText: Ext.util.JSON.encode(response)
        });
        this.tpl.overwrite(this.messageRecordPanel.body, this.record.data);
        // prefill attachmentCache, all attachments cache got saved the first time, to filemanager_attachmentcache
        //fixme: attachment cache created from Filemanager_Model_Node has size 0
        const attachments = this.resolveAttachmentCache('Filemanager_Model_Node', this.record);
        this.record.set('attachments', attachments, this.record);
      }).catch(exception => {
        Tine.log.debug(exception);
        Tine.log.debug(exception);
        // @todo add loadMask? move loadMask from GridDetailsPanel here?
        // this.getLoadMask().hide();
        // if (exception.code == 404) {
        this.tpl.overwrite(this.messageRecordPanel.body, {
          msg: this.app.i18n._('Message not available.')
        });
        // } else {
        //     // @todo handle exception?
        // }
      });
    }
  },

  /**
   * init single message template (this.tpl)
   * @private
   */
  initTemplate: function () {
    this.tpl = new Ext.XTemplate('{[this.showSpamToolbar(values)]}', '<div class="preview-panel-felamimail">', '{[this.showInfo(values)]}', '{[this.showAttachments(values.attachments, values)]}', '<div class="preview-panel-felamimail-filelocations">{[this.showFileLocations(values)]}</div>', '<div class="preview-panel-felamimail-preparedPart"></div>', '<div class="preview-panel-felamimail-body">{[this.showBody(values.body, values)]}</div>', '</div>', {
      app: this.app,
      panel: this,
      encode: function (value) {
        if (value) {
          // it should be enough to replace only 2 or more spaces
          value = value.replace(/\s{2,}/g, ' ');
          let encoded = Ext.util.Format.htmlEncode(value);
          encoded = Ext.util.Format.nl2br(encoded);
          return encoded;
        } else {
          return '';
        }
      },
      showSpamToolbar: function (messageData) {
        const app = Tine.Tinebase.appMgr.get('Felamimail');
        const account = app.getAccountStore().getById(messageData.account_id);
        const folder = app.getFolderStore().getById(messageData.folder_id);
        if (!folder || !account || !messageData.is_spam_suspicions) return '';
        const html = '<span style="width: 60%; padding: 5px;">' + app.i18n._('This message is probably SPAM. Please help to train your anti-SPAM system with a decision: "Yes, it is SPAM" or "No, it is not"') + '</span>';
        const aboutAction = `<span id="action_about" class="felamimail-action"><span class="felamimail-location-icon action_about"></span></span>`;
        const actions = '<span id="spam_action_spam" class="felamimail-action"><span class="felamimail-location-icon felamimail-action-spam"></span><span>' + app.i18n._('Yes, it is SPAM') + '</span></span>' + '<span id="spam_action_ham" class="felamimail-action"><span class="felamimail-location-icon felamimail-action-ham"></span><span>' + app.i18n._('No, it is not') + '</span></span>';
        return `<div id="spam_toolbar" class="felamimail_spam_suspicions_toolbar">${html}${aboutAction}<span>${actions}</span></div>`;
      },
      linkifyEmail(name, email) {
        const id = Ext.id() + ':' + email + Ext.util.Format.htmlEncode(':' + Ext.util.Format.trim(name));
        const address = name ? `${name} < ${email} >` : email;
        const link = document.createElement('div');
        link.className = 'preview-panel-felamimail-header-longtext';
        link.innerHTML = `<a id="${id}" class="tinebase-email-link">${address}</a>`;
        return link.outerHTML;
      },
      showInfo(values) {
        const app = Tine.Tinebase.appMgr.get('Felamimail');
        const items = ['subject', 'date', 'from', 'to', 'cc', 'bcc', 'extra'];
        const headerBlock = document.createElement('div');
        headerBlock.className = 'preview-panel-felamimail-headers';
        items.forEach(header => {
          let value = '';
          let headerValue = app.i18n._hidden(Ext.util.Format.capitalize(header));
          if (header === 'subject') value = this.encode(values.subject);
          if (header === 'from') value = this.showFrom(values.from_email, values.from_name);
          if (header === 'date') value = this.showDate(values.sent, values);
          if (header === 'extra') headerValue = this.panel.showExtraHeaderButton();
          if (['to', 'cc', 'bcc'].includes(header)) {
            if (!values.headers.hasOwnProperty(header)) return;
            const emails = this.panel.record.get(header);
            if (emails.length === 0) return;
            //TODO: bcc only store email in \Zend_Mail::addBcc($email), do we want to change it ?
            emails.forEach((emailData, idx) => {
              if (idx > 0) value += ',&nbsp';
              value += this.linkifyEmail(emailData === null || emailData === void 0 ? void 0 : emailData.name, emailData === null || emailData === void 0 ? void 0 : emailData.email);
            });
          }
          const row = this.panel.renderHeader(headerValue, value);
          headerBlock.appendChild(row);
        });
        return headerBlock.outerHTML;
      },
      showDate: function (sent, recordData) {
        var date = sent ? Ext.isDate(sent) ? sent : Date.parseDate(sent, Date.patterns.ISO8601Long) : Date.parseDate(recordData.received, Date.patterns.ISO8601Long);
        return date ? date.format('l') + ', ' + Tine.Tinebase.common.dateTimeRenderer(date) : '';
      },
      showFrom: function (email, name) {
        if (!name) return '';
        const emails = this.panel.record.get('from');
        const fromEmail = emails[0] ?? [];
        return this.linkifyEmail(fromEmail.name, fromEmail.email);
      },
      showBody: function (body, messageData) {
        body = body || '';
        if (body) {
          var account = this.app.getActiveAccount();
          if (account && (account.get('display_format') == 'plain' || account.get('display_format') == 'content_type' && messageData.body_content_type == 'text/plain')) {
            var width = this.panel.body.getWidth() - 25,
              height = this.panel.body.getHeight() - 90,
              id = Ext.id();
            if (height < 0) {
              // sometimes the height is negative, fix this here
              height = 500;
            }
            body = '<textarea ' + 'style="width: ' + width + 'px; height: ' + height + 'px; " ' + 'autocomplete="off" id="' + id + '" name="body" class="x-form-textarea x-form-field x-ux-display-background-border" readonly="" >' + body + '</textarea>';
          } else if (messageData.body_content_type != 'text/html' || messageData.body_content_type_of_body_property_of_this_record == 'text/plain') {
            // message content is text and account format non-text
            body = Ext.util.Format.nl2br(Ext.util.Format.wrapEmojis(body));
          } else {
            Ext.util.Format.linkSaveHtmlEncodeStepOne(body);
            Tine.Tinebase.common.linkifyText(Ext.util.Format.wrapEmojis(body), function (linkified) {
              var bodyEl = this.getMessageRecordPanel().getEl().query('div[class=preview-panel-felamimail-body]')[0];
              Ext.fly(bodyEl).update(Ext.util.Format.linkSaveHtmlEncodeStepTwo(linkified));
            }, this.panel);
          }
        }
        return body;
      },
      showAttachments: function (attachments, messageData) {
        const idPrefix = Ext.id();
        const attachmentsStr = this.app.i18n._('Attachments');
        if (!attachments || attachments.length === 0) return '';
        const attachmentBlock = document.createElement('div');
        attachmentBlock.className = 'preview-panel-felamimail-attachments';
        let result = `<span id=${idPrefix}:all style="padding-left:5px;" class="tinebase-download-link tinebase-download-all"><b>${attachmentsStr}:</b><div class="tinebase-download-link-wait"></div></span>`;
        for (var i = 0, id, cls; i < attachments.length; i++) {
          result += `<span id="${idPrefix}:${i}" style="padding-left:5px;" class="tinebase-download-link">` + '<i>' + attachments[i].filename + '</i>'
          // NOTE: size is 'transfer size' (base64 encoded) here.
          // @TODO replace size from message cache size when it's loaded?
          + ' (' + Ext.util.Format.fileSize(Math.round(attachments[i].size / 1.333)) + ')<div class="tinebase-download-link-wait"></div></span> ';
        }
        attachmentBlock.innerHTML = result;
        return attachmentBlock.outerHTML;
      },
      showFileLocations: function (messageData) {
        let fileLocations = _.get(messageData, 'fileLocations', []);
        if (fileLocations.length) {
          let app = Tine.Tinebase.appMgr.get('Felamimail');
          let text = app.formatMessage('{locationCount, plural, one {This message is filed at the following location} other {This message is filed at the following locations}}: {locationsHtml}', {
            locationCount: fileLocations.length,
            locationsHtml: Tine.Felamimail.MessageFileAction.getFileLocationText(fileLocations, ', ')
          });
          return text;
        } else {
          return '';
        }
      }
    });
  },
  /**
   * on click for attachment download / compose dlg / edit contact dlg
   *
   * @param {} e
   * @private
   */
  onClick: async function (e) {
    var _this = this;
    const selectors = ['span[class^=tinebase-download-link]', 'a[class=tinebase-email-link]', 'span[class=tinebase-showheaders-link]', 'a[href^=#]', 'span[class=felamimail-action]'];

    // find the correct target
    const selectorIdx = _.findIndex(selectors, sel => {
      return e.getTarget(sel);
    });
    const selector = selectors[selectorIdx];
    const target = e.getTarget(selector);
    Tine.log.debug('Tine.Felamimail.GridDetailsPanel::onClick found target:"' + selector + '".');
    if (this.nodeRecord && !this.record.get('from_node')) {
      this.record.set('from_node', this.nodeRecord.data);
    }
    const sourceModel = this.record.get('from_node') ? 'Filemanager_Model_Node' : 'Felamimail_Model_Message';
    switch (selector) {
      case 'span[class^=tinebase-download-link]':
        if (!this.record.bodyIsFetched()) {
          // sometimes there is bad timing and we do not have the attachments available -> refetch body
          // @todo make this work again - move Tine.Felamimail.GridDetailsPanel.refetchBody here?
          // this.refetchBody(this.record, this.onClick.createDelegate(this, [e]));
          return;
        }
        // make sure we get the attachment caches
        const attachments = this.resolveAttachmentCache(sourceModel, this.record, true);
        const idx = target.id.split(':')[1];
        const selectedAttachments = idx !== 'all' ? [attachments[idx]] : attachments;
        // remove part id if set (that is the case in message/rfc822 attachments)
        const messageId = this.record.id.match(/_/) ? this.record.id.split('_')[0] : this.record.id;
        const menu = Ext.create({
          xtype: 'menu',
          plugins: [{
            ptype: 'ux.itemregistry',
            key: 'Tine.Felamimail.MailDetailPanel.AttachmentMenu'
          }],
          items: [{
            text: this.app.i18n._('Open'),
            iconCls: 'action_preview',
            disabled: this.record.get('from_node'),
            // not implemented yet
            hidden: selectedAttachments.length !== 1 || _.get(selectedAttachments, '[0]content-type') !== 'message/rfc822',
            handler: () => {
              // fixme: messageId might be filemanager node id
              Tine.Felamimail.MessageDisplayDialog.openWindow({
                record: new Tine.Felamimail.Model.Message({
                  id: messageId + '_' + selectedAttachments[0].partId
                })
              });
            }
          }, {
            text: this.app.i18n._('Preview'),
            iconCls: 'action_preview',
            hidden: !selectedAttachments.length || !Tine.Tinebase.appMgr.isEnabled('Filemanager') || _.get(selectedAttachments, '[0]content-type') === 'message/rfc822' && selectedAttachments.length === 1,
            disabled: this.record.get('from_node'),
            // not implemented yet                           
            handler: () => {
              var _this$grid, _this$grid$getGrid;
              Tine.Filemanager.QuickLookPanel.openWindow({
                windowNamePrefix: `QuickLookPanel_${sourceModel}_Attachment_${idx}_`,
                record: this.record,
                initialApp: this.app,
                handleAttachments: this.quicklookHandleAttachments,
                sm: (_this$grid = this.grid) === null || _this$grid === void 0 ? void 0 : (_this$grid$getGrid = _this$grid.getGrid()) === null || _this$grid$getGrid === void 0 ? void 0 : _this$grid$getGrid.getSelectionModel(),
                initialAttachmentIdx: idx !== 'all' ? +idx : 0
              });
            }
          }, {
            xtype: 'menuseparator'
          }, AttachmentFileAction(async function (locations, action) {
            let updatedAttachments = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
            const attachments = updatedAttachments ?? selectedAttachments;
            return await _this.attachmentAnnimation(target, async () => {
              if (locations === 'download') {
                Ext.ux.file.Download.start({
                  params: {
                    requestType: 'HTTP',
                    method: 'Felamimail.downloadAttachments',
                    id: messageId,
                    partIds: _.map(attachments, 'partId'),
                    model: sourceModel
                  }
                });
              } else {
                await Tine.Felamimail.fileAttachments(messageId, locations, attachments, sourceModel, true);
              }
              return attachments.length;
            });
          }, {
            record: this.record,
            attachments: selectedAttachments
          })]
        });
        const actionUpdater = new Tine.widgets.ActionUpdater({
          recordClass: Tine.Felamimail.Model.Attachment,
          evalGrants: false,
          actions: menu
        });
        actionUpdater.updateActions(attachments.map(attachmentData => {
          return Tine.Tinebase.data.Record.setFromJson(Object.assign(attachmentData, {
            id: `${messageId}:${attachmentData.partId}`,
            messageId
          }), Tine.Felamimail.Model.Attachment);
        }), messageId);
        menu.showAt(e.getXY());
        break;
      case 'span[class=tinebase-showheaders-link]':
        // show headers

        var parts = target.id.split(':');
        var targetId = parts[0];
        var action = parts[1];
        var html = '';
        if (action === 'show') {
          const recordHeaders = this.record.get('headers');
          for (let header in recordHeaders) {
            if (recordHeaders.hasOwnProperty(header) && (header !== 'to' || header !== 'cc' || header !== 'bcc')) {
              const row = this.renderHeaderRaw(header, Ext.util.Format.htmlEncode(recordHeaders[header]));
              html += row.outerHTML;
            }
          }
          target.id = targetId + ':' + 'hide';
        } else {
          html = this.showExtraHeaderButton();
        }
        target.innerHTML = html;
        break;
      case 'a[href^=#]':
        e.stopEvent();
        var anchor = this.getEl().query('#' + target.href.replace(/.*#/, ''));
        if (anchor.length) {
          var scrollEl = Ext.fly(anchor[0]).findParent('.x-panel-body');
          if (scrollEl) {
            var box = Ext.fly(anchor[0]).getBox();
            // TODO improve accuracy of scrolling
            scrollEl.scrollTop = box.y - 180;
          }
        }
        break;
      case 'span[class=felamimail-action]':
        const match = target.id.match(/^spam_action_(.*)/);
        if (match.length > 1) {
          document.getElementById('spam_toolbar').remove();
          this.app.getMainScreen().getCenterPanel().processSpamStrategy(match[1]);
        }
        if (target.id === 'action_about') {
          Ext.Msg.alert(this.app.i18n._('Confirm SPAM Suspicion'), Tine.Tinebase.configManager.get('spamInfoDialogContent', 'Felamimail'));
        }
        break;
    }
  },
  resolveAttachmentCache(sourceModel, record) {
    let createPreviewInstantly = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    const attachments = record.get('attachments');
    _.each(attachments, attachment => {
      if (!attachment.promises) attachment.promises = [];
      if (attachment !== null && attachment !== void 0 && attachment.isPreviewReady) return;
      const promise = new Promise(async resolve => {
        let validCache = null;
        await Promise.all(attachment.promises).then(responses => {
          validCache = responses.find(r => {
            return r.isPreviewReady;
          });
        });
        if (validCache) {
          attachment.isPreviewReady = true;
          return resolve(validCache);
        }
        const attachmentId = [sourceModel, record.id, attachment.partId].join(':');
        await Tine.Felamimail.getAttachmentCache(attachmentId, createPreviewInstantly).then(async cache => {
          return resolve({
            cache: new Tine.Tinebase.Model.Tree_Node(cache.attachments[0]),
            createPreviewInstantly: createPreviewInstantly,
            isPreviewReady: cache.attachments[0].preview_count !== 0
          });
        }).catch(e => {
          console.error(e);
          return resolve(attachment);
        });
      });
      attachment.promises.push(promise);
    });
    record.set('attachments', attachments);
    return attachments;
  },
  // NOTE: runs in the scope of QuickLookPanel
  quicklookHandleAttachments: async function () {
    var _this$record;
    this.cardPanel.layout.setActiveItem(0); // wait cycle
    if ((_this$record = this.record) !== null && _this$record !== void 0 && _this$record.isAttachmentCache) return; // key-nav
    // remove part id if set (that is the case in message/rfc822 attachments)
    const messageId = this.record.get('messageId') || (String(this.record.id).match(/_/) ? this.record.id.split('_')[0] : this.record.id);
    const sourceModel = this.record.get('from_node') ? 'Filemanager_Model_Node' : 'Felamimail_Model_Message';
    if (this.record.constructor.hasField('attachments')) {
      await (0,waitFor_es6/* default */.Z)(() => {
        return this.record.bodyIsFetched();
      });
      let attachments = this.record.get('attachments');
      if (!attachments.length) {
        // might happen with keyNav in preview panel
        this.record = new Tine.Tinebase.Model.Tree_Node({
          name: 'Email has no attachemtns',
          path: ''
        });
        return;
      }

      // make sure we get the attachment caches
      attachments = Tine.Felamimail.MailDetailsPanel.prototype.resolveAttachmentCache(sourceModel, this.record, true);
      const initialAttachmentIdx = this.initialAttachmentIdx ?? 0;
      if (!attachments[initialAttachmentIdx].promises) return;
      await Promise.all(attachments[initialAttachmentIdx].promises).then(cachePromises => {
        let resolvedAttachmentData = {};
        const validResponse = cachePromises.find(cachePromise => {
          return cachePromise.isPreviewReady;
        });
        if (validResponse) {
          resolvedAttachmentData = validResponse.cache.data;
        } else {
          const invalidResponse = cachePromises.find(cachePromise => {
            return cachePromise === null || cachePromise === void 0 ? void 0 : cachePromise.cache;
          });
          if (invalidResponse) resolvedAttachmentData = invalidResponse.cache.data;
        }
        resolvedAttachmentData.messageId = this.record.id;
        this.record = Tine.Tinebase.data.Record.setFromJson(resolvedAttachmentData, Tine.Tinebase.Model.Tree_Node);
        this.record.isAttachmentCache = true;
      });
    }
  },
  attachmentAnnimation: async function (target, workload) {
    Ext.fly(target).addClass('tinebase-download-link-anim');
    let result;
    try {
      result = await workload();
    } finally {
      Ext.fly(target).removeClass('tinebase-download-link-anim');
    }
    return result;
  },
  renderHeader(header, value) {
    const row = document.createElement('div');
    row.className = 'preview-panel-felamimail-header-row';
    const rowLeft = document.createElement('div');
    rowLeft.className = 'preview-panel-felamimail-header-row-left';
    const rowRight = document.createElement('div');
    rowRight.className = 'preview-panel-felamimail-header-row-right';
    rowLeft.innerHTML = header;
    rowRight.innerHTML = value;
    row.appendChild(rowLeft);
    row.appendChild(rowRight);
    return row;
  },
  renderHeaderRaw(header, value) {
    const row = document.createElement('div');
    row.style.display = 'flex';
    row.style.margin = '5px';
    row.style.textAlign = 'left';
    const rowLeft = document.createElement('div');
    rowLeft.textContent = header;
    rowLeft.style.minWidth = '60px';
    const rowRight = document.createElement('div');
    rowRight.innerHTML = value;
    if (header.length > 10 || value.length > 50) {
      row.style.flexDirection = 'column';
      rowRight.style.paddingLeft = '55px';
    }
    row.appendChild(rowLeft);
    row.appendChild(rowRight);
    return row;
  },
  showExtraHeaderButton() {
    const qtip = this.app.i18n._('Show or hide header information');
    return ' <span ext:qtip="' + Tine.Tinebase.common.doubleEncode(qtip) + '" id="' + Ext.id() + ':show" class="tinebase-showheaders-link">[...]</span>';
  }
});
Ext.reg('felamimaildetailspanel', Tine.Felamimail.MailDetailsPanel);

/***/ }),

/***/ 85177:
/***/ (() => {

Tine.Felamimail.mailvelopeHelper = function () {
  return {
    //give mailvelope some seconds to load
    mailvelopeLoaded: Promise.race([new Promise(function (fullfill, reject) {
      if (typeof mailvelope !== 'undefined') {
        fullfill();
      } else {
        Ext.EventManager.addListener(window, 'mailvelope', fullfill);
      }
    }), new Promise(function (fullfill, reject) {
      (function () {
        reject(new Error("mailvelope not available. (don't panic this is not a problem you just can't work with crypted emails using mailvelope)"));
      }).defer(3000);
    })]
    // no catch here!!! otherwise the promise is fulfilled all the time!
    ),

    getKeyring: function () {
      return this.mailvelopeLoaded.then(function () {
        return new Promise(function (fullfill, reject) {
          var identifier = Tine.Tinebase.common.getUrl();
          mailvelope.getKeyring(identifier).then(fullfill, function (err) {
            mailvelope.createKeyring(identifier).then(fullfill);
          }).then(function (keyring) {
            // @TODO get dataurl of tine20logo
            //keyring.setLogo('data:image/png;base64,iVBORS==', 3)
          });
        });
      });
    }
  };
}();

/***/ }),

/***/ 88738:
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

/*
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2009-2013 Metaways Infosystems GmbH (http://www.metaways.de)
 */

__webpack_require__(46274);
Ext.ns('Tine.Felamimail');
Tine.Felamimail.MessageDisplayDialog = Ext.extend(Tine.Felamimail.GridDetailsPanel, {
  /**
   * @cfg {Tine.Felamimail.Model.Message}
   */
  record: null,
  autoScroll: false,
  hasDeleteAction: true,
  hasDownloadAction: true,
  initComponent: function () {
    if (Ext.isString(this.record)) {
      this.record = Tine.Felamimail.messageBackend.recordReader({
        responseText: this.record
      });
    }
    this.addEvents('remove');
    this.app = Tine.Tinebase.appMgr.get('Felamimail');
    this.i18n = this.app.i18n;

    // trick onPrint/onPrintPreview
    this.detailsPanel = this;
    this.initActions();
    this.initToolbar();
    Tine.log.debug('Tine.Felamimail.MessageDisplayDialog::initComponent() -> message record:');
    Tine.log.debug(this.record);
    this.supr().initComponent.apply(this, arguments);
  },
  /**
   * init actions
   */
  initActions: function () {
    this.action_deleteRecord = new Ext.Action({
      text: this.app.i18n._('Delete'),
      handler: this.onMessageDelete.createDelegate(this, [false]),
      iconCls: 'action_delete',
      disabled: this.record.id.match(/_/)
    });
    this.action_reply = new Ext.Action({
      text: this.app.i18n._('Reply'),
      handler: this.onMessageReplyTo.createDelegate(this, [false]),
      iconCls: 'action_email_reply'
    });
    this.action_replyAll = new Ext.Action({
      text: this.app.i18n._('Reply To All'),
      handler: this.onMessageReplyTo.createDelegate(this, [true]),
      iconCls: 'action_email_replyAll'
    });
    this.action_forward = new Ext.Action({
      text: this.app.i18n._('Forward'),
      handler: this.onMessageForward.createDelegate(this),
      iconCls: 'action_email_forward'
    });
    this.action_fileRecord = new Tine.Felamimail.MessageFileAction({
      disabled: this.record.id.match(/_/),
      record: this.record,
      scale: 'medium',
      rowspan: 2,
      iconAlign: 'top',
      arrowAlign: 'right'
    });
    this.action_print = new Ext.Action({
      text: this.app.i18n._('Print Message'),
      handler: this.onMessagePrint.createDelegate(this.app.getMainScreen().getCenterPanel(), [this]),
      iconCls: 'action_print',
      menu: {
        items: [new Ext.Action({
          text: this.app.i18n._('Print Preview'),
          handler: this.onMessagePrintPreview.createDelegate(this.app.getMainScreen().getCenterPanel(), [this]),
          iconCls: 'action_printPreview'
        })]
      }
    });
  },
  /**
   * init toolbar
   */
  initToolbar: function () {
    var actions = [];
    if (this.hasDeleteAction) {
      actions.push(Ext.apply(new Ext.Button(this.action_deleteRecord), {
        scale: 'medium',
        rowspan: 2,
        iconAlign: 'top'
      }));
    }
    actions.push([Ext.apply(new Ext.Button(this.action_reply), {
      scale: 'medium',
      rowspan: 2,
      iconAlign: 'top'
    }), Ext.apply(new Ext.Button(this.action_replyAll), {
      scale: 'medium',
      rowspan: 2,
      iconAlign: 'top'
    }), Ext.apply(new Ext.Button(this.action_forward), {
      scale: 'medium',
      rowspan: 2,
      iconAlign: 'top'
    }), Ext.apply(new Ext.SplitButton(this.action_print), {
      scale: 'medium',
      rowspan: 2,
      iconAlign: 'top',
      arrowAlign: 'right'
    })]);
    if (this.hasDownloadAction) {
      actions.push(this.action_fileRecord);
    }
    this.tbar = new Ext.Toolbar({
      defaults: {
        height: 55
      },
      items: [{
        xtype: 'buttongroup',
        columns: 5,
        items: actions
      }]
    });
  },
  /**
   * after render
   */
  afterRender: function () {
    this.supr().afterRender.apply(this, arguments);
    this.showMessage();
    var title = this.record.get('subject');
    if (title !== undefined) {
      // TODO make this work for attachment mails
      this.window.setTitle(title);
    }
  },
  /**
   * show message
   */
  showMessage: function () {
    this.layout.setActiveItem(this.getSingleRecordPanel());
    this.updateDetails(this.record, this.getSingleRecordPanel().body);
  },
  /**
   * executed after a msg compose
   * 
   * @param {String} composedMsg
   * @param {String} action
   * @param {Array}  [affectedMsgs]  messages affected 
   * 
   */
  onAfterCompose: function (composedMsg, action, affectedMsgs) {
    this.fireEvent('update', composedMsg, action, affectedMsgs);
  },
  /**
   * executed after deletion of this message
   */
  onAfterDelete: function () {
    this.fireEvent('remove', Ext.encode(this.record.data));
    this.window.close();
  },
  /**
   * delete message handler
   */
  onMessageDelete: function (force) {
    var mainApp = Ext.ux.PopupWindowMgr.getMainWindow().Tine.Tinebase.appMgr.get('Felamimail'),
      folderId = this.record.get('folder_id'),
      folder = mainApp.getFolderStore().getById(folderId),
      accountId = folder ? folder.get('account_id') : null,
      account = mainApp.getAccountStore().getById(accountId),
      trashId = account ? account.getTrashFolderId() : null;
    this.loadMask.show();
    if (trashId) {
      var filter = [{
        field: 'id',
        operator: 'equals',
        value: this.record.id
      }];
      Tine.Felamimail.messageBackend.moveMessages(filter, trashId, false, {
        callback: this.onAfterDelete.createDelegate(this, ['move'])
      });
    } else {
      Tine.Felamimail.messageBackend.addFlags(this.record.id, '\\Deleted', {
        callback: this.onAfterDelete.createDelegate(this, ['flag'])
      });
    }
  },
  /**
   * reply message handler
   */
  onMessageReplyTo: function (toAll) {
    Tine.Felamimail.MessageEditDialog.openWindow({
      replyTo: Ext.encode(this.record.data),
      replyToAll: toAll,
      listeners: {
        'update': this.onAfterCompose.createDelegate(this, ['reply', Ext.encode([this.record.data])], 1)
      }
    });
  },
  /**
   * forward message handler
   */
  onMessageForward: function () {
    Tine.Felamimail.MessageEditDialog.openWindow({
      forwardMsgs: Ext.encode([this.record.data]),
      listeners: {
        'update': this.onAfterCompose.createDelegate(this, ['forward', Ext.encode([this.record.data])], 1)
      }
    });
  },
  onMessagePrint: Tine.Felamimail.GridPanel.prototype.onPrint,
  onMessagePrintPreview: Tine.Felamimail.GridPanel.prototype.onPrintPreview
});
Tine.Felamimail.MessageDisplayDialog.openWindow = function (config) {
  var record = Ext.isString(config.record) ? Ext.util.JSON.decode(config.record) : config.record,
    id = record && record.id ? record.id : 0,
    window = Tine.WindowFactory.getWindow({
      width: 800,
      height: 700,
      name: 'TineFelamimailMessageDisplayDialog_' + id,
      contentPanelConstructor: 'Tine.Felamimail.MessageDisplayDialog',
      contentPanelConstructorConfig: config
    });
  return window;
};

/***/ }),

/***/ 72054:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var util_waitFor_es6__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(50696);
/*
 * Tine 2.0
 *
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2009-2020 Metaways Infosystems GmbH (http://www.metaways.de)
 */

const {
  retryAllRejectedPromises
} = __webpack_require__(32622);

__webpack_require__(46274);
Ext.namespace('Tine.Felamimail');

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.MessageEditDialog
 * @extends     Tine.widgets.dialog.EditDialog
 *
 * <p>Message Compose Dialog</p>
 * <p>This dialog is for composing emails with recipients, body and attachments.
 * you can choose from which account you want to send the mail.</p>
 *
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 *
 * @param       {Object} config
 * @constructor
 * Create a new MessageEditDialog
 */
Tine.Felamimail.MessageEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
  /**
   * @cfg {Boolean} autoSave
   * enable autosave as draft
   */
  autoSave: true,
  /**
   * @cfg {Array/String} bcc
   * initial config for bcc
   */
  bcc: null,
  /**
   * @cfg {String} body
   */
  msgBody: '',
  /**
   * @cfg {Array/String} cc
   * initial config for cc
   */
  cc: null,
  /**
   * @cfg {Array} of Tine.Felamimail.Model.Message (optionally encoded)
   * messages to forward
   */
  forwardMsgs: null,
  /**
   * @cfg {String} accountId
   * the accout id this message is sent from
   */
  accountId: null,
  /**
   * @cfg {Tine.Felamimail.Model.Message} (optionally encoded)
   * message to reply to
   */
  replyTo: null,
  /**
   * @cfg {Tine.Felamimail.Model.Message} (optionally encoded)
   * message to use as draft/template
   */
  draftOrTemplate: null,
  /**
   * @cfg {Boolean} (defaults to false)
   */
  replyToAll: false,
  /**
   * @cfg {String} subject
   */
  subject: '',
  /**
   * @cfg {Array/String} to
   * initial config for to
   */
  to: null,
  /**
   * validation error message
   * @type String
   */
  validationErrorMessage: '',
  /**
   * array with e-mail-addresses used as recipients
   * @type {Array}
   */
  mailAddresses: null,
  /**
   * json-encoded selection filter and to
   * @type {String} selectionFilter
   */
  selectionFilter: null,
  /**
   * holds default values for the record
   * @type {Object}
   */
  recordDefaults: null,
  /**
   * @type {String}
   */
  quotedPGPMessage: null,
  /**
   * @type {Boolean}
   */
  isDraft: false,
  /**
   * @type {Boolean}
   */
  isTemplate: false,
  /**
   * @type {String}
   */
  draftUid: null,
  /**
   * @type {String}
   */
  templateId: null,
  /**
   * @private
   */
  windowNamePrefix: 'MessageEditWindow_',
  appName: 'Felamimail',
  recordClass: Tine.Felamimail.Model.Message,
  recordProxy: Tine.Felamimail.messageBackend,
  loadRecord: false,
  evalGrants: false,
  hideAttachmentsPanel: true,
  bodyStyle: 'padding:0px',
  /**
   * overwrite update toolbars function (we don't have record grants)
   * @private
   */
  updateToolbars: Ext.emptyFn,
  // private
  initComponent: function () {
    this.autoSave = Tine.Tinebase.appMgr.get('Felamimail').featureEnabled('autoSaveDrafts');
    let me = this;
    if (me.autoSave) {
      me.trottledsaveAsDraft = _.throttle(_.bind(me.saveAsDraft, me), 5000, {
        leading: false
      });
      me.saveAsDraftPromise = Promise.resolve();
    }
    me.on('beforecancel', me.onBeforeCancel, this);
    Tine.Felamimail.MessageEditDialog.superclass.initComponent.call(this);
    Tine.Felamimail.mailvelopeHelper.mailvelopeLoaded.then(function () {
      me.button_toggleEncrypt.setVisible(true);
    })['catch'](function () {
      Tine.log.info('mailvelope not available');
    });
  },
  /**
   * init buttons
   */
  initButtons: function () {
    this.fbar = [];
    this.action_send = new Ext.Action({
      text: this.app.i18n._('Send'),
      handler: this.onSaveAndClose,
      iconCls: 'FelamimailIconCls',
      disabled: false,
      scope: this
    });
    this.action_searchContacts = new Ext.Action({
      text: this.app.i18n._('Search Recipients'),
      handler: this.onSearchContacts,
      iconCls: 'AddressbookIconCls',
      disabled: false,
      scope: this
    });
    this.action_saveAsDraft = new Ext.Action({
      text: this.app.i18n._('Save As Draft'),
      handler: this.onSaveInFolder.createDelegate(this, ['drafts_folder']),
      iconCls: 'action_saveAsDraft',
      disabled: false,
      scope: this
    });
    this.action_saveAsTemplate = new Ext.Action({
      text: this.app.i18n._('Save As Template'),
      handler: this.onSaveInFolder.createDelegate(this, ['templates_folder']),
      iconCls: 'action_saveAsTemplate',
      disabled: false,
      scope: this,
      listeners: {
        scope: this,
        selectionchange: this.onFileMessageSelectionChange
      }
    });
    this.action_fileRecord = new Tine.Felamimail.MessageFileAction({
      mode: 'selectOnly',
      composeDialog: this,
      listeners: {
        scope: this,
        selectionchange: this.onFileMessageSelectionChange
      }
    });
    this.button_fileMessage = new Ext.SplitButton(this.action_fileRecord);
    this.action_toggleReadingConfirmation = new Ext.Action({
      text: this.app.i18n._('Reading Confirmation'),
      handler: this.onToggleReadingConfirmation,
      iconCls: 'felamimail-action-reading-confirmation',
      disabled: false,
      scope: this,
      enableToggle: true
    });
    this.button_toggleReadingConfirmation = Ext.apply(new Ext.Button(this.action_toggleReadingConfirmation), {
      tooltip: this.app.i18n._('Activate this toggle button to receive a reading confirmation.')
    });
    this.action_toggleEncrypt = new Ext.Action({
      text: this.app.i18n._('Encrypt Email'),
      toggleHandler: this.onToggleEncrypt,
      iconCls: 'felamimail-action-decrypt',
      disabled: false,
      pressed: false,
      hidden: true,
      scope: this,
      enableToggle: true
    });
    this.button_toggleEncrypt = Ext.apply(new Ext.Button(this.action_toggleEncrypt), {
      tooltip: this.app.i18n._('Encrypt email using Mailvelope')
    });
    this.action_massMailing = new Ext.Action({
      text: this.app.i18n._('Mass Mailing'),
      handler: this.onToggleMassMailing,
      iconCls: 'FelamimailIconCls',
      disabled: false,
      scope: this,
      enableToggle: true
    });
    this.button_massMailing = Ext.apply(new Ext.Button(this.action_massMailing), {
      tooltip: this.app.i18n._('Activate this toggle button to send the mail as separate mail to each recipient.')
    });
    this.tbar = new Ext.Toolbar({
      defaults: {
        height: 43
      },
      items: [{
        xtype: 'buttongroup',
        columns: 7,
        items: [Ext.apply(new Ext.Button(this.action_send), {
          scale: 'medium',
          rowspan: 2,
          iconAlign: 'top'
        }), Ext.apply(new Ext.Button(this.action_cancel), {
          scale: 'medium',
          rowspan: 2,
          iconAlign: 'top'
        }), Ext.apply(new Ext.Button(this.action_searchContacts), {
          scale: 'medium',
          rowspan: 2,
          iconAlign: 'top',
          tooltip: this.app.i18n._('Click to search for and add recipients from the Addressbook.')
        }), this.action_saveAsDraft, this.action_saveAsTemplate, this.button_fileMessage, this.button_toggleReadingConfirmation, this.button_toggleEncrypt, this.button_massMailing]
      }]
    });
  },
  /**
   * @private
   */
  initRecord: function () {
    this.decodeMsgs();
    this.recordDefaults = Tine.Felamimail.Model.Message.getDefaultData();
    if (this.mailAddresses) {
      this.recordDefaults.to = Ext.decode(this.mailAddresses);
    } else if (this.selectionFilter) {
      // put filter into to, cc or bcc of record and the loading be handled by resolveRecipientFilter
      var filterAndTo = Ext.decode(this.selectionFilter);
      this.record.set(filterAndTo.to.toLowerCase(), filterAndTo.filter);
    }
    if (!this.record) {
      this.record = new Tine.Felamimail.Model.Message(this.recordDefaults, 0);
    }
    this.initAccountCombo();
    this.initFrom();
    this.initRecipients();
    this.initSubject();
    this.initContent();
    const currentAccount = Tine.Tinebase.appMgr.get('Felamimail').getAccountStore().getById(this.record.get('account_id'));

    // legacy handling:...
    // TODO add this information to attachment(s) + flags and remove this
    if (this.replyTo) {
      this.record.set('flags', '\\Answered');
      this.record.set('original_id', this.replyTo.id);
    } else if (this.forwardMsgs) {
      this.record.set('flags', 'Passed');
      this.record.set('original_id', this.forwardMsgs[0].id);
    } else if (this.draftOrTemplate) {
      this.record.set('original_id', this.draftOrTemplate.id);
    }
    this.button_fileMessage.toggle(currentAccount.get('message_sent_copy_behavior') !== 'skip');
    Tine.log.debug('Tine.Felamimail.MessageEditDialog::initRecord() -> record:');
    Tine.log.debug(this.record);
  },
  /**
   * show loadMask (loadRecord is false in this dialog)
   * @param {} ct
   * @param {} position
   */
  onRender: function (ct, position) {
    Tine.Felamimail.MessageEditDialog.superclass.onRender.call(this, ct, position);
    this.showLoadMask();
  },
  /**
   * handle attachments: attaches message attachments depends on emailForward preference
   * - keeps attachments as they are (emailForward -> message or when the message is draft or Template)
   * - keeps original message/attachments and attach original email as .eml attachment(messageAndAsAttachment)
   * - only attach original email as .eml attachment (onlyAsAttachment)
   *
   * @param {Tine.Felamimail.Model.Message} message
   */
  handleAttachmentsOfExistingMessage: function (message) {
    if (!this.isForwardedMessage() && !this.draftOrTemplate) return;
    const attachments = [];
    const forwardMode = !this.isForwardedMessage() ? '' : Tine[this.app.appName].registry.get('preferences').get('emlForward');
    if (forwardMode === 'message' || this.draftOrTemplate) {
      Ext.each(message.get('attachments'), function (attachment) {
        attachment = {
          name: attachment['filename'],
          type: attachment['content-type'],
          size: attachment['size'],
          id: message.id + '_' + attachment['partId']
        };
        attachments.push(attachment);
      }, this);
    }
    if (forwardMode === 'onlyAsAttachment' || forwardMode === 'messageAndAsAttachment') {
      const node = message.get('from_node');
      let rfc822Attachment = {
        name: message.get('subject'),
        type: 'message/rfc822',
        size: message.get('size'),
        id: message.id
      };
      if (node) {
        rfc822Attachment = _.assign(rfc822Attachment, {
          type: 'file',
          size: node.size,
          path: node.path,
          name: node.name,
          attachment_type: 'attachment'
        });
      }
      attachments.push(rfc822Attachment);
    }
    this.record.set('attachments', attachments);
  },
  /**
   * inits body and attachments from reply/forward/template
   *
   * @param {} message
   */
  initContent: function (message) {
    if (!this.record.get('body')) {
      const account = Tine.Tinebase.appMgr.get('Felamimail').getAccountStore().getById(this.record.get('account_id'));
      // we follow the account compose_format when fetch msg failed
      const format = account && account.get('compose_format') !== '' ? 'text/' + account.get('compose_format') : message.getBodyType();
      if (!this.msgBody) {
        message = this.getMessageFromConfig();
        if (message) {
          // fixme : how to deal with preserve_format = true && format = text/html && message.get('body_content_type') = text/plain ?
          // the conflict case : we want to show text/html but we also want to preserve format as text/plain
          let fetchType = format;
          if (message.bodyIsFetched() && account.get('preserve_format')) {
            // format of the received message. this is the format to preserve
            fetchType = message.get('body_content_type');
          }
          if (!message.bodyIsFetched() || fetchType !== message.getBodyType()) {
            // self callback when body needs to be (re) fetched
            return this.recordProxy.fetchBody(message, fetchType, {
              success: this.initContent.createDelegate(this),
              // set format to message body format if fetch fails
              failure: message.bodyIsFetched() ? this.initContent.createDelegate(this, [message]) : null
            });
          }
          this.setMessageBody(message, account, format);
          this.handleAttachmentsOfExistingMessage(message);
          let folder = this.app.getFolderStore().getById(message.get('folder_id'));
          if (folder) {
            this.isDraft = folder.get('globalname') === account.get('drafts_folder');
            this.isTemplate = folder.get('globalname') === account.get('templates_folder');
            if (this.isDraft) {
              this.record.set('messageuid', message.get('messageuid'));
              this.draftUid = message.get('messageuid');
            }
            if (this.isTemplate) {
              this.templateId = message.get('id');
            }
          }
        }
      }
      this.record.set('content_type', format);
      this.record.set('body', this.msgBody);
    }
    if (this.attachments) {
      this.handleExternalAttachments();
    }
    delete this.msgBody;
    this.onRecordLoad();
  },
  /**
   * handle attachments like external URLs (COSR)
   *
   * TODO: check if this overwrites existing attachments in some cases
   */
  handleExternalAttachments: function () {
    this.attachments = Ext.isArray(this.attachments) ? this.attachments : [this.attachments];
    var attachments = [];
    Ext.each(this.attachments, function (attachment) {
      // external URL with COSR header enabled
      if (Ext.isString(attachment)) {
        attachment = {
          url: attachment
        };
      }
      attachments.push(attachment);
    }, this);
    this.record.set('attachments', attachments);
    delete this.attachments;
  },
  /**
   * set message body: converts newlines, adds quotes
   *
   * @param {Tine.Felamimail.Model.Message} message
   * @param {Tine.Felamimail.Model.Account} account
   * @param {String}                        format
   */
  setMessageBody: function (message, account, format) {
    var preparedParts = message.get('preparedParts');
    this.msgBody = message.get('body');
    if (preparedParts && preparedParts.length > 0) {
      if (preparedParts[0].contentType == 'application/pgp-encrypted') {
        this.quotedPGPMessage = preparedParts[0].preparedData;
        this.msgBody = this.msgBody + this.app.i18n._('Encrypted Content');
        var me = this;
        this.isRendered().then(function () {
          me.button_toggleEncrypt.toggle();
        });
      }
    }
    if (this.replyTo) {
      if (format === 'text/plain') {
        this.msgBody = String('> ' + this.msgBody).replace(/\r?\n/g, '\n> ');
      } else {
        const blockquote = document.createElement('blockquote');
        blockquote.className = 'felamimail-body-blockquote';
        if (message.getBodyType() === 'text/plain') {
          blockquote.innerText = this.msgBody;
        } else {
          blockquote.innerHTML = this.msgBody;
        }
        this.msgBody = '<br/>' + blockquote.outerHTML;
      }
    }
    this.msgBody = this.getQuotedMailHeader(format) + this.msgBody;
    if (this.isForwardedMessage()) {
      const forwardMode = Tine[this.app.appName].registry.get('preferences').get('emlForward');
      if (forwardMode === 'onlyAsAttachment') {
        this.msgBody = '';
      }
    }
  },
  /**
   * returns true if message is forwarded
   *
   * @return {Boolean}
   */
  isForwardedMessage: function () {
    return this.forwardMsgs && this.forwardMsgs.length === 1;
  },
  getSignaturePosition: function (account) {
    return _.get(account, 'data.signature_position', 'below') ?? null;
  },
  /**
   * add default signature to message
   *
   */
  addDefaultSignature: function () {
    if (this.draftOrTemplate) return;
    this.updateSignature(this.record.get('account_id'), null, this.record.get('body'));
    const bodyContent = this.record.get('body');
    const format = this.record.get('content_type');
    const ch = format === 'text/html' ? '<br>' : '\n';
    if (bodyContent && bodyContent !== '' && !bodyContent.startsWith(ch)) {
      this.record.set('body', `${ch}${ch}${bodyContent}`);
      this.msgBody = this.record.get('body');
      this.bodyCards.layout.activeItem.setValue(this.msgBody);
    }
  },
  /**
   * get account signature text
   *
   * @param {Tine.Felamimail.Model.Account} account
   * @param {String} format
   * @param signature
   */
  getSignature: function (account, format, signature) {
    let signatureText = Tine.Felamimail.getSignature(account, signature);
    if (format === 'text/plain') {
      signatureText = Tine.Tinebase.common.html2text(signatureText);
    }
    return signatureText;
  },
  /**
   * inits / sets sender of message
   */
  initFrom: function () {
    if (!this.record.get('account_id')) {
      var _currentAccount$data, _currentAccount$data$;
      if (!this.accountId) {
        const message = this.getMessageFromConfig();
        const availableAccounts = this.accountCombo.store;
        const fromEmail = message ? message.get('from_email') : null;
        const fromAccountIdx = availableAccounts.find('email', fromEmail);
        const fromAccount = availableAccounts.getAt(fromAccountIdx);
        const folderId = message ? message.get('folder_id') : null;
        const folder = folderId ? Tine.Tinebase.appMgr.get('Felamimail').getFolderStore().getById(folderId) : null;
        let accountId = folder ? folder.get('account_id') : null;
        if (!accountId) {
          const activeAccount = Tine.Tinebase.appMgr.get('Felamimail').getActiveAccount();
          accountId = activeAccount ? activeAccount.id : null;
        }
        if (!this.replyTo) {
          this.from = fromAccount;
        }
        this.accountId = accountId;
      }
      const currentAccountId = this.accountId;
      const defaultAccountId = Tine.Felamimail.registry.get('preferences').get('defaultEmailAccount');
      const currentAccount = Tine.Tinebase.appMgr.get('Felamimail').getAccountStore().getById(this.accountId);
      if (!((_currentAccount$data = currentAccount.data) !== null && _currentAccount$data !== void 0 && (_currentAccount$data$ = _currentAccount$data.account_grants) !== null && _currentAccount$data$ !== void 0 && _currentAccount$data$.addGrant)) {
        this.accountId = defaultAccountId;
        this.showReplacedMailSenderNotification(currentAccountId, defaultAccountId);
      }
      this.record.set('account_id', this.accountId);
    }
    delete this.accountId;
  },
  /**
   * after render
   */
  afterRender: async function () {
    Tine.Felamimail.MessageEditDialog.superclass.afterRender.apply(this, arguments);
    this.getEl().on(Ext.EventManager.useKeydown ? 'keydown' : 'keypress', this.onKeyPress, this);
    this.recipientGrid.on('specialkey', function (field, e) {
      this.onKeyPress(e);
    }, this);
    this.htmlEditor.on('keydown', function (ed, e) {
      this.onKeyPress(e);
    }, this);
    this.htmlEditor.on('toggleFormat', this.onToggleFormat, this);
    this.initHtmlEditorDD();
    await (0,util_waitFor_es6__WEBPACK_IMPORTED_MODULE_0__/* ["default"] */ .Z)(() => {
      return this.recipientGrid.store.getCount() > 0;
    });
    _.delay(() => {
      // recipientGrid should have 1 empty item by default
      if (this.recipientGrid.store.getCount() > 1) {
        if (!this.subjectField.getValue()) {
          this.subjectField.focus();
        } else {
          this.htmlEditor.focus();
        }
      }
    }, 1000);
    if (this.record.get('massMailingFlag')) {
      this.button_massMailing.toggle();
      await this.switchMassMailingMode(this.record.get('massMailingFlag'));
    }
  },
  initHtmlEditorDD: function () {
    return;
    if (!this.htmlEditor.rendered) {
      return this.initHtmlEditorDD.defer(500, this);
    }
    this.htmlEditor.getDoc().addEventListener('dragover', function (e) {
      this.action_addAttachment.plugins[0].onBrowseButtonClick();
    }.createDelegate(this));
    this.htmlEditor.getDoc().addEventListener('drop', function (e) {
      this.action_addAttachment.plugins[0].onDrop(Ext.EventObject.setEvent(e));
    }.createDelegate(this));
  },
  /**
   * on key press
   * @param {} e
   * @param {} t
   * @param {} o
   */
  onKeyPress: function (e, t, o) {
    if ((e.getKey() == e.TAB || e.getKey() == e.ENTER) && !e.shiftKey) {
      if (e.getTarget('input[name=subject]')) {
        this.htmlEditor.focus.defer(50, this.htmlEditor);
      } else if (e.getTarget('input[type=text]')) {
        this.subjectField.focus.defer(50, this.subjectField);
      }
    }
    if (e.getTarget('body')) {
      if (e.getKey() == e.ENTER && e.ctrlKey) {
        this.onSaveAndClose();
      } else if (e.getKey() == e.TAB && e.shiftKey) {
        this.subjectField.focus.defer(50, this.subjectField);
      }
    }
    this.checkStates();
  },
  checkStates: function () {
    if (this.recipientGrid.initialLoad) {
      return _.delay(_.bind(this.checkStates, this), 250);
    }
    Tine.Felamimail.MessageEditDialog.superclass.checkStates.apply(this, arguments);
    if (this.autoSave && _.keys(this.record.getChanges()).length) {
      this.trottledsaveAsDraft();
    }
  },
  saveAsDraft: function () {
    let me = this;
    me.record.set('messageuid', me.draftUid);
    me.record.commit();
    me.action_saveAsDraft.setIconClass('x-btn-wait');
    return me.saveAsDraftPromise = retryAllRejectedPromises([() => {
      return Tine.Felamimail.saveDraft(me.record.data)
      // TODO log failures here for debugging
      .then(savedDraft => {
        if (!me.draftUid) {
          this.updateFolderCount('drafts_folder', 1);
        }
        me.draftUid = savedDraft.messageuid;
      }).finally(() => {
        me.action_saveAsDraft.setIconClass('action_saveAsDraft');
      });
    }], {
      maxAttempts: 5,
      delay: 500
    });
  },
  deleteDraft: async function (draftUid) {
    return await Tine.Felamimail.deleteDraft(draftUid, this.record.get('account_id'));
  },
  onBeforeCancel: function () {
    if (this.autoSave) {
      this.trottledsaveAsDraft.cancel();
    }
    if (this.draftUid) {
      Ext.MessageBox.show({
        title: this.app.i18n._('Discard this Draft?'),
        msg: this.app.i18n._('Do you want to discard the current draft?'),
        buttons: Ext.MessageBox.YESNO,
        fn: btn => {
          this.showLoadMask().then(() => {
            return btn === 'yes' ? this.deleteDraft(this.draftUid).then(() => {
              this.updateFolderCount('drafts_folder', -1);
            }) : this.saveAsDraft();
          }).then(_.bind(this.window.close, this.window, true));
        },
        icon: Ext.MessageBox.QUESTION
      });
      return false;
    }
  },
  /**
   * returns message passed with config
   *
   * @return {Tine.Felamimail.Model.Message}
   */
  getMessageFromConfig: function () {
    return this.replyTo ? this.replyTo : this.forwardMsgs && this.forwardMsgs.length === 1 ? this.forwardMsgs[0] : this.draftOrTemplate ? this.draftOrTemplate : null;
  },
  /**
   * inits to/cc/bcc
   */
  initRecipients: function () {
    if (this.replyTo) {
      this.initReplyRecipients();
    }
    Ext.each(['to', 'cc', 'bcc'], function (field) {
      if (this.draftOrTemplate) {
        this[field] = this.draftOrTemplate.get(field);
      }
      if (!this.record.get(field)) {
        this[field] = Ext.isArray(this[field]) ? this[field] : Ext.isString(this[field]) ? [this[field]] : [];
        this.record.set(field, this[field]);
      }
      delete this[field];
      this.resolveRecipientFilter(field);
    }, this);
  },
  /**
   * init recipients from reply/replyToAll information
   */
  initReplyRecipients: async function () {
    // should resolve recipients here , save data
    const replyTo = this.replyTo.get('headers')['reply-to'];
    if (replyTo) {
      this.to = replyTo;
    } else {
      const email = this.replyTo.get('from_email');
      const name = this.replyTo.get('from_name');
      this.to = [{
        'email': email ?? '',
        'email_type': '',
        'type': '',
        'n_fileas': '',
        'name': name !== email ? name : '',
        'contact_record': ''
      }];

      // we might get the recipient token from server
      if (this.replyTo.get('from')) {
        this.to = this.replyTo.get('from');
      }
    }
    if (this.replyToAll) {
      if (!Ext.isArray(this.to)) {
        this.to = [this.to];
      }
      this.to = this.to.concat(this.replyTo.get('to'));
      this.cc = this.replyTo.get('cc');

      // remove own email and all non-email strings/objects from to/cc
      const account = Tine.Tinebase.appMgr.get('Felamimail').getAccountStore().getById(this.record.get('account_id'));
      const ownEmailRegexp = new RegExp(window.lodash.escapeRegExp(account.get('email')));
      Ext.each(['to', 'cc'], function (field) {
        this[field] = _.filter(this[field], addressData => {
          const email = addressData.email ?? addressData;
          return Ext.isString(email) && email.match(/@/) && !ownEmailRegexp.test(email);
        });
      }, this);
    }
  },
  /**
   * resolve recipient filter / queries addressbook
   *
   * @param {String} field to/cc/bcc
   */
  resolveRecipientFilter: function (field) {
    if (!Ext.isEmpty(this.record.get(field)) && Ext.isObject(this.record.get(field)[0]) && (this.record.get(field)[0].operator || this.record.get(field)[0].condition)) {
      // found a filter
      var filter = this.record.get(field);
      this.record.set(field, []);
      this['AddressLoadMask'] = new Ext.LoadMask(Ext.getBody(), {
        msg: this.app.i18n._('Loading Mail Addresses')
      });
      this['AddressLoadMask'].show();
      Tine.Addressbook.searchContacts(filter, null, async function (response) {
        const mailAddresses = await Tine.Felamimail.GridPanelHook.prototype.getMailAddresses(response.results);
        this.record.set(field, mailAddresses);
        this.recipientGrid.syncRecipientsToStore([field], this.record, true, false);
        this['AddressLoadMask'].hide();
      }.createDelegate(this));
    }
  },
  /**
   * sets / inits subject
   */
  initSubject: function () {
    if (!this.record.get('subject')) {
      if (!this.subject) {
        if (this.replyTo) {
          this.setReplySubject();
        } else if (this.forwardMsgs) {
          this.setForwardSubject();
        } else if (this.draftOrTemplate) {
          this.subject = this.draftOrTemplate.get('subject');
        }
      }
      this.record.set('subject', this.subject);
    }
    delete this.subject;
  },
  /**
   * setReplySubject -> this.subject
   *
   * removes existing prefixes + just adds 'Re: '
   */
  setReplySubject: function () {
    var replyPrefix = 'Re: ',
      replySubject = this.replyTo.get('subject') ? this.replyTo.get('subject') : '',
      replySubject = replySubject.replace(/^((re|aw|antw|fwd|odp|sv|wg|tr|rép):\s*)*/i, replyPrefix);
    this.subject = replySubject;
  },
  /**
   * setForwardSubject -> this.subject
   */
  setForwardSubject: function () {
    this.subject = this.app.i18n._('Fwd:') + ' ';
    this.subject += this.forwardMsgs.length === 1 ? this.forwardMsgs[0].get('subject') : String.format(this.app.i18n._('{0} Message', '{0} Messages', this.forwardMsgs.length));
  },
  /**
   * decode this.replyTo / this.forwardMsgs from interwindow json transport
   */
  decodeMsgs: function () {
    if (Ext.isString(this.draftOrTemplate)) {
      this.draftOrTemplate = new this.recordClass(Ext.decode(this.draftOrTemplate));
    }
    if (Ext.isString(this.replyTo)) {
      this.replyTo = new this.recordClass(Ext.decode(this.replyTo));
    }
    if (Ext.isString(this.forwardMsgs)) {
      var msgs = [];
      Ext.each(Ext.decode(this.forwardMsgs), function (msg) {
        msgs.push(new this.recordClass(msg));
      }, this);
      this.forwardMsgs = msgs;
    }
  },
  /**
   * fix input fields layout
   */
  fixLayout: function () {
    if (!this.subjectField.rendered || !this.accountCombo.rendered || !this.recipientGrid.rendered) {
      return;
    }
    var scrollWidth = this.recipientGrid.getView().getScrollOffset();
    this.subjectField.setWidth(this.subjectField.getWidth() - scrollWidth + 1);
    this.accountCombo.setWidth(this.accountCombo.getWidth() - scrollWidth + 1);
  },
  /**
   * save message in folder
   *
   * @param {String} folderField
   */
  onSaveInFolder: function (folderField) {
    if (this.autoSave) {
      this.trottledsaveAsDraft.cancel();
    }
    this.flattenHtmlElements();
    this.onRecordUpdate();
    var account = Tine.Tinebase.appMgr.get('Felamimail').getAccountStore().getById(this.record.get('account_id')),
      folderName = account.get(folderField);
    Tine.log.debug('onSaveInFolder() - Save message in folder ' + folderName);
    Tine.log.debug(this.record);
    if (!folderName || folderName == '') {
      Ext.MessageBox.alert(i18n._('Failed'), String.format(this.app.i18n._('{0} account setting empty.'), folderField));
    } else if (this.attachmentGrid.isUploading()) {
      Ext.MessageBox.alert(i18n._('Failed'), this.app.i18n._('Files are still uploading.'));
    } else {
      this.loadMask.show();
      if (this.autoSave) {
        this.trottledsaveAsDraft.cancel();
      }
      this.recordProxy.saveInFolder(this.record, folderName, {
        scope: this,
        success: function (record) {
          try {
            this.fireEvent('update', Ext.util.JSON.encode(this.record.data));
          } finally {
            if (this.loadMask) {
              this.hideLoadMask();
            }
          }
          Promise.resolve().then(() => {
            if (this.draftUid) {
              return this.deleteDraft(this.draftUid);
            } else {
              this.updateFolderCount(folderField, 1);
            }
          }).then(_.bind(this.window.close, this.window, this));
        },
        failure: Tine.Felamimail.handleRequestException.createInterceptor(function () {
          this.hideLoadMask();
        }, this),
        timeout: 150000 // 3 minutes
      });
    }
  },

  updateFolderCount: function (folderName, count) {
    const account = this.app.getActiveAccount();
    const targetFolderId = account ? account.getSpecialFolderId(folderName) : null;
    const targetFolder = targetFolderId ? this.app.getFolderStore().getById(targetFolderId) : null;
    if (targetFolder) {
      targetFolder.set('cache_unreadcount', targetFolder.get('cache_unreadcount') + count);
      targetFolder.set('cache_totalcount', targetFolder.get('cache_totalcount') + count);
      targetFolder.set('cache_status', 'pending');
      targetFolder.commit();
    } else {
      Tine.log.info('Tine.Felamimail.MessageEditDialog::updateFolderCount() - target folder ' + folderName + ' no found');
    }
  },
  /**
   * toggle mass mailing
   *
   * @param {} button
   * @param {} e
   */
  onToggleMassMailing: async function (button, e) {
    const active = !this.record.get('massMailingFlag');
    if (!active && this.button_massMailing.pressed) this.button_massMailing.toggle();
    if (this.massMailingMode === active) return;
    this.record.set('massMailingFlag', active);
    await this.switchMassMailingMode(active);
  },
  async switchMassMailingMode(active) {
    this.massMailingMode = active;
    if (this.recipientGrid) this.recipientGrid.massMailingMode = active;
    this.massMailingInfoText.setVisible(active);
    if (active) {
      await this.recipientGrid.updateMassMailingRecipients();
    }
    this.doLayout();
  },
  onFileMessageSelectionChange: function (btn, selection) {
    const text = this.app.formatMessage('{locationCount, plural, one {This message will be filed at the following location} other {This message will be filed at the following locations}}: {locationsHtml}', {
      locationCount: selection.length,
      locationsHtml: Tine.Felamimail.MessageFileAction.getFileLocationText(selection, ', ')
    });
    this.messageFileInfoText.update(text);
    this.messageFileInfoText.setVisible(selection.length);
    this.doLayout();
  },
  /**
   * toggle Request Reading Confirmation
   */
  onToggleReadingConfirmation: function () {
    this.record.set('reading_conf', !this.record.get('reading_conf'));
  },
  onToggleEncrypt: function (btn, e) {
    btn.setIconClass(btn.pressed ? 'felamimail-action-encrypt' : 'felamimail-action-decrypt');
    const account = Tine.Tinebase.appMgr.get('Felamimail').getAccountStore().getById(this.record.get('account_id'));
    const text = this.bodyCards.layout.activeItem.getValue() || this.record.get('body');
    const format = this.record.getBodyType();
    const textEditor = format === 'text/html' ? this.htmlEditor : this.textEditor;
    this.bodyCards.layout.setActiveItem(btn.pressed ? this.mailvelopeWrap : textEditor);
    if (btn.pressed) {
      var me = this,
        textMsg = Tine.Tinebase.common.html2text(text),
        quotedMailHeader = '';
      if (this.quotedPGPMessage) {
        textMsg = this.getSignature(account, 'text/plain');
        quotedMailHeader = Ext.util.Format.htmlDecode(me.getQuotedMailHeader('text/plain'));
        quotedMailHeader = quotedMailHeader.replace(/\n/, "\n>");
      }
      Tine.Felamimail.mailvelopeHelper.getKeyring().then(function (keyring) {
        mailvelope.createEditorContainer('#' + me.mailvelopeWrap.id, keyring, {
          predefinedText: textMsg,
          quotedMailHeader: quotedMailHeader,
          quotedMail: me.quotedPGPMessage,
          keepAttachments: true,
          quota: 32 * 1024 * 1024
        }).then(function (editor) {
          me.mailvelopeEditor = editor;
        });
      });
      this.southPanel.collapse();
      this.southPanel.setVisible(false);
      this.btnAddAttachemnt.setDisabled(true);
      this.signatureCombo.setDisabled(true);
    } else {
      this.mailvelopeEditor = null;
      delete this.mailvelopeEditor;
      this.mailvelopeWrap.update('');
      this.southPanel.setVisible(true);
      this.btnAddAttachemnt.setDisabled(false);
      this.signatureCombo.setDisabled(false);
    }
  },
  /**
   * toggle format
   */
  onToggleFormat: function () {
    var source = this.bodyCards.layout.activeItem,
      format = source.mimeType,
      target = format === 'text/plain' ? this.htmlEditor : this.textEditor,
      convert = format === 'text/plain' ? Ext.util.Format.nl2br : Tine.Tinebase.common.html2text;
    if (format.match(/^text/)) {
      this.record.set('content_type', format);
      this.bodyCards.layout.setActiveItem(target);
      target.setValue(convert(source.getValue()));
    } else {
      // ignore toggle request for encrypted content
    }
  },
  /**
   * get quoted mail header
   *
   * @param format
   * @returns {String}
   */
  getQuotedMailHeader: function (format) {
    let header = '';
    if (this.replyTo) {
      const date = this.replyTo.get('sent') ? this.replyTo.get('sent') : this.replyTo.get('received') ? this.replyTo.get('received') : new Date();
      header = String.format(this.app.i18n._('On {0}, {1} wrote'), Tine.Tinebase.common.dateTimeRenderer(date), Ext.util.Format.htmlEncode(this.replyTo.get('from_name'))) + ':\n';
    } else if (this.isForwardedMessage()) {
      const forwardMode = Tine[this.app.appName].registry.get('preferences').get('emlForward');
      if (forwardMode !== 'onlyAsAttachment') {
        header = String.format('{0}-----' + this.app.i18n._('Original message') + '-----{1}', format == 'text/plain' ? '' : '<br /><b>', format == 'text/plain' ? '\n' : '</b><br />') + Tine.Felamimail.GridPanel.prototype.formatHeaders(this.forwardMsgs[0].get('headers'), false, true, format == 'text/plain') + (format == 'text/plain' ? '' : '<br /><br />');
      }
    }
    if (format === 'text/html' && header !== '') {
      const span = document.createElement('span');
      span.className = 'felamimail-body-quoted-header';
      span.innerHTML = header;
      return span.outerHTML;
    }
    return header;
  },
  /**
   * search for contacts as recipients
   */
  onSearchContacts: function () {
    Tine.Felamimail.RecipientPickerDialog.openWindow({
      record: Ext.encode(Ext.copyTo({}, this.record.data, ['subject', 'to', 'cc', 'bcc'])),
      listeners: {
        scope: this,
        'update': function (record) {
          const messageWithRecipients = Ext.isString(record) ? new this.recordClass(Ext.decode(record)) : record;
          this.recipientGrid.syncRecipientsToStore(['to', 'cc', 'bcc'], messageWithRecipients, true, true);
        }
      }
    });
  },
  /**
   * executed after record got updated from proxy
   *
   * @private
   */
  onRecordLoad: function () {
    var _this$htmlEditor;
    // interrupt process flow till dialog is rendered
    if (!this.rendered || this.record.get('content_type') === 'text/html' && !((_this$htmlEditor = this.htmlEditor) !== null && _this$htmlEditor !== void 0 && _this$htmlEditor.initialized)) {
      this.onRecordLoad.defer(250, this);
      return;
    }
    let title = this.app.i18n._('Compose email:');
    const editor = this.record.get('content_type') === 'text/html' ? this.htmlEditor : this.textEditor;
    if (this.record.get('subject')) {
      title = title + ' ' + this.record.get('subject');
    }
    this.window.setTitle(title);
    if (!this.button_toggleEncrypt.pressed) {
      editor.setValue(this.record.get('body'));
      this.bodyCards.layout.setActiveItem(editor);
    }

    // to make sure we have all recipients (for example when composing from addressbook with "all pages" filter)
    var ticketFn = this.onAfterRecordLoad.deferByTickets(this),
      wrapTicket = ticketFn();
    this.fireEvent('load', this, this.record, ticketFn);
    wrapTicket();
    this.getForm().loadRecord(this.record);
    this.attachmentGrid.loadRecord(this.record);
    if (this.from) {
      this.accountCombo.setValue(this.from.id);
    }
    this.addDefaultSignature();
    this.updateFileLocations();
    this.onFileMessageSelectionChange('', this.action_fileRecord.getSelected());
    this.onAfterRecordLoad();
  },
  updateFileLocations: function () {
    var _this$button_fileMess;
    const selections = this !== null && this !== void 0 && (_this$button_fileMess = this.button_fileMessage) !== null && _this$button_fileMess !== void 0 && _this$button_fileMess.pressed ? this.action_fileRecord.getSelected() : [];
    const fileLocations = [];
    const imapFolderIds = [];
    selections.forEach(item => {
      if ((item === null || item === void 0 ? void 0 : item.type) === 'folder') {
        var _item$record_id;
        if (item !== null && item !== void 0 && (_item$record_id = item.record_id) !== null && _item$record_id !== void 0 && _item$record_id.id) imapFolderIds.push(item.record_id.id);
      } else {
        fileLocations.push(item);
      }
    });
    this.record.set('fileLocations', fileLocations);
    this.record.set('sent_copy_folder', imapFolderIds);
  },
  /**
   * overwrite, just hide the loadMask
   */
  onAfterRecordLoad: function () {
    (function () {
      var autoSave = this.autoSave;
      this.autoSave = false;
      this.checkStates();
      this.record.commit();
      this.autoSave = autoSave;
    }).defer(100, this);
    if (this.loadMask) {
      this.hideLoadMask();
    }
  },
  /**
   * executed when record gets updated from form
   * - add attachments to record here
   * - add alias / from
   *
   * @private
   */
  onRecordUpdate: function () {
    this.record.data.attachments = [];
    var attachmentData = null;
    const format = this.bodyCards.layout.activeItem.mimeType;
    if (format.match(/^text/)) {
      const editor = format === 'text/html' ? this.htmlEditor : this.textEditor;
      this.record.set('content_type', format);
      this.record.set('body', editor.getValue());
    }
    this.attachmentGrid.store.each(function (attachment) {
      var fileData = Ext.copyTo({}, attachment.data, ['tempFile', 'name', 'path', 'size', 'type', 'id', 'attachment_type', 'password']);
      this.record.data.attachments.push(fileData);
    }, this);
    const accountId = this.accountCombo.getValue();
    const account = this.accountCombo.getStore().getById(accountId);
    const emailFrom = account.get('email');
    this.record.set('from_email', emailFrom);
    this.record.set('from_name', account.get('from'));
    Tine.Felamimail.MessageEditDialog.superclass.onRecordUpdate.call(this);
    this.record.set('account_id', account.get('original_id'));
    this.updateFileLocations();

    // need to sync once again to make sure we have the correct recipients
    this.recipientGrid.syncRecipientsToRecord();
  },
  onAfterApplyChanges: async function (closeWindow) {
    // grr. onRecordLoad hides loadMask
    this.showLoadMask.defer(10, this);
    if (this.autoSave) {
      await this.saveAsDraftPromise.then(() => {
        if (this.draftUid) {
          // autodelete draft when message is send
          return this.deleteDraft(this.draftUid);
        }
      });
    }
    Tine.Felamimail.MessageEditDialog.superclass.onAfterApplyChanges.call(this, closeWindow);
  },
  /**
   * init attachment grid + add button to toolbar
   */
  initAttachmentGrid: function () {
    if (!this.attachmentGrid) {
      this.attachmentGrid = new Tine.Felamimail.AttachmentUploadGrid({
        fieldLabel: this.app.i18n._('Attachments'),
        hideLabel: true,
        filesProperty: 'attachments',
        // TODO     think about that -> when we deactivate the top toolbar, we lose the dropzone for files!
        //showTopToolbar: false,
        anchor: '100% 95%'
      });

      // add file upload button to toolbar

      this.action_addAttachment = this.attachmentGrid.getAddAction();
      this.action_addAttachment.plugins[0].dropElSelector = 'div[id=' + this.id + ']';
      this.attachmentGrid.on('filesSelected', function (nodes) {
        this.southPanel.expand();
      }, this);
      this.btnAddAttachemnt = new Ext.Button(this.action_addAttachment);
      this.tbar.get(0).insert(2, Ext.apply(this.btnAddAttachemnt, {
        scale: 'medium',
        rowspan: 2,
        iconAlign: 'top'
      }));
    }
  },
  /**
   * init account (from) combobox
   *
   * - need to create a new store with an account record for each alias
   */
  initAccountCombo: function () {
    var accountStore = Tine.Tinebase.appMgr.get('Felamimail').getAccountStore(),
      accountComboStore = new Ext.data.ArrayStore({
        fields: Tine.Felamimail.Model.Account
      });
    var aliasAccount = null,
      aliases = null,
      id = null;
    accountStore.each(function (account) {
      aliases = [account.get('email')];
      if (account.get('type') === 'system') {
        // add identities / aliases to store (for systemaccounts)
        var user = Tine.Tinebase.registry.get('currentAccount');
        var systemAliases = _.get(user, 'emailUser.emailAliases', []) || [];
        if (systemAliases.length > 0 && typeof systemAliases[0].dispatch_address !== 'undefined') {
          var systemAliasAdresses = _.reduce(systemAliases, (aliases, alias) => {
            if (!!+alias.dispatch_address) {
              aliases.push(alias.email);
            }
            return aliases;
          }, []);
        } else {
          var systemAliasAdresses = systemAliases;
        }
        aliases = aliases.concat(systemAliasAdresses);
      }
      for (var i = 0; i < aliases.length; i++) {
        var _aliasAccount$data, _aliasAccount$data$ac;
        id = i == 0 ? account.id : Ext.id();
        aliasAccount = account.copy(id);
        if (i > 0) {
          aliasAccount.data.id = id;
          aliasAccount.set('email', aliases[i]);
        }
        let name = aliasAccount.get('from') ? aliasAccount.get('from') : aliasAccount.get('name');
        aliasAccount.set('name', name + ' (' + aliases[i] + ')');
        aliasAccount.set('original_id', account.id);

        // only add to combo if the account has email send grant
        if ((_aliasAccount$data = aliasAccount.data) !== null && _aliasAccount$data !== void 0 && (_aliasAccount$data$ac = _aliasAccount$data.account_grants) !== null && _aliasAccount$data$ac !== void 0 && _aliasAccount$data$ac.addGrant) {
          accountComboStore.add(aliasAccount);
        }
      }
    }, this);
    this.accountCombo = new Ext.form.ComboBox({
      name: 'account_id',
      ref: '../../accountCombo',
      plugins: [Ext.ux.FieldLabeler],
      fieldLabel: this.app.i18n._('From'),
      displayField: 'name',
      valueField: 'id',
      editable: false,
      triggerAction: 'all',
      store: accountComboStore,
      mode: 'local',
      listeners: {
        scope: this,
        select: this.onFromSelect
      }
    });
  },
  initSignatureCombo: function () {
    let me = this;
    let signature = this.app.getDefaultSignature(this.record.get('account_id'));
    this.signatureCombo = new Ext.form.ComboBox({
      displayField: 'name',
      value: _.get(signature, 'data.name', this.app.i18n._('None')),
      editable: false,
      triggerAction: 'all',
      store: new Ext.data.JsonStore({
        fields: Tine.Felamimail.Model.Signature,
        listeners: {
          scope: this,
          beforeload: (store, options) => {
            let account = this.app.getAccountStore().getById(this.record.get('account_id'));
            let signatures = _.concat({
              name: this.app.i18n._('None'),
              id: 'none'
            }, _.get(account, 'data.signatures', []));
            store.loadData(signatures);
            me.signatureCombo.lastQuery = Tine.Tinebase.data.Record.generateUID();
            return false;
          }
        }
      }),
      listeners: {
        scope: this,
        beforeselect: (combo, signature, index) => {
          this.updateSignature(signature.get('account_id'), signature);
        }
      }
    });
  },
  /**
   * updates signature in mail body
   *
   */
  updateSignature: function () {
    let accountId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
    let newSignatureRecord = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
    let bodyContent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
    accountId = accountId && accountId !== '' ? accountId : this.record.get('account_id');
    bodyContent = this.bodyCards.layout.activeItem.getValue() || bodyContent;
    const format = this.record.get('content_type');
    const oldAccount = this.app.getAccountStore().getById(this.record.get('account_id'));
    const oldSignatureRecord = this.signatureCombo.store.data.items.find(r => {
      var _r$data;
      return (r === null || r === void 0 ? void 0 : (_r$data = r.data) === null || _r$data === void 0 ? void 0 : _r$data.name) === this.signatureCombo.getValue();
    });
    const oldSignature = this.getSignature(oldAccount, format, oldSignatureRecord);
    const oldPosition = this.getSignaturePosition(oldAccount);
    const newAccount = this.app.getAccountStore().getById(accountId);
    const newSignature = this.getSignature(newAccount, format, newSignatureRecord);
    const newPosition = this.getSignaturePosition(newAccount);
    let matches = [];
    if (format === 'text/plain') {
      const resolvedSignature = newSignature === '' ? '' : `--\n${newSignature}`;
      if (oldSignature !== '') {
        matches = [...bodyContent.matchAll(new RegExp(`-*\\s*${oldSignature}`, 'g'))];
      }
      if (matches.length > 0) {
        const match = oldPosition === 'above' ? matches[0] : matches[matches.length - 1];
        const startPos = match.index;
        const endPos = match.index + match[0].length;
        if (oldPosition === newPosition) {
          // always remove old signature and replace new signature if found , it might be in the middle of the message
          bodyContent = bodyContent.slice(0, startPos) + resolvedSignature + bodyContent.slice(endPos);
        } else {
          bodyContent = bodyContent.slice(0, startPos) + bodyContent.slice(endPos);
          bodyContent = this.appendSignatureText(bodyContent, format, newPosition, resolvedSignature);
        }
      } else {
        bodyContent = this.appendSignatureText(bodyContent, format, newPosition, resolvedSignature);
      }
    } else {
      matches = this.htmlEditor.getDoc().getElementsByClassName('felamimail-body-signature-current');
      while (matches.length > 0) {
        // if the existing signature is unchanged, it should be removed here
        matches[0].outerHTML = matches[0].innerHTML.replace(`--<br>${oldSignature}`, '');
      }
      const resolvedSignature = newSignature === '' ? '' : `--<br>${newSignature}`;
      // bodyContent = this.appendSignatureHTML(format, newPosition, resolvedSignature);
      const signatureElement = document.createElement('span');
      signatureElement.className = 'felamimail-body-signature-current';
      signatureElement.innerHTML = resolvedSignature;
      bodyContent = this.appendHtmlNode(signatureElement, newPosition);
    }
    this.record.set('body', bodyContent);
    this.msgBody = this.record.get('body');
    this.bodyCards.layout.activeItem.setValue(bodyContent);
  },
  appendSignatureText(bodyContent, format, position, signatureText) {
    const ch = '\n';
    if (position === 'below' && !bodyContent.endsWith(ch)) bodyContent = `${bodyContent}${ch}${ch}`;
    if (position === 'above' && !bodyContent.startsWith(ch)) bodyContent = `${ch}${ch}${bodyContent}`;
    return position === 'above' ? `${ch}${ch}${signatureText}${bodyContent}` : `${bodyContent}${signatureText}`;
  },
  appendHtmlNode(element, position) {
    let targetClassName = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
    const quotedHeader = this.htmlEditor.getDoc().getElementsByClassName('felamimail-body-quoted-header')[0];
    const blockquote = this.htmlEditor.getDoc().getElementsByClassName('felamimail-body-blockquote')[0];
    const targetElement = targetClassName ? this.htmlEditor.getDoc().getElementsByClassName(targetClassName)[0] : null;
    let el = targetElement;
    if (!targetElement) el = position === 'above' ? quotedHeader : blockquote || quotedHeader;
    const insertNewLines = (target, count) => {
      for (let i = 0; i < count; i++) {
        var _target$previousSibli;
        if (!target.previousSibling || ((_target$previousSibli = target.previousSibling) === null || _target$previousSibli === void 0 ? void 0 : _target$previousSibli.nodeName) !== 'BR') target.insertAdjacentHTML("beforebegin", '<br>');
        target = target.previousSibling;
      }
      return target;
    };
    if (!el || position === 'below') {
      this.htmlEditor.getDoc().getElementsByTagName('body')[0].appendChild(element);
    } else {
      if (position === 'above') {
        const target = insertNewLines(el, 2);
        target.insertAdjacentElement("beforebegin", element);
      }
    }
    // add newline above new appended element
    el = this.htmlEditor.getDoc().getElementsByClassName(element.className)[0];
    if (el.innerHTML !== '') {
      insertNewLines(el, 2);
    }
    return this.htmlEditor.getDoc().getElementsByTagName('body')[0].innerHTML;
  },
  /**
   * if 'account_id' is changed we need to update the signature
   *
   * @param {} combo
   * @param {} record
   * @param {} index
   */
  onFromSelect: function (combo, record, index) {
    const newAccountId = record.get('original_id');
    const defaultSignature = this.app.getDefaultSignature(newAccountId);
    this.updateSignature(newAccountId, defaultSignature);
    this.signatureCombo.setValue(_.get(defaultSignature, 'data.name', this.app.i18n._('None')));

    // update reply-to
    const replyTo = record.get('reply_to');
    if (replyTo && replyTo !== '') {
      this.replyToField.setValue(replyTo);
    }
    this.record.set('account_id', newAccountId);
  },
  /**
   * returns dialog
   *
   * NOTE: when this method gets called, all initialisation is done.
   *
   * @return {Object}
   * @private
   */
  getFormItems: function () {
    this.initAttachmentGrid();
    this.initSignatureCombo();
    this.recipientGrid = new Tine.Felamimail.RecipientGrid({
      record: this.record,
      i18n: this.app.i18n,
      hideLabel: true,
      composeDlg: this,
      autoStartEditing: !this.AddressLoadMask
    });
    this.southPanel = new Ext.Panel({
      region: 'south',
      layout: 'form',
      height: 150,
      split: true,
      collapseMode: 'mini',
      header: false,
      collapsible: true,
      collapsed: this.record.bodyIsFetched() && (!this.record.get('attachments') || this.record.get('attachments').length == 0),
      items: [this.attachmentGrid]
    });
    this.textEditor = new Ext.Panel({
      layout: 'fit',
      mimeType: 'text/plain',
      cls: 'felamimail-edit-text-plain',
      flex: 1,
      // Take up all *remaining* vertical space
      setValue: function (v) {
        return this.items.get(0).setValue(v);
      },
      getValue: function () {
        return this.items.get(0).getValue();
      },
      tbar: ['->', {
        iconCls: 'x-edit-toggleFormat',
        tooltip: this.app.i18n._('Convert to formated text'),
        handler: this.onToggleFormat,
        scope: this
      }],
      items: [new Ext.form.TextArea({
        fieldLabel: this.app.i18n._('Body'),
        name: 'body_text'
      })]
    });
    this.htmlEditor = new Tine.Felamimail.ComposeEditor({
      border: false,
      fieldLabel: this.app.i18n._('Body'),
      name: 'body_html',
      mimeType: 'text/html',
      flex: 1 // Take up all *remaining* vertical space
    });

    this.mailvelopeWrap = new Ext.Container({
      flex: 1,
      // Take up all *remaining* vertical space
      mimeType: 'application/pgp-encrypted',
      getValue: function () {
        return '';
      }
    });
    const infoItems = [{
      // message file info text
      ref: '../../messageFileInfoText',
      style: 'display: block;'
    }, {
      // mass mailing info text
      html: this.app.i18n._('NOTE: This is mail will be sent as a mass mail, i.e. each recipient will get his or her own copy.'),
      ref: '../../massMailingInfoText',
      style: 'padding: 5px 0px; display: block;'
    }];
    const activeAccount = Tine.Tinebase.appMgr.get('Felamimail').getActiveAccount();
    return {
      border: false,
      frame: true,
      layout: 'border',
      items: [{
        ref: '../messageInfoFormPanel',
        layout: 'form',
        region: 'north',
        padding: '5px',
        border: false,
        autoHeight: true,
        align: 'left',
        labelWidth: 250,
        cls: 'felamimail-compose-info',
        margins: '0 0 10 0',
        defaults: {
          hidden: true,
          xtype: 'label'
        },
        items: infoItems
      }, {
        region: 'center',
        layout: {
          align: 'stretch',
          // Child items are stretched to full width
          type: 'vbox'
        },
        listeners: {
          'afterlayout': this.fixLayout,
          scope: this
        },
        items: [this.accountCombo, {
          // extuxclearabletextfield would be better, but breaks the layout big tim
          // TODO fix layout (equal width of input boxes)!
          xtype: 'textfield',
          plugins: [Ext.ux.FieldLabeler],
          fieldLabel: this.app.i18n._('Reply-To Email'),
          name: 'reply_to',
          ref: '../../replyToField',
          hidden: !Tine.Tinebase.appMgr.get('Felamimail').featureEnabled('showReplyTo'),
          emptyText: this.app.i18n._('Add email address here for reply-to'),
          // reply-to from account or email
          value: activeAccount ? activeAccount.get('reply_to') : ''
        }, this.recipientGrid, {
          xtype: 'textfield',
          plugins: [Ext.ux.FieldLabeler],
          fieldLabel: this.app.i18n._('Subject'),
          name: 'subject',
          ref: '../../subjectField',
          enableKeyEvents: true,
          maxLength: 998,
          // prevent long input
          autoCreate: {
            tag: 'input',
            type: 'text',
            maxlength: '900'
          },
          listeners: {
            scope: this,
            // update title on keyup event
            'keyup': function (field, e) {
              if (!e.isSpecialKey()) {
                this.window.setTitle(this.app.i18n._('Compose email:') + ' ' + field.getValue());
              }
            },
            'focus': function (field) {
              this.subjectField.focus(true, 100);
            }
          }
        }, new Ext.Toolbar({
          items: ['->', {
            xtype: 'tbtext',
            text: this.app.i18n._('Signature') + ':'
          }, this.signatureCombo]
        }), {
          layout: 'card',
          ref: '../../bodyCards',
          activeItem: 0,
          flex: 1,
          items: [this.textEditor, this.htmlEditor, this.mailvelopeWrap]
        }]
      }, this.southPanel]
    };
  },
  /**
   * is form valid (checks if attachments are still uploading / recipients set)
   *
   * @return {Boolean}
   */
  isValid: function () {
    var me = this;
    return Tine.Felamimail.MessageEditDialog.superclass.isValid.call(me).then(function () {
      if (me.attachmentGrid.isUploading()) {
        return Promise.reject(me.app.i18n._('Files are still uploading.'));
      }
      return me.validateRecipients();
    });
  },
  /**
   *
   * @return {Promise}
   */
  validateSystemlinkRecipients: function () {
    var me = this;
    return new Promise(async function (fulfill, reject) {
      const recipients = [];
      const resolvePromise = fulfill;
      me.recipientGrid.getStore().each(async function (recipient) {
        var _address;
        let address = recipient.get('address');
        if (!address) {
          return;
        }
        if ((_address = address) !== null && _address !== void 0 && _address.email) {
          var _address2;
          address = (_address2 = address) === null || _address2 === void 0 ? void 0 : _address2.email;
        }
        recipients.push(await me.extractMailFromString(address));
      });
      var hasSystemlinks = false;
      me.attachmentGrid.getStore().each(function (attachment) {
        if (attachment.get('attachment_type') === 'systemlink_fm') {
          hasSystemlinks = true;
          return false;
        }
      });
      if (hasSystemlinks) {
        Tine.Felamimail.doMailsBelongToAccount(recipients).then(function (res) {
          resolvePromise(Object.values(res));
        });
      } else {
        resolvePromise(false);
      }
    });
  },
  extractMailFromString: async function (string) {
    return await __webpack_require__.e(/* import() | Tinebase/js/email-addresses */ 5330).then(__webpack_require__.t.bind(__webpack_require__, 13023, 23)).then(_ref => {
      let {
        default: addrs
      } = _ref;
      const parsed = addrs.parseOneAddress(string.replace(',', ''));
      return parsed === null || parsed === void 0 ? void 0 : parsed.address;
    });
  },
  /**
   * generic apply changes handler
   * - NOTE: overwritten to check here if the subject is empty and if the user wants to send an empty message
   *
   * @param {Ext.Button} button
   * @param {Event} event
   * @param {Boolean} closeWindow
   */
  onApplyChanges: function (closeWindow, emptySubject, passwordSet, nonSystemAccountRecipients) {
    var me = this,
      _ = window.lodash;
    Tine.log.debug('Tine.Felamimail.MessageEditDialog::onApplyChanges()');
    this.loadMask.show();
    if (this.autoSave) {
      this.trottledsaveAsDraft.cancel();
    }
    if (Tine.Tinebase.appMgr.isEnabled('Filemanager') && undefined === nonSystemAccountRecipients) {
      this.validateSystemlinkRecipients().then(function (mails) {
        me.onApplyChanges(closeWindow, emptySubject, passwordSet, mails);
      });
      return;
    } else if (_.isArray(nonSystemAccountRecipients) && nonSystemAccountRecipients.length > 0) {
      let records = _.filter(me.recipientGrid.getStore().data.items, function (rec) {
        let match = false;
        _.each(nonSystemAccountRecipients, function (mail) {
          if (null !== rec.get('address').match(new RegExp(mail))) {
            match = true;
          }
        });
        return match;
      }.bind({
        'nonSystemAccountRecipients': nonSystemAccountRecipients
      }));
      _.each(records, function (rec) {
        var index = me.recipientGrid.getStore().indexOf(rec),
          row = me.recipientGrid.view.getRow(index);
        row.classList.add('felamimail-is-external-recipient');
      });
      Ext.MessageBox.confirm(this.app.i18n._('Warning'), this.app.i18n._('Some attachments are of type "systemlinks" whereas some of the recipients (marked yellow), couldn\'t be validated to be accounts on this installation. Only recipients with an active account will be able to open those attachments.') + "<br /><br />" + this.app.i18n._('Do you really want to send?'), function (button) {
        if (button == 'yes') {
          me.onApplyChanges(closeWindow, emptySubject, passwordSet, false);
        } else {
          this.hideLoadMask();
        }
      }, this);
      return;
    }

    // If filemanager attachments are possible check if passwords are required to enter
    if (Tine.Tinebase.appMgr.isEnabled('Filemanager') && passwordSet !== true) {
      var attachmentStore = this.attachmentGrid.getStore();
      if (attachmentStore.find('attachment_type', 'download_protected_fm') !== -1) {
        var dialog = new Tine.Tinebase.widgets.dialog.PasswordDialog();
        dialog.openWindow();

        // password entered
        dialog.on('apply', function (password) {
          attachmentStore.each(function (attachment) {
            if (attachment.get('attachment_type') === 'download_protected_fm') {
              attachment.data.password = password;
            }
          });
          me.onApplyChanges(closeWindow, emptySubject, true, nonSystemAccountRecipients);
        });

        // user presses cancel in dialog => allow to submit again or edit mail and so on!
        dialog.on('cancel', function () {
          this.hideLoadMask();
        }, this);
        return;
      }
    }
    if (!emptySubject && !this.getForm().findField('subject').getValue()) {
      Tine.log.debug('Tine.Felamimail.MessageEditDialog::onApplyChanges - empty subject');
      Ext.MessageBox.confirm(this.app.i18n._('Empty subject'), this.app.i18n._('Do you really want to send a message with an empty subject?'), function (button) {
        Tine.log.debug('Tine.Felamimail.MessageEditDialog::doApplyChanges - button: ' + button);
        if (button == 'yes') {
          this.onApplyChanges(closeWindow, true, true, nonSystemAccountRecipients);
        } else {
          this.hideLoadMask();
        }
      }, this);
      return;
    }
    this.flattenHtmlElements();
    this.record.set('body', this.bodyCards.layout.activeItem.getValue());
    Tine.log.debug('Tine.Felamimail.MessageEditDialog::doApplyChanges - call parent');
    this.doApplyChanges(closeWindow);
  },
  flattenHtmlElements() {
    const classesToRemove = ['felamimail-body-signature-current', 'felamimail-body-manage-consent-link'];
    classesToRemove.forEach(className => {
      const matches = this.htmlEditor.getDoc().getElementsByClassName(className);
      while (matches.length > 0) matches[0].outerHTML = matches[0].innerHTML;
    });
  },
  /**
   * checks recipients
   *
   * @return {Boolean}
   */
  validateRecipients: function () {
    var me = this;
    return new Promise(function (fulfill, reject) {
      var to = me.record.get('to'),
        cc = me.record.get('cc'),
        bcc = me.record.get('bcc'),
        bcc = me.record.get('bcc'),
        all = [].concat(to).concat(cc).concat(bcc);
      if (all.length == 0) {
        reject(me.app.i18n._('No recipients set.'));
      }
      if (me.button_toggleEncrypt.pressed && me.mailvelopeEditor) {
        // always add own address so send message can be decrypted
        all.push(me.record.get('from_email'));
        all = all.map(function (item) {
          return addressparser.parse(item.replace(/,/g, '\\\\,'))[0].address;
        });
        return Tine.Felamimail.mailvelopeHelper.getKeyring().then(function (keyring) {
          keyring.validKeyForAddress(all).then(function (result) {
            var missingKeys = [];
            for (var address in result) {
              if (!result[address]) {
                missingKeys.push(address);
              }
            }
            if (missingKeys.length) {
              reject(String.format(me.app.i18n._('Cannot encrypt message. Public keys for the following recipients are missing: {0}'), Ext.util.Format.htmlEncode(missingKeys.join(', '))));
            } else {
              // NOTE: we sync message here as we have a promise at hand and onRecordUpdate is done before validation
              return me.mailvelopeEditor.encrypt(all).then(function (armoredMessage) {
                me.record.set('body', armoredMessage);
                me.record.set('content_type', 'text/plain');
                // NOTE: Server would spoil MIME structure with attachments
                me.record.set('attachments', '');
                me.record.set('has_attachment', false);
                fulfill(true);
              });
            }
          });
        });
      } else {
        fulfill(true);
      }
    });
  },
  /**
   * get validation error message
   *
   * @return {String}
   */
  getValidationErrorMessage: function () {
    return this.validationErrorMessage;
  },
  /**
   * show notification for replaced mail sender
   *
   */
  showReplacedMailSenderNotification: function (currentAccountId, newAccountId) {
    var _newAccount$data, _currentAccount$data2;
    const currentAccount = this.app.getAccountStore().getById(currentAccountId);
    const newAccount = this.app.getAccountStore().getById(newAccountId);
    const title = this.app.i18n._('Attention');
    const message = String.format(this.app.i18n._('The from-address for this mail got changed to {0} , as you don\'t have the send mail right for the account {1}.'), newAccount === null || newAccount === void 0 ? void 0 : (_newAccount$data = newAccount.data) === null || _newAccount$data === void 0 ? void 0 : _newAccount$data.email, currentAccount === null || currentAccount === void 0 ? void 0 : (_currentAccount$data2 = currentAccount.data) === null || _currentAccount$data2 === void 0 ? void 0 : _currentAccount$data2.email);
    Ext.ux.MessageBox.msg(title, message, 5);
  }
});

/**
 * Felamimail Edit Popup
 *
 * @param   {Object} config
 * @return  {Ext.ux.Window}
 */
Tine.Felamimail.MessageEditDialog.openWindow = function (config) {
  var window = Tine.WindowFactory.getWindow({
    width: 750,
    height: 700,
    name: Tine.Felamimail.MessageEditDialog.prototype.windowNamePrefix + Ext.id(),
    contentPanelConstructor: 'Tine.Felamimail.MessageEditDialog',
    contentPanelConstructorConfig: config
  });
  return window;
};

/***/ }),

/***/ 46274:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _Tinebase_js_widgets_form_RecordEditFieldTriggerPlugin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(28198);
/*
 * Tine 2.0
 *
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Ching En Cheng <c.cheng@metaways.de>
 * @copyright   Copyright (c) 2022 Metaways Infosystems GmbH (http://www.metaways.de)
 */



/**
 * @namespace   Tine.widgets.tags
 * @class       Tine.widgets.tags.TagsMassDetachAction
 * @extends     Ext.Action
 */
Tine.Felamimail.MessageFileAction = function (config) {
  var _this$initialConfig, _this$initialConfig2;
  config.iconCls = 'action_file';
  config.app = Tine.Tinebase.appMgr.get('Felamimail');
  config.i18n = config.app.i18n;
  config.text = config.text ? config.text : config.i18n._('Save Message as');
  config.menu = new Ext.menu.Menu({});
  Ext.apply(this, config);
  Tine.Felamimail.MessageFileAction.superclass.constructor.call(this, config);
  if (!((_this$initialConfig = this.initialConfig) !== null && _this$initialConfig !== void 0 && _this$initialConfig.selectionModel) && (_this$initialConfig2 = this.initialConfig) !== null && _this$initialConfig2 !== void 0 && _this$initialConfig2.record) {
    _.assign(this.initialConfig, {
      selections: [this.initialConfig.record],
      selectionModel: {
        getSelectionFilter: () => {
          return [{
            field: 'id',
            operator: 'equals',
            value: this.initialConfig.record.id
          }];
        },
        getCount: () => {
          return 1;
        }
      }
    });
  }
  this.menu.on('beforeshow', this.showFileMenu, this);
  this.menu.hideOnClick = false;
  this.selectionHandler = this.mode === 'fileInstant' ? this.fileMessage.createDelegate(this) : this.selectLocation.createDelegate(this);
};
Ext.extend(Tine.Felamimail.MessageFileAction, Ext.Action, {
  /**
   * @cfg {String} fileInstant|selectOnly
   */
  mode: 'fileInstant',
  /**
   * @cfg {Tinebase.data.Record} record optional instead of selectionModel (implicit fom grid)
   */
  record: null,
  /**
   * @property {Boolean} isManualSelection (of file locations)
   */
  isManualSelection: false,
  requiredGrant: 'readGrant',
  allowMultiple: true,
  disabled: true,
  suggestionsLoaded: false,
  composeDialog: null,
  splitButton: null,
  saveToAllRecipients: false,
  initSplitButton: function () {
    if (this.mode === 'fileInstant') {
      return;
    }
    if (!this.splitButton) {
      if (!this.items[0]) {
        return;
      }
      this.splitButton = this.items[0];
      if (this.mode !== 'fileInstant') {
        this.splitButton.disabled = false;
        this.splitButton.enableToggle = true;

        // check suggestions (file_location) for reply/forward
        if (this.composeDialog) {
          this.composeDialog.on('load', this.onMessageLoad, this);
        }
      }
      this.splitButton.on('toggle', this.onToggle, this);
    }
    return this.splitButton;
  },
  arrowHandler: function () {
    if (this.mode === 'fileInstant') {
      return this.showFileMenu();
    }
    this.syncRecipients();
  },
  onToggle: function () {
    if (!this.splitButton) {
      this.splitButton = this.initSplitButton();
    }
    if (this.splitButton.pressed) {
      var _this$menu, _this$menu$items;
      _.each(_.filter((_this$menu = this.menu) === null || _this$menu === void 0 ? void 0 : (_this$menu$items = _this$menu.items) === null || _this$menu$items === void 0 ? void 0 : _this$menu$items.items, {
        isRecipientItem: true
      }), function (item) {
        item.suspendEvents();
        item.setChecked(true);
        item.resumeEvents();
      });
    } else {
      var _this$menu2, _this$menu2$items;
      _.each(_.filter((_this$menu2 = this.menu) === null || _this$menu2 === void 0 ? void 0 : (_this$menu2$items = _this$menu2.items) === null || _this$menu2$items === void 0 ? void 0 : _this$menu2$items.items, {
        checked: true
      }), function (item) {
        item.suspendEvents();
        item.setChecked(false);
        item.resumeEvents();
      });
    }
    const selection = this.getSelected();
    this.splitButton.fireEvent('selectionchange', this.splitButton, selection);
  },
  showFileMenu: async function () {
    this.initSplitButton();
    const selection = _.map(this.initialConfig.selections, 'data');
    if (!this.suggestionsLoaded || this.mode === 'fileInstant') {
      await this.loadSuggestions(selection);
    }
    if (this.composeDialog) {
      this.syncRecipients();
    }
  },
  /**
   * message is loaded in compose dialog
   *
   * @param dlg
   * @param message
   * @param ticketFn
   */
  onMessageLoad: function (dlg, message, ticketFn) {
    if (message.get('original_id')) {
      this.loadSuggestions(message.data).then(() => {
        // auto file if original_message (from forwared/reply) was filed
        if (_.find(_.map(this.menu.items, 'suggestion'), {
          type: 'file_location'
        })) {
          // @TODO: select this suggestion!
          this.handleBtnSelectionEvent();
        }
      }).catch(error => {
        Tine.log.notice('No file suggestions available for this message');
        Tine.log.notice(error);
        this.addStaticMenuItems();
      });
    } else {
      this.addStaticMenuItems();
    }
    this.composeDialog.recipientGrid.store.on('add', this.syncRecipients, this);
    this.composeDialog.recipientGrid.store.on('update', this.syncRecipients, this);
  },
  syncRecipients: function () {
    const emailsInRecipientGrid = [];
    _.each(this.composeDialog.recipientGrid.store.data.items, recipient => {
      const addressData = recipient.get('address');
      const email = addressData.email;
      const title = addressData !== null && addressData !== void 0 && addressData.name ? `${addressData.name} < ${addressData.email} >` : email;
      if (email) {
        emailsInRecipientGrid.push(email);
        const fileTarget = {
          record_title: title,
          model: Tine.Addressbook.Model.EmailAddress,
          data: addressData
        };
        if (!this.menu.getComponent(email)) {
          var _this$splitButton;
          const checked = ((_this$splitButton = this.splitButton) === null || _this$splitButton === void 0 ? void 0 : _this$splitButton.pressed) && !this.isManualSelection;
          this.menu.insert(0, {
            itemId: email,
            isRecipientItem: true,
            xtype: 'menucheckitem',
            checked: checked,
            fileTarget: fileTarget,
            // iconCls: fileTarget.model.getIconCls(),
            text: Ext.util.Format.htmlEncode(fileTarget.record_title),
            hideOnClick: false,
            checkHandler: item => {
              // uncheck saveToAllRecipients if any recipient is unchecked
              if (!item.checked && this.saveToAllRecipients) {
                const selectAllItem = this.menu.items.items.find(item => (item === null || item === void 0 ? void 0 : item.itemId) === 'selectAll');
                if (selectAllItem) {
                  this.saveToAllRecipients = false;
                  selectAllItem.setChecked(false);
                }
              }
              this.handleBtnSelectionEvent();
            }
          });
          if (checked) {
            this.handleBtnSelectionEvent();
          }
        }
      }
    });

    // remove all items no longer in recipient grid
    const items = this.menu.items.items.filter(item => {
      return (item === null || item === void 0 ? void 0 : item.isRecipientItem) && (item === null || item === void 0 ? void 0 : item.itemId) && emailsInRecipientGrid.indexOf(item.itemId) === -1 || (item === null || item === void 0 ? void 0 : item.itemId) === 'selectAll';
    });
    items.forEach(item => {
      this.menu.remove(item);
    });
    if (emailsInRecipientGrid.length > 0) {
      this.menu.insert(0, {
        itemId: 'selectAll',
        isRecipientItem: false,
        xtype: 'menucheckitem',
        checked: false,
        text: this.app.i18n._('Save To All Recipients'),
        hideOnClick: false,
        checkHandler: item => {
          if (item.checked !== this.saveToAllRecipients) {
            this.saveToAllRecipients = item.checked;
            _.each(this.menu.items.items, item => {
              if (item !== null && item !== void 0 && item.isRecipientItem) {
                item.setChecked(this.saveToAllRecipients);
              }
            });
          }
        }
      });
    }
  },
  handleBtnSelectionEvent: function () {
    const selection = this.getSelected();
    this.splitButton.suspendEvents();
    this.splitButton.toggle(selection.length);
    this.splitButton.resumeEvents();
    this.splitButton.fireEvent('selectionchange', this.splitButton, selection);
  },
  loadSuggestions: async function (messages) {
    const suggestionIds = [];
    this.setIconClass('x-btn-wait');
    this.menu.hide();
    this.menu.removeAll();
    return await Tine.Felamimail.getFileSuggestions(messages).then(suggestions => {
      //sort by suggestion.type so file_location record survives deduplication

      _.each(_.sortBy(suggestions, 'type'), suggestion => {
        let model = null;
        let record = null;
        let id;
        let suggestionId;
        let fileTarget;

        // file_location means message reference is already filed (global registry)
        if (suggestion.type === 'file_location') {
          id = suggestion.record.record_id;
          fileTarget = {
            record_title: suggestion.record.record_title,
            model: Tine.Tinebase.data.RecordMgr.get(suggestion.record.model),
            data: id
          };
        } else {
          model = Tine.Tinebase.data.RecordMgr.get(suggestion.model);
          record = Tine.Tinebase.data.Record.setFromJson(suggestion.record, model);
          id = record.getId();
          fileTarget = {
            record_title: record.getTitle(),
            model: model,
            data: id
          };
        }
        suggestionId = fileTarget.model.getPhpClassName() + '-' + id;
        if (suggestionIds.indexOf(suggestionId) < 0) {
          this.menu.addItem({
            itemId: suggestionId,
            isSuggestedItem: true,
            suggestion: suggestion,
            fileTarget: fileTarget,
            iconCls: fileTarget.model.getIconCls(),
            text: Ext.util.Format.htmlEncode(fileTarget.record_title),
            handler: this.selectionHandler,
            hideOnClick: false
          });
          suggestionIds.push(suggestionId);
        }
      });
      this.addDefaultSentImapFolder();
      this.addStaticMenuItems();
      this.addDownloadMenuItem();
      this.suggestionsLoaded = true;
      this.setIconClass('action_file');
    });
  },
  addDefaultSentImapFolder: function () {
    // get account sent folder config
    const defaultImapItem = this.getDefaultSentImapItem();
    if (!defaultImapItem) return;
    this.selectionHandler(defaultImapItem, null);
  },
  getDefaultSentImapItem: function () {
    var _this$composeDialog;
    if (!(this !== null && this !== void 0 && (_this$composeDialog = this.composeDialog) !== null && _this$composeDialog !== void 0 && _this$composeDialog.record)) return null;
    const accountId = this.composeDialog.record.get('account_id');
    const currentAccount = Tine.Tinebase.appMgr.get('Felamimail').getAccountStore().getById(accountId);
    const sentFolder = Tine.Tinebase.appMgr.get('Felamimail').getFolderStore().getById(currentAccount.getSpecialFolderId('sent_folder'));
    const selectedFolderState = Ext.state.Manager.get('Felamimail-TreePanel');
    const selectedFolder = Tine.Tinebase.appMgr.get('Felamimail').getFolderStore().getById(selectedFolderState === null || selectedFolderState === void 0 ? void 0 : selectedFolderState.selected);
    const messageCopyFolderType = currentAccount.get('message_sent_copy_behavior') ?? 'sent';
    if (messageCopyFolderType === 'skip') return null;
    let defaultFolder = messageCopyFolderType === 'source' ? selectedFolder : sentFolder;
    if (!defaultFolder) return null;
    if (messageCopyFolderType === 'source' && defaultFolder.isSystemFolder()) {
      defaultFolder = sentFolder;
    }
    const model = Tine.Tinebase.data.RecordMgr.get(defaultFolder.appName, defaultFolder.modelName);
    const defaultImapItem = new Ext.menu.Item({
      hideOnClick: false
    });
    const title = defaultFolder.isSystemFolder() ? this.app.i18n._(defaultFolder.get('globalname')) : defaultFolder.get('globalname');
    defaultImapItem.fileTarget = {
      type: 'folder',
      record_title: `${title} [IMAP]`,
      model: model,
      data: defaultFolder.data
    };
    return defaultImapItem;
  },
  addStaticMenuItems: function () {
    var _this$composeDialog2;
    this.menu.addItem('-');
    this.menu.addItem({
      text: this.app.i18n._('File (in Filemanager) ...'),
      hidden: !Tine.Tinebase.common.hasRight('run', 'Filemanager'),
      handler: this.selectFilemanagerFolder.createDelegate(this)
    });
    this.menu.addItem({
      text: this.app.i18n._('Attachment (of Record)'),
      menu: _.reduce(Tine.Tinebase.data.RecordMgr.items, (menu, model) => {
        if (model.hasField('attachments') && model.getMeta('appName') !== 'Felamimail') {
          menu.push({
            text: model.getRecordName() + ' ...',
            iconCls: model.getIconCls(),
            handler: this.selectAttachRecord.createDelegate(this, [model], true)
          });
        }
        return menu;
      }, [])
    });

    // only available for composing messages
    if (this !== null && this !== void 0 && (_this$composeDialog2 = this.composeDialog) !== null && _this$composeDialog2 !== void 0 && _this$composeDialog2.record) {
      this.menu.addItem({
        text: this.app.i18n._('Select other IMAP folder'),
        handler: this.selectImapFolder.createDelegate(this)
      });
    }
  },
  addDownloadMenuItem: function () {
    if (!_.isFunction(_.get(this, 'initialConfig.selectionModel.getSelectionFilter'))) return;
    const messageFilter = this.initialConfig.selectionModel.getSelectionFilter();
    const messageIds = messageFilter.length === 1 && messageFilter[0].field === 'id' ? messageFilter[0].value : null;
    const messageCount = this.initialConfig.selectionModel.getCount();
    if (messageCount === 1 && messageIds) {
      this.menu.addItem('-');
      this.menu.addItem({
        text: this.app.i18n._('Download'),
        iconCls: 'action_download',
        handler: this.onMessageDownload.createDelegate(this, [messageIds])
      });
    }
  },
  onMessageDownload: function (messageId) {
    const downloader = new Ext.ux.file.Download({
      params: {
        method: 'Felamimail.downloadMessage',
        requestType: 'HTTP',
        messageId: messageId
      }
    });
    downloader.start();
  },
  /**
   * directly file a single message
   * 
   * TODO: support file multi messages?
   *
   * @param item
   * @param e
   */
  fileMessage: function (item, e) {
    const messageFilter = this.initialConfig.selectionModel.getSelectionFilter();
    const messageCount = this.initialConfig.selectionModel.getCount();
    const locations = [this.itemToLocation(item)];
    this.setIconClass('x-btn-wait');
    Tine.Felamimail.fileMessages(messageFilter, locations).then(() => {
      const msg = this.app.formatMessage('{messageCount, plural, one {Message was saved} other {# messages where saved}}', {
        messageCount: messageCount
      });
      Ext.ux.MessageBox.msg(this.app.formatMessage('Success'), msg);
    }).catch(error => {
      Ext.Msg.show({
        title: this.app.formatMessage('Error'),
        msg: error.message,
        buttons: Ext.MessageBox.OK,
        icon: Ext.MessageBox.ERROR
      });
    }).then(() => {
      this.setIconClass('action_file');
      window.postal.publish({
        channel: "recordchange",
        topic: 'Felamimail.Message.massupdate',
        data: {}
      });
    });
  },
  /**
   * returns currently selected locations
   */
  getSelected: function () {
    var _this$menu3, _this$menu3$items, _this$menu4, _this$menu4$items;
    if (!(this !== null && this !== void 0 && (_this$menu3 = this.menu) !== null && _this$menu3 !== void 0 && (_this$menu3$items = _this$menu3.items) !== null && _this$menu3$items !== void 0 && _this$menu3$items.items)) {
      const defaultImapItem = this.getDefaultSentImapItem();
      if (defaultImapItem) {
        const selection = this.itemToLocation(defaultImapItem);
        return [selection];
      }
    }
    return _.reduce(this === null || this === void 0 ? void 0 : (_this$menu4 = this.menu) === null || _this$menu4 === void 0 ? void 0 : (_this$menu4$items = _this$menu4.items) === null || _this$menu4$items === void 0 ? void 0 : _this$menu4$items.items, (selected, item) => {
      if (item.checked && item !== null && item !== void 0 && item.fileTarget) {
        selected.push(this.itemToLocation(item));
      }
      return selected;
    }, []);
  },
  /**
   * converts (internal) item representation to location
   * @param item
   * @return {{type: string, model: String, record_id: data|{email}|*}}
   */
  itemToLocation: function (item) {
    var _item$fileTarget, _item$fileTarget$mode, _item$fileTarget$mode2, _item$fileTarget2, _item$fileTarget2$mod, _item$fileTarget2$mod2;
    const app = ((_item$fileTarget = item.fileTarget) === null || _item$fileTarget === void 0 ? void 0 : (_item$fileTarget$mode = _item$fileTarget.model) === null || _item$fileTarget$mode === void 0 ? void 0 : (_item$fileTarget$mode2 = _item$fileTarget$mode.getMeta) === null || _item$fileTarget$mode2 === void 0 ? void 0 : _item$fileTarget$mode2.call(_item$fileTarget$mode, 'appName')) ?? null;
    return {
      type: !app ? 'attachment' : app === 'Felamimail' ? 'folder' : 'node',
      model: (_item$fileTarget2 = item.fileTarget) === null || _item$fileTarget2 === void 0 ? void 0 : (_item$fileTarget2$mod = _item$fileTarget2.model) === null || _item$fileTarget2$mod === void 0 ? void 0 : (_item$fileTarget2$mod2 = _item$fileTarget2$mod.getPhpClassName) === null || _item$fileTarget2$mod2 === void 0 ? void 0 : _item$fileTarget2$mod2.call(_item$fileTarget2$mod),
      record_id: item.fileTarget.data,
      record_title: item.fileTarget.record_title
    };
  },
  selectLocation: function (item, e) {
    item.setVisible(!item.isSuggestedItem);
    const firstRecipientIdx = _.findIndex(this.menu.items.items, i => {
      return (i === null || i === void 0 ? void 0 : i.type) && i.type === item.type;
    });
    const firstFileLocationIdx = _.findIndex(this.menu.items.items, i => {
      return (i === null || i === void 0 ? void 0 : i.type) && ['attachment', 'folder', 'node'].includes(i.type);
    });
    const firstImapLocationIdx = _.findIndex(this.menu.items.items, i => {
      return (i === null || i === void 0 ? void 0 : i.type) && i.type === item.type;
    });
    item.selectItem = this.menu.insert(Math.max(0, firstFileLocationIdx), {
      text: item.fileTarget ? Ext.util.Format.htmlEncode(item.fileTarget.record_title) : item.text,
      checked: true,
      instantItem: item,
      fileTarget: item.fileTarget,
      hideOnClick: false,
      checkHandler: item => {
        item.setVisible(!item.instantItem.isSuggestedItem);
        item.instantItem.show();
        this.handleBtnSelectionEvent();
      }
    });
    this.isManualSelection = true;
    this.handleBtnSelectionEvent();
  },
  selectFilemanagerFolder: function (item, e) {
    const filePickerDialog = new Tine.Filemanager.FilePickerDialog({
      constraint: 'folder',
      singleSelect: true,
      requiredGrants: ['addGrant']
    });
    filePickerDialog.on('selected', this.onFilemanagerNodesSelected.createDelegate(this, [item, e], 0));
    filePickerDialog.openWindow();
  },
  onFilemanagerNodesSelected: function (item, e, nodes) {
    let nodeData = _.get(nodes[0], 'nodeRecord', nodes[0]);
    const fakeItem = new Ext.menu.Item({
      hideOnClick: false
    });
    nodeData = _.get(nodeData, 'data', nodeData);
    fakeItem.fileTarget = {
      record_title: nodeData.name,
      model: Tine.Filemanager.Model.Node,
      data: nodeData
    };
    this.selectionHandler(fakeItem, e);
  },
  selectImapFolder: function (item, e) {
    const accountId = this.composeDialog.record.get('account_id');
    const currentAccount = Tine.Tinebase.appMgr.get('Felamimail').getAccountStore().getById(accountId);
    const selectPanel = Tine.Felamimail.FolderSelectPanel.openWindow({
      account: currentAccount,
      listeners: {
        scope: this,
        folderselect: async function (node) {
          if (!node.attributes) return;
          const record = Tine.Tinebase.data.Record.setFromJson(node.attributes, Tine.Felamimail.Model.Folder);
          const model = Tine.Tinebase.data.RecordMgr.get('Felamimail', 'Folder');
          const title = record.isSystemFolder() ? this.app.i18n._(record.get('globalname')) : record.get('globalname');
          const fakeItem = new Ext.menu.Item({
            hideOnClick: false
          });
          fakeItem.fileTarget = {
            type: 'folder',
            record_title: `${title} [IMAP]`,
            model: model,
            data: record.data
          };
          this.selectionHandler(fakeItem, e);
          selectPanel.close();
        }
      }
    });
  },
  selectAttachRecord: function (item, e, model) {
    const pickerDialog = Tine.WindowFactory.getWindow({
      layout: 'fit',
      width: 250,
      height: 100,
      padding: '5px',
      modal: true,
      title: this.app.i18n._('Save Messages as Attachment'),
      items: new Tine.Tinebase.dialog.Dialog({
        listeners: {
          scope: this,
          apply: function (fileTarget) {
            item.fileTarget = fileTarget;
            this.selectionHandler(item, e);
          }
        },
        getEventData: function (eventName) {
          if (eventName === 'apply') {
            const attachRecord = this.getForm().findField('attachRecord').selectedRecord;
            return {
              record_title: attachRecord.getTitle(),
              model: model,
              data: attachRecord.data
            };
          }
        },
        items: Tine.widgets.form.RecordPickerManager.get(model.getMeta('appName'), model.getMeta('modelName'), {
          fieldLabel: model.getRecordName(),
          name: 'attachRecord',
          plugins: [new _Tinebase_js_widgets_form_RecordEditFieldTriggerPlugin__WEBPACK_IMPORTED_MODULE_0__/* ["default"] */ .Z({
            editDialogMode: 'remote'
          })]
        })
      })
    });
  }
});
Tine.Felamimail.MessageFileAction.getFileLocationText = function (locations) {
  let glue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
  return _.reduce(locations, function (text, location) {
    var _location$record_id;
    const model = _.isString(location.model) ? Tine.Tinebase.data.RecordMgr.get(location.model) : location.model;
    const recordId = (location === null || location === void 0 ? void 0 : (_location$record_id = location.record_id) === null || _location$record_id === void 0 ? void 0 : _location$record_id.id) ?? location.record_id;
    const iconCls = model ? model.getIconCls() : '';
    const icon = iconCls ? '<span class="felamimail-location-icon ' + iconCls + '"></span>' : '';
    const span = model ? '<span class="felamimail-location" ' + 'onclick="Tine.Felamimail.MessageFileAction.locationClickHandler(\'' + model.getPhpClassName() + "','" + recordId + '\')">' + icon + '<span class="felamimail-location-text">' + Ext.util.Format.htmlEncode(location.record_title) + '</span></span>' : '';
    return text.concat(span);
  }, []).join(glue);
};
Tine.Felamimail.MessageFileAction.locationClickHandler = function (recordClassName, recordId) {
  const recordClass = Tine.Tinebase.data.RecordMgr.get(recordClassName);
  const recordData = {};
  const editDialogClass = Tine.widgets.dialog.EditDialog.getConstructor(recordClass);
  recordData[recordClass.getMeta('idProperty')] = recordId;
  editDialogClass.openWindow({
    record: Tine.Tinebase.data.Record.setFromJson(recordData, recordClass),
    recordId: recordId
  });
};

/***/ }),

/***/ 48139:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2011 Metaways Infosystems GmbH (http://www.metaways.de)
 */
Ext.ns('Tine.Felamimail');

/**
 * display panel item registry per MIME type
 * 
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.MimeDisplayManager
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @singleton
 */
Tine.Felamimail.MimeDisplayManager = function () {
  var items = {},
    alternatives = {};
  return {
    /**
     * creates a new details panel for given mimeType
     * 
     * @param {String} mimeType
     * @return {Tine.widgets.grid.DisplayPanel}
     */
    create: function (mimeType, config) {
      var c = this.get(mimeType);
      if (!c) {
        throw new Ext.Error('No details panel registered for ' + mimeType);
      }
      return new c(config || {});
    },
    /**
     * returns basic form of MIME type if a display panel is registered for it
     * 
     * @param {String} mimeType
     * @return {String}
     */
    getMainType: function (mimeType) {
      var mainType = alternatives[mimeType];
      return items.hasOwnProperty(mainType) ? mainType : null;
    },
    /**
     * returns Display Panel for given MIME type
     * 
     * @param  {String} mimeType
     * @return {Function} consturctor function of a Tine.widgets.grid.DisplayPanel 
     */
    get: function (mimeType) {
      var mainType = this.getMainType(mimeType);
      return mainType ? items[mainType] : null;
    },
    /**
     * register Display Panel for given MIME type
     * 
     * @param {String/Array} mimeType
     * @param {Function} displayPanel consturctor function of a Tine.widgets.grid.DisplayPanel 
     * @param {Array} otherTypes
     */
    register: function (mimeType, displayPanel, otherTypes) {
      if (items.hasOwnProperty(mimeType)) {
        throw new Ext.Error('There is already an registration for ' + mimeType);
      }
      items[mimeType] = displayPanel;
      alternatives[mimeType] = mimeType;
      if (Ext.isArray(otherTypes)) {
        Ext.each(otherTypes, function (otherType) {
          if (alternatives.hasOwnProperty(otherType)) {
            throw new Ext.Error(alternatives[otherType] + ' is already registered as alternative for ' + otherType);
          }
          alternatives[otherType] = mimeType;
        }, this);
      }
    }
  };
}();

/***/ }),

/***/ 26309:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2018 Metaways Infosystems GmbH (http://www.metaways.de)
 * 
 * TODO         think about adding a generic felamimail backend with the exception handler
 */
Ext.ns('Tine.Felamimail.Model');

// for Tine.Felamimail.Model.Account see hack in Felamimail.initAccountModel

/**
 * @namespace Tine.Felamimail.Model
 * @class Tine.Felamimail.Model.Message
 * @extends Tine.Tinebase.data.Record
 * 
 * Message Record Definition
 */
Tine.Felamimail.Model.Message = Tine.Tinebase.data.Record.create([{
  name: 'id'
}, {
  name: 'messageuid'
}, {
  name: 'account_id'
}, {
  name: 'subject'
}, {
  name: 'from_email'
}, {
  name: 'from_name'
}, {
  name: 'sender'
}, {
  name: 'to'
}, {
  name: 'cc'
}, {
  name: 'bcc'
}, {
  name: 'from'
}, {
  name: 'sent',
  type: 'date',
  dateFormat: Date.patterns.ISO8601Long
}, {
  name: 'received',
  type: 'date',
  dateFormat: Date.patterns.ISO8601Long
}, {
  name: 'flags'
}, {
  name: 'size'
}, {
  name: 'body',
  defaultValue: undefined
}, {
  name: 'body_content_type_of_body_property_of_this_record'
}, {
  name: 'headers'
}, {
  name: 'content_type'
}, {
  name: 'body_content_type'
}, {
  name: 'structure'
}, {
  name: 'attachments'
}, {
  name: 'has_attachment',
  type: 'bool'
}, {
  name: 'original_id'
}, {
  name: 'folder_id'
}, {
  name: 'note'
}, {
  name: 'preparedParts'
},
// contains invitation event record
{
  name: 'reading_conf'
}, {
  name: 'massMailingFlag',
  type: 'bool'
}, {
  name: 'reply_to'
}, {
  name: 'fileLocations'
}, {
  name: 'from_node'
},
// JS only - contains node data if opened from Filemanager
{
  name: 'is_spam_suspicions',
  type: 'bool'
}, {
  name: 'tags'
}], {
  appName: 'Felamimail',
  modelName: 'Message',
  // gettext('GENDER_Message')
  idProperty: 'id',
  titleProperty: 'subject',
  // ngettext('Message', 'Messages', n);
  recordName: 'Message',
  // _('GENDER_Message')
  recordsName: 'Messages',
  containerProperty: 'folder_id',
  // ngettext('Folder', 'Folders', n);
  containerName: 'Folder',
  containersName: 'Folders',
  /**
   * check if message has given flag
   * 
   * @param  {String} flag
   * @return {Boolean}
   */
  hasFlag: function (flag) {
    var flags = this.get('flags') || [];
    return flags.indexOf(flag) >= 0;
  },
  /**
   * returns icon for message with TINE20 flag
   *  depending on the configured icons and the sender domain
   *
   * @returns {String}
   */
  getTine20Icon: function () {
    let flagConfigKey = null,
      email = this.get('from_email');
    if (Tine.Tinebase.common.checkEmailDomain(email)) {
      flagConfigKey = 'flagIconOwnDomain';
    } else {
      let otherDomainRegex = Tine.Tinebase.configManager.get('flagIconOtherDomainRegex', 'Felamimail');
      if (email.match(otherDomainRegex)) {
        flagConfigKey = 'flagIconOtherDomain';
      }
    }
    if (flagConfigKey) {
      return Tine.Tinebase.configManager.get(flagConfigKey, 'Felamimail');
    } else {
      return 'images/favicon.svg';
    }
  },
  getFlagIcons() {
    const icons = [];
    const app = Tine.Tinebase.appMgr.get('Felamimail');
    if (!this.hasFlag('\\Seen')) {
      icons.push({
        name: 'seen',
        src: 'images/icon-set/empty.svg',
        qtip: app.i18n._('Unread Message'),
        cls: 'unread-flag-dot'
      });
    }
    if (this.get('is_spam_suspicions')) {
      icons.push({
        name: 'spam',
        src: 'images/icon-set/icon_spam.svg',
        qtip: app.i18n._('This message might be SPAM')
      });
    }
    if (this.hasFlag('\\Answered')) {
      icons.push({
        name: 'answered',
        src: 'images/icon-set/icon_email_answer.svg',
        qtip: app.i18n._('Answered')
      });
    }
    if (this.hasFlag('Passed')) {
      icons.push({
        name: 'passed',
        src: 'images/icon-set/icon_email_forward.svg',
        qtip: app.i18n._('Forwarded')
      });
    }
    if (this.get('content_type') === 'multipart/encrypted') {
      icons.push({
        name: 'encrypted',
        src: 'images/icon-set/icon_lock.svg',
        qtip: app.i18n._('Encrypted Message')
      });
    }
    if (this.hasFlag('Tine20')) {
      icons.push({
        name: 'tine20',
        src: this.getTine20Icon(),
        qtip: app.i18n._('Tine20')
      });
    }
    return icons;
  },
  /**
   * adds given flag to message
   * 
   * @param  {String} flag
   * @return {Boolean} false if flag was already set before, else true
   */
  addFlag: function (flag) {
    Tine.log.info('Tine.Felamimail.Model.Message::addFlag - add flag ' + flag);
    if (!this.hasFlag(flag)) {
      var flags = Ext.unique(this.get('flags'));
      flags.push(flag);
      this.set('flags', flags);
      return true;
    }
    return false;
  },
  /**
   * check if body has been fetched
   * 
   * @return {Boolean}
   */
  bodyIsFetched: function () {
    return this.get('body') !== undefined;
  },
  /**
   * clears given flag from message
   * 
   * @param {String} flag
   * @return {Boolean} false if flag was not set before, else true
   */
  clearFlag: function (flag) {
    if (this.hasFlag(flag)) {
      var flags = Ext.unique(this.get('flags'));
      flags.remove(flag);
      this.set('flags', flags);
      return true;
    }
    return false;
  },
  /**
   * returns true if given record obsoletes this one
   * 
   * NOTE: this does only work for Tine.widgets.grid.GridPanel::onStoreBeforeLoadRecords record comparison
   * 
   * @param {Tine.Tinebase.data.Record} record
   * @return {Boolean}
   */
  isObsoletedBy: function (record) {
    return record.mtime || record.ctime > this.ctime;
  },
  /**
   * returns actual mimeType of the current body property
   *
   * NOTE: This is not the contents of body_content_type!
   *       body_content_type is the type of the original message derrived by the message structure.
   *       But the server transforms the original type into the requested format/display_format.
   */
  getBodyType: function () {
    return this.get('body_content_type_of_body_property_of_this_record');
  }
});

/**
 * get default message data
 * 
 * @return {Object}
 */
Tine.Felamimail.Model.Message.getDefaultData = function () {
  return {
    content_type: 'text/html'
  };
};

/**
 * get filtermodel for messages
 * 
 * @namespace Tine.Felamimail.Model
 * @static
 * @return {Object} filterModel definition
 */
Tine.Felamimail.Model.Message.getFilterModel = function () {
  const app = Tine.Tinebase.appMgr.get('Felamimail');
  return [{
    filtertype: 'tine.felamimail.folder.filtermodel',
    app: app
  }, {
    label: app.i18n._('Subject'),
    field: 'subject',
    operators: ['wordstartswith'],
    valueType: 'fulltext'
  }, {
    label: app.i18n._('Subject/From/To'),
    field: 'query',
    operators: ['wordstartswith'],
    valueType: 'fulltext'
  }, {
    label: app.i18n._('From (Email)'),
    field: 'from_email',
    operators: ['contains']
  }, {
    label: app.i18n._('From (Name)'),
    field: 'from_name',
    operators: ['contains']
  }, {
    label: app.i18n._('To'),
    field: 'to_list',
    operators: ['wordstartswith'],
    valueType: 'fulltext'
  }, {
    label: app.i18n._('Cc'),
    field: 'cc_list',
    operators: ['wordstartswith'],
    valueType: 'fulltext'
  }, {
    label: app.i18n._('Bcc'),
    field: 'bcc_list',
    operators: ['wordstartswith'],
    valueType: 'fulltext'
  }, {
    label: app.i18n._('Flags'),
    field: 'flags',
    filtertype: 'tinebase.multiselect',
    app: app,
    multiselectFieldConfig: {
      valueStore: Tine.Felamimail.loadFlagsStore()
    }
  }, {
    label: app.i18n._('Received'),
    field: 'received',
    valueType: 'datetime',
    pastOnly: true
  }, {
    label: app.i18n._('Size'),
    field: 'size',
    valueType: 'number'
  }, {
    filtertype: 'tinebase.tag',
    app: app
  }];
};

/**
 * @namespace Tine.Felamimail
 * @class Tine.Felamimail.messageBackend
 * @extends Tine.Tinebase.data.RecordProxy
 * 
 * Message Backend
 * 
 * TODO make clear/addFlags send filter as param instead of array of ids
 */
Tine.Felamimail.messageBackend = new Tine.Tinebase.data.RecordProxy({
  appName: 'Felamimail',
  modelName: 'Message',
  recordClass: Tine.Felamimail.Model.Message,
  /**
   * move messages to folder
   *
   * @param {Object} filter filter data
   * @param {String} targetFolderId
   * @param {Boolean} keepOriginalMessages
   * @param {Object} options
   * @return  {Number} Ext.Ajax transaction id
   */
  moveMessages: function (filter, targetFolderId, keepOriginalMessages, options) {
    options = options || {};
    options.params = options.params || {};
    var p = options.params;
    p.method = this.appName + '.moveMessages';
    p.filterData = filter;
    p.targetFolderId = targetFolderId;
    p.keepOriginalMessages = keepOriginalMessages;
    options.beforeSuccess = function (response) {
      return [Tine.Felamimail.folderBackend.recordReader(response)];
    };

    // increase timeout as this can take a longer (5 minutes)
    options.timeout = 300000;
    options.failure = Tine.Felamimail.handleRequestException;
    return this.doXHTTPRequest(options);
  },
  /**
   * fetches body and additional headers (which are needed for the preview panel) into given message
   * 
   * @param {Message} message
   * @param {String} mimeType
   * @param {Function|Object} callback (NOTE: this has NOTHING to do with standard Ext request callback fn)
   */
  fetchBody: function (message, mimeType, callback) {
    if (mimeType === 'configured') {
      const account = Tine.Tinebase.appMgr.get('Felamimail').getAccountStore().getById(message.get('account_id'));
      if (account) {
        mimeType = account.get('display_format');
        if (!mimeType.match(/^text\//)) {
          mimeType = 'text/' + mimeType;
        }
        if (account.get('preserve_format')) {
          // format of the received message. this is the format to preserve
          mimeType = message.get('body_content_type');
        }
      } else {
        // no account found, might happen for .eml emails
        mimeType = 'text/plain';
      }
    }
    return this.loadRecord(message, {
      params: {
        mimeType: mimeType
      },
      timeout: 120000,
      // 2 minutes
      scope: this,
      suppressBusEvents: true,
      // skip events to prevent gird reloads on message fetch
      success: function (response, options) {
        var msg = this.recordReader({
          responseText: Ext.util.JSON.encode(response.data)
        });
        // NOTE: Flags from the server might be outdated, so we skip them
        Ext.copyTo(message.data, msg.data, Tine.Felamimail.Model.Message.getFieldNames().remove('flags'));
        if (Ext.isFunction(callback)) {
          callback(message);
        } else if (callback.success) {
          Ext.callback(callback.success, callback.scope, [message]);
        }
      },
      failure: function (exception) {
        if (callback.failure) {
          Ext.callback(callback.failure, callback.scope, [exception]);
        } else {
          this.handleRequestException(exception);
        }
      }
    });
  },
  /**
   * saves a message into a folder
   * 
   * @param   {Ext.data.Record} record
   * @param   {String} folderName
   * @param   {Object} options
   * @return  {Number} Ext.Ajax transaction id
   * @success {Ext.data.Record}
   */
  saveInFolder: function (record, folderName, options) {
    options = options || {};
    options.params = options.params || {};
    options.beforeSuccess = function (response) {
      return [this.recordReader(response)];
    };
    var p = options.params;
    p.method = this.appName + '.saveMessageInFolder';
    p.recordData = record.data;
    p.folderName = folderName;

    // increase timeout as this can take a longer (5 minutes)
    options.timeout = 300000;
    return this.doXHTTPRequest(options);
  },
  /**
   * add given flags to given messages
   *
   * @param  {String/Array} ids
   * @param  {String/Array} flags
   */
  addFlags: function (ids, flags, options) {
    options = options || {};
    options.params = options.params || {};
    var p = options.params;
    p.method = this.appName + '.addFlags';
    p.filterData = ids;
    p.flags = flags;

    // increase timeout as this can take a longer (5 minutes)
    options.timeout = 300000;
    return this.doXHTTPRequest(options);
  },
  /**
   * clear given flags from given messages
   *
   * @param  {String/Array} ids
   * @param  {String/Array} flags
   */
  clearFlags: function (ids, flags, options) {
    options = options || {};
    options.params = options.params || {};
    var p = options.params;
    p.method = this.appName + '.clearFlags';
    p.filterData = ids;
    p.flags = flags;

    // increase timeout as this can take a longer (5 minutes)
    options.timeout = 300000;
    return this.doXHTTPRequest(options);
  },
  /**
   * @param Tine.Tinebase.Model.Tree_Node node
   * @param {Object} options
   * @returns {*}
   */
  getMessageFromNode: function (node, options) {
    options = options || {};
    options.params = options.params || {};

    // @todo needed?
    options.beforeSuccess = function (response) {
      return [this.recordReader(response)];
    };
    var p = options.params;
    p.method = this.appName + '.getMessageFromNode';
    p.nodeId = node.id;
    return this.doXHTTPRequest(options);
  },
  /**
   * exception handler for this proxy
   * 
   * @param {Tine.Exception} exception
   */
  handleRequestException: function (exception) {
    Tine.Felamimail.handleRequestException(exception);
  }
});

/**
 * @namespace Tine.Felamimail.Model
 * @class Tine.Felamimail.Model.Record
 * @extends Ext.data.Record
 * 
 * Folder Record Definition
 */
Tine.Felamimail.Model.Folder = Tine.Tinebase.data.Record.create([{
  name: 'id'
}, {
  name: 'localname'
}, {
  name: 'globalname'
}, {
  name: 'path'
},
// /accountid/folderid/...
{
  name: 'parent'
}, {
  name: 'parent_path'
},
// /accountid/folderid/...
{
  name: 'account_id'
}, {
  name: 'has_children',
  type: 'bool'
}, {
  name: 'is_selectable',
  type: 'bool'
}, {
  name: 'system_folder',
  type: 'bool'
}, {
  name: 'imap_status'
}, {
  name: 'imap_timestamp',
  type: 'date',
  dateFormat: Date.patterns.ISO8601Long
}, {
  name: 'imap_uidvalidity',
  type: 'int'
}, {
  name: 'imap_totalcount',
  type: 'int'
}, {
  name: 'imap_lastmodseq',
  type: 'int'
}, {
  name: 'cache_status'
}, {
  name: 'cache_recentcount',
  type: 'int'
}, {
  name: 'cache_totalcount',
  type: 'int'
}, {
  name: 'cache_unreadcount',
  type: 'int'
}, {
  name: 'cache_timestamp',
  type: 'date',
  dateFormat: Date.patterns.ISO8601Long
}, {
  name: 'cache_job_actions_est',
  type: 'int'
}, {
  name: 'cache_job_actions_done',
  type: 'int'
}, {
  name: 'quota_usage',
  type: 'int'
}, {
  name: 'quota_limit',
  type: 'int'
}, {
  name: 'client_access_time',
  type: 'date',
  dateFormat: Date.patterns.ISO8601Long
},
// client only {@see Tine.Felamimail.folderBackend#updateMessageCache}
{
  name: 'unread_children',
  type: 'Array',
  defaultValue: []
} // client only / array of unread child ids
], {
  // translations for system folders:
  // i18n._('INBOX') i18n._('Drafts') i18n._('Sent') i18n._('Templates') i18n._('Junk') i18n._('Trash')

  appName: 'Felamimail',
  modelName: 'Folder',
  // gettext('GENDER_Folder')
  idProperty: 'id',
  titleProperty: 'localname',
  // ngettext('Folder', 'Folders', n);
  recordName: 'Folder',
  recordsName: 'Folders',
  // ngettext('record list', 'record lists', n);
  containerName: 'Folder list',
  containersName: 'Folder lists',
  /**
   * is this folder the currently selected folder
   * 
   * @return {Boolean}
   */
  isCurrentSelection: function () {
    if (Tine.Tinebase.appMgr.get(this.appName).getMainScreen().getTreePanel()) {
      // get active node
      var node = Tine.Tinebase.appMgr.get(this.appName).getMainScreen().getTreePanel().getSelectionModel().getSelectedNode();
      if (node && node.attributes.folder_id) {
        return node.id == this.id;
      }
    }
    return false;
  },
  /**
   * is this folder an inbox?
   * 
   * @return {Boolean}
   */
  isInbox: function () {
    return Ext.util.Format.lowercase(this.get('localname')) === 'inbox';
  },
  /**
   * is this folder the system folder?
   *
   * @return {Boolean}
   */
  isSystemFolder: function () {
    const systemFolderList = ['INBOX', 'Drafts', 'Sent', 'Templates', 'Junk', 'Trash'];
    return systemFolderList.includes(this.get('globalname'));
  },
  /**
   * returns true if current folder needs an update
   */
  needsUpdate: function (updateInterval) {
    if (!this.get('is_selectable')) {
      return false;
    }
    var timestamp = this.get('client_access_time');
    return this.get('cache_status') !== 'complete' || !Ext.isDate(timestamp) || timestamp.getElapsed() > updateInterval;
  }
});

/**
 * @namespace Tine.Felamimail
 * @class Tine.Felamimail.folderBackend
 * @extends Tine.Tinebase.data.RecordProxy
 * 
 * Folder Backend
 */
Tine.Felamimail.folderBackend = new Tine.Tinebase.data.RecordProxy({
  appName: 'Felamimail',
  modelName: 'Folder',
  recordClass: Tine.Felamimail.Model.Folder,
  /**
   * update message cache of given folder for given execution time and sets the client_access_time
   * 
   * @param   {String} folderId
   * @param   {Number} executionTime (seconds)
   * @return  {Number} Ext.Ajax transaction id
   */
  updateMessageCache: function (folderId, executionTime, options) {
    options = options || {};
    options.params = options.params || {};
    var p = options.params;
    p.method = this.appName + '.updateMessageCache';
    p.folderId = folderId;
    p.time = executionTime;
    options.beforeSuccess = function (response) {
      var folder = this.recordReader(response);
      folder.set('client_access_time', new Date());
      return [folder];
    };

    // give 5 times more before timeout
    options.timeout = executionTime * 5000;
    return this.doXHTTPRequest(options);
  },
  /**
   * exception handler for this proxy
   * 
   * @param {Tine.Exception} exception
   */
  handleRequestException: function (exception) {
    Tine.Felamimail.handleRequestException(exception);
  }
});

/**
 * @namespace Tine.Felamimail.Model
 * @class Tine.Felamimail.Model.Vacation
 * @extends Tine.Tinebase.data.Record
 * 
 * Vacation Record Definition
 */
Tine.Felamimail.Model.Vacation = Tine.Tinebase.data.Record.create(Tine.Tinebase.Model.modlogFields.concat([{
  name: 'id'
}, {
  name: 'reason'
}, {
  name: 'enabled',
  type: 'boolean'
}, {
  name: 'days'
}, {
  name: 'start_date',
  type: 'date'
}, {
  name: 'end_date',
  type: 'date'
}, {
  name: 'contact_ids'
}, {
  name: 'template_id'
}, {
  name: 'signature'
}, {
  name: 'mime'
}]), {
  appName: 'Felamimail',
  modelName: 'Vacation',
  idProperty: 'id',
  titleProperty: 'id',
  // ngettext('Vacation', 'Vacations', n);
  recordName: 'Vacation',
  recordsName: 'Vacations',
  // ngettext('record list', 'record lists', n);
  containerName: 'Vacation list',
  containersName: 'Vacation lists'
});

/**
 * get default data for vacation
 * 
 * @return {Object}
 */
Tine.Felamimail.Model.Vacation.getDefaultData = function () {
  return {
    days: 7,
    mime: 'multipart/alternative'
  };
};

/**
 * @namespace Tine.Felamimail
 * @class Tine.Felamimail.vacationBackend
 * @extends Tine.Tinebase.data.RecordProxy
 * 
 * Vacation Backend
 */
Tine.Felamimail.vacationBackend = new Tine.Tinebase.data.RecordProxy({
  appName: 'Felamimail',
  modelName: 'Vacation',
  recordClass: Tine.Felamimail.Model.Vacation,
  /**
   * exception handler for this proxy
   * 
   * @param {Tine.Exception} exception
   */
  handleRequestException: function (exception) {
    Tine.Felamimail.handleRequestException(exception);
  }
});

/**
 * @namespace Tine.Felamimail.Model
 * @class Tine.Felamimail.Model.Rule
 * @extends Tine.Tinebase.data.Record
 * 
 * Rule Record Definition
 */
Tine.Felamimail.Model.Rule = Tine.Tinebase.data.Record.create(Tine.Tinebase.Model.modlogFields.concat([{
  name: 'id',
  sortType: function (value) {
    // should be sorted as int
    return parseInt(value, 10);
  }
}, {
  name: 'action_type'
}, {
  name: 'action_argument'
}, {
  name: 'conjunction'
}, {
  name: 'enabled',
  type: 'boolean'
}, {
  name: 'conditions'
}, {
  name: 'account_id'
}]), {
  appName: 'Felamimail',
  modelName: 'Rule',
  idProperty: 'id',
  titleProperty: 'id',
  // ngettext('Rule', 'Rules', n);
  recordName: 'Rule',
  recordsName: 'Rules',
  // ngettext('record list', 'record lists', n);
  containerName: 'Rule list',
  containersName: 'Rule lists'
});

/**
 * get default data for rules
 * 
 * @return {Object}
 */
Tine.Felamimail.Model.Rule.getDefaultData = function () {
  return {
    enabled: true,
    conditions: [{
      test: 'address',
      header: 'from',
      comperator: 'contains',
      key: ''
    }],
    conjunction: 'allof',
    action_type: 'fileinto',
    action_argument: ''
  };
};

/**
 * @namespace Tine.Felamimail
 * @class Tine.Felamimail.rulesBackend
 * @extends Tine.Tinebase.data.RecordProxy
 * 
 * Rule Backend
 */
Tine.Felamimail.RulesBackend = Ext.extend(Tine.Tinebase.data.RecordProxy, {
  appName: 'Felamimail',
  modelName: 'Rule',
  recordClass: Tine.Felamimail.Model.Rule,
  /**
   * searches all (lightweight) records matching filter
   * 
   * @param   {Object} filter accountId
   * @param   {Object} paging
   * @param   {Object} options
   * @return  {Number} Ext.Ajax transaction id
   * @success {Object} root:[records], totalcount: number
   */
  searchRecords: function (filter, paging, options) {
    options = options || {};
    options.params = options.params || {};
    var p = options.params;
    p.method = this.appName + '.get' + this.modelName + 's';
    p.accountId = filter;
    options.beforeSuccess = function (response) {
      return [this.jsonReader.read(response)];
    };

    // increase timeout as this can take a longer (1 minute)
    options.timeout = 60000;
    return this.doXHTTPRequest(options);
  },
  /**
   * save sieve rules
   *
   * @param  {String}     accountId
   * @param  {Array}      rules
   * @param  {Object}     options
   */
  saveRules: function (accountId, rules, options) {
    options = options || {};
    options.params = options.params || {};
    var p = options.params;
    p.method = this.appName + '.saveRules';
    p.accountId = accountId;
    p.rulesData = rules;
    return this.doXHTTPRequest(options);
  },
  /**
   * saves a single record
   * 
   * NOTE: Single rule records can't be saved
   * 
   * @param   {Ext.data.Record} record
   * @param   {Object} options
   * @return  {Number} Ext.Ajax transaction id
   * @success {Ext.data.Record}
   */
  saveRecord: function (record, options, additionalArguments) {
    // does nothing
  },
  /**
   * exception handler for this proxy
   * 
   * @param {Tine.Exception} exception
   */
  handleRequestException: function (exception) {
    Tine.Felamimail.handleRequestException(exception);
  }
});
Tine.Felamimail.rulesBackend = new Tine.Felamimail.RulesBackend({});

/**
 * @namespace Tine.Felamimail.Model
 * @class Tine.Felamimail.Model.Flag
 * @extends Tine.Tinebase.data.Record
 * 
 * Flag Record Definition
 */
Tine.Felamimail.Model.Flag = Tine.Tinebase.data.Record.create(Tine.Tinebase.Model.modlogFields.concat([{
  name: 'id'
}, {
  name: 'name'
}]), {
  appName: 'Felamimail',
  modelName: 'Flag',
  idProperty: 'id',
  titleProperty: 'id',
  // ngettext('Flag', 'Flags', n);
  recordName: 'Flag',
  recordsName: 'Flags',
  // ngettext('Flag list', 'Flag lists', n);
  containerName: 'Flag list',
  containersName: 'Flag lists'
});
Tine.Felamimail.Model.Attachment = Tine.Tinebase.data.Record.create([{
  name: 'id'
},
// messageId:partId
{
  name: 'messageId'
}, {
  name: 'cid'
}, {
  name: 'content-type'
}, {
  name: 'description'
}, {
  name: 'filename'
}, {
  name: 'partId'
}, {
  name: 'size'
}, {
  name: 'cache'
}], {
  appName: 'Felamimail',
  modelName: 'Attachment',
  idProperty: 'partId',
  titleProperty: 'fileName',
  // ngettext('Attachment', 'Attachments', n);
  recordName: 'Attachment',
  recordsName: 'Attachments'
});

/***/ }),

/***/ 3220:
/***/ (() => {

Tine.Felamimail.PGPDetailsPanel = Ext.extend(Ext.Component, {
  render: function (el) {
    var me = this,
      body = Ext.get(el),
      height = me.detailsPanel.el.getHeight() - (body.getXY()[1] - me.detailsPanel.el.getXY()[1]);
    me.detailsPanel.on('resize', function () {
      body.setHeight(me.detailsPanel.el.getHeight() - (body.getXY()[1] - me.detailsPanel.el.getXY()[1]));
    }, me);
    me.detailsPanel.getLoadMask().show();
    body.dom.id = Ext.id();
    body.setHeight(height);
    Tine.Felamimail.mailvelopeHelper.getKeyring().then(function (keyring) {
      var armoredMsg = me.preparedPart.preparedData;
      mailvelope.createDisplayContainer('#' + body.dom.id, armoredMsg, keyring).then(function () {
        me.detailsPanel.getLoadMask().hide();
      })['catch'](function (error) {
        Tine.log.warn(arguments);
        var app = Tine.Tinebase.appMgr.get('Felamimail'),
          msg = app.i18n._('Mailvelope decryption Error [{0}]');
        me.detailsPanel.getLoadMask().hide();
        Ext.Msg.alert(String.format(msg, error.code), error.message, function () {
          me.detailsPanel.record.set('preparedParts', '');
          me.detailsPanel.currentId = '';
          me.detailsPanel.updateDetails(me.detailsPanel.record, body);
        }, this);
      });
    })['catch'](function () {
      var app = Tine.Tinebase.appMgr.get('Felamimail'),
        msg = app.i18n._('To decrypt this message please install {0} with API support enabled');
      msg = String.format(msg, '<a href="https://www.mailvelope.com" target="_blank">Mailvelope</a>');
      Ext.Msg.alert(app.i18n._('PGP encrypted Message'), msg);
      me.detailsPanel.getLoadMask().hide();
    });
  }
});
Tine.Felamimail.MimeDisplayManager.register('application/pgp-encrypted', Tine.Felamimail.PGPDetailsPanel);

/***/ }),

/***/ 97710:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2009-2013 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.namespace('Tine.Felamimail');

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.RecipientGrid
 * @extends     Ext.grid.EditorGridPanel
 * 
 * <p>Recipient Grid Panel</p>
 * <p>grid panel for to/cc/bcc recipients</p>
 * <pre>
 * </pre>
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Felamimail.RecipientGrid
 */
Tine.Felamimail.RecipientGrid = Ext.extend(Ext.grid.EditorGridPanel, {
  /**
   * @private
   */
  cls: 'felamimail-recipient-grid',
  /**
   * the message record
   * @type Tine.Felamimail.Model.Message
   * @property record
   */
  record: null,
  /**
   * message compose dlg
   * @type Tine.Felamimail.MessageEditDialog
   */
  composeDlg: null,
  /**
   * @type Ext.Menu
   * @property contextMenu
   */
  contextMenu: null,
  /**
   * @type Ext.data.SimpleStore
   * @property store
   */
  store: null,
  /**
   * @cfg {Boolean} autoStartEditing
   */
  autoStartEditing: false,
  /**
   * @cfg {String} autoExpandColumn
   * auto expand column of grid
   */
  autoExpandColumn: 'address',
  autoExpandMax: 2000,
  /**
   * @cfg {Number} clicksToEdit
   * clicks to edit for editor grid panel
   */
  clicksToEdit: 2,
  /**
   * @cfg {Number} numberOfRecordsForFixedHeight
   */
  numberOfRecordsForFixedHeight: 6,
  /**
   * @cfg {Boolean} header
   * show header
   */
  header: false,
  /**
   * @cfg {Boolean} border
   * show border
   */
  border: false,
  /**
   * @cfg {Boolean} deferredRender
   * deferred rendering
   */
  deferredRender: false,
  forceValidation: true,
  skipAutoCheckboxSelection: true,
  enableDrop: true,
  ddGroup: 'recipientDDGroup',
  /**
   * options are saved in onAfterEdit
   * 
   * @type Ext.data.Record
   */
  lastEditedRecord: null,
  /**
   * grid view config
   * - do not mark records as dirty
   * 
   * @type Object
   */
  viewConfig: {
    markDirty: false
  },
  massMailingMode: false,
  /**
   * @private
   */
  initComponent: function () {
    this.app = Tine.Tinebase.appMgr.get('Felamimail');
    this.initialLoad = true;
    this.initStore();
    this.initColumnModel();
    this.initActions();
    this.sm = new Ext.grid.RowSelectionModel();
    Tine.Felamimail.RecipientGrid.superclass.initComponent.call(this);
    this.on('rowcontextmenu', this.onCtxMenu, this);
    // this is relayed by the contact search combo
    this.on('contextmenu', this.onCtxMenu.createDelegate(this, [this, null], 0), this);
    this.on('cellclick', this.onCellClick, this);
    this.on('beforeedit', this.onBeforeEdit, this);
    this.on('afteredit', this.onAfterEdit, this);
    this.postalSubscriptions = [];
    this.postalSubscriptions.push(postal.subscribe({
      channel: "recordchange",
      topic: 'Addressbook.*.*',
      callback: _.bind(this.onContactUpdate, this)
    }));
  },
  onContactUpdate: async function (contactData) {
    if (!contactData.email) return;
    const existingRecords = this.store.queryBy(function (item) {
      var _token$contact_record, _this$lastEditedRecor;
      const token = item.get('address');
      return contactData.id === (token === null || token === void 0 ? void 0 : (_token$contact_record = token.contact_record) === null || _token$contact_record === void 0 ? void 0 : _token$contact_record.id) || ((_this$lastEditedRecor = this.lastEditedRecord) === null || _this$lastEditedRecor === void 0 ? void 0 : _this$lastEditedRecor.id) === item.id;
    }, this);
    existingRecords.items.forEach(async function (record) {
      const existingToken = record.get('address');
      const token = {
        ...existingToken
      };
      token.type = contactData.type;
      token.n_fileas = contactData.n_fileas;
      token.email_type = existingToken.email_type.length ? existingToken.email_type : 'email';
      token.email = contactData[token.email_type];
      token.name = contactData.n_fn;
      token.contact_record = contactData;
      await this.updateRecipientsToken(record, [token], record.get('type'));
    }, this);
  },
  /**
   * show context menu
   * 
   * @param {Tine.Felamimail.RecipientGrid} grid
   * @param {Number} row
   * @param {Event} e
   */
  onCtxMenu: async function (grid, row, e) {
    const targetInput = e.getTarget('input[type=text]', 1, true);
    if (targetInput) return;
    const activeRow = row === null ? this.activeEditor ? this.activeEditor.row : 0 : row;
    e.stopEvent();
    const sm = grid.getSelectionModel();
    if (!sm.isSelected(activeRow)) {
      sm.selectRow(activeRow);
    }
  },
  /**
   * init store
   * @private
   */
  initStore: async function () {
    if (!this.record) {
      this.initStore.defer(200, this);
      return false;
    }
    this.store = new Ext.data.SimpleStore({
      fields: ['type', 'address']
    });
    await this.initRecord().then(() => {
      this.syncRecipientsToStore(['to', 'cc', 'bcc'], this.record);
    });
    this.initialLoad = false;
    this.store.on('update', this.onUpdateStore, this);
    this.store.on('add', this.onAddStore, this);
  },
  /**
   * init cm
   * @private
   */
  initColumnModel: function () {
    this.searchCombo = new Tine.Felamimail.ContactSearchCombo({
      lazyInit: false,
      listeners: {
        scope: this,
        specialkey: this.onSearchComboSpecialkey,
        select: this.onSearchComboSelect,
        render: combo => {
          combo.getEl().on('paste', async (e, dom) => {
            e = e.browserEvent;
            const clipboardData = e.clipboardData || window.clipboardData;
            const pastedData = clipboardData.getData('Text');
            // replace new line to comma , and then remove start/ending special chars
            const value = _.compact(_.map(pastedData.replace(/\r?\n+/g, '\n').split('\n'), email => {
              return String(email).replace(/^[,;]+|[,;]+$/, '');
            })).join(',').replace(/^[,;]+|[,;]+$/, '');
            if (!this.loadMask) {
              this.loadMask = new Ext.LoadMask(Ext.getBody(), {
                msg: this.app.i18n._('Loading Mail Addresses')
              });
            }
            this.loadMask.show();
            const contacts = await Tine.Tinebase.common.findContactsByEmailString(value);
            await this.updateRecipientsToken(null, contacts);
            this.loadMask.hide();
          });
        },
        blur: async combo => {
          const value = combo.getRawValue();
          Tine.log.debug('Tine.Felamimail.MessageEditDialog::onSearchComboBlur() -> current value: ' + value);
          if (value !== '') {
            const sm = this.getSelectionModel();
            const records = sm.getSelections();
            const oldRecord = this.lastEditedRecord ?? records[0];
            const recipients = await Tine.Tinebase.common.findContactsByEmailString(value);
            await this.updateRecipientsToken(oldRecord, recipients, null, true);
          }
        }
      },
      validate: record => {
        return this.validateRecipientToken(record === null || record === void 0 ? void 0 : record.data).isValid;
      }
    });
    this.RecipientTypeCombo = new Ext.form.ComboBox({
      typeAhead: false,
      triggerAction: 'all',
      lazyRender: true,
      editable: false,
      mode: 'local',
      value: null,
      forceSelection: true,
      lazyInit: false,
      store: [['to', this.app.i18n._('To:')], ['cc', this.app.i18n._('Cc:')], ['bcc', this.app.i18n._('Bcc:')]],
      listeners: {
        focus: function (combo) {
          combo.onTriggerClick();
        },
        select: function (combo, record) {
          this.fireEvent('blur', this);
        }
      }
    });
    this.cm = new Ext.grid.ColumnModel([{
      resizable: true,
      id: 'type',
      dataIndex: 'type',
      width: 104,
      menuDisabled: true,
      header: 'type',
      renderer: value => {
        let result = '';
        if (this.record.get('massMailingFlag')) {
          const qtip = Ext.util.Format.htmlEncode(this.app.i18n._('A separate mail is send to each recipient.'));
          return `<div ext:qtip="${qtip}">${this.app.i18n._('Mass')}:</div>`;
        } else {
          const qtip = Ext.util.Format.htmlEncode(this.app.i18n._('Click here to set To/CC/BCC.'));
          const type = Ext.util.Format.capitalize(value);
          result = '<div ext:qtip="' + qtip + '">' + Ext.util.Format.htmlEncode(`${this.app.i18n._hidden(type)}:`) + '</div>';
          return Tine.Tinebase.common.cellEditorHintRenderer(result);
        }
      },
      editor: this.RecipientTypeCombo
    }, {
      resizable: true,
      menuDisabled: true,
      id: 'address',
      dataIndex: 'address',
      header: 'address',
      editor: this.searchCombo,
      columnWidth: 1,
      renderer: token => {
        if (!(token !== null && token !== void 0 && token.email)) return '';
        const renderEmail = this.searchCombo.renderEmailAddress(token);
        const iconCls = this.searchCombo.renderAddressIconCls(token);
        return iconCls + '<span class="tinebase-contact-link">' + renderEmail + '</span>' + '<div class="tinebase-contact-link-wait"></div>';
      }
    }]);
  },
  /**
   * specialkey is pressed in search combo
   * 
   * @param {Combo} combo
   * @param {Event} e
   */
  onSearchComboSpecialkey: async function (combo, e) {
    var _this$activeEditor;
    const value = combo.getRawValue();
    const key = e.getKey();
    Tine.log.debug(`Tine.Felamimail.MessageEditDialog::onSearchComboSpecialkey() -> key : ${key}, current value: ${value}`);
    if (!this.activeEditor) return;
    switch (key) {
      case e.BACKSPACE:
        if (value === '' && this.store.getCount() > 1 && this.activeEditor.row > 0) {
          this.store.remove(this.activeEditor.record);
          this.activeEditor.row -= 1;
          const record = this.store.getAt(this.activeEditor.row);
          this.activeEditor.record.set('address', record.data.address);
          const selModel = this.getSelectionModel();
          if (!selModel.isSelected(this.activeEditor.row)) {
            selModel.selectRow(this.activeEditor.row);
          }
          this.setFixedHeight(false);
          this.ownerCt.doLayout();
          this.startEditing.defer(100, this, [this.activeEditor.row, this.activeEditor.col]);
        }
        break;
      case e.ESC:
        // TODO should ESC close the compose window if search combo is already empty?
        //            if (value == '') {
        //                this.fireEvent('specialkey', this, e);
        //            }
        if (this.activeEditor.startValue === '') {
          this.startEditing.defer(100, this, [this.activeEditor.row, this.activeEditor.col]);
        }
        break;
      case e.TAB:
      case e.ENTER:
        // jump to subject if we are in the last row, and it is empty OR TAB was pressed
        if (combo !== null && combo !== void 0 && combo.selectedRecord) {
          await this.onSearchComboSelect(combo, combo.selectedRecord);
        }
        if (this.store.getCount() === ((_this$activeEditor = this.activeEditor) === null || _this$activeEditor === void 0 ? void 0 : _this$activeEditor.row) + 1) {
          Tine.log.debug(`Tine.Felamimail.MessageEditDialog::onSearchComboSpecialkey() -> last row`);
          if (value === '') {
            this.fireEvent('specialkey', combo, e);
          }
        }
        break;
    }
    return true;
  },
  onSearchComboSelect: async function (combo, value, startValue) {
    Tine.log.debug('Tine.Felamimail.MessageEditDialog::onSearchComboSelect()');
    if (value === '') {
      this.onDelete();
      return;
    }
    const token = value.data ?? '';
    await this.updateRecipientsToken(this.lastEditedRecord, [token], null, true);
    combo.selectedRecord = null;
  },
  /**
   * validate recipient token
   *
   * default validation:
   * - all contacts are valid
   * 
   * validation in mass mailing mode: 
   * - all contacts are valid, but the list contacts should show the tip accordingly
   * 
   * @param token
   */
  validateRecipientToken(token) {
    let result = {
      isValid: true,
      tip: 'skip checking token'
    };
    if (!token || !this.massMailingMode) {
      return result;
    }
    // in mass mailing mode, list should be valid to be displayed in searchContactCombo,
    if (token !== null && token !== void 0 && token.emails) {
      return {
        isValid: true,
        tip: this.app.i18n._('Send message to individual list members instead')
      };
    }
    return result;
  },
  /**
   * start editing (check if message compose dlg is saving/sending first)
   * 
   * @param {} row
   * @param {} col
   */
  startEditing: function (row, col) {
    this.lastEditedRecord = this.store.getAt(row);
    if (this.massMailingMode && col === 0) return;
    if (!this.composeDlg || !this.composeDlg.saving) {
      Tine.Felamimail.RecipientGrid.superclass.startEditing.apply(this, arguments);
    }
  },
  preEditValue: function (r, field) {
    const value = r.data[field];
    if (value !== null && value !== void 0 && value.email) {
      return this.autoEncode && Ext.isString(value.email) ? Ext.util.Format.htmlDecode(value.email) : value.email;
    } else {
      return Tine.Felamimail.RecipientGrid.superclass.preEditValue.apply(this, arguments);
    }
  },
  /**
   * on contact click
   *
   * @param e
   */
  onContactClick: async function (e) {
    var _target$parentNode;
    const target = e.target;

    //skip non tinebase-contact-link target
    if (!target.className.includes('tinebase-contact-link') && !(target !== null && target !== void 0 && (_target$parentNode = target.parentNode) !== null && _target$parentNode !== void 0 && _target$parentNode.className.includes('tinebase-contact-link'))) {
      return;
    }
    const row = this.getView().findRowIndex(target);
    const col = this.getView().findCellIndex(target);
    const record = this.store.getAt(row);
    const position = Ext.fly(target).getXY();
    position[1] = position[1] + Ext.fly(target).getHeight();
    const targetInput = e.getTarget('input[type=text]', 1, true);
    const contact = record.get('address');
    this.lastEditedRecord = record;
    if (targetInput || !record || record.get('address') === '' || col !== 1 || row === false || !(contact !== null && contact !== void 0 && contact.email)) {
      return;
    }
    const contactCtxMenu = await Tine.Tinebase.tineInit.getEmailContextMenu(targetInput, contact.email, contact.name, contact.type);
    const adb = Tine.Tinebase.appMgr.get('Addressbook');
    if (adb) {
      let index = 0;
      switch (contact.type) {
        case 'mailingList':
        case 'group':
        case 'list':
          const item = new Ext.Action({
            text: this.app.i18n._('Resolve to single contact'),
            iconCls: '',
            handler: async item => {
              var _record$data, _record$data$address;
              const members = record === null || record === void 0 ? void 0 : (_record$data = record.data) === null || _record$data === void 0 ? void 0 : (_record$data$address = _record$data.address) === null || _record$data$address === void 0 ? void 0 : _record$data$address.emails;
              await this.updateRecipientsToken(record, members, record.get('type'), true);
            }
          });
          contactCtxMenu.insert(index, item);
          index++;
          break;
        default:
          const {
            results: contacts
          } = await Tine.Addressbook.searchContactsByRecipientsToken([contact]);
          const options = [];
          _.each(contacts, emailData => {
            const selected = emailData.email === contact.email;
            const emailType = (emailData === null || emailData === void 0 ? void 0 : emailData.email_type) === 'email' ? adb.i18n._('E-Mail') : adb.i18n._('E-Mail (private)');
            const displayTitle = `${emailType} -- ${emailData.email}`;
            if (options.includes(displayTitle)) return;
            const emailItem = new Ext.Action({
              text: displayTitle,
              iconCls: selected ? 'action_enable' : '',
              handler: async item => {
                if (!selected) {
                  const newSelectedContact = _.find(contacts, contact => {
                    return item.text.includes(contact.email);
                  });
                  await this.updateRecipientsToken(record, [newSelectedContact], record.get('type'));
                }
              }
            });
            contactCtxMenu.insert(index, emailItem);
            options.push(displayTitle);
            index++;
          });
          break;
      }
      if (index > 0) {
        contactCtxMenu.insert(index, '-');
      }
    }
    contactCtxMenu.addMenuItem(this.action_remove);
    if (!this.isDbClick) {
      contactCtxMenu.showAt(position);
    }
  },
  /**
   * resolve recipient token
   *
   * default:
   * - when the list email is not empty. it should not be expanded, user should resolve it to members manually
   * - when the list email is empty, it should be resolve to members automatically.
   *
   * mass mailing mode:
   * - all the list email should be removed and expanded to members automatically
   *
   * @param token
   */
  resolveRecipientToken(token) {
    if (token === '') return [];
    if (!this.validateRecipientToken(token).isValid) return [];
    if (token !== null && token !== void 0 && token.emails) {
      if (token.email === '' || this.massMailingMode) {
        var _token$emails;
        // list without email and emails should be ignored
        if (((_token$emails = token.emails) === null || _token$emails === void 0 ? void 0 : _token$emails.length) === 0) return [];
        // mass mailing mode should always expand the list
        const members = [];
        const contactsToResolve = [];
        token.emails.forEach(member => {
          const result = this.validateRecipientToken(member);
          member.type = token.type + 'Member';
          member.note = this.app.i18n._('from') + ' ' + token.name;
          if (result !== null && result !== void 0 && result.isValid) {
            members.push(member);
          } else {
            contactsToResolve.push({
              result: result,
              token: member
            });
          }
        });
        this.showInvalidContactDialog(contactsToResolve, ['Ok']);
        return members;
      }
    }
    return [token];
  },
  // private
  onCellDblClick: function onCellClick(g, row, col, e) {
    this.isDbClick = true;
    this.startEditing(row, col);
  },
  // private
  onCellClick: async function onCellClick(g, row, col, e) {
    this.isDbClick = false;
    if (col === 0) {
      this.startEditing(row, col);
    }
    if (col === 1) {
      await this.onContactClick(e);
    }
  },
  // private
  onMouseDown: async function (e, target) {
    const row = this.getView().findRowIndex(target);
    if (row === false) return;
    const col = this.getView().findCellIndex(target);
    const record = this.store.getAt(row);
    const activeRow = row === null ? this.activeEditor ? this.activeEditor.row : 0 : row;
    const selModel = this.getSelectionModel();
    if (!selModel.isSelected(activeRow)) {
      selModel.selectRow(activeRow);
    }
    if (col === 1 && record.get('address') === '') {
      this.startEditing.defer(50, this, [row, col]);
    }
  },
  /**
   * init actions / ctx menu
   * @private
   */
  initActions: function () {
    this.action_remove = new Ext.Action({
      text: i18n._('Remove'),
      handler: this.onDelete,
      iconCls: 'action_delete',
      scope: this,
      disable: false
    });
    this.contextMenu = new Ext.menu.Menu({
      items: this.action_remove
    });
  },
  /**
   * start editing after render
   * @private
   */
  afterRender: async function () {
    Tine.Felamimail.RecipientGrid.superclass.afterRender.call(this);
    // kill x-scrollers
    this.el.child('div[class=x-grid3-scroller]').setStyle('overflow-x', 'hidden');
  },
  /**
   * set grid to fixed height if it has more than X records
   *  
   * @param {} doLayout
   */
  setFixedHeight: function (doLayout) {
    if (this.store.getCount() > this.numberOfRecordsForFixedHeight) {
      this.setHeight(155);
    } else {
      this.setHeight(this.store.getCount() * 24 + 1);
    }
    if (doLayout && doLayout === true) {
      this.ownerCt.doLayout();
    }
  },
  /**
   * store has been updated
   * 
   * @param {} store
   * @param {} record
   * @param {} operation
   * @private
   */
  onUpdateStore: function (store, record, operation) {
    this.syncRecipientsToRecord();
  },
  /**
   * on add event of store
   * 
   * @param {} store
   * @param {} records
   * @param {} index
   */
  onAddStore: function (store, records, index) {
    this.syncRecipientsToRecord();
  },
  /**
   * sync grid with record
   * -> update record to/cc/bcc
   */
  syncRecipientsToRecord: function () {
    Tine.Tinebase.common.assertComparable(this.record.data.to);
    Tine.Tinebase.common.assertComparable(this.record.data.cc);
    Tine.Tinebase.common.assertComparable(this.record.data.bcc);

    // update record recipient fields
    this.record.set('to', Tine.Tinebase.common.assertComparable([]));
    this.record.set('cc', Tine.Tinebase.common.assertComparable([]));
    this.record.set('bcc', Tine.Tinebase.common.assertComparable([]));

    // update record recipient fields
    this.store.each(function (record, index) {
      const addressData = record.get('address') ?? '';
      const type = record.get('type') ?? '';
      if (type !== '' && addressData !== '') {
        this.record.data[type].push(addressData);
      }
    }, this);
  },
  /**
   * resolve recipients to token
   * 
   */
  initRecord: async function () {
    const promises = [];
    ['to', 'cc', 'bcc'].forEach(type => {
      const promise = new Promise(async resolve => {
        let contacts = this.record.data[type];
        let emails = _.filter(contacts, addressData => {
          return _.isString(addressData);
        });
        if (emails.length > 0) {
          emails = _.join(emails, ', ');
          const resolvedContacts = await Tine.Tinebase.common.findContactsByEmailString(emails);
          _.each(contacts, (addressData, idx) => {
            if (_.isString(addressData)) {
              contacts[idx] = _.find(resolvedContacts, contact => {
                return addressData.includes(contact.email);
              });
            }
          });
        }
        this.record.data[type] = contacts;
        resolve();
      });
      promises.push(promise);
    });
    await Promise.all(promises);
  },
  /**
   * sync grid with record
   * -> update store
   *
   * @param {Array} types
   * @param {Tine.Felamimail.Model.Message} record
   * @param {Boolean} setHeight
   * @param {Boolean} clearStore
   */
  syncRecipientsToStore: function (types, record, setHeight, clearStore) {
    if (clearStore) {
      this.store.removeAll(true);
    }
    record = record || this.record;
    let contacts = [];
    types.forEach(type => {
      const data = record.data[type].map(addressData => new Ext.data.Record({
        type: type,
        'address': addressData
      }));
      contacts = contacts.concat(data);
    });
    this.store.add(contacts);
    this.store.sort('address');
    if (setHeight && setHeight === true) {
      this.setFixedHeight(true);
    }
    this.addEmptyRowAndDoLayout(contacts.length === 0);
  },
  // save selected record for usage in onAfterEdit
  onEditComplete: async function (ed, value, startValue) {
    var _rowRecord$data;
    const row = ed.row;
    const col = ed.col;
    const rowRecord = this.store.getAt(row);
    let contact = (rowRecord === null || rowRecord === void 0 ? void 0 : (_rowRecord$data = rowRecord.data) === null || _rowRecord$data === void 0 ? void 0 : _rowRecord$data.address) ?? '';
    rowRecord.selectedRecord = contact ?? null;
    if (col === 1) {
      if (contact !== '' && startValue === contact.email) return;
      value = contact;
    }
    Tine.Felamimail.RecipientGrid.superclass.onEditComplete.call(this, ed, value, startValue);
  },
  /**
   * after edit
   * 
   * @param {} o
   */
  onAfterEdit: async function (o) {
    Tine.log.debug('Tine.Felamimail.MessageEditDialog::onAfterEdit()');
    Tine.log.debug(o);
    if (o.field === 'address') {
      Ext.fly(this.getView().getCell(o.row, o.column)).removeClass('x-grid3-td-address-editing');
      if (o.originalValue !== '' && o.value === '') {
        this.store.remove(o.record);
        this.setFixedHeight(true);
      }
      if (o.originalValue !== '') {
        this.addEmptyRowAndDoLayout(true);
      }
    }
    if (o.field === 'type') {
      if (o.value !== '' && o.record.get('address') !== '') {
        const contact = o.record.get('address');
        await this.updateRecipientsToken(o.record, [contact], o.value);
      }
    }
  },
  /**
   * delete handler
   */
  onDelete: function (btn, e) {
    const sm = this.getSelectionModel();
    const records = sm.getSelections();
    Ext.each(records, function (record) {
      if (record.get('address') !== '' && this.store.getCount() > 0) {
        this.store.remove(record);
        this.store.fireEvent('update', this.store);
      }
    }, this);
    this.setFixedHeight(true);
    this.addEmptyRowAndDoLayout(true);
  },
  /**
   * on before edit
   * 
   * @param {} o
   */
  onBeforeEdit: function (o) {
    if (this.record.get('massMailingFlag') && o.column === 0) {
      o.cancel = true;
      return;
    }
    this.getView().el.select('.x-grid3-td-address-editing').removeClass('x-grid3-td-address-editing');
    Ext.fly(this.getView().getCell(o.row, o.column)).addClass('x-grid3-td-address-editing');
  },
  /**
   * add recipients to grid store
   */
  updateRecipientsToken: async function () {
    var _recipientTokens;
    let oldContactRecord = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
    let recipientTokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
    let type = arguments.length > 2 ? arguments[2] : undefined;
    let autoStartEdit = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
    let addEmptyRow = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
    if (((_recipientTokens = recipientTokens) === null || _recipientTokens === void 0 ? void 0 : _recipientTokens.length) === 0 && recipientTokens !== '') return;
    // resolve recipients
    if (!recipientTokens) {
      var _oldContactRecord$dat;
      if (!(oldContactRecord !== null && oldContactRecord !== void 0 && (_oldContactRecord$dat = oldContactRecord.data) !== null && _oldContactRecord$dat !== void 0 && _oldContactRecord$dat.address)) return;
      recipientTokens = this.resolveRecipientToken(oldContactRecord.data.address);
    } else {
      recipientTokens = recipientTokens.reduce((resolvedTokens, token) => {
        return resolvedTokens.concat(this.resolveRecipientToken(token));
      }, []);
    }
    const sm = this.getSelectionModel();
    const records = sm.getSelections();
    const oldRecord = this.store.indexOf(oldContactRecord) > -1 ? oldContactRecord : records[0];
    const index = oldRecord ? this.store.indexOf(oldRecord) : -1;
    if (!type) {
      type = this.activeEditor ? this.activeEditor.record.data.type : this.lastActiveEditor ? this.lastActiveEditor.record.data.type : this.lastEditedRecord ? this.lastEditedRecord.data.type : 'to';
    }
    const skipUpdateCheck = recipientTokens.length === 1;
    if (skipUpdateCheck) {
      const token = recipientTokens[0];
      const oldEmailData = oldRecord.get('address');
      let skipUpdate = oldEmailData !== '';
      _.each(token, (value, key) => {
        const keysToValidate = ['type', 'n_fileas', 'email_type', 'email', 'name', 'note'];
        if (keysToValidate.includes(key) && oldEmailData[key] !== value) skipUpdate = false;
      });
      if (skipUpdate) return;
    }
    if (index > -1 && oldRecord.get('address') !== '') this.store.remove(oldRecord);
    const updatedRecords = [];
    const existingRecords = [];
    // collect and ignore existing records
    recipientTokens.forEach(token => {
      if (!token.email) return;
      const existingRecipientToken = this.store.findBy(function (record) {
        const addressData = record.get('address') ?? '';
        if (addressData !== '' && (addressData === null || addressData === void 0 ? void 0 : addressData.email) === token.email && (addressData === null || addressData === void 0 ? void 0 : addressData.name) === token.name) {
          existingRecords.push(record);
          return true;
        }
      }, this);
      if (existingRecipientToken === -1) {
        if (this.validateRecipientToken(token).isValid) {
          const record = new Ext.data.Record({
            type: type,
            'address': token
          });
          updatedRecords.push(record);
          this.lastEditedRecord = record;
        }
      }
    });
    // add new resolved records to store
    if (updatedRecords.length > 0) {
      if (index > -1) {
        this.store.insert(index, updatedRecords.reverse());
      } else {
        this.store.add(updatedRecords);
      }
    }

    // highlight all the resolved records in UI
    const highlightRecords = updatedRecords.concat(existingRecords);
    highlightRecords.forEach(record => {
      const row = this.getView().getRow(this.store.indexOf(record));
      if (row) {
        Ext.fly(row).highlight('', {
          duration: 2
        });
        this.getView().focusRow(row);
      }
    });
    if (existingRecords.length) await new Promise(delay => setTimeout(delay, 1000));
    if (addEmptyRow) this.addEmptyRowAndDoLayout(autoStartEdit);
  },
  /**
   * adds row and adjusts layout
   *
   * @param autoStartEdit
   */
  addEmptyRowAndDoLayout: function (autoStartEdit) {
    var _record$data3;
    const record = this.activeEditor ? this.activeEditor.record : this.lastEditedRecord;
    const existingEmptyRecords = this.store.queryBy(function (record) {
      var _record$data2;
      if (!(record !== null && record !== void 0 && (_record$data2 = record.data) !== null && _record$data2 !== void 0 && _record$data2.address) || record.data.address === '') return true;
    }, this);
    const emptyRecord = new Ext.data.Record({
      type: (record === null || record === void 0 ? void 0 : (_record$data3 = record.data) === null || _record$data3 === void 0 ? void 0 : _record$data3.type) ?? 'to',
      'address': ''
    });
    existingEmptyRecords.items.forEach(record => {
      this.store.remove(record);
    });
    this.store.add(emptyRecord);
    this.store.commitChanges();
    this.setFixedHeight(false);
    if (this.ownerCt) this.ownerCt.doLayout();
    const selModel = this.getSelectionModel();
    const emptyRecordIdx = this.store.indexOf(emptyRecord);
    if (!selModel.isSelected(emptyRecordIdx)) selModel.selectRow(emptyRecordIdx);
    if (autoStartEdit) this.startEditing.defer(100, this, [emptyRecordIdx, 1]);
  },
  async updateMassMailingRecipients() {
    if (!this.store) return;
    const contactsToResolve = [];
    this.store.each(record => {
      const token = record.data.address;
      const result = this.validateRecipientToken(token);
      // token with emails should be collected and resolved to list members
      if (!result.isValid || token !== null && token !== void 0 && token.emails) {
        contactsToResolve.push({
          result: result,
          token: token,
          record: record
        });
      }
    });
    const result = await this.showInvalidContactDialog(contactsToResolve);
    if (result && contactsToResolve.length > 0) {
      const records = _.map(contactsToResolve, 'record');
      await Promise.all(records.map(record => {
        var _record$data4, _record$data4$address;
        const members = (record === null || record === void 0 ? void 0 : (_record$data4 = record.data) === null || _record$data4 === void 0 ? void 0 : (_record$data4$address = _record$data4.address) === null || _record$data4$address === void 0 ? void 0 : _record$data4$address.emails) ?? null;
        return this.updateRecipientsToken(record, members, 'bcc', false, false);
      }));
      this.addEmptyRowAndDoLayout(true);
    }
    if (this.massMailingMode) {
      this.store.each(record => {
        record.set('type', 'bcc');
        record.commit();
      });
    }
    this.view.refresh();
    if (this.composeDlg) {
      if (!result) this.composeDlg.onToggleMassMailing();
    }
  },
  showInvalidContactDialog(contactsToResolve) {
    let buttonOptions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ['No', 'Yes'];
    return new Promise(resolve => {
      if (!this.massMailingMode) return resolve(false);
      if (contactsToResolve.length === 0) return resolve(true);
      const text = contactsToResolve.map(item => {
        var _item$result;
        const iconCls = this.searchCombo.renderAddressIconCls(item.token);
        const renderEmail = this.searchCombo.renderEmailAddress(item.token);
        const tip = Ext.util.Format.htmlEncode((item === null || item === void 0 ? void 0 : (_item$result = item.result) === null || _item$result === void 0 ? void 0 : _item$result.tip) ?? '');
        return '<span>' + `${iconCls}<div ext:qtip="${tip}">${renderEmail}</div>` + '</span></br>';
      });
      const dialog = Tine.widgets.dialog.FileListDialog.openWindow({
        modal: true,
        allowCancel: false,
        height: 180,
        width: 500,
        title: this.app.i18n._('The following recipients will be removed from this mass mail'),
        text: text.join(''),
        scope: this,
        buttonOptions: buttonOptions,
        handler: async button => {
          resolve(['YES', 'OK'].includes(button.toUpperCase()));
        }
      });
    });
  }
});
Ext.reg('felamimailrecipientgrid', Tine.Felamimail.RecipientGrid);

/***/ }),

/***/ 12707:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.namespace('Tine.Felamimail');

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.RecipientPickerDialog
 * @extends     Tine.widgets.dialog.EditDialog
 * 
 * <p>Message Compose Dialog</p>
 * <p>This dialog is for searching contacts in the addressbook and adding them to the recipient list in the email compose dialog.</p>
 * <p>
 * </p>
 * 
 * @author      Philipp Schuele <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * 
 * @param       {Object} config
 * @constructor
 * Create a new RecipientPickerDialog
 */
Tine.Felamimail.RecipientPickerDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
  /**
   * @private
   */
  windowNamePrefix: 'RecipientPickerWindow_',
  appName: 'Felamimail',
  recordClass: Tine.Felamimail.Model.Message,
  recordProxy: Tine.Felamimail.messageBackend,
  loadRecord: false,
  evalGrants: false,
  mode: 'local',
  hideAttachmentsPanel: true,
  needsUpdateEvent: true,
  bodyStyle: 'padding:0px',
  /**
   * overwrite update toolbars function (we don't have record grants)
   * @private
   */
  updateToolbars: Ext.emptyFn,
  /**
   * @private
   */
  onRecordLoad: function () {
    // interrupt process flow till dialog is rendered
    if (!this.rendered) {
      this.onRecordLoad.defer(250, this);
      return;
    }
    var subject = this.record.get('subject') != '' ? this.record.get('subject') : this.app.i18n._('(new message)');
    this.window.setTitle(String.format(this.app.i18n._('Select recipients for "{0}"'), subject));
    this.hideLoadMask();
  },
  /**
   * returns dialog
   * 
   * NOTE: when this method gets called, all initialisation is done.
   * 
   * @return {Object}
   * @private
   */
  getFormItems: function () {
    var adbApp = Tine.Tinebase.appMgr.get('Addressbook');
    this.treePanel = new Tine.widgets.container.TreePanel({
      allowMultiSelection: true,
      region: 'west',
      filterMode: 'filterToolbar',
      recordClass: Tine.Addressbook.Model.Contact,
      app: adbApp,
      width: 200,
      minSize: 100,
      maxSize: 300,
      border: false,
      enableDrop: false
    });
    this.contactGrid = new Tine.Felamimail.ContactGridPanel({
      region: 'center',
      messageRecord: this.record,
      app: adbApp,
      plugins: [this.treePanel.getFilterPlugin()]
    });
    this.westPanel = new Tine.widgets.mainscreen.WestPanel({
      app: adbApp,
      hasFavoritesPanel: true,
      ContactTreePanel: this.treePanel,
      ContactFilterPanel: new Tine.widgets.persistentfilter.PickerPanel({
        filter: [{
          field: 'model',
          operator: 'equals',
          value: 'Addressbook_Model_ContactFilter'
        }],
        app: adbApp,
        grid: this.contactGrid
      }),
      additionalItems: [new Tine.Felamimail.RecipientPickerFavoritePanel({
        app: this.app,
        grid: this.contactGrid
      })],
      getFavoritesPanel: function () {
        return this.ContactFilterPanel;
      },
      getContainerTreePanel: function () {
        return this.ContactTreePanel;
      }
    });
    return {
      border: false,
      layout: 'border',
      items: [{
        cls: 'tine-mainscreen-centerpanel-west',
        region: 'west',
        stateful: false,
        layout: 'border',
        split: true,
        width: 200,
        minSize: 100,
        maxSize: 300,
        border: false,
        collapsible: true,
        collapseMode: 'mini',
        header: false,
        items: [{
          border: false,
          region: 'center',
          items: [this.westPanel]
        }]
      }, this.contactGrid]
    };
  }
});

/**
 * Felamimail Edit Popup
 * 
 * @param   {Object} config
 * @return  {Ext.ux.Window}
 */
Tine.Felamimail.RecipientPickerDialog.openWindow = function (config) {
  var window = Tine.WindowFactory.getWindow({
    width: 1000,
    height: 600,
    name: Tine.Felamimail.RecipientPickerDialog.prototype.windowNamePrefix + Ext.id(),
    contentPanelConstructor: 'Tine.Felamimail.RecipientPickerDialog',
    contentPanelConstructorConfig: config
  });
  return window;
};

/***/ }),

/***/ 31163:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2011 Metaways Infosystems GmbH (http://www.metaways.de)
 */
Ext.ns('Tine.Felamimail');

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.RecipientPickerFavoritePanel
 * @extends     Ext.tree.TreePanel
 * 
 * <p>PersistentFilter Picker Panel</p>
 * 
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Felamimail.RecipientPickerFavoritePanel
 */
Tine.Felamimail.RecipientPickerFavoritePanel = Ext.extend(Tine.widgets.persistentfilter.PickerPanel, {
  collapsible: true,
  baseCls: 'ux-arrowcollapse',
  animCollapse: true,
  titleCollapse: true,
  draggable: true,
  autoScroll: false,
  /**
   * no context menu
   * @type Function
   */
  onContextMenu: Ext.emptyFn,
  /**
   * @private
   */
  initComponent: function () {
    this.title = this.app.i18n._('Recipient filter');
    this.store = new Ext.data.ArrayStore({
      fields: Tine.widgets.persistentfilter.model.PersistentFilter.getFieldDefinitions(),
      sortInfo: {
        field: 'name',
        direction: 'ASC'
      }
    });
    var label = '';
    Ext.each(['all', 'to', 'cc', 'bcc'], function (field) {
      switch (field) {
        case 'all':
          label = this.app.i18n._('All recipients');
          break;
        default:
          label = String.format(this.app.i18n._('"{0}" recipients'), field);
          break;
      }
      this.store.add([new Tine.widgets.persistentfilter.model.PersistentFilter({
        filters: field,
        name: label,
        model: 'Addressbook_Model_Contact',
        application_id: this.app.id,
        id: Ext.id()
      })]);
    }, this);
    this.filterNode = new Ext.tree.AsyncTreeNode({
      id: '_recipientFilter',
      leaf: false,
      expanded: true
    });
    Tine.Felamimail.RecipientPickerFavoritePanel.superclass.initComponent.call(this);
  },
  /**
   * load grid from saved filter
   * 
   * -> overwritten to allow to dynamically update email filter
   * 
   *  @param {Tine.widgets.persistentfilter.model.PersistentFilter} persistentFilter
   */
  onFilterSelect: function (persistentFilter) {
    var emailRecipients = [];
    switch (persistentFilter.get('filters')) {
      case 'all':
        Ext.each(['to', 'cc', 'bcc'], function (field) {
          emailRecipients = emailRecipients.concat(this.grid.messageRecord.data[field]);
        }, this);
        break;
      default:
        emailRecipients = this.grid.messageRecord.data[persistentFilter.get('filters')];
        break;
    }
    var filterValue = [],
      emailRegExp = /<([^>]*)/,
      filter = [{
        field: 'container_id',
        operator: 'in',
        value: []
      }];
    Ext.each(emailRecipients, function (email) {
      emailRegExp.exec(email);
      if (RegExp.$1 != '') {
        filterValue.push(RegExp.$1);
      }
    }, this);
    if (filterValue.length > 0) {
      filter = [{
        field: 'email_query',
        operator: 'in',
        value: filterValue
      }];
    }
    var updatedPersistentFilter = new Tine.widgets.persistentfilter.model.PersistentFilter({
      filters: filter,
      name: persistentFilter.get('name'),
      model: 'Addressbook_Model_Contact',
      application_id: this.app.id,
      id: Ext.id()
    });
    Tine.Felamimail.RecipientPickerFavoritePanel.superclass.onFilterSelect.call(this, updatedPersistentFilter);
  }
});

/***/ }),

/***/ 90562:
/***/ (() => {

Tine.widgets.form.FieldManager.register('Felamimail', 'Signature', 'signature', {
  xtype: 'htmleditor',
  name: 'signature',
  defaultFont: Tine.Felamimail.registry.get('preferences').get('defaultfont') || 'tahoma',
  height: 300,
  getDocMarkup: function () {
    var markup = '<span id="felamimail\-body\-signature">' + '</span>';
    return markup;
  },
  plugins: [new Ext.ux.form.HtmlEditor.RemoveFormat()]
}, Tine.widgets.form.FieldManager.CATEGORY_EDITDIALOG);

/***/ }),

/***/ 38062:
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

/*
 * Tine 2.0
 *
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2019 Metaways Infosystems GmbH (http://www.metaways.de)
 */

__webpack_require__(90562);
Tine.Felamimail.SignatureGridPanel = Ext.extend(Ext.grid.GridPanel, {
  editDialog: null,
  // private
  enableHdMenu: false,
  autoExpandColumn: 'name',
  border: false,
  initComponent: function () {
    let me = this;
    me.app = Tine.Tinebase.appMgr.get('Felamimail');
    me.title = me.app.i18n._('Signatures');
    me.recordClass = Tine.Tinebase.data.RecordMgr.get('Felamimail.Model.Signature');
    me.store = new Ext.data.JsonStore({
      autoLoad: false,
      fields: me.recordClass,
      sortInfo: {
        field: 'name',
        direction: 'ASC'
      }
    });
    me.columns = [{
      id: 'name',
      dataIndex: 'name',
      header: me.app.i18n._('Name'),
      sortable: false
    }, me.checkColumn = new Ext.ux.grid.CheckColumn({
      id: 'is_default',
      header: me.app.i18n._('Is Default'),
      dataIndex: 'is_default',
      width: 100,
      listeners: {
        checkchange: _.bind(me.onCheckChange, me)
      }
    })];
    me.plugins = [me.checkColumn];
    me.tbar = [{
      ref: '../addAction',
      text: me.app.i18n._('Add Signature'),
      iconCls: 'action_add',
      handler: _.bind(me.onEditSignature, me, 'add')
    }, {
      ref: '../editAction',
      text: me.app.i18n._('Edit Signature'),
      iconCls: 'action_edit',
      disabled: true,
      handler: _.bind(me.onEditSignature, me, 'edit')
    }, {
      ref: '../deleteAction',
      text: me.app.i18n._('Delete Signature'),
      iconCls: 'action_delete',
      disabled: true,
      handler: _.bind(me.onDeleteSignature, me)
    }];
    me.selModel = new Ext.grid.RowSelectionModel({
      // singleSelect: true,
      listeners: {
        selectionchange: _.bind(me.onSelectionChange, me)
      }
    });
    me.on('rowdblclick', _.bind(me.onEditSignature, me, 'edit'));
    me.editDialog.on('load', me.onRecordLoad, me);
    me.editDialog.on('save', me.onRecordUpdate, me);
    Tine.Felamimail.SignatureGridPanel.superclass.initComponent.call(this);
  },
  onSelectionChange: function (sm) {
    let me = this;
    me.editAction.setDisabled(sm.getCount() !== 1);
    me.deleteAction.setDisabled(!sm.getCount());
  },
  onRecordLoad: function (ed, record) {
    const signatures = _.get(record, 'data.signatures', []);
    this.store.loadData(signatures);
    const idx = this.store.find('is_default', true);
    if (idx === -1 && this.store.totalLength > 0) {
      const record = this.store.getAt(0);
      record.set('is_default', true);
      record.commit();
    }
  },
  onRecordUpdate: function (dlg, record) {
    let me = this;
    Tine.Tinebase.common.assertComparable(record.data.signatures);
    record.set('signatures', _.map(_.get(me, 'store.data.items', []), 'data'));
  },
  onEditSignature: function (action) {
    let me = this;
    let recordData = action !== 'add' ? me.selModel.getSelected().data : {
      id: Tine.Tinebase.data.Record.generateUID(),
      account_id: me.editDialog.record.get('id')
    };
    Tine.Felamimail.SignatureEditDialog.openWindow({
      mode: 'local',
      record: JSON.stringify(recordData),
      listeners: {
        scope: me,
        'update': record => {
          let signature = Tine.Tinebase.data.Record.setFromJson(record, me.recordClass);
          let existing = me.store.getById(signature.id);
          if (existing) {
            me.store.remove(existing);
          }
          me.store.add(signature);
          let hasDefault = _.reduce(me.store.data.items, (hd, r) => {
            return hd || r.get('is_default');
          }, false);
          me.onCheckChange(null, signature.get('is_default') || !hasDefault, null, signature);
        }
      }
    });
  },
  onDeleteSignature: function () {
    let me = this;
    me.store.remove(me.selModel.getSelected());
  },
  onCheckChange: function (cc, newVal, oldVal, record) {
    if (newVal) {
      this.store.each(r => {
        r.set('is_default', false);
        r.commit();
      });
      record.set('is_default', true);
      record.commit();
    }
  }
});

/***/ }),

/***/ 57671:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2009-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 */

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.TreeLoader
 * @extends     Tine.widgets.tree.Loader
 * 
 * <p>Felamimail Account/Folder Tree Loader</p>
 * <p>
 * </p>
 * 
 * @author      Philipp Schuele <p.schuele@metaways.de>
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Felamimail.TreeLoader
 * 
 */
Tine.Felamimail.TreeLoader = Ext.extend(Tine.widgets.tree.Loader, {
  /**
   * request data
   * 
   * @param {Ext.tree.TreeNode} node
   * @param {Function} callback Function to call after the node has been loaded. The
   * function is passed the TreeNode which was requested to be loaded.
   * @param (Object) scope The cope (this reference) in which the callback is executed.
   * defaults to the loaded TreeNode.
   */
  requestData: function (node, callback, scope) {
    if (this.fireEvent("beforeload", this, node, callback) !== false) {
      var fstore = Tine.Tinebase.appMgr.get('Felamimail').getFolderStore(),
        folder = fstore.getById(node.attributes.folder_id),
        path = folder ? folder.get('path') : node.attributes.path;

      // we need to call doQuery fn from store to transparently do async request
      fstore.asyncQuery('parent_path', path, function (node, callback, scope, data) {
        if (data) {
          node.beginUpdate();
          data.each(function (folderRecord) {
            var n = this.createNode(folderRecord.copy().data);
            if (n) {
              node.appendChild(n);
            }
          }, this);
          node.endUpdate();
        }
        this.runCallback(callback, scope || node, [node]);
      }, [node, callback, scope], this, fstore);
    } else {
      // if the load is cancelled, make sure we notify
      // the node that we are done
      this.runCallback(callback, scope || node, []);
    }
  },
  /**
   * inspectCreateNode
   * 
   * @private
   */
  inspectCreateNode: function (attr) {
    var account = Tine.Tinebase.appMgr.get('Felamimail').getAccountStore().getById(attr.account_id);

    // NOTE cweiss 2010-06-15 this has to be precomputed on server side!
    attr.has_children = account && account.get('has_children_support') ? attr.has_children : true;
    if (attr.has_children == "0") {
      attr.has_children = false;
    }
    Ext.apply(attr, {
      leaf: !attr.has_children,
      expandable: attr.has_children,
      cls: 'x-tree-node-collapsed',
      folder_id: attr.id,
      folderNode: true,
      allowDrop: true,
      text: this.app.i18n._hidden(attr.localname)
    });

    // show standard folders icons 
    if (account) {
      if (account.get('trash_folder') === attr.globalname) {
        if (attr.cache_totalcount > 0) {
          attr.cls = 'felamimail-node-trash-full';
        } else {
          attr.cls = 'felamimail-node-trash';
        }
      }
      if (account.get('sent_folder') === attr.globalname) {
        attr.cls = 'felamimail-node-sent';
      }
      if (account.get('drafts_folder') === attr.globalname) {
        attr.cls = 'felamimail-node-drafts';
      }
      if (account.get('templates_folder') === attr.globalname) {
        attr.cls = 'felamimail-node-templates';
      }
    }
    if (attr.globalname.match(/^inbox$/i)) {
      attr.cls = 'felamimail-node-inbox';
      attr.text = this.app.i18n._hidden('INBOX');
    }
    if (attr.globalname.match(/^junk$/i)) {
      attr.cls = 'felamimail-node-junk';
    }
    if (!attr.is_selectable) {
      attr.cls = 'felamimail-node-unselectable';
    }
  }
});

/***/ }),

/***/ 89697:
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2009-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.namespace('Tine.Felamimail');
__webpack_require__(87879);

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.FilterPanel
 * @extends     Tine.widgets.persistentfilter.PickerPanel
 * 
 * <p>Felamimail Favorites Panel</p>
 * 
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Felamimail.FilterPanel
 */
Tine.Felamimail.FilterPanel = Ext.extend(Tine.widgets.persistentfilter.PickerPanel, {
  filterModel: 'Felamimail_Model_MessageFilter'
});

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.TreePanel
 * @extends     Ext.tree.TreePanel
 * 
 * <p>Account/Folder Tree Panel</p>
 * <p>Tree of Accounts with folders</p>
 * <pre>
 * low priority:
 * TODO         make inbox/drafts/templates configurable in account
 * TODO         disable delete action in account ctx menu if user has no manage_accounts right
 * </pre>
 * 
 * @author      Philipp Schuele <p.schuele@metaways.de>
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Felamimail.TreePanel
 * 
 */
Tine.Felamimail.TreePanel = function (config) {
  Ext.apply(this, config);
  this.addEvents(
  /**
   * @event containeradd
   * Fires when a folder was added
   * @param {folder} the new folder
   */
  'containeradd',
  /**
   * @event containerdelete
   * Fires when a folder got deleted
   * @param {folder} the deleted folder
   */
  'containerdelete',
  /**
   * @event containerrename
   * Fires when a folder got renamed
   * @param {folder} the renamed folder
   */
  'containerrename');
  Tine.Felamimail.TreePanel.superclass.constructor.call(this);
};
Ext.extend(Tine.Felamimail.TreePanel, Ext.tree.TreePanel, {
  /**
   * @property app
   * @type Tine.Felamimail.Application
   */
  app: null,
  /**
   * @property accountStore
   * @type Ext.data.JsonStore
   */
  accountStore: null,
  /**
   * @type Ext.data.JsonStore
   */
  folderStore: null,
  /**
   * @cfg {String} containerName
   */
  containerName: 'Folder',
  /**
   * TreePanel config
   * @private
   */
  rootVisible: true,
  /**
   * drag n drop
   */
  enableDrop: true,
  ddGroup: 'mailToTreeDDGroup',
  /**
   * @cfg
   */
  border: false,
  filterMode: 'filterToolbar',
  /**
   * define state events
   */
  stateful: true,
  stateId: 'Felamimail-TreePanel',
  stateEvents: ['expandnode', 'collapsenode'],
  /**
   * is needed by Tine.widgets.mainscreen.WestPanel to fake container tree panel
   */
  selectContainerPath: Ext.emptyFn,
  /**
   * init
   * @private
   */
  initComponent: function () {
    this.recordClass = Tine.Felamimail.Model.Account;

    // get folder store
    this.folderStore = Tine.Tinebase.appMgr.get('Felamimail').getFolderStore();

    // init tree loader
    this.loader = new Tine.Felamimail.TreeLoader({
      folderStore: this.folderStore,
      app: this.app
    });

    // set the root node
    this.root = new Ext.tree.TreeNode({
      cls: 'felamimail-node-root',
      text: 'default',
      draggable: false,
      allowDrop: false,
      expanded: true,
      leaf: false,
      id: 'root'
    });

    // add account nodes
    this.initAccounts();

    // init drop zone
    this.dropConfig = {
      ddGroup: this.ddGroup || 'TreeDD',
      appendOnly: this.ddAppendOnly === true,
      notifyEnter: function () {
        this.isDropSensitive = true;
      }.createDelegate(this),
      notifyOut: function () {
        this.isDropSensitive = false;
      }.createDelegate(this),
      onNodeOver: function (n, dd, e, data) {
        var node = n.node;

        // auto node expand check (only for non-account nodes)
        if (!this.expandProcId && node.attributes.allowDrop && node.hasChildNodes() && !node.isExpanded()) {
          this.queueExpand(node);
        } else if (!node.attributes.allowDrop) {
          this.cancelExpand();
        }
        return node.attributes.allowDrop ? 'tinebase-tree-drop-move' : false;
      },
      isValidDropPoint: function (n, dd, e, data) {
        return n.node.attributes.allowDrop;
      }
    };

    // init selection model (multiselect)
    this.selModel = new Ext.tree.MultiSelectionModel({});

    // init context menu
    this.initContextMenu();

    // add listeners
    this.on('beforestatesave', this.saveStateIf, this);
    this.on('beforeclick', this.onBeforeClick, this);
    this.on('click', this.onClick, this);
    this.on('contextmenu', this.onContextMenu, this);
    this.on('beforenodedrop', this.onBeforenodedrop, this);
    this.on('append', this.onAppend, this);
    this.on('containeradd', this.onFolderAdd, this);
    this.on('containerrename', this.onFolderRename, this);
    this.on('containerdelete', this.onFolderDelete, this);
    this.selModel.on('selectionchange', this.onSelectionChange, this);
    this.folderStore.on('update', this.onUpdateFolderStore, this);

    // call parent::initComponent
    Tine.Felamimail.TreePanel.superclass.initComponent.call(this);
    this.treeSorter = new Ext.tree.TreeSorter(this, {
      dir: "asc",
      priorityList: ['INBOX', 'Drafts', 'Sent', 'Templates', 'Junk', 'Trash'],
      priorityProperty: 'globalname',
      doSort: function (node) {
        if (node.ownerTree.getRootNode() !== node) {
          node.sort(this.sortFn);
        }
      }
    });
  },
  /**
   * get state
   */
  getState: function () {
    return {
      paths: this.getExpandedPaths(this.getRootNode()),
      selected: _.get(this.getSelectionModel().getSelectedNode(), 'id'),
      selectedGlobalName: _.get(this.getSelectionModel().getSelectedNode(), 'attributes.globalname')
    };
  },
  /**
   * get the deepest nodes expanded paths
   *
   * @param node
   */
  getExpandedPaths: function (node) {
    let paths = [];
    if (node.expanded) {
      paths.push(node.getPath());
      if (node.hasChildNodes()) {
        Ext.each(node.childNodes, function (childNode) {
          paths = paths.concat(this.getExpandedPaths(childNode));
        }, this);
      }
    }
    paths.reverse();
    paths = _.reduce(paths, (reduced, path) => {
      return reduced.concat(String(reduced[reduced.length - 1]).startsWith(path) ? [] : [path]);
    }, []);
    return paths;
  },
  /**
   * set expanded paths
   */
  setExpandedPaths: async function (state) {
    let promises = [];
    if (!(state !== null && state !== void 0 && state.paths)) {
      return;
    }
    _.each(state.paths, path => {
      const promise = new Promise((resolve, reject) => {
        this.expandPath(path, null, function (success, oLastNode) {
          if (success) {
            resolve();
          } else {
            reject('expand paths failed.');
          }
        });
      });
      promises.push(promise);
    });
    return Promise.all(promises);
  },
  /**
   * add accounts from registry as nodes to root node
   * @private
   */
  initAccounts: function () {
    this.accountStore = this.app.getAccountStore();
    this.accountStore.each(this.addAccount, this);
    this.accountStore.on('load', function (store, records) {
      this.root.removeAll();
      _.map(records, _.bind(this.addAccount, this));
      this.selectLastSelectedNode();
    }, this);
    this.accountStore.on('add', function (store, records) {
      _.map(records, _.bind(this.addAccount, this));
    }, this);
    this.accountStore.on('update', this.onAccountUpdate, this);
    this.accountStore.on('remove', this.deleteAccount, this);
  },
  /**
   * initiates tree context menus
   *
   * @private
   */
  initContextMenu: function () {
    this.accountMenu = Tine.widgets.tree.ContextMenu.getMenu({
      app: 'Felamimail',
      actionMgr: Tine.Felamimail.nodeActionsMgr,
      nodeName: this.app.i18n.n_('Account', 'Accounts', 1),
      scope: this,
      backend: 'Felamimail',
      backendModel: 'Account',
      actions: Tine.Felamimail.nodeActions.accountActions
    });
    this.folderMenu = Tine.widgets.tree.ContextMenu.getMenu({
      app: 'Felamimail',
      actionMgr: Tine.Felamimail.nodeActionsMgr,
      nodeName: this.app.i18n.n_('Folder', 'Folders', 1),
      scope: this,
      backend: 'Felamimail',
      backendModel: 'Folder',
      actions: Tine.Felamimail.nodeActions.folderActions
    });
    this.actionUpdater = new Tine.widgets.ActionUpdater({
      recordClass: Tine.Felamimail.Model.Account,
      actions: _.concat(this.accountMenu.items.items, this.folderMenu.items.items)
    });
  },
  /**
   * init extra tool tips
   */
  initToolTips: function () {
    this.folderTip = new Ext.ToolTip({
      target: this.getEl(),
      delegate: 'a.x-tree-node-anchor',
      renderTo: document.body,
      listeners: {
        beforeshow: this.updateFolderTip.createDelegate(this)
      }
    });
    this.folderProgressTip = new Ext.ToolTip({
      target: this.getEl(),
      delegate: '.felamimail-node-statusbox-progress',
      renderTo: document.body,
      listeners: {
        beforeshow: this.updateProgressTip.createDelegate(this)
      }
    });
    this.folderUnreadTip = new Ext.ToolTip({
      target: this.getEl(),
      delegate: '.felamimail-node-statusbox-unread',
      renderTo: document.body,
      listeners: {
        beforeshow: this.updateUnreadTip.createDelegate(this)
      }
    });
  },
  /**
   * called when tree selection changes
   * 
   * @param {} sm
   * @param {} node
   */
  onSelectionChange: function (sm, nodes) {
    if (this.filterMode === 'gridFilter' && this.filterPlugin) {
      this.filterPlugin.onFilterChange();
    }
    if (this.filterMode === 'filterToolbar' && this.filterPlugin) {
      // get filterToolbar
      let ftb = this.filterPlugin.getGridPanel().filterToolbar;
      // in case of filterPanel
      ftb = ftb.activeFilterPanel ? ftb.activeFilterPanel : ftb;

      // set ftb filters according to tree selection
      const oldPathFilters = ftb.filterStore.query('field', 'path');
      const newPathFilter = this.getFilterPlugin().getFilter();
      const grid = this.filterPlugin.getGridPanel();
      const pathFilterValue = newPathFilter !== null && newPathFilter !== void 0 && newPathFilter.value && _.isArray(newPathFilter === null || newPathFilter === void 0 ? void 0 : newPathFilter.value) ? newPathFilter.value[0] : null;
      const isSentFolder = grid.isSendFolderPath(pathFilterValue);

      // not longer needed as the query filter now contains 'to' as well
      // ftb.defaultFilter = isSentFolder ? 'to_list' : 'query';

      if (oldPathFilters.length > 0) {
        // update path filter
        ftb.supressEvents = true;
        _.each(oldPathFilters.items, (oldPathFilter, idx) => {
          if (idx + 1 === oldPathFilters.length) {
            ftb.setFilterData(oldPathFilter, newPathFilter);
          } else {
            ftb.deleteFilter(oldPathFilter);
          }
        });
        ftb.supressEvents = false;
      } else {
        ftb.addFilter(new ftb.record(newPathFilter));
      }
      ftb.onFiltertrigger();

      // finally select the selected node, as filtertrigger clears all selections
      sm.suspendEvents();
      Ext.each(nodes, function (node) {
        sm.select(node, Ext.EventObject, true);
      }, this);
      sm.resumeEvents();
    }
    this.saveState();
  },
  /**
   * returns a filter plugin to be used in a grid
   * @private
   */
  getFilterPlugin: function () {
    if (!this.filterPlugin) {
      this.filterPlugin = new Tine.widgets.tree.FilterPlugin({
        treePanel: this,
        field: 'path',
        nodeAttributeField: 'path',
        singleNodeOperator: 'in'
      });
    }
    return this.filterPlugin;
  },
  /**
   * convert containerPath to treePath
   * 
   * @param {String}  containerPath
   * @return {String} treePath
   */
  getTreePath: function (path) {
    return '/root' + path;
  },
  /**
   * @private
   * 
   * expand default account and select INBOX
   */
  afterRender: function () {
    Tine.Felamimail.TreePanel.superclass.afterRender.call(this);
    this.initToolTips();
    this.selectLastSelectedNode();
  },
  /**
   * select the last selected node
   * - expand the recorded expanded node from db
   */
  selectLastSelectedNode: async function () {
    if (!this.root.rendered) {
      return;
    }
    const state = Ext.state.Manager.get(this.stateId);
    this.applyingState = true;
    this.expandPortalColumn();
    try {
      await this.setExpandedPaths(state);
    } catch (error) {
      this.selectInbox();
      this.applyingState = false;
    }
    let node = this.getNodeById(_.get(state, 'selected'));
    if (!node) {
      this.selectInbox();
    } else {
      if (!node.isSelected()) {
        node.select();
      }
    }
    this.applyingState = false;
  },
  /**
   * expand portal column "Email Accounts"
   */
  expandPortalColumn: function () {
    Ext.each(this.app.getMainScreen().getWestPanel().getPortalColumn().items.items, function (item) {
      if (Ext.isFunction(item.expand) && item.recordClass) {
        item.expand();
      }
    });
  },
  /**
   * select inbox of account
   * @param {Record} account
   */
  selectInbox: function (account) {
    const accountId = account ? account.id : Tine.Felamimail.registry.get('preferences').get('defaultEmailAccount');
    this.expandPath('/root/' + accountId + '/', null, function (success, parentNode) {
      Ext.each(parentNode.childNodes, function (node) {
        if (Ext.util.Format.lowercase(node.attributes.localname) === 'inbox' && !node.isSelected()) {
          node.select();
          return false;
        }
      }, this);
    });
  },
  /**
   * return flag for applying state
   */
  saveStateIf() {
    return !this.applyingState;
  },
  /**
   * called when an account record updates
   * 
   * @param {Ext.data.JsonStore} store
   * @param {Tine.Felamimail.Model.Account} record
   * @param {String} action
   */
  onAccountUpdate: function (store, record, action) {
    if (action === Ext.data.Record.EDIT) {
      this.updateAccountStatus(record);
      this.selectLastSelectedNode();
    }
  },
  /**
   * on append node
   * 
   * render status box
   * 
   * @param {Tine.Felamimail.TreePanel} tree
   * @param {Ext.Tree.TreeNode} node
   * @param {Ext.Tree.TreeNode} appendedNode
   * @param {Number} index
   */
  onAppend: function (tree, node, appendedNode, index) {
    appendedNode.ui.render = appendedNode.ui.render.createSequence(function () {
      var app = Tine.Tinebase.appMgr.get('Felamimail'),
        folder = app.getFolderStore().getById(appendedNode.id);
      if (folder) {
        app.getMainScreen().getTreePanel().addStatusboxesToNodeUi(this);
        app.getMainScreen().getTreePanel().updateFolderStatus(folder);
      }
    }, appendedNode.ui);
  },
  /**
   * add status boxes
   * 
   * @param {Object} nodeUi
   */
  addStatusboxesToNodeUi: function (nodeUi) {
    var _nodeUi$elNode;
    if (nodeUi !== null && nodeUi !== void 0 && (_nodeUi$elNode = nodeUi.elNode) !== null && _nodeUi$elNode !== void 0 && _nodeUi$elNode.lastChild) {
      var _nodeUi$elNode$lastCh;
      if (((_nodeUi$elNode$lastCh = nodeUi.elNode.lastChild) === null || _nodeUi$elNode$lastCh === void 0 ? void 0 : _nodeUi$elNode$lastCh.className) === 'felamimail-node-statusbox') {
        return;
      }
      Ext.DomHelper.insertAfter(nodeUi.elNode.lastChild, {
        tag: 'span',
        'class': 'felamimail-node-statusbox',
        cn: [{
          'tag': 'img',
          'src': Ext.BLANK_IMAGE_URL,
          'class': 'felamimail-node-statusbox-progress'
        }, {
          'tag': 'span',
          'class': 'felamimail-node-statusbox-unread'
        }]
      });
    }
  },
  /**
   * on before click handler
   * - accounts are not clickable because fetching all messages of account is too expensive
   * - skip event for folders that are not selectable
   * 
   * @param {Ext.tree.AsyncTreeNode} node
   */
  onBeforeClick: function (node) {
    const account = this.accountStore.getById(node.id);
    if (account || !this.app.getFolderStore().getById(node.id).get('is_selectable')) {
      // account node should not trigger click event but still need to be expanded
      if (account && node.isExpandable()) {
        node.toggle();
      }
      return false;
    }
  },
  /**
   * on click handler
   * 
   * - expand node
   * - update filter toolbar of grid
   * - start check mails delayed task
   * 
   * @param {Ext.tree.AsyncTreeNode} node
   * @private
   */
  onClick: function (node) {
    if (node.isExpandable() && !node.expanded) {
      // NOTE: we don't expand here any longer - macos and win don't do it either
      // node.expand();
    }
    if (node.id && node.id !== '/' && node.attributes.globalname !== '') {
      const folder = this.app.getFolderStore().getById(node.id);
      if (folder) {
        if (folder.get('cache_status') === 'pending') {
          this.app.checkMails(folder, Ext.emptyFn);
        }
        // lasy wait for selection change
        _.delay(() => {
          this.updateFolderStatus(folder);
        }, 100);
      }
    }
  },
  /**
   * show context menu for folder tree
   * 
   * items:
   * - create folder
   * - rename folder
   * - delete folder
   * - ...
   * 
   * @param {} node
   * @param {} event
   * @private
   */
  onContextMenu: function (node, event) {
    event.stopEvent();
    Tine.log.debug(node);

    // legacy for reload action
    this.ctxNode = node;
    const folder = this.app.getFolderStore().getById(node.attributes.id);
    this.ctxMenu = !folder ? this.accountMenu : this.folderMenu;
    this.actionUpdater.updateActions([node]);
    this.ctxMenu.showAt(event.getXY());
  },
  /**
   * mail(s) got dropped on node
   * 
   * @param {Object} dropEvent
   * @private
   */
  onBeforenodedrop: function (dropEvent) {
    var targetFolderId = dropEvent.target.attributes.folder_id,
      targetFolder = this.app.getFolderStore().getById(targetFolderId);
    this.app.getMainScreen().getCenterPanel().moveSelectedMessages(targetFolder, false);
    return true;
  },
  /**
   * cleanup on destruction
   */
  onDestroy: function () {
    this.folderStore.un('update', this.onUpdateFolderStore, this);
  },
  /**
   * folder store gets updated -> update tree nodes
   * 
   * @param {Tine.Felamimail.FolderStore} store
   * @param {Tine.Felamimail.Model.Folder} record
   * @param {String} operation
   */
  onUpdateFolderStore: function (store, record, operation) {
    if (operation === Ext.data.Record.EDIT) {
      this.updateFolderStatus(record);
    }
  },
  /**
   * add new folder to the store
   * 
   * @param {Object} folderData
   */
  onFolderAdd: function (folderData) {
    var recordData = Ext.copyTo({}, folderData, Tine.Felamimail.Model.Folder.getFieldNames());
    var newRecord = Tine.Felamimail.folderBackend.recordReader({
      responseText: Ext.util.JSON.encode(recordData)
    });
    this.ctxNode.expand();
    this.ctxNode.appendChild(this.loader.createNode(folderData));
    const parentRecord = this.folderStore.getById(this.ctxNode.id);
    if (parentRecord) parentRecord.set('has_children', true);
    this.folderStore.add([newRecord]);
    this.initNewFolderNode(newRecord);
  },
  /**
   * init new folder node
   * 
   * @param {Tine.Felamimail.Model.Folder} newRecord
   */
  initNewFolderNode: function (newRecord) {
    // update paths in node
    var appendedNode = this.getNodeById(newRecord.id);
    if (!appendedNode) {
      // node is not yet rendered -> reload parent
      var parentId = newRecord.get('parent_path').split('/').pop(),
        parentNode = this.getNodeById(parentId);
      if (Ext.isFunction(parentNode.reload)) {
        return parentNode.reload(function () {
          this.initNewFolderNode(newRecord);
        }, this);
      } else {
        return;
      }
    }
    appendedNode.attributes.path = newRecord.get('path');
    appendedNode.attributes.parent_path = newRecord.get('parent_path');

    // add unreadcount/progress/tooltip
    this.addStatusboxesToNodeUi(appendedNode.ui);
    this.updateFolderStatus(newRecord);
  },
  /**
   * rename folder in the store
   * 
   * @param {Object} folderData
   */
  onFolderRename: function (folderData) {
    var record = this.folderStore.getById(folderData.id);
    record.set('globalname', folderData.globalname);
    record.set('localname', folderData.localname);
    Tine.log.debug('Renamed folder:' + record.get('globalname'));
  },
  /**
   * remove deleted folder from the store
   * 
   * @param {Object} folderData
   */
  onFolderDelete: function (folderData) {
    // if we deleted account, remove it from account store
    if (folderData.record && folderData.record.modelName === 'Account') {
      this.accountStore.remove(this.accountStore.getById(folderData.id));
    }
    this.folderStore.remove(this.folderStore.getById(folderData.id));
  },
  /**
   * returns tree node id the given el is child of
   * 
   * @param  {HTMLElement} el
   * @return {String}
   */
  getElsParentsNodeId: function (el) {
    return Ext.fly(el, '_treeEvents').up('div[class^=x-tree-node-el]').getAttribute('tree-node-id', 'ext');
  },
  /**
   * updates account status icon in this tree
   * 
   * @param {Tine.Felamimail.Model.Account} account
   */
  updateAccountStatus: function (account) {
    var imapStatus = account.get('imap_status'),
      node = this.getNodeById(account.id),
      ui = node ? node.getUI() : null,
      nodeEl = ui ? ui.getEl() : null;
    Tine.log.info('Account ' + account.get('name') + ' updated with imap_status: ' + imapStatus);
    if (node && node.ui.rendered) {
      var statusEl = Ext.get(Ext.DomQuery.selectNode('span[class=felamimail-node-accountfailure]', nodeEl));
      if (!statusEl) {
        // create statusEl on the fly
        statusEl = Ext.DomHelper.insertAfter(ui.elNode.lastChild, {
          'tag': 'span',
          'class': 'felamimail-node-accountfailure'
        }, true);
        statusEl.on('click', function () {
          Tine.Felamimail.folderBackend.handleRequestException(account.getLastIMAPException());
        }, this);
      }
      statusEl.setVisible(imapStatus === 'failure');
    }
  },
  /**
   * updates folder status icons/info in this tree
   * 
   * @param {Tine.Felamimail.Model.Folder} folder
   */
  updateFolderStatus: function (folder) {
    var unreadcount = folder.get('cache_unreadcount'),
      totalcount = folder.get('cache_totalcount'),
      progress = Math.round(folder.get('cache_job_actions_done') / folder.get('cache_job_actions_est') * 10) * 10,
      node = this.getNodeById(folder.id),
      ui = node ? node.getUI() : null,
      nodeEl = ui ? ui.getEl() : null,
      cacheStatus = folder.get('cache_status'),
      lastCacheStatus = folder.modified ? folder.modified.cache_status : null,
      isSelected = folder.isCurrentSelection(),
      account = folder ? this.accountStore.getById(folder.get('account_id')) : this.accountStore.getById(node.id);
    this.setUnreadClass(folder.id);
    if (node && node.ui.rendered) {
      var domNode = Ext.DomQuery.selectNode('span[class=felamimail-node-statusbox-unread]', nodeEl);
      if (domNode) {
        //draft folder show totalcount instead  
        if (folder.get('globalname') === account.get('drafts_folder')) {
          Ext.fly(domNode).update(totalcount).setVisible(totalcount > 0);
        } else {
          // update unreadcount + visibity
          Ext.fly(domNode).update(unreadcount).setVisible(unreadcount > 0);
        }

        // update progress
        var progressEl = Ext.get(Ext.DomQuery.selectNode('img[class^=felamimail-node-statusbox-progress]', nodeEl));
        progressEl.removeClass(['felamimail-node-statusbox-progress-pie', 'felamimail-node-statusbox-progress-loading']);
        if (!Ext.isNumber(progress)) {
          progressEl.addClass('felamimail-node-statusbox-progress-loading');
        } else {
          progressEl.addClass('felamimail-node-statusbox-progress-pie');
          progressEl.addClass('felamimail-node-statusbox-progress-pie-' + progress);
        }
        progressEl.setVisible(isSelected && cacheStatus !== 'complete' && cacheStatus !== 'disconnect' && progress !== 100 && lastCacheStatus !== 'complete');
      }
    }
  },
  /**
   * set unread class of folder node and parents
   * 
   * @param {Tine.Felamimail.Model.Folder} folder
   * 
   * TODO make it work correctly for parents (use events) and activate again
   */
  setUnreadClass: function (folderId) {
    var folder = this.app.getFolderStore().getById(folderId),
      node = this.getNodeById(folderId),
      isUnread = folder.get('cache_unreadcount') > 0,
      hasUnreadChildren = folder.get('unread_children').length > 0;
    if (node && node.ui.rendered) {
      var ui = node.getUI();
      ui[isUnread || hasUnreadChildren ? 'addClass' : 'removeClass']('felamimail-node-unread');
    }

    // get parent, update and call recursivly
    //        var parentFolder = this.app.getFolderStore().getParent(folder);
    //        if (parentFolder) {
    //            // need to create a copy of the array here (and make sure it is unique)
    //            var unreadChildren = Ext.unique(parentFolder.get('unread_children'));
    //                
    //            if (isUnread || hasUnreadChildren) {
    //                unreadChildren.push(folderId);
    //            } else {
    //                unreadChildren.remove(folderId);
    //            }
    //            parentFolder.set('unread_children', unreadChildren);
    //            this.setUnreadClass(parentFolder.id);
    //        }
  },

  /**
   * updates the given tip
   * @param {Ext.Tooltip} tip
   */
  updateFolderTip: function (tip) {
    var folderId = this.getElsParentsNodeId(tip.triggerElement),
      folder = this.app.getFolderStore().getById(folderId),
      account = this.accountStore.getById(folderId);
    if (folder && !this.isDropSensitive) {
      var info = ['<table>', '<tr>', '<td>', this.app.i18n._('Total Messages:'), '</td>', '<td>', folder.get('cache_totalcount'), '</td>', '</tr>', '<tr>', '<td>', this.app.i18n._('Unread Messages:'), '</td>', '<td>', folder.get('cache_unreadcount'), '</td>', '</tr>', '<tr>', '<td>', this.app.i18n._('Name on Server:'), '</td>', '<td>', folder.get('globalname'), '</td>', '</tr>', '<tr>', '<td>', this.app.i18n._('Last update:'), '</td>', '<td>', Tine.Tinebase.common.dateTimeRenderer(folder.get('client_access_time')), '</td>', '</tr>', '</table>'];
      tip.body.dom.innerHTML = info.join('');
    } else {
      return false;
    }
  },
  /**
   * updates the given tip
   * @param {Ext.Tooltip} tip
   */
  updateProgressTip: function (tip) {
    var folderId = this.getElsParentsNodeId(tip.triggerElement),
      folder = this.app.getFolderStore().getById(folderId),
      progress = Math.round(folder.get('cache_job_actions_done') / folder.get('cache_job_actions_est') * 100);
    if (!this.isDropSensitive) {
      tip.body.dom.innerHTML = String.format(this.app.i18n._('Fetching messages... ({0} done)'), progress + '%');
    } else {
      return false;
    }
  },
  /**
   * updates the given tip
   * @param {Ext.Tooltip} tip
   */
  updateUnreadTip: function (tip) {
    var folderId = this.getElsParentsNodeId(tip.triggerElement),
      folder = this.app.getFolderStore().getById(folderId),
      count = folder.get('cache_unreadcount');
    if (!this.isDropSensitive) {
      tip.body.dom.innerHTML = String.format(this.app.i18n.ngettext('{0} unread message', '{0} unread messages', count), count);
    } else {
      return false;
    }
  },
  /**
   * decrement unread count of currently selected folder
   */
  decrementCurrentUnreadCount: function () {
    var store = Tine.Tinebase.appMgr.get('Felamimail').getFolderStore(),
      node = this.getSelectionModel().getSelectedNode(),
      folder = node ? store.getById(node.id) : null;
    if (folder) {
      folder.set('cache_unreadcount', parseInt(folder.get('cache_unreadcount'), 10) - 1);
      folder.commit();
    }
  },
  /**
   * add account record to root node
   * 
   * @param {Tine.Felamimail.Model.Account} record
   */
  addAccount: function (record) {
    var node = new Ext.tree.AsyncTreeNode({
      id: record.data.id,
      path: '/' + record.data.id,
      record: record,
      globalname: '',
      draggable: false,
      allowDrop: false,
      expanded: false,
      text: Ext.util.Format.htmlEncode(record.get('name')),
      qtip: Ext.util.Format.htmlEncode(record.get('host')),
      leaf: false,
      cls: 'felamimail-node-account',
      delimiter: record.get('delimiter'),
      ns_personal: record.get('ns_personal'),
      account_id: record.data.id,
      listeners: {
        scope: this,
        load: function (node) {
          var account = this.accountStore.getById(node.id);
          this.updateAccountStatus(account);
        }
      }
    });

    // we don't want appending folder effects
    this.suspendEvents();
    this.root.appendChild(node);
    this.resumeEvents();
    _.defer(() => {
      this.app.getMainScreen().getCenterPanel().action_write.setDisabled(!this.app.getActiveAccount());
    });
  },
  deleteAccount: function (store, account) {
    let accountNode = this.root.findChild('path', '/' + account.data.id);
    if (accountNode) {
      try {
        accountNode.remove(true);
      } catch (e) {}
    }
  },
  /**
   * get active account by checking selected node
   * @return Tine.Felamimail.Model.Account
   */
  getActiveAccount: function () {
    var result = null;
    var node = this.getSelectionModel().getSelectedNode();
    if (node) {
      var accountId = node.attributes.account_id;
      result = this.accountStore.getById(accountId);
    }
    return result;
  }
});

/***/ }),

/***/ 51496:
/***/ (() => {

Ext.ns('Tine.Felamimail.admin');
Tine.Felamimail.admin.showAccountGridPanel = function () {
  var app = Tine.Tinebase.appMgr.get('Felamimail');
  if (!Tine.Felamimail.admin.emailAccountsGridPanel) {
    Tine.Felamimail.admin.emailAccountsGridPanel = new Tine.Felamimail.AccountGridPanel({
      asAdminModule: true,
      // NOTE: needed so ADMIN API's are used
      recordProxy: new Tine.Tinebase.data.RecordProxy({
        appName: 'Admin',
        modelName: 'EmailAccount',
        recordClass: Tine.Felamimail.Model.Account,
        idProperty: 'id'
      }),
      initActions: function () {
        let isSystemAccountActionUpdater = function (action, grants, records, isFilterSelect) {
          var enabled = !isFilterSelect && records && records.length === 1 && _.indexOf(['system', 'shared', 'userInternal'], records[0].get('type')) > -1;
          action.setDisabled(!enabled);
        };
        this.action_editVacation = new Ext.Action({
          text: this.app.i18n._('Edit Vacation Message'),
          iconCls: 'action_email_replyAll',
          allowMultiple: false,
          requiredGrant: 'editGrant',
          disabled: true,
          actionUpdater: isSystemAccountActionUpdater,
          handler: () => {
            let account = this.grid.getSelectionModel().getSelections()[0];
            let record = new Tine.Felamimail.Model.Vacation({
              id: account.id
            }, account.id);
            let popupWindow = Tine.Felamimail.sieve.VacationEditDialog.openWindow({
              asAdminModule: true,
              account: account,
              record: record
            });
          }
        });
        this.action_editSieveRules = new Ext.Action({
          text: this.app.i18n._('Edit Filter Rules'),
          iconCls: 'action_email_forward',
          allowMultiple: false,
          requiredGrant: 'editGrant',
          disabled: true,
          actionUpdater: isSystemAccountActionUpdater,
          handler: () => {
            let account = this.grid.getSelectionModel().getSelections()[0];
            let popupWindow = Tine.Felamimail.sieve.RulesDialog.openWindow({
              asAdminModule: true,
              account: account
            });
          }
        });
        Tine.Felamimail.AccountGridPanel.prototype.initActions.call(this);
        this.actionUpdater.addActions([this.action_editVacation, this.action_editSieveRules]);
      },
      getCustomColumns: function () {
        return [{
          header: this.app.i18n._('E-mail usage'),
          id: 'emailQuota',
          hidden: this.isEmailBackend,
          dataIndex: 'email_imap_user',
          renderer: Tine.Admin.user.GridPanel.prototype.emailQuotaRenderer,
          sortable: false
        }];
      },
      getActionToolbarItems: function () {
        return [Ext.apply(new Ext.Button(this.action_editVacation), {
          scale: 'medium',
          rowspan: 2,
          iconAlign: 'top'
        }), Ext.apply(new Ext.Button(this.action_editSieveRules), {
          scale: 'medium',
          rowspan: 2,
          iconAlign: 'top'
        })];
      },
      getContextMenuItems: function () {
        return ['-', this.action_editVacation, this.action_editSieveRules];
      }
    });
  } else {
    Tine.Felamimail.admin.emailAccountsGridPanel.loadGridData.defer(100, Tine.Felamimail.admin.emailAccountsGridPanel, []);
  }
  Tine.Tinebase.MainScreen.setActiveContentPanel(Tine.Felamimail.admin.emailAccountsGridPanel, true);
  Tine.Tinebase.MainScreen.setActiveToolbar(Tine.Felamimail.admin.emailAccountsGridPanel.actionToolbar, true);
};

/***/ }),

/***/ 87879:
/***/ (function() {

/*
 * Tine 2.0
 *
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Ching En Cheng <c.cheng@metaways.de>
 * @copyright   Copyright (c) 2023 Metaways Infosystems GmbH (http://www.metaways.de)
 */
Ext.ns('Tine.Felamimail.nodeActions');

/**
 * @singleton
 */
Tine.Felamimail.nodeActionsMgr = new (Ext.extend(Tine.widgets.ActionManager, {
  actionConfigs: Tine.Felamimail.nodeActions,
  /**
   * generic action needs to set custom actionUpdater
   */
  getInitialConfig() {
    return {
      app: Tine.Tinebase.appMgr.get('Felamimail'),
      actionUpdater: Tine.Felamimail.nodeActions.actionUpdater
    };
  }
}))();
Tine.Felamimail.nodeActions.accountActions = ['AddFolderAction', 'UpdateFolderCacheAction', 'EditVacationAction', 'EditRulesAction', 'EditNotificationAction', 'EditAccountAction', 'ApproveMigrationAction', 'delete'];
Tine.Felamimail.nodeActions.folderActions = ['MarkFolderSeenAction', 'AddFolderAction', 'RefreshFolderAction', 'MoveFolderAction', 'EmptyFolderAction', 'rename', 'delete'];

/**
 * helper for disabled field
 *
 * @param action
 * @param grants
 * @param records
 * @param isFilterSelect
 * @param filteredContainers
 * @returns {*}
 */
Tine.Felamimail.nodeActions.actionUpdater = function (action, grants, records, isFilterSelect, filteredContainers) {
  // run default updater
  Tine.widgets.ActionUpdater.prototype.defaultUpdater(action, grants, records, isFilterSelect);
  action.setDisabled(false);
  const node = records[0];
  const record = new Tine.Felamimail.Model.Account(node.attributes);
  const app = action.app;
  const folder = app.getFolderStore().getById(record.id);
  const account = folder ? app.accountStore.getById(folder.get('account_id')) : app.accountStore.getById(record.id);

  // action should always have account record
  action.account = account;
  action.folder = folder;
  action.node = node;
  const accountActions = Tine.Felamimail.nodeActions.accountActions;
  const folderActions = Tine.Felamimail.nodeActions.folderActions;
  const defaultHidden = !folder ? !accountActions.includes(action.itemId) : !folderActions.includes(action.itemId);
  action.baseAction.setHidden(defaultHidden);
  if (!folder) {
    if (account.get('ns_personal') !== 'default') {
      switch (action.itemId) {
        case 'EditAccountAction':
        case 'UpdateFolderCacheAction':
          break;
        case 'AddFolderAction':
          // check account personal namespace -> disable 'add folder' if namespace is other than root
          action.setDisabled(account.get('ns_personal') !== '');
          break;
        case 'EditVacationAction':
        case 'EditRulesAction':
          // disable filter rules/vacation if no sieve hostname is set
          action.setDisabled(account.get('sieve_hostname') == null || account.get('sieve_hostname') === '');
          break;
        case 'EditNotificationAction':
          if (account && account.get('type') !== 'system' && account.get('type') !== 'shared') {
            action.baseAction.setHidden(true);
          }
          break;
        case 'ApproveMigrationAction':
          action.setDisabled(account.get('migration_approved') === 1 || account.get('type') === 'user' || account.get('type') === 'shared');
          if (!Tine.Tinebase.registry.get('manageImapEmailUser') || !Tine.Tinebase.appMgr.get('Felamimail').featureEnabled('accountMigration')) {
            action.baseAction.setHidden(true);
          }
          break;
        case 'delete':
          action.setDisabled(account.get('type') === 'system' || account.get('type') === 'shared');
          break;
      }
    }
  } else {
    const isTrashFolder = (folder.get('globalname') === account.get('trash_folder') || folder.get('localname').match(/^junk$/i)) ?? false;
    const isFolderSelectable = folder.get('is_selectable');
    const isSystemFolder = folder.get('system_folder');
    action.baseAction.setHidden(!folderActions.includes(action.itemId) && !isFolderSelectable);
    switch (action.itemId) {
      case 'MarkFolderSeenAction':
      case 'AddFolderAction':
      case 'RefreshFolderAction':
        break;
      case 'EmptyFolderAction':
        action.baseAction.setHidden(!isTrashFolder);
        break;
      case 'MoveFolderAction':
      case 'rename':
      case 'delete':
        action.baseAction.setHidden(isSystemFolder || isTrashFolder);
        break;
    }
  }
};

/**
 * Empty Folder
 */
Tine.Felamimail.nodeActions.EmptyFolderAction = {
  app: 'Felamimail',
  text: 'Empty Folder',
  itemId: 'EmptyFolderAction',
  iconCls: 'action_folder_emptytrash',
  scope: this,
  handler: async function (action) {
    const app = action.app;
    const folder = action.folder;
    const selectedNode = action.node;
    try {
      if (selectedNode) {
        selectedNode.getUI().addClass("x-tree-node-loading");
        const result = await Tine.Felamimail.emptyFolder(folder.id);
        const folderRecord = Tine.Felamimail.folderBackend.recordReader({
          responseText: result
        });
        app.getFolderStore().updateFolder(folderRecord);
        selectedNode.removeAll();
        selectedNode.getUI().removeClass("x-tree-node-loading");
      } else {
        folder.set('cache_unreadcount', 0);
      }
    } catch (e) {
      Tine.Felamimail.folderBackend.handleRequestException(e.data);
    }
  },
  actionUpdater: Tine.Felamimail.nodeActions.actionUpdater
};

// we need this for adding folders to account (root level)
Tine.Felamimail.nodeActions.AddFolderAction = {
  app: 'Felamimail',
  text: 'Add Folder',
  itemId: 'AddFolderAction',
  iconCls: 'action_add',
  scope: this,
  handler: function (action) {
    if (!action.node) return;
    const app = action.app;
    const account = action.account;
    const selectedNode = action.node;
    const treePanel = selectedNode.ownerTree;
    Ext.MessageBox.prompt(String.format(i18n._('New {0}'), app.i18n._('Folder')), String.format(i18n._('Please enter the name of the new {0}:'), app.i18n._('Folder')), async (btn, folderName) => {
      if (btn === 'ok') {
        if (!folderName) {
          return Ext.Msg.alert(String.format(i18n._('No {0} added'), app.i18n._('Folder')), String.format(i18n._('You have to supply a {0} name!'), app.i18n._('Folder')));
        }
        Ext.MessageBox.wait(i18n._('Please wait'), String.format(i18n._('Creating {0}...'), app.i18n._('Folder')));
        if (treePanel) {
          treePanel.fireEvent('containeradd', await Tine.Felamimail.addFolder(folderName, selectedNode.attributes.globalname, account.id), selectedNode);
        }
        Ext.MessageBox.hide();
      }
    }, this);
  },
  actionUpdater: Tine.Felamimail.nodeActions.actionUpdater
};
Tine.Felamimail.nodeActions.EditAccountAction = {
  app: 'Felamimail',
  text: 'Edit Account',
  itemId: 'EditAccountAction',
  iconCls: 'FelamimailIconCls',
  scope: this,
  disabled: false,
  handler: function (action) {
    if (!action.node) return;
    const app = action.app;
    const account = action.account;
    const selectedNode = action.node;
    const treePanel = selectedNode.ownerTree;
    const popupWindow = Tine.Felamimail.AccountEditDialog.openWindow({
      record: account,
      listeners: {
        'update': _.bind(function (record) {
          const account = new Tine.Felamimail.Model.Account(Ext.util.JSON.decode(record));
          // update tree node + store
          selectedNode.setText(account.get('name'));

          // reload tree node + remove all folders of this account from store ?
          app.folderStore.resetQueryAndRemoveRecords('parent_path', '/' + account.id);
          selectedNode.reload(_.bind(function (callback) {
            if (treePanel) {
              const nodeToSelect = treePanel.getNodeById(_.get(selectedNode, 'id'), '');
              if (nodeToSelect) {
                treePanel.getSelectionModel().select(nodeToSelect);
              }
            }
          }, this));
        }, this)
      }
    });
  },
  actionUpdater: Tine.Felamimail.nodeActions.actionUpdater
};
Tine.Felamimail.nodeActions.EditVacationAction = {
  app: 'Felamimail',
  text: 'Edit Vacation Message',
  itemId: 'EditVacationAction',
  iconCls: 'action_email_replyAll',
  scope: this,
  handler: function (action) {
    const popupWindow = Tine.Felamimail.sieve.VacationEditDialog.openWindow({
      account: action.account,
      record: new Tine.Felamimail.Model.Vacation({
        id: action.account.id
      }, action.account.id)
    });
  },
  actionUpdater: Tine.Felamimail.nodeActions.actionUpdater
};
Tine.Felamimail.nodeActions.EditRulesAction = {
  app: 'Felamimail',
  text: 'Edit Filter Rules',
  itemId: 'EditRulesAction',
  iconCls: 'action_email_forward',
  scope: this,
  handler: function (action) {
    const popupWindow = Tine.Felamimail.sieve.RulesDialog.openWindow({
      account: action.account
    });
  },
  actionUpdater: Tine.Felamimail.nodeActions.actionUpdater
};
Tine.Felamimail.nodeActions.EditNotificationAction = {
  app: 'Felamimail',
  text: 'Notifications',
  itemId: 'EditNotificationAction',
  iconCls: 'felamimail-action-sieve-notification',
  scope: this,
  handler: function (action) {
    const popupWindow = Tine.Felamimail.sieve.NotificationDialog.openWindow({
      record: action.account
    });
  },
  actionUpdater: Tine.Felamimail.nodeActions.actionUpdater
};
Tine.Felamimail.nodeActions.MarkFolderSeenAction = {
  app: 'Felamimail',
  text: 'Mark Folder as read',
  itemId: 'MarkFolderSeenAction',
  iconCls: 'action_mark_read',
  scope: this,
  handler: function (action) {
    if (!action.node) return;
    const app = action.app;
    const folder = action.folder;
    const selectedNode = action.node;
    const filter = [{
      field: 'folder_id',
      operator: 'equals',
      value: folder.id
    }, {
      field: 'flags',
      operator: 'notin',
      value: ['\\Seen']
    }];
    Tine.Felamimail.messageBackend.addFlags(filter, '\\Seen', {
      callback: function () {
        folder.set('cache_unreadcount', 0);
        if (selectedNode) {
          app.getMainScreen().getCenterPanel().loadGridData({
            removeStrategy: 'keepBuffered'
          });
        }
      }
    });
  },
  actionUpdater: Tine.Felamimail.nodeActions.actionUpdater
};
Tine.Felamimail.nodeActions.UpdateFolderCacheAction = {
  app: 'Felamimail',
  text: 'Update Folder List',
  itemId: 'UpdateFolderCacheAction',
  iconCls: 'action_update_cache',
  scope: this,
  handler: async function (action) {
    if (!action.node) return;
    const app = action.app;
    const account = action.account;
    const folder = action.folder;
    const selectedNode = action.node;
    const treePanel = selectedNode.ownerTree;
    try {
      treePanel.getSelectionModel().clearSelections();
      selectedNode.getUI().addClass("x-tree-node-loading");
      // call update folder cache
      const folderName = folder ? folder.get('globalname') : '';
      await Tine.Felamimail.updateFolderCache(account.id, folderName);

      // clear query to query server again and reload subfolders
      app.folderStore.resetQueryAndRemoveRecords('parent_path', (folder ? folder.get('path') : '/') + account.id);
      selectedNode.reload(function (callback) {
        treePanel.selectInbox(account);
      }, this);
    } catch (e) {
      Tine.Felamimail.folderBackend.handleRequestException(e.data);
    } finally {
      selectedNode.getUI().removeClass("x-tree-node-loading");
    }
  },
  actionUpdater: Tine.Felamimail.nodeActions.actionUpdater
};
Tine.Felamimail.nodeActions.ApproveMigrationAction = {
  app: 'Felamimail',
  text: 'Approve Migration',
  itemId: 'ApproveMigrationAction',
  iconCls: 'action_approve_migration',
  scope: this,
  qtip: 'Agree that this account can be transferred to other users',
  handler: async function (action) {
    if (!action.node) return;
    const app = action.app;
    const account = action.account;
    const selectedNode = action.node;
    if ((await Ext.MessageBox.show({
      title: app.i18n._('Approve Migration'),
      msg: app.i18n._('I hereby agree that this mailbox may be converted to a shared mailbox or transferred to another user.'),
      buttons: Ext.MessageBox.YESNO,
      icon: Ext.MessageBox.QUESTION
    })) === 'yes') {
      try {
        selectedNode.getUI().addClass("x-tree-node-loading");
        await Tine.Felamimail.approveAccountMigration(account.id);
        account.set('migration_approved', 1);
      } catch (e) {
        Tine.Felamimail.folderBackend.handleRequestException(e.data);
      } finally {
        selectedNode.getUI().removeClass("x-tree-node-loading");
      }
    }
  },
  actionUpdater: Tine.Felamimail.nodeActions.actionUpdater
};
Tine.Felamimail.nodeActions.MoveFolderAction = {
  app: 'Felamimail',
  text: 'Move Folder',
  itemId: 'MoveFolderAction',
  iconCls: 'action_move',
  scope: this,
  handler: function (action) {
    if (!action.node) return;
    const app = action.app;
    const account = action.account;
    const folder = action.folder;
    const selectedNode = action.node;
    const treePanel = selectedNode.ownerTree;
    treePanel.getSelectionModel().clearSelections();
    const selectPanel = Tine.Felamimail.FolderSelectPanel.openWindow({
      account: account,
      listeners: {
        scope: this,
        async folderselect(newParentNode) {
          selectPanel.close();
          newParentNode = treePanel.getNodeById(newParentNode.id); // switch context
          const parentGlobalname = newParentNode.attributes.globalname;
          if (parentGlobalname.replace(new RegExp(`^${folder.get('globalname').replace('.', '\.')}`), '') !== parentGlobalname) {
            return Ext.Msg.alert(app.i18n._('Invalid Selection'), app.i18n._('You cannot move the folder to an own sub folder!'));
          }
          const newGlobalName = _.compact([parentGlobalname, folder.get('localname')]).join(account.get('delimiter'));
          try {
            var _folderStore$getById;
            selectedNode.getUI().addClass("x-tree-node-loading");
            newParentNode.getUI().addClass("x-tree-node-loading");
            const result = await Tine.Felamimail.moveFolder(newGlobalName, folder.get('globalname'), account.id);
            const folderStore = newParentNode.ownerTree.folderStore;
            newParentNode.appendChild(newParentNode.ownerTree.loader.createNode(result));
            selectedNode.remove();
            folderStore.remove(folderStore.getById(selectedNode.id));
            const newRecord = Tine.Felamimail.folderBackend.recordReader({
              responseText: result
            });
            (_folderStore$getById = folderStore.getById(newParentNode.id)) === null || _folderStore$getById === void 0 ? void 0 : _folderStore$getById.set('has_children', true);
            folderStore.add([newRecord]);
            treePanel.initNewFolderNode(newRecord);
            newParentNode.expand(false, false, () => {
              treePanel.getNodeById(selectedNode.id).select();
            });
          } catch (e) {
            Tine.Felamimail.folderBackend.handleRequestException(e.data);
          } finally {
            selectedNode.getUI().removeClass("x-tree-node-loading");
            newParentNode.getUI().removeClass("x-tree-node-loading");
          }
        }
      }
    });
  },
  actionUpdater: Tine.Felamimail.nodeActions.actionUpdater
};
Tine.Felamimail.nodeActions.RefreshFolderAction = {
  app: 'Felamimail',
  text: 'Update Folder',
  itemId: 'RefreshFolderAction',
  iconCls: 'action_update_cache',
  scope: this,
  handler: async function (action) {
    if (!action.node) return;
    const app = action.app;
    const folder = action.folder;
    const selectedNode = action.node;
    try {
      selectedNode.getUI().addClass("x-tree-node-loading");
      await Tine.Felamimail.refreshFolder(folder.get('id'));
      Ext.Msg.show({
        title: app.i18n._('Update Folder'),
        msg: app.i18n._('Folder cache has been cleared!'),
        icon: Ext.MessageBox.INFO,
        buttons: Ext.Msg.OK,
        scope: this
      });
    } catch (e) {
      Tine.Felamimail.folderBackend.handleRequestException(e.data);
    } finally {
      selectedNode.getUI().removeClass("x-tree-node-loading");
    }
  },
  actionUpdater: Tine.Felamimail.nodeActions.actionUpdater
};

/***/ }),

/***/ 12600:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010-2017 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.namespace('Tine.Felamimail.sieve');
Tine.Felamimail.sieve.NotificationDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
  /**
   * @private
   */
  windowNamePrefix: 'NotificationWindow_',
  appName: 'Felamimail',
  loadRecord: true,
  tbarItems: [],
  evalGrants: false,
  readonlyReason: false,
  initComponent: function () {
    this.recordClass = Tine.Felamimail.Model.Account;
    this.recordProxy = Tine.Felamimail.accountBackend;
    Tine.Felamimail.sieve.NotificationDialog.superclass.initComponent.call(this);
  },
  /**
   * overwrite update toolbars function (we don't have record grants yet)
   * 
   * @private
   */
  updateToolbars: function () {},
  /**
   * executed after record got updated from proxy
   * 
   * @private
   */
  onRecordLoad: function () {
    // interrupt process flow till dialog is rendered
    if (!this.rendered) {
      this.onRecordLoad.defer(250, this);
      return;
    }
    this.getForm().loadRecord(this.record);

    // load sieve notification emails
    const data = this.record.data.sieve_notification_email.split(',');
    this.sieveNotifyGrid.setStoreFromArray(data.map(e => {
      return {
        'email': e
      };
    }));
    var title = String.format(this.app.i18n._('Notification for {0}'), this.record.get('name'));
    this.window.setTitle(title);
    this.loadMask.hide();
  },
  onRecordUpdate: function () {
    Tine.Felamimail.sieve.NotificationDialog.superclass.onRecordUpdate.call(this);
    // update sieve notification emails
    const notifyEmails = this.sieveNotifyGrid.getFromStoreAsArray();
    this.record.set('sieve_notification_email', notifyEmails.filter(e => e === null || e === void 0 ? void 0 : e.email).map(e => e === null || e === void 0 ? void 0 : e.email).join());
  },
  /**
   * returns dialog
   * 
   * NOTE: when this method gets called, all initalisation is done.
   * 
   * @return {Object}
   * @private
   * 
   */
  getFormItems: function () {
    return {
      xtype: 'tabpanel',
      deferredRender: false,
      border: false,
      activeTab: 0,
      items: [{
        title: this.app.i18n._('Notifications'),
        autoScroll: true,
        border: false,
        frame: true,
        xtype: 'columnform',
        formDefaults: {
          xtype: 'textfield',
          anchor: '100%',
          labelSeparator: '',
          columnWidth: 1
        },
        items: [[this.initSieveEmailNotifyGrid()]]
      }]
    };
  },
  initSieveEmailNotifyGrid: function (additionConfig) {
    const app = Tine.Tinebase.appMgr.get('Felamimail');
    const config = _.assign({
      autoExpandColumn: 'email',
      quickaddMandatory: 'email',
      frame: false,
      useBBar: true,
      height: 200,
      columnWidth: 1,
      recordClass: Ext.data.Record.create([{
        name: 'email'
      }])
    }, additionConfig);
    this.sieveNotifyGrid = new Tine.widgets.grid.QuickaddGridPanel(Ext.apply({
      cm: new Ext.grid.ColumnModel([{
        id: 'email',
        header: app.i18n._('Notification Email'),
        dataIndex: 'email',
        hideable: false,
        sortable: true,
        quickaddField: new Ext.form.TextField({
          emptyText: app.i18n.gettext('Add a new email ...'),
          vtype: 'email'
        }),
        editor: new Ext.form.TextField({
          allowBlank: false
        })
      }])
    }, config));
    return this.sieveNotifyGrid;
  }
});

/**
 * Felamimail Edit Popup
 * 
 * @param   {Object} config
 * @return  {Ext.ux.Window}
 */
Tine.Felamimail.sieve.NotificationDialog.openWindow = function (config) {
  var window = Tine.WindowFactory.getWindow({
    width: 320,
    height: 200,
    name: Tine.Felamimail.sieve.NotificationDialog.prototype.windowNamePrefix + Ext.id(),
    contentPanelConstructor: 'Tine.Felamimail.sieve.NotificationDialog',
    contentPanelConstructorConfig: config
  });
  return window;
};

/***/ }),

/***/ 71182:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.namespace('Tine.Felamimail.sieve');

/**
 * @namespace   Tine.Felamimail.sieve
 * @class       Tine.Felamimail.sieve.RuleConditionsPanel
 * @extends     Tine.widgets.grid.FilterToolbar
 * 
 * <p>Sieve Filter Conditions Panel</p>
 * <p>
 * mapping when getting filter values:
 *  field       -> test_header or 'size'
 *  operator    -> comperator
 *  value       -> key
 * </p>
 * <p>
 * </p>
 * 
 * @param       {Object} config
 * @constructor
 * Create a new RuleConditionsPanel
 */
Tine.Felamimail.sieve.RuleConditionsPanel = Ext.extend(Tine.widgets.grid.FilterToolbar, {
  defaultFilter: 'from',
  neverAllowSaving: true,
  showSearchButton: false,
  customFilterSorting: true,
  // unused fn
  onFiltertrigger: Ext.emptyFn,
  initComponent: function () {
    this.app = Tine.Tinebase.appMgr.get('Felamimail');
    this.rowPrefix = '';
    this.filterModels = Tine.Felamimail.sieve.RuleConditionsPanel.getFilterModel(this.app);
    this.supr().initComponent.call(this);
  },
  /**
   * gets filter data (use getValue() if we don't have a store/plugins)
   * 
   * @return {Array} of filter records
   */
  getAllFilterData: function () {
    return this.getValue();
  }
});

/**
 * get rule conditions for filter model and condition renderer
 * 
 * @param {} app
 * @return {Array}
 */
Tine.Felamimail.sieve.RuleConditionsPanel.getFilterModel = function (app) {
  if (!app) {
    app = Tine.Tinebase.appMgr.get('Felamimail');
  }
  return [{
    sort: 10,
    label: app.i18n._('From (Email)'),
    field: 'from',
    operators: ['contains', 'regex'],
    emptyText: 'test@example.org'
  }, {
    sort: 20,
    label: app.i18n._('From (Email and Name)'),
    field: 'fromheader',
    operators: ['contains', 'regex'],
    emptyText: 'name or email'
  }, {
    sort: 30,
    label: app.i18n._('To (Email)'),
    field: 'to',
    operators: ['contains', 'regex'],
    emptyText: 'test@example.org'
  }, {
    sort: 40,
    label: app.i18n._('To (Email CC)'),
    field: 'cc',
    operators: ['contains', 'regex'],
    emptyText: 'test@example.org'
  }, {
    sort: 50,
    label: app.i18n._('Subject'),
    field: 'subject',
    operators: ['contains', 'regex'],
    emptyText: app.i18n._('Subject')
  }, {
    sort: 60,
    label: app.i18n._('Size'),
    field: 'size',
    operators: ['greater', 'less'],
    valueType: 'number',
    defaultOperator: 'greater'
  }, {
    sort: 70,
    label: app.i18n._('Header contains'),
    field: 'header',
    operators: ['freeform'],
    defaultOperator: 'freeform',
    emptyTextOperator: app.i18n._('Header name'),
    emptyText: app.i18n._('Header value')
  }, {
    sort: 80,
    label: app.i18n._('Header regex'),
    field: 'headerregex',
    operators: ['freeform'],
    defaultOperator: 'freeform',
    emptyTextOperator: app.i18n._('Header name'),
    emptyText: app.i18n._('Header value')
  }];
};

/***/ }),

/***/ 63312:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.namespace('Tine.Felamimail.sieve');

/**
 * @namespace   Tine.Felamimail.sieve
 * @class       Tine.Felamimail.sieve.RuleEditDialog
 * @extends     Tine.widgets.dialog.EditDialog
 * 
 * <p>Sieve Filter Dialog</p>
 * <p>This dialog is editing a filter rule.</p>
 * <p>
 * </p>
 * 
 * @author      Philipp Schuele <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010 Metaways Infosystems GmbH (http://www.metaways.de)
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * 
 * @param       {Object} config
 * @constructor
 * Create a new RuleEditDialog
 */
Tine.Felamimail.sieve.RuleEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
  /**
   * @cfg {Tine.Felamimail.Model.Account}
   */
  account: null,
  /**
   * @private
   */
  windowNamePrefix: 'RuleEditWindow_',
  appName: 'Felamimail',
  recordClass: Tine.Felamimail.Model.Rule,
  mode: 'local',
  loadRecord: true,
  tbarItems: [],
  evalGrants: false,
  conjunctionCombo: null,
  /**
   * overwrite update toolbars function (we don't have record grants yet)
   * 
   * @private
   */
  updateToolbars: function () {},
  /**
   * @private
   */
  onRender: function (ct, position) {
    Tine.Felamimail.sieve.RuleEditDialog.superclass.onRender.call(this, ct, position);
    this.onChangeType.defer(250, this);
  },
  /**
   * Change type card layout depending on selected combo box entry and set field value
   */
  onChangeType: function () {
    var type = this.actionTypeCombo.getValue();
    var cardLayout = Ext.getCmp(this.idPrefix + 'CardLayout').getLayout();
    if (cardLayout !== 'card') {
      cardLayout.setActiveItem(this.idPrefix + type);
      if (this.record.get('action_type') == type) {
        var field = this.getForm().findField('action_argument_' + type);
        if (field !== null) {
          switch (type) {
            case 'redirect':
              var data = this.record.get('action_argument'),
                checkbox = this.getForm().findField('action_argument_redirect_copy');
              field.setValue(data.emails);
              checkbox.setValue(data.copy);
              break;
            default:
              field.setValue(this.record.get('action_argument'));
          }
        }
      }
    }
  },
  /**
   * executed after record got updated from proxy
   * 
   * @private
   */
  onRecordLoad: function () {
    // interrupt process flow till dialog is rendered
    if (!this.rendered) {
      this.onRecordLoad.defer(250, this);
      return;
    }
    var title = this.app.i18n._('Edit Filter Rule');
    this.window.setTitle(title);
    this.getForm().loadRecord(this.record);
    this.conditionsPanel.conjunctionOperator = this.record.get('conjunction') == 'anyof' ? 'or' : 'and';
    this.hideLoadMask();
  },
  /**
   * @private
   */
  onRecordUpdate: function () {
    Tine.Felamimail.sieve.RuleEditDialog.superclass.onRecordUpdate.call(this);
    this.record.set('conditions', this.getConditions());
    var argumentFieldName = 'action_argument_' + this.actionTypeCombo.getValue(),
      argumentField = this.getForm().findField(argumentFieldName),
      argumentValue = argumentField !== null ? argumentField.getValue() : '';

    // add additional action arguments
    if (argumentFieldName === 'action_argument_redirect') {
      argumentValue = {
        emails: argumentValue.trim(),
        copy: this.getForm().findField('action_argument_redirect_copy').getValue()
      };
    }
    this.record.set('action_argument', argumentValue);
  },
  /**
   * get conditions and do the mapping
   * 
   * @return {Array}
   */
  getConditions: function () {
    var conditions = this.conditionsPanel.getAllFilterData();
    var result = [],
      i = 0,
      condition,
      test,
      comperator,
      header;
    for (i = 0; i < conditions.length; i++) {
      // set defaults
      comperator = conditions[i].operator;
      header = conditions[i].field;
      test = 'header';
      switch (conditions[i].field) {
        case 'from':
        case 'to':
          test = 'address';
          break;
        case 'fromheader':
          header = 'From';
          break;
        case 'size':
          test = 'size';
          comperator = conditions[i].operator == 'greater' ? 'over' : 'under';
          break;
        case 'header':
          header = conditions[i].operator;
          comperator = 'contains';
          break;
        case 'headerregex':
          header = conditions[i].operator;
          comperator = 'regex';
          break;
      }
      condition = {
        test: test,
        header: header,
        comperator: comperator,
        key: conditions[i].value
      };
      result.push(condition);
    }
    return result;
  },
  /**
   * get conditions filter data (reverse of getConditions)
   * 
   * @return {Array}
   */
  getConditionsFilter: function () {
    var conditions = this.record.get('conditions');
    var result = [],
      i = 0,
      filter,
      operator,
      field;
    for (i = 0; i < conditions.length; i++) {
      field = conditions[i].header;
      switch (field) {
        case 'size':
          operator = conditions[i].comperator == 'over' ? 'greater' : 'less';
          break;
        case 'from':
        case 'to':
        case 'subject':
          operator = conditions[i].comperator;
          break;
        case 'cc':
          field = 'cc';
          break;
        default:
          if (field == 'From') {
            operator = conditions[i].comperator;
            field = 'fromheader';
          } else {
            operator = field;
            field = conditions[i].comperator == 'contains' ? 'header' : 'headerregex';
          }
      }
      filter = {
        field: field,
        operator: operator,
        value: conditions[i].key
      };
      result.push(filter);
    }
    return result;
  },
  /**
   * sets the conjunction operator of the filtertoolbar to show the correct text for anyof or allof
   */
  onConjunctionChange: function () {
    this.conditionsPanel.conjunctionOperator = this.conjunctionCombo.getValue() == 'anyof' ? 'or' : 'and';
    this.conditionsPanel.onFilterRowsChange();
  },
  /**
   * returns dialog
   * 
   * NOTE: when this method gets called, all initialization is done.
   * 
   * @return {Object}
   * @private
   */
  getFormItems: function () {
    this.conditionsPanel = new Tine.Felamimail.sieve.RuleConditionsPanel({
      filters: this.getConditionsFilter()
    });
    this.actionTypeCombo = new Ext.form.ComboBox({
      hideLabel: true,
      name: 'action_type',
      typeAhead: false,
      triggerAction: 'all',
      lazyRender: true,
      editable: false,
      mode: 'local',
      forceSelection: true,
      value: 'discard',
      columnWidth: 0.4,
      store: Tine.Felamimail.sieve.RuleEditDialog.getActionTypes(this.app),
      listeners: {
        scope: this,
        change: this.onChangeType,
        select: this.onChangeType
      }
    });
    this.conjunctionCombo = new Ext.form.ComboBox({
      hideLabel: false,
      width: 250,
      fieldLabel: '<span class="felamimail-sieverule-conjunction">' + this.app.i18n._('Apply filter if') + '</span>',
      labelSeparator: '',
      name: 'conjunction',
      store: [['allof', this.app.i18n._('all conditions match')], ['anyof', this.app.i18n._('any condition matches')]],
      value: 'allof',
      listeners: {
        scope: this,
        change: this.onConjunctionChange,
        select: this.onConjunctionChange
      }
    });
    this.idPrefix = Ext.id();
    return [{
      xtype: 'panel',
      layout: 'border',
      autoScroll: true,
      items: [{
        region: 'north',
        border: false,
        xtype: 'panel',
        autoScroll: true,
        height: 200,
        items: [{
          layout: 'form',
          labelWidth: 200,
          frame: true,
          region: 'center',
          autoScroll: true,
          border: false,
          items: [this.conjunctionCombo]
        }, this.conditionsPanel],
        listeners: {
          scope: this,
          afterlayout: function (ct, layout) {
            ct.suspendEvents();
            if (this.conditionsPanel.getHeight() < 170) {
              ct.setHeight(this.conditionsPanel.getHeight() + 35);
            }
            ct.ownerCt.layout.layout();
            ct.resumeEvents();
          }
        }
      }, {
        title: this.app.i18n._('Do this action:'),
        region: 'center',
        border: false,
        frame: true,
        xtype: 'panel',
        layout: 'column',
        items: [this.actionTypeCombo,
        // TODO try to add a spacer/margin between the two input fields
        /*{
            // spacer
            columnWidth: 0.1,
            layout: 'fit',
            title: '',
            items: []
        }, */
        {
          id: this.idPrefix + 'CardLayout',
          layout: 'card',
          activeItem: this.idPrefix + 'fileinto',
          border: false,
          columnWidth: 0.5,
          xtype: 'panel',
          defaults: {
            border: false
          },
          items: [{
            id: this.idPrefix + 'fileinto',
            layout: 'form',
            items: [{
              name: 'action_argument_fileinto',
              xtype: 'felamimailfolderselect',
              width: 200,
              hideLabel: true,
              account: this.account
            }]
          }, {
            // TODO add email validator?
            id: this.idPrefix + 'redirect',
            layout: 'form',
            items: [{
              name: 'action_argument_redirect',
              xtype: 'textfield',
              emptyText: 'test@example.org',
              width: 200,
              maxLength: 255,
              hideLabel: true
            }, {
              name: 'action_argument_redirect_copy',
              xtype: 'checkbox',
              fieldLabel: this.app.i18n._('Keep a copy')
            }]
          }, {
            id: this.idPrefix + 'reject',
            layout: 'form',
            items: [{
              name: 'action_argument_reject',
              xtype: 'textarea',
              width: 300,
              maxLength: 255,
              height: 60,
              hideLabel: true
            }]
          }, {
            id: this.idPrefix + 'vacation',
            layout: 'form',
            items: [{
              name: 'action_argument_vacation',
              xtype: 'textarea',
              width: 300,
              height: 60,
              hideLabel: true
            }]
          }, {
            id: this.idPrefix + 'discard',
            layout: 'fit',
            items: []
          }]
        }]
      }]
    }];
  }
});

/**
 * Felamimail Edit Popup
 * 
 * @param   {Object} config
 * @return  {Ext.ux.Window}
 */
Tine.Felamimail.sieve.RuleEditDialog.openWindow = function (config) {
  var window = Tine.WindowFactory.getWindow({
    width: 700,
    height: 300,
    name: Tine.Felamimail.sieve.RuleEditDialog.prototype.windowNamePrefix + Ext.id(),
    contentPanelConstructor: 'Tine.Felamimail.sieve.RuleEditDialog',
    contentPanelConstructorConfig: config
  });
  return window;
};

/**
 * get action types for action combo and action type renderer
 * 
 * @param {} app
 * @return {Array}
 */
Tine.Felamimail.sieve.RuleEditDialog.getActionTypes = function (app) {
  if (!app) {
    app = Tine.Tinebase.appMgr.get('Felamimail');
  }
  return [['fileinto', app.i18n._('Move mail to folder')], ['redirect', app.i18n._('Redirect mail to address')], ['reject', app.i18n._('Reject mail with this text')], ['discard', app.i18n._('Discard mail')], ['vacation', app.i18n._('Auto-Reply mail with this text')]
  //['keep',        app.i18n._('Keep mail')],
  ];
};

/***/ }),

/***/ 68845:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.namespace('Tine.Felamimail.sieve');

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.sieve.RulesDialog
 * @extends     Tine.widgets.dialog.EditDialog
 * 
 * <p>Sieve Filter Dialog</p>
 * <p>This dialog is for editing sieve filters (rules).</p>
 * <p>
 * </p>
 * 
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * 
 * @param       {Object} config
 * @constructor
 * Create a new RulesDialog
 */
Tine.Felamimail.sieve.RulesDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
  /**
   * @cfg {Tine.Felamimail.Model.Account}
   */
  account: null,
  /**
   * @private
   */
  windowNamePrefix: 'RulesWindow_',
  appName: 'Felamimail',
  //    loadRecord: false,
  mode: 'local',
  tbarItems: [],
  evalGrants: false,
  //private
  initComponent: function () {
    this.recordProxy = this.asAdminModule ? new Tine.Felamimail.RulesBackend({
      appName: 'Admin',
      modelName: 'SieveRule'
    }) : Tine.Felamimail.rulesBackend;
    Tine.Felamimail.sieve.RulesDialog.superclass.initComponent.call(this);
    this.i18nRecordName = this.app.i18n._('Sieve Filter Rules');
  },
  /**
   * returns canonical path part
   * @returns {string}
   */
  getCanonicalPathSegment: function () {
    return ['', this.appName, 'EditDialog'].join(Tine.Tinebase.CanonicalPath.separator);
  },
  /**
   * overwrite update toolbars function (we don't have record grants yet)
   * 
   * @private
   */
  updateToolbars: Ext.emptyFn,
  /**
   * init record to edit
   * -> we don't have a real record here
   */
  initRecord: function () {
    this.onRecordLoad();
  },
  /**
   * executed after record got updated from proxy
   * -> we don't have a real record here
   * 
   * @private
   */
  onRecordLoad: function () {
    // interrupt process flow till dialog is rendered
    if (!this.rendered) {
      this.onRecordLoad.defer(250, this);
      return;
    }
    var accountName = this.account ? this.account.get('name') : 'unknown',
      title = String.format(this.app.i18n._('Sieve Filter Rules for {0}'), accountName);
    this.window.setTitle(title);
    const hasRight = Tine.Felamimail.AccountEditDialog.prototype.checkAccountEditRight(this.account);
    this.action_saveAndClose.setDisabled(!hasRight);
    this.hideLoadMask();
  },
  /**
   * returns dialog
   * 
   * NOTE: when this method gets called, all initalisation is done.
   * 
   * @return {Object}
   * @private
   * 
   */
  getFormItems: function () {
    this.rulesGrid = new Tine.Felamimail.sieve.RulesGridPanel({
      account: this.account,
      recordProxy: this.recordProxy
    });
    return [this.rulesGrid];
  },
  /**
   * apply changes handler (get rules and send them to saveRules)
   */
  onApplyChanges: function (closeWindow) {
    var rules = [];
    this.rulesGrid.store.each(function (record) {
      rules.push(record.data);
    });
    this.loadMask.show();
    this.recordProxy.saveRules(this.account.id, rules, {
      scope: this,
      success: function (record) {
        if (closeWindow) {
          this.purgeListeners();
          this.window.close();
        }
      },
      failure: Tine.Felamimail.handleRequestException.createSequence(function () {
        this.hideLoadMask();
      }, this),
      timeout: 150000 // 3 minutes
    });
  }
});

/**
 * Felamimail Edit Popup
 * 
 * @param   {Object} config
 * @return  {Ext.ux.Window}
 */
Tine.Felamimail.sieve.RulesDialog.openWindow = function (config) {
  var window = Tine.WindowFactory.getWindow({
    width: 800,
    height: 400,
    name: Tine.Felamimail.sieve.RulesDialog.prototype.windowNamePrefix + Ext.id(),
    contentPanelConstructor: 'Tine.Felamimail.sieve.RulesDialog',
    contentPanelConstructorConfig: config
  });
  return window;
};

/***/ }),

/***/ 80316:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.ns('Tine.Felamimail.sieve');

/**
 * @namespace Tine.Felamimail
 * @class     Tine.Felamimail.sieve.RulesGridPanel
 * @extends   Tine.widgets.grid.GridPanel
 * Rules Grid Panel <br>
 * 
 * @author      Philipp Schuele <p.schuele@metaways.de>
 */
Tine.Felamimail.sieve.RulesGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
  /**
   * @cfg {Tine.Felamimail.Model.Account}
   */
  account: null,
  // model generics
  recordClass: Tine.Felamimail.Model.Rule,
  recordProxy: Tine.Felamimail.rulesBackend,
  // grid specific
  defaultSortInfo: {
    field: 'id',
    dir: 'ASC'
  },
  storeRemoteSort: false,
  // not yet
  evalGrants: false,
  usePagingToolbar: false,
  splitAddButton: false,
  // disable filterToolBar
  editDialog: true,
  // disable this check as records are only deleted locally
  disableDeleteActionCheckServiceMap: Ext.emptyFn,
  newRecordIcon: 'action_new_rule',
  editDialogClass: Tine.Felamimail.sieve.RuleEditDialog,
  stateful: false,
  initComponent: function () {
    this.app = Tine.Tinebase.appMgr.get('Felamimail');
    this.initColumns();
    this.editDialogConfig = {
      account: this.account
    };
    this.supr().initComponent.call(this);
  },
  /**
   * Return CSS class to apply to rows depending on enabled status
   * 
   * @param {Tine.Felamimail.Model.Rule} record
   * @param {Integer} index
   * @return {String}
   */
  getViewRowClass: function (record, index) {
    var className = '';
    if (!record.get('enabled')) {
      className += ' felamimail-sieverule-disabled';
    }
    return className;
  },
  /**
   * init actions with actionToolbar, contextMenu and actionUpdater
   * 
   * @private
   */
  initActions: function () {
    this.action_moveup = new Ext.Action({
      text: this.app.i18n._('Move up'),
      handler: this.onMoveRecord.createDelegate(this, ['up']),
      scope: this,
      iconCls: 'action_move_up'
    });
    this.action_movedown = new Ext.Action({
      text: this.app.i18n._('Move down'),
      handler: this.onMoveRecord.createDelegate(this, ['down']),
      scope: this,
      iconCls: 'action_move_down'
    });
    this.action_enable = new Ext.Action({
      text: this.app.i18n._('Enable'),
      handler: this.onEnableDisable.createDelegate(this, [true]),
      scope: this,
      iconCls: 'action_enable'
    });
    this.action_disable = new Ext.Action({
      text: this.app.i18n._('Disable'),
      handler: this.onEnableDisable.createDelegate(this, [false]),
      scope: this,
      iconCls: 'action_disable'
    });
    this.supr().initActions.call(this);
  },
  /**
   * enable / disable rule
   * 
   * @param {Boolean} state
   */
  onEnableDisable: function (state) {
    var selectedRows = this.grid.getSelectionModel().getSelections();
    for (var i = 0; i < selectedRows.length; i++) {
      selectedRows[i].set('enabled', state);
    }
  },
  /**
   * move record up or down
   * 
   * @param {String} dir (up|down)
   */
  onMoveRecord: function (dir) {
    var sm = this.grid.getSelectionModel();
    if (sm.getCount() == 1) {
      var selectedRows = sm.getSelections(),
        record = selectedRows[0],
        allRecords = this.store.data.items,
        index = this.store.indexOf(record),
        switchRecordIndex = dir == 'down' ? index + 1 : index - 1,
        switchRecord = this.store.getAt(switchRecordIndex);
      if (switchRecord) {
        var oldId = record.id,
          switchId = switchRecord.id;
        record.set('id', Ext.id());
        record.id = Ext.id();
        switchRecord.set('id', oldId);
        switchRecord.id = oldId;
        record.set('id', switchId);
        record.id = switchId;

        // NOTE: we need to reload store as selection model is confused otherwise
        this.store.loadRecords({
          records: allRecords
        }, {
          add: false
        }, true);
        this.store.sort('id', 'ASC');
        sm.selectRecords([record]);
      }
    }
  },
  /**
   * add custom items to action toolbar
   * 
   * @return {Object}
   */
  getActionToolbarItems: function () {
    return [{
      xtype: 'buttongroup',
      columns: 1,
      frame: false,
      items: [this.action_moveup, this.action_movedown]
    }];
  },
  /**
   * add custom items to context menu
   * 
   * @return {Array}
   */
  getContextMenuItems: function () {
    var items = ['-', this.action_moveup, this.action_movedown, '-', this.action_enable, this.action_disable];
    return items;
  },
  /**
   * init columns
   */
  initColumns: function () {
    this.gridConfig.columns = [{
      id: 'id',
      header: this.app.i18n._("ID"),
      width: 40,
      sortable: false,
      dataIndex: 'id',
      hidden: true
    }, {
      id: 'conditions',
      header: this.app.i18n._("Conditions"),
      width: 200,
      sortable: false,
      dataIndex: 'conditions',
      scope: this,
      renderer: this.conditionsRenderer
    }, {
      id: 'action',
      header: this.app.i18n._("Action"),
      width: 250,
      sortable: false,
      dataIndex: 'action_type',
      scope: this,
      renderer: this.actionRenderer
    }];
  },
  /**
   * init layout
   */
  initLayout: function () {
    this.supr().initLayout.call(this);
    this.items.push({
      region: 'north',
      height: 55,
      layout: 'card',
      activeItem: 0,
      border: false,
      items: this.actionToolbar
    });
  },
  /**
   * preform the initial load of grid data
   */
  initialLoad: function () {
    this.store.load.defer(10, this.store, [typeof this.autoLoad == 'object' ? this.autoLoad : undefined]);
  },
  /**
   * called before store queries for data
   */
  onStoreBeforeload: function (store, options) {
    Tine.Felamimail.sieve.RulesGridPanel.superclass.onStoreBeforeload.call(this, store, options);
    options.params.filter = this.account ? this.account.id : '';
  },
  /**
   * action renderer
   * 
   * @param {Object} type
   * @param {Object} metadata
   * @param {Object} record
   * @return {String}
   */
  actionRenderer: function (type, metadata, record) {
    var types = Tine.Felamimail.sieve.RuleEditDialog.getActionTypes(this.app),
      result = type,
      action_argument = record.get('action_argument');
    for (var i = 0; i < types.length; i++) {
      if (types[i][0] == type) {
        result = types[i][1];
      }
    }
    if (action_argument && action_argument != '') {
      result += ': ';
      if (Ext.isObject(action_argument)) {
        if (action_argument.copy) {
          result += action_argument.emails + ' (' + this.app.i18n._('Keep a copy') + ')';
        } else {
          result += action_argument.emails + '';
        }
      } else {
        result += action_argument;
      }
    }
    return Ext.util.Format.htmlEncode(result);
  },
  /**
   * conditions renderer
   * 
   * @param {Object} value
   * @return {String}
   * 
   * TODO show more conditions?
   */
  conditionsRenderer: function (value) {
    var result = '';
    if (!this.app) {
      this.app = Tine.Tinebase.appMgr.get('Felamimail');
    }

    // show only first condition
    if (value && value.length > 0) {
      var condition = value[0];

      // get header/comperator translation
      var filterModels = Tine.Felamimail.sieve.RuleConditionsPanel.getFilterModel(this.app),
        header,
        comperator,
        found = false;
      Ext.each(filterModels, function (filterModel) {
        if (condition.header == filterModel.field) {
          header = filterModel.label;
          if (condition.header == 'size') {
            comperator = condition.comperator == 'over' ? i18n._('is greater than') : i18n._('is less than');
          } else {
            comperator = i18n._(condition.comperator);
          }
          found = true;
        }
      }, this);
      if (found === true) {
        result = header + ' ' + comperator + ' "' + condition.key + '"';
      } else {
        result = condition.comperator == 'contains' ? this.app.i18n._('Header "{0}" contains "{1}"') : this.app.i18n._('Header "{0}" matches "{1}"');
        result = String.format(result, condition.header, condition.key);
      }
    }
    return Ext.util.Format.htmlEncode(result);
  },
  /**
   * on update after edit
   * 
   * @param {String} encodedRecordData (json encoded)
   */
  onUpdateRecord: function (encodedRecordData) {
    var newRecord = Tine.Felamimail.rulesBackend.recordReader({
      responseText: encodedRecordData
    });
    if (!newRecord.id) {
      var lastRecord = null,
        nextId = null;
      do {
        // get next free id
        lastRecord = this.store.getAt(this.store.getCount() - 1);
        nextId = lastRecord ? (parseInt(lastRecord.id, 10) + 1).toString() : '1';
      } while (this.store.getById(newRecord.id));
      newRecord.set('id', nextId);
      newRecord.id = nextId;
    } else {
      // need to set data.id as well to make sure we don't get any duplicates here
      newRecord.set('id', newRecord.id);
      this.store.remove(this.store.getById(newRecord.id));
    }
    this.store.addSorted(newRecord);

    // some eyecandy
    var row = this.getView().getRow(this.store.indexOf(newRecord));
    Ext.fly(row).highlight();
  },
  /**
   * generic delete handler
   */
  onDeleteRecords: function (btn, e) {
    var sm = this.grid.getSelectionModel();
    var records = sm.getSelections();
    Ext.each(records, function (record) {
      this.store.remove(record);
    });
  }
});

/***/ }),

/***/ 53539:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010-2020 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.namespace('Tine.Felamimail.sieve');

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.sieve.VacationEditDialog
 * @extends     Tine.widgets.dialog.EditDialog
 *
 * <p>Sieve Filter Dialog</p>
 * <p>This dialog is editing sieve filters (vacation and rules).</p>
 * <p>
 * </p>
 *
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 *
 * @param       {Object} config
 * @constructor
 * Create a new VacationEditDialog
 */

Tine.Felamimail.sieve.VacationEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
  /**
   * @cfg {Tine.Felamimail.Model.Account}
   */
  account: null,
  editDialog: null,
  /**
   * @private
   */
  windowNamePrefix: 'VacationEditWindow_',
  appName: 'Felamimail',
  asAdminModule: false,
  loadRecord: true,
  tbarItems: [],
  evalGrants: false,
  readonlyReason: false,
  initComponent: function () {
    if (!this.recordProxy) {
      this.recordProxy = new Tine.Tinebase.data.RecordProxy({
        appName: this.asAdminModule ? 'Admin' : 'Felamimail',
        modelName: this.asAdminModule ? 'SieveVacation' : 'Vacation',
        recordClass: Tine.Felamimail.Model.Vacation,
        idProperty: 'id'
      });
    }
    Tine.Felamimail.sieve.VacationEditDialog.superclass.initComponent.call(this);
  },
  /**
   * overwrite update toolbars function (we don't have record grants yet)
   *
   * @private
   */
  updateToolbars: function () {},
  /**
   * executed after record got updated from proxy
   *
   * @private
   */
  onRecordLoad: async function () {
    await this.afterIsRendered();
    const hasRight = Tine.Felamimail.AccountEditDialog.prototype.checkAccountEditRight(this.account);
    this.action_saveAndClose.setDisabled(!hasRight);

    // mime type is always multipart/alternative
    this.record.set('mime', 'multipart/alternative');
    if (this.account && this.account.get('signature')) {
      this.record.set('signature', this.account.get('signature'));
    }
    this.getForm().loadRecord(this.record);
    var title = String.format(this.app.i18n._('Vacation Message for {0}'), this.account.get('name'));
    this.window.setTitle(title);
    this.checkStates();
    Tine.log.debug('Tine.Felamimail.sieve.VacationEditDialog::onRecordLoad() -> record:');
    Tine.log.debug(this.record);
    this.hideLoadMask();
  },
  /**
   * returns dialog
   *
   * NOTE: when this method gets called, all initalisation is done.
   *
   * @return {Object}
   * @private
   *
   */
  getFormItems: function () {
    return this.vacationPanel = new Tine.Felamimail.sieve.VacationPanel({
      title: i18n._('Vacation'),
      account: this.account,
      asAdminModule: true,
      editDialog: this
    });
  },
  /**
   * generic request exception handler
   *
   * @param {Object} exception
   */
  onRequestFailed: function (exception) {
    this.saving = false;
    Tine.Felamimail.handleRequestException(exception);
    this.hideLoadMask();
  },
  /**
   * executed when record gets updated from form
   */
  onRecordUpdate: async function () {
    Tine.Felamimail.sieve.VacationEditDialog.superclass.onRecordUpdate.call(this);
    let contactIds = [];
    Ext.each(['contact_id1', 'contact_id2'], function (field) {
      if (this.getForm().findField(field) && this.getForm().findField(field).getValue() !== '') {
        contactIds.push(this.getForm().findField(field).getValue());
      }
    }, this);
    let template = this.getForm().findField('template_id').getValue();
    this.record.set('contact_ids', contactIds);
    this.record.set('template_id', template);
    if (template !== '') {
      try {
        let response = await Tine.Felamimail.getVacationMessage(this.record.data);
        this.vacationPanel.reasonEditor.setValue(response.message);
      } catch (e) {
        Tine.Felamimail.handleRequestException(exception);
      }
    }
    let form = this.getForm();
    form.updateRecord(this.record);
    this.checkStates();
  },
  /**
   * call checkState for every field
   */
  checkStates: function () {
    this.getForm().items.each(function (item) {
      if (Ext.isFunction(item.checkState)) {
        item.checkState(this);
      }
    }, this);
  }
});

/**
 * Felamimail Edit Popup
 *
 * @param   {Object} config
 * @return  {Ext.ux.Window}
 */
Tine.Felamimail.sieve.VacationEditDialog.openWindow = function (config) {
  var window = Tine.WindowFactory.getWindow({
    width: 640,
    height: 550,
    name: Tine.Felamimail.sieve.VacationEditDialog.prototype.windowNamePrefix + Ext.id(),
    contentPanelConstructor: 'Tine.Felamimail.sieve.VacationEditDialog',
    contentPanelConstructorConfig: config
  });
  return window;
};

/***/ }),

/***/ 30092:
/***/ (() => {

/*
 * Tine 2.0
 *
 * @package     Felamimail
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Ching En Cheng <c.cheng@metaways.de>
 * @copyright   Copyright (c) 2020 Metaways Infosystems GmbH (http://www.metaways.de)
 */

Ext.ns('Tine.Felamimail');
/**
 * @param config
 * @constructor
 */
Tine.Felamimail.sieve.VacationPanel = function (config) {
  Ext.apply(this, config);
  Tine.Felamimail.sieve.VacationPanel.superclass.constructor.call(this);
};

/**
 * @namespace   Tine.Felamimail
 * @class       Tine.Felamimail.sieve.VacationPanel
 * @extends     Ext.Panel
 */
Ext.extend(Tine.Felamimail.sieve.VacationPanel, Ext.Panel, {
  /**
   * @cfg {Tine.Felamimail.Model.Account}
   */
  account: null,
  /**
   * @private
   */
  appName: 'Felamimail',
  tbarItems: [],
  evalGrants: false,
  readonlyReason: false,
  asAdminModule: false,
  layout: 'vbox',
  layoutConfig: {
    align: 'stretch'
  },
  border: false,
  app: null,
  initComponent: function () {
    this.initReasonEditor();
    var generalItems = this.getGeneralItems();
    this.items = [{
      title: this.app.i18n._('General'),
      autoScroll: true,
      border: false,
      frame: true,
      xtype: 'columnform',
      formDefaults: {
        anchor: '100%',
        labelSeparator: '',
        columnWidth: 1
      },
      items: generalItems
    }, {
      title: this.app.i18n._('Advanced'),
      collapsible: true,
      collapseMode: 'mini',
      collapsed: true,
      autoScroll: true,
      border: false,
      frame: true,
      xtype: 'columnform',
      formDefaults: {
        anchor: '100%',
        labelSeparator: '',
        columnWidth: 1
      },
      items: [[{
        fieldLabel: this.app.i18n._('Only send all X days to the same sender'),
        name: 'days',
        value: 7,
        xtype: 'numberfield',
        allowNegative: false,
        minValue: 1,
        checkState: () => {
          this.editDialog.getForm().findField('days').setDisabled(!this.editDialog.getForm().findField('enabled').getValue());
        }
      }]]
    }];
    this.supr().initComponent.call(this);
  },
  /**
   * overwrite update toolbars function (we don't have record grants yet)
   *
   * @private
   */
  updateToolbars: function () {},
  /**
   * init reason editor
   */
  initReasonEditor: function () {
    this.app = Tine.Tinebase.appMgr.get('Felamimail');
    var reg = this.app.getRegistry(),
      readonly = reg.get('config').vacationMessageCustomAllowed && reg.get('config').vacationMessageCustomAllowed.value === 0;
    this.reasonEditor = new Ext.form.HtmlEditor({
      fieldLabel: this.app.i18n._('Incoming mails will be answered with this text:'),
      name: 'reason',
      allowBlank: true,
      disabled: true,
      height: 220,
      readOnly: readonly,
      getDocMarkup: function () {
        var markup = '<html><body></body></html>';
        return markup;
      },
      checkState: () => {
        this.editDialog.getForm().findField('reason').setDisabled(!this.editDialog.getForm().findField('enabled').getValue());
      },
      plugins: [new Ext.ux.form.HtmlEditor.RemoveFormat(), new Ext.ux.form.HtmlEditor.SelectImage()]
    });
  },
  /**
   * get items for general tab
   *
   * @return Array
   */
  getGeneralItems: function () {
    let me = this;
    let items = [[{
      fieldLabel: this.app.i18n._('Status'),
      name: 'enabled',
      typeAhead: false,
      triggerAction: 'all',
      lazyRender: true,
      editable: false,
      mode: 'local',
      forceSelection: true,
      value: 0,
      xtype: 'combo',
      store: [[0, this.app.i18n._('I am available (vacation message disabled)')], [1, this.app.i18n._('I am not available (vacation message enabled)')]],
      listeners: {
        scope: me,
        select: function (combo, record) {
          me.editDialog.onRecordUpdate();
        }
      }
    }]];

    // TODO always add date items? check capabilities here / pass it in registry?
    items = items.concat(this.getDateItems());
    const templates = this.app.getRegistry().get('vacationTemplates');
    if (templates.totalcount > 0) {
      items = items.concat(this.getTemplateItems(templates));
    }
    items.push([this.reasonEditor]);
    return items;
  },
  /**
   * @return Array
   */
  getDateItems: function () {
    const commonConfig = this.getCommonFieldConfig();
    return [[Ext.apply({
      fieldLabel: this.app.i18n._('Start Date'),
      emptyText: this.app.i18n._('Set vacation start date ...'),
      name: 'start_date',
      xtype: 'datefield',
      checkState: () => {
        this.editDialog.getForm().findField('start_date').setDisabled(!this.editDialog.getForm().findField('enabled').getValue());
      }
    }, commonConfig), Ext.apply({
      fieldLabel: this.app.i18n._('End Date'),
      emptyText: this.app.i18n._('Set vacation end date ...'),
      name: 'end_date',
      xtype: 'datefield',
      checkState: () => {
        this.editDialog.getForm().findField('end_date').setDisabled(!this.editDialog.getForm().findField('enabled').getValue());
      }
    }, commonConfig)]];
  },
  getCommonFieldConfig: function () {
    return {
      listeners: {
        scope: this,
        select: this.onSelectTemplateField
      },
      columnWidth: 0.5
    };
  },
  /**
   * get items for vacation templates
   *
   * @return Array
   *
   * TODO use grid panel for x representatives?
   * @param templates
   */
  getTemplateItems: function (templates) {
    Tine.log.debug('Tine.Felamimail.sieve.VacationPanel::getTemplateItems()');
    Tine.log.debug(templates);
    const commonConfig = this.getCommonFieldConfig();
    return [[new Tine.Addressbook.SearchCombo(Ext.apply({
      fieldLabel: this.app.i18n._('Representative #1'),
      emptyText: this.app.i18n._('Choose first Representative ...'),
      blurOnSelect: true,
      name: 'contact_id1',
      selectOnFocus: true,
      forceSelection: false,
      allowBlank: true,
      checkState: () => {
        this.editDialog.getForm().findField('contact_id1').setDisabled(!this.editDialog.getForm().findField('enabled').getValue());
      }
    }, commonConfig)), new Tine.Addressbook.SearchCombo(Ext.apply({
      fieldLabel: this.app.i18n._('Representative #2'),
      emptyText: this.app.i18n._('Choose second Representative ...'),
      blurOnSelect: true,
      name: 'contact_id2',
      selectOnFocus: true,
      forceSelection: false,
      allowBlank: true,
      checkState: () => {
        this.editDialog.getForm().findField('contact_id2').setDisabled(!this.editDialog.getForm().findField('enabled').getValue());
      }
    }, commonConfig))], [{
      fieldLabel: this.app.i18n._('Message Template'),
      xtype: 'combo',
      mode: 'local',
      listeners: {
        scope: this,
        select: this.onTemplateComboSelect
      },
      displayField: 'name',
      name: 'template_id',
      valueField: 'id',
      triggerAction: 'all',
      emptyText: this.app.i18n._('Choose Template ...'),
      editable: false,
      store: new Ext.data.JsonStore({
        id: 'timezone',
        root: 'results',
        totalProperty: 'totalcount',
        fields: ['id', 'name', 'type'],
        // TODO use Tine.Filemanager.Model.Node or generic File model?
        data: templates
      }),
      checkState: () => {
        this.editDialog.getForm().findField('template_id').setDisabled(!this.editDialog.getForm().findField('enabled').getValue());
      }
    }]];
  },
  /**
   * template field has been selected, check if new vacation message needs to be fetched
   * - do this only if template has already been selected
   */
  onSelectTemplateField: async function () {
    if (this.editDialog.getForm().findField('template_id').getValue() !== '') {
      this.editDialog.onRecordUpdate();
    }
  },
  /**
   * template combo select event handler
   *
   * @param {} combo
   * @param {} record
   * @param {} index
   */
  onTemplateComboSelect: async function (combo, record, index) {
    Tine.log.debug('Tine.Felamimail.sieve.VacationPanel::onTemplateComboSelect()');
    Tine.log.debug(record);
    if (record.data && record.get('type') === 'file') {
      this.editDialog.onRecordUpdate();
    } else {
      // TODO do something?
    }
  }
});
Ext.reg('Tine.Felamimail.sieve.VacationPanel', Tine.Felamimail.sieve.VacationPanel);

/***/ }),

/***/ 3096:
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {

/*
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2010 Metaways Infosystems GmbH (http://www.metaways.de)
 */

var EventEmitter = __webpack_require__(17187);
Ext.ns('Tine.Tinebase');

/**
 * Abstract base class for all Tine applications
 * NOTE: app class for Tinebase app itself is Tine.Tinebase.Application.Tinebase (see bellow)
 * 
 * @namespace   Tine.Tinebase
 * @class       Tine.Tinebase.Application
 * @extends     Ext.util.Observable
 * @consturctor
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 */
Tine.Tinebase.Application = function (config) {
  config = config || {};
  Ext.apply(this, config);
  Tine.Tinebase.Application.superclass.constructor.call(this);
  this.hasMainScreen = Ext.isBoolean(this.hasMainScreen) ? this.hasMainScreen : this.getMainScreen != Tine.Tinebase.Application.prototype.getMainScreen || typeof Tine[this.appName].MainScreen === 'function';
  this.hasMainScreen = this.hasMainScreen && Tine.Tinebase.common.hasRight('mainscreen', this.appName);
  this.i18n = new Locale.Gettext();
  this.i18n.textdomain(this.appName);
  if (this.appName === 'Tinebase') {
    Ext.apply(this, Tine.Tinebase.Application.Tinebase);
  }

  /**
   * in memory msg bus for sync events
   */
  (() => {
    let ee = new EventEmitter();
    this.on = ee.on;
    this.emit = ee.emit;
  })();
  this.init();
  if (Tine.CoreData && Tine.CoreData.Manager) {
    Tine.log.debug('Tine.Tinebase.Application - register core data for ' + this.appName);
    this.registerCoreData.defer(500, this);
  }
  this.initAutoHooks();
  this.initRoutes();
};
Ext.extend(Tine.Tinebase.Application, Ext.util.Observable, {
  /**
   * @cfg {String} appName
   * untranslated application name (required)
   */
  appName: null,
  /**
   * @cfg {Boolean} hasMainScreen
   */
  hasMainScreen: null,
  /**
   * @cfg {Object} routes
   */
  routes: null,
  /**
   * @cfg {String} defaultRoute
   */
  defaultRoute: 'mainscreen',
  /**
   * @property {Locale.gettext} i18n
   */
  i18n: null,
  /**
   * returns title of this application
   * 
   * @return {String}
   */
  getTitle: function () {
    return this.i18n._(this.appName);
  },
  formatMessage: function (template) {
    arguments[0] = this.i18n._hidden(template);
    return formatMessage.apply(formatMessage, arguments);
  },
  /**
   * returns iconCls of this application
   * 
   * @param {String} target
   * @return {String}
   */
  getIconCls: function (target) {
    return 'ApplicationIconCls ' + this.appName + 'IconCls';
  },
  /**
   * returns the mainscreen of this application
   * 
   * @return {Tine.widgets.app.MainScreen}
   */
  getMainScreen: function () {
    if (this.hasMainScreen && !this.mainScreen) {
      this.mainScreen = new Tine[this.appName].MainScreen({
        app: this
      });
    }
    return this.mainScreen;
  },
  /**
   * returns registry of this app
   * 
   * @return {Ext.util.MixedCollection}
   */
  getRegistry: function () {
    return Tine[this.appName].registry;
  },
  /**
   * returns true if a specific feature is enabled for this application
   * 
   * @param {String} featureName
   * @return {Boolean}
   */
  featureEnabled: function (featureName) {
    var _Tine$this$appName$re;
    var featureConfig = (_Tine$this$appName$re = Tine[this.appName].registry.get("config")) === null || _Tine$this$appName$re === void 0 ? void 0 : _Tine$this$appName$re.features,
      result = featureConfig && featureConfig.value[featureName];
    if (result == undefined) {
      // check defaults if key is missing
      result = featureConfig && featureConfig.definition && featureConfig.definition['default'] && featureConfig.definition['default'][featureName];
    }
    return result;
  },
  /**
   * template function for subclasses to initialize application
   */
  init: Ext.emptyFn,
  /**
   * template function for subclasses to register app core data
   */
  registerCoreData: Ext.emptyFn,
  /**
   * init some auto hooks
   */
  initAutoHooks: function () {
    if (this.addButtonText) {
      Ext.ux.ItemRegistry.registerItem('Tine.widgets.grid.GridPanel.addButton', {
        text: this.i18n._hidden(this.addButtonText),
        iconCls: this.getIconCls(),
        scope: this,
        handler: function () {
          var ms = this.getMainScreen(),
            cp = ms.getCenterPanel();
          cp.onEditInNewWindow.call(cp, {});
        }
      });
    }
  },
  initRoutes: function () {
    var route, action;
    if (this.routes) {
      for (route in this.routes) {
        if (this.routes.hasOwnProperty(route)) {
          action = this.routes[route];
          if (Ext.isString(action) && Ext.isFunction(this[action])) {
            action = this[action].createDelegate(this);
          }
          if (route[0] != '/') {
            route = '/' + this.appName + '/' + route;
          }
          Tine.Tinebase.router.on(route, action);
        }
      }
    }

    // default mainscreen route
    if (!this.routes || !this.routes['']) {
      var me = this;
      Tine.Tinebase.router.on('/' + this.appName, function () {
        Tine.Tinebase.MainScreenPanel.show(me);
      });
    }
  },
  /**
   * @param {String} action
   * @param {Array} params
   */
  dispatchRoute: function (action, params) {
    var route, methodName, paramNames;
    if (this.routes) {
      for (route in this.routes) {
        if (this.routes.hasOwnProperty(route)) {
          paramNames = route.split('/');
          if (action == paramNames.shift()) {
            methodName = this.routes[route].action;
            break;
          }
        }
      }
    }
    if (methodName) {
      // @TODO validate parameters according to docs

      return this[methodName].apply(this, params);
    }
    Ext.MessageBox.show(Ext.apply(defaults, {
      title: i18n._('Not Supported'),
      msg: i18n._('Your request is not supported by this version.'),
      fn: function () {
        Tine.Tinebase.common.reload();
      }
    }));
  },
  /**
   * get route for this app
   * @return {string}
   */
  getRoute() {
    return '/' + this.appName;
  },
  /**
   * template function for subclasses is called before app activation. Return false to cancel activation
   */
  onBeforeActivate: Ext.emptyFn,
  /**
   * template function for subclasses is called after app activation.
   */
  onActivate: Ext.emptyFn,
  /**
   * template function for subclasses is called before app deactivation. Return false to cancel deactivation
   */
  onBeforeDeActivate: Ext.emptyFn,
  /**
   * template function for subclasses is called after app deactivation.
   */
  onDeActivate: Ext.emptyFn
});
Tine.Tinebase.featureEnabled = function (featureName) {
  return Tine.Tinebase.appMgr.get('Tinebase').featureEnabled(featureName);
};
Tine.Tinebase.Application.Tinebase = {
  registerCoreData() {
    // don't use this -> register from server only
  }
};

/***/ }),

/***/ 50728:
/***/ (() => {

Ext.ns('Ext.ux');

/**
 * plugin to insert additional registered items into a container
 *
 * @namespace   Ext.ux
 * @class       Ext.ux.ItemRegistry
 * @autor       Cornelius Weiss <c.weiss@metaways.de>
 * @license     BSD, MIT and GPL
 *
 * @example
// register 'additional item' for myDialog
Ext.ux.ItemRegistry.registerItem('myDialog', 'add-item-xtype', 20);

// in myDialog use itemRegistyPlugin
myDialog = Ext.extend(Ext.Container, {
    ...
    plugins: [{
        ptype: 'ux.itemregistry',
        key:   'myDialog'
    }]
})
 */
Ext.ux.ItemRegistry = function (config) {
  Ext.apply(this, config);
};

/**
 * @static
 * @private
 */
Ext.ux.ItemRegistry.itemMap = {};
Ext.ux.ItemRegistry.AUTO_ID = 1;

/**
 * registers an item for a given key
 * @static
 * 
 * @param {String|Ext.Component|Object} key
 * @param {String/Constructor/Object} item
 * @param {Number} pos (optional)
 */
Ext.ux.ItemRegistry.registerItem = function (key, item, pos) {
  var _ = window.lodash;
  if (_.isString(key)) {
    if (!Ext.ux.ItemRegistry.itemMap.hasOwnProperty(key)) {
      Ext.ux.ItemRegistry.itemMap[key] = [];
    }
    Ext.ux.ItemRegistry.itemMap[key].push({
      item: item,
      pos: pos
    });
  } else {
    // initialised component
    var dynKey = 'Ext.ux.ItemRegistry-' + ++Ext.ux.ItemRegistry.AUTO_ID,
      plugin = new Ext.ux.ItemRegistry({
        key: dynKey
      });
    Ext.ux.ItemRegistry.registerItem(dynKey, item, pos);
    key.plugins = key.plugins || [];
    key.plugins.push(plugin);
    if (_.get(key, 'items.items')) {
      plugin.init(key);
    }
  }
};
Ext.ux.ItemRegistry.prototype = {
  /**
   * @cfg {String} key
   * key the items are registered under. If no key is given, the itemId
   * of the component will be used
   */
  key: null,
  init: function (cmp) {
    this.cmp = cmp;
    if (!this.key) {
      this.key = cmp.getItemId();
    }

    // give static item pos to existing items
    this.cmp.items.each(function (item, idx) {
      if (!item.hasOwnProperty('registerdItemPos')) {
        item.registerdItemPos = idx * 10;
      }
    }, this);
    var regItems = Ext.ux.ItemRegistry.itemMap[this.key] || [];
    Ext.each(regItems, function (reg) {
      // key hat / -> find item defined by first part and register item,regItem,possuffix -> return
      var path = String(reg.pos).split('/');
      if (path.length > 1) {
        var idx = +path.shift(),
          item = this.cmp.items.get(idx);
        if (item) {
          // console.info(path.join('/'))
          Ext.ux.ItemRegistry.registerItem(this.cmp.items.get(idx), reg.item, path.join('/'));
        } else {
          console.warn('cannot register for path - ' + path.join('/'));
        }
        return;
      }
      var addItem = this.getItem(reg),
        addPos = null;
      if (!addItem) {
        console.warn('item not found');
        return;
      }

      // insert item 
      this.cmp.items.each(function (item, idx) {
        if (addItem.registerdItemPos < item.registerdItemPos) {
          this.cmp.insert(idx, addItem);
          addPos = idx;
          return false;
        }
        return true;
      }, this);
      if (!Ext.isNumber(addPos)) {
        this.cmp.add(addItem);
      }
    }, this);
  },
  getItem: function (reg) {
    var def = reg.item,
      item;
    if (typeof def === 'function') {
      try {
        item = new def(this.config);
      } catch (error) {
        console.error('Ext.ux.ItemRegistry::getItem failed to create');
        console.error(error);
        return;
      }
    } else {
      if (Ext.isString(def)) {
        def = {
          xtype: def
        };
      }
      item = this.cmp.lookupComponent(Object.assign(def, this.config));
    }
    item.registerdItemPos = reg.pos ? reg.pos : this.cmp.items.length * 10;
    return item;
  }
};
Ext.ComponentMgr.registerPlugin('ux.itemregistry', Ext.ux.ItemRegistry);

/* test - uncomment to run
if (! window.lodash) {
    window.lodash = _;
}

Ext.onReady(function() {
    var testWin = new Ext.Window({
        width: 640,
        height: 480,
        layout: 'fit',
        title: 'ux.itemregistry test',
        items: [{
            xtype: 'tabpanel',
            activeTab: 0,
            border: false,
            defaults: {border: false},
            itemId: 'testWin',
            plugins: ['ux.itemregistry'],
            items: [{
                title: 'basepanel',
                html: 'basepanel'
            }, {
                title: 'no pos',
                html: 'no pos'
            }, {
                title: 'pos 50',
                html: 'pos 50',
                registerdItemPos: 50
            }]
        }]
    });
    testWin.show();

    Ext.ux.ItemRegistry.registerItem(testWin.items.get(0), {
        xtype: 'panel',
        title: 'key-cmp',
        html: 'register item in an existing component'
    }, 80);
});

itemRegTestPanel20 = Ext.extend(Ext.Panel, {
    title: 'add panel pos 20',
    html: 'add panel pos 20',

    initComponent: function() {
        // example how to hook in owner
        this.on('added', function(me, owner, pos) {
            owner.on('tabchange', function() {
                console.log('tabchange');
            })
        }),

        itemRegTestPanel20.superclass.initComponent.call(this);
    }
});
Ext.ux.ItemRegistry.registerItem('testWin', itemRegTestPanel20, 20);

itemRegTestPanel60 = {
    xtype: 'panel',
    title: 'add panel pos 60',
    items: [{items: [{html: 'add panel pos 60'}]}]
};
Ext.ux.ItemRegistry.registerItem('testWin', itemRegTestPanel60, 60);
Ext.ux.ItemRegistry.registerItem(itemRegTestPanel60, {html: 'registered in component config'}, 20);
Ext.ux.ItemRegistry.registerItem('testWin', {html: 'registered with path position '}, '4/0/20');
*/

/***/ }),

/***/ 98154:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";

// EXPORTS
__webpack_require__.d(__webpack_exports__, {
  "Z": () => (/* binding */ ux_form_FieldTriggerPlugin)
});

// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js
var injectStylesIntoStyleTag = __webpack_require__(93379);
var injectStylesIntoStyleTag_default = /*#__PURE__*/__webpack_require__.n(injectStylesIntoStyleTag);
// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/styleDomAPI.js
var styleDomAPI = __webpack_require__(7795);
var styleDomAPI_default = /*#__PURE__*/__webpack_require__.n(styleDomAPI);
// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/insertBySelector.js
var insertBySelector = __webpack_require__(90569);
var insertBySelector_default = /*#__PURE__*/__webpack_require__.n(insertBySelector);
// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js
var setAttributesWithoutAttributes = __webpack_require__(3565);
var setAttributesWithoutAttributes_default = /*#__PURE__*/__webpack_require__.n(setAttributesWithoutAttributes);
// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/insertStyleElement.js
var insertStyleElement = __webpack_require__(19216);
var insertStyleElement_default = /*#__PURE__*/__webpack_require__.n(insertStyleElement);
// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/styleTagTransform.js
var styleTagTransform = __webpack_require__(44589);
var styleTagTransform_default = /*#__PURE__*/__webpack_require__.n(styleTagTransform);
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!../css/ux/form/FieldTriggerPlugin.css
var FieldTriggerPlugin = __webpack_require__(96334);
;// CONCATENATED MODULE: ../css/ux/form/FieldTriggerPlugin.css

      
      
      
      
      
      
      
      
      

var options = {};

options.styleTagTransform = (styleTagTransform_default());
options.setAttributes = (setAttributesWithoutAttributes_default());

      options.insert = insertBySelector_default().bind(null, "head");
    
options.domAPI = (styleDomAPI_default());
options.insertStyleElement = (insertStyleElement_default());

var update = injectStylesIntoStyleTag_default()(FieldTriggerPlugin/* default */.Z, options);




       /* harmony default export */ const form_FieldTriggerPlugin = (FieldTriggerPlugin/* default */.Z && FieldTriggerPlugin/* default.locals */.Z.locals ? FieldTriggerPlugin/* default.locals */.Z.locals : undefined);

;// CONCATENATED MODULE: ./ux/form/FieldTriggerPlugin.js
/*
 * Tine 2.0
 *
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiß <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2020 Metaways Infosystems GmbH (http://www.metaways.de)
 */

class FieldTriggerPlugin_FieldTriggerPlugin {
  triggerClass = 'x-form-trigger';
  visible = true;
  qtip = null;
  #trigger;
  constructor(config) {
    _.assign(this, config);
  }
  async init(field) {
    this.field = field;
    await field.afterIsRendered();
    const wrap = field.el.parent('.x-form-field-wrap') || field.el.parent('.tw-relpickercombocmp') || field.el.parent('.x-form-element') || field.el.parent('.x-grid-editor');
    if (wrap) {
      this.#trigger = wrap.createChild(this.triggerConfig || {
        tag: "img",
        src: Ext.BLANK_IMAGE_URL,
        cls: "x-form-trigger x-form-trigger-plugin " + this.triggerClass
      });
      this.setVisible(this.visible);
      if (this.qtip) {
        this.setQtip(this.qtip);
      }
      field.mon(this.#trigger, 'click', this.onTriggerClick, this, {
        preventDefault: true
      });
      this.#trigger.addClassOnOver('x-form-trigger-over');
      this.#trigger.addClassOnClick('x-form-trigger-click');

      // preserve space for triggers
      wrap.addClass('x-form-trigger-plugin-wrap');
      const paddingRight = Number(String(field.el.getStyle('padding-right')).replace('px', ''));
      field.el.setStyle({
        'box-sizing': 'border-box',
        'padding-right': paddingRight + 17 + 'px',
        'width': '100%',
        'height': field.getHeight() + 'px'
      });
      field.el.autoBoxAdjust = false;
      field.onResize(wrap.getWidth(), wrap.getHeight());
    }
  }
  setTriggerClass(triggerClass) {
    if (this.#trigger) {
      this.#trigger.removeClass(this.triggerClass);
      this.#trigger.addClass(triggerClass);
      this.triggerClass = triggerClass;
    }
  }
  setVisible(visible) {
    this.visible = visible;
    if (this.#trigger) {
      this.#trigger.setVisible(visible);
    }
  }
  setQtip(qtip) {
    this.qtip = qtip;
    if (this.#trigger) {
      this.#trigger.set({
        'ext:qtip': Tine.Tinebase.common.doubleEncode(qtip)
      });
    }
  }
  update(html) {
    if (this.#trigger) {
      this.#trigger.update(html);
    }
  }
}
/* harmony default export */ const ux_form_FieldTriggerPlugin = (FieldTriggerPlugin_FieldTriggerPlugin);

/***/ }),

/***/ 79886:
/***/ (() => {

/*
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2017 Metaways Infosystems GmbH (http://www.metaways.de)
 */
Ext.ns('Tine.widgets');

/**
 * @namespace   Tine.widgets
 * @class       Tine.widgets.MainScreen
 * @extends     Ext.Panel
 *
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 */
Tine.widgets.MainScreen = Ext.extend(Ext.Panel, {
  /**
   * @cfg {Tine.Tinebase.Application} app
   * instance of the app object (required)
   */
  app: null,
  /**
   * @cfg {String} activeContentType
   */
  activeContentType: null,
  /**
   * @cfg {Array} contentTypes
   */
  contentTypes: null,
  /**
   * @cfg {String} centerPanelClassName
   * name of centerpanel class name suffix in namespace of this app (defaults to GridPanel)
   * the class name will be expanded to Tine[this.appName][contentType + this.centerPanelClassNameSuffix]
   */
  centerPanelClassNameSuffix: 'GridPanel',
  /**
   * @cfg {Bool} useModuleTreePanel
   * use modulePanel (defaults to null -> autodetection)
   */
  useModuleTreePanel: null,
  /**
   * @cfg {Number} northHeight
   */
  northHeight: 44,
  /**
   * @cfg {Number} westWidth
   */
  westWidth: 200,
  layout: 'border',
  border: false,
  stateful: true,
  xtype: 'Tine.widgets.MainScreen',
  initComponent: function () {
    var registeredContentTypes = _.get(Tine.widgets.MainScreen.registerContentType, 'registry.' + this.app.appName, []);
    this.contentTypes = (this.contentTypes || []).concat(registeredContentTypes);
    this.useModuleTreePanel = Ext.isArray(this.contentTypes) && this.contentTypes.length > 1;
    this.initLayout();
    this.initMessageBus();
    if (this.cls) {
      this.cls = this.cls + ' ' + 't-app-' + this.app.appName.toLowerCase();
    } else {
      this.cls = 't-app-' + this.app.appName.toLowerCase();
    }
    this.stateId = this.app.appName + '-mainScreen';
    this.on('resize', this.applyResponsiveLayout, this);
    this.tbar = {
      hidden: true,
      items: [{
        iconCls: 'action_menu',
        handler: () => {
          this.layout.west.slideOut();
        }
      }, '->', {
        text: '...',
        handler: btn => {
          const tbar = _.get(_.get(this.northCardPanel, 'layout.activeItem'), 'items.items[0]');
          if (tbar) {
            tbar.layout.initMore();
            const moreMenu = tbar.layout.moreMenu;
            moreMenu.showAll = true;
            moreMenu[new Date().getTime() - moreMenu.lastVisible < 100 ? 'hide' : 'show'](btn.el);
            moreMenu.showAll = false;
          }
        }
      }]
    };
    Tine.widgets.MainScreen.superclass.initComponent.apply(this, arguments);
  },
  applyResponsiveLayout: function (me, width, height) {
    // @TODO centralize breakpoints / have more breakpoints
    if (!this._initResponsiveLayout) {
      this.northCardPanel.on('beforeshow', () => {
        return 1024 < this.getWidth();
      });
      this._initResponsiveLayout = true;
    }
    const isSmall = width < 1024;
    this.westRegionPanel.afterIsRendered().then(panel => {
      panel[isSmall ? 'collapse' : 'expand']();
    });
    this.northCardPanel.afterIsRendered().then(panel => {
      panel.setVisible(!isSmall);
    });
    this.westRegionPanel.on('click', () => {});
    this.getTopToolbar().setVisible(isSmall);
    _.defer(_.bind(this.doLayout, this), true);
  },
  onDestroy: function () {
    _.each(this.postalSubscriptions, subscription => {
      subscription.unsubscribe();
    });
    return this.supr().onDestroy.call(this);
  },
  initMessageBus: function () {
    this.postalSubscriptions = [];
    // NOTE: it should be enough to manage on contentType level?
    // _.each(Tine.Tinebase.areaLocks.getLocks(this.app.appName), (areaLock) => {
    //     this.postalSubscriptions.push(postal.subscribe({
    //         channel: "areaLocks",
    //         topic: areaLock + '.*',
    //         callback: this.onAreaLockChange.createDelegate(this)
    //     }));
    // });
  },

  onAreaLockChange: function (data, e) {
    var topic = e.topic,
      locked = !!Tine.Tinebase.areaLocks.getLocks(this.app.appName, true).length,
      cp = this.getCenterPanel(),
      grid = cp ? cp.getGrid() : null,
      store = grid.getStore();

    // shouldn't this be done by the gird itself?
    if (locked) {
      store.removeAll();
      // @TODO: quit bg refresh task?
    } else {
      store.reload();
    }
  },
  /**
   * returns canonical path part
   * @returns {string}
   */
  getCanonicalPathSegment: function () {
    if (this.app) {
      return ['', this.app.name, 'MainScreen'].join(Tine.Tinebase.CanonicalPath.separator);
    }
  },
  getState: function () {
    return {
      westWidth: this.westRegionPanel.getWidth(),
      northHeight: this.northCardPanel.getHeight()
    };
  },
  applyState: function (state) {
    this.westRegionPanel.setWidth(state.westWidth);
    if (state.northHeight > 0) {
      // NOTE: in some wired cases the state is 0 - and the user has no option to resize it
      this.northCardPanel.setHeight(state.northHeight);
    }
  },
  /**
   * @private
   */
  initLayout: function () {
    this.items = [{
      ref: 'northCardPanel',
      cls: 'tine-mainscreen-centerpanel-north',
      region: 'north',
      layout: 'card',
      activeItem: 0,
      height: this.northHeight,
      border: false,
      items: []
    }, {
      ref: 'centerCardPanel',
      cls: 'tine-mainscreen-centerpanel-center',
      region: 'center',
      animate: true,
      border: false,
      layout: 'card',
      activeItem: 0,
      defaults: {
        hideMode: 'offsets'
      },
      items: []
    }, {
      ref: 'westRegionPanel',
      cls: 'tine-mainscreen-centerpanel-west',
      region: 'west',
      //id: 'west',
      stateful: false,
      split: true,
      width: this.westWidth,
      minSize: 100,
      border: false,
      collapsible: true,
      collapseMode: 'mini',
      header: false,
      layout: 'fit',
      listeners: {
        scope: this,
        resize: this.saveState,
        afterrender: function () {
          // add to scrollmanager
          if (arguments[0] && arguments[0].hasOwnProperty('body')) {
            Ext.dd.ScrollManager.register(arguments[0].body);
          }
        }
      },
      autoScroll: true,
      tbar: [{
        buttonAlign: 'center'
      }],
      items: [{
        ref: '../moduleCardPanel',
        cls: 'tine-mainscreen-centerpanel-west-modules',
        border: false,
        autoScroll: false,
        autoHeight: true,
        style: {
          width: '100%'
        },
        layout: 'card',
        activeItem: 0,
        items: []
      }, {
        ref: '../westCardPanel',
        cls: 'tine-mainscreen-centerpanel-west-treecards',
        border: false,
        style: {
          width: '100%'
        },
        autoScroll: false,
        layout: 'card',
        activeItem: 0,
        items: []
      }]
    }];
  },
  afterRender: function () {
    Tine.widgets.MainScreen.superclass.afterRender.call(this);

    // Tine.Tinebase.areaLocks.registerMaskEl(this.app.appName, this.getEl());
    // _.each(Tine.Tinebase.areaLocks.getLocks(this.app.appName), (areaLock) => {
    //     Tine.Tinebase.areaLocks.unlock(areaLock);
    // });
    this.setActiveContentType(this.activeContentType);
  },
  /**
   * returns active content type
   * 
   * @return {String}
   */
  getActiveContentType: function () {
    return this.activeContentType ? this.activeContentType : '';
  },
  getContentTypeDefinition: function (contentType) {
    var _ = window.lodash;
    return _.find(this.contentTypes, {
      contentType: contentType
    }) || _.find(this.contentTypes, {
      model: contentType
    }) || _.find(this.contentTypes, {
      modelName: contentType
    });
  },
  /**
   * get center panel for given contentType
   * 
   * @param {String} contentType
   * @return {Ext.Panel}
   */
  getCenterPanel: function (contentType) {
    contentType = contentType || this.getActiveContentType();
    // if (!contentType) return;

    var def = this.getContentTypeDefinition(contentType) || {},
      suffix = def && def.xtype ? '' : this.centerPanelClassNameSuffix;
    if (!this[contentType + suffix]) {
      // try {
      if (def && def.xtype) {
        this[contentType + suffix] = Ext.create(def);
      } else if (Tine[def.appName || this.app.appName][contentType + suffix]) {
        this[contentType + suffix] = new Tine[def.appName || this.app.appName][contentType + suffix](_.merge({
          app: def.appName ? Tine.Tinebase.appMgr.get(def.appName) : this.app,
          mainScreen: this,
          plugins: (() => {
            const wp = this.getWestPanel();
            return wp && wp.getFilterPlugin ? [wp.getFilterPlugin(contentType)] : [];
          })()
        }, (def === null || def === void 0 ? void 0 : def.config) || {}));
      } else {
        return null;
      }
      if (this[contentType + suffix].cls) {
        this[contentType + suffix].cls = this[contentType + suffix].cls + ' t-contenttype-' + contentType.toLowerCase();
      } else {
        this[contentType + suffix].cls = 't-contenttype-' + contentType.toLowerCase();
      }
      // } catch (e) {
      //     Tine.log.error('Could not create centerPanel "Tine.' + this.app.appName + '.' + contentType + suffix + '"');
      //     Tine.log.error(e.stack ? e.stack : e);
      //     this[contentType + suffix] = new Ext.Panel({html: 'ERROR'});
      // }
    }

    return this[contentType + suffix];
  },
  /**
   * get north panel for given contentType
   * 
   * @param {String} contentType
   * @return {Ext.Panel}
   */
  getNorthPanel: function (contentType) {
    contentType = contentType || this.getActiveContentType();
    if (!this[contentType + 'ActionToolbar']) {
      try {
        var cp = this.getCenterPanel(contentType);
        if (Ext.isFunction(cp.getActionToolbar)) {
          this[contentType + 'ActionToolbar'] = cp.getActionToolbar();
          if (this[contentType + 'ActionToolbar'].cls) {
            this[contentType + 'ActionToolbar'].cls = this[contentType + 'ActionToolbar'].cls + ' t-contenttype-' + contentType.toLowerCase();
          } else {
            this[contentType + 'ActionToolbar'].cls = 't-contenttype-' + contentType.toLowerCase();
          }
        }
      } catch (e) {
        Tine.log.error('Could not create northPanel');
        Tine.log.error(e.stack ? e.stack : e);
        this[contentType + 'ActionToolbar'] = new Ext.Panel({
          html: 'ERROR'
        });
      }
    }
    return this[contentType + 'ActionToolbar'];
  },
  /**
   * get module tree panel
   * 
   * @return {Ext.Panel}
   */
  getModuleTreePanel: function () {
    if (!this.moduleTreePanel) {
      if (this.useModuleTreePanel) {
        this.moduleTreePanel = new Tine.widgets.ContentTypeTreePanel({
          app: this.app,
          mainScreen: this,
          contentTypes: this.contentTypes,
          contentType: this.getActiveContentType()
        });
        var me = this;
        this.moduleTreePanel.on('click', function (node, event) {
          // NOTE: 'this' is moduleTreePanel here (no scope provided)
          if (node != this.lastClickedNode) {
            this.lastClickedNode = node;
            this.fireEvent('selectionchange');
          } else if (me.getWestPanel().hasFavoritesPanel) {
            // select default favorite
            // NOTE: a lot of models don't have a default favorite defined...
            me.getWestPanel().getFavoritesPanel().selectDefault();
          }
        });
      } else {
        this.moduleTreePanel = new Ext.Panel({
          html: '',
          border: false,
          frame: false
        });
      }
    }
    return this.moduleTreePanel;
  },
  /**
   * get panel for westCardPanel region of given contentType
   *
   * NOTE: do not confuse this with westRegionPanel!
   * 
   * @return {Ext.Panel}
   */
  getWestPanel: function (contentType) {
    contentType = contentType || this.getActiveContentType();
    var _ = window.lodash,
      def = this.getContentTypeDefinition(contentType),
      app = _.get(def, 'app', this.app),
      wpName = _.upperFirst(contentType + 'WestPanel');
    if (!this[wpName]) {
      var wpconfig = {
        app: app,
        mainScreen: this,
        contentTypes: this.contentTypes,
        contentType: contentType,
        listeners: {
          scope: this,
          // clear gird selection on favorite change, module change, container change?
          selectionchange: function () {
            var cp = this.getCenterPanel();
            if (cp) {
              try {
                var grid = cp.getGrid();
                if (grid) {
                  var sm = grid.getSelectionModel();
                  if (sm) {
                    sm.clearSelections();
                    cp.actionUpdater.updateActions(sm);
                  }
                }
              } catch (e) {
                // do nothing - no grid
              }
            }
          }
        }
      };
      try {
        if (Tine[app.name].hasOwnProperty(wpName)) {
          this[wpName] = new Tine[app.appName][wpName](wpconfig);
        } else {
          this[wpName] = new Tine.widgets.mainscreen.WestPanel(wpconfig);
        }
      } catch (e) {
        Tine.log.error('Could not create westPanel');
        Tine.log.error(e.stack ? e.stack : e);
        this[wpName] = new Ext.Panel({
          html: 'ERROR'
        });
      }
    }
    return this[wpName];
  },
  /**
   * shows center panel in mainscreen
   */
  showCenterPanel: function () {
    this.setActiveContentPanel(this.getCenterPanel(this.getActiveContentType()), true);
  },
  /**
   * shows module tree panel in mainscreen
   */
  showModuleTreePanel: function () {
    this.setActiveModulePanel(this.getModuleTreePanel(), true);
  },
  /**
   * shows west panel in mainscreen
   */
  showWestCardPanel: function () {
    // add save favorites button to toolbar if favoritesPanel exists
    var westPanel = this.getWestPanel(),
      favoritesPanel = westPanel.hasFavoritesPanel ? westPanel.getFavoritesPanel() : false,
      westPanelToolbar = this.westRegionPanel.getTopToolbar();
    westPanelToolbar.removeAll();
    if (favoritesPanel) {
      westPanelToolbar.addButton({
        xtype: 'button',
        text: i18n._('Save current view as favorite'),
        iconCls: 'action_saveFilter',
        scope: this,
        handler: function () {
          favoritesPanel.saveFilter.call(favoritesPanel);
        }
      });

      // westPanelToolbar.show();
      // flat design
      westPanelToolbar.hide();
    } else {
      westPanelToolbar.hide();
    }
    westPanelToolbar.doLayout();
    this.setActiveTreePanel(westPanel, true);
  },
  /**
   * shows north panel in mainscreen
   */
  showNorthPanel: function () {
    this.setActiveToolbar(this.getNorthPanel(this.getActiveContentType()), true);
  },
  /**
   * sets the active content type
   *
   * @param {String} contentType to activate
   */
  setActiveContentType: function (contentType) {
    if (contentType === null) {
      // use first valid content type
      if (this.contentTypes && this.contentTypes.length > 0) {
        contentType = this.contentTypes[0].modelName;
      }
    }
    this.activeContentType = contentType;
    this.showWestCardPanel();
    this.showCenterPanel();
    this.showNorthPanel();
    this.showModuleTreePanel();
  },
  /**
   * sets the active content panel
   *
   * @param {Ext.Panel} item Panel to activate
   * @param {Bool} keep keep panel
   */
  setActiveContentPanel: function (panel, keep) {
    Ext.ux.layout.CardLayout.helper.setActiveCardPanelItem(this.centerCardPanel, panel, keep);
  },
  /**
   * sets the active tree panel
   *
   * @param {Ext.Panel} panel Panel to activate
   * @param {Bool} keep keep panel
   */
  setActiveTreePanel: function (panel, keep) {
    Ext.ux.layout.CardLayout.helper.setActiveCardPanelItem(this.westCardPanel, panel, keep);
  },
  /**
   * sets the active module tree panel
   *
   * @param {Ext.Panel} panel Panel to activate
   * @param {Bool} keep keep panel
   */
  setActiveModulePanel: function (panel, keep) {
    Ext.ux.layout.CardLayout.helper.setActiveCardPanelItem(this.moduleCardPanel, panel, keep);
  },
  /**
   * sets item
   *
   * @param {Ext.Toolbar} panel toolbar to activate
   * @param {Bool} keep keep panel
   */
  setActiveToolbar: function (panel, keep) {
    if (panel) {
      if (!this.northCardPanel.isVisible()) {
        this.northCardPanel.show();
        this.northCardPanel.ownerCt.doLayout();
        panel.show(); // Nasty resize prob!
      }

      Ext.ux.layout.CardLayout.helper.setActiveCardPanelItem(this.northCardPanel, panel, keep);
    } else {
      this.northCardPanel.hide();
      this.northCardPanel.ownerCt.doLayout();
    }
  },
  /**
   * gets the currently displayed toolbar
   *
   * @return {Ext.Toolbar}
   */
  getActiveToolbar: function () {
    var northPanel = this.northCardPanel;
    if (northPanel.layout.activeItem && northPanel.layout.activeItem.el) {
      return northPanel.layout.activeItem.el;
    } else {
      return false;
    }
  }
});

/**
 * content type registry
 *
 * @param {String} appName
 * @param {Collection} contentType
 *   contentType:   {String|Object|Model}
 *   text:          {String}
 *   group:         {String} (optional)
 *   xtype:         {String} (optional)
 */
Tine.widgets.MainScreen.registerContentType = function (appName, contentType) {
  var registeredContentTypes = _.get(Tine.widgets.MainScreen.registerContentType, 'registry.' + appName, []);
  if (_.isFunction(contentType.getMeta)) {
    contentType = {
      appName: contentType.getMeta('appName'),
      modelName: contentType.getMeta('modelName')
    };
  }
  registeredContentTypes.push(contentType);
  _.set(Tine.widgets.MainScreen.registerContentType, 'registry.' + appName, registeredContentTypes);
};

/***/ }),

/***/ 28198:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _ux_form_FieldTriggerPlugin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(98154);
/*
 * Tine 2.0
 *
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiß <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2020 Metaways Infosystems GmbH (http://www.metaways.de)
 */


class RecordEditFieldTriggerPlugin extends _ux_form_FieldTriggerPlugin__WEBPACK_IMPORTED_MODULE_0__/* ["default"] */ .Z {
  allowCreateNew = true;

  /**
   * properties from record.json to preserve when editing (see Ext.copyTo for syntax)
   * @type {string}
   */
  preserveJsonProps = '';
  editDialogMode = null;
  triggerClass = 'action_edit';
  constructor(config) {
    super(config);
    _.assign(this, config);
  }
  async init(field) {
    this.visible = this.allowCreateNew;
    await super.init(field);
    this.assertState();
    field.setValue = field.setValue.createSequence(_.bind(this.assertState, this));
    field.clearValue = field.clearValue.createSequence(_.bind(this.assertState, this));
  }
  assertState() {
    this.setVisible((!!this.field.selectedRecord || this.allowCreateNew) && !this.field.readOnly && !this.field.disabled);
    this.setTriggerClass(!!this.field.selectedRecord ? 'action_edit' : 'action_add');
  }

  // allow to configure defaults from outside
  async getRecordDefaults() {
    return {};
  }
  async onTriggerClick() {
    // let me = this;
    let editDialogClass = Tine.widgets.dialog.EditDialog.getConstructor(this.field.recordClass);
    if (editDialogClass) {
      var _this$editDialogConfi;
      const record = this.field.selectedRecord || Object.assign(Tine.Tinebase.data.Record.setFromJson(Ext.apply(this.field.recordClass.getDefaultData(), await this.getRecordDefaults()), this.field.recordClass), {
        phantom: true
      });
      const mode = this.editDialogMode ?? ((_this$editDialogConfi = this.editDialogConfig) === null || _this$editDialogConfi === void 0 ? void 0 : _this$editDialogConfi.mode) ?? editDialogClass.prototype.mode;
      if (!this.field.selectedRecord && mode === 'remote') {
        // prevent loading non existing remote record
        record.setId(0);
      }
      if (this.attachments) {
        record.set('attachments', this.attachments);
      }
      editDialogClass.openWindow(Object.assign({
        mode,
        record,
        recordId: record.getId(),
        needsUpdateEvent: true,
        listeners: {
          scope: this,
          'update': updatedRecord => {
            let record = !updatedRecord.data ? Tine.Tinebase.data.Record.setFromJson(updatedRecord, this.field.recordClass) : updatedRecord;
            Tine.Tinebase.common.assertComparable(record.data);
            if (this.field.selectedRecord && this.preserveJsonProps) {
              Ext.copyTo(record.json, this.field.selectedRecord.json, this.preserveJsonProps);
            }
            // here we loose record.json data from old record! -> update existing record? vs. have preserveJSON props? // not a problem?
            this.field.setValue(record);
            this.field.fireEvent('select', this.field, record);
          },
          'cancel': () => {
            if (new Date().getTime() - 1000 < this.field.blurOnSelectLastRun) return;
            _.delay(() => {
              this.field.blurOnSelectLastRun = new Date().getTime();
              const focusClass = this.field.focusClass;
              this.field.focusClass = '';
              Ext.form.TriggerField.superclass.onBlur.call(this.field);
              this.field.focusClass = focusClass;
            }, 100);
          }
        }
      }, this.editDialogConfig || {}));
    }
  }
}
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (RecordEditFieldTriggerPlugin);

/***/ }),

/***/ 17457:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

"use strict";
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _Tinebase_js_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(87537);
/* harmony import */ var _Tinebase_js_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_Tinebase_js_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _Tinebase_js_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(23645);
/* harmony import */ var _Tinebase_js_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_Tinebase_js_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(61667);
/* harmony import */ var _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2__);
// Imports



var ___CSS_LOADER_URL_IMPORT_0___ = new URL(/* asset import */ __webpack_require__(27127), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_1___ = new URL(/* asset import */ __webpack_require__(62699), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_2___ = new URL(/* asset import */ __webpack_require__(29913), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_3___ = new URL(/* asset import */ __webpack_require__(73824), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_4___ = new URL(/* asset import */ __webpack_require__(42832), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_5___ = new URL(/* asset import */ __webpack_require__(82152), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_6___ = new URL(/* asset import */ __webpack_require__(29906), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_7___ = new URL(/* asset import */ __webpack_require__(97972), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_8___ = new URL(/* asset import */ __webpack_require__(24343), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_9___ = new URL(/* asset import */ __webpack_require__(53421), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_10___ = new URL(/* asset import */ __webpack_require__(50938), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_11___ = new URL(/* asset import */ __webpack_require__(90417), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_12___ = new URL(/* asset import */ __webpack_require__(99874), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_13___ = new URL(/* asset import */ __webpack_require__(91738), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_14___ = new URL(/* asset import */ __webpack_require__(11404), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_15___ = new URL(/* asset import */ __webpack_require__(70423), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_16___ = new URL(/* asset import */ __webpack_require__(11779), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_17___ = new URL(/* asset import */ __webpack_require__(55759), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_18___ = new URL(/* asset import */ __webpack_require__(31885), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_19___ = new URL(/* asset import */ __webpack_require__(96353), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_20___ = new URL(/* asset import */ __webpack_require__(61639), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_21___ = new URL(/* asset import */ __webpack_require__(40997), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_22___ = new URL(/* asset import */ __webpack_require__(38861), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_23___ = new URL(/* asset import */ __webpack_require__(24185), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_24___ = new URL(/* asset import */ __webpack_require__(14368), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_25___ = new URL(/* asset import */ __webpack_require__(98163), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_26___ = new URL(/* asset import */ __webpack_require__(56310), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_27___ = new URL(/* asset import */ __webpack_require__(38082), __webpack_require__.b);
var ___CSS_LOADER_URL_IMPORT_28___ = new URL(/* asset import */ __webpack_require__(65147), __webpack_require__.b);
var ___CSS_LOADER_EXPORT___ = _Tinebase_js_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_Tinebase_js_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));
var ___CSS_LOADER_URL_REPLACEMENT_0___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_0___);
var ___CSS_LOADER_URL_REPLACEMENT_1___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_1___);
var ___CSS_LOADER_URL_REPLACEMENT_2___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_2___);
var ___CSS_LOADER_URL_REPLACEMENT_3___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_3___);
var ___CSS_LOADER_URL_REPLACEMENT_4___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_4___);
var ___CSS_LOADER_URL_REPLACEMENT_5___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_5___);
var ___CSS_LOADER_URL_REPLACEMENT_6___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_6___);
var ___CSS_LOADER_URL_REPLACEMENT_7___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_7___);
var ___CSS_LOADER_URL_REPLACEMENT_8___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_8___);
var ___CSS_LOADER_URL_REPLACEMENT_9___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_9___);
var ___CSS_LOADER_URL_REPLACEMENT_10___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_10___);
var ___CSS_LOADER_URL_REPLACEMENT_11___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_11___);
var ___CSS_LOADER_URL_REPLACEMENT_12___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_12___);
var ___CSS_LOADER_URL_REPLACEMENT_13___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_13___);
var ___CSS_LOADER_URL_REPLACEMENT_14___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_14___);
var ___CSS_LOADER_URL_REPLACEMENT_15___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_15___);
var ___CSS_LOADER_URL_REPLACEMENT_16___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_16___);
var ___CSS_LOADER_URL_REPLACEMENT_17___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_17___);
var ___CSS_LOADER_URL_REPLACEMENT_18___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_18___);
var ___CSS_LOADER_URL_REPLACEMENT_19___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_19___);
var ___CSS_LOADER_URL_REPLACEMENT_20___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_20___);
var ___CSS_LOADER_URL_REPLACEMENT_21___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_21___);
var ___CSS_LOADER_URL_REPLACEMENT_22___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_22___);
var ___CSS_LOADER_URL_REPLACEMENT_23___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_23___);
var ___CSS_LOADER_URL_REPLACEMENT_24___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_24___);
var ___CSS_LOADER_URL_REPLACEMENT_25___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_25___);
var ___CSS_LOADER_URL_REPLACEMENT_26___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_26___);
var ___CSS_LOADER_URL_REPLACEMENT_27___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_27___);
var ___CSS_LOADER_URL_REPLACEMENT_28___ = _Tinebase_js_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_28___);
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".felamimail-action-encrypt {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_0___ + ") !important;\n}\n\n.felamimail-action-decrypt {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_1___ + ") !important;\n}\n\n.action_email_flag {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_2___ + ") !important;\n}\n\n.action_email_reply {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_3___ + ") !important;\n}\n\n.action_email_replyAll {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_4___ + ") !important;\n}\n\n.action_email_forward {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_5___ + ") !important;\n}\n\n.action_update_cache {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_6___ + ") !important;\n}\n\n.action_mark_read {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_7___ + ") !important;\n}\n\n.action_approve_migration {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_8___ + ") !important;\n}\n\n.action_file {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_9___ + ") !important;\n}\n\n.FelamimailIconCls {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_6___ + ") !important;\n}\n\n.action_folder_emptytrash {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_10___ + ") !important;\n}\n\n.action_saveAsDraft {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_11___ + ") !important;\n}\n\n.action_saveAsTemplate {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_12___ + ") !important;\n}\n\n.action_email_download {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_9___ + ") !important;\n}\n\n/* rule grid actions */\n\n.action_new_rule {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_13___ + ") !important;\n}\n\n.action_move_up {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_14___ + ") !important;\n}\n\n.action_move_down {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_15___ + ") !important;\n}\n\n/* other actions */\n\n.felamimail-action-reading-confirmation {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_16___ + ") !important;\n}\n\n\n.felamimail-action-sieve-notification {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_17___ + ") !important;\n}\n\n.felamimail-action-spam {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_18___ + ") !important;\n}\n\n.felamimail-action-ham {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_19___ + ") !important;\n}\n\n.felamimail-action {\n    padding: 5px;\n    margin: 5px;\n    cursor: pointer;\n    background-color: #D8A45B;\n    border-radius: 5px;\n}\n\n.felamimail-action:hover {\n    filter: brightness(1.2);\n}\n\n/***** flag classes ******/\n\n.flag_unread td{\n    font-weight: bold;\n}\n\n.unread-flag-dot {\n    -webkit-mask-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_20___ + ") !important;\n    -webkit-mask-repeat: no-repeat;\n    background-color: #4e79b2;\n}\n\n.flag_flagged {\n    color: #ff0000;\n}\n\n.flag_deleted td {\n    text-decoration: line-through;\n    color: #aaaaaa;\n}\n\n.is_spam_suspicions td {\n    color: #D8A45B;\n}\n\n.is_spam_suspicions_toolbar {\n    background-color: #D8A45B;\n}\n\n.FelamimailFlagIcon {\n    width: 12px;\n    height: 12px;\n    padding-left: 2px;\n}\n\n.FelamimailFlagIcon.MessageFileIcon {\n    cursor: pointer;\n}\n\n/**** preview *****/\n\n.preview-panel-felamimail {\n    padding: 5px;\n    overflow: hidden;\n}\n\n.preview-panel-felamimail-headers {\n    background-color: #dddddd;\n    overflow: auto;\n    padding: 5px;\n}\n\n.preview-panel-felamimail-header-row {\n    display: flex;\n    padding-top: 3px;\n}\n\n.preview-panel-felamimail-header-row-left {\n    font-weight: bold;\n    min-width: 50px;\n    text-align: right;\n}\n\n.preview-panel-felamimail-header-row-right {\n    padding-left: 10px;\n    display: flex;\n    text-align: left;\n    flex-wrap: wrap;\n}\n\n.preview-panel-felamimail-header-longtext {\n    white-space: nowrap;\n}\n\n.preview-panel-felamimail-attachments {\n    margin-top: 5px;\n    padding: 3px;\n    background-color: #e8e8e8;\n}\n\n/* note: el tyle of html areas underling textarea are copied to iframe*/\n.felamimail-message-body-html, \n.preview-panel-felamimail-body {\n    overflow: auto;\n    font-size: 13px;\n    padding: 10px;\n}\n\n.preview-panel-felamimail-body ul {\n    list-style: circle outside; \n    margin-left: 20px;\n}\n.preview-panel-felamimail-body ol {\n    list-style: decimal outside; \n    margin-left: 20px;\n}\n\n.preview-panel-felamimail-body strong { \n    font-weight: bold; \n    font-style: inherit;\n}\n\n.preview-panel-felamimail-body em { \n    font-style: italic; \n    font-weight: inherit;\n}\n\n.preview-panel-felamimail-body .x-form-textarea {\n    margin-top: 5px;\n    font: normal 13px courier;\n    border: 0;\n}\n\n.felamimail-edit-text-plain .x-form-textarea {\n    font-size: 13px;\n    padding-top: 5px;\n    padding-left: 5px;\n}\n\n.felamimail_spam_suspicions_toolbar {\n    padding: 5px;\n    background-color: #D8A45B;\n    display: flex;\n    flex-direction: row;\n    justify-content: space-around;\n    align-items: center;\n    flex-wrap: wrap;\n}\n\n/***** node classes ******/\n\n.felamimail-node-root {\n    display: none;\n}\n\n.felamimail-node-unread span {\n    font-weight: bold;\n}\n\n.felamimail-node-trash img.x-tree-node-icon {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_21___ + ");\n}\n\n.felamimail-node-trash-full img.x-tree-node-icon {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_22___ + ");\n}\n\n.felamimail-node-sent img.x-tree-node-icon {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_23___ + ");\n}\n\n.felamimail-node-inbox img.x-tree-node-icon {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_24___ + ");\n}\n\n.felamimail-node-drafts img.x-tree-node-icon {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_11___ + ");\n}\n\n.felamimail-node-templates img.x-tree-node-icon {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_12___ + ");\n}\n\n.felamimail-node-junk img.x-tree-node-icon {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_22___ + ");\n}\n\n.felamimail-node-unselectable img.x-tree-node-icon {\n    background-image: url(" + ___CSS_LOADER_URL_REPLACEMENT_25___ + ");\n    opacity: 0.5;\n}\n\n.felamimail-node-account img.x-tree-node-icon {\n    background-image: url(" + ___CSS_LOADER_URL_REPLACEMENT_26___ + ");\n}\n\n.felamimail-node-statusbox {\n    padding-right: 5px;\n    position: absolute;\n    right: 0;\n}\n\n.felamimail-node-statusbox-progress {\n    width: 14px;\n    height: 14px;\n    margin-top: 2px;\n    background-repeat: no-repeat;\n    vertical-align: top;\n    margin-right: 5px;\n}\n\n.felamimail-node-statusbox-progress-pie {\n    background-size: contain;\n    width: 17px;\n    height: 17px;\n    margin-top: 1px;\n}\n\n.ext-gecko .felamimail-node-statusbox-unread {\n    -moz-border-radius: 8px;\n}\n\n.felamimail-node-statusbox-unread {\n    border-radius: 8px;\n    background-color: #D9E8FB;\n    color: black;\n    line-height: 16px;\n    margin-top: 1px;\n    padding: 0 5px;\n}\n\n.x-tree-selected .felamimail-node-statusbox-unread {\n    background-color: #FFFFFF;\n}\n\n.felamimail-node-accountfailure {\n    position: absolute;\n    right: 5px;\n    width: 16px;\n    height: 16px;\n    background-size: 16px 16px;\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_27___ + ");\n    margin-right: 10px;\n}\n\n/***** email composing dialog ******/\n\n/* disable grid headers */\n.felamimail-recipient-grid .x-grid3-hd-row, #felamimail-attachment-grid .x-grid3-hd-row {\n    display:none;\n}\n\n/*.felamimail-recipient-grid .x-grid3 {*/\n/*    background-color: #DFE8F6;*/\n/*}*/\n\n/*.felamimail-recipient-grid tr {*/\n/*    border-color: #DFE8F6;  */\n/*}*/\n\n.felamimail-body-blockquote {\n    margin: 5px 10px 0 3px;\n    padding-left: 10px;\n    border-left: 2px solid #000088;\n}\n\n.x-fmail-uploadrow .x-grid3-cell-inner {\n    background-image:url(" + ___CSS_LOADER_URL_REPLACEMENT_28___ + ");\n    background-repeat: no-repeat;\n    padding-left: 19px;\n}\n\n/*.felamimail-recipient-grid .x-grid3-td-type {*/\n/*    background-color: #DFE8F6;*/\n/*}*/\n\n/*.felamimail-recipient-grid .x-grid3-td-address {*/\n/*    background-color: white;*/\n/*    border: 1px solid #B5B8C8;*/\n/*}*/\n\n.felamimail-recipient-grid .x-grid3-td-address-editing {\n    background-color: transparent;\n    border: 0;\n}\n\n.felamimail-no-email {\n    color: #999999;\n}\n\n/*.felamimail-recipient-grid {*/\n/*    background-image:url(../../library/ExtJS/resources/images/default/grid/wait.gif);*/\n/*    background-repeat: no-repeat;*/\n/*}*/\n\n.x-grid3-cell-inner.x-grid3-col-address {\n    background-color: #fff;\n    display: flex;\n    align-items: center;\n}\n\n/***** sieve rules ******/\n\n.felamimail-sieverule-disabled {\n    color: #aaaaaa;\n}\n\n.felamimail-sieverule-conjunction {\n    color: #15428b;\n    font: bold 11px tahoma,arial,verdana,sans-serif;\n}\n\n.felamimail-is-external-recipient .x-grid3-col-address {\n    background: yellow;\n}\n\n/***** location renderer ******/\n.preview-panel-felamimail-filelocations,\n.felamimail-compose-info .x-panel-body {\n    background-color: #ffffaa;\n    border-radius: 5px;\n}\n\n.felamimail-location {\n    padding-right: 5px;\n    cursor: pointer;\n}\n\n.felamimail-location-icon {\n    background-size: 16px;\n    height: 16px;\n    width: 16px;\n    padding-left: 3px;\n    background-repeat: no-repeat;\n    display: inline-block;\n    vertical-align: middle;\n}\n\n.dark-mode .is_spam_suspicions td {\n    color: #7a5010;\n}\n\n.dark-mode .preview-panel-felamimail-body img {\n    filter: invert(1) hue-rotate(180deg);\n}\n\n.felamimail-message-title {\n    margin: 3px;\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n}\n\n.felamimail-message-title-row {\n    display: flex;\n    min-width: 16px;\n    align-items: center;\n    justify-content: flex-end;\n    padding-right: 16px;\n}\n\n.felamimail-message-title-text-small{\n    font-size: 12px;\n    padding: 0 5px;\n    white-space: nowrap;\n    overflow: hidden !important;\n    text-overflow: ellipsis;\n    opacity: 0.7;\n}\n\n.felamimail-message-title-text-medium {\n    font-size: 14px;\n    padding: 0 5px;\n    white-space: nowrap;\n    overflow: hidden !important;\n    text-overflow: ellipsis;\n}\n\n.felamimail-message-icon {\n    width: 16px;\n    height: 16px;\n    background-size: 16px 16px;\n    background-repeat: no-repeat;\n    margin: 0 2px;\n}\n", "",{"version":3,"sources":["webpack://./../../Felamimail/css/Felamimail.css"],"names":[],"mappings":"AAAA;IACI,mEAAoE;AACxE;;AAEA;IACI,mEAAyE;AAC7E;;AAEA;IACI,mEAAoE;AACxE;;AAEA;IACI,mEAA4E;AAChF;;AAEA;IACI,mEAAgF;AACpF;;AAEA;IACI,mEAA6E;AACjF;;AAEA;IACI,mEAAqE;AACzE;;AAEA;IACI,mEAA0E;AAC9E;;AAEA;IACI,mEAA0F;AAC9F;;AAEA;IACI,mEAAwE;AAC5E;;AAEA;IACI,mEAAqE;AACzE;;AAEA;IACI,oEAA2E;AAC/E;;AAEA;IACI,oEAAsE;AAC1E;;AAEA;IACI,oEAA+E;AACnF;;AAEA;IACI,mEAAwE;AAC5E;;AAEA,sBAAsB;;AAEtB;IACI,oEAA2E;AAC/E;;AAEA;IACI,oEAA6E;AACjF;;AAEA;IACI,oEAA+E;AACnF;;AAEA,kBAAkB;;AAElB;IACI,oEAAqE;AACzE;;;AAGA;IACI,oEAAoE;AACxE;;AAEA;IACI,oEAAoE;AACxE;;AAEA;IACI,oEAAmE;AACvE;;AAEA;IACI,YAAY;IACZ,WAAW;IACX,eAAe;IACf,yBAAyB;IACzB,kBAAkB;AACtB;;AAEA;IACI,uBAAuB;AAC3B;;AAEA,0BAA0B;;AAE1B;IACI,iBAAiB;AACrB;;AAEA;IACI,sEAA4E;IAC5E,8BAA8B;IAC9B,yBAAyB;AAC7B;;AAEA;IACI,cAAc;AAClB;;AAEA;IACI,6BAA6B;IAC7B,cAAc;AAClB;;AAEA;IACI,cAAc;AAClB;;AAEA;IACI,yBAAyB;AAC7B;;AAEA;IACI,WAAW;IACX,YAAY;IACZ,iBAAiB;AACrB;;AAEA;IACI,eAAe;AACnB;;AAEA,mBAAmB;;AAEnB;IACI,YAAY;IACZ,gBAAgB;AACpB;;AAEA;IACI,yBAAyB;IACzB,cAAc;IACd,YAAY;AAChB;;AAEA;IACI,aAAa;IACb,gBAAgB;AACpB;;AAEA;IACI,iBAAiB;IACjB,eAAe;IACf,iBAAiB;AACrB;;AAEA;IACI,kBAAkB;IAClB,aAAa;IACb,gBAAgB;IAChB,eAAe;AACnB;;AAEA;IACI,mBAAmB;AACvB;;AAEA;IACI,eAAe;IACf,YAAY;IACZ,yBAAyB;AAC7B;;AAEA,uEAAuE;AACvE;;IAEI,cAAc;IACd,eAAe;IACf,aAAa;AACjB;;AAEA;IACI,0BAA0B;IAC1B,iBAAiB;AACrB;AACA;IACI,2BAA2B;IAC3B,iBAAiB;AACrB;;AAEA;IACI,iBAAiB;IACjB,mBAAmB;AACvB;;AAEA;IACI,kBAAkB;IAClB,oBAAoB;AACxB;;AAEA;IACI,eAAe;IACf,yBAAyB;IACzB,SAAS;AACb;;AAEA;IACI,eAAe;IACf,gBAAgB;IAChB,iBAAiB;AACrB;;AAEA;IACI,YAAY;IACZ,yBAAyB;IACzB,aAAa;IACb,mBAAmB;IACnB,6BAA6B;IAC7B,mBAAmB;IACnB,eAAe;AACnB;;AAEA,0BAA0B;;AAE1B;IACI,aAAa;AACjB;;AAEA;IACI,iBAAiB;AACrB;;AAEA;IACI,yDAA0D;AAC9D;;AAEA;IACI,yDAA8D;AAClE;;AAEA;IACI,yDAAgE;AACpE;;AAEA;IACI,yDAA6D;AACjE;;AAEA;IACI,yDAA2D;AAC/D;;AAEA;IACI,yDAAoE;AACxE;;AAEA;IACI,yDAA8D;AAClE;;AAEA;IACI,0DAA4D;IAC5D,YAAY;AAChB;;AAEA;IACI,0DAA0D;AAC9D;;AAEA;IACI,kBAAkB;IAClB,kBAAkB;IAClB,QAAQ;AACZ;;AAEA;IACI,WAAW;IACX,YAAY;IACZ,eAAe;IACf,4BAA4B;IAC5B,mBAAmB;IACnB,iBAAiB;AACrB;;AAEA;IACI,wBAAwB;IACxB,WAAW;IACX,YAAY;IACZ,eAAe;AACnB;;AAEA;IACI,uBAAuB;AAC3B;;AAEA;IACI,kBAAkB;IAClB,yBAAyB;IACzB,YAAY;IACZ,iBAAiB;IACjB,eAAe;IACf,cAAc;AAClB;;AAEA;IACI,yBAAyB;AAC7B;;AAEA;IACI,kBAAkB;IAClB,UAAU;IACV,WAAW;IACX,YAAY;IACZ,0BAA0B;IAC1B,yDAA4D;IAC5D,kBAAkB;AACtB;;AAEA,oCAAoC;;AAEpC,yBAAyB;AACzB;IACI,YAAY;AAChB;;AAEA,wCAAwC;AACxC,iCAAiC;AACjC,IAAI;;AAEJ,kCAAkC;AAClC,+BAA+B;AAC/B,IAAI;;AAEJ;IACI,sBAAsB;IACtB,kBAAkB;IAClB,8BAA8B;AAClC;;AAEA;IACI,yDAAgF;IAChF,4BAA4B;IAC5B,kBAAkB;AACtB;;AAEA,gDAAgD;AAChD,iCAAiC;AACjC,IAAI;;AAEJ,mDAAmD;AACnD,+BAA+B;AAC/B,iCAAiC;AACjC,IAAI;;AAEJ;IACI,6BAA6B;IAC7B,SAAS;AACb;;AAEA;IACI,cAAc;AAClB;;AAEA,+BAA+B;AAC/B,wFAAwF;AACxF,oCAAoC;AACpC,IAAI;;AAEJ;IACI,sBAAsB;IACtB,aAAa;IACb,mBAAmB;AACvB;;AAEA,yBAAyB;;AAEzB;IACI,cAAc;AAClB;;AAEA;IACI,cAAc;IACd,+CAA+C;AACnD;;AAEA;IACI,kBAAkB;AACtB;;AAEA,+BAA+B;AAC/B;;IAEI,yBAAyB;IACzB,kBAAkB;AACtB;;AAEA;IACI,kBAAkB;IAClB,eAAe;AACnB;;AAEA;IACI,qBAAqB;IACrB,YAAY;IACZ,WAAW;IACX,iBAAiB;IACjB,4BAA4B;IAC5B,qBAAqB;IACrB,sBAAsB;AAC1B;;AAEA;IACI,cAAc;AAClB;;AAEA;IACI,oCAAoC;AACxC;;AAEA;IACI,WAAW;IACX,aAAa;IACb,mBAAmB;IACnB,8BAA8B;AAClC;;AAEA;IACI,aAAa;IACb,eAAe;IACf,mBAAmB;IACnB,yBAAyB;IACzB,mBAAmB;AACvB;;AAEA;IACI,eAAe;IACf,cAAc;IACd,mBAAmB;IACnB,2BAA2B;IAC3B,uBAAuB;IACvB,YAAY;AAChB;;AAEA;IACI,eAAe;IACf,cAAc;IACd,mBAAmB;IACnB,2BAA2B;IAC3B,uBAAuB;AAC3B;;AAEA;IACI,WAAW;IACX,YAAY;IACZ,0BAA0B;IAC1B,4BAA4B;IAC5B,aAAa;AACjB","sourcesContent":[".felamimail-action-encrypt {\n    background-image:url(../../images/icon-set/icon_lock.svg) !important;\n}\n\n.felamimail-action-decrypt {\n    background-image:url(../../images/icon-set/icon_lock_open.svg) !important;\n}\n\n.action_email_flag {\n    background-image:url(../../images/icon-set/icon_flag.svg) !important;\n}\n\n.action_email_reply {\n    background-image:url(../../images/icon-set/icon_email_answer.svg) !important;\n}\n\n.action_email_replyAll {\n    background-image:url(../../images/icon-set/icon_email_answer_all.svg) !important;\n}\n\n.action_email_forward {\n    background-image:url(../../images/icon-set/icon_email_forward.svg) !important;\n}\n\n.action_update_cache {\n    background-image:url(../../images/icon-set/icon_email.svg) !important;\n}\n\n.action_mark_read {\n    background-image:url(../../images/icon-set/icon_email_mark.svg) !important;\n}\n\n.action_approve_migration {\n    background-image:url(../../images/icon-set/icon_calendar_attendee_accepted.svg) !important;\n}\n\n.action_file {\n    background-image:url(../../images/icon-set/icon_download.svg) !important;\n}\n\n.FelamimailIconCls {\n    background-image:url(../../images/icon-set/icon_email.svg) !important;\n}\n\n.action_folder_emptytrash {\n    background-image:url(../../images/icon-set/icon_trash_empty.svg) !important;\n}\n\n.action_saveAsDraft {\n    background-image:url(../../images/icon-set/icon_drafts.svg) !important;\n}\n\n.action_saveAsTemplate {\n    background-image:url(../../images/icon-set/icon_folder_template.svg) !important;\n}\n\n.action_email_download {\n    background-image:url(../../images/icon-set/icon_download.svg) !important;\n}\n\n/* rule grid actions */\n\n.action_new_rule {\n    background-image:url(../../images/icon-set/icon_action_plus.svg) !important;\n}\n\n.action_move_up {\n    background-image:url(../../images/icon-set/icon_arrow_full_up.svg) !important;\n}\n\n.action_move_down {\n    background-image:url(../../images/icon-set/icon_arrow_full_down.svg) !important;\n}\n\n/* other actions */\n\n.felamimail-action-reading-confirmation {\n    background-image:url(../../images/icon-set/icon_tasks.svg) !important;\n}\n\n\n.felamimail-action-sieve-notification {\n    background-image:url(../../images/icon-set/icon_bell.svg) !important;\n}\n\n.felamimail-action-spam {\n    background-image:url(../../images/icon-set/icon_spam.svg) !important;\n}\n\n.felamimail-action-ham {\n    background-image:url(../../images/icon-set/icon_ham.svg) !important;\n}\n\n.felamimail-action {\n    padding: 5px;\n    margin: 5px;\n    cursor: pointer;\n    background-color: #D8A45B;\n    border-radius: 5px;\n}\n\n.felamimail-action:hover {\n    filter: brightness(1.2);\n}\n\n/***** flag classes ******/\n\n.flag_unread td{\n    font-weight: bold;\n}\n\n.unread-flag-dot {\n    -webkit-mask-image:url(../../images/icon-set/icon_sticky_dot.svg) !important;\n    -webkit-mask-repeat: no-repeat;\n    background-color: #4e79b2;\n}\n\n.flag_flagged {\n    color: #ff0000;\n}\n\n.flag_deleted td {\n    text-decoration: line-through;\n    color: #aaaaaa;\n}\n\n.is_spam_suspicions td {\n    color: #D8A45B;\n}\n\n.is_spam_suspicions_toolbar {\n    background-color: #D8A45B;\n}\n\n.FelamimailFlagIcon {\n    width: 12px;\n    height: 12px;\n    padding-left: 2px;\n}\n\n.FelamimailFlagIcon.MessageFileIcon {\n    cursor: pointer;\n}\n\n/**** preview *****/\n\n.preview-panel-felamimail {\n    padding: 5px;\n    overflow: hidden;\n}\n\n.preview-panel-felamimail-headers {\n    background-color: #dddddd;\n    overflow: auto;\n    padding: 5px;\n}\n\n.preview-panel-felamimail-header-row {\n    display: flex;\n    padding-top: 3px;\n}\n\n.preview-panel-felamimail-header-row-left {\n    font-weight: bold;\n    min-width: 50px;\n    text-align: right;\n}\n\n.preview-panel-felamimail-header-row-right {\n    padding-left: 10px;\n    display: flex;\n    text-align: left;\n    flex-wrap: wrap;\n}\n\n.preview-panel-felamimail-header-longtext {\n    white-space: nowrap;\n}\n\n.preview-panel-felamimail-attachments {\n    margin-top: 5px;\n    padding: 3px;\n    background-color: #e8e8e8;\n}\n\n/* note: el tyle of html areas underling textarea are copied to iframe*/\n.felamimail-message-body-html, \n.preview-panel-felamimail-body {\n    overflow: auto;\n    font-size: 13px;\n    padding: 10px;\n}\n\n.preview-panel-felamimail-body ul {\n    list-style: circle outside; \n    margin-left: 20px;\n}\n.preview-panel-felamimail-body ol {\n    list-style: decimal outside; \n    margin-left: 20px;\n}\n\n.preview-panel-felamimail-body strong { \n    font-weight: bold; \n    font-style: inherit;\n}\n\n.preview-panel-felamimail-body em { \n    font-style: italic; \n    font-weight: inherit;\n}\n\n.preview-panel-felamimail-body .x-form-textarea {\n    margin-top: 5px;\n    font: normal 13px courier;\n    border: 0;\n}\n\n.felamimail-edit-text-plain .x-form-textarea {\n    font-size: 13px;\n    padding-top: 5px;\n    padding-left: 5px;\n}\n\n.felamimail_spam_suspicions_toolbar {\n    padding: 5px;\n    background-color: #D8A45B;\n    display: flex;\n    flex-direction: row;\n    justify-content: space-around;\n    align-items: center;\n    flex-wrap: wrap;\n}\n\n/***** node classes ******/\n\n.felamimail-node-root {\n    display: none;\n}\n\n.felamimail-node-unread span {\n    font-weight: bold;\n}\n\n.felamimail-node-trash img.x-tree-node-icon {\n    background-image:url(../../images/icon-set/icon_trash.svg);\n}\n\n.felamimail-node-trash-full img.x-tree-node-icon {\n    background-image:url(../../images/icon-set/icon_trash_neg.svg);\n}\n\n.felamimail-node-sent img.x-tree-node-icon {\n    background-image:url(../../images/icon-set/icon_folder_sent.svg);\n}\n\n.felamimail-node-inbox img.x-tree-node-icon {\n    background-image:url(../../images/icon-set/icon_email_in.svg);\n}\n\n.felamimail-node-drafts img.x-tree-node-icon {\n    background-image:url(../../images/icon-set/icon_drafts.svg);\n}\n\n.felamimail-node-templates img.x-tree-node-icon {\n    background-image:url(../../images/icon-set/icon_folder_template.svg);\n}\n\n.felamimail-node-junk img.x-tree-node-icon {\n    background-image:url(../../images/icon-set/icon_trash_neg.svg);\n}\n\n.felamimail-node-unselectable img.x-tree-node-icon {\n    background-image: url(../../images/icon-set/icon_folder.svg);\n    opacity: 0.5;\n}\n\n.felamimail-node-account img.x-tree-node-icon {\n    background-image: url(../../images/icon-set/icon_base.svg);\n}\n\n.felamimail-node-statusbox {\n    padding-right: 5px;\n    position: absolute;\n    right: 0;\n}\n\n.felamimail-node-statusbox-progress {\n    width: 14px;\n    height: 14px;\n    margin-top: 2px;\n    background-repeat: no-repeat;\n    vertical-align: top;\n    margin-right: 5px;\n}\n\n.felamimail-node-statusbox-progress-pie {\n    background-size: contain;\n    width: 17px;\n    height: 17px;\n    margin-top: 1px;\n}\n\n.ext-gecko .felamimail-node-statusbox-unread {\n    -moz-border-radius: 8px;\n}\n\n.felamimail-node-statusbox-unread {\n    border-radius: 8px;\n    background-color: #D9E8FB;\n    color: black;\n    line-height: 16px;\n    margin-top: 1px;\n    padding: 0 5px;\n}\n\n.x-tree-selected .felamimail-node-statusbox-unread {\n    background-color: #FFFFFF;\n}\n\n.felamimail-node-accountfailure {\n    position: absolute;\n    right: 5px;\n    width: 16px;\n    height: 16px;\n    background-size: 16px 16px;\n    background-image:url(../../images/icon-set/icon_failure.svg);\n    margin-right: 10px;\n}\n\n/***** email composing dialog ******/\n\n/* disable grid headers */\n.felamimail-recipient-grid .x-grid3-hd-row, #felamimail-attachment-grid .x-grid3-hd-row {\n    display:none;\n}\n\n/*.felamimail-recipient-grid .x-grid3 {*/\n/*    background-color: #DFE8F6;*/\n/*}*/\n\n/*.felamimail-recipient-grid tr {*/\n/*    border-color: #DFE8F6;  */\n/*}*/\n\n.felamimail-body-blockquote {\n    margin: 5px 10px 0 3px;\n    padding-left: 10px;\n    border-left: 2px solid #000088;\n}\n\n.x-fmail-uploadrow .x-grid3-cell-inner {\n    background-image:url(../../library/ExtJS/resources/images/default/grid/wait.gif);\n    background-repeat: no-repeat;\n    padding-left: 19px;\n}\n\n/*.felamimail-recipient-grid .x-grid3-td-type {*/\n/*    background-color: #DFE8F6;*/\n/*}*/\n\n/*.felamimail-recipient-grid .x-grid3-td-address {*/\n/*    background-color: white;*/\n/*    border: 1px solid #B5B8C8;*/\n/*}*/\n\n.felamimail-recipient-grid .x-grid3-td-address-editing {\n    background-color: transparent;\n    border: 0;\n}\n\n.felamimail-no-email {\n    color: #999999;\n}\n\n/*.felamimail-recipient-grid {*/\n/*    background-image:url(../../library/ExtJS/resources/images/default/grid/wait.gif);*/\n/*    background-repeat: no-repeat;*/\n/*}*/\n\n.x-grid3-cell-inner.x-grid3-col-address {\n    background-color: #fff;\n    display: flex;\n    align-items: center;\n}\n\n/***** sieve rules ******/\n\n.felamimail-sieverule-disabled {\n    color: #aaaaaa;\n}\n\n.felamimail-sieverule-conjunction {\n    color: #15428b;\n    font: bold 11px tahoma,arial,verdana,sans-serif;\n}\n\n.felamimail-is-external-recipient .x-grid3-col-address {\n    background: yellow;\n}\n\n/***** location renderer ******/\n.preview-panel-felamimail-filelocations,\n.felamimail-compose-info .x-panel-body {\n    background-color: #ffffaa;\n    border-radius: 5px;\n}\n\n.felamimail-location {\n    padding-right: 5px;\n    cursor: pointer;\n}\n\n.felamimail-location-icon {\n    background-size: 16px;\n    height: 16px;\n    width: 16px;\n    padding-left: 3px;\n    background-repeat: no-repeat;\n    display: inline-block;\n    vertical-align: middle;\n}\n\n.dark-mode .is_spam_suspicions td {\n    color: #7a5010;\n}\n\n.dark-mode .preview-panel-felamimail-body img {\n    filter: invert(1) hue-rotate(180deg);\n}\n\n.felamimail-message-title {\n    margin: 3px;\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n}\n\n.felamimail-message-title-row {\n    display: flex;\n    min-width: 16px;\n    align-items: center;\n    justify-content: flex-end;\n    padding-right: 16px;\n}\n\n.felamimail-message-title-text-small{\n    font-size: 12px;\n    padding: 0 5px;\n    white-space: nowrap;\n    overflow: hidden !important;\n    text-overflow: ellipsis;\n    opacity: 0.7;\n}\n\n.felamimail-message-title-text-medium {\n    font-size: 14px;\n    padding: 0 5px;\n    white-space: nowrap;\n    overflow: hidden !important;\n    text-overflow: ellipsis;\n}\n\n.felamimail-message-icon {\n    width: 16px;\n    height: 16px;\n    background-size: 16px 16px;\n    background-repeat: no-repeat;\n    margin: 0 2px;\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 96334:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

"use strict";
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _js_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(87537);
/* harmony import */ var _js_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_js_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _js_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(23645);
/* harmony import */ var _js_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_js_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _js_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_js_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".x-form-trigger-plugin-wrap .x-form-twin-triggers img.x-form-trigger {\n    height: 18px;\n}\n\n/*.ext-webkit .x-small-editor .x-form-trigger-plugin-wrap .x-form-trigger.x-form-trigger-plugin,*/\n/*.x-small-editor .x-form-trigger-plugin-wrap .x-form-trigger.x-form-trigger-plugin,*/\n/*.x-toolbar-cell .x-form-trigger-plugin-wrap .x-form-twin-triggers img.x-form-trigger {*/\n/*    height: 16px;*/\n/*}*/\n\n.x-form-trigger.x-form-trigger-plugin {\n    background-repeat: no-repeat;\n    box-sizing: border-box;\n    background-size: 11px;\n    width: 17px;\n    height: 18px;\n    position: absolute;\n    right: 0px;\n    top: 4px;\n}\n\n.x-grid-editor .x-form-trigger.x-form-trigger-plugin {\n    background-position: 0px 3px !important;\n    top: -1px;\n}\n\n.x-form-trigger.x-form-trigger-plugin:hover {\n    background-position: 1px 1px !important;\n}\n\n.x-form-field-trigger-wrap + .x-form-trigger.x-form-trigger-plugin {\n    right: 17px;\n}\n\n.x-form-field-twin-trigger-wrap + .x-form-trigger.x-form-trigger-plugin {\n    right: 36px;\n}\n\n.x-item-disabled + .x-form-trigger.x-form-trigger-plugin {\n    opacity: .6;\n}\n\n.x-form-field-wrap.x-form-field-trigger-wrap.x-form-field-twin-trigger-wrap > .x-form-trigger-plugin {\n    /*right: 32px;*/\n    top: 0px;\n}\n\n.x-form-field-wrap.x-form-field-trigger-wrap.x-form-field-twin-trigger-wrap.x-form-trigger-plugin-wrap > img:nth-child(3) {\n    right: 32px;\n}\n\n.x-form-field-wrap.x-form-field-trigger-wrap.x-form-field-twin-trigger-wrap.x-form-trigger-plugin-wrap > img:nth-child(4) {\n    right: 48px;\n}\n\n.x-form-field-wrap.x-form-field-trigger-wrap.x-form-field-twin-trigger-wrap.x-form-trigger-plugin-wrap > img:nth-child(5) {\n    right: 64px;\n}\n\n.x-form-field-wrap.x-form-field-trigger-wrap.x-form-field-twin-trigger-wrap.x-form-trigger-plugin-wrap > img:nth-child(6) {\n    right: 80px;\n}\n\n.x-trigger-wrap-focus + .x-form-trigger.x-form-trigger-plugin {\n    border-color: #0062a7;\n}\n", "",{"version":3,"sources":["webpack://./../css/ux/form/FieldTriggerPlugin.css"],"names":[],"mappings":"AAAA;IACI,YAAY;AAChB;;AAEA,iGAAiG;AACjG,qFAAqF;AACrF,yFAAyF;AACzF,oBAAoB;AACpB,IAAI;;AAEJ;IACI,4BAA4B;IAC5B,sBAAsB;IACtB,qBAAqB;IACrB,WAAW;IACX,YAAY;IACZ,kBAAkB;IAClB,UAAU;IACV,QAAQ;AACZ;;AAEA;IACI,uCAAuC;IACvC,SAAS;AACb;;AAEA;IACI,uCAAuC;AAC3C;;AAEA;IACI,WAAW;AACf;;AAEA;IACI,WAAW;AACf;;AAEA;IACI,WAAW;AACf;;AAEA;IACI,eAAe;IACf,QAAQ;AACZ;;AAEA;IACI,WAAW;AACf;;AAEA;IACI,WAAW;AACf;;AAEA;IACI,WAAW;AACf;;AAEA;IACI,WAAW;AACf;;AAEA;IACI,qBAAqB;AACzB","sourcesContent":[".x-form-trigger-plugin-wrap .x-form-twin-triggers img.x-form-trigger {\n    height: 18px;\n}\n\n/*.ext-webkit .x-small-editor .x-form-trigger-plugin-wrap .x-form-trigger.x-form-trigger-plugin,*/\n/*.x-small-editor .x-form-trigger-plugin-wrap .x-form-trigger.x-form-trigger-plugin,*/\n/*.x-toolbar-cell .x-form-trigger-plugin-wrap .x-form-twin-triggers img.x-form-trigger {*/\n/*    height: 16px;*/\n/*}*/\n\n.x-form-trigger.x-form-trigger-plugin {\n    background-repeat: no-repeat;\n    box-sizing: border-box;\n    background-size: 11px;\n    width: 17px;\n    height: 18px;\n    position: absolute;\n    right: 0px;\n    top: 4px;\n}\n\n.x-grid-editor .x-form-trigger.x-form-trigger-plugin {\n    background-position: 0px 3px !important;\n    top: -1px;\n}\n\n.x-form-trigger.x-form-trigger-plugin:hover {\n    background-position: 1px 1px !important;\n}\n\n.x-form-field-trigger-wrap + .x-form-trigger.x-form-trigger-plugin {\n    right: 17px;\n}\n\n.x-form-field-twin-trigger-wrap + .x-form-trigger.x-form-trigger-plugin {\n    right: 36px;\n}\n\n.x-item-disabled + .x-form-trigger.x-form-trigger-plugin {\n    opacity: .6;\n}\n\n.x-form-field-wrap.x-form-field-trigger-wrap.x-form-field-twin-trigger-wrap > .x-form-trigger-plugin {\n    /*right: 32px;*/\n    top: 0px;\n}\n\n.x-form-field-wrap.x-form-field-trigger-wrap.x-form-field-twin-trigger-wrap.x-form-trigger-plugin-wrap > img:nth-child(3) {\n    right: 32px;\n}\n\n.x-form-field-wrap.x-form-field-trigger-wrap.x-form-field-twin-trigger-wrap.x-form-trigger-plugin-wrap > img:nth-child(4) {\n    right: 48px;\n}\n\n.x-form-field-wrap.x-form-field-trigger-wrap.x-form-field-twin-trigger-wrap.x-form-trigger-plugin-wrap > img:nth-child(5) {\n    right: 64px;\n}\n\n.x-form-field-wrap.x-form-field-trigger-wrap.x-form-field-twin-trigger-wrap.x-form-trigger-plugin-wrap > img:nth-child(6) {\n    right: 80px;\n}\n\n.x-trigger-wrap-focus + .x-form-trigger.x-form-trigger-plugin {\n    border-color: #0062a7;\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 23645:
/***/ ((module) => {

"use strict";


/*
  MIT License http://www.opensource.org/licenses/mit-license.php
  Author Tobias Koppers @sokra
*/
module.exports = function (cssWithMappingToString) {
  var list = [];

  // return the list of modules as css string
  list.toString = function toString() {
    return this.map(function (item) {
      var content = "";
      var needLayer = typeof item[5] !== "undefined";
      if (item[4]) {
        content += "@supports (".concat(item[4], ") {");
      }
      if (item[2]) {
        content += "@media ".concat(item[2], " {");
      }
      if (needLayer) {
        content += "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {");
      }
      content += cssWithMappingToString(item);
      if (needLayer) {
        content += "}";
      }
      if (item[2]) {
        content += "}";
      }
      if (item[4]) {
        content += "}";
      }
      return content;
    }).join("");
  };

  // import a list of modules into the list
  list.i = function i(modules, media, dedupe, supports, layer) {
    if (typeof modules === "string") {
      modules = [[null, modules, undefined]];
    }
    var alreadyImportedModules = {};
    if (dedupe) {
      for (var k = 0; k < this.length; k++) {
        var id = this[k][0];
        if (id != null) {
          alreadyImportedModules[id] = true;
        }
      }
    }
    for (var _k = 0; _k < modules.length; _k++) {
      var item = [].concat(modules[_k]);
      if (dedupe && alreadyImportedModules[item[0]]) {
        continue;
      }
      if (typeof layer !== "undefined") {
        if (typeof item[5] === "undefined") {
          item[5] = layer;
        } else {
          item[1] = "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {").concat(item[1], "}");
          item[5] = layer;
        }
      }
      if (media) {
        if (!item[2]) {
          item[2] = media;
        } else {
          item[1] = "@media ".concat(item[2], " {").concat(item[1], "}");
          item[2] = media;
        }
      }
      if (supports) {
        if (!item[4]) {
          item[4] = "".concat(supports);
        } else {
          item[1] = "@supports (".concat(item[4], ") {").concat(item[1], "}");
          item[4] = supports;
        }
      }
      list.push(item);
    }
  };
  return list;
};

/***/ }),

/***/ 61667:
/***/ ((module) => {

"use strict";


module.exports = function (url, options) {
  if (!options) {
    options = {};
  }
  if (!url) {
    return url;
  }
  url = String(url.__esModule ? url.default : url);

  // If url is already wrapped in quotes, remove them
  if (/^['"].*['"]$/.test(url)) {
    url = url.slice(1, -1);
  }
  if (options.hash) {
    url += options.hash;
  }

  // Should url be wrapped?
  // See https://drafts.csswg.org/css-values-3/#urls
  if (/["'() \t\n]|(%20)/.test(url) || options.needQuotes) {
    return "\"".concat(url.replace(/"/g, '\\"').replace(/\n/g, "\\n"), "\"");
  }
  return url;
};

/***/ }),

/***/ 87537:
/***/ ((module) => {

"use strict";


module.exports = function (item) {
  var content = item[1];
  var cssMapping = item[3];
  if (!cssMapping) {
    return content;
  }
  if (typeof btoa === "function") {
    var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(cssMapping))));
    var data = "sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(base64);
    var sourceMapping = "/*# ".concat(data, " */");
    return [content].concat([sourceMapping]).join("\n");
  }
  return [content].join("\n");
};

/***/ }),

/***/ 26911:
/***/ (() => {

// Production steps of ECMA-262, Edition 6, 22.1.2.1
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
if ( !Array.from ) {
  Array.from = (function () {
    var toStr = Object.prototype.toString;
    var isCallable = function (fn) {
      return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
    };
    var toInteger = function (value) {
      var number = Number(value);
      if (isNaN(number)) { return 0; }
      if (number === 0 || !isFinite(number)) { return number; }
      return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
    };
    var maxSafeInteger = Math.pow(2, 53) - 1;
    var toLength = function (value) {
      var len = toInteger(value);
      return Math.min(Math.max(len, 0), maxSafeInteger);
    };

    // The length property of the from method is 1.
    return function from(arrayLike/*, mapFn, thisArg */) {
      // 1. Let C be the this value.
      var C = this;

      // 2. Let items be ToObject(arrayLike).
      var items = Object(arrayLike);

      // 3. ReturnIfAbrupt(items).
      if (arrayLike == null) {
        throw new TypeError("Array.from requires an array-like object - not null or undefined");
      }

      // 4. If mapfn is undefined, then let mapping be false.
      var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
      var T;
      if (typeof mapFn !== 'undefined') {
        // 5. else
        // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
        if (!isCallable(mapFn)) {
          throw new TypeError('Array.from: when provided, the second argument must be a function');
        }

        // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
        if (arguments.length > 2) {
          T = arguments[2];
        }
      }

      // 10. Let lenValue be Get(items, "length").
      // 11. Let len be ToLength(lenValue).
      var len = toLength(items.length);

      // 13. If IsConstructor(C) is true, then
      // 13. a. Let A be the result of calling the [[Construct]] internal method 
      // of C with an argument list containing the single item len.
      // 14. a. Else, Let A be ArrayCreate(len).
      var A = isCallable(C) ? Object(new C(len)) : new Array(len);

      // 16. Let k be 0.
      var k = 0;
      // 17. Repeat, while k < len… (also steps a - h)
      var kValue;
      while (k < len) {
        kValue = items[k];
        if (mapFn) {
          A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
        } else {
          A[k] = kValue;
        }
        k += 1;
      }
      // 18. Let putStatus be Put(A, "length", len, true).
      A.length = len;
      // 20. Return A.
      return A;
    };
  }());
}


/***/ }),

/***/ 32622:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {

// node core modules

// 3rd party modules

// local modules
const { responseByMode } = __webpack_require__(82848);

const delay = (t, v) => new Promise(resolve => setTimeout(resolve.bind(null, v), t));

/**
 * Execute promise which is provided as a separate function in order to make a clean/original promise call.
 * If there was an error, log it and return rejected promise execution so it might be used again for retrial
 *
 * @param {Object} [logger]     Custom logger that can be provided
 * @returns {function(*=): Promise<T | {rejectedPromise: *, error: any, status: string}>}
 */
const reflectFactory = (logger) =>
  executePromise => {
    if (typeof executePromise === 'function') {
      return executePromise()
        .then(data => ({ data, status: 'resolved' }))
        .catch((error) => {
          logger && logger.error('Reflect promise error: ', error);
          return { error, status: 'rejected', rejectedPromise: executePromise };
        });
    }

    return executePromise.then(data => ({ data, status: 'resolved' }))
      .catch((error) => {
        logger && logger.error('Reflect promise error: ', error);
        return { error, status: 'rejected' };
      });
  };

/**
 * This method resolves all promises without failing, logs an error if there is logger provided and displays a final status of executed promise.
 * It can be "resolved" or "rejected". If "rejected", it also provides a reference to rejected function that returns a promise.
 * The purpose is to continue with the execution of all promises even if some of them were rejected
 *
 * @param {Array} listOfPromises      Array of promises, which are mapped into reflect function
 * @param {Object} [logger]           Custom logger that can be provided
 * @returns {Promise<any[]>}
 */
const reflectAllPromises = (listOfPromises, logger) => {
  const reflect = reflectFactory(logger);
  return Promise.all(listOfPromises.map(reflect));
};

/**
 * This method runs promises in parallel, and collects all rejected promises. Once all rejected promises are collected,
 * the retry mechanism kicks-in and retries rejected promises (also in parallel) until there are no more attempts.
 * If maximum retry attempts is exceeded, the method will return all rejected promises so the caller may try to
 * use different strategy for resolving them.
 *
 * @param {Array} listOfPromises              A list of functions that return a promise
 * @param {Object} retryParams                A configuration object, relevant for retrying mechanism
 * @param {Number} retryParams.maxAttempts    Maximum number of attempts by retry mechanism.
 *                                            If not provided, there will be no retries
 * @param {Number} retryParams.delay          Delay the method execution by certain period of time. The default value
 *                                            is 1000ms
 * @param {Object} [logger]                   Custom logger that can be provided
 * @returns {Promise<any[]>}
 */
const retryAllRejectedPromises = async (listOfPromises, retryParams, logger) => {
  const allPromises = await reflectAllPromises(listOfPromises, logger);

  const rejectedPromises = (allPromises || [])
    .filter(singlePromise => singlePromise.status === 'rejected')
    .map(({ rejectedPromise }) => rejectedPromise);

  if (rejectedPromises && rejectedPromises.length) {
    if (retryParams.maxAttempts > 0) {
      logger && logger.debug('Trying to run [%d] rejected promise(s), attempts left %d',
        rejectedPromises.length, retryParams.maxAttempts);

      retryParams.maxAttempts -= 1;

      await delay(retryParams.delay || 1000);
      return retryAllRejectedPromises(rejectedPromises, retryParams, logger);
    }

    logger && logger.debug('Failed to execute [%d] promise(s)', rejectedPromises.length);
    return rejectedPromises;
  }

  return [];
};

/**
 * This method runs promises in parallel, and collects all rejected promises. Once all rejected promises are collected,
 * the retry mechanism kicks-in and retries rejected promises in NEXT the event loop (also in parallel) until there are no more attempts.
 * This also means that the execution will not stop, and the method will always resolve to true!
 * If maximum retry attempts is exceeded, the method will log an error message about number of rejected promise executions,
 * but it will NOT return rejected promises.
 *
 * @param {Array} listOfPromises              A list of functions that return a promise
 * @param {Object} retryParams                A configuration object, relevant for retrying mechanism
 * @param {Number} retryParams.maxAttempts    Maximum number of attempts by retry mechanism.
 *                                            If not provided, there will be no retries
 * @param {Number} retryParams.delay          Delay the method execution by certain period of time. The default value
 *                                            is 1000ms
 * @param {Object} [logger]                   Custom logger that can be provided
 * @returns {Promise<void>}
 */
const reflectAndRetryAllRejectedPromises = async (listOfPromises, retryParams, logger) => {
  const allPromises = await reflectAllPromises(listOfPromises, logger);

  const rejectedPromises = (allPromises || [])
    .filter(singlePromise => singlePromise.status === 'rejected')
    .map(({ rejectedPromise }) => rejectedPromise);

  if (rejectedPromises && rejectedPromises.length) {
    if (retryParams.maxAttempts > 0) {
      setTimeout(async () => {
        logger && logger.debug('Trying to run [%d] rejected promise(s), attempts left %d',
          rejectedPromises.length, retryParams.maxAttempts);
        retryParams.maxAttempts -= 1;

        await reflectAndRetryAllRejectedPromises(rejectedPromises, retryParams, logger);
      }, retryParams.delay || 1000);
    } else {
      logger && logger.debug('Failed to execute [%d] promise(s)', rejectedPromises.length);
    }
  }
};

/**
 * This method is batching list of promises. The batches are invoked with `reflectAllPromises`, so both resolved
 * and rejected results are kept. Based on `responseMode` you can receive different data.
 *
 * @param {Number} maxBatchSize                   Number of batches to be invoked in parallel
 * @param {Number} delayInMs                      Delay between batch execution
 * @param {(
 * 'ONLY_RESOLVED' | 'ONLY_REJECTED' |
 *  'ALL' | 'ALL_SPLIT'
 * )} responseMode                                Different mode will provide different responses,
 *                                                depending on caller requirements.
 * @returns {Function}
 */
const batchPromises = ({ maxBatchSize = 2, delayInMs = 1000, responseMode = 'ALL' }) =>
  /**
   * @param {Array} promises        List of promises to batch
   * @returns {Promise<any[]>}
   */
  async promises => {
    if (!Object.keys(responseByMode).includes(responseMode)) {
      throw new Error('Invalid responseMode provided');
    }
    let allPromises = [];
    const resolvedPromises = [];
    const rejectedPromises = [];

    // prepare the batches
    const promiseBatches = promises.reduce((result, singlePromise) => {
      if (result[result.length - 1].length < maxBatchSize) {
        result[result.length - 1].push(singlePromise);
        return result;
      }

      result[result.length] = [singlePromise];
      return result;
    }, [[]]);

    for (const promiseBatch of promiseBatches) {
      const executedPromises = await reflectAllPromises(promiseBatch);
      executedPromises.forEach(({ status, data, error }) =>
        status === 'rejected'
          ? rejectedPromises.push(error)
          : resolvedPromises.push(data));

      allPromises.push(...executedPromises);
      await delay(delayInMs);
    }

    allPromises = allPromises.map(({ data, error }) => data || error);
    return responseByMode[responseMode]({ resolvedPromises, rejectedPromises, allPromises });
  };

/**
 * For provided list of promises and `raceTimeoutInMs` method will execute promises in parallel and wait for the
 * response for certain amount of time. After time is out (based on `raceTimeoutInMs`) caller may decide what response
 * to receive. By default method returns only promises that "won" the timeout race. Otherwise it can return all
 * reflected promises including promises that "lost" the race.
 *
 * @param {Array} listOfPromises                  A list of promises to race against time
 * @param {Number} [raceTimeoutInMs]              The time in milliseconds that each promise will race against
 * @param {String} [raceTimeoutMessage]           A custom message provided for promise which "lost" the race
 * @param {(
 * 'ONLY_RESOLVED' | 'ONLY_WINNER_PROMISES' | 'ALL'
 * )} responseMode                                Different mode will provide different responses,
 *                                                depending on caller requirements.
 * @returns {Promise<any[]>}
 */
const racePromisesWithTime = async ({ listOfPromises, raceTimeoutInMs = 1000, raceTimeoutMessage = 'Promise timeout limit reached', responseMode = 'ONLY_WINNER_PROMISES' }) => {
  const allPromises = await reflectAllPromises(listOfPromises.map(promise =>
    Promise.race([promise, delay(raceTimeoutInMs, { timeoutPromise: promise, raceTimeoutMessage })]))
  );

  const resolvedPromises = allPromises
    .map(({ data }) => data)
    .filter(Boolean);

  const winnerPromises = resolvedPromises
    .filter(data => data && !data.timeoutPromise);

  return responseByMode[responseMode]({ resolvedPromises, winnerPromises, allPromises });
};

module.exports = {
  reflectAllPromises,
  retryAllRejectedPromises,
  reflectAndRetryAllRejectedPromises,
  batchPromises,
  racePromisesWithTime,
  delay
};


/***/ }),

/***/ 82848:
/***/ ((module) => {

// node core modules

// 3rd party modules

// local modules

const responseByMode = {
  ALL: ({ allPromises }) => allPromises,
  ONLY_RESOLVED: ({ resolvedPromises }) => resolvedPromises,
  ONLY_WINNER_PROMISES: ({ winnerPromises }) => winnerPromises,
  ONLY_REJECTED: ({ rejectedPromises }) => rejectedPromises,
  ALL_SPLIT: ({ resolvedPromises, rejectedPromises }) => [resolvedPromises, rejectedPromises]
};

module.exports = {
  responseByMode
};


/***/ }),

/***/ 58940:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _Tinebase_js_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(93379);
/* harmony import */ var _Tinebase_js_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_Tinebase_js_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _Tinebase_js_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7795);
/* harmony import */ var _Tinebase_js_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_Tinebase_js_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _Tinebase_js_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(90569);
/* harmony import */ var _Tinebase_js_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_Tinebase_js_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _Tinebase_js_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(3565);
/* harmony import */ var _Tinebase_js_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_Tinebase_js_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var _Tinebase_js_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(19216);
/* harmony import */ var _Tinebase_js_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_Tinebase_js_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var _Tinebase_js_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(44589);
/* harmony import */ var _Tinebase_js_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_Tinebase_js_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__);
/* harmony import */ var _Tinebase_js_node_modules_css_loader_dist_cjs_js_Felamimail_css__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(17457);

      
      
      
      
      
      
      
      
      

var options = {};

options.styleTagTransform = (_Tinebase_js_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default());
options.setAttributes = (_Tinebase_js_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default());

      options.insert = _Tinebase_js_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default().bind(null, "head");
    
options.domAPI = (_Tinebase_js_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default());
options.insertStyleElement = (_Tinebase_js_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default());

var update = _Tinebase_js_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_Tinebase_js_node_modules_css_loader_dist_cjs_js_Felamimail_css__WEBPACK_IMPORTED_MODULE_6__/* ["default"] */ .Z, options);




       /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_Tinebase_js_node_modules_css_loader_dist_cjs_js_Felamimail_css__WEBPACK_IMPORTED_MODULE_6__/* ["default"] */ .Z && _Tinebase_js_node_modules_css_loader_dist_cjs_js_Felamimail_css__WEBPACK_IMPORTED_MODULE_6__/* ["default"].locals */ .Z.locals ? _Tinebase_js_node_modules_css_loader_dist_cjs_js_Felamimail_css__WEBPACK_IMPORTED_MODULE_6__/* ["default"].locals */ .Z.locals : undefined);


/***/ }),

/***/ 93379:
/***/ ((module) => {

"use strict";


var stylesInDOM = [];

function getIndexByIdentifier(identifier) {
  var result = -1;

  for (var i = 0; i < stylesInDOM.length; i++) {
    if (stylesInDOM[i].identifier === identifier) {
      result = i;
      break;
    }
  }

  return result;
}

function modulesToDom(list, options) {
  var idCountMap = {};
  var identifiers = [];

  for (var i = 0; i < list.length; i++) {
    var item = list[i];
    var id = options.base ? item[0] + options.base : item[0];
    var count = idCountMap[id] || 0;
    var identifier = "".concat(id, " ").concat(count);
    idCountMap[id] = count + 1;
    var indexByIdentifier = getIndexByIdentifier(identifier);
    var obj = {
      css: item[1],
      media: item[2],
      sourceMap: item[3],
      supports: item[4],
      layer: item[5]
    };

    if (indexByIdentifier !== -1) {
      stylesInDOM[indexByIdentifier].references++;
      stylesInDOM[indexByIdentifier].updater(obj);
    } else {
      var updater = addElementStyle(obj, options);
      options.byIndex = i;
      stylesInDOM.splice(i, 0, {
        identifier: identifier,
        updater: updater,
        references: 1
      });
    }

    identifiers.push(identifier);
  }

  return identifiers;
}

function addElementStyle(obj, options) {
  var api = options.domAPI(options);
  api.update(obj);

  var updater = function updater(newObj) {
    if (newObj) {
      if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) {
        return;
      }

      api.update(obj = newObj);
    } else {
      api.remove();
    }
  };

  return updater;
}

module.exports = function (list, options) {
  options = options || {};
  list = list || [];
  var lastIdentifiers = modulesToDom(list, options);
  return function update(newList) {
    newList = newList || [];

    for (var i = 0; i < lastIdentifiers.length; i++) {
      var identifier = lastIdentifiers[i];
      var index = getIndexByIdentifier(identifier);
      stylesInDOM[index].references--;
    }

    var newLastIdentifiers = modulesToDom(newList, options);

    for (var _i = 0; _i < lastIdentifiers.length; _i++) {
      var _identifier = lastIdentifiers[_i];

      var _index = getIndexByIdentifier(_identifier);

      if (stylesInDOM[_index].references === 0) {
        stylesInDOM[_index].updater();

        stylesInDOM.splice(_index, 1);
      }
    }

    lastIdentifiers = newLastIdentifiers;
  };
};

/***/ }),

/***/ 90569:
/***/ ((module) => {

"use strict";


var memo = {};
/* istanbul ignore next  */

function getTarget(target) {
  if (typeof memo[target] === "undefined") {
    var styleTarget = document.querySelector(target); // Special case to return head of iframe instead of iframe itself

    if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {
      try {
        // This will throw an exception if access to iframe is blocked
        // due to cross-origin restrictions
        styleTarget = styleTarget.contentDocument.head;
      } catch (e) {
        // istanbul ignore next
        styleTarget = null;
      }
    }

    memo[target] = styleTarget;
  }

  return memo[target];
}
/* istanbul ignore next  */


function insertBySelector(insert, style) {
  var target = getTarget(insert);

  if (!target) {
    throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");
  }

  target.appendChild(style);
}

module.exports = insertBySelector;

/***/ }),

/***/ 19216:
/***/ ((module) => {

"use strict";


/* istanbul ignore next  */
function insertStyleElement(options) {
  var element = document.createElement("style");
  options.setAttributes(element, options.attributes);
  options.insert(element, options.options);
  return element;
}

module.exports = insertStyleElement;

/***/ }),

/***/ 3565:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {

"use strict";


/* istanbul ignore next  */
function setAttributesWithoutAttributes(styleElement) {
  var nonce =  true ? __webpack_require__.nc : 0;

  if (nonce) {
    styleElement.setAttribute("nonce", nonce);
  }
}

module.exports = setAttributesWithoutAttributes;

/***/ }),

/***/ 7795:
/***/ ((module) => {

"use strict";


/* istanbul ignore next  */
function apply(styleElement, options, obj) {
  var css = "";

  if (obj.supports) {
    css += "@supports (".concat(obj.supports, ") {");
  }

  if (obj.media) {
    css += "@media ".concat(obj.media, " {");
  }

  var needLayer = typeof obj.layer !== "undefined";

  if (needLayer) {
    css += "@layer".concat(obj.layer.length > 0 ? " ".concat(obj.layer) : "", " {");
  }

  css += obj.css;

  if (needLayer) {
    css += "}";
  }

  if (obj.media) {
    css += "}";
  }

  if (obj.supports) {
    css += "}";
  }

  var sourceMap = obj.sourceMap;

  if (sourceMap && typeof btoa !== "undefined") {
    css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */");
  } // For old IE

  /* istanbul ignore if  */


  options.styleTagTransform(css, styleElement, options.options);
}

function removeStyleElement(styleElement) {
  // istanbul ignore if
  if (styleElement.parentNode === null) {
    return false;
  }

  styleElement.parentNode.removeChild(styleElement);
}
/* istanbul ignore next  */


function domAPI(options) {
  var styleElement = options.insertStyleElement(options);
  return {
    update: function update(obj) {
      apply(styleElement, options, obj);
    },
    remove: function remove() {
      removeStyleElement(styleElement);
    }
  };
}

module.exports = domAPI;

/***/ }),

/***/ 44589:
/***/ ((module) => {

"use strict";


/* istanbul ignore next  */
function styleTagTransform(css, styleElement) {
  if (styleElement.styleSheet) {
    styleElement.styleSheet.cssText = css;
  } else {
    while (styleElement.firstChild) {
      styleElement.removeChild(styleElement.firstChild);
    }

    styleElement.appendChild(document.createTextNode(css));
  }
}

module.exports = styleTagTransform;

/***/ }),

/***/ 91738:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMzUuNiwyMWMwLTIuNi0wLjctNS4xLTItNy4zcy0zLjEtNC01LjMtNS4zcy00LjctMi03LjMtMnMtNS4xLDAuNy03LjMsMnMtNCwzLjEtNS4zLDUuM3MtMiw0LjctMiw3LjNzMC43LDUuMSwyLDcuMwoJczMuMSw0LDUuMyw1LjNzNC43LDIsNy4zLDJzNS4xLTAuNyw3LjMtMnM0LTMuMSw1LjMtNS4zUzM1LjYsMjMuNiwzNS42LDIxeiBNMzgsMjFjMCwzLjEtMC44LDUuOS0yLjMsOC41Yy0xLjUsMi42LTMuNiw0LjctNi4yLDYuMgoJQzI2LjksMzcuMiwyNC4xLDM4LDIxLDM4Yy0zLjEsMC01LjktMC44LTguNS0yLjNjLTIuNi0xLjUtNC43LTMuNi02LjItNi4yUzQsMjQuMSw0LDIxYzAtMy4xLDAuOC01LjksMi4zLTguNXMzLjYtNC43LDYuMi02LjIKCVMxNy45LDQsMjEsNGMzLjEsMCw1LjksMC44LDguNSwyLjNzNC43LDMuNiw2LjIsNi4yQzM3LjIsMTUuMSwzOCwxNy45LDM4LDIxeiBNMjEuNywzMC44aC0xLjRjLTAuMiwwLTAuNC0wLjEtMC41LTAuMgoJYy0wLjEtMC4xLTAuMi0wLjMtMC4yLTAuNXYtNy43aC03LjdjLTAuMiwwLTAuNC0wLjEtMC41LTAuMmMtMC4xLTAuMS0wLjItMC4zLTAuMi0wLjV2LTEuNGMwLTAuMiwwLjEtMC40LDAuMi0wLjUKCWMwLjEtMC4xLDAuMy0wLjIsMC41LTAuMmg3Ljd2LTcuN2MwLTAuMiwwLjEtMC40LDAuMi0wLjVjMC4xLTAuMSwwLjMtMC4yLDAuNS0wLjJoMS40YzAuMiwwLDAuNCwwLjEsMC41LDAuMgoJYzAuMSwwLjEsMC4yLDAuMywwLjIsMC41djcuN2g3LjdjMC4yLDAsMC40LDAuMSwwLjUsMC4yYzAuMSwwLjEsMC4yLDAuMywwLjIsMC41djEuNGMwLDAuMi0wLjEsMC40LTAuMiwwLjVzLTAuMywwLjItMC41LDAuMmgtNy43Cgl2Ny43YzAsMC4yLTAuMSwwLjQtMC4yLDAuNVMyMS45LDMwLjgsMjEuNywzMC44eiIvPgo8L3N2Zz4K";

/***/ }),

/***/ 70423:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMTkuOSwyOC40TDcuNSwxNkM3LjIsMTUuNyw3LDE1LjMsNywxNC44YzAtMC41LDAuMi0wLjksMC41LTEuMkM3LjksMTMuMiw4LjMsMTMsOC44LDEzaDI0LjhjMC41LDAsMC45LDAuMiwxLjIsMC41CgljMC40LDAuNCwwLjUsMC44LDAuNSwxLjJjMCwwLjUtMC4yLDAuOS0wLjUsMS4yTDIyLjQsMjguNGMtMC40LDAuNC0wLjgsMC41LTEuMiwwLjVDMjAuNywyOC45LDIwLjMsMjguOCwxOS45LDI4LjR6Ii8+Cjwvc3ZnPgo=";

/***/ }),

/***/ 11404:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMjEuMiwxM2MwLjUsMCwwLjksMC4yLDEuMiwwLjVsMTIuNCwxMi40YzAuNCwwLjQsMC41LDAuOCwwLjUsMS4yYzAsMC41LTAuMiwwLjktMC41LDEuMmMtMC40LDAuNC0wLjgsMC41LTEuMiwwLjVIOC44CgljLTAuNSwwLTAuOS0wLjItMS4yLTAuNUM3LjIsMjguMSw3LDI3LjcsNywyNy4yYzAtMC41LDAuMi0wLjksMC41LTEuMmwxMi40LTEyLjRDMjAuMywxMy4yLDIwLjcsMTMsMjEuMiwxM3oiLz4KPC9zdmc+Cg==";

/***/ }),

/***/ 56310:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMzMuNSwyMy4zdjEwLjZjMCwwLjQtMC4xLDAuNy0wLjQsMWMtMC4zLDAuMy0wLjYsMC40LTEsMC40aC04LjV2LTguNUgxOHY4LjVIOS41Yy0wLjQsMC0wLjctMC4xLTEtMC40CgljLTAuMy0wLjMtMC40LTAuNi0wLjQtMVYyMy4zYzAsMCwwLDAsMC0wLjFjMCwwLDAtMC4xLDAtMC4xbDEyLjctMTAuNWwxMi43LDEwLjVDMzMuNSwyMy4yLDMzLjUsMjMuMywzMy41LDIzLjN6IE0zOC41LDIxLjgKCWwtMS40LDEuNmMtMC4xLDAuMS0wLjMsMC4yLTAuNSwwLjJoLTAuMWMtMC4yLDAtMC4zLTAuMS0wLjUtMC4yTDIwLjgsMTAuOEw1LjUsMjMuNWMtMC4yLDAuMS0wLjQsMC4yLTAuNSwwLjIKCWMtMC4yLDAtMC4zLTAuMS0wLjUtMC4ybC0xLjQtMS42QzMsMjEuNywzLDIxLjUsMywyMS4zYzAtMC4yLDAuMS0wLjQsMC4yLTAuNUwxOS4xLDcuNmMwLjUtMC40LDEtMC42LDEuNy0wLjZzMS4yLDAuMiwxLjcsMC42CglsNS40LDQuNVY3LjhjMC0wLjIsMC4xLTAuNCwwLjItMC41YzAuMS0wLjEsMC4zLTAuMiwwLjUtMC4yaDQuMmMwLjIsMCwwLjQsMC4xLDAuNSwwLjJjMC4xLDAuMSwwLjIsMC4zLDAuMiwwLjV2OWw0LjgsNAoJYzAuMSwwLjEsMC4yLDAuMywwLjIsMC41UzM4LjYsMjEuNywzOC41LDIxLjh6Ii8+Cjwvc3ZnPgo=";

/***/ }),

/***/ 55759:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMzYuNiwzMC43YzAsMC43LTAuMiwxLjItMC43LDEuN2MtMC41LDAuNS0xLDAuNy0xLjcsMC43aC04LjVjMCwxLjMtMC41LDIuNS0xLjQsMy40Yy0wLjksMC45LTIuMSwxLjQtMy40LDEuNAoJYy0xLjMsMC0yLjUtMC41LTMuNC0xLjRjLTAuOS0wLjktMS40LTIuMS0xLjQtMy40SDcuNGMtMC43LDAtMS4yLTAuMi0xLjctMC43Yy0wLjUtMC41LTAuNy0xLTAuNy0xLjdjMC42LTAuNSwxLjItMS4xLDEuNy0xLjcKCWMwLjUtMC42LDEuMS0xLjMsMS42LTIuM2MwLjYtMC45LDEtMS45LDEuNC0zczAuNy0yLjQsMC45LTMuOWMwLjItMS41LDAuNC0zLjIsMC40LTQuOWMwLTEuOSwwLjctMy43LDIuMi01LjRzMy40LTIuNyw1LjgtMwoJQzE5LDYuMywxOSw2LjEsMTksNS44YzAtMC41LDAuMi0wLjksMC41LTEuM0MxOS44LDQuMiwyMC4zLDQsMjAuOCw0czAuOSwwLjIsMS4zLDAuNXMwLjUsMC44LDAuNSwxLjNjMCwwLjMtMC4xLDAuNS0wLjIsMC43CgljMi40LDAuNCw0LjMsMS40LDUuOCwzczIuMiwzLjQsMi4yLDUuNGMwLDEuOCwwLjEsMy40LDAuNCw0LjljMC4yLDEuNSwwLjYsMi44LDAuOSwzLjljMC40LDEuMSwwLjksMi4xLDEuNCwzCgljMC42LDAuOSwxLjEsMS43LDEuNiwyLjNDMzUuNCwyOS42LDM1LjksMzAuMiwzNi42LDMwLjd6Ii8+Cjwvc3ZnPgo=";

/***/ }),

/***/ 24343:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMzIuMSw3SDEwLjJDOC41LDcsNyw4LjUsNywxMC4ydjIxLjljMCwxLjgsMS41LDMuMiwzLjIsMy4yaDIxLjljMS44LDAsMy4yLTEuNSwzLjItMy4yVjEwLjJDMzUuNCw4LjUsMzMuOSw3LDMyLjEsN3oKCSBNMzIuNSwzMS4xYzAsMC44LTAuNiwxLjQtMS40LDEuNEgxMS4zYy0wLjgsMC0xLjQtMC42LTEuNC0xLjRWMTEuM2MwLTAuOCwwLjYtMS40LDEuNC0xLjRoMTkuOGMwLjgsMCwxLjQsMC42LDEuNCwxLjRWMzEuMXoKCSBNMzAuMywxNS4zYzAuMiwwLjIsMC40LDAuNSwwLjQsMC45cy0wLjEsMC42LTAuNCwwLjlMMTkuMiwyOC4yYy0wLjIsMC4yLTAuNSwwLjQtMC45LDAuNHMtMC42LTAuMS0wLjktMC40bC01LjMtNS4zCgljLTAuMi0wLjItMC40LTAuNS0wLjQtMC45YzAtMC4zLDAuMS0wLjYsMC40LTAuOWwwLjMtMC4zYzAuMi0wLjIsMC41LTAuNCwwLjktMC40YzAuMywwLDAuNiwwLjEsMC45LDAuNGw0LDRsOS45LTkuOQoJYzAuMi0wLjIsMC41LTAuNCwwLjktMC40YzAuMywwLDAuNiwwLjEsMC45LDAuNEwzMC4zLDE1LjN6Ii8+Cjwvc3ZnPgo=";

/***/ }),

/***/ 53421:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMjguNywzMS45YzAtMC4zLTAuMS0wLjYtMC4zLTAuOHMtMC41LTAuMy0wLjgtMC4zYy0wLjMsMC0wLjYsMC4xLTAuOCwwLjNzLTAuMywwLjUtMC4zLDAuOHMwLjEsMC42LDAuMywwLjgKCWMwLjIsMC4yLDAuNSwwLjMsMC44LDAuM2MwLjMsMCwwLjYtMC4xLDAuOC0wLjNDMjguNiwzMi41LDI4LjcsMzIuMywyOC43LDMxLjl6IE0zMy4yLDMxLjljMC0wLjMtMC4xLTAuNi0wLjMtMC44cy0wLjUtMC4zLTAuOC0wLjMKCXMtMC42LDAuMS0wLjgsMC4zcy0wLjMsMC41LTAuMywwLjhzMC4xLDAuNiwwLjMsMC44YzAuMiwwLjIsMC41LDAuMywwLjgsMC4zczAuNi0wLjEsMC44LTAuM0MzMy4xLDMyLjUsMzMuMiwzMi4zLDMzLjIsMzEuOXoKCSBNMzUuNSwyOHY1LjdjMCwwLjUtMC4yLDAuOS0wLjUsMS4ycy0wLjcsMC41LTEuMiwwLjVINy43Yy0wLjUsMC0wLjktMC4yLTEuMi0wLjVDNi4yLDM0LjUsNiwzNC4xLDYsMzMuNlYyOGMwLTAuNSwwLjItMC45LDAuNS0xLjIKCWMwLjMtMC4zLDAuNy0wLjUsMS4yLTAuNWg3LjZjMC4yLDAuNywwLjcsMS4yLDEuMiwxLjZjMC42LDAuNCwxLjIsMC42LDIsMC42SDIzYzAuNywwLDEuNC0wLjIsMi0wLjZzMS0xLDEuMi0xLjZoNy42CgljMC41LDAsMC45LDAuMiwxLjIsMC41QzM1LjMsMjcuMSwzNS41LDI3LjUsMzUuNSwyOHogTTExLjgsMTYuOGMwLjItMC41LDAuNS0wLjcsMS0wLjdoNC41VjguMWMwLTAuMywwLjEtMC42LDAuMy0wLjgKCUMxNy45LDcuMSwxOC4yLDcsMTguNSw3SDIzYzAuMywwLDAuNiwwLjEsMC44LDAuM2MwLjIsMC4yLDAuMywwLjUsMC4zLDAuOHY3LjloNC41YzAuNSwwLDAuOCwwLjIsMSwwLjdjMC4yLDAuNSwwLjEsMC45LTAuMiwxLjIKCWwtNy45LDcuOWMtMC4yLDAuMi0wLjUsMC4zLTAuOCwwLjNzLTAuNi0wLjEtMC44LTAuM0wxMiwxOEMxMS42LDE3LjYsMTEuNiwxNy4yLDExLjgsMTYuOHoiLz4KPC9zdmc+Cg==";

/***/ }),

/***/ 90417:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMzQuOSwzMC44VjE2LjdjMC0wLjUtMC4yLTEtMC42LTEuNHMtMC44LTAuNi0xLjQtMC42SDE4LjhjLTAuNSwwLTEtMC4yLTEuNC0wLjZjLTAuNC0wLjQtMC42LTAuOC0wLjYtMS40di0xLjMKCWMwLTAuNS0wLjItMS0wLjYtMS40Yy0wLjQtMC40LTAuOC0wLjYtMS40LTAuNkg4LjVjLTAuNSwwLTEsMC4yLTEuNCwwLjZTNi42LDExLDYuNiwxMS41djE5LjNjMCwwLjUsMC4yLDEsMC42LDEuNHMwLjgsMC42LDEuNCwwLjYKCUgzM2MwLjUsMCwxLTAuMiwxLjQtMC42UzM0LjksMzEuNCwzNC45LDMwLjh6IE0zNy41LDE2Ljd2MTQuMmMwLDEuMi0wLjQsMi4zLTEuMywzLjJjLTAuOSwwLjktMS45LDEuMy0zLjIsMS4zSDguNQoJYy0xLjIsMC0yLjMtMC40LTMuMi0xLjNTNCwzMi4xLDQsMzAuOFYxMS41YzAtMS4yLDAuNC0yLjMsMS4zLTMuMlM3LjMsNyw4LjUsN0gxNWMxLjIsMCwyLjMsMC40LDMuMiwxLjNjMC45LDAuOSwxLjMsMS45LDEuMywzLjIKCXYwLjZIMzNjMS4yLDAsMi4zLDAuNCwzLjIsMS4zQzM3LjEsMTQuNCwzNy41LDE1LjQsMzcuNSwxNi43eiIvPgo8cGF0aCBkPSJNMTYuMiwzMC4xbDEuNy0xLjZsLTIuMS0yLjNsLTEuNywxLjZsMCwwLjhsMS40LDAuMWwtMC4xLDEuNEwxNi4yLDMwLjF6IE0yMywyMC4yYy0wLjEtMC4yLTAuMy0wLjItMC41LDBsLTUuMiw0LjcKCWMtMC4yLDAuMi0wLjIsMC4zLDAsMC41YzAuMSwwLjIsMC4zLDAuMiwwLjUsMGw1LjItNC43QzIzLjEsMjAuNSwyMy4yLDIwLjMsMjMsMjAuMnogTTIyLjksMTguMWwzLjksNC4zbC0xMC4xLDkuMWwtNC4xLTAuMgoJbDAuMi00LjFMMjIuOSwxOC4xeiBNMjkuMSwyMC4zbC0xLjQsMS4ybC0zLjktNC4zbDEuNC0xLjJjMC4zLTAuMywwLjYtMC40LDEtMC4zczAuNywwLjIsMC45LDAuNGwyLjEsMi4zYzAuMywwLjMsMC40LDAuNiwwLjMsMQoJQzI5LjUsMTkuOCwyOS40LDIwLjEsMjkuMSwyMC4zeiIvPgo8L3N2Zz4K";

/***/ }),

/***/ 29906:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMzcuNiwzMi40di0xNmMtMC40LDAuNS0xLjQsMS4yLTIsMS42Yy0zLjgsMi44LTYuOCw1LTksNi44Yy0wLjcsMC42LTEuMywxLTEuOCwxLjNzLTEuMSwwLjYtMS44LDEKCWMtMC44LDAuMy0xLjUsMC41LTIuMiwwLjVoMGMtMC43LDAtMS40LTAuMi0yLjItMC41Yy0wLjgtMC4zLTEuNC0wLjctMS44LTFzLTEtMC44LTEuOC0xLjNjLTIuMi0xLjgtNS4yLTQtOS02LjgKCWMtMC41LTAuNC0xLjUtMS4yLTItMS42djE2YzAsMC4yLDAuMSwwLjMsMC4yLDAuNUM0LjUsMzIuOSw0LjcsMzMsNC44LDMzaDMyLjFjMC4yLDAsMC4zLTAuMSwwLjUtMC4yUzM3LjYsMzIuNSwzNy42LDMyLjR6CgkgTTM3LjYsMTF2LTAuN2wwLDBsLTAuMS0wLjVsLTAuMS0wLjJsLTAuMi0wLjJsLTAuMy0wLjFINC44Yy0wLjIsMC0wLjMsMC4xLTAuNSwwLjJDNC4yLDkuNyw0LjIsOS44LDQuMiwxMGMwLDIuMywxLjYsNC41LDMuNiw2LjEKCWMyLjcsMi4xLDUuNSw0LjMsOC41LDYuNWMwLjEsMC4xLDAuMywwLjMsMC43LDAuNnMwLjcsMC42LDEsMC44YzAuMiwwLjIsMC42LDAuNCwwLjksMC42YzAuNCwwLjMsMC43LDAuNCwxLjEsMC42CgljMC4zLDAuMSwwLjYsMC4yLDAuOSwwLjJoMGMwLjMsMCwwLjYtMC4xLDAuOS0wLjJjMC4zLTAuMSwwLjctMC4zLDEuMS0wLjZjMC40LTAuMywwLjctMC41LDAuOS0wLjZjMC4yLTAuMiwwLjYtMC40LDEtMC44CglzMC43LTAuNSwwLjctMC42YzIuOS0yLjMsNS43LTQuNCw4LjUtNi41YzAuOC0wLjYsMi4xLTEuNywyLjctMi43UzM3LjYsMTEuOCwzNy42LDExeiBNMzkuOCwxMC4ydjIxLjljMCwwLjktMC4zLDEuNi0xLDIuMwoJYy0wLjcsMC42LTEuNSwwLjktMi40LDAuOWgtMzFjLTAuOSwwLTEuNy0wLjMtMi40LTAuOVMyLDMzLDIsMzIuMVYxMC4yYzAtMC45LDAuMy0xLjYsMS0yLjNTNC40LDcsNS40LDdoMzFjMC45LDAsMS43LDAuMywyLjQsMC45CglDMzkuNSw4LjYsMzkuOCw5LjMsMzkuOCwxMC4yeiIvPgo8L3N2Zz4K";

/***/ }),

/***/ 73824:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMzksMzAuN1YxN2MtMC40LDAuNC0xLjMsMS4xLTEuOCwxLjRjLTMuNCwyLjQtNiw0LjMtOCw1LjhjLTAuNiwwLjUtMS4yLDAuOS0xLjYsMS4ycy0wLjksMC42LTEuNiwwLjgKCWMtMC43LDAuMy0xLjMsMC40LTEuOSwwLjRoMGMtMC42LDAtMS4yLTAuMS0xLjktMC40Yy0wLjctMC4zLTEuMi0wLjYtMS42LTAuOGMtMC40LTAuMy0wLjktMC43LTEuNi0xLjJjLTItMS41LTQuNy0zLjUtOC01LjgKCWMtMC41LTAuMy0xLjQtMS0xLjgtMS40djEzLjdjMCwwLjEsMC4xLDAuMywwLjIsMC40czAuMywwLjIsMC40LDAuMmgyOC43YzAuMiwwLDAuMy0wLjEsMC40LTAuMlMzOSwzMC44LDM5LDMwLjd6IE0zOSwxMi40di0wLjYKCWwwLDBsLTAuMS0wLjRsLTAuMS0wLjJsLTAuMi0wLjFsLTAuMywwSDkuOGMtMC4yLDAtMC4zLDAuMS0wLjQsMC4ycy0wLjIsMC4yLTAuMiwwLjRjMCwyLDEuNCwzLjgsMy4yLDUuMmMyLjQsMS44LDQuOSwzLjYsNy41LDUuNgoJYzAuMSwwLjEsMC4zLDAuMiwwLjcsMC41czAuNywwLjUsMC45LDAuN2MwLjIsMC4yLDAuNSwwLjMsMC44LDAuNmMwLjMsMC4yLDAuNywwLjQsMSwwLjVjMC4zLDAuMSwwLjYsMC4yLDAuOCwwLjJoMAoJYzAuMywwLDAuNS0wLjEsMC44LTAuMnMwLjYtMC4zLDEtMC41YzAuMy0wLjIsMC42LTAuNCwwLjgtMC42YzAuMi0wLjIsMC41LTAuNCwwLjktMC43czAuNi0wLjUsMC43LTAuNWMyLjYtMS45LDUuMS0zLjgsNy41LTUuNgoJYzAuNy0wLjUsMS44LTEuNSwyLjQtMi4zQzM4LjgsMTMuNiwzOSwxMy4xLDM5LDEyLjR6IE00MSwxMS44djE4LjdjMCwwLjgtMC4zLDEuNC0wLjksMS45Yy0wLjYsMC41LTEuMywwLjgtMi4xLDAuOEgxMC4yCgljLTAuOCwwLTEuNS0wLjMtMi4xLTAuOGMtMC42LTAuNS0wLjktMS4yLTAuOS0xLjlWMTEuOGMwLTAuOCwwLjMtMS40LDAuOS0xLjlDOC43LDkuMyw5LjQsOSwxMC4yLDlIMzhjMC44LDAsMS41LDAuMywyLjEsMC44CglDNDAuNywxMC4zLDQxLDExLDQxLDExLjh6Ii8+CjxwYXRoIGQ9Ik00LjYsMjMuOWgyLjJjMC40LDAsMC44LDAsMS4yLDBjMC40LDAsMC43LDAuMSwxLDAuMWMwLjMsMC4xLDAuNiwwLjIsMC45LDAuM2MwLjMsMC4xLDAuNSwwLjMsMC43LDAuNQoJYzAuMiwwLjIsMC40LDAuNCwwLjUsMC43YzAuMSwwLjMsMC4zLDAuNiwwLjMsMC45YzAuMSwwLjQsMC4xLDAuOCwwLjEsMS4yYzAsMC4yLDAsMC41LDAsMC44YzAsMCwwLDAuMSwwLDAuMmMwLDAuMSwwLDAuMSwwLDAuMgoJYzAsMC4xLDAsMC4xLDAuMSwwLjJjMCwwLDAuMSwwLjEsMC4yLDAuMWMwLjEsMCwwLjEsMCwwLjItMC4xYzAsMCwwLjEtMC4xLDAuMS0wLjFjMC0wLjEsMC4xLTAuMSwwLjEtMC4yYzAtMC4xLDAuMS0wLjEsMC4xLTAuMgoJYzAuNi0xLjMsMC45LTIuMywwLjktMy4xYzAtMC45LTAuMS0xLjctMC40LTIuM2MtMC43LTEuOC0yLjctMi43LTYtMi43SDQuNiBNMS4xLDIxLjgiLz4KPHBhdGggZD0iTTQuOSwyMy4yTDYuNywyNUM2LjksMjUuMSw3LDI1LjMsNywyNS41cy0wLjEsMC40LTAuMiwwLjVsLTAuNSwwLjVjLTAuMiwwLjItMC4zLDAuMi0wLjUsMC4yYy0wLjIsMC0wLjQtMC4xLTAuNi0wLjIKCWwtMy45LTMuOUMxLjEsMjIuNCwxLDIyLjIsMSwyMmMwLTAuMiwwLjEtMC40LDAuMi0wLjZsMy45LTMuOWMwLjItMC4xLDAuMy0wLjIsMC42LTAuMmMwLjIsMCwwLjQsMC4xLDAuNSwwLjJMNi43LDE4CglDNi45LDE4LjEsNywxOC4zLDcsMTguNWMwLDAuMi0wLjEsMC40LTAuMiwwLjZsLTEuOCwxLjgiLz4KPC9zdmc+Cg==";

/***/ }),

/***/ 42832:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIzLjAuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41MiA0Mi41MiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNDIuNTIgNDIuNTI7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHBhdGggZD0iTTM4LjQxLDguNDNjLTAuNTktMC41NC0xLjMtMC44MS0yLjEzLTAuODFIOC41N2MtMC44MywwLTEuNTQsMC4yNy0yLjEzLDAuODFDNi4xOSw4LjY2LDYsOC45Miw1Ljg2LDkuMTlIMzQuNQoJYzAuODcsMCwxLjYyLDAuMjgsMi4yNCwwLjg1YzAuNjIsMC41NywwLjkzLDEuMjUsMC45MywyLjA1djE5LjQ0YzAuMjYtMC4xMywwLjUxLTAuMjgsMC43NC0wLjQ5YzAuNTktMC41NCwwLjg4LTEuMTksMC44OC0xLjk0CglWMTAuMzhDMzkuMjksOS42MiwzOSw4Ljk3LDM4LjQxLDguNDN6Ii8+CjxwYXRoIGQ9Ik0zNC45LDMxLjZWMTcuOTJjLTAuNCwwLjQxLTEuMjksMS4wNi0xLjc1LDEuNDFjLTMuMzYsMi4zNi02LjA0LDQuMy04LjAyLDUuODFjLTAuNjQsMC40OS0xLjE2LDAuODgtMS41NiwxLjE1CglzLTAuOTQsMC41NS0xLjYzLDAuODNjLTAuNjgsMC4yOC0xLjMzLDAuNDItMS45MywwLjQyaC0wLjA0Yy0wLjYsMC0xLjI1LTAuMTQtMS45My0wLjQyYy0wLjY4LTAuMjgtMS4yMy0wLjU2LTEuNjMtMC44MwoJYy0wLjQtMC4yOC0wLjkyLTAuNjYtMS41Ni0xLjE1Yy0xLjk4LTEuNTEtNC42Ni0zLjQ1LTguMDItNS44MWMtMC40Ni0wLjM0LTEuMzgtMS0xLjc4LTEuNDFWMzEuNmMwLDAuMTUsMC4wNiwwLjI4LDAuMTgsMC4zOQoJYzAuMTIsMC4xMSwwLjI2LDAuMTYsMC40MiwwLjE2SDM0LjNjMC4xNiwwLDAuMy0wLjA1LDAuNDItMC4xNkMzNC44NCwzMS44OCwzNC45LDMxLjc1LDM0LjksMzEuNnogTTM0LjksMTMuMzZ2LTAuNjNsLTAuMDEtMC4wMwoJbC0wLjA2LTAuNDJsLTAuMS0wLjE2bC0wLjE3LTAuMTNsLTAuMjYtMC4wNEg1LjY1Yy0wLjE2LDAtMC4zLDAuMDYtMC40MiwwLjE3cy0wLjE4LDAuMjQtMC4xOCwwLjRjMCwxLjk3LDEuNCwzLjg0LDMuMjUsNS4yCgljMi40MiwxLjc5LDQuOTQsMy42NSw3LjU1LDUuNTljMC4wOCwwLjA2LDAuMjksMC4yMywwLjY2LDAuNTJzMC42NSwwLjUxLDAuODcsMC42NmMwLjIxLDAuMTUsMC40OSwwLjM0LDAuODQsMC41NgoJYzAuMzUsMC4yMiwwLjY2LDAuMzgsMC45NSwwLjQ4YzAuMjksMC4xMSwwLjU2LDAuMTYsMC44MSwwLjE2aDAuMDRjMC4yNSwwLDAuNTItMC4wNSwwLjgxLTAuMTZzMC42MS0wLjI3LDAuOTUtMC40OAoJYzAuMzUtMC4yMiwwLjYyLTAuNCwwLjg0LTAuNTZjMC4yMS0wLjE1LDAuNS0wLjM3LDAuODctMC42NnMwLjU4LTAuNDYsMC42Ni0wLjUyYzIuNjEtMS45NCw1LjEzLTMuOCw3LjU1LTUuNTkKCWMwLjY4LTAuNTEsMS44NS0xLjQ3LDIuNDMtMi4zMkMzNC42OSwxNC41NCwzNC45LDE0LjA1LDM0LjksMTMuMzZ6IE0zNi44NiwxMi42OVYzMS40YzAsMC43Ni0wLjI5LDEuNC0wLjg4LDEuOTQKCWMtMC41OSwwLjU0LTEuMywwLjgxLTIuMTMsMC44MUg2LjEzYy0wLjgzLDAtMS41NC0wLjI3LTIuMTMtMC44MXMtMC44OC0xLjE5LTAuODgtMS45NFYxMi42OWMwLTAuNzYsMC4yOS0xLjQsMC44OC0xLjk0CgljMC41OS0wLjU0LDEuMy0wLjgxLDIuMTMtMC44MWgyNy43MWMwLjgzLDAsMS41NCwwLjI3LDIuMTMsMC44MUMzNi41NiwxMS4yOCwzNi44NiwxMS45MywzNi44NiwxMi42OXoiLz4KPHBhdGggZD0iTTE3LjQ3LDE5LjU4aDIuMjRjMC40NCwwLDAuODQsMC4wMSwxLjE5LDAuMDRjMC4zNSwwLjAzLDAuNywwLjA4LDEuMDUsMC4xNWMwLjM1LDAuMDcsMC42NSwwLjE3LDAuOTEsMC4yOQoJYzAuMjYsMC4xMiwwLjUsMC4yOCwwLjcyLDAuNDdjMC4yMiwwLjE5LDAuNCwwLjQyLDAuNTQsMC42OWMwLjE0LDAuMjcsMC4yNSwwLjU4LDAuMzMsMC45NGMwLjA4LDAuMzYsMC4xMiwwLjc3LDAuMTIsMS4yMwoJYzAsMC4yNS0wLjAxLDAuNTMtMC4wMywwLjg0YzAsMC4wMy0wLjAxLDAuMDgtMC4wMiwwLjE2Yy0wLjAxLDAuMDgtMC4wMiwwLjE0LTAuMDIsMC4xOGMwLDAuMDcsMC4wMiwwLjEyLDAuMDYsMC4xNwoJYzAuMDQsMC4wNSwwLjA5LDAuMDcsMC4xNiwwLjA3YzAuMDcsMCwwLjE0LTAuMDQsMC4xOS0wLjEyYzAuMDMtMC4wNCwwLjA2LTAuMDksMC4wOS0wLjE1YzAuMDMtMC4wNiwwLjA2LTAuMTMsMC4wOS0wLjIKCWMwLjAzLTAuMDgsMC4wNi0wLjEzLDAuMDctMC4xNmMwLjU4LTEuMjksMC44Ni0yLjMyLDAuODYtMy4wN2MwLTAuOS0wLjEyLTEuNjYtMC4zNi0yLjI3Yy0wLjczLTEuODMtMi43Mi0yLjc0LTUuOTUtMi43NGgtMi4yNAoJIE0xMy45NiwxNy41MyIvPgo8cGF0aCBkPSJNMTcuNzcsMTguODdsMS43OCwxLjc4YzAuMTUsMC4xNSwwLjIzLDAuMzMsMC4yMywwLjU1cy0wLjA4LDAuNC0wLjIzLDAuNTVsLTAuNDUsMC40NWMtMC4xNSwwLjE1LTAuMzQsMC4yMy0wLjU1LDAuMjMKCWMtMC4yMSwwLTAuNC0wLjA4LTAuNTUtMC4yM2wtMy45NS0zLjk1Yy0wLjE1LTAuMTQtMC4yMi0wLjMyLTAuMjItMC41NWMwLTAuMjIsMC4wNy0wLjQsMC4yMi0wLjU1TDE4LDEzLjIKCWMwLjE2LTAuMTUsMC4zNC0wLjIyLDAuNTUtMC4yMmMwLjIxLDAsMC4zOSwwLjA3LDAuNTUsMC4yMmwwLjQ1LDAuNDVjMC4xNSwwLjE1LDAuMjMsMC4zNCwwLjIzLDAuNTVzLTAuMDgsMC40LTAuMjMsMC41NQoJbC0xLjc4LDEuNzgiLz4KPC9zdmc+Cg==";

/***/ }),

/***/ 82152:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMzEuOCwzMC43VjE3Yy0wLjQsMC40LTEuMywxLjEtMS44LDEuNGMtMy40LDIuNC02LDQuMy04LDUuOGMtMC42LDAuNS0xLjIsMC45LTEuNiwxLjJzLTAuOSwwLjYtMS42LDAuOAoJYy0wLjcsMC4zLTEuMywwLjQtMS45LDAuNGgwYy0wLjYsMC0xLjItMC4xLTEuOS0wLjRjLTAuNy0wLjMtMS4yLTAuNi0xLjYtMC44cy0wLjktMC43LTEuNi0xLjJjLTItMS41LTQuNy0zLjUtOC01LjgKCWMtMC41LTAuMy0xLjQtMS0xLjgtMS40djEzLjdjMCwwLjEsMC4xLDAuMywwLjIsMC40YzAuMSwwLjEsMC4zLDAuMiwwLjQsMC4yaDI4LjdjMC4yLDAsMC4zLTAuMSwwLjQtMC4yUzMxLjgsMzAuOCwzMS44LDMwLjd6CgkgTTMxLjgsMTIuNHYtMC42bDAsMGwtMC4xLTAuNGwtMC4xLTAuMmwtMC4yLTAuMWwtMC4zLDBIMi41Yy0wLjIsMC0wLjMsMC4xLTAuNCwwLjJjLTAuMSwwLjEtMC4yLDAuMi0wLjIsMC40YzAsMiwxLjQsMy44LDMuMiw1LjIKCWMyLjQsMS44LDQuOSwzLjYsNy41LDUuNmMwLjEsMC4xLDAuMywwLjIsMC43LDAuNXMwLjcsMC41LDAuOSwwLjdzMC41LDAuMywwLjgsMC42YzAuMywwLjIsMC43LDAuNCwxLDAuNXMwLjYsMC4yLDAuOCwwLjJoMAoJYzAuMywwLDAuNS0wLjEsMC44LTAuMmMwLjMtMC4xLDAuNi0wLjMsMS0wLjVjMC4zLTAuMiwwLjYtMC40LDAuOC0wLjZjMC4yLTAuMiwwLjUtMC40LDAuOS0wLjdzMC42LTAuNSwwLjctMC41CgljMi42LTEuOSw1LjEtMy44LDcuNS01LjZjMC43LTAuNSwxLjgtMS41LDIuNC0yLjNDMzEuNiwxMy42LDMxLjgsMTMuMSwzMS44LDEyLjR6IE0zMy43LDExLjh2MTguN2MwLDAuOC0wLjMsMS40LTAuOSwxLjkKCWMtMC42LDAuNS0xLjMsMC44LTIuMSwwLjhIM2MtMC44LDAtMS41LTAuMy0yLjEtMC44UzAsMzEuMiwwLDMwLjVWMTEuOGMwLTAuOCwwLjMtMS40LDAuOS0xLjlDMS41LDkuMywyLjIsOSwzLDloMjcuNwoJYzAuOCwwLDEuNSwwLjMsMi4xLDAuOEMzMy40LDEwLjMsMzMuNywxMSwzMy43LDExLjh6Ii8+CjxwYXRoIGQ9Ik00Mi41LDI0LjRjMCwwLjItMC4xLDAuMy0wLjIsMC40bC01LjIsNS4yYy0wLjEsMC4xLTAuMywwLjItMC40LDAuMnMtMC4zLTAuMS0wLjQtMC4yYy0wLjEtMC4xLTAuMi0wLjMtMC4yLTAuNHYtMi44aC02LjMKCWMtMC4yLDAtMC4zLTAuMS0wLjQtMC4ycy0wLjItMC4zLTAuMi0wLjR2LTMuN2MwLTAuMiwwLjEtMC4zLDAuMi0wLjRjMC4xLTAuMSwwLjMtMC4yLDAuNC0wLjJoNi4zdi0yLjhjMC0wLjIsMC4xLTAuMywwLjItMC40CgljMC4xLTAuMSwwLjMtMC4yLDAuNC0wLjJzMC4zLDAuMSwwLjQsMC4ybDUuMiw1LjJDNDIuNSwyNC4xLDQyLjUsMjQuMyw0Mi41LDI0LjR6Ii8+Cjwvc3ZnPgo=";

/***/ }),

/***/ 14368:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMjcuNiwyMC45YzAuMSwwLjEsMC4xLDAuMiwwLjEsMC40djFjMCwwLjIsMCwwLjMtMC4xLDAuNGMtMC4xLDAuMS0wLjIsMC4xLTAuNCwwLjFIMTUuN2MtMC4yLDAtMC4zLDAtMC40LTAuMQoJYy0wLjEtMC4xLTAuMS0wLjItMC4xLTAuNHYtMWMwLTAuMiwwLTAuMywwLjEtMC40YzAuMS0wLjEsMC4yLTAuMSwwLjQtMC4xaDExLjVDMjcuNCwyMC43LDI3LjUsMjAuOCwyNy42LDIwLjl6IE0yNy42LDE2LjcKCWMtMC4xLTAuMS0wLjItMC4xLTAuNC0wLjFIMTUuN2MtMC4yLDAtMC4zLDAtMC40LDAuMWMtMC4xLDAuMS0wLjEsMC4yLTAuMSwwLjR2MWMwLDAuMiwwLDAuMywwLjEsMC40YzAuMSwwLjEsMC4yLDAuMSwwLjQsMC4xaDExLjUKCWMwLjIsMCwwLjMsMCwwLjQtMC4xYzAuMS0wLjEsMC4xLTAuMiwwLjEtMC40di0xQzI3LjcsMTYuOSwyNy43LDE2LjgsMjcuNiwxNi43eiBNMTEsNi4xaDEyLjV2Ni44YzAsMC40LDAuMiwwLjgsMC41LDEuMQoJczAuNywwLjUsMS4xLDAuNWg2Ljh2OS4ySDM0VjEyLjljMC0wLjQtMC4xLTAuOS0wLjMtMS40Yy0wLjItMC41LTAuNS0wLjktMC44LTEuMmwtNS4xLTUuMWMtMC4zLTAuMy0wLjctMC42LTEuMi0wLjgKCUMyNiw0LjEsMjUuNSw0LDI1LjEsNEgxMC41QzEwLDQsOS43LDQuMiw5LjQsNC41QzkuMSw0LjgsOC45LDUuMSw4LjksNS42djE4LjFIMTFWNi4xeiBNMjUuNiw2LjJjMC4zLDAuMSwwLjUsMC4yLDAuNywwLjRsNS4xLDUuMQoJYzAuMSwwLjEsMC4zLDAuNCwwLjQsMC43aC02LjFWNi4yeiIvPgo8cGF0aCBkPSJNNy45LDM4aDI2LjZjMC44LDAsMS41LTAuMywyLTAuOGMwLjYtMC41LDAuOS0xLjEsMC45LTEuOXYtMThjMC0wLjctMC4zLTEuMy0wLjktMS45Yy0wLjMtMC4zLTAuNy0wLjUtMS0wLjZ2Mi40bDAsMGwwLDAuMQoJVjE4YzAsMC43LTAuMiwxLjEtMC44LDJjLTAuNiwwLjgtMS43LDEuNy0yLjMsMi4yYy0yLjMsMS43LTQuNywzLjUtNy4zLDUuNGMtMC4xLDAuMS0wLjMsMC4yLTAuNiwwLjVjLTAuMywwLjMtMC42LDAuNS0wLjgsMC42CgljLTAuMiwwLjEtMC41LDAuMy0wLjgsMC41Yy0wLjMsMC4yLTAuNiwwLjQtMC45LDAuNWMtMC4zLDAuMS0wLjUsMC4yLTAuOCwwLjJoMGMtMC4yLDAtMC41LTAuMS0wLjgtMC4yYy0wLjMtMC4xLTAuNi0wLjMtMC45LTAuNQoJYy0wLjMtMC4yLTAuNi0wLjQtMC44LTAuNWMtMC4yLTAuMS0wLjUtMC40LTAuOC0wLjZjLTAuMy0wLjMtMC42LTAuNC0wLjYtMC41Yy0yLjUtMS45LTQuOS0zLjctNy4zLTUuNGMtMS43LTEuMy0zLjEtMy0zLjEtNC45aDAKCXYtMC4xYzAsMCwwLDAsMCwwYzAsMCwwLDAsMCwwdi0yLjNjLTAuNCwwLjEtMC43LDAuMy0xLDAuNkM1LjMsMTYsNSwxNi42LDUsMTcuNHYxOGMwLDAuNywwLjMsMS4zLDAuOSwxLjlDNi40LDM3LjcsNy4xLDM4LDcuOSwzOHoKCSBNNi45LDIyLjRjMC40LDAuNCwxLjMsMSwxLjcsMS40YzMuMiwyLjMsNS44LDQuMSw3LjcsNS42YzAuNiwwLjUsMS4xLDAuOCwxLjUsMS4xYzAuNCwwLjMsMC45LDAuNSwxLjYsMC44CgljMC43LDAuMywxLjMsMC40LDEuOSwwLjRoMGMwLjYsMCwxLjItMC4xLDEuOS0wLjRjMC43LTAuMywxLjItMC41LDEuNi0wLjhjMC40LTAuMywwLjktMC42LDEuNS0xLjFjMS45LTEuNSw0LjUtMy4zLDcuNy01LjYKCWMwLjQtMC4zLDEuMy0xLDEuNy0xLjR2MTMuMWMwLDAuMS0wLjEsMC4zLTAuMiwwLjRjLTAuMSwwLjEtMC4zLDAuMi0wLjQsMC4ySDcuNGMtMC4yLDAtMC4zLTAuMS0wLjQtMC4yYy0wLjEtMC4xLTAuMi0wLjItMC4yLTAuNAoJVjIyLjR6Ii8+Cjwvc3ZnPgo=";

/***/ }),

/***/ 97972:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMjIuNiwyMC45YzAuMSwwLjEsMC4xLDAuMiwwLjEsMC40djFjMCwwLjIsMCwwLjMtMC4xLDAuNGMtMC4xLDAuMS0wLjIsMC4xLTAuNCwwLjFIMTAuN2MtMC4yLDAtMC4zLDAtMC40LTAuMQoJYy0wLjEtMC4xLTAuMS0wLjItMC4xLTAuNHYtMWMwLTAuMiwwLTAuMywwLjEtMC40YzAuMS0wLjEsMC4yLTAuMSwwLjQtMC4xaDExLjVDMjIuNCwyMC43LDIyLjUsMjAuOCwyMi42LDIwLjl6IE0yMi42LDE2LjcKCWMtMC4xLTAuMS0wLjItMC4xLTAuNC0wLjFIMTAuN2MtMC4yLDAtMC4zLDAtMC40LDAuMWMtMC4xLDAuMS0wLjEsMC4yLTAuMSwwLjR2MWMwLDAuMiwwLDAuMywwLjEsMC40YzAuMSwwLjEsMC4yLDAuMSwwLjQsMC4xaDExLjUKCWMwLjIsMCwwLjMsMCwwLjQtMC4xYzAuMS0wLjEsMC4xLTAuMiwwLjEtMC40di0xQzIyLjcsMTYuOSwyMi43LDE2LjgsMjIuNiwxNi43eiBNNiw2LjFoMTIuNXY2LjhjMCwwLjQsMC4yLDAuOCwwLjUsMS4xCglzMC43LDAuNSwxLjEsMC41aDYuOHY5LjJIMjlWMTIuOWMwLTAuNC0wLjEtMC45LTAuMy0xLjRjLTAuMi0wLjUtMC41LTAuOS0wLjgtMS4ybC01LjEtNS4xYy0wLjMtMC4zLTAuNy0wLjYtMS4yLTAuOAoJQzIxLDQuMSwyMC41LDQsMjAuMSw0SDUuNUM1LDQsNC43LDQuMiw0LjQsNC41QzQuMSw0LjgsMy45LDUuMSwzLjksNS42djE4LjFINlY2LjF6IE0yMC42LDYuMmMwLjMsMC4xLDAuNSwwLjIsMC43LDAuNGw1LjEsNS4xCgljMC4xLDAuMSwwLjMsMC40LDAuNCwwLjdoLTYuMVY2LjJ6IE0zMC40LDM1LjljLTAuMSwwLjEtMC4zLDAuMi0wLjQsMC4ySDIuNGMtMC4yLDAtMC4zLTAuMS0wLjQtMC4yYy0wLjEtMC4xLTAuMi0wLjItMC4yLTAuNFYyMi40CgljMC40LDAuNCwxLjMsMSwxLjcsMS40YzMuMiwyLjMsNS44LDQuMSw3LjcsNS42YzAuNiwwLjUsMS4xLDAuOCwxLjUsMS4xYzAuNCwwLjMsMC45LDAuNSwxLjYsMC44YzAuNywwLjMsMS4zLDAuNCwxLjksMC40aDAKCWMwLjYsMCwxLjItMC4xLDEuOS0wLjRjMC43LTAuMywxLjItMC41LDEuNi0wLjhjMC40LTAuMywwLjktMC42LDEuNS0xLjFjMS45LTEuNSw0LjUtMy4zLDcuNy01LjZjMC40LTAuMywxLjMtMSwxLjctMS40djIuOWgxLjkKCXYtNy45YzAtMC43LTAuMy0xLjMtMC45LTEuOWMtMC4zLTAuMy0wLjctMC41LTEtMC42djIuNGwwLDBsMCwwLjFWMThjMCwwLjctMC4yLDEuMS0wLjgsMmMtMC42LDAuOC0xLjcsMS43LTIuMywyLjIKCWMtMi4zLDEuNy00LjcsMy41LTcuMyw1LjRjLTAuMSwwLjEtMC4zLDAuMi0wLjYsMC41Yy0wLjMsMC4zLTAuNiwwLjUtMC44LDAuNmMtMC4yLDAuMS0wLjUsMC4zLTAuOCwwLjVjLTAuMywwLjItMC42LDAuNC0wLjksMC41CgljLTAuMywwLjEtMC41LDAuMi0wLjgsMC4yaDBjLTAuMiwwLTAuNS0wLjEtMC44LTAuMmMtMC4zLTAuMS0wLjYtMC4zLTAuOS0wLjVjLTAuMy0wLjItMC42LTAuNC0wLjgtMC41Yy0wLjItMC4xLTAuNS0wLjQtMC44LTAuNgoJcy0wLjYtMC40LTAuNi0wLjVjLTIuNS0xLjktNC45LTMuNy03LjMtNS40Yy0xLjctMS4zLTMuMS0zLTMuMS00LjloMHYtMC4xYzAsMCwwLDAsMCwwYzAsMCwwLDAsMCwwdi0yLjNjLTAuNCwwLjEtMC43LDAuMy0xLDAuNgoJQzAuMywxNiwwLDE2LjYsMCwxNy40djE4YzAsMC43LDAuMywxLjMsMC45LDEuOWMwLjYsMC41LDEuMiwwLjgsMiwwLjhoMjYuNmMwLjgsMCwxLjUtMC4zLDItMC44YzAuNS0wLjUsMC44LTEsMC44LTEuN2gtMS45CglDMzAuNSwzNS43LDMwLjUsMzUuOCwzMC40LDM1Ljl6IE00Mi4xLDI3LjRsLTMuNi0wLjVsLTEuNi0zLjNjLTAuMS0wLjItMC4yLTAuMy0wLjQtMC4zcy0wLjMsMC4xLTAuNCwwLjNsLTEuNiwzLjNsLTMuNiwwLjUKCWMtMC4zLDAtMC40LDAuMi0wLjQsMC4zYzAsMC4xLDAuMSwwLjIsMC4yLDAuM2wyLjYsMi41bC0wLjYsMy42YzAsMC4xLDAsMC4xLDAsMC4xYzAsMC4xLDAsMC4yLDAuMSwwLjNjMC4xLDAuMSwwLjEsMC4xLDAuMiwwLjEKCWMwLjEsMCwwLjIsMCwwLjMtMC4xbDMuMi0xLjdsMy4yLDEuN2MwLjEsMC4xLDAuMiwwLjEsMC4zLDAuMWMwLjEsMCwwLjIsMCwwLjItMC4xYzAuMS0wLjEsMC4xLTAuMiwwLjEtMC4zYzAtMC4xLDAtMC4xLDAtMC4xCglsLTAuNi0zLjZsMi42LTIuNWMwLjEtMC4xLDAuMi0wLjIsMC4yLTAuM0M0Mi41LDI3LjUsNDIuMywyNy40LDQyLjEsMjcuNHoiLz4KPC9zdmc+Cg==";

/***/ }),

/***/ 38082:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMjEuMSwyNy45Yy0xLDAtMS45LDAuOS0xLjksMS45YzAsMSwwLjksMS45LDEuOSwxLjljMSwwLDEuOS0wLjksMS45LTEuOUMyMy4xLDI4LjcsMjIuMiwyNy45LDIxLjEsMjcuOUwyMS4xLDI3Ljl6CgkgTTIxLjEsMjcuOSBNMzguOCwzNC42YzEuMi0yLjEsMS4yLTQuNSwwLTYuNmwtMTItMjAuN0MyNS43LDUuMywyMy42LDQsMjEuMiw0cy00LjUsMS4yLTUuNywzLjNsLTEyLDIwLjdjLTEuMiwyLjEtMS4yLDQuNiwwLDYuNgoJYzEuMiwyLDMuMywzLjMsNS43LDMuM2gyMy45QzM1LjUsMzgsMzcuNiwzNi43LDM4LjgsMzQuNkwzOC44LDM0LjZ6IE0zNi4yLDMzLjFjLTAuNywxLjEtMS44LDEuOC0zLjIsMS44SDkuMgoJYy0xLjMsMC0yLjUtMC43LTMuMS0xLjhjLTAuNy0xLjEtMC43LTIuNSwwLTMuNkwxOCw4LjhDMTguNyw3LjcsMTkuOCw3LDIxLjIsN2MxLjMsMCwyLjUsMC43LDMuMSwxLjhsMTIsMjAuNwoJQzM2LjksMzAuNywzNi45LDMyLDM2LjIsMzMuMUwzNi4yLDMzLjF6IE0zNi4yLDMzLjEgTTIwLjcsMTQuNWMtMC45LDAuMy0xLjUsMS4xLTEuNSwyLjFjMCwwLjYsMC4xLDEuMiwwLjEsMS44CgljMC4xLDIuMywwLjMsNC42LDAuNCw2LjhjMCwwLjgsMC42LDEuMywxLjQsMS4zczEuNC0wLjYsMS40LTEuNGMwLTAuNSwwLTAuOSwwLTEuNGMwLjEtMS41LDAuMi0yLjksMC4zLTQuNGMwLTEsMC4xLTEuOSwwLjItMi45CgljMC0wLjMsMC0wLjYtMC4yLTFDMjIuNSwxNC43LDIxLjYsMTQuMywyMC43LDE0LjVMMjAuNywxNC41eiBNMjAuNywxNC41Ii8+Cjwvc3ZnPgo=";

/***/ }),

/***/ 29913:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMzQuNSwyMy45VjEyLjVjLTIuMSwxLjEtNCwxLjctNS42LDEuN2MtMSwwLTEuOS0wLjItMi43LTAuNmMtMS4yLTAuNi0yLjQtMS4xLTMuNC0xLjRjLTEtMC4zLTIuMS0wLjUtMy4zLTAuNQoJYy0yLjEsMC00LjYsMC44LTcuNCwyLjN2MTFjMy0xLjQsNS43LTIuMSw4LTIuMWMwLjcsMCwxLjMsMCwxLjksMC4xczEuMiwwLjMsMS44LDAuNWMwLjYsMC4yLDEuMSwwLjQsMS40LDAuNnMwLjgsMC40LDEuNSwwLjcKCWwwLjUsMC4zYzAuNSwwLjMsMS4yLDAuNCwxLjksMC40QzMwLjYsMjUuNiwzMi40LDI1LDM0LjUsMjMuOXogTTkuNyw5LjRjMCwwLjQtMC4xLDAuOC0wLjMsMS4yYy0wLjIsMC40LTAuNSwwLjYtMC45LDAuOHYyMy4zCgljMCwwLjItMC4xLDAuMy0wLjIsMC40cy0wLjMsMC4yLTAuNCwwLjJINi44Yy0wLjIsMC0wLjMtMC4xLTAuNC0wLjJzLTAuMi0wLjMtMC4yLTAuNFYxMS40Yy0wLjQtMC4yLTAuNi0wLjUtMC45LTAuOAoJQzUuMSwxMC4yLDUsOS44LDUsOS40YzAtMC43LDAuMi0xLjIsMC43LTEuN0M2LjIsNy4yLDYuNyw3LDcuNCw3UzguNiw3LjIsOSw3LjdDOS41LDguMiw5LjcsOC43LDkuNyw5LjR6IE0zNi45LDEwLjV2MTQuMQoJYzAsMC41LTAuMiwwLjgtMC42LDEuMWMtMC4xLDAuMS0wLjIsMC4xLTAuMywwLjJjLTIuNywxLjQtNC45LDIuMS02LjgsMi4xYy0xLjEsMC0yLjEtMC4yLTIuOS0wLjZsLTAuNS0wLjMKCWMtMC44LTAuNC0xLjQtMC43LTEuOC0wLjlzLTEtMC40LTEuNy0wLjVjLTAuNy0wLjItMS40LTAuMy0yLjEtMC4zYy0xLjMsMC0yLjcsMC4zLTQuMywwLjhzLTMsMS4yLTQuMiwxLjkKCWMtMC4yLDAuMS0wLjQsMC4yLTAuNiwwLjJjLTAuMiwwLTAuNCwwLTAuNi0wLjFjLTAuNC0wLjItMC42LTAuNi0wLjYtMVYxMy40YzAtMC40LDAuMi0wLjgsMC42LTFjMC40LTAuMywwLjktMC41LDEuNC0wLjgKCWMwLjUtMC4zLDEuMi0wLjYsMi4xLTFjMC45LTAuNCwxLjgtMC43LDIuOC0wLjljMS0wLjIsMi0wLjQsMi45LTAuNGMxLjQsMCwyLjcsMC4yLDMuOSwwLjZzMi41LDAuOSwzLjksMS42YzAuNSwwLjIsMSwwLjQsMS42LDAuNAoJYzEuNSwwLDMuNC0wLjcsNS43LTIuMWMwLjMtMC4xLDAuNS0wLjMsMC42LTAuM2MwLjQtMC4yLDAuOC0wLjIsMS4xLDBDMzYuNyw5LjgsMzYuOSwxMC4xLDM2LjksMTAuNXoiLz4KPC9zdmc+Cg==";

/***/ }),

/***/ 98163:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMzQuOSwzMC44VjE2LjdjMC0wLjUtMC4yLTEtMC42LTEuNHMtMC44LTAuNi0xLjQtMC42SDE4LjhjLTAuNSwwLTEtMC4yLTEuNC0wLjZjLTAuNC0wLjQtMC42LTAuOC0wLjYtMS40di0xLjMKCWMwLTAuNS0wLjItMS0wLjYtMS40Yy0wLjQtMC40LTAuOC0wLjYtMS40LTAuNkg4LjVjLTAuNSwwLTEsMC4yLTEuNCwwLjZTNi42LDExLDYuNiwxMS41djE5LjNjMCwwLjUsMC4yLDEsMC42LDEuNHMwLjgsMC42LDEuNCwwLjYKCUgzM2MwLjUsMCwxLTAuMiwxLjQtMC42UzM0LjksMzEuNCwzNC45LDMwLjh6IE0zNy41LDE2Ljd2MTQuMmMwLDEuMi0wLjQsMi4zLTEuMywzLjJjLTAuOSwwLjktMS45LDEuMy0zLjIsMS4zSDguNQoJYy0xLjIsMC0yLjMtMC40LTMuMi0xLjNTNCwzMi4xLDQsMzAuOFYxMS41YzAtMS4yLDAuNC0yLjMsMS4zLTMuMlM3LjMsNyw4LjUsN0gxNWMxLjIsMCwyLjMsMC40LDMuMiwxLjNjMC45LDAuOSwxLjMsMS45LDEuMywzLjIKCXYwLjZIMzNjMS4yLDAsMi4zLDAuNCwzLjIsMS4zQzM3LjEsMTQuNCwzNy41LDE1LjQsMzcuNSwxNi43eiIvPgo8L3N2Zz4K";

/***/ }),

/***/ 24185:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMzQuOSwzMC44VjE2LjdjMC0wLjUtMC4yLTEtMC42LTEuNHMtMC44LTAuNi0xLjQtMC42SDE4LjhjLTAuNSwwLTEtMC4yLTEuNC0wLjZjLTAuNC0wLjQtMC42LTAuOC0wLjYtMS40di0xLjMKCWMwLTAuNS0wLjItMS0wLjYtMS40Yy0wLjQtMC40LTAuOC0wLjYtMS40LTAuNkg4LjVjLTAuNSwwLTEsMC4yLTEuNCwwLjZTNi42LDExLDYuNiwxMS41djE5LjNjMCwwLjUsMC4yLDEsMC42LDEuNHMwLjgsMC42LDEuNCwwLjYKCUgzM2MwLjUsMCwxLTAuMiwxLjQtMC42UzM0LjksMzEuNCwzNC45LDMwLjh6IE0zNy41LDE2Ljd2MTQuMmMwLDEuMi0wLjQsMi4zLTEuMywzLjJjLTAuOSwwLjktMS45LDEuMy0zLjIsMS4zSDguNQoJYy0xLjIsMC0yLjMtMC40LTMuMi0xLjNTNCwzMi4xLDQsMzAuOFYxMS41YzAtMS4yLDAuNC0yLjMsMS4zLTMuMlM3LjMsNyw4LjUsN0gxNWMxLjIsMCwyLjMsMC40LDMuMiwxLjNjMC45LDAuOSwxLjMsMS45LDEuMywzLjIKCXYwLjZIMzNjMS4yLDAsMi4zLDAuNCwzLjIsMS4zQzM3LjEsMTQuNCwzNy41LDE1LjQsMzcuNSwxNi43eiIvPgo8cGF0aCBkPSJNMjkuNiwyOS4zdi04LjFjLTAuMiwwLjItMC44LDAuNi0xLDAuOGMtMiwxLjQtMy42LDIuNS00LjcsMy40Yy0wLjQsMC4zLTAuNywwLjUtMC45LDAuN2MtMC4yLDAuMi0wLjYsMC4zLTEsMC41CgljLTAuNCwwLjItMC44LDAuMi0xLjEsMC4yaDBjLTAuNCwwLTAuNy0wLjEtMS4xLTAuMmMtMC40LTAuMi0wLjctMC4zLTEtMC41Yy0wLjItMC4yLTAuNS0wLjQtMC45LTAuN2MtMS4yLTAuOS0yLjgtMi00LjctMy40CgljLTAuMy0wLjItMC44LTAuNi0xLjEtMC44djguMWMwLDAuMSwwLDAuMiwwLjEsMC4yYzAuMSwwLjEsMC4yLDAuMSwwLjMsMC4xaDE2LjljMC4xLDAsMC4yLDAsMC4zLTAuMQoJQzI5LjUsMjkuNSwyOS42LDI5LjQsMjkuNiwyOS4zeiBNMjkuNiwxOC41di0wLjRsMCwwbDAtMC4ybC0wLjEtMC4xbC0wLjEtMC4xbC0wLjIsMEgxMi4zYy0wLjEsMC0wLjIsMC0wLjMsMC4xCgljLTAuMSwwLjEtMC4xLDAuMS0wLjEsMC4yYzAsMS4yLDAuOCwyLjMsMS45LDMuMWMxLjQsMS4xLDIuOSwyLjIsNC41LDMuM2MwLDAsMC4yLDAuMSwwLjQsMC4zYzAuMiwwLjIsMC40LDAuMywwLjUsMC40CgljMC4xLDAuMSwwLjMsMC4yLDAuNSwwLjNjMC4yLDAuMSwwLjQsMC4yLDAuNiwwLjNjMC4yLDAuMSwwLjMsMC4xLDAuNSwwLjFoMGMwLjEsMCwwLjMsMCwwLjUtMC4xYzAuMi0wLjEsMC40LTAuMiwwLjYtMC4zCglzMC40LTAuMiwwLjUtMC4zYzAuMS0wLjEsMC4zLTAuMiwwLjUtMC40YzAuMi0wLjIsMC4zLTAuMywwLjQtMC4zYzEuNS0xLjEsMy0yLjIsNC41LTMuM2MwLjQtMC4zLDEuMS0wLjksMS40LTEuNAoJQzI5LjQsMTkuMiwyOS42LDE5LDI5LjYsMTguNXogTTMwLjcsMTguMXYxMS4xYzAsMC40LTAuMiwwLjgtMC41LDEuMWMtMC4zLDAuMy0wLjgsMC41LTEuMywwLjVIMTIuNmMtMC41LDAtMC45LTAuMi0xLjMtMC41CgljLTAuMy0wLjMtMC41LTAuNy0wLjUtMS4xVjE4LjFjMC0wLjQsMC4yLTAuOCwwLjUtMS4xYzAuMy0wLjMsMC44LTAuNSwxLjMtMC41aDE2LjRjMC41LDAsMC45LDAuMiwxLjMsMC41CglDMzAuNiwxNy4zLDMwLjcsMTcuNywzMC43LDE4LjF6Ii8+Cjwvc3ZnPgo=";

/***/ }),

/***/ 99874:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJFYmVuZV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDQyLjUgNDIuNSIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNDIuNSA0Mi41OyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cGF0aCBkPSJNMzQuOSwzMC44VjE2LjdjMC0wLjUtMC4yLTEtMC42LTEuNHMtMC44LTAuNi0xLjQtMC42SDE4LjhjLTAuNSwwLTEtMC4yLTEuNC0wLjZjLTAuNC0wLjQtMC42LTAuOC0wLjYtMS40di0xLjMgYzAtMC41LTAuMi0xLTAuNi0xLjRjLTAuNC0wLjQtMC44LTAuNi0xLjQtMC42SDguNWMtMC41LDAtMSwwLjItMS40LDAuNlM2LjYsMTEsNi42LDExLjV2MTkuM2MwLDAuNSwwLjIsMSwwLjYsMS40czAuOCwwLjYsMS40LDAuNiBIMzNjMC41LDAsMS0wLjIsMS40LTAuNlMzNC45LDMxLjQsMzQuOSwzMC44eiBNMzcuNSwxNi43djE0LjJjMCwxLjItMC40LDIuMy0xLjMsMy4yYy0wLjksMC45LTEuOSwxLjMtMy4yLDEuM0g4LjUgYy0xLjIsMC0yLjMtMC40LTMuMi0xLjNTNCwzMi4xLDQsMzAuOFYxMS41YzAtMS4yLDAuNC0yLjMsMS4zLTMuMlM3LjMsNyw4LjUsN0gxNWMxLjIsMCwyLjMsMC40LDMuMiwxLjNjMC45LDAuOSwxLjMsMS45LDEuMywzLjIgdjAuNkgzM2MxLjIsMCwyLjMsMC40LDMuMiwxLjNDMzcuMSwxNC40LDM3LjUsMTUuNCwzNy41LDE2Ljd6IE0xNi42LDI0LjlIOS4zdi03LjNoNy4zVjI0Ljl6IE0zMi4yLDI5LjloLTUuMVYxNy42aDUuMVYyOS45eiBNMTYuNiwyOS45SDkuM3YtMy4zaDcuM1YyOS45eiBNMjUuNSwyMi4zaC03LjN2LTQuN2g3LjNWMjIuM3ogTTI1LjUsMzBoLTcuM3YtNi4xaDcuM1YzMHoiLz4KPC9zdmc+";

/***/ }),

/***/ 96353:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjMuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsOiNGRkZGRkY7fQo8L3N0eWxlPgo8Zz4KCTxwYXRoIGQ9Ik0yNy4yLDIwSDE1LjhjLTAuMiwwLTAuMywwLTAuNCwwLjFjLTAuMSwwLjEtMC4xLDAuMi0wLjEsMC40djFjMCwwLjIsMCwwLjMsMC4xLDAuNGMwLjEsMC4xLDAuMiwwLjEsMC40LDAuMWgxMS41CgkJYzAuMiwwLDAuMywwLDAuNC0wLjFjMC4xLTAuMSwwLjEtMC4yLDAuMS0wLjR2LTFjMC0wLjIsMC0wLjMtMC4xLTAuNEMyNy41LDIwLDI3LjQsMjAsMjcuMiwyMHoiLz4KCTxwYXRoIGQ9Ik0yNy42LDE1LjljLTAuMS0wLjEtMC4yLTAuMS0wLjQtMC4xSDE1LjhjLTAuMiwwLTAuMywwLTAuNCwwLjFjLTAuMSwwLjEtMC4xLDAuMi0wLjEsMC40djFjMCwwLjIsMCwwLjMsMC4xLDAuNAoJCWMwLjEsMC4xLDAuMiwwLjEsMC40LDAuMWgxMS41YzAuMiwwLDAuMywwLDAuNC0wLjFjMC4xLTAuMSwwLjEtMC4yLDAuMS0wLjR2LTFDMjcuOCwxNi4yLDI3LjcsMTYsMjcuNiwxNS45eiIvPgoJPHBhdGggZD0iTTM2LjYsMTQuOGMtMC4zLTAuMy0wLjctMC41LTEtMC42djIuNGwwLDBsMCwwLjF2MC42YzAsMC43LTAuMiwxLjEtMC44LDJjLTAuMiwwLjMtMC41LDAuNi0wLjgsMC45di04CgkJYzAtMC40LTAuMS0wLjktMC4zLTEuNGMtMC4yLTAuNS0wLjUtMC45LTAuOC0xLjJsLTUuMS01LjFjLTAuMy0wLjMtMC43LTAuNi0xLjItMC44Yy0wLjUtMC4yLTEtMC4zLTEuNC0wLjNIMTAuNQoJCWMtMC40LDAtMC44LDAuMi0xLjEsMC41QzkuMSw0LDksNC40LDksNC44djE1LjdjLTEuMi0xLjItMi0yLjUtMi4xLTRoMHYtMC4xYzAsMCwwLDAsMCwwYzAsMCwwLDAsMCwwdi0yLjNjLTAuNCwwLjEtMC43LDAuMy0xLDAuNgoJCUM1LjMsMTUuMyw1LDE1LjksNSwxNi42djE4YzAsMC43LDAuMywxLjMsMC45LDEuOWMwLjYsMC41LDEuMiwwLjgsMiwwLjhoMjYuNmMwLjgsMCwxLjUtMC4zLDItMC44YzAuNi0wLjUsMC45LTEuMSwwLjktMS45di0xOAoJCUMzNy41LDE1LjksMzcuMiwxNS4zLDM2LjYsMTQuOHogTTI1LjcsNS41YzAuMywwLjEsMC41LDAuMiwwLjcsMC40bDUuMSw1LjFjMC4xLDAuMSwwLjMsMC40LDAuNCwwLjdoLTYuMVY1LjV6IE0yNC42LDI3LjMKCQljLTAuMywwLjMtMC42LDAuNS0wLjgsMC42Yy0wLjIsMC4xLTAuNSwwLjMtMC44LDAuNVMyMi4zLDI4LjksMjIsMjljLTAuMywwLjEtMC41LDAuMi0wLjgsMC4yaDBjLTAuMiwwLTAuNS0wLjEtMC44LTAuMgoJCWMtMC4zLTAuMS0wLjYtMC4zLTAuOS0wLjVzLTAuNi0wLjQtMC44LTAuNWMtMC4yLTAuMS0wLjUtMC40LTAuOC0wLjZjLTAuMy0wLjMtMC42LTAuNC0wLjYtMC41Yy0yLjEtMS42LTQuMi0zLjEtNi4yLTQuNlY1LjRoMTIuNQoJCXY2LjhjMCwwLjQsMC4yLDAuOCwwLjUsMS4xYzAuMywwLjMsMC43LDAuNSwxLjEsMC41aDYuOHY4LjFjLTIuMiwxLjYtNC40LDMuMi02LjcsNUMyNS4yLDI2LjksMjQuOSwyNywyNC42LDI3LjN6Ii8+CjwvZz4KPGc+Cgk8cGF0aCBjbGFzcz0ic3QwIiBkPSJNMTYuMiwzMC41djEuOUgxN3YtMS45aDEuNHY1LjFIMTd2LTIuMWgtMC45djIuMWgtMS40di01LjFIMTYuMnoiLz4KCTxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0yMS45LDMwLjVsMS4zLDUuMWgtMS41bC0wLjEtMC45aC0xLjFsLTAuMSwwLjloLTEuNWwxLjMtNS4xSDIxLjl6IE0yMSwzMS42bC0wLjQsMi4yaDAuOEwyMSwzMS42TDIxLDMxLjZ6IgoJCS8+Cgk8cGF0aCBjbGFzcz0ic3QwIiBkPSJNMjUuNSwzMC41bDAuNiwzaDBsMC42LTNoMi4xdjUuMWgtMS4zdi0zLjhoMGwtMC44LDMuOGgtMWwtMC44LTMuOGgwdjMuOGgtMS4zdi01LjFIMjUuNXoiLz4KPC9nPgo8L3N2Zz4K";

/***/ }),

/***/ 27127:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBpZD0iSWNvbl9QYXNzd29ydCIgZD0iTTEyLjUsMTkuNWgxNi43di00LjRjMC0yLjQtMC44LTQuNC0yLjUtNi4xYy0xLjctMS43LTMuMy0yLjUtNS43LTIuNVMxNi43LDcuMywxNSw5cy0yLjUsMy43LTIuNSw2LjEKCVYxOS41eiBNMzQuOCwyMS44djEzLjljMCwwLjYtMC4yLDEuMi0wLjcsMS42Yy0wLjUsMC41LTEsMC43LTEuNiwwLjdIOS4zYy0wLjYsMC0xLjItMC4yLTEuNi0wLjdjLTAuNS0wLjUtMC43LTEtMC43LTEuNlYyMS44CgljMC0wLjYsMC4yLTEuMiwwLjctMS42YzAuNS0wLjUsMS0wLjcsMS42LTAuN2gwLjh2LTQuNmMwLTMsMS4xLTUuNSwzLjItNy42UzE3LjksNCwyMC45LDRzNS41LDEuMSw3LjYsMy4yczMuMiw0LjcsMy4yLDcuNnY0LjZoMC44CgljMC42LDAsMS4yLDAuMiwxLjYsMC43QzM0LjYsMjAuNiwzNC44LDIxLjEsMzQuOCwyMS44eiIvPgo8L3N2Zz4K";

/***/ }),

/***/ 62699:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8Zz4KCTxwYXRoIGQ9Ik0xMi41LDE5LjV2LTQuNGMwLTIuNCwwLjgtNC40LDIuNS02LjFjMS43LTEuNywzLjUtMi41LDUuOS0yLjVzNCwwLjgsNS43LDIuNWMxLjQsMS40LDIuMiwzLDIuNSw0LjkKCQljMC4xLDAuOCwyLjcsMC40LDIuNS0wLjZjLTAuMy0yLjMtMS4zLTQuMy0zLjEtNi4xQzI2LjQsNS4xLDIzLjksNCwyMC45LDRzLTUuNSwxLjEtNy42LDMuMnMtMy4yLDQuNy0zLjIsNy42djQuNkg5LjMKCQljLTAuNiwwLTEuMiwwLjItMS42LDAuN2MtMC41LDAuNS0wLjcsMS0wLjcsMS42djEzLjljMCwwLjYsMC4yLDEuMiwwLjcsMS42czEsMC43LDEuNiwwLjdoMjMuMmMwLjYsMCwxLjItMC4yLDEuNi0wLjcKCQljMC41LTAuNSwwLjctMSwwLjctMS42VjIxLjhjMC0wLjYtMC4yLTEuMi0wLjctMS42Yy0wLjUtMC41LTEtMC43LTEuNi0wLjdMMTIuNSwxOS41eiIvPgo8L2c+Cjwvc3ZnPgo=";

/***/ }),

/***/ 31885:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64, ";

/***/ }),

/***/ 61639:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJFYmVuZV8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8Y2lyY2xlIGlkPSJlNDAwM2EiIGN4PSIyMS4yIiBjeT0iMjEuMiIgcj0iMTQuMiIvPgo8L3N2Zz4K";

/***/ }),

/***/ 11779:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMzYuNSwyNHY2YzAsMS41LTAuNSwyLjgtMS42LDMuOHMtMi4zLDEuNi0zLjgsMS42SDEwLjRjLTEuNSwwLTIuOC0wLjUtMy44LTEuNlM1LDMxLjQsNSwzMFYxMi40YzAtMS41LDAuNS0yLjgsMS42LTMuOAoJUzguOSw3LDEwLjQsN2gyMC42YzAuOCwwLDEuNSwwLjIsMi4yLDAuNWMwLjIsMC4xLDAuMywwLjIsMC4zLDAuNGMwLDAuMiwwLDAuNC0wLjIsMC41bC0wLjksMC45Yy0wLjEsMC4xLTAuMywwLjItMC40LDAuMgoJYzAsMC0wLjEsMC0wLjIsMGMtMC4zLTAuMS0wLjYtMC4xLTAuOC0wLjFIMTAuNGMtMC44LDAtMS41LDAuMy0yLjEsMC45Yy0wLjYsMC42LTAuOSwxLjMtMC45LDIuMVYzMGMwLDAuOCwwLjMsMS41LDAuOSwyLjEKCWMwLjYsMC42LDEuMywwLjksMi4xLDAuOWgyMC42YzAuOCwwLDEuNS0wLjMsMi4xLTAuOWMwLjYtMC42LDAuOS0xLjMsMC45LTIuMXYtNC44YzAtMC4yLDAuMS0wLjMsMC4yLTAuNGwxLjItMS4yCgljMC4xLTAuMSwwLjMtMC4yLDAuNC0wLjJjMC4xLDAsMC4yLDAsMC4yLDAuMUMzNi4zLDIzLjYsMzYuNSwyMy43LDM2LjUsMjR6IE0zMy40LDE1LjZMMjAuNywyOC40Yy0wLjMsMC4zLTAuNiwwLjQtMSwwLjQKCWMtMC40LDAtMC43LTAuMS0xLTAuNGwtNi4xLTYuMWMtMC4zLTAuMy0wLjQtMC42LTAuNC0xczAuMS0wLjcsMC40LTFsMC40LTAuNGMwLjMtMC4zLDAuNi0wLjQsMS0wLjRjMC40LDAsMC43LDAuMSwxLDAuNGw0LjYsNC42CglMMzEsMTMuMWMwLjMtMC4zLDAuNi0wLjQsMS0wLjRjMC40LDAsMC43LDAuMSwxLDAuNGwwLjUsMC41YzAuMywwLjMsMC40LDAuNiwwLjQsMUMzMy45LDE1LDMzLjcsMTUuMywzMy40LDE1LjZ6Ii8+Cjwvc3ZnPgo=";

/***/ }),

/***/ 40997:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMTcuNCwxOC4ydjEwLjZjMCwwLjItMC4xLDAuMy0wLjIsMC40Yy0wLjEsMC4xLTAuMywwLjItMC40LDAuMmgtMS4yYy0wLjIsMC0wLjMtMC4xLTAuNC0wLjJjLTAuMS0wLjEtMC4yLTAuMy0wLjItMC40CglWMTguMmMwLTAuMiwwLjEtMC4zLDAuMi0wLjRjMC4xLTAuMSwwLjMtMC4yLDAuNC0wLjJoMS4yYzAuMiwwLDAuMywwLjEsMC40LDAuMkMxNy40LDE3LjksMTcuNCwxOCwxNy40LDE4LjJ6IE0yMi4yLDE4LjJ2MTAuNgoJYzAsMC4yLTAuMSwwLjMtMC4yLDAuNGMtMC4xLDAuMS0wLjMsMC4yLTAuNCwwLjJoLTEuMmMtMC4yLDAtMC4zLTAuMS0wLjQtMC4yYy0wLjEtMC4xLTAuMi0wLjMtMC4yLTAuNFYxOC4yYzAtMC4yLDAuMS0wLjMsMC4yLTAuNAoJYzAuMS0wLjEsMC4zLTAuMiwwLjQtMC4yaDEuMmMwLjIsMCwwLjMsMC4xLDAuNCwwLjJDMjIuMSwxNy45LDIyLjIsMTgsMjIuMiwxOC4yeiBNMjYuOSwxOC4ydjEwLjZjMCwwLjItMC4xLDAuMy0wLjIsMC40CgljLTAuMSwwLjEtMC4zLDAuMi0wLjQsMC4yaC0xLjJjLTAuMiwwLTAuMy0wLjEtMC40LTAuMmMtMC4xLTAuMS0wLjItMC4zLTAuMi0wLjRWMTguMmMwLTAuMiwwLjEtMC4zLDAuMi0wLjQKCWMwLjEtMC4xLDAuMy0wLjIsMC40LTAuMmgxLjJjMC4yLDAsMC4zLDAuMSwwLjQsMC4yQzI2LjgsMTcuOSwyNi45LDE4LDI2LjksMTguMnogTTI5LjMsMzEuNlYxNC4xSDEyLjd2MTcuNWMwLDAuMywwLDAuNSwwLjEsMC43CgljMC4xLDAuMiwwLjIsMC40LDAuMywwLjVjMC4xLDAuMSwwLjIsMC4yLDAuMiwwLjJoMTUuNGMwLDAsMC4xLTAuMSwwLjItMC4yYzAuMS0wLjEsMC4yLTAuMywwLjMtMC41QzI5LjIsMzIuMSwyOS4zLDMxLjksMjkuMywzMS42Cgl6IE0xNi45LDExLjdoOC4zbC0wLjktMi4yYy0wLjEtMC4xLTAuMi0wLjItMC4zLTAuMmgtNS45Yy0wLjEsMC0wLjIsMC4xLTAuMywwLjJMMTYuOSwxMS43eiBNMzQsMTIuM3YxLjJjMCwwLjItMC4xLDAuMy0wLjIsMC40CgljLTAuMSwwLjEtMC4zLDAuMi0wLjQsMC4yaC0xLjh2MTcuNWMwLDEtMC4zLDEuOS0wLjksMi42cy0xLjMsMS4xLTIuMSwxLjFIMTMuM2MtMC44LDAtMS41LTAuNC0yLjEtMS4xYy0wLjYtMC43LTAuOS0xLjYtMC45LTIuNgoJVjE0LjFIOC42Yy0wLjIsMC0wLjMtMC4xLTAuNC0wLjJTOCwxMy43LDgsMTMuNXYtMS4yYzAtMC4yLDAuMS0wLjMsMC4yLTAuNHMwLjMtMC4yLDAuNC0wLjJoNS43bDEuMy0zLjFjMC4yLTAuNSwwLjUtMC44LDEtMS4yCglDMTcuMSw3LjIsMTcuNSw3LDE4LDdoNS45YzAuNSwwLDEsMC4yLDEuNSwwLjVjMC41LDAuMywwLjgsMC43LDEsMS4ybDEuMywzLjFoNS43YzAuMiwwLDAuMywwLjEsMC40LDAuMkMzMy45LDEyLDM0LDEyLjEsMzQsMTIuM3oiCgkvPgo8L3N2Zz4K";

/***/ }),

/***/ 50938:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMTgsMTQuOGMwLjEsMC4xLDAuMiwwLjMsMC4yLDAuNHYxMC42YzAsMC4yLTAuMSwwLjMtMC4yLDAuNHMtMC4zLDAuMi0wLjQsMC4yaC0xLjJjLTAuMiwwLTAuMy0wLjEtMC40LTAuMgoJcy0wLjItMC4zLTAuMi0wLjRWMTUuMmMwLTAuMiwwLjEtMC4zLDAuMi0wLjRjMC4xLTAuMSwwLjMtMC4yLDAuNC0wLjJoMS4yQzE3LjgsMTQuNiwxNy45LDE0LjcsMTgsMTQuOHogTTEyLjksMTQuNmgtMS4yCgljLTAuMiwwLTAuMywwLjEtMC40LDAuMmMtMC4xLDAuMS0wLjIsMC4zLTAuMiwwLjR2MTAuNmMwLDAuMiwwLjEsMC4zLDAuMiwwLjRjMC4xLDAuMSwwLjMsMC4yLDAuNCwwLjJoMS4yYzAuMiwwLDAuMy0wLjEsMC40LTAuMgoJczAuMi0wLjMsMC4yLTAuNFYxNS4yYzAtMC4yLTAuMS0wLjMtMC4yLTAuNEMxMy4yLDE0LjcsMTMsMTQuNiwxMi45LDE0LjZ6IE0zOC4yLDI4YzAsMS44LTAuNCwzLjUtMS4zLDVjLTAuOSwxLjUtMi4xLDIuNy0zLjYsMy42CgljLTEuNSwwLjktMy4yLDEuMy01LDEuM3MtMy41LTAuNC01LTEuM2MtMS41LTAuOS0yLjctMi4xLTMuNi0zLjZjLTAuMS0wLjItMC4yLTAuNC0wLjMtMC43SDkuM2MtMC44LDAtMS41LTAuNC0yLjEtMS4xCglzLTAuOS0xLjYtMC45LTIuNlYxMS4xSDQuNmMtMC4yLDAtMC4zLTAuMS0wLjQtMC4yQzQuMSwxMC44LDQsMTAuNyw0LDEwLjVWOS4zQzQsOS4xLDQuMSw5LDQuMiw4LjlzMC4zLTAuMiwwLjQtMC4yaDUuN2wxLjMtMy4xCgljMC4yLTAuNSwwLjUtMC44LDEtMS4yQzEzLjEsNC4yLDEzLjUsNCwxNCw0aDUuOWMwLjUsMCwxLDAuMiwxLjUsMC41YzAuNSwwLjMsMC44LDAuNywxLDEuMmwxLjMsMy4xaDUuN2MwLjIsMCwwLjMsMC4xLDAuNCwwLjIKCVMzMCw5LjEsMzAsOS4zdjEuMmMwLDAuMi0wLjEsMC4zLTAuMiwwLjRjLTAuMSwwLjEtMC4zLDAuMi0wLjQsMC4yaC0xLjh2Ny4xYzAuMiwwLDAuNCwwLDAuNywwYzEuOCwwLDMuNSwwLjQsNSwxLjMKCWMxLjUsMC45LDIuNywyLjEsMy42LDMuNkMzNy44LDI0LjYsMzguMiwyNi4yLDM4LjIsMjh6IE0xMi45LDguN2g4LjNsLTAuOS0yLjJjLTAuMS0wLjEtMC4yLTAuMi0wLjMtMC4yaC01LjkKCWMtMC4xLDAtMC4yLDAuMS0wLjMsMC4yTDEyLjksOC43eiBNMTkuNywyM2MwLjItMC40LDAuNS0wLjgsMC44LTEuMnYtNi42YzAtMC4yLDAuMS0wLjMsMC4yLTAuNGMwLjEtMC4xLDAuMy0wLjIsMC40LTAuMmgxLjIKCWMwLjIsMCwwLjMsMC4xLDAuNCwwLjJjMC4xLDAuMSwwLjIsMC4zLDAuMiwwLjR2NC41YzAuMS0wLjEsMC4zLTAuMiwwLjQtMC4zYzAuNi0wLjQsMS4zLTAuNiwxLjktMC44di03LjVIOC43djE3LjUKCWMwLDAuMywwLDAuNSwwLjEsMC43czAuMiwwLjQsMC4zLDAuNUM5LjIsMjkuOSw5LjMsMzAsOS4zLDMwaDkuM2MtMC4xLTAuNi0wLjItMS4zLTAuMi0yQzE4LjQsMjYuMiwxOC44LDI0LjYsMTkuNywyM3ogTTM2LjIsMjgKCWMwLTEuNC0wLjQtMi44LTEuMS00Yy0wLjctMS4yLTEuNy0yLjItMi45LTIuOXMtMi41LTEuMS00LTEuMXMtMi44LDAuNC00LDEuMXMtMi4yLDEuNy0yLjksMi45Yy0wLjcsMS4yLTEuMSwyLjUtMS4xLDQKCXMwLjQsMi44LDEuMSw0czEuNywyLjIsMi45LDIuOXMyLjUsMS4xLDQsMS4xczIuOC0wLjQsNC0xLjFzMi4yLTEuNywyLjktMi45UzM2LjIsMjkuNSwzNi4yLDI4eiBNMzIuNiwzMS4yTDI5LjQsMjhsMy4yLTMuMgoJYzAuMS0wLjEsMC4xLTAuMiwwLjEtMC4zYzAtMC4xLDAtMC4yLTAuMS0wLjNMMzIsMjMuN2MtMC4xLTAuMS0wLjItMC4xLTAuMy0wLjFjLTAuMSwwLTAuMiwwLTAuMywwLjFsLTMuMiwzLjJsLTMuMi0zLjIKCWMtMC4xLTAuMS0wLjItMC4xLTAuMy0wLjFjLTAuMSwwLTAuMiwwLTAuMywwLjFMMjQsMjQuM2MtMC4xLDAuMS0wLjEsMC4yLTAuMSwwLjNjMCwwLjEsMCwwLjIsMC4xLDAuM2wzLjIsMy4yTDI0LDMxLjIKCWMtMC4xLDAuMS0wLjEsMC4yLTAuMSwwLjNjMCwwLjEsMCwwLjIsMC4xLDAuM2wwLjYsMC42YzAuMSwwLjEsMC4yLDAuMSwwLjMsMC4xYzAuMSwwLDAuMiwwLDAuMy0wLjFsMy4yLTMuMmwzLjIsMy4yCgljMC4xLDAuMSwwLjIsMC4xLDAuMywwLjFjMC4xLDAsMC4yLDAsMC4zLTAuMWwwLjYtMC42YzAuMS0wLjEsMC4xLTAuMiwwLjEtMC4zQzMyLjcsMzEuNCwzMi43LDMxLjMsMzIuNiwzMS4yeiIvPgo8L3N2Zz4K";

/***/ }),

/***/ 38861:
/***/ ((module) => {

"use strict";
module.exports = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIxLjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0Mi41IDQyLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyLjUgNDIuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8cGF0aCBkPSJNMzMuOCwxMS45Yy0wLjEtMC4xLTAuMy0wLjItMC40LTAuMmgtNS43bC0xLjMtMy4xYy0wLjItMC41LTAuNS0wLjgtMS0xLjJjLTAuNS0wLjMtMS0wLjUtMS41LTAuNUgxOGMtMC41LDAtMSwwLjItMS41LDAuNQoJYy0wLjUsMC4zLTAuOCwwLjctMSwxLjJsLTEuMywzLjFIOC42Yy0wLjIsMC0wLjMsMC4xLTAuNCwwLjJTOCwxMi4xLDgsMTIuM3YxLjJjMCwwLjIsMC4xLDAuMywwLjIsMC40YzAuMSwwLjEsMC4zLDAuMiwwLjQsMC4yaDEuOAoJdjE3LjZjMCwxLDAuMywxLjksMC45LDIuNnMxLjMsMS4xLDIuMSwxLjFoMTUuNGMwLjgsMCwxLjUtMC40LDIuMS0xLjFzMC45LTEuNiwwLjktMi42VjE0LjFoMS44YzAuMiwwLDAuMy0wLjEsMC40LTAuMgoJYzAuMS0wLjEsMC4yLTAuMywwLjItMC40di0xLjJDMzQsMTIuMSwzMy45LDEyLDMzLjgsMTEuOXogTTE3LjgsOS42YzAuMS0wLjEsMC4yLTAuMiwwLjMtMC4yaDUuOWMwLjEsMCwwLjIsMC4xLDAuMywwLjJsMC45LDIuMgoJaC04LjNMMTcuOCw5LjZ6IE0xNy40LDI4LjVjMCwwLjItMC4xLDAuMy0wLjIsMC40cy0wLjMsMC4yLTAuNCwwLjJoLTEuMmMtMC4yLDAtMC4zLTAuMS0wLjQtMC4yYy0wLjEtMC4xLTAuMi0wLjMtMC4yLTAuNFYxNy45CgljMC0wLjIsMC4xLTAuMywwLjItMC40YzAuMS0wLjEsMC4zLTAuMiwwLjQtMC4yaDEuMmMwLjIsMCwwLjMsMC4xLDAuNCwwLjJzMC4yLDAuMywwLjIsMC40VjI4LjV6IE0yMi4yLDI4LjVjMCwwLjItMC4xLDAuMy0wLjIsMC40CglzLTAuMywwLjItMC40LDAuMmgtMS4yYy0wLjIsMC0wLjMtMC4xLTAuNC0wLjJzLTAuMi0wLjMtMC4yLTAuNFYxNy45YzAtMC4yLDAuMS0wLjMsMC4yLTAuNHMwLjMtMC4yLDAuNC0wLjJoMS4yCgljMC4yLDAsMC4zLDAuMSwwLjQsMC4yczAuMiwwLjMsMC4yLDAuNFYyOC41eiBNMjYuOSwyOC41YzAsMC4yLTAuMSwwLjMtMC4yLDAuNGMtMC4xLDAuMS0wLjMsMC4yLTAuNCwwLjJoLTEuMgoJYy0wLjIsMC0wLjMtMC4xLTAuNC0wLjJzLTAuMi0wLjMtMC4yLTAuNFYxNy45YzAtMC4yLDAuMS0wLjMsMC4yLTAuNHMwLjMtMC4yLDAuNC0wLjJoMS4yYzAuMiwwLDAuMywwLjEsMC40LDAuMgoJYzAuMSwwLjEsMC4yLDAuMywwLjIsMC40VjI4LjV6Ii8+Cjwvc3ZnPgo=";

/***/ }),

/***/ 65147:
/***/ ((module) => {

"use strict";
module.exports = "data:image/gif;base64,R0lGODlhEgASAMQaAHl5d66urMXFw3l5dpSUk5WVlKOjoq+vrsbGw6Sko7u7uaWlpbm5t3h4doiIhtLSz4aGhJaWlsbGxNHRzrCwr5SUkqKiobq6uNHRz4eHhf///wAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFAAAaACwAAAAAEgASAAAFaaAmjmRplstyrkmbrCNFaUZtaFF0HvyhWRZNYVgwBY4BEmFJOB1NlYpJoYBpHI7RZXtZZb4ZEbd7AodFDIYVAjFJJCYA4ISoI0hyuUnAF2geDxoDgwMnfBoYiRgaDQ1WiIqPJBMTkpYaIQAh+QQFAAAaACwBAAEAEAAQAAAFY6AmjhpFkSh5rEc6KooWzIG2LOilX3Kd/AnSjjcyGA0oBiNlsZAkEtcoEtEgrghpYVsQeAVSgpig8UpFlQrp8Ug5HCiMHEPK2DOkOR0A0NzxJBMTGnx8GhAQZwOLA2ckDQ0uIQAh+QQFAAAaACwBAAEAEAAQAAAFZKAmjpqikCh5rVc6SpLGthSFIjiiMYx2/AeSYCggBY4B1DB1JD0ertFiocFYMdGENnHFugxgg2YyiYosFhIAkIpEUOs1qUAvkAb4gcbh0BD+BCgNDRoZhhkaFRVmh4hmIxAQLiEAIfkEBQAAGgAsAQABABAAEAAABWOgJo6aJJEoiaxIOj6PJsyCpigopmNyff0X0o43AgZJk0mKwSABAK4RhaJ5PqOH7GHAHUQD4ICm0YiKwCSHI7VYoDLwDClBT5Di8khEY+gbUBAQGgWEBRoWFmYEiwRmJBUVLiEAIfkEBQAAGgAsAQABABAAEAAABWSgJo7a85Aoia1YOgKAxraShMKwNk0a4iOkgXBAEhgFqEYjZSQ5HK6RQqHJWDPRi/Zyxbq2Fw0EEhUxGKRIJEWhoArwAulAP5AIeIJmsdAE/gEoFRUaCYYJfoFRBowGZSQWFi4hACH5BAUAABoALAEAAQAQABAAAAVloCaOGgCQKGma6eg42iAP2vOgWZ5pTaNhQAxJtxsFhSQIJDWZkCKR1kgi0RSuBSliiyB4CVKBWKCpVKQiMWmxSCkUqIQ8QbrYLySD3qChUDR3eCQWFhoHhwcaDAxoAY4BaCSOLSEAIfkEBQAAGgAsAQABABAAEAAABWOgJo6a45Aoma1ZOkaRxrYAgBZ4oUGQVtckgpBAGhgHqEol1WiQFgvX6PHQJK4JKWaLMXgNWq7GYpGKJhMShZKSSFCH+IGEqCNIgXxAo1BoBIACKHkaF4YXf4JSh4hmIwwMLiEAIfkEBQAAGgAsAQABABAAEAAABWSgJo5aFJEoWaxFOi6LRsyE5jhooidaVWmZYIZkKBpIwiHJYklBICQKxTUCADSH7IFqtQa+AepgPNB8qaJGg6RQpB4P1GV+IWHuGBK9LpFo8HkkDAwaCIYIGhMTaAKNAmgkjS4hADs=";

/***/ })

}]);