--- /dev/null
+/**
+ * @file
+ * Extends the Drupal AJAX functionality to integrate the dialog API.
+ */
+
+(function ($, Drupal) {
+ /**
+ * Initialize dialogs for Ajax purposes.
+ *
+ * @type {Drupal~behavior}
+ *
+ * @prop {Drupal~behaviorAttach} attach
+ * Attaches the behaviors for dialog ajax functionality.
+ */
+ Drupal.behaviors.dialog = {
+ attach(context, settings) {
+ const $context = $(context);
+
+ // Provide a known 'drupal-modal' DOM element for Drupal-based modal
+ // dialogs. Non-modal dialogs are responsible for creating their own
+ // elements, since there can be multiple non-modal dialogs at a time.
+ if (!$('#drupal-modal').length) {
+ // Add 'ui-front' jQuery UI class so jQuery UI widgets like autocomplete
+ // sit on top of dialogs. For more information see
+ // http://api.jqueryui.com/theming/stacking-elements/.
+ $('<div id="drupal-modal" class="ui-front"/>').hide().appendTo('body');
+ }
+
+ // Special behaviors specific when attaching content within a dialog.
+ // These behaviors usually fire after a validation error inside a dialog.
+ const $dialog = $context.closest('.ui-dialog-content');
+ if ($dialog.length) {
+ // Remove and replace the dialog buttons with those from the new form.
+ if ($dialog.dialog('option', 'drupalAutoButtons')) {
+ // Trigger an event to detect/sync changes to buttons.
+ $dialog.trigger('dialogButtonsChange');
+ }
+
+ // Force focus on the modal when the behavior is run.
+ $dialog.dialog('widget').trigger('focus');
+ }
+
+ const originalClose = settings.dialog.close;
+ // Overwrite the close method to remove the dialog on closing.
+ settings.dialog.close = function (event) {
+ originalClose.apply(settings.dialog, arguments);
+ $(event.target).remove();
+ };
+ },
+
+ /**
+ * Scan a dialog for any primary buttons and move them to the button area.
+ *
+ * @param {jQuery} $dialog
+ * An jQuery object containing the element that is the dialog target.
+ *
+ * @return {Array}
+ * An array of buttons that need to be added to the button area.
+ */
+ prepareDialogButtons($dialog) {
+ const buttons = [];
+ const $buttons = $dialog.find('.form-actions input[type=submit], .form-actions a.button');
+ $buttons.each(function () {
+ // Hidden form buttons need special attention. For browser consistency,
+ // the button needs to be "visible" in order to have the enter key fire
+ // the form submit event. So instead of a simple "hide" or
+ // "display: none", we set its dimensions to zero.
+ // See http://mattsnider.com/how-forms-submit-when-pressing-enter/
+ const $originalButton = $(this).css({
+ display: 'block',
+ width: 0,
+ height: 0,
+ padding: 0,
+ border: 0,
+ overflow: 'hidden',
+ });
+ buttons.push({
+ text: $originalButton.html() || $originalButton.attr('value'),
+ class: $originalButton.attr('class'),
+ click(e) {
+ // If the original button is an anchor tag, triggering the "click"
+ // event will not simulate a click. Use the click method instead.
+ if ($originalButton.is('a')) {
+ $originalButton[0].click();
+ }
+ else {
+ $originalButton.trigger('mousedown').trigger('mouseup').trigger('click');
+ e.preventDefault();
+ }
+ },
+ });
+ });
+ return buttons;
+ },
+ };
+
+ /**
+ * Command to open a dialog.
+ *
+ * @param {Drupal.Ajax} ajax
+ * The Drupal Ajax object.
+ * @param {object} response
+ * Object holding the server response.
+ * @param {number} [status]
+ * The HTTP status code.
+ *
+ * @return {bool|undefined}
+ * Returns false if there was no selector property in the response object.
+ */
+ Drupal.AjaxCommands.prototype.openDialog = function (ajax, response, status) {
+ if (!response.selector) {
+ return false;
+ }
+ let $dialog = $(response.selector);
+ if (!$dialog.length) {
+ // Create the element if needed.
+ $dialog = $(`<div id="${response.selector.replace(/^#/, '')}" class="ui-front"/>`).appendTo('body');
+ }
+ // Set up the wrapper, if there isn't one.
+ if (!ajax.wrapper) {
+ ajax.wrapper = $dialog.attr('id');
+ }
+
+ // Use the ajax.js insert command to populate the dialog contents.
+ response.command = 'insert';
+ response.method = 'html';
+ ajax.commands.insert(ajax, response, status);
+
+ // Move the buttons to the jQuery UI dialog buttons area.
+ if (!response.dialogOptions.buttons) {
+ response.dialogOptions.drupalAutoButtons = true;
+ response.dialogOptions.buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
+ }
+
+ // Bind dialogButtonsChange.
+ $dialog.on('dialogButtonsChange', () => {
+ const buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
+ $dialog.dialog('option', 'buttons', buttons);
+ });
+
+ // Open the dialog itself.
+ response.dialogOptions = response.dialogOptions || {};
+ const dialog = Drupal.dialog($dialog.get(0), response.dialogOptions);
+ if (response.dialogOptions.modal) {
+ dialog.showModal();
+ }
+ else {
+ dialog.show();
+ }
+
+ // Add the standard Drupal class for buttons for style consistency.
+ $dialog.parent().find('.ui-dialog-buttonset').addClass('form-actions');
+ };
+
+ /**
+ * Command to close a dialog.
+ *
+ * If no selector is given, it defaults to trying to close the modal.
+ *
+ * @param {Drupal.Ajax} [ajax]
+ * The ajax object.
+ * @param {object} response
+ * Object holding the server response.
+ * @param {string} response.selector
+ * The selector of the dialog.
+ * @param {bool} response.persist
+ * Whether to persist the dialog element or not.
+ * @param {number} [status]
+ * The HTTP status code.
+ */
+ Drupal.AjaxCommands.prototype.closeDialog = function (ajax, response, status) {
+ const $dialog = $(response.selector);
+ if ($dialog.length) {
+ Drupal.dialog($dialog.get(0)).close();
+ if (!response.persist) {
+ $dialog.remove();
+ }
+ }
+
+ // Unbind dialogButtonsChange.
+ $dialog.off('dialogButtonsChange');
+ };
+
+ /**
+ * Command to set a dialog property.
+ *
+ * JQuery UI specific way of setting dialog options.
+ *
+ * @param {Drupal.Ajax} [ajax]
+ * The Drupal Ajax object.
+ * @param {object} response
+ * Object holding the server response.
+ * @param {string} response.selector
+ * Selector for the dialog element.
+ * @param {string} response.optionsName
+ * Name of a key to set.
+ * @param {string} response.optionValue
+ * Value to set.
+ * @param {number} [status]
+ * The HTTP status code.
+ */
+ Drupal.AjaxCommands.prototype.setDialogOption = function (ajax, response, status) {
+ const $dialog = $(response.selector);
+ if ($dialog.length) {
+ $dialog.dialog('option', response.optionName, response.optionValue);
+ }
+ };
+
+ /**
+ * Binds a listener on dialog creation to handle the cancel link.
+ *
+ * @param {jQuery.Event} e
+ * The event triggered.
+ * @param {Drupal.dialog~dialogDefinition} dialog
+ * The dialog instance.
+ * @param {jQuery} $element
+ * The jQuery collection of the dialog element.
+ * @param {object} [settings]
+ * Dialog settings.
+ */
+ $(window).on('dialog:aftercreate', (e, dialog, $element, settings) => {
+ $element.on('click.dialog', '.dialog-cancel', (e) => {
+ dialog.close('cancel');
+ e.preventDefault();
+ e.stopPropagation();
+ });
+ });
+
+ /**
+ * Removes all 'dialog' listeners.
+ *
+ * @param {jQuery.Event} e
+ * The event triggered.
+ * @param {Drupal.dialog~dialogDefinition} dialog
+ * The dialog instance.
+ * @param {jQuery} $element
+ * jQuery collection of the dialog element.
+ */
+ $(window).on('dialog:beforeclose', (e, dialog, $element) => {
+ $element.off('.dialog');
+ });
+}(jQuery, Drupal));