6 (function ($, window, Drupal) {
8 * Provide the summary information for the block settings vertical tabs.
10 * @type {Drupal~behavior}
12 * @prop {Drupal~behaviorAttach} attach
13 * Attaches the behavior for the block settings summaries.
15 Drupal.behaviors.blockSettingsSummary = {
17 // The drupalSetSummary method required for this behavior is not available
18 // on the Blocks administration page, so we need to make sure this
19 // behavior is processed only if drupalSetSummary is defined.
20 if (typeof $.fn.drupalSetSummary === 'undefined') {
25 * Create a summary for checkboxes in the provided context.
27 * @param {HTMLDocument|HTMLElement} context
28 * A context where one would find checkboxes to summarize.
31 * A string with the summary.
33 function checkboxesSummary(context) {
35 const $checkboxes = $(context).find('input[type="checkbox"]:checked + label');
36 const il = $checkboxes.length;
37 for (let i = 0; i < il; i++) {
38 vals.push($($checkboxes[i]).html());
41 vals.push(Drupal.t('Not restricted'));
43 return vals.join(', ');
46 $('[data-drupal-selector="edit-visibility-node-type"], [data-drupal-selector="edit-visibility-language"], [data-drupal-selector="edit-visibility-user-role"]').drupalSetSummary(checkboxesSummary);
48 $('[data-drupal-selector="edit-visibility-request-path"]').drupalSetSummary((context) => {
49 const $pages = $(context).find('textarea[name="visibility[request_path][pages]"]');
51 return Drupal.t('Not restricted');
54 return Drupal.t('Restricted to certain pages');
60 * Move a block in the blocks table between regions via select list.
62 * This behavior is dependent on the tableDrag behavior, since it uses the
63 * objects initialized in that behavior to update the row.
65 * @type {Drupal~behavior}
67 * @prop {Drupal~behaviorAttach} attach
68 * Attaches the tableDrag behaviour for blocks in block administration.
70 Drupal.behaviors.blockDrag = {
71 attach(context, settings) {
72 // tableDrag is required and we should be on the blocks admin page.
73 if (typeof Drupal.tableDrag === 'undefined' || typeof Drupal.tableDrag.blocks === 'undefined') {
78 * Function to check empty regions and toggle classes based on this.
80 * @param {jQuery} table
81 * The jQuery object representing the table to inspect.
82 * @param {jQuery} rowObject
83 * The jQuery object representing the table row.
85 function checkEmptyRegions(table, rowObject) {
86 table.find('tr.region-message').each(function () {
87 const $this = $(this);
88 // If the dragged row is in this region, but above the message row,
89 // swap it down one space.
90 if ($this.prev('tr').get(0) === rowObject.element) {
91 // Prevent a recursion problem when using the keyboard to move rows
93 if ((rowObject.method !== 'keyboard' || rowObject.direction === 'down')) {
94 rowObject.swap('after', this);
97 // This region has become empty.
98 if ($this.next('tr').is(':not(.draggable)') || $this.next('tr').length === 0) {
99 $this.removeClass('region-populated').addClass('region-empty');
101 // This region has become populated.
102 else if ($this.is('.region-empty')) {
103 $this.removeClass('region-empty').addClass('region-populated');
109 * Function to update the last placed row with the correct classes.
111 * @param {jQuery} table
112 * The jQuery object representing the table to inspect.
113 * @param {jQuery} rowObject
114 * The jQuery object representing the table row.
116 function updateLastPlaced(table, rowObject) {
117 // Remove the color-success class from new block if applicable.
118 table.find('.color-success').removeClass('color-success');
120 const $rowObject = $(rowObject);
121 if (!$rowObject.is('.drag-previous')) {
122 table.find('.drag-previous').removeClass('drag-previous');
123 $rowObject.addClass('drag-previous');
128 * Update block weights in the given region.
130 * @param {jQuery} table
131 * Table with draggable items.
132 * @param {string} region
133 * Machine name of region containing blocks to update.
135 function updateBlockWeights(table, region) {
136 // Calculate minimum weight.
137 let weight = -Math.round(table.find('.draggable').length / 2);
138 // Update the block weights.
139 table.find(`.region-${region}-message`).nextUntil('.region-title')
140 .find('select.block-weight').val(() =>
141 // Increment the weight before assigning it to prevent using the
142 // absolute minimum available weight. This way we always have an
143 // unused upper and lower bound, which makes manually setting the
144 // weights easier for users who prefer to do it that way.
148 const table = $('#blocks');
149 // Get the blocks tableDrag object.
150 const tableDrag = Drupal.tableDrag.blocks;
151 // Add a handler for when a row is swapped, update empty regions.
152 tableDrag.row.prototype.onSwap = function (swappedRow) {
153 checkEmptyRegions(table, this);
154 updateLastPlaced(table, this);
157 // Add a handler so when a row is dropped, update fields dropped into
159 tableDrag.onDrop = function () {
160 const dragObject = this;
161 const $rowElement = $(dragObject.rowObject.element);
162 // Use "region-message" row instead of "region" row because
163 // "region-{region_name}-message" is less prone to regexp match errors.
164 const regionRow = $rowElement.prevAll('tr.region-message').get(0);
165 const regionName = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2');
166 const regionField = $rowElement.find('select.block-region-select');
167 // Check whether the newly picked region is available for this block.
168 if (regionField.find(`option[value=${regionName}]`).length === 0) {
169 // If not, alert the user and keep the block in its old region
171 window.alert(Drupal.t('The block cannot be placed in this region.'));
172 // Simulate that there was a selected element change, so the row is
173 // put back to from where the user tried to drag it.
174 regionField.trigger('change');
177 // Update region and weight fields if the region has been changed.
178 if (!regionField.is(`.block-region-${regionName}`)) {
179 const weightField = $rowElement.find('select.block-weight');
180 const oldRegionName = weightField[0].className.replace(/([^ ]+[ ]+)*block-weight-([^ ]+)([ ]+[^ ]+)*/, '$2');
181 regionField.removeClass(`block-region-${oldRegionName}`).addClass(`block-region-${regionName}`);
182 weightField.removeClass(`block-weight-${oldRegionName}`).addClass(`block-weight-${regionName}`);
183 regionField.val(regionName);
186 updateBlockWeights(table, regionName);
189 // Add the behavior to each region select list.
190 $(context).find('select.block-region-select').once('block-region-select')
191 .on('change', function (event) {
192 // Make our new row and select field.
193 const row = $(this).closest('tr');
194 const select = $(this);
195 // Find the correct region and insert the row as the last in the
197 tableDrag.rowObject = new tableDrag.row(row[0]);
198 const region_message = table.find(`.region-${select[0].value}-message`);
199 const region_items = region_message.nextUntil('.region-message, .region-title');
200 if (region_items.length) {
201 region_items.last().after(row);
203 // We found that region_message is the last row.
205 region_message.after(row);
207 updateBlockWeights(table, select[0].value);
208 // Modify empty regions with added or removed fields.
209 checkEmptyRegions(table, tableDrag.rowObject);
210 // Update last placed block indication.
211 updateLastPlaced(table, row);
212 // Show unsaved changes warning.
213 if (!tableDrag.changed) {
214 $(Drupal.theme('tableDragChangedWarning')).insertBefore(tableDrag.table).hide().fadeIn('slow');
215 tableDrag.changed = true;
217 // Remove focus from selectbox.
218 select.trigger('blur');
222 }(jQuery, window, Drupal));