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