Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / modules / field_ui / field_ui.es6.js
1 /**
2  * @file
3  * Attaches the behaviors for the Field UI module.
4  */
5
6 (function($, Drupal, drupalSettings) {
7   /**
8    * @type {Drupal~behavior}
9    *
10    * @prop {Drupal~behaviorAttach} attach
11    *   Adds behaviors to the field storage add form.
12    */
13   Drupal.behaviors.fieldUIFieldStorageAddForm = {
14     attach(context) {
15       const $form = $(context)
16         .find('[data-drupal-selector="field-ui-field-storage-add-form"]')
17         .once('field_ui_add');
18       if ($form.length) {
19         // Add a few 'js-form-required' and 'form-required' css classes here.
20         // We can not use the Form API '#required' property because both label
21         // elements for "add new" and "re-use existing" can never be filled and
22         // submitted at the same time. The actual validation will happen
23         // server-side.
24         $form
25           .find(
26             '.js-form-item-label label,' +
27               '.js-form-item-field-name label,' +
28               '.js-form-item-existing-storage-label label',
29           )
30           .addClass('js-form-required form-required');
31
32         const $newFieldType = $form.find('select[name="new_storage_type"]');
33         const $existingStorageName = $form.find(
34           'select[name="existing_storage_name"]',
35         );
36         const $existingStorageLabel = $form.find(
37           'input[name="existing_storage_label"]',
38         );
39
40         // When the user selects a new field type, clear the "existing field"
41         // selection.
42         $newFieldType.on('change', function() {
43           if ($(this).val() !== '') {
44             // Reset the "existing storage name" selection.
45             $existingStorageName.val('').trigger('change');
46           }
47         });
48
49         // When the user selects an existing storage name, clear the "new field
50         // type" selection and populate the 'existing_storage_label' element.
51         $existingStorageName.on('change', function() {
52           const value = $(this).val();
53           if (value !== '') {
54             // Reset the "new field type" selection.
55             $newFieldType.val('').trigger('change');
56
57             // Pre-populate the "existing storage label" element.
58             if (
59               typeof drupalSettings.existingFieldLabels[value] !== 'undefined'
60             ) {
61               $existingStorageLabel.val(
62                 drupalSettings.existingFieldLabels[value],
63               );
64             }
65           }
66         });
67       }
68     },
69   };
70
71   /**
72    * Attaches the fieldUIOverview behavior.
73    *
74    * @type {Drupal~behavior}
75    *
76    * @prop {Drupal~behaviorAttach} attach
77    *   Attaches the fieldUIOverview behavior.
78    *
79    * @see Drupal.fieldUIOverview.attach
80    */
81   Drupal.behaviors.fieldUIDisplayOverview = {
82     attach(context, settings) {
83       $(context)
84         .find('table#field-display-overview')
85         .once('field-display-overview')
86         .each(function() {
87           Drupal.fieldUIOverview.attach(
88             this,
89             settings.fieldUIRowsData,
90             Drupal.fieldUIDisplayOverview,
91           );
92         });
93     },
94   };
95
96   /**
97    * Namespace for the field UI overview.
98    *
99    * @namespace
100    */
101   Drupal.fieldUIOverview = {
102     /**
103      * Attaches the fieldUIOverview behavior.
104      *
105      * @param {HTMLTableElement} table
106      *   The table element for the overview.
107      * @param {object} rowsData
108      *   The data of the rows in the table.
109      * @param {object} rowHandlers
110      *   Handlers to be added to the rows.
111      */
112     attach(table, rowsData, rowHandlers) {
113       const tableDrag = Drupal.tableDrag[table.id];
114
115       // Add custom tabledrag callbacks.
116       tableDrag.onDrop = this.onDrop;
117       tableDrag.row.prototype.onSwap = this.onSwap;
118
119       // Create row handlers.
120       $(table)
121         .find('tr.draggable')
122         .each(function() {
123           // Extract server-side data for the row.
124           const row = this;
125           if (row.id in rowsData) {
126             const data = rowsData[row.id];
127             data.tableDrag = tableDrag;
128
129             // Create the row handler, make it accessible from the DOM row
130             // element.
131             const rowHandler = new rowHandlers[data.rowHandler](row, data);
132             $(row).data('fieldUIRowHandler', rowHandler);
133           }
134         });
135     },
136
137     /**
138      * Event handler to be attached to form inputs triggering a region change.
139      */
140     onChange() {
141       const $trigger = $(this);
142       const $row = $trigger.closest('tr');
143       const rowHandler = $row.data('fieldUIRowHandler');
144
145       const refreshRows = {};
146       refreshRows[rowHandler.name] = $trigger.get(0);
147
148       // Handle region change.
149       const region = rowHandler.getRegion();
150       if (region !== rowHandler.region) {
151         // Remove parenting.
152         $row.find('select.js-field-parent').val('');
153         // Let the row handler deal with the region change.
154         $.extend(refreshRows, rowHandler.regionChange(region));
155         // Update the row region.
156         rowHandler.region = region;
157       }
158
159       // Ajax-update the rows.
160       Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows);
161     },
162
163     /**
164      * Lets row handlers react when a row is dropped into a new region.
165      */
166     onDrop() {
167       const dragObject = this;
168       const row = dragObject.rowObject.element;
169       const $row = $(row);
170       const rowHandler = $row.data('fieldUIRowHandler');
171       if (typeof rowHandler !== 'undefined') {
172         const regionRow = $row.prevAll('tr.region-message').get(0);
173         const region = regionRow.className.replace(
174           /([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/,
175           '$2',
176         );
177
178         if (region !== rowHandler.region) {
179           // Let the row handler deal with the region change.
180           const refreshRows = rowHandler.regionChange(region);
181           // Update the row region.
182           rowHandler.region = region;
183           // Ajax-update the rows.
184           Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows);
185         }
186       }
187     },
188
189     /**
190      * Refreshes placeholder rows in empty regions while a row is being dragged.
191      *
192      * Copied from block.js.
193      *
194      * @param {HTMLElement} draggedRow
195      *   The tableDrag rowObject for the row being dragged.
196      */
197     onSwap(draggedRow) {
198       const rowObject = this;
199       $(rowObject.table)
200         .find('tr.region-message')
201         .each(function() {
202           const $this = $(this);
203           // If the dragged row is in this region, but above the message row, swap
204           // it down one space.
205           if (
206             $this.prev('tr').get(0) ===
207             rowObject.group[rowObject.group.length - 1]
208           ) {
209             // Prevent a recursion problem when using the keyboard to move rows
210             // up.
211             if (
212               rowObject.method !== 'keyboard' ||
213               rowObject.direction === 'down'
214             ) {
215               rowObject.swap('after', this);
216             }
217           }
218           // This region has become empty.
219           if (
220             $this.next('tr').is(':not(.draggable)') ||
221             $this.next('tr').length === 0
222           ) {
223             $this.removeClass('region-populated').addClass('region-empty');
224           }
225           // This region has become populated.
226           else if ($this.is('.region-empty')) {
227             $this.removeClass('region-empty').addClass('region-populated');
228           }
229         });
230     },
231
232     /**
233      * Triggers Ajax refresh of selected rows.
234      *
235      * The 'format type' selects can trigger a series of changes in child rows.
236      * The #ajax behavior is therefore not attached directly to the selects, but
237      * triggered manually through a hidden #ajax 'Refresh' button.
238      *
239      * @param {object} rows
240      *   A hash object, whose keys are the names of the rows to refresh (they
241      *   will receive the 'ajax-new-content' effect on the server side), and
242      *   whose values are the DOM element in the row that should get an Ajax
243      *   throbber.
244      */
245     AJAXRefreshRows(rows) {
246       // Separate keys and values.
247       const rowNames = [];
248       const ajaxElements = [];
249       Object.keys(rows || {}).forEach(rowName => {
250         rowNames.push(rowName);
251         ajaxElements.push(rows[rowName]);
252       });
253
254       if (rowNames.length) {
255         // Add a throbber next each of the ajaxElements.
256         $(ajaxElements).after(Drupal.theme.ajaxProgressThrobber());
257
258         // Fire the Ajax update.
259         $('input[name=refresh_rows]').val(rowNames.join(' '));
260         $('input[data-drupal-selector="edit-refresh"]').trigger('mousedown');
261
262         // Disabled elements do not appear in POST ajax data, so we mark the
263         // elements disabled only after firing the request.
264         $(ajaxElements).prop('disabled', true);
265       }
266     },
267   };
268
269   /**
270    * Row handlers for the 'Manage display' screen.
271    *
272    * @namespace
273    */
274   Drupal.fieldUIDisplayOverview = {};
275
276   /**
277    * Constructor for a 'field' row handler.
278    *
279    * This handler is used for both fields and 'extra fields' rows.
280    *
281    * @constructor
282    *
283    * @param {HTMLTableRowElement} row
284    *   The row DOM element.
285    * @param {object} data
286    *   Additional data to be populated in the constructed object.
287    *
288    * @return {Drupal.fieldUIDisplayOverview.field}
289    *   The field row handler constructed.
290    */
291   Drupal.fieldUIDisplayOverview.field = function(row, data) {
292     this.row = row;
293     this.name = data.name;
294     this.region = data.region;
295     this.tableDrag = data.tableDrag;
296     this.defaultPlugin = data.defaultPlugin;
297
298     // Attach change listener to the 'plugin type' select.
299     this.$pluginSelect = $(row).find('.field-plugin-type');
300     this.$pluginSelect.on('change', Drupal.fieldUIOverview.onChange);
301
302     // Attach change listener to the 'region' select.
303     this.$regionSelect = $(row).find('select.field-region');
304     this.$regionSelect.on('change', Drupal.fieldUIOverview.onChange);
305
306     return this;
307   };
308
309   Drupal.fieldUIDisplayOverview.field.prototype = {
310     /**
311      * Returns the region corresponding to the current form values of the row.
312      *
313      * @return {string}
314      *   Either 'hidden' or 'content'.
315      */
316     getRegion() {
317       return this.$regionSelect.val();
318     },
319
320     /**
321      * Reacts to a row being changed regions.
322      *
323      * This function is called when the row is moved to a different region, as
324      * a
325      * result of either :
326      * - a drag-and-drop action (the row's form elements then probably need to
327      * be updated accordingly)
328      * - user input in one of the form elements watched by the
329      *   {@link Drupal.fieldUIOverview.onChange} change listener.
330      *
331      * @param {string} region
332      *   The name of the new region for the row.
333      *
334      * @return {object}
335      *   A hash object indicating which rows should be Ajax-updated as a result
336      *   of the change, in the format expected by
337      *   {@link Drupal.fieldUIOverview.AJAXRefreshRows}.
338      */
339     regionChange(region) {
340       // Replace dashes with underscores.
341       region = region.replace(/-/g, '_');
342
343       // Set the region of the select list.
344       this.$regionSelect.val(region);
345
346       // Restore the formatter back to the default formatter only if it was
347       // disabled previously. Pseudo-fields do not have default formatters,
348       // we just return to 'visible' for those.
349       if (this.region === 'hidden') {
350         const value =
351           typeof this.defaultPlugin !== 'undefined'
352             ? this.defaultPlugin
353             : this.$pluginSelect.find('option').val();
354
355         if (typeof value !== 'undefined') {
356           this.$pluginSelect.val(value);
357         }
358       }
359
360       const refreshRows = {};
361       refreshRows[this.name] = this.$pluginSelect.get(0);
362
363       return refreshRows;
364     },
365   };
366 })(jQuery, Drupal, drupalSettings);