Verified Commit db089644 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3324062 by effulgentsia, nod_, longwave, alexpott, catch, lauriii,...

Issue #3324062 by effulgentsia, nod_, longwave, alexpott, catch, lauriii, andypost: [Regression] Changes to Drupal.ajax in 9.5.x have caused regressions in paragraphs_features module
parent d2cc35b9
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
@@ -517,6 +517,9 @@
    ajax.options = {
      url: ajax.url,
      data: ajax.submit,
      isInProgress() {
        return ajax.ajaxing;
      },
      beforeSerialize(elementSettings, options) {
        return ajax.beforeSerialize(elementSettings, options);
      },
@@ -564,6 +567,17 @@
            // finished executing.
            .then(() => {
              ajax.ajaxing = false;
              // jQuery normally triggers the ajaxSuccess, ajaxComplete, and
              // ajaxStop events after the "success" function passed to $.ajax()
              // returns, but we prevented that via
              // $.event.special[EVENT_NAME].trigger in order to wait for the
              // commands to finish executing. Now that they have, re-trigger
              // those events.
              $(document).trigger('ajaxSuccess', [xmlhttprequest, this]);
              $(document).trigger('ajaxComplete', [xmlhttprequest, this]);
              if (--$.active === 0) {
                $(document).trigger('ajaxStop');
              }
            })
        );
      },
