798673a1d86c4990be33da05d846b114d246ddc2
[yaffs-website] / web / core / misc / dialog / dialog.ajax.es6.js
1 /**
2  * @file
3  * Extends the Drupal AJAX functionality to integrate the dialog API.
4  */
5
6 (function ($, Drupal) {
7   /**
8    * Initialize dialogs for Ajax purposes.
9    *
10    * @type {Drupal~behavior}
11    *
12    * @prop {Drupal~behaviorAttach} attach
13    *   Attaches the behaviors for dialog ajax functionality.
14    */
15   Drupal.behaviors.dialog = {
16     attach(context, settings) {
17       const $context = $(context);
18
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');
27       }
28
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');
32       if ($dialog.length) {
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');
37         }
38
39         // Force focus on the modal when the behavior is run.
40         $dialog.dialog('widget').trigger('focus');
41       }
42
43       const originalClose = settings.dialog.close;
44       // Overwrite the close method to remove the dialog on closing.
45       settings.dialog.close = function (event) {
46         originalClose.apply(settings.dialog, arguments);
47         $(event.target).remove();
48       };
49     },
50
51     /**
52      * Scan a dialog for any primary buttons and move them to the button area.
53      *
54      * @param {jQuery} $dialog
55      *   An jQuery object containing the element that is the dialog target.
56      *
57      * @return {Array}
58      *   An array of buttons that need to be added to the button area.
59      */
60     prepareDialogButtons($dialog) {
61       const buttons = [];
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({
70           display: 'block',
71           width: 0,
72           height: 0,
73           padding: 0,
74           border: 0,
75           overflow: 'hidden',
76         });
77         buttons.push({
78           text: $originalButton.html() || $originalButton.attr('value'),
79           class: $originalButton.attr('class'),
80           click(e) {
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();
85             }
86             else {
87               $originalButton.trigger('mousedown').trigger('mouseup').trigger('click');
88               e.preventDefault();
89             }
90           },
91         });
92       });
93       return buttons;
94     },
95   };
96
97   /**
98    * Command to open a dialog.
99    *
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.
106    *
107    * @return {bool|undefined}
108    *   Returns false if there was no selector property in the response object.
109    */
110   Drupal.AjaxCommands.prototype.openDialog = function (ajax, response, status) {
111     if (!response.selector) {
112       return false;
113     }
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');
118     }
119     // Set up the wrapper, if there isn't one.
120     if (!ajax.wrapper) {
121       ajax.wrapper = $dialog.attr('id');
122     }
123
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);
128
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);
133     }
134
135     // Bind dialogButtonsChange.
136     $dialog.on('dialogButtonsChange', () => {
137       const buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
138       $dialog.dialog('option', 'buttons', buttons);
139     });
140
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) {
145       dialog.showModal();
146     }
147     else {
148       dialog.show();
149     }
150
151     // Add the standard Drupal class for buttons for style consistency.
152     $dialog.parent().find('.ui-dialog-buttonset').addClass('form-actions');
153   };
154
155   /**
156    * Command to close a dialog.
157    *
158    * If no selector is given, it defaults to trying to close the modal.
159    *
160    * @param {Drupal.Ajax} [ajax]
161    *   The ajax object.
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.
170    */
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) {
176         $dialog.remove();
177       }
178     }
179
180     // Unbind dialogButtonsChange.
181     $dialog.off('dialogButtonsChange');
182   };
183
184   /**
185    * Command to set a dialog property.
186    *
187    * JQuery UI specific way of setting dialog options.
188    *
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
198    *   Value to set.
199    * @param {number} [status]
200    *   The HTTP status code.
201    */
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);
206     }
207   };
208
209   /**
210    * Binds a listener on dialog creation to handle the cancel link.
211    *
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]
219    *   Dialog settings.
220    */
221   $(window).on('dialog:aftercreate', (e, dialog, $element, settings) => {
222     $element.on('click.dialog', '.dialog-cancel', (e) => {
223       dialog.close('cancel');
224       e.preventDefault();
225       e.stopPropagation();
226     });
227   });
228
229   /**
230    * Removes all 'dialog' listeners.
231    *
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.
238    */
239   $(window).on('dialog:beforeclose', (e, dialog, $element) => {
240     $element.off('.dialog');
241   });
242 }(jQuery, Drupal));