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