diff -u b/core/includes/ajax.inc b/core/includes/ajax.inc --- b/core/includes/ajax.inc +++ b/core/includes/ajax.inc @@ -473,6 +473,11 @@ // manipulation method is used. The method used is specified by // #ajax['method']. The default method is 'replaceWith', which completely // replaces the old wrapper element and its content with the new HTML. + // Since this is the primary response content returned to the client, we + // also attach the page title. It is up to client code to determine if and + // how to display that. For example, if the requesting element is configured + // to display the response in a dialog (via #ajax['dialog']), it can use + // this for the dialog title. $html = is_string($page_callback_result) ? $page_callback_result : drupal_render($page_callback_result); $commands[] = ajax_command_insert(NULL, $html) + array('title' => drupal_get_title()); // Add the status messages inside the new content's wrapper element, so that @@ -589,13 +594,22 @@ if (isset($element['#ajax']['event'])) { $element['#attached']['library'][] = array('system', 'jquery.form'); $element['#attached']['library'][] = array('system', 'drupal.ajax'); - if (!empty($element['#ajax']['modal'])) { + if (!empty($element['#ajax']['dialog'])) { $element['#attached']['library'][] = array('system', 'drupal.dialog'); } $settings = $element['#ajax']; - // Assign default settings. + // Assign default settings. When 'path' is set to NULL, ajax.js submits the + // Ajax request to the same URL as the form or link destination is for + // someone with JavaScript disabled. This is generally preferred as a way to + // ensure consistent server processing for js and no-js users, and Drupal's + // content negotiation takes care of formatting the response appropriately. + // However, 'path' and 'options' may be set when wanting server processing + // to be substantially different for a JavaScript triggered submission. + // One such substantial difference is form elements that use + // #ajax['callback'] for determining which part of the form needs + // re-rendering. For that, we have a special 'system/ajax' route. $settings += array( 'path' => isset($settings['callback']) ? 'system/ajax' : NULL, 'options' => array(), diff -u b/core/misc/ajax.js b/core/misc/ajax.js --- b/core/misc/ajax.js +++ b/core/misc/ajax.js @@ -78,37 +78,6 @@ }; /** - * Makes sure that a hidden dom element with id 'drupal-modal' exists. - */ -Drupal.behaviors.AJAXModalDialog = { - attach: function () { - if (!$('#drupal-modal').length) { - $('
').hide().appendTo('body'); - } - } -}; - -/** - * Binds a listener on modal dialog creation to handle the cancel link. - */ -$(window).on('dialog:aftercreate', function (e, dialog, $element, settings) { - if (settings.modal) { - $element.on('click.ajaxmodal', '.modal-cancel', function (e) { - dialog.close('cancel'); - e.preventDefault(); - e.stopPropagation(); - }); - } -}); - -/** - * Removes all 'ajaxmodal' listeners. - */ -$(window).on('dialog:beforeclose', function (e, dialog, $element) { - $element.off('.ajaxmodal'); -}); - -/** * Ajax object. * * All Ajax objects on a page are accessible through the global Drupal.ajax @@ -157,11 +126,13 @@ this.wrapper = '#' + this.wrapper; } - // For Ajax responses that are wanted in a modal, use the needed wrapper and - // method. - if (this.modal) { - this.wrapper = '#drupal-modal'; + // For Ajax responses that are wanted in a dialog, use the needed method. + // If wanted in a modal dialog, also use the needed wrapper. + if (this.dialog) { this.method = 'html'; + if (this.dialog.modal) { + this.wrapper = '#drupal-modal'; + } } this.element = element; @@ -189,7 +160,7 @@ // figure out a work around to this problem, we prevent AJAX-enabling // elements that submit to the same URL as the form when there's a file // input. For example, this means the Delete button on the edit form of - // an Article node doesn't open its confirmation form in a modal. + // an Article node doesn't open its confirmation form in a dialog. if (this.form.find(':file').length) { return; } @@ -587,8 +558,15 @@ // Add the new content to the page. wrapper[method](new_content); - if (ajax.modal && (response.title || !$(ajax.wrapper).length)) { - Drupal.dialog(wrapper, {title: response.title}).showModal(); + // If the requesting object wanted the response in a dialog, open that + // dialog. However, a single server response can include multiple insert + // commands (e.g., one for the primary content and another one for status + // messages), but we only want to open the dialog once, so we assume that + // only commands with a title property are dialog eligible. + // @todo Consider whether this is overloading title inappropriately, and + // if so, find another way to determine dialog eligibility. + if (ajax.dialog && ('title' in response)) { + Drupal.dialog(wrapper, {title: response.title}).show(ajax.dialog); } // Immediately hide the new content if we're using any effects. diff -u b/core/misc/dialog.js b/core/misc/dialog.js --- b/core/misc/dialog.js +++ b/core/misc/dialog.js @@ -7,50 +7,76 @@ (function ($, Drupal, drupalSettings) { - "use strict"; +"use strict"; - drupalSettings.modal = { - autoOpen: true, - dialogClass: '', - close: function (e) { - Drupal.detachBehaviors(e.target, null, 'unload'); +drupalSettings.dialog = { + autoOpen: true, + dialogClass: '', + close: function (e) { + Drupal.detachBehaviors(e.target, null, 'unload'); + } +}; + +Drupal.behaviors.dialog = { + attach: function () { + // Provide a known 'drupal-modal' dom element for Drupal code to use for + // modal dialogs. Since there can be multiple non-modal dialogs at a time, + // it is the responsibility of calling code to create the elements it needs. + if (!$('#drupal-modal').length) { + $('').hide().appendTo('body'); } - }; + } +}; - Drupal.dialog = function (element, options) { +Drupal.dialog = function (element, options) { - function openDialog (settings) { - settings = $.extend(settings, defaults); - // Trigger a global event to allow scripts to bind events to the dialog. - $(window).trigger('dialog:beforecreate', [dialog, $element, settings]); - $element.dialog(settings); - dialog.open = true; - $(window).trigger('dialog:aftercreate', [dialog, $element, settings]); - } + function openDialog (settings) { + settings = $.extend(settings, defaults); + // Trigger a global event to allow scripts to bind events to the dialog. + $(window).trigger('dialog:beforecreate', [dialog, $element, settings]); + $element.dialog(settings); + dialog.open = true; + $(window).trigger('dialog:aftercreate', [dialog, $element, settings]); + } + + function closeDialog (value) { + $(window).trigger('dialog:beforeclose', [dialog, $element]); + $element.dialog('close'); + dialog.returnValue = value; + dialog.open = false; + $(window).trigger('dialog:afterclose', [dialog, $element]); + } + + var undef; + var $element = $(element); + var defaults = $.extend(options, drupalSettings.dialog); + var dialog = { + open: false, + returnValue: undef, + show: function (settings) { + openDialog(settings); + }, + close: closeDialog + }; - function closeDialog (value) { - $(window).trigger('dialog:beforeclose', [dialog, $element]); - $element.dialog('close'); - dialog.returnValue = value; - dialog.open = false; - $(window).trigger('dialog:afterclose', [dialog, $element]); - } + return dialog; +}; - var undef; - var $element = $(element); - var defaults = $.extend(options, drupalSettings.modal); - var dialog = { - open: false, - returnValue: undef, - show: function () { - openDialog({ modal: false }); - }, - showModal: function () { - openDialog({ modal: true }); - }, - close: closeDialog - }; +/** + * Binds a listener on dialog creation to handle the cancel link. + */ +$(window).on('dialog:aftercreate', function (e, dialog, $element, settings) { + $element.on('click.dialog', '.dialog-cancel', function (e) { + dialog.close('cancel'); + e.preventDefault(); + e.stopPropagation(); + }); +}); - return dialog; - }; +/** + * Removes all 'dialog' listeners. + */ +$(window).on('dialog:beforeclose', function (e, dialog, $element) { + $element.off('.dialog'); +}); })(jQuery, Drupal, drupalSettings); diff -u b/core/modules/node/lib/Drupal/node/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php --- b/core/modules/node/lib/Drupal/node/NodeFormController.php +++ b/core/modules/node/lib/Drupal/node/NodeFormController.php @@ -269,7 +269,7 @@ // @todo Move this to EntityFormController::actions() so it applies to all // entity types by default? - $element['delete']['#ajax']['modal'] = TRUE; + $element['delete']['#ajax']['dialog'] = array('modal' => TRUE); return $element; } diff -u b/core/modules/node/node.admin.inc b/core/modules/node/node.admin.inc --- b/core/modules/node/node.admin.inc +++ b/core/modules/node/node.admin.inc @@ -557,7 +557,7 @@ $operations['delete'] = array( 'title' => t('delete'), 'href' => 'node/' . $node->nid . '/delete', - 'ajax' => array('modal' => TRUE), + 'ajax' => array('dialog' => array('modal' =>TRUE)), 'query' => $destination, ); } diff -u b/core/modules/system/system.module b/core/modules/system/system.module --- b/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -1171,7 +1171,7 @@ ), ); - // Drupal's Ajax framework, modal component. + // Drupal's dialog component. $libraries['drupal.dialog'] = array( 'title' => 'Drupal Dialog', 'version' => VERSION, @@ -3366,7 +3366,7 @@ '#title' => $no ? $no : t('Cancel'), '#href' => $options['path'], '#attributes' => array( - 'class' => array('modal-cancel'), + 'class' => array('dialog-cancel'), ), '#options' => $options, );