(bug 12650) Make it possible to enter separate expiry times for each restriction...
[lhc/web/wiklou.git] / skins / common / protect.js
1 /**
2 * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox)
3 * on the protection form
4 *
5 * @param String tableId Identifier of the table containing UI bits
6 * @param String labelText Text to use for the checkbox label
7 */
8 function protectInitialize( tableId, labelText, types ) {
9 if( !( document.createTextNode && document.getElementById && document.getElementsByTagName ) )
10 return false;
11
12 var box = document.getElementById( tableId );
13 if( !box )
14 return false;
15
16 var tbody = box.getElementsByTagName( 'tbody' )[0];
17 var row = document.createElement( 'tr' );
18 tbody.appendChild( row );
19
20 row.appendChild( document.createElement( 'td' ) );
21 var col = document.createElement( 'td' );
22 row.appendChild( col );
23 // If there is only one protection type, there is nothing to chain
24 if( types > 1 ) {
25 var check = document.createElement( 'input' );
26 check.id = 'mwProtectUnchained';
27 check.type = 'checkbox';
28 col.appendChild( check );
29 addClickHandler( check, protectChainUpdate );
30
31 col.appendChild( document.createTextNode( ' ' ) );
32 var label = document.createElement( 'label' );
33 label.htmlFor = 'mwProtectUnchained';
34 label.appendChild( document.createTextNode( labelText ) );
35 col.appendChild( label );
36
37 check.checked = !protectAllMatch();
38 protectEnable( check.checked );
39 }
40
41 setCascadeCheckbox();
42
43 return true;
44 }
45
46 /**
47 * Determine if, given the cascadeable protection levels
48 * and what is currently selected, if the cascade box
49 * can be checked
50 *
51 * @return boolean
52 *
53 */
54 function setCascadeCheckbox() {
55 // For non-existent titles, there is no cascade option
56 if( !document.getElementById( 'mwProtect-cascade' ) ) {
57 return false;
58 }
59 var lists = protectSelectors();
60 for( var i = 0; i < lists.length; i++ ) {
61 if( lists[i].selectedIndex > -1 ) {
62 var items = lists[i].getElementsByTagName( 'option' );
63 var selected = items[ lists[i].selectedIndex ].value;
64 if( !isCascadeableLevel(selected) ) {
65 document.getElementById( 'mwProtect-cascade' ).checked = false;
66 document.getElementById( 'mwProtect-cascade' ).disabled = true;
67 return false;
68 }
69 }
70 }
71 document.getElementById( 'mwProtect-cascade' ).disabled = false;
72 return true;
73 }
74
75 /**
76 * Is this protection level cascadeable?
77 * @param String level
78 *
79 * @return boolean
80 *
81 */
82 function isCascadeableLevel( level ) {
83 for (var k = 0; k < wgCascadeableLevels.length; k++) {
84 if ( wgCascadeableLevels[k] == level ) {
85 return true;
86 }
87 }
88 return false;
89 }
90
91 /**
92 * When protection levels are locked together, update the rest
93 * when one action's level changes
94 *
95 * @param Element source Level selector that changed
96 */
97 function protectLevelsUpdate(source) {
98 if( !protectUnchained() )
99 protectUpdateAll( source.selectedIndex );
100 setCascadeCheckbox();
101 }
102
103 /**
104 * When protection levels are locked together, update the
105 * expiries when one changes
106 *
107 * @param Element source expiry input that changed
108 */
109
110 function protectExpiryUpdate(source) {
111 if( !protectUnchained() ) {
112 var expiry = source.value;
113 expiryForInputs(function(set) {
114 set.value = expiry;
115 });
116 }
117 }
118
119 /**
120 * When protection levels are locked together, update the
121 * expiry lists when one changes and clear the custom inputs
122 *
123 * @param Element source expiry selector that changed
124 */
125 function protectExpiryListUpdate(source) {
126 if( !protectUnchained() ) {
127 var expiry = source.value;
128 expiryListForInputs(function(set) {
129 set.value = expiry;
130 });
131 expiryForInputs(function(set) {
132 set.value = '';
133 });
134 }
135 }
136
137 /**
138 * Update chain status and enable/disable various bits of the UI
139 * when the user changes the "unlock move permissions" checkbox
140 */
141 function protectChainUpdate() {
142 if( protectUnchained() ) {
143 protectEnable( true );
144 } else {
145 protectChain();
146 protectEnable( false );
147 }
148 setCascadeCheckbox();
149 }
150
151 /**
152 * Are all actions protected at the same level?
153 *
154 * @return boolean
155 */
156 function protectAllMatch() {
157 var values = new Array();
158 protectForSelectors(function(set) {
159 values[values.length] = set.selectedIndex;
160 });
161 for (var i = 1; i < values.length; i++) {
162 if (values[i] != values[0]) {
163 return false;
164 }
165 }
166 return true;
167 }
168
169 /**
170 * Is protection chaining on or off?
171 *
172 * @return bool
173 */
174 function protectUnchained() {
175 var unchain = document.getElementById( 'mwProtectUnchained' );
176 return unchain
177 ? unchain.checked
178 : true; // No control, so we need to let the user set both levels
179 }
180
181 /**
182 * Find the highest-protected action and set all others to that level
183 */
184 function protectChain() {
185 var maxIndex = -1;
186 protectForSelectors(function(set) {
187 if (set.selectedIndex > maxIndex) {
188 maxIndex = set.selectedIndex;
189 }
190 });
191 protectUpdateAll(maxIndex);
192 }
193
194 /**
195 * Protect all actions at the specified level
196 *
197 * @param int index Protection level
198 */
199 function protectUpdateAll(index) {
200 protectForSelectors(function(set) {
201 if (set.selectedIndex != index) {
202 set.selectedIndex = index;
203 }
204 });
205 }
206
207 /**
208 * Apply a callback to each protection selector
209 *
210 * @param callable func Callback function
211 */
212 function protectForSelectors(func) {
213 var selectors = protectSelectors();
214 for (var i = 0; i < selectors.length; i++) {
215 func(selectors[i]);
216 }
217 }
218
219 /**
220 * Get a list of all protection selectors on the page
221 *
222 * @return Array
223 */
224 function protectSelectors() {
225 var all = document.getElementsByTagName("select");
226 var ours = new Array();
227 for (var i = 0; i < all.length; i++) {
228 var set = all[i];
229 if (set.id.match(/^mwProtect-level-/)) {
230 ours[ours.length] = set;
231 }
232 }
233 return ours;
234 }
235
236 /**
237 * Apply a callback to each expiry input
238 *
239 * @param callable func Callback function
240 */
241 function expiryForInputs(func) {
242 var inputs = expiryInputs();
243 for (var i = 0; i < inputs.length; i++) {
244 func(inputs[i]);
245 }
246 }
247
248 /**
249 * Get a list of all expiry inputs on the page
250 *
251 * @return Array
252 */
253 function expiryInputs() {
254 var all = document.getElementsByTagName("input");
255 var ours = new Array();
256 for (var i = 0; i < all.length; i++) {
257 var set = all[i];
258 if (set.name.match(/^mwProtect-expiry-/)) {
259 ours[ours.length] = set;
260 }
261 }
262 return ours;
263 }
264
265 /**
266 * Apply a callback to each expiry selector list
267 * @param callable func Callback function
268 */
269 function expiryListForInputs(func) {
270 var inputs = expiryListInputs();
271 for (var i = 0; i < inputs.length; i++) {
272 func(inputs[i]);
273 }
274 }
275
276 /**
277 * Get a list of all expiry selector lists on the page
278 *
279 * @return Array
280 */
281 function expiryListInputs() {
282 var all = document.getElementsByTagName("select");
283 var ours = new Array();
284 for (var i = 0; i < all.length; i++) {
285 var set = all[i];
286 if (set.id.match(/^mwProtectExpiryList-/)) {
287 ours[ours.length] = set;
288 }
289 }
290 return ours;
291 }
292
293 /**
294 * Enable/disable protection selectors and expiry inputs
295 *
296 * @param boolean val Enable?
297 */
298 function protectEnable(val) {
299 // fixme
300 var first = true;
301 protectForSelectors(function(set) {
302 if (first) {
303 first = false;
304 } else {
305 set.disabled = !val;
306 set.style.visible = val ? "visible" : "hidden";
307 }
308 });
309 first = true;
310 expiryForInputs(function(set) {
311 if (first) {
312 first = false;
313 } else {
314 set.disabled = !val;
315 set.style.visible = val ? "visible" : "hidden";
316 }
317 });
318 first = true;
319 expiryListForInputs(function(set) {
320 if (first) {
321 first = false;
322 } else {
323 set.disabled = !val;
324 set.style.visible = val ? "visible" : "hidden";
325 }
326 });
327 }