1/** 2 * Some of these scripts were taken from wikipedia.org and were modified for DokuWiki 3 */ 4 5/** 6 * Some browser detection 7 */ 8var clientPC = navigator.userAgent.toLowerCase(); // Get client info 9var is_macos = navigator.appVersion.indexOf('Mac') != -1; 10var is_gecko = ((clientPC.indexOf('gecko')!=-1) && (clientPC.indexOf('spoofer')==-1) && 11 (clientPC.indexOf('khtml') == -1) && (clientPC.indexOf('netscape/7.0')==-1)); 12var is_safari = ((clientPC.indexOf('applewebkit')!=-1) && (clientPC.indexOf('spoofer')==-1)); 13var is_khtml = (navigator.vendor == 'KDE' || ( document.childNodes && !document.all && !navigator.taintEnabled )); 14if (clientPC.indexOf('opera')!=-1) { 15 var is_opera = true; 16 var is_opera_preseven = (window.opera && !document.childNodes); 17 var is_opera_seven = (window.opera && document.childNodes); 18} 19 20/** 21 * Handy shortcut to document.getElementById 22 * 23 * This function was taken from the prototype library 24 * 25 * @link http://prototype.conio.net/ 26 */ 27function $() { 28 var elements = new Array(); 29 30 for (var i = 0; i < arguments.length; i++) { 31 var element = arguments[i]; 32 if (typeof element == 'string') 33 element = document.getElementById(element); 34 35 if (arguments.length == 1) 36 return element; 37 38 elements.push(element); 39 } 40 41 return elements; 42} 43 44/** 45 * Simple function to check if a global var is defined 46 * 47 * @author Kae Verens 48 * @link http://verens.com/archives/2005/07/25/isset-for-javascript/#comment-2835 49 */ 50function isset(varname){ 51 return(typeof(window[varname])!='undefined'); 52} 53 54/** 55 * Select elements by their class name 56 * 57 * @author Dustin Diaz <dustin [at] dustindiaz [dot] com> 58 * @link http://www.dustindiaz.com/getelementsbyclass/ 59 */ 60function getElementsByClass(searchClass,node,tag) { 61 var classElements = new Array(); 62 if ( node == null ) 63 node = document; 64 if ( tag == null ) 65 tag = '*'; 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]; 72 j++; 73 } 74 } 75 return classElements; 76} 77 78/** 79 * Get the X offset of the top left corner of the given object 80 * 81 * @link http://www.quirksmode.org/js/findpos.html 82 */ 83function findPosX(object){ 84 var curleft = 0; 85 var obj = $(object); 86 if (obj.offsetParent){ 87 do { 88 curleft += obj.offsetLeft; 89 } while (obj = obj.offsetParent); 90 } 91 else if (obj.x){ 92 curleft += obj.x; 93 } 94 return curleft; 95} //end findPosX function 96 97/** 98 * Get the Y offset of the top left corner of the given object 99 * 100 * @link http://www.quirksmode.org/js/findpos.html 101 */ 102function findPosY(object){ 103 var curtop = 0; 104 var obj = $(object); 105 if (obj.offsetParent){ 106 do { 107 curtop += obj.offsetTop; 108 } while (obj = obj.offsetParent); 109 } 110 else if (obj.y){ 111 curtop += obj.y; 112 } 113 return curtop; 114} //end findPosY function 115 116/** 117 * Get the computed style of a node. 118 * 119 * @link https://acidmartin.wordpress.com/2008/08/26/style-get-any-css-property-value-of-an-object/ 120 * @link http://svn.dojotoolkit.org/src/dojo/trunk/_base/html.js 121 */ 122function gcs(node){ 123 if(node.currentStyle){ 124 return node.currentStyle; 125 }else{ 126 return node.ownerDocument.defaultView.getComputedStyle(node, null); 127 } 128} 129 130/** 131 * Escape special chars in JavaScript 132 * 133 * @author Andreas Gohr <andi@splitbrain.org> 134 */ 135function jsEscape(text){ 136 var re=new RegExp("\\\\","g"); 137 text=text.replace(re,"\\\\"); 138 re=new RegExp("'","g"); 139 text=text.replace(re,"\\'"); 140 re=new RegExp('"',"g"); 141 text=text.replace(re,'"'); 142 re=new RegExp("\\\\\\\\n","g"); 143 text=text.replace(re,"\\n"); 144 return text; 145} 146 147/** 148 * This function escapes some special chars 149 * @deprecated by above function 150 */ 151function escapeQuotes(text) { 152 var re=new RegExp("'","g"); 153 text=text.replace(re,"\\'"); 154 re=new RegExp('"',"g"); 155 text=text.replace(re,'"'); 156 re=new RegExp("\\n","g"); 157 text=text.replace(re,"\\n"); 158 return text; 159} 160 161/** 162 * Adds a node as the first childenode to the given parent 163 * 164 * @see appendChild() 165 */ 166function prependChild(parent,element) { 167 if(!parent.firstChild){ 168 parent.appendChild(element); 169 }else{ 170 parent.insertBefore(element,parent.firstChild); 171 } 172} 173 174/** 175 * Prints a animated gif to show the search is performed 176 * 177 * Because we need to modify the DOM here before the document is loaded 178 * and parsed completely we have to rely on document.write() 179 * 180 * @author Andreas Gohr <andi@splitbrain.org> 181 */ 182function showLoadBar(){ 183 184 document.write('<img src="'+DOKU_BASE+'lib/images/loading.gif" '+ 185 'width="150" height="12" alt="..." />'); 186 187 /* this does not work reliable in IE 188 obj = $(id); 189 190 if(obj){ 191 obj.innerHTML = '<img src="'+DOKU_BASE+'lib/images/loading.gif" '+ 192 'width="150" height="12" alt="..." />'; 193 obj.style.display="block"; 194 } 195 */ 196} 197 198/** 199 * Disables the animated gif to show the search is done 200 * 201 * @author Andreas Gohr <andi@splitbrain.org> 202 */ 203function hideLoadBar(id){ 204 obj = $(id); 205 if(obj) obj.style.display="none"; 206} 207 208/** 209 * Adds the toggle switch to the TOC 210 */ 211function addTocToggle() { 212 if(!document.getElementById) return; 213 var header = $('toc__header'); 214 if(!header) return; 215 var toc = $('toc__inside'); 216 217 var obj = document.createElement('span'); 218 obj.id = 'toc__toggle'; 219 obj.style.cursor = 'pointer'; 220 if (toc && toc.style.display == 'none') { 221 obj.innerHTML = '<span>+</span>'; 222 obj.className = 'toc_open'; 223 } else { 224 obj.innerHTML = '<span>−</span>'; 225 obj.className = 'toc_close'; 226 } 227 228 prependChild(header,obj); 229 obj.parentNode.onclick = toggleToc; 230 obj.parentNode.style.cursor = 'pointer'; 231} 232 233/** 234 * This toggles the visibility of the Table of Contents 235 */ 236function toggleToc() { 237 var toc = $('toc__inside'); 238 var obj = $('toc__toggle'); 239 if(toc.style.display == 'none') { 240 toc.style.display = ''; 241 obj.innerHTML = '<span>−</span>'; 242 obj.className = 'toc_close'; 243 } else { 244 toc.style.display = 'none'; 245 obj.innerHTML = '<span>+</span>'; 246 obj.className = 'toc_open'; 247 } 248} 249 250/** 251 * Create JavaScript mouseover popup 252 */ 253function insitu_popup(target, popup_id) { 254 255 // get or create the popup div 256 var fndiv = $(popup_id); 257 if(!fndiv){ 258 fndiv = document.createElement('div'); 259 fndiv.id = popup_id; 260 fndiv.className = 'insitu-footnote JSpopup dokuwiki'; 261 262 // autoclose on mouseout - ignoring bubbled up events 263 addEvent(fndiv,'mouseout',function(e){ 264 var p = e.relatedTarget || e.toElement; 265 while (p && p !== this) { 266 p = p.parentNode; 267 } 268 if (p === this) { 269 return; 270 } 271 // okay, hide it 272 this.style.display='none'; 273 }); 274 getElementsByClass('dokuwiki', document.body, 'div')[0].appendChild(fndiv); 275 } 276 277 var non_static_parent = fndiv.parentNode; 278 while (non_static_parent != document && gcs(non_static_parent)['position'] == 'static') { 279 non_static_parent = non_static_parent.parentNode; 280 } 281 282 var fixed_target_parent = target; 283 while (fixed_target_parent != document && gcs(fixed_target_parent)['position'] != 'fixed') { 284 fixed_target_parent = fixed_target_parent.parentNode; 285 } 286 287 // position the div and make it visible 288 if (fixed_target_parent != document) { 289 // the target has position fixed, that means the footnote needs to be fixed, too 290 fndiv.style.position = 'fixed'; 291 } else { 292 fndiv.style.position = 'absolute'; 293 } 294 295 if (fixed_target_parent != document || non_static_parent == document) { 296 fndiv.style.left = findPosX(target)+'px'; 297 fndiv.style.top = (findPosY(target)+target.offsetHeight * 1.5) + 'px'; 298 } else { 299 fndiv.style.left = (findPosX(target) - findPosX(non_static_parent)) +'px'; 300 fndiv.style.top = (findPosY(target)+target.offsetHeight * 1.5 - findPosY(non_static_parent)) + 'px'; 301 } 302 303 fndiv.style.display = ''; 304 return fndiv; 305} 306 307/** 308 * Display an insitu footnote popup 309 * 310 * @author Andreas Gohr <andi@splitbrain.org> 311 * @author Chris Smith <chris@jalakai.co.uk> 312 */ 313function footnote(e){ 314 var fndiv = insitu_popup(e.target, 'insitu__fn'); 315 316 // locate the footnote anchor element 317 var a = $("fn__" + e.target.id.substr(5)); 318 if (!a){ return; } 319 320 // anchor parent is the footnote container, get its innerHTML 321 var content = new String (a.parentNode.parentNode.innerHTML); 322 323 // strip the leading content anchors and their comma separators 324 content = content.replace(/<sup>.*<\/sup>/gi, ''); 325 content = content.replace(/^\s+(,\s+)+/,''); 326 327 // prefix ids on any elements with "insitu__" to ensure they remain unique 328 content = content.replace(/\bid=(['"])([^"']+)\1/gi,'id="insitu__$2'); 329 330 // now put the content into the wrapper 331 fndiv.innerHTML = content; 332} 333 334/** 335 * Add the event handlers to footnotes 336 * 337 * @author Andreas Gohr <andi@splitbrain.org> 338 */ 339addInitEvent(function(){ 340 var elems = getElementsByClass('fn_top',null,'a'); 341 for(var i=0; i<elems.length; i++){ 342 addEvent(elems[i],'mouseover',function(e){footnote(e);}); 343 } 344}); 345 346/** 347 * Add the edit window size controls 348 */ 349function initSizeCtl(ctlid,edid){ 350 if(!document.getElementById){ return; } 351 352 var ctl = $(ctlid); 353 var textarea = $(edid); 354 if(!ctl || !textarea) return; 355 356 var hgt = DokuCookie.getValue('sizeCtl'); 357 if(hgt){ 358 textarea.style.height = hgt; 359 }else{ 360 textarea.style.height = '300px'; 361 } 362 363 var wrp = DokuCookie.getValue('wrapCtl'); 364 if(wrp){ 365 setWrap(textarea, wrp); 366 } // else use default value 367 368 var l = document.createElement('img'); 369 var s = document.createElement('img'); 370 var w = document.createElement('img'); 371 l.src = DOKU_BASE+'lib/images/larger.gif'; 372 s.src = DOKU_BASE+'lib/images/smaller.gif'; 373 w.src = DOKU_BASE+'lib/images/wrap.gif'; 374 addEvent(l,'click',function(){sizeCtl(edid,100);}); 375 addEvent(s,'click',function(){sizeCtl(edid,-100);}); 376 addEvent(w,'click',function(){toggleWrap(edid);}); 377 ctl.appendChild(l); 378 ctl.appendChild(s); 379 ctl.appendChild(w); 380} 381 382/** 383 * This sets the vertical size of the editbox 384 */ 385function sizeCtl(edid,val){ 386 var textarea = $(edid); 387 var height = parseInt(textarea.style.height.substr(0,textarea.style.height.length-2)); 388 height += val; 389 textarea.style.height = height+'px'; 390 391 DokuCookie.setValue('sizeCtl',textarea.style.height); 392} 393 394/** 395 * Toggle the wrapping mode of a textarea 396 */ 397function toggleWrap(edid){ 398 var textarea = $(edid); 399 var wrap = textarea.getAttribute('wrap'); 400 if(wrap && wrap.toLowerCase() == 'off'){ 401 setWrap(textarea, 'soft'); 402 }else{ 403 setWrap(textarea, 'off'); 404 } 405 406 DokuCookie.setValue('wrapCtl',textarea.getAttribute('wrap')); 407} 408 409/** 410 * Set the wrapping mode of a textarea 411 * 412 * @author Fluffy Convict <fluffyconvict@hotmail.com> 413 * @author <shutdown@flashmail.com> 414 * @link http://news.hping.org/comp.lang.javascript.archive/12265.html 415 * @link https://bugzilla.mozilla.org/show_bug.cgi?id=41464 416 */ 417function setWrap(textarea, wrapAttrValue){ 418 textarea.setAttribute('wrap', wrapAttrValue); 419 420 // Fix display for mozilla 421 var parNod = textarea.parentNode; 422 var nxtSib = textarea.nextSibling; 423 parNod.removeChild(textarea); 424 parNod.insertBefore(textarea, nxtSib); 425} 426 427/** 428 * Handler to close all open Popups 429 */ 430function closePopups(){ 431 if(!document.getElementById){ return; } 432 433 var divs = document.getElementsByTagName('div'); 434 for(var i=0; i < divs.length; i++){ 435 if(divs[i].className.indexOf('JSpopup') != -1){ 436 divs[i].style.display = 'none'; 437 } 438 } 439} 440 441/** 442 * Looks for an element with the ID scroll__here at scrolls to it 443 */ 444function scrollToMarker(){ 445 var obj = $('scroll__here'); 446 if(obj) obj.scrollIntoView(); 447} 448 449/** 450 * Looks for an element with the ID focus__this at sets focus to it 451 */ 452function focusMarker(){ 453 var obj = $('focus__this'); 454 if(obj) obj.focus(); 455} 456 457/** 458 * Remove messages 459 */ 460function cleanMsgArea(){ 461 var elems = getElementsByClass('(success|info|error)',document,'div'); 462 if(elems){ 463 for(var i=0; i<elems.length; i++){ 464 elems[i].style.display = 'none'; 465 } 466 } 467} 468 469/** 470 * disable multiple revisions checkboxes if two are checked 471 * 472 * @author Anika Henke <anika@selfthinker.org> 473 */ 474addInitEvent(function(){ 475 var revForm = $('page__revisions'); 476 if (!revForm) return; 477 var elems = revForm.elements; 478 var countTicks = 0; 479 for (var i=0; i<elems.length; i++) { 480 var input1 = elems[i]; 481 if (input1.type=='checkbox') { 482 addEvent(input1,'click',function(e){ 483 if (this.checked) countTicks++; 484 else countTicks--; 485 for (var j=0; j<elems.length; j++) { 486 var input2 = elems[j]; 487 if (countTicks >= 2) input2.disabled = (input2.type=='checkbox' && !input2.checked); 488 else input2.disabled = (input2.type!='checkbox'); 489 } 490 }); 491 input1.checked = false; // chrome reselects on back button which messes up the logic 492 } else if(input1.type=='submit'){ 493 input1.disabled = true; 494 } 495 } 496}); 497 498/** 499 * Autosubmit quick select forms 500 * 501 * When a <select> tag has the class "quickselect", this script will 502 * automatically submit its parent form when the select value changes. 503 * It also hides the submit button of the form. 504 * 505 * @author Andreas Gohr <andi@splitbrain.org> 506 */ 507addInitEvent(function(){ 508 var selects = getElementsByClass('quickselect',document,'select'); 509 for(var i=0; i<selects.length; i++){ 510 // auto submit on change 511 addEvent(selects[i],'change',function(e){ 512 this.form.submit(); 513 }); 514 // hide submit buttons 515 var btns = selects[i].form.getElementsByTagName('input'); 516 for(var j=0; j<btns.length; j++){ 517 if(btns[j].type == 'submit'){ 518 btns[j].style.display = 'none'; 519 } 520 } 521 } 522}); 523 524/** 525 * Display error for Windows Shares on browsers other than IE 526 * 527 * @author Michael Klier <chi@chimeric.de> 528 */ 529function checkWindowsShares() { 530 if(!LANG['nosmblinks']) return true; 531 if(document.all != null) return true; 532 533 var elems = getElementsByClass('windows',document,'a'); 534 if(elems){ 535 for(var i=0; i<elems.length; i++){ 536 var share = elems[i]; 537 addEvent(share,'click',function(){ 538 alert(LANG['nosmblinks']); 539 }); 540 } 541 } 542} 543 544/** 545 * Add the event handler for the Windows Shares check 546 * 547 * @author Michael Klier <chi@chimeric.de> 548 */ 549addInitEvent(function(){ 550 checkWindowsShares(); 551}); 552 553/** 554 * Highlight the section when hovering over the appropriate section edit button 555 * 556 * @author Andreas Gohr <andi@splitbrain.org> 557 */ 558addInitEvent(function(){ 559 var btns = getElementsByClass('btn_secedit',document,'form'); 560 for(var i=0; i<btns.length; i++){ 561 addEvent(btns[i],'mouseover',function(e){ 562 var tgt = this.parentNode; 563 var nr = tgt.className.match(/(\s+|^)editbutton_(\d+)(\s+|$)/)[2]; 564 do { 565 tgt = tgt.previousSibling; 566 } while (tgt !== null && typeof tgt.tagName === 'undefined'); 567 if (tgt === null) return; 568 while(typeof tgt.className === 'undefined' || 569 tgt.className.match('(\\s+|^)sectionedit' + nr + '(\\s+|$)') === null) { 570 if (typeof tgt.className !== 'undefined') { 571 tgt.className += ' section_highlight'; 572 } 573 tgt = (tgt.previousSibling !== null) ? tgt.previousSibling : tgt.parentNode; 574 } 575 if (typeof tgt.className !== 'undefined') tgt.className += ' section_highlight'; 576 }); 577 578 addEvent(btns[i],'mouseout',function(e){ 579 var secs = getElementsByClass('section_highlight'); 580 for(var j=0; j<secs.length; j++){ 581 secs[j].className = secs[j].className.replace(/section_highlight/g,''); 582 } 583 }); 584 } 585}); 586 587