@@ -1747,4 +1761,50 @@
      });
    },
  };

  /**
   * Delay jQuery's global completion events until after commands have executed.
   *
   * jQuery triggers the ajaxSuccess, ajaxComplete, and ajaxStop events after
   * a successful response is returned and local success and complete events
   * are triggered. However, Drupal Ajax responses contain commands that run
   * asynchronously in a queue, so the following stops these events from getting
   * triggered until after the Promise that executes the command queue is
   * resolved.
   */
  const stopEvent = (xhr, settings) => {
    return (
      // Only interfere with Drupal's Ajax responses.
      xhr.getResponseHeader('X-Drupal-Ajax-Token') === '1' &&
      // The isInProgress() function might not be defined if the Ajax request
      // was initiated without Drupal.ajax() or new Drupal.Ajax().
      settings.isInProgress &&
      // Until this is false, the Ajax request isn't completely done (the
      // response's commands might still be running).
      settings.isInProgress()
    );
  };
  $.extend(true, $.event.special, {
    ajaxSuccess: {
      trigger(event, xhr, settings) {
        if (stopEvent(xhr, settings)) {
          return false;
        }
      },
    },
    ajaxComplete: {
      trigger(event, xhr, settings) {
        if (stopEvent(xhr, settings)) {
          // jQuery decrements its internal active ajax counter even when we
          // stop the ajaxComplete event, but we don't want that counter
          // decremented, because for our purposes this request is still active
          // while commands are executing. By incrementing it here, the net
          // effect is that it remains unchanged. By remaining above 0, the
          // ajaxStop event is also prevented.
          $.active++;
          return false;
        }
      },
    },
  });
})(jQuery, window, Drupal, drupalSettings, loadjs, window.tabbable);
+37 −8
Original line number Diff line number Diff line
@@ -179,6 +179,9 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
    ajax.options = {
      url: ajax.url,
      data: ajax.submit,
      isInProgress: function isInProgress() {
        return ajax.ajaxing;
      },
      beforeSerialize: function beforeSerialize(elementSettings, options) {
        return ajax.beforeSerialize(elementSettings, options);
      },
@@ -191,6 +194,7 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
        return ajax.beforeSend(xmlhttprequest, options);
      },
      success: function success(response, status, xmlhttprequest) {
        var _this = this;
        if (typeof response === 'string') {
          response = $.parseJSON(response);
        }
@@ -202,6 +206,11 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
        }
        return Promise.resolve(ajax.success(response, status)).then(function () {
          ajax.ajaxing = false;
          $(document).trigger('ajaxSuccess', [xmlhttprequest, _this]);
          $(document).trigger('ajaxComplete', [xmlhttprequest, _this]);
          if (--$.active === 0) {
            $(document).trigger('ajaxStop');
          }
        });
      },
      error: function error(xmlhttprequest, status, _error) {
@@ -356,19 +365,19 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
    $('body').append(this.progress.element);
  };
  Drupal.Ajax.prototype.commandExecutionQueue = function (response, status) {
    var _this = this;
    var _this2 = this;
    var ajaxCommands = this.commands;
    return Object.keys(response || {}).reduce(function (executionQueue, key) {
      return executionQueue.then(function () {
        var command = response[key].command;
        if (command && ajaxCommands[command]) {
          return ajaxCommands[command](_this, response[key], status);
          return ajaxCommands[command](_this2, response[key], status);
        }
      });
    }, Promise.resolve());
  };
  Drupal.Ajax.prototype.success = function (response, status) {
    var _this2 = this;
    var _this3 = this;
    if (this.progress.element) {
      $(this.progress.element).remove();
    }
@@ -384,7 +393,7 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
      return command === 'focusFirst' || command === 'invoke' && method === 'focus';
    });
    return this.commandExecutionQueue(response, status).then(function () {
      if (!focusChanged && _this2.element && !$(_this2.element).data('disable-refocus')) {
      if (!focusChanged && _this3.element && !$(_this3.element).data('disable-refocus')) {
        var target = false;
        for (var n = elementParents.length - 1; !target && n >= 0; n--) {
          target = document.querySelector("[data-drupal-selector=\"".concat(elementParents[n].getAttribute('data-drupal-selector'), "\"]"));
@@ -393,11 +402,11 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
          $(target).trigger('focus');
        }
      }
      if (_this2.$form && document.body.contains(_this2.$form.get(0))) {
        var settings = _this2.settings || drupalSettings;
        Drupal.attachBehaviors(_this2.$form.get(0), settings);
      if (_this3.$form && document.body.contains(_this3.$form.get(0))) {
        var settings = _this3.settings || drupalSettings;
        Drupal.attachBehaviors(_this3.$form.get(0), settings);
      }
      _this2.settings = null;
      _this3.settings = null;
    }).catch(function (error) {
      return console.error(Drupal.t('An error occurred during the execution of the Ajax response: !error', {
        '!error': error
@@ -610,4 +619,24 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
      });
    }
  };
  var stopEvent = function stopEvent(xhr, settings) {
    return xhr.getResponseHeader('X-Drupal-Ajax-Token') === '1' && settings.isInProgress && settings.isInProgress();
  };
  $.extend(true, $.event.special, {
    ajaxSuccess: {
      trigger: function trigger(event, xhr, settings) {
        if (stopEvent(xhr, settings)) {
          return false;
        }
      }
    },
    ajaxComplete: {
      trigger: function trigger(event, xhr, settings) {
        if (stopEvent(xhr, settings)) {
          $.active++;
          return false;
        }
      }
    }
  });
})(jQuery, window, Drupal, drupalSettings, loadjs, window.tabbable);
 No newline at end of file
+6 −0
Original line number Diff line number Diff line
@@ -43,3 +43,9 @@ command_promise:
    - core/drupal
    - core/drupal.ajax
    - core/es6-promise

global_events:
  js:
    js/global_events.js: {}
  dependencies:
    - core/drupal.ajax
+14 −0
Original line number Diff line number Diff line
@@ -101,3 +101,17 @@ ajax_test.promise:
    _form: '\Drupal\ajax_test\Form\AjaxTestFormPromise'
  requirements:
    _access: 'TRUE'

ajax_test.global_events:
  path: '/ajax-test/global-events'
  defaults:
    _controller: '\Drupal\ajax_test\Controller\AjaxTestController::globalEvents'
  requirements:
    _access: 'TRUE'

ajax_test.global_events_clear_log:
  path: '/ajax-test/global-events/clear-log'
  defaults:
    _controller: '\Drupal\ajax_test\Controller\AjaxTestController::globalEventsClearLog'
  requirements:
    _access: 'TRUE'
+14 −0
Original line number Diff line number Diff line
/**
 * @file
 * For testing that jQuery's ajaxSuccess, ajaxComplete, and ajaxStop events
 * are triggered only after commands in a Drupal Ajax response are executed.
 */

(($, Drupal) => {
  ['ajaxSuccess', 'ajaxComplete', 'ajaxStop'].forEach((eventName) => {
    $(document)[eventName](() => {
      $('#test_global_events_log').append(eventName);
      $('#test_global_events_log2').append(eventName);
    });
  });
})(jQuery, Drupal);
Loading