3 * Extends the Drupal AJAX functionality to integrate the dialog API.
6 (function ($, Drupal) {
8 * Initialize dialogs for Ajax purposes.
10 * @type {Drupal~behavior}
12 * @prop {Drupal~behaviorAttach} attach
13 * Attaches the behaviors for dialog ajax functionality.
15 Drupal.behaviors.dialog = {
16 attach(context, settings) {
17 const $context = $(context);
19 // Provide a known 'drupal-modal' DOM element for Drupal-based modal
20 // dialogs. Non-modal dialogs are responsible for creating their own
21 // elements, since there can be multiple non-modal dialogs at a time.
22 if (!$('#drupal-modal').length) {
23 // Add 'ui-front' jQuery UI class so jQuery UI widgets like autocomplete
24 // sit on top of dialogs. For more information see
25 // http://api.jqueryui.com/theming/stacking-elements/.
26 $('<div id="drupal-modal" class="ui-front"/>').hide().appendTo('body');
29 // Special behaviors specific when attaching content within a dialog.
30 // These behaviors usually fire after a validation error inside a dialog.
31 const $dialog = $context.closest('.ui-dialog-content');
33 // Remove and replace the dialog buttons with those from the new form.
34 if ($dialog.dialog('option', 'drupalAutoButtons')) {
35 // Trigger an event to detect/sync changes to buttons.
36 $dialog.trigger('dialogButtonsChange');
39 // Force focus on the modal when the behavior is run.
40 $dialog.dialog('widget').trigger('focus');
43 const originalClose = settings.dialog.close;
44 // Overwrite the close method to remove the dialog on closing.
45 settings.dialog.close = function (event, ...args) {
46 originalClose.apply(settings.dialog, [event, ...args]);
47 $(event.target).remove();
52 * Scan a dialog for any primary buttons and move them to the button area.
54 * @param {jQuery} $dialog
55 * An jQuery object containing the element that is the dialog target.
58 * An array of buttons that need to be added to the button area.
60 prepareDialogButtons($dialog) {
62 const $buttons = $dialog.find('.form-actions input[type=submit], .form-actions a.button');
63 $buttons.each(function () {
64 // Hidden form buttons need special attention. For browser consistency,
65 // the button needs to be "visible" in order to have the enter key fire
66 // the form submit event. So instead of a simple "hide" or
67 // "display: none", we set its dimensions to zero.
68 // See http://mattsnider.com/how-forms-submit-when-pressing-enter/
69 const $originalButton = $(this).css({
78 text: $originalButton.html() || $originalButton.attr('value'),
79 class: $originalButton.attr('class'),
81 // If the original button is an anchor tag, triggering the "click"
82 // event will not simulate a click. Use the click method instead.
83 if ($originalButton.is('a')) {
84 $originalButton[0].click();
87 $originalButton.trigger('mousedown').trigger('mouseup').trigger('click');
98 * Command to open a dialog.
100 * @param {Drupal.Ajax} ajax
101 * The Drupal Ajax object.
102 * @param {object} response
103 * Object holding the server response.
104 * @param {number} [status]
105 * The HTTP status code.
107 * @return {bool|undefined}
108 * Returns false if there was no selector property in the response object.
110 Drupal.AjaxCommands.prototype.openDialog = function (ajax, response, status) {
111 if (!response.selector) {
114 let $dialog = $(response.selector);
115 if (!$dialog.length) {
116 // Create the element if needed.
117 $dialog = $(`<div id="${response.selector.replace(/^#/, '')}" class="ui-front"/>`).appendTo('body');
119 // Set up the wrapper, if there isn't one.
121 ajax.wrapper = $dialog.attr('id');
124 // Use the ajax.js insert command to populate the dialog contents.
125 response.command = 'insert';
126 response.method = 'html';
127 ajax.commands.insert(ajax, response, status);
129 // Move the buttons to the jQuery UI dialog buttons area.
130 if (!response.dialogOptions.buttons) {
131 response.dialogOptions.drupalAutoButtons = true;
132 response.dialogOptions.buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
135 // Bind dialogButtonsChange.
136 $dialog.on('dialogButtonsChange', () => {
137 const buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
138 $dialog.dialog('option', 'buttons', buttons);
141 // Open the dialog itself.
142 response.dialogOptions = response.dialogOptions || {};
143 const dialog = Drupal.dialog($dialog.get(0), response.dialogOptions);
144 if (response.dialogOptions.modal) {
151 // Add the standard Drupal class for buttons for style consistency.
152 $dialog.parent().find('.ui-dialog-buttonset').addClass('form-actions');
156 * Command to close a dialog.
158 * If no selector is given, it defaults to trying to close the modal.
160 * @param {Drupal.Ajax} [ajax]
162 * @param {object} response
163 * Object holding the server response.
164 * @param {string} response.selector
165 * The selector of the dialog.
166 * @param {bool} response.persist
167 * Whether to persist the dialog element or not.
168 * @param {number} [status]
169 * The HTTP status code.
171 Drupal.AjaxCommands.prototype.closeDialog = function (ajax, response, status) {
172 const $dialog = $(response.selector);
173 if ($dialog.length) {
174 Drupal.dialog($dialog.get(0)).close();
175 if (!response.persist) {
180 // Unbind dialogButtonsChange.
181 $dialog.off('dialogButtonsChange');
185 * Command to set a dialog property.
187 * JQuery UI specific way of setting dialog options.
189 * @param {Drupal.Ajax} [ajax]
190 * The Drupal Ajax object.
191 * @param {object} response
192 * Object holding the server response.
193 * @param {string} response.selector
194 * Selector for the dialog element.
195 * @param {string} response.optionsName
196 * Name of a key to set.
197 * @param {string} response.optionValue
199 * @param {number} [status]
200 * The HTTP status code.
202 Drupal.AjaxCommands.prototype.setDialogOption = function (ajax, response, status) {
203 const $dialog = $(response.selector);
204 if ($dialog.length) {
205 $dialog.dialog('option', response.optionName, response.optionValue);
210 * Binds a listener on dialog creation to handle the cancel link.
212 * @param {jQuery.Event} e
213 * The event triggered.
214 * @param {Drupal.dialog~dialogDefinition} dialog
215 * The dialog instance.
216 * @param {jQuery} $element
217 * The jQuery collection of the dialog element.
218 * @param {object} [settings]
221 $(window).on('dialog:aftercreate', (e, dialog, $element, settings) => {
222 $element.on('click.dialog', '.dialog-cancel', (e) => {
223 dialog.close('cancel');
230 * Removes all 'dialog' listeners.
232 * @param {jQuery.Event} e
233 * The event triggered.
234 * @param {Drupal.dialog~dialogDefinition} dialog
235 * The dialog instance.
236 * @param {jQuery} $element
237 * jQuery collection of the dialog element.
239 $(window).on('dialog:beforeclose', (e, dialog, $element) => {
240 $element.off('.dialog');