3 var ProtectionForm = window.ProtectionForm = {
5 * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox)
6 * on the protection form
11 var $cell = $( '<td>' ),
12 $row = $( '<tr>' ).append( $cell );
14 if ( !$( '#mwProtectSet' ).length ) {
18 if ( mw.config.get( 'wgCascadeableLevels' ) !== undefined ) {
19 $( 'form#mw-Protect-Form' ).submit( this.toggleUnchainedInputs.bind( ProtectionForm, true ) );
21 this.getExpirySelectors().each( function () {
22 $( this ).change( ProtectionForm.updateExpiryList.bind( ProtectionForm, this ) );
24 this.getExpiryInputs().each( function () {
25 $( this ).on( 'keyup change', ProtectionForm.updateExpiry.bind( ProtectionForm, this ) );
27 this.getLevelSelectors().each( function () {
28 $( this ).change( ProtectionForm.updateLevels.bind( ProtectionForm, this ) );
31 $( '#mwProtectSet > tbody > tr:first' ).after( $row );
33 // If there is only one protection type, there is nothing to chain
34 if ( $( '[id ^= mw-protect-table-]' ).length > 1 ) {
37 .attr( { id: 'mwProtectUnchained', type: 'checkbox' } )
38 .click( this.onChainClick.bind( this ) )
39 .prop( 'checked', !this.areAllTypesMatching() ),
40 document.createTextNode( ' ' ),
42 .attr( 'for', 'mwProtectUnchained' )
43 .text( mw.msg( 'protect-unchain-permissions' ) )
46 this.toggleUnchainedInputs( !this.areAllTypesMatching() );
49 $( '#mwProtect-reason' ).byteLimit( 180 );
51 this.updateCascadeCheckbox();
56 * Sets the disabled attribute on the cascade checkbox depending on the current selected levels
58 updateCascadeCheckbox: function () {
59 this.getLevelSelectors().each( function () {
60 if ( !ProtectionForm.isCascadeableLevel( $( this ).val() ) ) {
61 $( '#mwProtect-cascade' ).prop( { checked: false, disabled: true } );
64 $( '#mwProtect-cascade' ).prop( 'disabled', false );
70 * Checks if a certain protection level is cascadeable.
72 * @param {string} level
75 isCascadeableLevel: function ( level ) {
76 return $.inArray( level, mw.config.get( 'wgCascadeableLevels' ) ) !== -1;
80 * When protection levels are locked together, update the rest
81 * when one action's level changes
83 * @param {Element} source Level selector that changed
85 updateLevels: function ( source ) {
86 if ( !this.isUnchained() ) {
87 this.setAllSelectors( source.selectedIndex );
89 this.updateCascadeCheckbox();
93 * When protection levels are locked together, update the
94 * expiries when one changes
96 * @param {Element} source expiry input that changed
99 updateExpiry: function ( source ) {
100 if ( !this.isUnchained() ) {
101 this.getExpiryInputs().each( function () {
102 this.value = source.value;
105 if ( this.isUnchained() ) {
106 $( '#' + source.id.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' ) ).val( 'othertime' );
108 this.getExpirySelectors().each( function () {
109 this.value = 'othertime';
115 * When protection levels are locked together, update the
116 * expiry lists when one changes and clear the custom inputs
118 * @param {Element} source Expiry selector that changed
120 updateExpiryList: function ( source ) {
121 if ( !this.isUnchained() ) {
122 this.getExpirySelectors().each( function () {
123 this.value = source.value;
125 this.getExpiryInputs().each( function () {
132 * Update chain status and enable/disable various bits of the UI
133 * when the user changes the "unlock move permissions" checkbox
135 onChainClick: function () {
136 this.toggleUnchainedInputs( this.isUnchained() );
137 if ( !this.isUnchained() ) {
138 this.setAllSelectors( this.getMaxLevel() );
140 this.updateCascadeCheckbox();
144 * Returns true if the named attribute in all objects in the given array are matching
146 * @param {Object[]} objects
147 * @param {string} attrName
150 matchAttribute: function ( objects, attrName ) {
151 return $.map( objects, function ( object ) {
152 return object[ attrName ];
153 } ).filter( function ( item, index, a ) {
154 return index === a.indexOf( item );
159 * Are all actions protected at the same level, with the same expiry time?
163 areAllTypesMatching: function () {
164 return this.matchAttribute( this.getLevelSelectors(), 'selectedIndex' ) &&
165 this.matchAttribute( this.getExpirySelectors(), 'selectedIndex' ) &&
166 this.matchAttribute( this.getExpiryInputs(), 'value' );
170 * Is protection chaining off?
174 isUnchained: function () {
175 var element = document.getElementById( 'mwProtectUnchained' );
178 true; // No control, so we need to let the user set both levels
182 * Find the highest protection level in any selector
186 getMaxLevel: function () {
187 return Math.max.apply( Math, this.getLevelSelectors().map( function () {
188 return this.selectedIndex;
193 * Protect all actions at the specified level
195 * @param {number} index Protection level
197 setAllSelectors: function ( index ) {
198 this.getLevelSelectors().each( function () {
199 this.selectedIndex = index;
204 * Get a list of all protection selectors on the page
208 getLevelSelectors: function () {
209 return $( 'select[id ^= mwProtect-level-]' );
213 * Get a list of all expiry inputs on the page
217 getExpiryInputs: function () {
218 return $( 'input[id ^= mwProtect-][id $= -expires]' );
222 * Get a list of all expiry selector lists on the page
226 getExpirySelectors: function () {
227 return $( 'select[id ^= mwProtectExpirySelection-]' );
231 * Enable/disable protection selectors and expiry inputs
233 * @param {boolean} val Enable?
235 toggleUnchainedInputs: function ( val ) {
236 var setDisabled = function () { this.disabled = !val; };
237 this.getLevelSelectors().slice( 1 ).each( setDisabled );
238 this.getExpiryInputs().slice( 1 ).each( setDisabled );
239 this.getExpirySelectors().slice( 1 ).each( setDisabled );
243 $( ProtectionForm.init.bind( ProtectionForm ) );
245 }( mediaWiki, jQuery ) );