3 reasonCodePointLimit
= mw
.config
.get( 'wgCommentCodePointLimit' ),
4 reasonByteLimit
= mw
.config
.get( 'wgCommentByteLimit' );
6 ProtectionForm
= window
.ProtectionForm
= {
8 * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox)
9 * on the protection form
14 var $cell
= $( '<td>' ),
15 $row
= $( '<tr>' ).append( $cell
);
17 if ( !$( '#mwProtectSet' ).length
) {
21 if ( mw
.config
.get( 'wgCascadeableLevels' ) !== undefined ) {
22 $( 'form#mw-Protect-Form' ).on( 'submit', this.toggleUnchainedInputs
.bind( ProtectionForm
, true ) );
24 this.getExpirySelectors().each( function () {
25 $( this ).on( 'change', ProtectionForm
.updateExpiryList
.bind( ProtectionForm
, this ) );
27 this.getExpiryInputs().each( function () {
28 $( this ).on( 'keyup change', ProtectionForm
.updateExpiry
.bind( ProtectionForm
, this ) );
30 this.getLevelSelectors().each( function () {
31 $( this ).on( 'change', ProtectionForm
.updateLevels
.bind( ProtectionForm
, this ) );
34 $( '#mwProtectSet > tbody > tr:first' ).after( $row
);
36 // If there is only one protection type, there is nothing to chain
37 if ( $( '[id ^= mw-protect-table-]' ).length
> 1 ) {
40 .attr( { id
: 'mwProtectUnchained', type
: 'checkbox' } )
41 .on( 'click', this.onChainClick
.bind( this ) )
42 .prop( 'checked', !this.areAllTypesMatching() ),
43 document
.createTextNode( ' ' ),
45 .attr( 'for', 'mwProtectUnchained' )
46 .text( mw
.msg( 'protect-unchain-permissions' ) )
49 this.toggleUnchainedInputs( !this.areAllTypesMatching() );
52 // Arbitrary 75 to leave some space for the autogenerated null edit's summary
53 if ( reasonCodePointLimit
) {
54 $( '#mwProtect-reason' ).codePointLimit( reasonCodePointLimit
- 75 );
55 } else if ( reasonByteLimit
) {
56 $( '#mwProtect-reason' ).byteLimit( reasonByteLimit
- 75 );
59 this.updateCascadeCheckbox();
64 * Sets the disabled attribute on the cascade checkbox depending on the current selected levels
66 updateCascadeCheckbox: function () {
67 this.getLevelSelectors().each( function () {
68 if ( !ProtectionForm
.isCascadeableLevel( $( this ).val() ) ) {
69 $( '#mwProtect-cascade' ).prop( { checked
: false, disabled
: true } );
72 $( '#mwProtect-cascade' ).prop( 'disabled', false );
78 * Checks if a certain protection level is cascadeable.
80 * @param {string} level
83 isCascadeableLevel: function ( level
) {
84 var cascadeableLevels
= mw
.config
.get( 'wgCascadeableLevels' );
86 if ( !Array
.isArray( cascadeableLevels
) ) {
90 return cascadeableLevels
.indexOf( level
) !== -1;
94 * When protection levels are locked together, update the rest
95 * when one action's level changes
97 * @param {Element} source Level selector that changed
99 updateLevels: function ( source
) {
100 if ( !this.isUnchained() ) {
101 this.setAllSelectors( source
.selectedIndex
);
103 this.updateCascadeCheckbox();
107 * When protection levels are locked together, update the
108 * expiries when one changes
110 * @param {Element} source expiry input that changed
113 updateExpiry: function ( source
) {
114 if ( !this.isUnchained() ) {
115 this.getExpiryInputs().each( function () {
116 this.value
= source
.value
;
119 if ( this.isUnchained() ) {
120 $( '#' + source
.id
.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' ) ).val( 'othertime' );
122 this.getExpirySelectors().each( function () {
123 this.value
= 'othertime';
129 * When protection levels are locked together, update the
130 * expiry lists when one changes and clear the custom inputs
132 * @param {Element} source Expiry selector that changed
134 updateExpiryList: function ( source
) {
135 if ( !this.isUnchained() ) {
136 this.getExpirySelectors().each( function () {
137 this.value
= source
.value
;
139 this.getExpiryInputs().each( function () {
146 * Update chain status and enable/disable various bits of the UI
147 * when the user changes the "unlock move permissions" checkbox
149 onChainClick: function () {
150 this.toggleUnchainedInputs( this.isUnchained() );
151 if ( !this.isUnchained() ) {
152 this.setAllSelectors( this.getMaxLevel() );
154 this.updateCascadeCheckbox();
158 * Returns true if the named attribute in all objects in the given array are matching
160 * @param {Object[]} objects
161 * @param {string} attrName
164 matchAttribute: function ( objects
, attrName
) {
165 // eslint-disable-next-line jquery/no-map-util
166 return $.map( objects
, function ( object
) {
167 return object
[ attrName
];
168 } ).filter( function ( item
, index
, a
) {
169 return index
=== a
.indexOf( item
);
174 * Are all actions protected at the same level, with the same expiry time?
178 areAllTypesMatching: function () {
179 return this.matchAttribute( this.getLevelSelectors(), 'selectedIndex' ) &&
180 this.matchAttribute( this.getExpirySelectors(), 'selectedIndex' ) &&
181 this.matchAttribute( this.getExpiryInputs(), 'value' );
185 * Is protection chaining off?
189 isUnchained: function () {
190 var element
= document
.getElementById( 'mwProtectUnchained' );
193 true; // No control, so we need to let the user set both levels
197 * Find the highest protection level in any selector
201 getMaxLevel: function () {
202 return Math
.max
.apply( Math
, this.getLevelSelectors().map( function () {
203 return this.selectedIndex
;
208 * Protect all actions at the specified level
210 * @param {number} index Protection level
212 setAllSelectors: function ( index
) {
213 this.getLevelSelectors().each( function () {
214 this.selectedIndex
= index
;
219 * Get a list of all protection selectors on the page
223 getLevelSelectors: function () {
224 return $( 'select[id ^= mwProtect-level-]' );
228 * Get a list of all expiry inputs on the page
232 getExpiryInputs: function () {
233 return $( 'input[id ^= mwProtect-][id $= -expires]' );
237 * Get a list of all expiry selector lists on the page
241 getExpirySelectors: function () {
242 return $( 'select[id ^= mwProtectExpirySelection-]' );
246 * Enable/disable protection selectors and expiry inputs
248 * @param {boolean} val Enable?
250 toggleUnchainedInputs: function ( val
) {
251 var setDisabled = function () {
252 this.disabled
= !val
;
254 this.getLevelSelectors().slice( 1 ).each( setDisabled
);
255 this.getExpiryInputs().slice( 1 ).each( setDisabled
);
256 this.getExpirySelectors().slice( 1 ).each( setDisabled
);
260 $( ProtectionForm
.init
.bind( ProtectionForm
) );