3 var ProtectionForm = window.ProtectionForm = {
5 * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox)
6 * on the protection form
9 var $cell = $( '<td>' ), $row = $( '<tr>' ).append( $cell );
11 if ( !$( '#mwProtectSet' ).length ) {
15 if ( mw.config.get( 'wgCascadeableLevels' ) !== undefined ) {
16 $( 'form#mw-Protect-Form' ).submit( this.toggleUnchainedInputs.bind( ProtectionForm, true ) );
18 this.getExpirySelectors().each( function () {
19 $( this ).change( ProtectionForm.updateExpiryList.bind( ProtectionForm, this ) );
21 this.getExpiryInputs().each( function () {
22 $( this ).on( 'keyup change', ProtectionForm.updateExpiry.bind( ProtectionForm, this ) );
24 this.getLevelSelectors().each( function () {
25 $( this ).change( ProtectionForm.updateLevels.bind( ProtectionForm, this ) );
28 $( '#mwProtectSet > tbody > tr:first' ).after( $row );
30 // If there is only one protection type, there is nothing to chain
31 if ( $( '[id ^= mw-protect-table-]' ).length > 1 ) {
34 .attr( { id: 'mwProtectUnchained', type: 'checkbox' } )
35 .click( this.onChainClick.bind( this ) )
36 .prop( 'checked', !this.areAllTypesMatching() ),
37 document.createTextNode( ' ' ),
39 .attr( 'for', 'mwProtectUnchained' )
40 .text( mw.msg( 'protect-unchain-permissions' ) )
43 this.toggleUnchainedInputs( !this.areAllTypesMatching() );
46 $( '#mwProtect-reason' ).byteLimit( 180 );
48 this.updateCascadeCheckbox();
52 * Sets the disabled attribute on the cascade checkbox depending on the current selected levels
54 updateCascadeCheckbox: function () {
55 this.getLevelSelectors().each( function () {
56 if ( !ProtectionForm.isCascadeableLevel( $( this ).val() ) ) {
57 $( '#mwProtect-cascade' ).prop( { checked: false, disabled: true } );
60 $( '#mwProtect-cascade' ).prop( 'disabled', false );
66 * Checks if a cerain protection level is cascadeable.
68 * @param {string} level
71 isCascadeableLevel: function ( level ) {
72 return $.inArray( level, mw.config.get( 'wgCascadeableLevels' ) ) !== -1;
76 * When protection levels are locked together, update the rest
77 * when one action's level changes
79 * @param {Element} source Level selector that changed
81 updateLevels: function ( source ) {
82 if ( !this.isUnchained() ) {
83 this.setAllSelectors( source.selectedIndex );
85 this.updateCascadeCheckbox();
89 * When protection levels are locked together, update the
90 * expiries when one changes
92 * @param {Element} source expiry input that changed
95 updateExpiry: function ( source ) {
96 if ( !this.isUnchained() ) {
97 this.getExpiryInputs().each( function () {
98 this.value = source.value;
101 if ( this.isUnchained() ) {
102 $( '#' + source.id.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' ) ).val( 'othertime' );
104 this.getExpirySelectors().each( function () {
105 this.value = 'othertime';
111 * When protection levels are locked together, update the
112 * expiry lists when one changes and clear the custom inputs
114 * @param {Element} source Expiry selector that changed
116 updateExpiryList: function ( source ) {
117 if ( !this.isUnchained() ) {
118 this.getExpirySelectors().each( function () {
119 this.value = source.value;
121 this.getExpiryInputs().each( function () {
128 * Update chain status and enable/disable various bits of the UI
129 * when the user changes the "unlock move permissions" checkbox
131 onChainClick: function () {
132 this.toggleUnchainedInputs( this.isUnchained() );
133 if ( !this.isUnchained() ) {
134 this.setAllSelectors( this.getMaxLevel() );
136 this.updateCascadeCheckbox();
140 * Returns true if the named attribute in all objects in the given array are matching
142 * @param {Object[]} objects
143 * @param {string} attrName
146 matchAttribute: function ( objects, attrName ) {
147 return $.map( objects, function ( object ) {
148 return object[attrName];
149 } ).filter( function ( item, index, a ) {
150 return index === a.indexOf( item );
155 * Are all actions protected at the same level, with the same expiry time?
159 areAllTypesMatching: function () {
160 return this.matchAttribute( this.getLevelSelectors(), 'selectedIndex' )
161 && this.matchAttribute( this.getExpirySelectors(), 'selectedIndex' )
162 && this.matchAttribute( this.getExpiryInputs(), 'value' );
166 * Is protection chaining off?
170 isUnchained: function () {
171 var element = document.getElementById( 'mwProtectUnchained' );
174 : true; // No control, so we need to let the user set both levels
178 * Find the highest protection level in any selector
181 getMaxLevel: function () {
182 return Math.max.apply( Math, this.getLevelSelectors().map( function () {
183 return this.selectedIndex;
188 * Protect all actions at the specified level
190 * @param {number} index Protection level
192 setAllSelectors: function ( index ) {
193 this.getLevelSelectors().each( function () {
194 this.selectedIndex = index;
199 * Get a list of all protection selectors on the page
203 getLevelSelectors: function () {
204 return $( 'select[id ^= mwProtect-level-]' );
208 * Get a list of all expiry inputs on the page
212 getExpiryInputs: function () {
213 return $( 'input[id ^= mwProtect-][id $= -expires]' );
217 * Get a list of all expiry selector lists on the page
221 getExpirySelectors: function () {
222 return $( 'select[id ^= mwProtectExpirySelection-]' );
226 * Enable/disable protection selectors and expiry inputs
228 * @param {boolean} val Enable?
230 toggleUnchainedInputs: function ( val ) {
231 var setDisabled = function () { this.disabled = !val; };
232 this.getLevelSelectors().slice( 1 ).each( setDisabled );
233 this.getExpiryInputs().slice( 1 ).each( setDisabled );
234 this.getExpirySelectors().slice( 1 ).each( setDisabled );
238 $( ProtectionForm.init.bind( ProtectionForm ) );
240 }( mediaWiki, jQuery ) );