/** * @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/. $('
') .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, ...args) { originalClose.apply(settings.dialog, [event, ...args]); $(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 = $( `
`, ).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);