Source: modules/quickedit/js/util.es6.js

/**
 * @file
 * Provides utility functions for Quick Edit.
 */

(function ($, Drupal) {
  /**
   * @namespace
   */
  Drupal.quickedit.util = Drupal.quickedit.util || {};

  /**
   * @namespace
   */
  Drupal.quickedit.util.constants = {};

  /**
   *
   * @type {string}
   */
  Drupal.quickedit.util.constants.transitionEnd =
    'transitionEnd.quickedit webkitTransitionEnd.quickedit transitionend.quickedit msTransitionEnd.quickedit oTransitionEnd.quickedit';

  /**
   * Converts a field id into a formatted url path.
   *
   * @example
   * Drupal.quickedit.util.buildUrl(
   *   'node/1/body/und/full',
   *   '/quickedit/form/!entity_type/!id/!field_name/!langcode/!view_mode'
   * );
   *
   * @param {string} id
   *   The id of an editable field.
   * @param {string} urlFormat
   *   The Controller route for field processing.
   *
   * @return {string}
   *   The formatted URL.
   */
  Drupal.quickedit.util.buildUrl = function (id, urlFormat) {
    const parts = id.split('/');
    return Drupal.formatString(decodeURIComponent(urlFormat), {
      '!entity_type': parts[0],
      '!id': parts[1],
      '!field_name': parts[2],
      '!langcode': parts[3],
      '!view_mode': parts[4],
    });
  };

  /**
   * Shows a network error modal dialog.
   *
   * @param {string} title
   *   The title to use in the modal dialog.
   * @param {string} message
   *   The message to use in the modal dialog.
   */
  Drupal.quickedit.util.networkErrorModal = function (title, message) {
    const $message = $(`<div>${message}</div>`);
    const networkErrorModal = Drupal.dialog($message.get(0), {
      title,
      dialogClass: 'quickedit-network-error',
      buttons: [
        {
          text: Drupal.t('OK'),
          click() {
            networkErrorModal.close();
          },
          primary: true,
        },
      ],
      create() {
        $(this).parent().find('.ui-dialog-titlebar-close').remove();
      },
      close(event) {
        // Automatically destroy the DOM element that was used for the dialog.
        $(event.target).remove();
      },
    });
    networkErrorModal.showModal();
  };

  /**
   * @namespace
   */
  Drupal.quickedit.util.form = {
    /**
     * Loads a form, calls a callback to insert.
     *
     * Leverages {@link Drupal.Ajax}' ability to have scoped (per-instance)
     * command implementations to be able to call a callback.
     *
     * @param {object} options
     *   An object with the following keys:
     * @param {string} options.fieldID
     *   The field ID that uniquely identifies the field for which this form
     *   will be loaded.
     * @param {bool} options.nocssjs
     *   Boolean indicating whether no CSS and JS should be returned (necessary
     *   when the form is invisible to the user).
     * @param {bool} options.reset
     *   Boolean indicating whether the data stored for this field's entity in
     *   PrivateTempStore should be used or reset.
     * @param {function} callback
     *   A callback function that will receive the form to be inserted, as well
     *   as the ajax object, necessary if the callback wants to perform other
     *   Ajax commands.
     */
    load(options, callback) {
      const fieldID = options.fieldID;

      // Create a Drupal.ajax instance to load the form.
      const formLoaderAjax = Drupal.ajax({
        url: Drupal.quickedit.util.buildUrl(
          fieldID,
          Drupal.url(
            'quickedit/form/!entity_type/!id/!field_name/!langcode/!view_mode',
          ),
        ),
        submit: {
          nocssjs: options.nocssjs,
          reset: options.reset,
        },
        error(xhr, url) {
          // Show a modal to inform the user of the network error.
          const fieldLabel = Drupal.quickedit.metadata.get(fieldID, 'label');
          const message = Drupal.t(
            'Could not load the form for <q>@field-label</q>, either due to a website problem or a network connection problem.<br>Please try again.',
            { '@field-label': fieldLabel },
          );
          Drupal.quickedit.util.networkErrorModal(
            Drupal.t('Network problem!'),
            message,
          );

          // Change the state back to "candidate", to allow the user to start
          // in-place editing of the field again.
          const fieldModel = Drupal.quickedit.app.model.get('activeField');
          fieldModel.set('state', 'candidate');
        },
      });
      // Implement a scoped quickeditFieldForm AJAX command: calls the callback.
      formLoaderAjax.commands.quickeditFieldForm = function (
        ajax,
        response,
        status,
      ) {
        callback(response.data, ajax);
        Drupal.ajax.instances[this.instanceIndex] = null;
      };
      // This will ensure our scoped quickeditFieldForm AJAX command gets
      // called.
      formLoaderAjax.execute();
    },

    /**
     * Creates a {@link Drupal.Ajax} instance that is used to save a form.
     *
     * @param {object} options
     *   Submit options to the form.
     * @param {bool} options.nocssjs
     *   Boolean indicating whether no CSS and JS should be returned (necessary
     *   when the form is invisible to the user).
     * @param {Array.<string>} options.other_view_modes
     *   Array containing view mode IDs (of other instances of this field on the
     *   page).
     * @param {jQuery} $submit
     *   The submit element.
     *
     * @return {Drupal.Ajax}
     *   A {@link Drupal.Ajax} instance.
     */
    ajaxifySaving(options, $submit) {
      // Re-wire the form to handle submit.
      const settings = {
        url: $submit.closest('form').attr('action'),
        setClick: true,
        event: 'click.quickedit',
        progress: false,
        submit: {
          nocssjs: options.nocssjs,
          other_view_modes: options.other_view_modes,
        },

        /**
         * Reimplement the success handler.
         *
         * Ensure {@link Drupal.attachBehaviors} does not get called on the
         * form.
         *
         * @param {Drupal.AjaxCommands~commandDefinition} response
         *   The Drupal AJAX response.
         * @param {number} [status]
         *   The HTTP status code.
         */
        success(response, status) {
          Object.keys(response || {}).forEach((i) => {
            if (response[i].command && this.commands[response[i].command]) {
              this.commands[response[i].command](this, response[i], status);
            }
          });
        },
        base: $submit.attr('id'),
        element: $submit[0],
      };

      return Drupal.ajax(settings);
    },

    /**
     * Cleans up the {@link Drupal.Ajax} instance that is used to save the form.
     *
     * @param {Drupal.Ajax} ajax
     *   A {@link Drupal.Ajax} instance that was returned by
     *   {@link Drupal.quickedit.form.ajaxifySaving}.
     */
    unajaxifySaving(ajax) {
      $(ajax.element).off('click.quickedit');
    },
  };
})(jQuery, Drupal);