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