Newer
Older
Alex Pott
committed
/**
* @file
* Block behaviors.
*/
(function ($, window, Drupal) {
/**
* Provide the summary information for the block settings vertical tabs.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the behavior for the block settings summaries.
*/
Drupal.behaviors.blockSettingsSummary = {
attach() {
Alex Pott
committed
// The drupalSetSummary method required for this behavior is not available
// on the Blocks administration page, so we need to make sure this
// behavior is processed only if drupalSetSummary is defined.
if (typeof $.fn.drupalSetSummary === 'undefined') {
return;
}
/**
* Create a summary for checkboxes in the provided context.
*
* @param {HTMLDocument|HTMLElement} context
* A context where one would find checkboxes to summarize.
*
* @return {string}
* A string with the summary.
*/
function checkboxesSummary(context) {
const vals = [];
const $checkboxes = $(context).find('input[type="checkbox"]:checked + label');
const il = $checkboxes.length;
for (let i = 0; i < il; i++) {
Alex Pott
committed
vals.push($($checkboxes[i]).html());
}
if (!vals.length) {
vals.push(Drupal.t('Not restricted'));
}
return vals.join(', ');
}
$('[data-drupal-selector="edit-visibility-node-type"], [data-drupal-selector="edit-visibility-language"], [data-drupal-selector="edit-visibility-user-role"]').drupalSetSummary(checkboxesSummary);
$('[data-drupal-selector="edit-visibility-request-path"]').drupalSetSummary((context) => {
const $pages = $(context).find('textarea[name="visibility[request_path][pages]"]');
Alex Pott
committed
if (!$pages.val()) {
return Drupal.t('Not restricted');
}
return Drupal.t('Restricted to certain pages');
Alex Pott
committed
});
},
Alex Pott
committed
};
/**
* Move a block in the blocks table between regions via select list.
*
* This behavior is dependent on the tableDrag behavior, since it uses the
* objects initialized in that behavior to update the row.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the tableDrag behaviour for blocks in block administration.
*/
Drupal.behaviors.blockDrag = {
attach(context, settings) {
Alex Pott
committed
// tableDrag is required and we should be on the blocks admin page.
if (typeof Drupal.tableDrag === 'undefined' || typeof Drupal.tableDrag.blocks === 'undefined') {
return;
}
/**
* Function to check empty regions and toggle classes based on this.
*
* @param {jQuery} table
* The jQuery object representing the table to inspect.
* @param {jQuery} rowObject
* The jQuery object representing the table row.
*/
function checkEmptyRegions(table, rowObject) {
table.find('tr.region-message').each(function () {
const $this = $(this);
Alex Pott
committed
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
// If the dragged row is in this region, but above the message row,
// swap it down one space.
if ($this.prev('tr').get(0) === rowObject.element) {
// Prevent a recursion problem when using the keyboard to move rows
// up.
if ((rowObject.method !== 'keyboard' || rowObject.direction === 'down')) {
rowObject.swap('after', this);
}
}
// This region has become empty.
if ($this.next('tr').is(':not(.draggable)') || $this.next('tr').length === 0) {
$this.removeClass('region-populated').addClass('region-empty');
}
// This region has become populated.
else if ($this.is('.region-empty')) {
$this.removeClass('region-empty').addClass('region-populated');
}
});
}
/**
* Function to update the last placed row with the correct classes.
*
* @param {jQuery} table
* The jQuery object representing the table to inspect.
* @param {jQuery} rowObject
* The jQuery object representing the table row.
*/
function updateLastPlaced(table, rowObject) {
// Remove the color-success class from new block if applicable.
table.find('.color-success').removeClass('color-success');
const $rowObject = $(rowObject);
Alex Pott
committed
if (!$rowObject.is('.drag-previous')) {
table.find('.drag-previous').removeClass('drag-previous');
$rowObject.addClass('drag-previous');
}
}
/**
* Update block weights in the given region.
*
* @param {jQuery} table
* Table with draggable items.
* @param {string} region
* Machine name of region containing blocks to update.
*/
function updateBlockWeights(table, region) {
// Calculate minimum weight.
let weight = -Math.round(table.find('.draggable').length / 2);
Alex Pott
committed
// Update the block weights.
table.find(`.region-${region}-message`).nextUntil('.region-title')
.find('select.block-weight').val(() =>
Alex Pott
committed
// Increment the weight before assigning it to prevent using the
// absolute minimum available weight. This way we always have an
// unused upper and lower bound, which makes manually setting the
// weights easier for users who prefer to do it that way.
++weight);
Alex Pott
committed
}
const table = $('#blocks');
Alex Pott
committed
// Get the blocks tableDrag object.
const tableDrag = Drupal.tableDrag.blocks;
Alex Pott
committed
// Add a handler for when a row is swapped, update empty regions.
tableDrag.row.prototype.onSwap = function (swappedRow) {
checkEmptyRegions(table, this);
updateLastPlaced(table, this);
};
// Add a handler so when a row is dropped, update fields dropped into
// new regions.
tableDrag.onDrop = function () {
const dragObject = this;
const $rowElement = $(dragObject.rowObject.element);
Alex Pott
committed
// Use "region-message" row instead of "region" row because
// "region-{region_name}-message" is less prone to regexp match errors.
const regionRow = $rowElement.prevAll('tr.region-message').get(0);
const regionName = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2');
const regionField = $rowElement.find('select.block-region-select');
Alex Pott
committed
// Check whether the newly picked region is available for this block.
if (regionField.find(`option[value=${regionName}]`).length === 0) {
Alex Pott
committed
// If not, alert the user and keep the block in its old region
// setting.
window.alert(Drupal.t('The block cannot be placed in this region.'));
// Simulate that there was a selected element change, so the row is
// put back to from where the user tried to drag it.
regionField.trigger('change');
}
// Update region and weight fields if the region has been changed.
if (!regionField.is(`.block-region-${regionName}`)) {
const weightField = $rowElement.find('select.block-weight');
const oldRegionName = weightField[0].className.replace(/([^ ]+[ ]+)*block-weight-([^ ]+)([ ]+[^ ]+)*/, '$2');
regionField.removeClass(`block-region-${oldRegionName}`).addClass(`block-region-${regionName}`);
weightField.removeClass(`block-weight-${oldRegionName}`).addClass(`block-weight-${regionName}`);
Alex Pott
committed
regionField.val(regionName);
}
updateBlockWeights(table, regionName);
};
// Add the behavior to each region select list.
$(context).find('select.block-region-select').once('block-region-select')
.on('change', function (event) {
// Make our new row and select field.
const row = $(this).closest('tr');
const select = $(this);
Alex Pott
committed
// Find the correct region and insert the row as the last in the
// region.
tableDrag.rowObject = new tableDrag.row(row[0]);
Jess
committed
const region_message = table.find(`.region-${select[0].value}-message`);
const region_items = region_message.nextUntil('.region-message, .region-title');
if (region_items.length) {
region_items.last().after(row);
Alex Pott
committed
}
Jess
committed
// We found that region_message is the last row.
Alex Pott
committed
else {
Jess
committed
region_message.after(row);
Alex Pott
committed
}
updateBlockWeights(table, select[0].value);
// Modify empty regions with added or removed fields.
checkEmptyRegions(table, tableDrag.rowObject);
// Update last placed block indication.
updateLastPlaced(table, row);
// Show unsaved changes warning.
if (!tableDrag.changed) {
$(Drupal.theme('tableDragChangedWarning')).insertBefore(tableDrag.table).hide().fadeIn('slow');
tableDrag.changed = true;
}
// Remove focus from selectbox.
select.trigger('blur');
});
},
Alex Pott
committed
};
}(jQuery, window, Drupal));