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