2 * Some of these scripts were taken from wikipedia.org and were modified for DokuWiki
6 * Some browser detection
8 var clientPC = navigator.userAgent.toLowerCase(); // Get client info
9 var is_macos = navigator.appVersion.indexOf('Mac') != -1;
10 var is_gecko = ((clientPC.indexOf('gecko')!=-1) && (clientPC.indexOf('spoofer')==-1) &&
11 (clientPC.indexOf('khtml') == -1) && (clientPC.indexOf('netscape/7.0')==-1));
12 var is_safari = ((clientPC.indexOf('AppleWebKit')!=-1) && (clientPC.indexOf('spoofer')==-1));
13 var is_khtml = (navigator.vendor == 'KDE' || ( document.childNodes && !document.all && !navigator.taintEnabled ));
14 if (clientPC.indexOf('opera')!=-1) {
16 var is_opera_preseven = (window.opera && !document.childNodes);
17 var is_opera_seven = (window.opera && document.childNodes);
21 * Handy shortcut to document.getElementById
23 * This function was taken from the prototype library
25 * @link http://prototype.conio.net/
28 var elements = new Array();
30 for (var i = 0; i < arguments.length; i++) {
31 var element = arguments[i];
32 if (typeof element == 'string')
33 element = document.getElementById(element);
35 if (arguments.length == 1)
38 elements.push(element);
45 * Simple function to check if a global var is defined
48 * @link http://verens.com/archives/2005/07/25/isset-for-javascript/#comment-2835
50 function isset(varname){
51 return(typeof(window[varname])!='undefined');
55 * Select elements by their class name
57 * @author Dustin Diaz <dustin [at] dustindiaz [dot] com>
58 * @link http://www.dustindiaz.com/getelementsbyclass/
60 function getElementsByClass(searchClass,node,tag) {
61 var classElements = new Array();
66 var els = node.getElementsByTagName(tag);
67 var elsLen = els.length;
68 var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
69 for (var i = 0, j = 0; i < elsLen; i++) {
70 if ( pattern.test(els[i].className) ) {
71 classElements[j] = els[i];
79 * Get the X offset of the top left corner of the given object
81 * @link http://www.quirksmode.org/index.html?/js/findpos.html
83 function findPosX(object){
86 if (obj.offsetParent){
87 while (obj.offsetParent){
88 curleft += obj.offsetLeft;
89 obj = obj.offsetParent;
96 } //end findPosX function
99 * Get the Y offset of the top left corner of the given object
101 * @link http://www.quirksmode.org/index.html?/js/findpos.html
103 function findPosY(object){
106 if (obj.offsetParent){
107 while (obj.offsetParent){
108 curtop += obj.offsetTop;
109 obj = obj.offsetParent;
116 } //end findPosY function
119 * Escape special chars in JavaScript
121 * @author Andreas Gohr <andi@splitbrain.org>
123 function jsEscape(text){
124 var re=new RegExp("\\\\","g");
125 text=text.replace(re,"\\\\");
126 re=new RegExp("'","g");
127 text=text.replace(re,"\\'");
128 re=new RegExp('"',"g");
129 text=text.replace(re,'"');
130 re=new RegExp("\\\\\\\\n","g");
131 text=text.replace(re,"\\n");
136 * This function escapes some special chars
137 * @deprecated by above function
139 function escapeQuotes(text) {
140 var re=new RegExp("'","g");
141 text=text.replace(re,"\\'");
142 re=new RegExp('"',"g");
143 text=text.replace(re,'"');
144 re=new RegExp("\\n","g");
145 text=text.replace(re,"\\n");
150 * Adds a node as the first childenode to the given parent
154 function prependChild(parent,element) {
155 if(!parent.firstChild){
156 parent.appendChild(element);
158 parent.insertBefore(element,parent.firstChild);
163 * Prints a animated gif to show the search is performed
165 * Because we need to modify the DOM here before the document is loaded
166 * and parsed completely we have to rely on document.write()
168 * @author Andreas Gohr <andi@splitbrain.org>
170 function showLoadBar(){
172 document.write('<img src="'+DOKU_BASE+'lib/images/loading.gif" '+
173 'width="150" height="12" alt="..." />');
175 /* this does not work reliable in IE
179 obj.innerHTML = '<img src="'+DOKU_BASE+'lib/images/loading.gif" '+
180 'width="150" height="12" alt="..." />';
181 obj.style.display="block";
187 * Disables the animated gif to show the search is done
189 * @author Andreas Gohr <andi@splitbrain.org>
191 function hideLoadBar(id){
193 if(obj) obj.style.display="none";
197 * Adds the toggle switch to the TOC
199 function addTocToggle() {
200 if(!document.getElementById) return;
201 var header = $('toc__header');
203 var toc = $('toc__inside');
205 var obj = document.createElement('span');
206 obj.id = 'toc__toggle';
207 obj.style.cursor = 'pointer';
208 if (toc && toc.style.display == 'none') {
209 obj.innerHTML = '<span>+</span>';
210 obj.className = 'toc_open';
212 obj.innerHTML = '<span>−</span>';
213 obj.className = 'toc_close';
216 prependChild(header,obj);
217 obj.parentNode.onclick = toggleToc;
219 obj.parentNode.style.cursor = 'pointer';
220 obj.parentNode.style.cursor = 'hand';
225 * This toggles the visibility of the Table of Contents
227 function toggleToc() {
228 var toc = $('toc__inside');
229 var obj = $('toc__toggle');
230 if(toc.style.display == 'none') {
231 toc.style.display = '';
232 obj.innerHTML = '<span>−</span>';
233 obj.className = 'toc_close';
235 toc.style.display = 'none';
236 obj.innerHTML = '<span>+</span>';
237 obj.className = 'toc_open';
242 * This enables/disables checkboxes for acl-administration
244 * @author Frank Schubert <frank@schokilade.de>
246 function checkAclLevel(){
247 if(document.getElementById) {
248 var scope = $('acl_scope').value;
250 //check for namespace
251 if( (scope.indexOf(":*") > 0) || (scope == "*") ){
252 document.getElementsByName('acl_checkbox[4]')[0].disabled=false;
253 document.getElementsByName('acl_checkbox[8]')[0].disabled=false;
255 document.getElementsByName('acl_checkbox[4]')[0].checked=false;
256 document.getElementsByName('acl_checkbox[8]')[0].checked=false;
258 document.getElementsByName('acl_checkbox[4]')[0].disabled=true;
259 document.getElementsByName('acl_checkbox[8]')[0].disabled=true;
265 * Display an insitu footnote popup
267 * @author Andreas Gohr <andi@splitbrain.org>
268 * @author Chris Smith <chris@jalakai.co.uk>
270 function footnote(e){
272 var id = obj.id.substr(5);
274 // get or create the footnote popup div
275 var fndiv = $('insitu__fn');
277 fndiv = document.createElement('div');
278 fndiv.id = 'insitu__fn';
279 fndiv.className = 'insitu-footnote JSpopup dokuwiki';
281 // autoclose on mouseout - ignoring bubbled up events
282 addEvent(fndiv,'mouseout',function(e){
283 if(e.target != fndiv){
287 // check if the element was really left
288 if(e.pageX){ // Mozilla
289 var bx1 = findPosX(fndiv);
290 var bx2 = bx1 + fndiv.offsetWidth;
291 var by1 = findPosY(fndiv);
292 var by2 = by1 + fndiv.offsetHeight;
295 if(x > bx1 && x < bx2 && y > by1 && y < by2){
296 // we're still inside boundaries
301 if(e.offsetX > 0 && e.offsetX < fndiv.offsetWidth-1 &&
302 e.offsetY > 0 && e.offsetY < fndiv.offsetHeight-1){
303 // we're still inside boundaries
309 fndiv.style.display='none';
311 document.body.appendChild(fndiv);
314 // locate the footnote anchor element
315 var a = $( "fn__"+id );
318 // anchor parent is the footnote container, get its innerHTML
319 var content = new String (a.parentNode.parentNode.innerHTML);
321 // strip the leading content anchors and their comma separators
322 content = content.replace(/<sup>.*<\/sup>/gi, '');
323 content = content.replace(/^\s+(,\s+)+/,'');
325 // prefix ids on any elements with "insitu__" to ensure they remain unique
326 content = content.replace(/\bid=\"(.*?)\"/gi,'id="insitu__$1');
328 // now put the content into the wrapper
329 fndiv.innerHTML = content;
331 // position the div and make it visible
333 if(e.pageX){ // Mozilla
340 fndiv.style.position = 'absolute';
341 fndiv.style.left = (x+2)+'px';
342 fndiv.style.top = (y+2)+'px';
343 fndiv.style.display = '';
347 * Add the event handlers to footnotes
349 * @author Andreas Gohr <andi@splitbrain.org>
351 addInitEvent(function(){
352 var elems = getElementsByClass('fn_top',null,'a');
353 for(var i=0; i<elems.length; i++){
354 addEvent(elems[i],'mouseover',function(e){footnote(e);});
359 * Add the edit window size controls
361 function initSizeCtl(ctlid,edid){
362 if(!document.getElementById){ return; }
365 var textarea = $(edid);
366 if(!ctl || !textarea) return;
368 var hgt = DokuCookie.getValue('sizeCtl');
370 textarea.style.height = hgt;
372 textarea.style.height = '300px';
375 var wrp = DokuCookie.getValue('wrapCtl');
377 setWrap(textarea, wrp);
378 } // else use default value
380 var l = document.createElement('img');
381 var s = document.createElement('img');
382 var w = document.createElement('img');
383 l.src = DOKU_BASE+'lib/images/larger.gif';
384 s.src = DOKU_BASE+'lib/images/smaller.gif';
385 w.src = DOKU_BASE+'lib/images/wrap.gif';
386 addEvent(l,'click',function(){sizeCtl(edid,100);});
387 addEvent(s,'click',function(){sizeCtl(edid,-100);});
388 addEvent(w,'click',function(){toggleWrap(edid);});
395 * This sets the vertical size of the editbox
397 function sizeCtl(edid,val){
398 var textarea = $(edid);
399 var height = parseInt(textarea.style.height.substr(0,textarea.style.height.length-2));
401 textarea.style.height = height+'px';
403 DokuCookie.setValue('sizeCtl',textarea.style.height);
407 * Toggle the wrapping mode of a textarea
409 function toggleWrap(edid){
410 var textarea = $(edid);
411 var wrap = textarea.getAttribute('wrap');
412 if(wrap && wrap.toLowerCase() == 'off'){
413 setWrap(textarea, 'soft');
415 setWrap(textarea, 'off');
418 DokuCookie.setValue('wrapCtl',textarea.getAttribute('wrap'));
422 * Set the wrapping mode of a textarea
424 * @author Fluffy Convict <fluffyconvict@hotmail.com>
425 * @author <shutdown@flashmail.com>
426 * @link http://news.hping.org/comp.lang.javascript.archive/12265.html
427 * @link https://bugzilla.mozilla.org/show_bug.cgi?id=41464
429 function setWrap(textarea, wrapAttrValue){
430 textarea.setAttribute('wrap', wrapAttrValue);
432 // Fix display for mozilla
433 var parNod = textarea.parentNode;
434 var nxtSib = textarea.nextSibling;
435 parNod.removeChild(textarea);
436 parNod.insertBefore(textarea, nxtSib);
440 * Handler to close all open Popups
442 function closePopups(){
443 if(!document.getElementById){ return; }
445 var divs = document.getElementsByTagName('div');
446 for(var i=0; i < divs.length; i++){
447 if(divs[i].className.indexOf('JSpopup') != -1){
448 divs[i].style.display = 'none';
454 * Looks for an element with the ID scroll__here at scrolls to it
456 function scrollToMarker(){
457 var obj = $('scroll__here');
458 if(obj) obj.scrollIntoView();
462 * Looks for an element with the ID focus__this at sets focus to it
464 function focusMarker(){
465 var obj = $('focus__this');
472 function cleanMsgArea(){
473 var elems = getElementsByClass('(success|info|error)',document,'div');
475 for(var i=0; i<elems.length; i++){
476 elems[i].style.display = 'none';
482 * disable multiple revisions checkboxes if two are checked
484 * @author Anika Henke <anika@selfthinker.org>
486 addInitEvent(function(){
487 var revForm = $('page__revisions');
488 if (!revForm) return;
489 var elems = revForm.elements;
491 for (var i=0; i<elems.length; i++) {
492 var input1 = elems[i];
493 if (input1.type=='checkbox') {
494 addEvent(input1,'click',function(e){
495 if (this.checked) countTicks++;
497 for (var j=0; j<elems.length; j++) {
498 var input2 = elems[j];
499 if (countTicks >= 2) input2.disabled = (input2.type=='checkbox' && !input2.checked);
500 else input2.disabled = (input2.type!='checkbox');
503 } else if(input1.type=='submit'){
504 input1.disabled = true;
510 * Add the event handler to the actiondropdown
512 * @author Andreas Gohr <andi@splitbrain.org>
514 addInitEvent(function(){
515 var selector = $('action__selector');
516 if(!selector) return;
518 addEvent(selector,'change',function(e){
522 $('action__selectorbtn').style.display = 'none';
526 * Display error for Windows Shares on browsers other than IE
528 * @author Michael Klier <chi@chimeric.de>
530 function checkWindowsShares() {
531 if(!LANG['nosmblinks']) return true;
532 var elems = getElementsByClass('windows',document,'a');
534 for(var i=0; i<elems.length; i++){
535 var share = elems[i];
536 addEvent(share,'click',function(){
537 if(document.all == null) {
538 alert(LANG['nosmblinks']);
546 * Add the event handler for the Windows Shares check
548 * @author Michael Klier <chi@chimeric.de>
550 addInitEvent(function(){
551 checkWindowsShares();
555 * Highlight the section when hovering over the appropriate section edit button
557 * @author Andreas Gohr <andi@splitbrain.org>
559 addInitEvent(function(){
560 var break_classes = new RegExp('secedit|toc|page');
561 var btns = getElementsByClass('btn_secedit',document,'form');
562 for(var i=0; i<btns.length; i++){
563 addEvent(btns[i],'mouseover',function(e){
565 if(tgt.form) tgt = tgt.form;
566 tgt = tgt.parentNode.previousSibling;
567 if(tgt.nodeName != "DIV") tgt = tgt.previousSibling;
568 while(!break_classes.test(tgt.className)) {
569 tgt.className += ' section_highlight';
570 if (tgt.tagName == 'H1') break;
571 tgt = (tgt.previousSibling != null) ? tgt.previousSibling : tgt.parentNode;
575 addEvent(btns[i],'mouseout',function(e){
576 var secs = getElementsByClass('section_highlight');
577 for(var j=0; j<secs.length; j++){
578 secs[j].className = secs[j].className.replace(/section_highlight/,'');
580 var secs = getElementsByClass('section_highlight');