Chris@0: /** Chris@0: * @file Chris@0: * User permission page behaviors. Chris@0: */ Chris@0: Chris@17: (function($, Drupal) { Chris@0: /** Chris@0: * Shows checked and disabled checkboxes for inherited permissions. Chris@0: * Chris@0: * @type {Drupal~behavior} Chris@0: * Chris@0: * @prop {Drupal~behaviorAttach} attach Chris@0: * Attaches functionality to the permissions table. Chris@0: */ Chris@0: Drupal.behaviors.permissions = { Chris@0: attach(context) { Chris@0: const self = this; Chris@17: $('table#permissions') Chris@17: .once('permissions') Chris@17: .each(function() { Chris@17: // On a site with many roles and permissions, this behavior initially Chris@17: // has to perform thousands of DOM manipulations to inject checkboxes Chris@17: // and hide them. By detaching the table from the DOM, all operations Chris@17: // can be performed without triggering internal layout and re-rendering Chris@17: // processes in the browser. Chris@17: const $table = $(this); Chris@17: let $ancestor; Chris@17: let method; Chris@17: if ($table.prev().length) { Chris@17: $ancestor = $table.prev(); Chris@17: method = 'after'; Chris@17: } else { Chris@17: $ancestor = $table.parent(); Chris@17: method = 'append'; Chris@17: } Chris@17: $table.detach(); Chris@0: Chris@17: // Create dummy checkboxes. We use dummy checkboxes instead of reusing Chris@17: // the existing checkboxes here because new checkboxes don't alter the Chris@17: // submitted form. If we'd automatically check existing checkboxes, the Chris@17: // permission table would be polluted with redundant entries. This Chris@17: // is deliberate, but desirable when we automatically check them. Chris@17: const $dummy = $( Chris@17: '', Chris@17: ) Chris@17: .attr( Chris@17: 'title', Chris@17: Drupal.t( Chris@17: 'This permission is inherited from the authenticated user role.', Chris@17: ), Chris@17: ) Chris@17: .hide(); Chris@0: Chris@17: $table Chris@17: .find('input[type="checkbox"]') Chris@17: .not('.js-rid-anonymous, .js-rid-authenticated') Chris@17: .addClass('real-checkbox js-real-checkbox') Chris@17: .after($dummy); Chris@0: Chris@17: // Initialize the authenticated user checkbox. Chris@17: $table Chris@17: .find('input[type=checkbox].js-rid-authenticated') Chris@17: .on('click.permissions', self.toggle) Chris@17: // .triggerHandler() cannot be used here, as it only affects the first Chris@17: // element. Chris@17: .each(self.toggle); Chris@0: Chris@17: // Re-insert the table into the DOM. Chris@17: $ancestor[method]($table); Chris@17: }); Chris@0: }, Chris@0: Chris@0: /** Chris@0: * Toggles all dummy checkboxes based on the checkboxes' state. Chris@0: * Chris@0: * If the "authenticated user" checkbox is checked, the checked and disabled Chris@0: * checkboxes are shown, the real checkboxes otherwise. Chris@0: */ Chris@0: toggle() { Chris@0: const authCheckbox = this; Chris@0: const $row = $(this).closest('tr'); Chris@0: // jQuery performs too many layout calculations for .hide() and .show(), Chris@0: // leading to a major page rendering lag on sites with many roles and Chris@0: // permissions. Therefore, we toggle visibility directly. Chris@17: $row.find('.js-real-checkbox').each(function() { Chris@17: this.style.display = authCheckbox.checked ? 'none' : ''; Chris@0: }); Chris@17: $row.find('.js-dummy-checkbox').each(function() { Chris@17: this.style.display = authCheckbox.checked ? '' : 'none'; Chris@0: }); Chris@0: }, Chris@0: }; Chris@17: })(jQuery, Drupal);