text: /**
   * Copyright © Magento, Inc. All rights reserved.
   * See COPYING.txt for license details.
   */
  
  /**
   * @api
   */
  define([
      'Magento_Ui/js/lib/view/utils/async',
      'uiCollection',
      'uiRegistry',
      'underscore',
      './modal'
  ], function ($, Collection, registry, _) {
      'use strict';
  
      return Collection.extend({
          defaults: {
              template: 'ui/modal/modal-component',
              title: '',
              subTitle: '',
              options: {
                  modalClass: '',
                  title: '',
                  subTitle: '',
                  buttons: [],
                  keyEventHandlers: {}
              },
              valid: true,
              links: {
                  title: 'options.title',
                  subTitle: 'options.subTitle'
              },
              listens: {
                  state: 'onState',
                  title: 'setTitle',
                  'options.subTitle': 'setSubTitle'
              },
              modalClass: 'modal-component',
              onCancel: 'closeModal'
          },
  
          /**
           * Initializes component.
           *
           * @returns {Object} Chainable.
           */
          initialize: function () {
              this._super();
              _.bindAll(this,
                  'initModal',
                  'openModal',
                  'closeModal',
                  'toggleModal',
                  'setPrevValues',
                  'validate');
              this.initializeContent();
  
              return this;
          },
  
          /**
           * Initializes modal configuration
           *
           * @returns {Object} Chainable.
           */
          initConfig: function () {
              return this._super()
                  .initSelector()
                  .initModalEvents();
          },
  
          /**
           * Configure modal selector
           *
           * @returns {Object} Chainable.
           */
          initSelector: function () {
              var modalClass = this.name.replace(/\./g, '_');
  
              this.contentSelector = '.' + this.modalClass;
              this.options.modalClass = this.options.modalClass + ' ' + modalClass;
              this.rootSelector = '.' + modalClass;
  
              return this;
          },
  
          /**
           * Configure modal keyboard handlers
           * and outer click
           *
           * @returns {Object} Chainable.
           */
          initModalEvents: function () {
              this.options.keyEventHandlers.escapeKey = this.options.outerClickHandler = this[this.onCancel].bind(this);
  
              return this;
          },
  
          /**
           * Initialize modal's content components
           */
          initializeContent: function () {
              $.async({
                  component: this.name
              }, this.initModal);
          },
  
          /**
           * Init toolbar section so other components will be able to place something in it
           */
          initToolbarSection: function () {
              this.set('toolbarSection', this.modal.data('mage-modal').modal.find('header').get(0));
          },
  
          /**
           * Initializes observable properties.
           *
           * @returns {Object} Chainable.
           */
          initObservable: function () {
              this._super();
              this.observe(['state', 'focused']);
  
              return this;
          },
  
          /**
           * Wrap content in a modal of certain type
           *
           * @param {HTMLElement} element
           * @returns {Object} Chainable.
           */
          initModal: function (element) {
              if (!this.modal) {
                  this.overrideModalButtonCallback();
                  this.options.modalCloseBtnHandler = this[this.onCancel].bind(this);
                  this.modal = $(element).modal(this.options);
                  this.initToolbarSection();
  
                  if (this.waitCbk) {
                      this.waitCbk();
                      this.waitCbk = null;
                  }
              }
  
              return this;
          },
  
          /**
           * Open modal
           */
          openModal: function () {
              if (this.modal) {
                  this.state(true);
              } else {
                  this.waitCbk = this.openModal;
              }
          },
  
          /**
           * Close modal
           */
          closeModal: function () {
              if (this.modal) {
                  this.state(false);
              } else {
                  this.waitCbk = this.closeModal;
              }
          },
  
          /**
           * Toggle modal
           */
          toggleModal: function () {
              if (this.modal) {
                  this.state(!this.state());
              } else {
                  this.waitCbk = this.toggleModal;
              }
          },
  
          /**
           * Sets title for modal
           *
           * @param {String} title
           */
          setTitle: function (title) {
              if (this.title !== title) {
                  this.title = title;
              }
  
              if (this.modal) {
                  this.modal.modal('setTitle', title);
              }
          },
  
          /**
           * Sets subTitle for modal
           *
           * @param {String} subTitle
           */
          setSubTitle: function (subTitle) {
              if (this.subTitle !== subTitle) {
                  this.subTitle = subTitle;
              }
  
              if (this.modal) {
                  this.modal.modal('setSubTitle', subTitle);
              }
          },
  
          /**
           * Wrap content in a modal of certain type
           *
           * @param {Boolean} state
           */
          onState: function (state) {
              if (state) {
                  this.modal.modal('openModal');
                  this.applyData();
              } else {
                  this.modal.modal('closeModal');
              }
          },
  
          /**
           * Validate everything validatable in modal
           */
          validate: function (elem) {
              if (typeof elem.validate === 'function') {
                  this.valid = this.valid & elem.validate().valid;
              } else if (elem.elems) {
                  elem.elems().forEach(this.validate, this);
              }
          },
  
          /**
           * Reset data from provider
           */
          resetData: function () {
              this.elems().forEach(this.resetValue, this);
          },
  
          /**
           * Update 'applied' property with data from modal content
           */
          applyData: function () {
              var applied = {};
  
              this.elems().forEach(this.gatherValues.bind(this, applied), this);
              this.applied = applied;
          },
  
          /**
           * Gather values from modal content
           *
           * @param {Array} applied
           * @param {HTMLElement} elem
           */
          gatherValues: function (applied, elem) {
              if (typeof elem.value === 'function') {
                  applied[elem.name] = elem.value();
              } else if (elem.elems) {
                  elem.elems().forEach(this.gatherValues.bind(this, applied), this);
              }
          },
  
          /**
           * Set to previous values from modal content
           *
           * @param {HTMLElement} elem
           */
          setPrevValues: function (elem) {
              if (typeof elem.value === 'function') {
                  this.modal.focus();
                  elem.value(this.applied[elem.name]);
              } else if (elem.elems) {
                  elem.elems().forEach(this.setPrevValues, this);
              }
          },
  
          /**
           * Triggers some method in every modal child elem, if this method is defined
           *
           * @param {Object} action - action configuration,
           * must contain actionName and targetName and
           * can contain params
           */
          triggerAction: function (action) {
              var targetName = action.targetName,
                  params = action.params || [],
                  actionName = action.actionName,
                  target;
  
              target = registry.async(targetName);
  
              if (target && typeof target === 'function' && actionName) {
                  params.unshift(actionName);
                  target.apply(target, params);
              }
          },
  
          /**
           * Override modal buttons callback placeholders with real callbacks
           */
          overrideModalButtonCallback: function () {
              var buttons = this.options.buttons;
  
              if (buttons && buttons.length) {
                  buttons.forEach(function (button) {
                      button.click = this.getButtonClickHandler(button.actions);
                  }, this);
              }
          },
  
          /**
           * Generate button click handler based on button's 'actions' configuration
           */
          getButtonClickHandler: function (actionsConfig) {
              var actions = actionsConfig.map(
                  function (actionConfig) {
                      if (_.isObject(actionConfig)) {
                          return this.triggerAction.bind(this, actionConfig);
                      }
  
                      return this[actionConfig] ? this[actionConfig].bind(this) : function () {};
                  }, this);
  
              return function () {
                  actions.forEach(
                      function (action) {
                          action();
                      }
                  );
              };
          },
  
          /**
           * Cancels changes in modal:
           * returning elems values to the previous state,
           * and close modal
           */
          actionCancel: function () {
              this.elems().forEach(this.setPrevValues, this);
              this.closeModal();
          },
  
          /**
           * Accept changes in modal by not preventing them.
           * Can be extended by exporting 'gatherValues' result somewhere
           */
          actionDone: function () {
              this.valid = true;
              this.elems().forEach(this.validate, this);
  
              if (this.valid) {
                  this.closeModal();
              }
          }
      });
  });
  