block.js 9.0 KB

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