2a017d29f8a705774d515321d6b7a818c1df4d50
[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"/>')
27           .hide()
28           .appendTo('body');
29       }
30
31       // Special behaviors specific when attaching content within a dialog.
32       // These behaviors usually fire after a validation error inside a dialog.
33       const $dialog = $context.closest('.ui-dialog-content');
34       if ($dialog.length) {
35         // Remove and replace the dialog buttons with those from the new form.
36         if ($dialog.dialog('option', 'drupalAutoButtons')) {
37           // Trigger an event to detect/sync changes to buttons.
38           $dialog.trigger('dialogButtonsChange');
39         }
40
41         // Force focus on the modal when the behavior is run.
42         $dialog.dialog('widget').trigger('focus');
43       }
44
45       const originalClose = settings.dialog.close;
46       // Overwrite the close method to remove the dialog on closing.
47       settings.dialog.close = function(event, ...args) {
48         originalClose.apply(settings.dialog, [event, ...args]);
49         $(event.target).remove();
50       };
51     },
52
53     /**
54      * Scan a dialog for any primary buttons and move them to the button area.
55      *
56      * @param {jQuery} $dialog
57      *   An jQuery object containing the element that is the dialog target.
58      *
59      * @return {Array}
60      *   An array of buttons that need to be added to the button area.
61      */
62     prepareDialogButtons($dialog) {
63       const buttons = [];
64       const $buttons = $dialog.find(
65         '.form-actions input[type=submit], .form-actions a.button',
66       );
67       $buttons.each(function() {
68         // Hidden form buttons need special attention. For browser consistency,
69         // the button needs to be "visible" in order to have the enter key fire
70         // the form submit event. So instead of a simple "hide" or
71         // "display: none", we set its dimensions to zero.
72         // See http://mattsnider.com/how-forms-submit-when-pressing-enter/
73         const $originalButton = $(this).css({
74           display: 'block',
75           width: 0,
76           height: 0,
77           padding: 0,
78           border: 0,
79           overflow: 'hidden',
80         });
81         buttons.push({
82           text: $originalButton.html() || $originalButton.attr('value'),
83           class: $originalButton.attr('class'),
84           click(e) {
85             // If the original button is an anchor tag, triggering the "click"
86             // event will not simulate a click. Use the click method instead.
87             if ($originalButton.is('a')) {
88               $originalButton[0].click();
89             } else {
90               $originalButton
91                 .trigger('mousedown')
92                 .trigger('mouseup')
93                 .trigger('click');
94               e.preventDefault();
95             }
96           },
97         });
98       });
99       return buttons;
100     },
101   };
102
103   /**
104    * Command to open a dialog.
105    *
106    * @param {Drupal.Ajax} ajax
107    *   The Drupal Ajax object.
108    * @param {object} response
109    *   Object holding the server response.
110    * @param {number} [status]
111    *   The HTTP status code.
112    *
113    * @return {bool|undefined}
114    *   Returns false if there was no selector property in the response object.
115    */
116   Drupal.AjaxCommands.prototype.openDialog = function(ajax, response, status) {
117     if (!response.selector) {
118       return false;
119     }
120     let $dialog = $(response.selector);
121     if (!$dialog.length) {
122       // Create the element if needed.
123       $dialog = $(
124         `<div id="${response.selector.replace(/^#/, '')}" class="ui-front"/>`,
125       ).appendTo('body');
126     }
127     // Set up the wrapper, if there isn't one.
128     if (!ajax.wrapper) {
129       ajax.wrapper = $dialog.attr('id');
130     }
131
132     // Use the ajax.js insert command to populate the dialog contents.
133     response.command = 'insert';
134     response.method = 'html';
135     ajax.commands.insert(ajax, response, status);
136
137     // Move the buttons to the jQuery UI dialog buttons area.
138     if (!response.dialogOptions.buttons) {
139       response.dialogOptions.drupalAutoButtons = true;
140       response.dialogOptions.buttons = Drupal.behaviors.dialog.prepareDialogButtons(
141         $dialog,
142       );
143     }
144
145     // Bind dialogButtonsChange.
146     $dialog.on('dialogButtonsChange', () => {
147       const buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
148       $dialog.dialog('option', 'buttons', buttons);
149     });
150
151     // Open the dialog itself.
152     response.dialogOptions = response.dialogOptions || {};
153     const dialog = Drupal.dialog($dialog.get(0), response.dialogOptions);
154     if (response.dialogOptions.modal) {
155       dialog.showModal();
156     } else {
157       dialog.show();
158     }
159
160     // Add the standard Drupal class for buttons for style consistency.
161     $dialog
162       .parent()
163       .find('.ui-dialog-buttonset')
164       .addClass('form-actions');
165   };
166
167   /**
168    * Command to close a dialog.
169    *
170    * If no selector is given, it defaults to trying to close the modal.
171    *
172    * @param {Drupal.Ajax} [ajax]
173    *   The ajax object.
174    * @param {object} response
175    *   Object holding the server response.
176    * @param {string} response.selector
177    *   The selector of the dialog.
178    * @param {bool} response.persist
179    *   Whether to persist the dialog element or not.
180    * @param {number} [status]
181    *   The HTTP status code.
182    */
183   Drupal.AjaxCommands.prototype.closeDialog = function(ajax, response, status) {
184     const $dialog = $(response.selector);
185     if ($dialog.length) {
186       Drupal.dialog($dialog.get(0)).close();
187       if (!response.persist) {
188         $dialog.remove();
189       }
190     }
191
192     // Unbind dialogButtonsChange.
193     $dialog.off('dialogButtonsChange');
194   };
195
196   /**
197    * Command to set a dialog property.
198    *
199    * JQuery UI specific way of setting dialog options.
200    *
201    * @param {Drupal.Ajax} [ajax]
202    *   The Drupal Ajax object.
203    * @param {object} response
204    *   Object holding the server response.
205    * @param {string} response.selector
206    *   Selector for the dialog element.
207    * @param {string} response.optionsName
208    *   Name of a key to set.
209    * @param {string} response.optionValue
210    *   Value to set.
211    * @param {number} [status]
212    *   The HTTP status code.
213    */
214   Drupal.AjaxCommands.prototype.setDialogOption = function(
215     ajax,
216     response,
217     status,
218   ) {
219     const $dialog = $(response.selector);
220     if ($dialog.length) {
221       $dialog.dialog('option', response.optionName, response.optionValue);
222     }
223   };
224
225   /**
226    * Binds a listener on dialog creation to handle the cancel link.
227    *
228    * @param {jQuery.Event} e
229    *   The event triggered.
230    * @param {Drupal.dialog~dialogDefinition} dialog
231    *   The dialog instance.
232    * @param {jQuery} $element
233    *   The jQuery collection of the dialog element.
234    * @param {object} [settings]
235    *   Dialog settings.
236    */
237   $(window).on('dialog:aftercreate', (e, dialog, $element, settings) => {
238     $element.on('click.dialog', '.dialog-cancel', e => {
239       dialog.close('cancel');
240       e.preventDefault();
241       e.stopPropagation();
242     });
243   });
244
245   /**
246    * Removes all 'dialog' listeners.
247    *
248    * @param {jQuery.Event} e
249    *   The event triggered.
250    * @param {Drupal.dialog~dialogDefinition} dialog
251    *   The dialog instance.
252    * @param {jQuery} $element
253    *   jQuery collection of the dialog element.
254    */
255   $(window).on('dialog:beforeclose', (e, dialog, $element) => {
256     $element.off('.dialog');
257   });
258 })(jQuery, Drupal);