Merge "Use WebRequest instead of $_SERVER in ApiMain."
[lhc/web/wiklou.git] / skins / common / protect.js
1
2 window.ProtectionForm = {
3 'existingMatch': false,
4
5 /**
6 * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox)
7 * on the protection form
8 *
9 * @param opts Object : parameters with members:
10 * tableId Identifier of the table containing UI bits
11 * labelText Text to use for the checkbox label
12 * numTypes The number of protection types
13 * existingMatch True if all the existing expiry times match
14 */
15 'init': function( opts ) {
16 if( !( document.createTextNode && document.getElementById && document.getElementsByTagName ) )
17 return false;
18
19 var box = document.getElementById( opts.tableId );
20 if( !box )
21 return false;
22
23 var boxbody = box.getElementsByTagName('tbody')[0];
24 var row = document.createElement( 'tr' );
25 boxbody.insertBefore( row, boxbody.firstChild.nextSibling );
26
27 this.existingMatch = opts.existingMatch;
28
29 var cell = document.createElement( 'td' );
30 row.appendChild( cell );
31 // If there is only one protection type, there is nothing to chain
32 if( opts.numTypes > 1 ) {
33 var check = document.createElement( 'input' );
34 check.id = 'mwProtectUnchained';
35 check.type = 'checkbox';
36 cell.appendChild( check );
37 addClickHandler( check, function() { ProtectionForm.onChainClick(); } );
38
39 cell.appendChild( document.createTextNode( ' ' ) );
40 var label = document.createElement( 'label' );
41 label.htmlFor = 'mwProtectUnchained';
42 label.appendChild( document.createTextNode( opts.labelText ) );
43 cell.appendChild( label );
44
45 check.checked = !this.areAllTypesMatching();
46 this.enableUnchainedInputs( check.checked );
47 }
48
49 $( '#mwProtect-reason' ).byteLimit( 180 );
50
51 this.updateCascadeCheckbox();
52
53 return true;
54 },
55
56 /**
57 * Sets the disabled attribute on the cascade checkbox depending on the current selected levels
58 */
59 'updateCascadeCheckbox': function() {
60 // For non-existent titles, there is no cascade option
61 if( !document.getElementById( 'mwProtect-cascade' ) ) {
62 return;
63 }
64 var lists = this.getLevelSelectors();
65 for( var i = 0; i < lists.length; i++ ) {
66 if( lists[i].selectedIndex > -1 ) {
67 var items = lists[i].getElementsByTagName( 'option' );
68 var selected = items[ lists[i].selectedIndex ].value;
69 if( !this.isCascadeableLevel(selected) ) {
70 document.getElementById( 'mwProtect-cascade' ).checked = false;
71 document.getElementById( 'mwProtect-cascade' ).disabled = true;
72 return;
73 }
74 }
75 }
76 document.getElementById( 'mwProtect-cascade' ).disabled = false;
77 },
78
79 /**
80 * Checks if a cerain protection level is cascadeable.
81 * @param level {String}
82 * @return {Boolean}
83 */
84 'isCascadeableLevel': function( level ) {
85 var cascadeLevels, len, i;
86
87 cascadeLevels = mw.config.get( 'wgCascadeableLevels' );
88 // cascadeLevels isn't defined on all pages
89 if ( cascadeLevels ) {
90 for ( i = 0, len = cascadeLevels.length; i < len; i += 1 ) {
91 if ( cascadeLevels[i] === level ) {
92 return true;
93 }
94 }
95 }
96 return false;
97 },
98
99 /**
100 * When protection levels are locked together, update the rest
101 * when one action's level changes
102 *
103 * @param source Element Level selector that changed
104 */
105 'updateLevels': function(source) {
106 if( !this.isUnchained() )
107 this.setAllSelectors( source.selectedIndex );
108 this.updateCascadeCheckbox();
109 },
110
111 /**
112 * When protection levels are locked together, update the
113 * expiries when one changes
114 *
115 * @param source Element expiry input that changed
116 */
117
118 'updateExpiry': function(source) {
119 if( !this.isUnchained() ) {
120 var expiry = source.value;
121 this.forEachExpiryInput(function(element) {
122 element.value = expiry;
123 });
124 }
125 var listId = source.id.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' );
126 var list = document.getElementById( listId );
127 if (list && list.value != 'othertime' ) {
128 if ( this.isUnchained() ) {
129 list.value = 'othertime';
130 } else {
131 this.forEachExpirySelector(function(element) {
132 element.value = 'othertime';
133 });
134 }
135 }
136 },
137
138 /**
139 * When protection levels are locked together, update the
140 * expiry lists when one changes and clear the custom inputs
141 *
142 * @param source Element expiry selector that changed
143 */
144 'updateExpiryList': function(source) {
145 if( !this.isUnchained() ) {
146 var expiry = source.value;
147 this.forEachExpirySelector(function(element) {
148 element.value = expiry;
149 });
150 this.forEachExpiryInput(function(element) {
151 element.value = '';
152 });
153 }
154 },
155
156 /**
157 * Update chain status and enable/disable various bits of the UI
158 * when the user changes the "unlock move permissions" checkbox
159 */
160 'onChainClick': function() {
161 if( this.isUnchained() ) {
162 this.enableUnchainedInputs( true );
163 } else {
164 this.setAllSelectors( this.getMaxLevel() );
165 this.enableUnchainedInputs( false );
166 }
167 this.updateCascadeCheckbox();
168 },
169
170 /**
171 * Returns true if the named attribute in all objects in the given array are matching
172 */
173 'matchAttribute' : function( objects, attrName ) {
174 var value = null;
175
176 // Check levels
177 for ( var i = 0; i < objects.length; i++ ) {
178 var element = objects[i];
179 if ( value == null ) {
180 value = element[attrName];
181 } else {
182 if ( value != element[attrName] ) {
183 return false;
184 }
185 }
186 }
187 return true;
188 },
189
190 /**
191 * Are all actions protected at the same level, with the same expiry time?
192 *
193 * @return boolean
194 */
195 'areAllTypesMatching': function() {
196 return this.existingMatch
197 && this.matchAttribute( this.getLevelSelectors(), 'selectedIndex' )
198 && this.matchAttribute( this.getExpirySelectors(), 'selectedIndex' )
199 && this.matchAttribute( this.getExpiryInputs(), 'value' );
200 },
201
202 /**
203 * Is protection chaining off?
204 *
205 * @return bool
206 */
207 'isUnchained': function() {
208 var element = document.getElementById( 'mwProtectUnchained' );
209 return element
210 ? element.checked
211 : true; // No control, so we need to let the user set both levels
212 },
213
214 /**
215 * Find the highest protection level in any selector
216 */
217 'getMaxLevel': function() {
218 var maxIndex = -1;
219 this.forEachLevelSelector(function(element) {
220 if (element.selectedIndex > maxIndex) {
221 maxIndex = element.selectedIndex;
222 }
223 });
224 return maxIndex;
225 },
226
227 /**
228 * Protect all actions at the specified level
229 *
230 * @param index int Protection level
231 */
232 'setAllSelectors': function(index) {
233 this.forEachLevelSelector(function(element) {
234 if (element.selectedIndex != index) {
235 element.selectedIndex = index;
236 }
237 });
238 },
239
240 /**
241 * Apply a callback to each protection selector
242 *
243 * @param func callable Callback function
244 */
245 'forEachLevelSelector': function(func) {
246 var selectors = this.getLevelSelectors();
247 for (var i = 0; i < selectors.length; i++) {
248 func(selectors[i]);
249 }
250 },
251
252 /**
253 * Get a list of all protection selectors on the page
254 *
255 * @return Array
256 */
257 'getLevelSelectors': function() {
258 var all = document.getElementsByTagName("select");
259 var ours = [];
260 for (var i = 0; i < all.length; i++) {
261 var element = all[i];
262 if (element.id.match(/^mwProtect-level-/)) {
263 ours[ours.length] = element;
264 }
265 }
266 return ours;
267 },
268
269 /**
270 * Apply a callback to each expiry input
271 *
272 * @param func callable Callback function
273 */
274 'forEachExpiryInput': function(func) {
275 var inputs = this.getExpiryInputs();
276 for (var i = 0; i < inputs.length; i++) {
277 func(inputs[i]);
278 }
279 },
280
281 /**
282 * Get a list of all expiry inputs on the page
283 *
284 * @return Array
285 */
286 'getExpiryInputs': function() {
287 var all = document.getElementsByTagName("input");
288 var ours = [];
289 for (var i = 0; i < all.length; i++) {
290 var element = all[i];
291 if (element.name.match(/^mwProtect-expiry-/)) {
292 ours[ours.length] = element;
293 }
294 }
295 return ours;
296 },
297
298 /**
299 * Apply a callback to each expiry selector list
300 * @param func callable Callback function
301 */
302 'forEachExpirySelector': function(func) {
303 var inputs = this.getExpirySelectors();
304 for (var i = 0; i < inputs.length; i++) {
305 func(inputs[i]);
306 }
307 },
308
309 /**
310 * Get a list of all expiry selector lists on the page
311 *
312 * @return Array
313 */
314 'getExpirySelectors': function() {
315 var all = document.getElementsByTagName("select");
316 var ours = [];
317 for (var i = 0; i < all.length; i++) {
318 var element = all[i];
319 if (element.id.match(/^mwProtectExpirySelection-/)) {
320 ours[ours.length] = element;
321 }
322 }
323 return ours;
324 },
325
326 /**
327 * Enable/disable protection selectors and expiry inputs
328 *
329 * @param val boolean Enable?
330 */
331 'enableUnchainedInputs': function(val) {
332 var first = true;
333 this.forEachLevelSelector(function(element) {
334 if (first) {
335 first = false;
336 } else {
337 element.disabled = !val;
338 }
339 });
340 first = true;
341 this.forEachExpiryInput(function(element) {
342 if (first) {
343 first = false;
344 } else {
345 element.disabled = !val;
346 }
347 });
348 first = true;
349 this.forEachExpirySelector(function(element) {
350 if (first) {
351 first = false;
352 } else {
353 element.disabled = !val;
354 }
355 });
356 }
357 };