Import: Handle uploads with sha1 starting with 0 properly
[mediawiki.git] / resources / src / mediawiki.legacy / protect.js
blob6226c90b8dd9f55a3b0e2fdf67eb17904b1a31b6
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>' ),
10                         $row = $( '<tr>' ).append( $cell );
12                 if ( !$( '#mwProtectSet' ).length ) {
13                         return false;
14                 }
16                 if ( mw.config.get( 'wgCascadeableLevels' ) !== undefined ) {
17                         $( 'form#mw-Protect-Form' ).submit( this.toggleUnchainedInputs.bind( ProtectionForm, true ) );
18                 }
19                 this.getExpirySelectors().each( function () {
20                         $( this ).change( ProtectionForm.updateExpiryList.bind( ProtectionForm, this ) );
21                 } );
22                 this.getExpiryInputs().each( function () {
23                         $( this ).on( 'keyup change', ProtectionForm.updateExpiry.bind( ProtectionForm, this ) );
24                 } );
25                 this.getLevelSelectors().each( function () {
26                         $( this ).change( ProtectionForm.updateLevels.bind( ProtectionForm, this ) );
27                 } );
29                 $( '#mwProtectSet > tbody > tr:first' ).after( $row );
31                 // If there is only one protection type, there is nothing to chain
32                 if ( $( '[id ^= mw-protect-table-]' ).length > 1 ) {
33                         $cell.append(
34                                 $( '<input>' )
35                                         .attr( { id: 'mwProtectUnchained', type: 'checkbox' } )
36                                         .click( this.onChainClick.bind( this ) )
37                                         .prop( 'checked', !this.areAllTypesMatching() ),
38                                 document.createTextNode( ' ' ),
39                                 $( '<label>' )
40                                         .attr( 'for', 'mwProtectUnchained' )
41                                         .text( mw.msg( 'protect-unchain-permissions' ) )
42                         );
44                         this.toggleUnchainedInputs( !this.areAllTypesMatching() );
45                 }
47                 $( '#mwProtect-reason' ).byteLimit( 180 );
49                 this.updateCascadeCheckbox();
50         },
52         /**
53          * Sets the disabled attribute on the cascade checkbox depending on the current selected levels
54          */
55         updateCascadeCheckbox: function () {
56                 this.getLevelSelectors().each( function () {
57                         if ( !ProtectionForm.isCascadeableLevel( $( this ).val() ) ) {
58                                 $( '#mwProtect-cascade' ).prop( { checked: false, disabled: true } );
59                                 return false;
60                         } else {
61                                 $( '#mwProtect-cascade' ).prop( 'disabled', false );
62                         }
63                 } );
64         },
66         /**
67          * Checks if a certain protection level is cascadeable.
68          *
69          * @param {string} level
70          * @return {boolean}
71          */
72         isCascadeableLevel: function ( level ) {
73                 return $.inArray( level, mw.config.get( 'wgCascadeableLevels' ) ) !== -1;
74         },
76         /**
77          * When protection levels are locked together, update the rest
78          * when one action's level changes
79          *
80          * @param {Element} source Level selector that changed
81          */
82         updateLevels: function ( source ) {
83                 if ( !this.isUnchained() ) {
84                         this.setAllSelectors( source.selectedIndex );
85                 }
86                 this.updateCascadeCheckbox();
87         },
89         /**
90          * When protection levels are locked together, update the
91          * expiries when one changes
92          *
93          * @param {Element} source expiry input that changed
94          */
96         updateExpiry: function ( source ) {
97                 if ( !this.isUnchained() ) {
98                         this.getExpiryInputs().each( function () {
99                                 this.value = source.value;
100                         } );
101                 }
102                 if ( this.isUnchained() ) {
103                         $( '#' + source.id.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' ) ).val( 'othertime' );
104                 } else {
105                         this.getExpirySelectors().each( function () {
106                                 this.value = 'othertime';
107                         } );
108                 }
109         },
111         /**
112          * When protection levels are locked together, update the
113          * expiry lists when one changes and clear the custom inputs
114          *
115          * @param {Element} source Expiry selector that changed
116          */
117         updateExpiryList: function ( source ) {
118                 if ( !this.isUnchained() ) {
119                         this.getExpirySelectors().each( function () {
120                                 this.value = source.value;
121                         } );
122                         this.getExpiryInputs().each( function () {
123                                 this.value = '';
124                         } );
125                 }
126         },
128         /**
129          * Update chain status and enable/disable various bits of the UI
130          * when the user changes the "unlock move permissions" checkbox
131          */
132         onChainClick: function () {
133                 this.toggleUnchainedInputs( this.isUnchained() );
134                 if ( !this.isUnchained() ) {
135                         this.setAllSelectors( this.getMaxLevel() );
136                 }
137                 this.updateCascadeCheckbox();
138         },
140         /**
141          * Returns true if the named attribute in all objects in the given array are matching
142          *
143          * @param {Object[]} objects
144          * @param {string} attrName
145          * @return {boolean}
146          */
147         matchAttribute: function ( objects, attrName ) {
148                 return $.map( objects, function ( object ) {
149                         return object[ attrName ];
150                 } ).filter( function ( item, index, a ) {
151                         return index === a.indexOf( item );
152                 } ).length === 1;
153         },
155         /**
156          * Are all actions protected at the same level, with the same expiry time?
157          *
158          * @return {boolean}
159          */
160         areAllTypesMatching: function () {
161                 return this.matchAttribute( this.getLevelSelectors(), 'selectedIndex' )
162                         && this.matchAttribute( this.getExpirySelectors(), 'selectedIndex' )
163                         && this.matchAttribute( this.getExpiryInputs(), 'value' );
164         },
166         /**
167          * Is protection chaining off?
168          *
169          * @return {boolean}
170          */
171         isUnchained: function () {
172                 var element = document.getElementById( 'mwProtectUnchained' );
173                 return element
174                         ? element.checked
175                         : true; // No control, so we need to let the user set both levels
176         },
178         /**
179          * Find the highest protection level in any selector
180          *
181          * @return {number}
182          */
183         getMaxLevel: function () {
184                 return Math.max.apply( Math, this.getLevelSelectors().map( function () {
185                         return this.selectedIndex;
186                 } ) );
187         },
189         /**
190          * Protect all actions at the specified level
191          *
192          * @param {number} index Protection level
193          */
194         setAllSelectors: function ( index ) {
195                 this.getLevelSelectors().each( function () {
196                         this.selectedIndex = index;
197                 } );
198         },
200         /**
201          * Get a list of all protection selectors on the page
202          *
203          * @return {jQuery}
204          */
205         getLevelSelectors: function () {
206                 return $( 'select[id ^= mwProtect-level-]' );
207         },
209         /**
210          * Get a list of all expiry inputs on the page
211          *
212          * @return {jQuery}
213          */
214         getExpiryInputs: function () {
215                 return $( 'input[id ^= mwProtect-][id $= -expires]' );
216         },
218         /**
219          * Get a list of all expiry selector lists on the page
220          *
221          * @return {jQuery}
222          */
223         getExpirySelectors: function () {
224                 return $( 'select[id ^= mwProtectExpirySelection-]' );
225         },
227         /**
228          * Enable/disable protection selectors and expiry inputs
229          *
230          * @param {boolean} val Enable?
231          */
232         toggleUnchainedInputs: function ( val ) {
233                 var setDisabled = function () { this.disabled = !val; };
234                 this.getLevelSelectors().slice( 1 ).each( setDisabled );
235                 this.getExpiryInputs().slice( 1 ).each( setDisabled );
236                 this.getExpirySelectors().slice( 1 ).each( setDisabled );
237         }
240 $( ProtectionForm.init.bind( ProtectionForm ) );
242 }( mediaWiki, jQuery ) );