+ if ( isUnchained() ) {
+ $( '#' + event.target.id.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' ) ).val( 'othertime' );
+ } else {
+ getExpirySelectors().each( function () {
+ this.value = 'othertime';
+ } );
+ }
+ }
+
+ /**
+ * When protection levels are locked together, update the
+ * expiry lists when one changes and clear the custom inputs
+ *
+ * @param {Event} event Expiry selector that changed
+ */
+ function updateExpiryList( event ) {
+ if ( !isUnchained() ) {
+ getExpirySelectors().each( function () {
+ this.value = event.target.value;
+ } );
+ getExpiryInputs().each( function () {
+ this.value = '';
+ } );
+ }
+ }
+
+ /**
+ * Update chain status and enable/disable various bits of the UI
+ * when the user changes the "unlock move permissions" checkbox
+ */
+ function onChainClick() {
+ toggleUnchainedInputs( isUnchained() );
+ if ( !isUnchained() ) {
+ setAllSelectors( getMaxLevel() );
+ }
+ updateCascadeCheckbox();
+ }
+
+ /**
+ * Returns true if the named attribute in all objects in the given array are matching
+ *
+ * @param {Object[]} objects
+ * @param {string} attrName
+ * @return {boolean}
+ */
+ function matchAttribute( objects, attrName ) {
+ // eslint-disable-next-line no-jquery/no-map-util
+ return $.map( objects, function ( object ) {
+ return object[ attrName ];
+ } ).filter( function ( item, index, a ) {
+ return index === a.indexOf( item );
+ } ).length === 1;
+ }
+
+ /**
+ * Are all actions protected at the same level, with the same expiry time?
+ *
+ * @return {boolean}
+ */
+ function areAllTypesMatching() {
+ return matchAttribute( getLevelSelectors(), 'selectedIndex' ) &&
+ matchAttribute( getExpirySelectors(), 'selectedIndex' ) &&
+ matchAttribute( getExpiryInputs(), 'value' );
+ }
+
+ /**
+ * Is protection chaining off?
+ *
+ * @return {boolean}
+ */
+ function isUnchained() {
+ var element = document.getElementById( 'mwProtectUnchained' );
+ return element ?
+ element.checked :
+ true; // No control, so we need to let the user set both levels
+ }
+
+ /**
+ * Find the highest protection level in any selector
+ *
+ * @return {number}
+ */
+ function getMaxLevel() {
+ return Math.max.apply( Math, getLevelSelectors().map( function () {
+ return this.selectedIndex;
+ } ) );
+ }
+
+ /**
+ * Protect all actions at the specified level
+ *
+ * @param {number} index Protection level
+ */
+ function setAllSelectors( index ) {
+ getLevelSelectors().each( function () {
+ this.selectedIndex = index;
+ } );
+ }
+
+ /**
+ * Get a list of all protection selectors on the page
+ *
+ * @return {jQuery}
+ */
+ function getLevelSelectors() {
+ return $( 'select[id ^= mwProtect-level-]' );
+ }
+
+ /**
+ * Get a list of all expiry inputs on the page
+ *
+ * @return {jQuery}
+ */
+ function getExpiryInputs() {
+ return $( 'input[id ^= mwProtect-][id $= -expires]' );
+ }
+
+ /**
+ * Get a list of all expiry selector lists on the page
+ *
+ * @return {jQuery}
+ */
+ function getExpirySelectors() {
+ return $( 'select[id ^= mwProtectExpirySelection-]' );
+ }
+
+ /**
+ * Enable/disable protection selectors and expiry inputs
+ *
+ * @param {boolean} val Enable?
+ */
+ function toggleUnchainedInputs( val ) {
+ var setDisabled = function () {
+ this.disabled = !val;
+ };
+ getLevelSelectors().slice( 1 ).each( setDisabled );
+ getExpiryInputs().slice( 1 ).each( setDisabled );
+ getExpirySelectors().slice( 1 ).each( setDisabled );
+ }
+
+ $( init );