Implement extension registration from an extension.json file
[mediawiki.git] / resources / src / mediawiki.legacy / protect.js
blobf9069b6fd05ac8e244507db47d917f36101246b1
1 ( function ( mw, $ ) {
3 var ProtectionForm = window.ProtectionForm = {
4         /**
5          * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox)
6          * on the protection form
7          */
8         init: function () {
9                 var $cell = $( '<td>' ), $row = $( '<tr>' ).append( $cell );
11                 if ( !$( '#mwProtectSet' ).length ) {
12                         return false;
13                 }
15                 if ( mw.config.get( 'wgCascadeableLevels' ) !== undefined ) {
16                         $( 'form#mw-Protect-Form' ).submit( this.toggleUnchainedInputs.bind( ProtectionForm, true ) );
17                 }
18                 this.getExpirySelectors().each( function () {
19                         $( this ).change( ProtectionForm.updateExpiryList.bind( ProtectionForm, this ) );
20                 } );
21                 this.getExpiryInputs().each( function () {
22                         $( this ).on( 'keyup change', ProtectionForm.updateExpiry.bind( ProtectionForm, this ) );
23                 } );
24                 this.getLevelSelectors().each( function () {
25                         $( this ).change( ProtectionForm.updateLevels.bind( ProtectionForm, this ) );
26                 } );
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 ) {
32                         $cell.append(
33                                 $( '<input>' )
34                                         .attr( { id: 'mwProtectUnchained', type: 'checkbox' } )
35                                         .click( this.onChainClick.bind( this ) )
36                                         .prop( 'checked', !this.areAllTypesMatching() ),
37                                 document.createTextNode( ' ' ),
38                                 $( '<label>' )
39                                         .attr( 'for', 'mwProtectUnchained' )
40                                         .text( mw.msg( 'protect-unchain-permissions' ) )
41                         );
43                         this.toggleUnchainedInputs( !this.areAllTypesMatching() );
44                 }
46                 $( '#mwProtect-reason' ).byteLimit( 180 );
48                 this.updateCascadeCheckbox();
49         },
51         /**
52          * Sets the disabled attribute on the cascade checkbox depending on the current selected levels
53          */
54         updateCascadeCheckbox: function () {
55                 this.getLevelSelectors().each( function () {
56                         if ( !ProtectionForm.isCascadeableLevel( $( this ).val() ) ) {
57                                 $( '#mwProtect-cascade' ).prop( { checked: false, disabled: true } );
58                                 return false;
59                         } else {
60                                 $( '#mwProtect-cascade' ).prop( 'disabled', false );
61                         }
62                 } );
63         },
65         /**
66          * Checks if a cerain protection level is cascadeable.
67          *
68          * @param {string} level
69          * @return {boolean}
70          */
71         isCascadeableLevel: function ( level ) {
72                 return $.inArray( level, mw.config.get( 'wgCascadeableLevels' ) ) !== -1;
73         },
75         /**
76          * When protection levels are locked together, update the rest
77          * when one action's level changes
78          *
79          * @param {Element} source Level selector that changed
80          */
81         updateLevels: function ( source ) {
82                 if ( !this.isUnchained() ) {
83                         this.setAllSelectors( source.selectedIndex );
84                 }
85                 this.updateCascadeCheckbox();
86         },
88         /**
89          * When protection levels are locked together, update the
90          * expiries when one changes
91          *
92          * @param {Element} source expiry input that changed
93          */
95         updateExpiry: function ( source ) {
96                 if ( !this.isUnchained() ) {
97                         this.getExpiryInputs().each( function () {
98                                 this.value = source.value;
99                         } );
100                 }
101                 if ( this.isUnchained() ) {
102                         $( '#' + source.id.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' ) ).val( 'othertime' );
103                 } else {
104                         this.getExpirySelectors().each( function () {
105                                 this.value = 'othertime';
106                         } );
107                 }
108         },
110         /**
111          * When protection levels are locked together, update the
112          * expiry lists when one changes and clear the custom inputs
113          *
114          * @param {Element} source Expiry selector that changed
115          */
116         updateExpiryList: function ( source ) {
117                 if ( !this.isUnchained() ) {
118                         this.getExpirySelectors().each( function () {
119                                 this.value = source.value;
120                         } );
121                         this.getExpiryInputs().each( function () {
122                                 this.value = '';
123                         } );
124                 }
125         },
127         /**
128          * Update chain status and enable/disable various bits of the UI
129          * when the user changes the "unlock move permissions" checkbox
130          */
131         onChainClick: function () {
132                 this.toggleUnchainedInputs( this.isUnchained() );
133                 if ( !this.isUnchained() ) {
134                         this.setAllSelectors( this.getMaxLevel() );
135                 }
136                 this.updateCascadeCheckbox();
137         },
139         /**
140          * Returns true if the named attribute in all objects in the given array are matching
141          *
142          * @param {Object[]} objects
143          * @param {string} attrName
144          * @return {boolean}
145          */
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 );
151                 } ).length === 1;
152         },
154         /**
155          * Are all actions protected at the same level, with the same expiry time?
156          *
157          * @return {boolean}
158          */
159         areAllTypesMatching: function () {
160                 return this.matchAttribute( this.getLevelSelectors(), 'selectedIndex' )
161                         && this.matchAttribute( this.getExpirySelectors(), 'selectedIndex' )
162                         && this.matchAttribute( this.getExpiryInputs(), 'value' );
163         },
165         /**
166          * Is protection chaining off?
167          *
168          * @return {boolean}
169          */
170         isUnchained: function () {
171                 var element = document.getElementById( 'mwProtectUnchained' );
172                 return element
173                         ? element.checked
174                         : true; // No control, so we need to let the user set both levels
175         },
177         /**
178          * Find the highest protection level in any selector
179          * @return {number}
180          */
181         getMaxLevel: function () {
182                 return Math.max.apply( Math, this.getLevelSelectors().map( function () {
183                         return this.selectedIndex;
184                 } ) );
185         },
187         /**
188          * Protect all actions at the specified level
189          *
190          * @param {number} index Protection level
191          */
192         setAllSelectors: function ( index ) {
193                 this.getLevelSelectors().each( function () {
194                         this.selectedIndex = index;
195                 } );
196         },
198         /**
199          * Get a list of all protection selectors on the page
200          *
201          * @return {jQuery}
202          */
203         getLevelSelectors: function () {
204                 return $( 'select[id ^= mwProtect-level-]' );
205         },
207         /**
208          * Get a list of all expiry inputs on the page
209          *
210          * @return {jQuery}
211          */
212         getExpiryInputs: function () {
213                 return $( 'input[id ^= mwProtect-][id $= -expires]' );
214         },
216         /**
217          * Get a list of all expiry selector lists on the page
218          *
219          * @return {jQuery}
220          */
221         getExpirySelectors: function () {
222                 return $( 'select[id ^= mwProtectExpirySelection-]' );
223         },
225         /**
226          * Enable/disable protection selectors and expiry inputs
227          *
228          * @param {boolean} val Enable?
229          */
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 );
235         }
238 $( ProtectionForm.init.bind( ProtectionForm ) );
240 }( mediaWiki, jQuery ) );