Merge "API: Add show=unread to ApiQueryWatchlist"
[mediawiki.git] / skins / common / protect.js
blobdc142ca9672506530d318335570b33ee009877c5
1 ( function ( mw, $ ) {
3 var ProtectionForm = window.ProtectionForm = {
4 existingMatch: false,
6 /**
7 * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox)
8 * on the protection form
10 * @param opts Object : parameters with members:
11 * tableId Identifier of the table containing UI bits
12 * labelText Text to use for the checkbox label
13 * numTypes The number of protection types
14 * existingMatch True if all the existing expiry times match
16 init: function ( opts ) {
17 var box, boxbody, row, cell, check, label;
19 if ( !( document.createTextNode && document.getElementById && document.getElementsByTagName ) ) {
20 return false;
23 box = document.getElementById( opts.tableId );
24 if ( !box ) {
25 return false;
28 boxbody = box.getElementsByTagName( 'tbody' )[0];
29 row = document.createElement( 'tr' );
30 boxbody.insertBefore( row, boxbody.firstChild.nextSibling );
32 this.existingMatch = opts.existingMatch;
34 cell = document.createElement( 'td' );
35 row.appendChild( cell );
36 // If there is only one protection type, there is nothing to chain
37 if ( opts.numTypes > 1 ) {
38 check = document.createElement( 'input' );
39 check.id = 'mwProtectUnchained';
40 check.type = 'checkbox';
41 $( check ).click( function () {
42 ProtectionForm.onChainClick();
43 } );
45 label = document.createElement( 'label' );
46 label.htmlFor = 'mwProtectUnchained';
47 label.appendChild( document.createTextNode( opts.labelText ) );
49 cell.appendChild( check );
50 cell.appendChild( document.createTextNode( ' ' ) );
51 cell.appendChild( label );
53 check.checked = !this.areAllTypesMatching();
54 this.enableUnchainedInputs( check.checked );
57 $( '#mwProtect-reason' ).byteLimit( 180 );
59 this.updateCascadeCheckbox();
61 return true;
64 /**
65 * Sets the disabled attribute on the cascade checkbox depending on the current selected levels
67 updateCascadeCheckbox: function () {
68 var i, lists, items, selected;
70 // For non-existent titles, there is no cascade option
71 if ( !document.getElementById( 'mwProtect-cascade' ) ) {
72 return;
74 lists = this.getLevelSelectors();
75 for ( i = 0; i < lists.length; i++ ) {
76 if ( lists[i].selectedIndex > -1 ) {
77 items = lists[i].getElementsByTagName( 'option' );
78 selected = items[ lists[i].selectedIndex ].value;
79 if ( !this.isCascadeableLevel( selected ) ) {
80 document.getElementById( 'mwProtect-cascade' ).checked = false;
81 document.getElementById( 'mwProtect-cascade' ).disabled = true;
82 return;
86 document.getElementById( 'mwProtect-cascade' ).disabled = false;
89 /**
90 * Checks if a cerain protection level is cascadeable.
91 * @param level {String}
92 * @return {Boolean}
94 isCascadeableLevel: function ( level ) {
95 var cascadeLevels, len, i;
97 cascadeLevels = mw.config.get( 'wgCascadeableLevels' );
98 // cascadeLevels isn't defined on all pages
99 if ( cascadeLevels ) {
100 for ( i = 0, len = cascadeLevels.length; i < len; i += 1 ) {
101 if ( cascadeLevels[i] === level ) {
102 return true;
106 return false;
110 * When protection levels are locked together, update the rest
111 * when one action's level changes
113 * @param source Element Level selector that changed
115 updateLevels: function ( source ) {
116 if ( !this.isUnchained() ) {
117 this.setAllSelectors( source.selectedIndex );
119 this.updateCascadeCheckbox();
123 * When protection levels are locked together, update the
124 * expiries when one changes
126 * @param source Element expiry input that changed
129 updateExpiry: function ( source ) {
130 var expiry, listId, list;
132 if ( !this.isUnchained() ) {
133 expiry = source.value;
134 this.forEachExpiryInput( function ( element ) {
135 element.value = expiry;
136 } );
138 listId = source.id.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' );
139 list = document.getElementById( listId );
140 if ( list && list.value !== 'othertime' ) {
141 if ( this.isUnchained() ) {
142 list.value = 'othertime';
143 } else {
144 this.forEachExpirySelector( function ( element ) {
145 element.value = 'othertime';
146 } );
152 * When protection levels are locked together, update the
153 * expiry lists when one changes and clear the custom inputs
155 * @param source Element expiry selector that changed
157 updateExpiryList: function ( source ) {
158 var expiry;
159 if ( !this.isUnchained() ) {
160 expiry = source.value;
161 this.forEachExpirySelector( function ( element ) {
162 element.value = expiry;
163 } );
164 this.forEachExpiryInput( function ( element ) {
165 element.value = '';
166 } );
171 * Update chain status and enable/disable various bits of the UI
172 * when the user changes the "unlock move permissions" checkbox
174 onChainClick: function () {
175 if ( this.isUnchained() ) {
176 this.enableUnchainedInputs( true );
177 } else {
178 this.setAllSelectors( this.getMaxLevel() );
179 this.enableUnchainedInputs( false );
181 this.updateCascadeCheckbox();
185 * Returns true if the named attribute in all objects in the given array are matching
187 matchAttribute: function ( objects, attrName ) {
188 var i, element, value;
190 // Check levels
191 value = null;
192 for ( i = 0; i < objects.length; i++ ) {
193 element = objects[i];
194 if ( value === null ) {
195 value = element[attrName];
196 } else {
197 if ( value !== element[attrName] ) {
198 return false;
202 return true;
206 * Are all actions protected at the same level, with the same expiry time?
208 * @return boolean
210 areAllTypesMatching: function () {
211 return this.existingMatch
212 && this.matchAttribute( this.getLevelSelectors(), 'selectedIndex' )
213 && this.matchAttribute( this.getExpirySelectors(), 'selectedIndex' )
214 && this.matchAttribute( this.getExpiryInputs(), 'value' );
218 * Is protection chaining off?
220 * @return bool
222 isUnchained: function () {
223 var element = document.getElementById( 'mwProtectUnchained' );
224 return element
225 ? element.checked
226 : true; // No control, so we need to let the user set both levels
230 * Find the highest protection level in any selector
232 getMaxLevel: function () {
233 var maxIndex = -1;
234 this.forEachLevelSelector( function ( element ) {
235 if ( element.selectedIndex > maxIndex ) {
236 maxIndex = element.selectedIndex;
238 } );
239 return maxIndex;
243 * Protect all actions at the specified level
245 * @param index int Protection level
247 setAllSelectors: function ( index ) {
248 this.forEachLevelSelector( function ( element ) {
249 if ( element.selectedIndex !== index ) {
250 element.selectedIndex = index;
252 } );
256 * Apply a callback to each protection selector
258 * @param func callable Callback function
260 forEachLevelSelector: function ( func ) {
261 var i, selectors;
263 selectors = this.getLevelSelectors();
264 for ( i = 0; i < selectors.length; i++ ) {
265 func( selectors[i] );
270 * Get a list of all protection selectors on the page
272 * @return Array
274 getLevelSelectors: function () {
275 var i, ours, all, element;
277 all = document.getElementsByTagName( 'select' );
278 ours = [];
279 for ( i = 0; i < all.length; i++ ) {
280 element = all[i];
281 if ( element.id.match( /^mwProtect-level-/ ) ) {
282 ours[ours.length] = element;
285 return ours;
289 * Apply a callback to each expiry input
291 * @param func callable Callback function
293 forEachExpiryInput: function ( func ) {
294 var i, inputs;
296 inputs = this.getExpiryInputs();
297 for ( i = 0; i < inputs.length; i++ ) {
298 func( inputs[i] );
303 * Get a list of all expiry inputs on the page
305 * @return Array
307 getExpiryInputs: function () {
308 var i, all, element, ours;
310 all = document.getElementsByTagName( 'input' );
311 ours = [];
312 for ( i = 0; i < all.length; i++ ) {
313 element = all[i];
314 if ( element.name.match( /^mwProtect-expiry-/ ) ) {
315 ours[ours.length] = element;
318 return ours;
322 * Apply a callback to each expiry selector list
323 * @param func callable Callback function
325 forEachExpirySelector: function ( func ) {
326 var i, inputs;
328 inputs = this.getExpirySelectors();
329 for ( i = 0; i < inputs.length; i++ ) {
330 func( inputs[i] );
335 * Get a list of all expiry selector lists on the page
337 * @return Array
339 getExpirySelectors: function () {
340 var i, all, ours, element;
342 all = document.getElementsByTagName( 'select' );
343 ours = [];
344 for ( i = 0; i < all.length; i++ ) {
345 element = all[i];
346 if ( element.id.match( /^mwProtectExpirySelection-/ ) ) {
347 ours[ours.length] = element;
350 return ours;
354 * Enable/disable protection selectors and expiry inputs
356 * @param val boolean Enable?
358 enableUnchainedInputs: function ( val ) {
359 var first = true;
361 this.forEachLevelSelector( function ( element ) {
362 if ( first ) {
363 first = false;
364 } else {
365 element.disabled = !val;
367 } );
368 first = true;
369 this.forEachExpiryInput( function ( element ) {
370 if ( first ) {
371 first = false;
372 } else {
373 element.disabled = !val;
375 } );
376 first = true;
377 this.forEachExpirySelector( function ( element ) {
378 if ( first ) {
379 first = false;
380 } else {
381 element.disabled = !val;
383 } );
387 }( mediaWiki, jQuery ) );