New tabbed preferences UI
[lhc/web/wiklou.git] / skins / common / wikibits.js
1 // Wikipedia JavaScript support functions
2
3 var clientPC = navigator.userAgent.toLowerCase(); // Get client info
4 var is_gecko = ((clientPC.indexOf('gecko')!=-1) && (clientPC.indexOf('spoofer')==-1)
5 && (clientPC.indexOf('khtml') == -1) && (clientPC.indexOf('netscape/7.0')==-1));
6 var is_safari = ((clientPC.indexOf('AppleWebKit')!=-1) && (clientPC.indexOf('spoofer')==-1));
7 var is_khtml = (navigator.vendor == 'KDE' || ( document.childNodes && !document.all && !navigator.taintEnabled ));
8 if (clientPC.indexOf('opera')!=-1) {
9 var is_opera = true;
10 var is_opera_preseven = (window.opera && !document.childNodes);
11 var is_opera_seven = (window.opera && document.childNodes);
12 }
13
14 // add any onload functions in this hook (please don't hard-code any events in the xhtml source)
15 function onloadhook () {
16 // don't run anything below this for non-dom browsers
17 if(!(document.getElementById && document.getElementsByTagName)) return;
18 histrowinit();
19 unhidetzbutton();
20 tabbedprefs();
21 akeytt();
22 }
23 if (window.addEventListener) window.addEventListener("load",onloadhook,false);
24 else if (window.attachEvent) window.attachEvent("onload",onloadhook);
25
26
27 // document.write special stylesheet links
28 if(typeof stylepath != 'undefined' && typeof skin != 'undefined') {
29 if (is_opera_preseven) {
30 document.write('<link rel="stylesheet" type="text/css" href="'+stylepath+'/'+skin+'/Opera6Fixes.css">');
31 } else if (is_opera_seven) {
32 document.write('<link rel="stylesheet" type="text/css" href="'+stylepath+'/'+skin+'/Opera7Fixes.css">');
33 } else if (is_khtml) {
34 document.write('<link rel="stylesheet" type="text/css" href="'+stylepath+'/'+skin+'/KHTMLFixes.css">');
35 }
36 }
37 // Un-trap us from framesets
38 if( window.top != window ) window.top.location = window.location;
39
40 // for enhanced RecentChanges
41 function toggleVisibility( _levelId, _otherId, _linkId) {
42 var thisLevel = document.getElementById( _levelId );
43 var otherLevel = document.getElementById( _otherId );
44 var linkLevel = document.getElementById( _linkId );
45 if ( thisLevel.style.display == 'none' ) {
46 thisLevel.style.display = 'block';
47 otherLevel.style.display = 'none';
48 linkLevel.style.display = 'inline';
49 } else {
50 thisLevel.style.display = 'none';
51 otherLevel.style.display = 'inline';
52 linkLevel.style.display = 'none';
53 }
54 }
55
56 // page history stuff
57 // attach event handlers to the input elements on history page
58 function histrowinit () {
59 hf = document.getElementById('pagehistory');
60 if(!hf) return;
61 lis = hf.getElementsByTagName('li');
62 for (i=0;i<lis.length;i++) {
63 inputs=lis[i].getElementsByTagName('input');
64 if(inputs[0] && inputs[1]) {
65 inputs[0].onclick = diffcheck;
66 inputs[1].onclick = diffcheck;
67 }
68 }
69 diffcheck();
70 }
71 // check selection and tweak visibility/class onclick
72 function diffcheck() {
73 var dli = false; // the li where the diff radio is checked
74 var oli = false; // the li where the oldid radio is checked
75 hf = document.getElementById('pagehistory');
76 if(!hf) return;
77 lis = hf.getElementsByTagName('li');
78 for (i=0;i<lis.length;i++) {
79 inputs=lis[i].getElementsByTagName('input');
80 if(inputs[1] && inputs[0]) {
81 if(inputs[1].checked || inputs[0].checked) { // this row has a checked radio button
82 if(inputs[1].checked && inputs[0].checked && inputs[0].value == inputs[1].value) return false;
83 if(oli) { // it's the second checked radio
84 if(inputs[1].checked) {
85 oli.className = "selected";
86 return false
87 }
88 } else if (inputs[0].checked) {
89 return false;
90 }
91 if(inputs[0].checked) dli = lis[i];
92 if(!oli) inputs[0].style.visibility = 'hidden';
93 if(dli) inputs[1].style.visibility = 'hidden';
94 lis[i].className = "selected";
95 oli = lis[i];
96 } else { // no radio is checked in this row
97 if(!oli) inputs[0].style.visibility = 'hidden';
98 else inputs[0].style.visibility = 'visible';
99 if(dli) inputs[1].style.visibility = 'hidden';
100 else inputs[1].style.visibility = 'visible';
101 lis[i].className = "";
102 }
103 }
104 }
105 }
106
107 // generate toc from prefs form, fold sections
108 // XXX: needs testing on IE/Mac and safari
109 // more comments to follow
110 function tabbedprefs() {
111 var prefform = document.getElementById('preferences');
112 if(!prefform || !document.createElement) return;
113 if(prefform.nodeName.toLowerCase() == 'a') return; // Occasional IE problem
114 prefform.className = prefform.className + 'jsprefs';
115 var sections = new Array();
116 children = prefform.childNodes;
117 var seci = 0;
118 for(i=0;i<children.length;i++) {
119 if(children[i].nodeName.toLowerCase() == 'fieldset') {
120 children[i].id = 'prefsection-' + seci;
121 children[i].className = 'prefsection';
122 if(is_opera || is_khtml) children[i].className = 'prefsection operaprefsection';
123 legends = children[i].getElementsByTagName('legend');
124 sections[seci] = new Object();
125 legends[0].className = 'mainLegend';
126 if(legends[0] && legends[0].firstChild.nodeValue)
127 sections[seci].text = legends[0].firstChild.nodeValue;
128 else
129 sections[seci].text = '# ' + seci;
130 sections[seci].secid = children[i].id;
131 seci++;
132 if(sections.length != 1) children[i].style.display = 'none';
133 else var selectedid = children[i].id;
134 }
135 }
136 var toc = document.createElement('ul');
137 toc.id = 'preftoc';
138 toc.selectedid = selectedid;
139 for(i=0;i<sections.length;i++) {
140 var li = document.createElement('li');
141 if(i == 0) li.className = 'selected';
142 var a = document.createElement('a');
143 a.href = '#' + sections[i].secid;
144 a.onclick = uncoversection;
145 a.appendChild(document.createTextNode(sections[i].text));
146 a.secid = sections[i].secid;
147 li.appendChild(a);
148 toc.appendChild(li);
149 }
150 prefform.parentNode.insertBefore(toc, prefform.parentNode.childNodes[0]);
151 document.getElementById('prefsubmit').id = 'prefcontrol';
152 }
153 function uncoversection() {
154 oldsecid = this.parentNode.parentNode.selectedid;
155 newsec = document.getElementById(this.secid);
156 if(oldsecid != this.secid) {
157 ul = document.getElementById('preftoc');
158 document.getElementById(oldsecid).style.display = 'none';
159 newsec.style.display = 'block';
160 ul.selectedid = this.secid;
161 lis = ul.getElementsByTagName('li');
162 for(i=0;i< lis.length;i++) {
163 lis[i].className = '';
164 }
165 this.parentNode.className = 'selected';
166 }
167 return false;
168 }
169
170 // Timezone stuff
171 // tz in format [+-]HHMM
172 function checkTimezone( tz, msg ) {
173 var localclock = new Date();
174 // returns negative offset from GMT in minutes
175 var tzRaw = localclock.getTimezoneOffset();
176 var tzHour = Math.floor( Math.abs(tzRaw) / 60);
177 var tzMin = Math.abs(tzRaw) % 60;
178 var tzString = ((tzRaw >= 0) ? "-" : "+") + ((tzHour < 10) ? "0" : "") + tzHour + ((tzMin < 10) ? "0" : "") + tzMin;
179 if( tz != tzString ) {
180 var junk = msg.split( '$1' );
181 document.write( junk[0] + "UTC" + tzString + junk[1] );
182 }
183 }
184 function unhidetzbutton() {
185 tzb = document.getElementById('guesstimezonebutton')
186 if(tzb) tzb.style.display = 'inline';
187 }
188
189 // in [-]HH:MM format...
190 // won't yet work with non-even tzs
191 function fetchTimezone() {
192 // FIXME: work around Safari bug
193 var localclock = new Date();
194 // returns negative offset from GMT in minutes
195 var tzRaw = localclock.getTimezoneOffset();
196 var tzHour = Math.floor( Math.abs(tzRaw) / 60);
197 var tzMin = Math.abs(tzRaw) % 60;
198 var tzString = ((tzRaw >= 0) ? "-" : "") + ((tzHour < 10) ? "0" : "") + tzHour +
199 ":" + ((tzMin < 10) ? "0" : "") + tzMin;
200 return tzString;
201 }
202
203 function guessTimezone(box) {
204 document.getElementsByName("wpHourDiff")[0].value = fetchTimezone();
205 }
206
207 function showTocToggle() {
208 if (document.createTextNode) {
209 // Uses DOM calls to avoid document.write + XHTML issues
210
211 var linkHolder = document.getElementById('toctitle')
212 if (!linkHolder) return;
213
214 var outerSpan = document.createElement('span');
215 outerSpan.className = 'toctoggle';
216
217 var toggleLink = document.createElement('a');
218 toggleLink.id = 'togglelink';
219 toggleLink.className = 'internal';
220 toggleLink.href = 'javascript:toggleToc()';
221 toggleLink.appendChild(document.createTextNode(tocHideText));
222
223 outerSpan.appendChild(document.createTextNode('['));
224 outerSpan.appendChild(toggleLink);
225 outerSpan.appendChild(document.createTextNode(']'));
226
227 linkHolder.appendChild(document.createTextNode(' '));
228 linkHolder.appendChild(outerSpan);
229
230 var cookiePos = document.cookie.indexOf("hidetoc=");
231 if (cookiePos > -1 && document.cookie.charAt(cookiePos + 8) == 1)
232 toggleToc();
233 }
234 }
235
236 function changeText(el, newText) {
237 // Safari work around
238 if (el.innerText)
239 el.innerText = newText;
240 else if (el.firstChild && el.firstChild.nodeValue)
241 el.firstChild.nodeValue = newText;
242 }
243
244 function toggleToc() {
245 var toc = document.getElementById('toc').getElementsByTagName('ul')[0];
246 var toggleLink = document.getElementById('togglelink')
247
248 if(toc && toggleLink && toc.style.display == 'none') {
249 changeText(toggleLink, tocHideText);
250 toc.style.display = 'block';
251 document.cookie = "hidetoc=0";
252 } else {
253 changeText(toggleLink, tocShowText);
254 toc.style.display = 'none';
255 document.cookie = "hidetoc=1";
256 }
257 }
258
259 // this function generates the actual toolbar buttons with localized text
260 // we use it to avoid creating the toolbar where javascript is not enabled
261 function addButton(imageFile, speedTip, tagOpen, tagClose, sampleText) {
262
263 // Don't generate buttons for browsers which don't fully
264 // support it.
265 if(!document.selection && !is_gecko) {
266 return false;
267 }
268 imageFile=escapeQuotesHTML(imageFile);
269 speedTip=escapeQuotesHTML(speedTip);
270 tagOpen=escapeQuotes(tagOpen);
271 tagClose=escapeQuotes(tagClose);
272 sampleText=escapeQuotes(sampleText);
273 var mouseOver="";
274
275 document.write("<a href=\"javascript:insertTags");
276 document.write("('"+tagOpen+"','"+tagClose+"','"+sampleText+"');\">");
277 document.write("<img width=\"23\" height=\"22\" src=\""+imageFile+"\" border=\"0\" alt=\""+speedTip+"\" title=\""+speedTip+"\""+mouseOver+">");
278 document.write("</a>");
279 return;
280 }
281
282 function escapeQuotes(text) {
283 var re=new RegExp("'","g");
284 text=text.replace(re,"\\'");
285 re=new RegExp("\\n","g");
286 text=text.replace(re,"\\n");
287 return escapeQuotesHTML(text);
288 }
289
290 function escapeQuotesHTML(text) {
291 var re=new RegExp('&',"g");
292 text=text.replace(re,"&amp;");
293 var re=new RegExp('"',"g");
294 text=text.replace(re,"&quot;");
295 var re=new RegExp('<',"g");
296 text=text.replace(re,"&lt;");
297 var re=new RegExp('>',"g");
298 text=text.replace(re,"&gt;");
299 return text;
300 }
301
302 // apply tagOpen/tagClose to selection in textarea,
303 // use sampleText instead of selection if there is none
304 // copied and adapted from phpBB
305 function insertTags(tagOpen, tagClose, sampleText) {
306
307 var txtarea = document.editform.wpTextbox1;
308 // IE
309 if(document.selection && !is_gecko) {
310 var theSelection = document.selection.createRange().text;
311 if(!theSelection) { theSelection=sampleText;}
312 txtarea.focus();
313 if(theSelection.charAt(theSelection.length - 1) == " "){// exclude ending space char, if any
314 theSelection = theSelection.substring(0, theSelection.length - 1);
315 document.selection.createRange().text = tagOpen + theSelection + tagClose + " ";
316 } else {
317 document.selection.createRange().text = tagOpen + theSelection + tagClose;
318 }
319
320 // Mozilla
321 } else if(txtarea.selectionStart || txtarea.selectionStart == '0') {
322 var replaced = false;
323 var startPos = txtarea.selectionStart;
324 var endPos = txtarea.selectionEnd;
325 if(endPos-startPos) replaced=true;
326 var scrollTop=txtarea.scrollTop;
327 var myText = (txtarea.value).substring(startPos, endPos);
328 if(!myText) { myText=sampleText;}
329 if(myText.charAt(myText.length - 1) == " "){ // exclude ending space char, if any
330 subst = tagOpen + myText.substring(0, (myText.length - 1)) + tagClose + " ";
331 } else {
332 subst = tagOpen + myText + tagClose;
333 }
334 txtarea.value = txtarea.value.substring(0, startPos) + subst +
335 txtarea.value.substring(endPos, txtarea.value.length);
336 txtarea.focus();
337 //set new selection
338 if(replaced){
339 var cPos=startPos+(tagOpen.length+myText.length+tagClose.length);
340 txtarea.selectionStart=cPos;
341 txtarea.selectionEnd=cPos;
342 }else{
343 txtarea.selectionStart=startPos+tagOpen.length;
344 txtarea.selectionEnd=startPos+tagOpen.length+myText.length;
345 }
346 txtarea.scrollTop=scrollTop;
347
348 // All other browsers get no toolbar.
349 // There was previously support for a crippled "help"
350 // bar, but that caused more problems than it solved.
351 }
352 // reposition cursor if possible
353 if (txtarea.createTextRange) txtarea.caretPos = document.selection.createRange().duplicate();
354 }
355
356 function akeytt() {
357 if(typeof ta == "undefined" || !ta) return;
358 pref = 'alt-';
359 if(is_safari || navigator.userAgent.toLowerCase().indexOf( 'mac' ) + 1
360 || navigator.userAgent.toLowerCase().indexOf( 'konqueror' ) + 1 ) pref = 'control-';
361 if(is_opera) pref = 'shift-esc-';
362
363 for(id in ta) {
364 n = document.getElementById(id);
365 if(n){
366 // Are we putting accesskey in it
367 if(ta[id][0].length > 0) {
368 // Is this object a object? If not assume it's the next child.
369
370 if ( n.nodeName.toLowerCase() == "a" ) {
371 a = n;
372 } else {
373 a = n.childNodes[0];
374 }
375
376 if(a){
377 a.accessKey = ta[id][0];
378 ak = ' ['+pref+ta[id][0]+']';
379 }
380 } else {
381 // We don't care what type the object is when assigning tooltip
382 a = n;
383 ak = '';
384 }
385
386 if (a) {
387 a.title = ta[id][1]+ak;
388 }
389 }
390 }
391 }
392
393 function setupRightClickEdit() {
394 if( document.getElementsByTagName ) {
395 var divs = document.getElementsByTagName( 'div' );
396 for( var i = 0; i < divs.length; i++ ) {
397 var el = divs[i];
398 if( el.className == 'editsection' ) {
399 addRightClickEditHandler( el );
400 }
401 }
402 }
403 }
404
405 function addRightClickEditHandler( el ) {
406 for( var i = 0; i < el.childNodes.length; i++ ) {
407 var link = el.childNodes[i];
408 if( link.nodeType == 1 && link.nodeName.toLowerCase() == 'a' ) {
409 var editHref = link.getAttribute( 'href' );
410
411 // find the following a
412 var next = el.nextSibling;
413 while( next.nodeType != 1 )
414 next = next.nextSibling;
415
416 // find the following header
417 next = next.nextSibling;
418 while( next.nodeType != 1 )
419 next = next.nextSibling;
420
421 if( next && next.nodeType == 1 &&
422 next.nodeName.match( /^[Hh][1-6]$/ ) ) {
423 next.oncontextmenu = function() {
424 document.location = editHref;
425 return false;
426 }
427 }
428 }
429 }
430 }
431
432 function fillDestFilename() {
433 if (!document.getElementById) return;
434 var path = document.getElementById('wpUploadFile').value;
435 // Find trailing part
436 var slash = path.lastIndexOf( '/' );
437 var backslash = path.lastIndexOf( '\\' );
438 var fname;
439 if ( slash == -1 && backslash == -1 ) {
440 fname = path;
441 } else if ( slash > backslash ) {
442 fname = path.substring( slash+1, 10000 );
443 } else {
444 fname = path.substring( backslash+1, 10000 );
445 }
446
447 // Capitalise first letter and replace spaces by underscores
448 fname = fname.charAt(0).toUpperCase().concat(fname.substring(1,10000)).replace( / /g, '_' );
449
450 // Output result
451 var destFile = document.getElementById('wpDestFile');
452 if (destFile) destFile.value = fname;
453 }
454
455
456 function considerChangingExpiryFocus() {
457 if (!document.getElementById) return;
458 var drop = document.getElementById('wpBlockExpiry');
459 if (!drop) return;
460 var field = document.getElementById('wpBlockOther');
461 if (!field) return;
462 var opt = drop.value;
463 if (opt == 'other')
464 field.style.display = '';
465 else
466 field.style.display = 'none';
467 }