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