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