xref: /dokuwiki/lib/scripts/script.js (revision ea4a69def51a12c7aaf1d7ffbb513a3d5cb78ef0)
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/index.html?/js/findpos.html
82 */
83function findPosX(object){
84  var curleft = 0;
85  var obj = $(object);
86  if (obj.offsetParent){
87    while (obj.offsetParent){
88      curleft += obj.offsetLeft;
89      obj = obj.offsetParent;
90    }
91  }
92  else if (obj.x){
93    curleft += obj.x;
94  }
95  return curleft;
96} //end findPosX function
97
98/**
99 * Get the Y offset of the top left corner of the given object
100 *
101 * @link http://www.quirksmode.org/index.html?/js/findpos.html
102 */
103function findPosY(object){
104  var curtop = 0;
105  var obj = $(object);
106  if (obj.offsetParent){
107    while (obj.offsetParent){
108      curtop += obj.offsetTop;
109      obj = obj.offsetParent;
110    }
111  }
112  else if (obj.y){
113    curtop += obj.y;
114  }
115  return curtop;
116} //end findPosY function
117
118/**
119 * Escape special chars in JavaScript
120 *
121 * @author Andreas Gohr <andi@splitbrain.org>
122 */
123function 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,'&quot;');
130    re=new RegExp("\\\\\\\\n","g");
131    text=text.replace(re,"\\n");
132    return text;
133}
134
135/**
136 * This function escapes some special chars
137 * @deprecated by above function
138 */
139function escapeQuotes(text) {
140  var re=new RegExp("'","g");
141  text=text.replace(re,"\\'");
142  re=new RegExp('"',"g");
143  text=text.replace(re,'&quot;');
144  re=new RegExp("\\n","g");
145  text=text.replace(re,"\\n");
146  return text;
147}
148
149/**
150 * Adds a node as the first childenode to the given parent
151 *
152 * @see appendChild()
153 */
154function prependChild(parent,element) {
155    if(!parent.firstChild){
156        parent.appendChild(element);
157    }else{
158        parent.insertBefore(element,parent.firstChild);
159    }
160}
161
162/**
163 * Prints a animated gif to show the search is performed
164 *
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()
167 *
168 * @author Andreas Gohr <andi@splitbrain.org>
169 */
170function showLoadBar(){
171
172  document.write('<img src="'+DOKU_BASE+'lib/images/loading.gif" '+
173                 'width="150" height="12" alt="..." />');
174
175  /* this does not work reliable in IE
176  obj = $(id);
177
178  if(obj){
179    obj.innerHTML = '<img src="'+DOKU_BASE+'lib/images/loading.gif" '+
180                    'width="150" height="12" alt="..." />';
181    obj.style.display="block";
182  }
183  */
184}
185
186/**
187 * Disables the animated gif to show the search is done
188 *
189 * @author Andreas Gohr <andi@splitbrain.org>
190 */
191function hideLoadBar(id){
192  obj = $(id);
193  if(obj) obj.style.display="none";
194}
195
196/**
197 * Adds the toggle switch to the TOC
198 */
199function addTocToggle() {
200    if(!document.getElementById) return;
201    var header = $('toc__header');
202    if(!header) return;
203    var toc = $('toc__inside');
204
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';
211    } else {
212	obj.innerHTML    = '<span>&minus;</span>';
213	obj.className    = 'toc_close';
214    }
215
216    prependChild(header,obj);
217    obj.parentNode.onclick = toggleToc;
218    try {
219       obj.parentNode.style.cursor = 'pointer';
220       obj.parentNode.style.cursor = 'hand';
221    }catch(e){}
222}
223
224/**
225 * This toggles the visibility of the Table of Contents
226 */
227function 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>&minus;</span>';
233    obj.className       = 'toc_close';
234  } else {
235    toc.style.display   = 'none';
236    obj.innerHTML       = '<span>+</span>';
237    obj.className       = 'toc_open';
238  }
239}
240
241/**
242 * This enables/disables checkboxes for acl-administration
243 *
244 * @author Frank Schubert <frank@schokilade.de>
245 */
246function checkAclLevel(){
247  if(document.getElementById) {
248    var scope = $('acl_scope').value;
249
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;
254    }else{
255      document.getElementsByName('acl_checkbox[4]')[0].checked=false;
256      document.getElementsByName('acl_checkbox[8]')[0].checked=false;
257
258      document.getElementsByName('acl_checkbox[4]')[0].disabled=true;
259      document.getElementsByName('acl_checkbox[8]')[0].disabled=true;
260    }
261  }
262}
263
264/**
265 * Display an insitu footnote popup
266 *
267 * @author Andreas Gohr <andi@splitbrain.org>
268 * @author Chris Smith <chris@jalakai.co.uk>
269 */
270function footnote(e){
271    var obj = e.target;
272    var id = obj.id.substr(5);
273
274    // get or create the footnote popup div
275    var fndiv = $('insitu__fn');
276    if(!fndiv){
277        fndiv = document.createElement('div');
278        fndiv.id        = 'insitu__fn';
279        fndiv.className = 'insitu-footnote JSpopup dokuwiki';
280
281        // autoclose on mouseout - ignoring bubbled up events
282        addEvent(fndiv,'mouseout',function(e){
283            if(e.target != fndiv){
284                e.stopPropagation();
285                return;
286            }
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;
293                var x = e.pageX;
294                var y = e.pageY;
295                if(x > bx1 && x < bx2 && y > by1 && y < by2){
296                    // we're still inside boundaries
297                    e.stopPropagation();
298                    return;
299                }
300            }else{              // IE
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
304                    e.stopPropagation();
305                    return;
306                }
307            }
308            // okay, hide it
309            fndiv.style.display='none';
310        });
311        document.body.appendChild(fndiv);
312    }
313
314    // locate the footnote anchor element
315    var a = $( "fn__"+id );
316    if (!a){ return; }
317
318    // anchor parent is the footnote container, get its innerHTML
319    var content = new String (a.parentNode.parentNode.innerHTML);
320
321    // strip the leading content anchors and their comma separators
322    content = content.replace(/<sup>.*<\/sup>/gi, '');
323    content = content.replace(/^\s+(,\s+)+/,'');
324
325    // prefix ids on any elements with "insitu__" to ensure they remain unique
326    content = content.replace(/\bid=\"(.*?)\"/gi,'id="insitu__$1');
327
328    // now put the content into the wrapper
329    fndiv.innerHTML = content;
330
331    // position the div and make it visible
332    var x; var y;
333    if(e.pageX){        // Mozilla
334        x = e.pageX;
335        y = e.pageY;
336    }else{              // IE
337        x = e.offsetX;
338        y = e.offsetY;
339    }
340    fndiv.style.position = 'absolute';
341    fndiv.style.left = (x+2)+'px';
342    fndiv.style.top  = (y+2)+'px';
343    fndiv.style.display = '';
344}
345
346/**
347 * Add the event handlers to footnotes
348 *
349 * @author Andreas Gohr <andi@splitbrain.org>
350 */
351addInitEvent(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);});
355    }
356});
357
358/**
359 * Add the edit window size controls
360 */
361function initSizeCtl(ctlid,edid){
362    if(!document.getElementById){ return; }
363
364    var ctl      = $(ctlid);
365    var textarea = $(edid);
366    if(!ctl || !textarea) return;
367
368    var hgt = DokuCookie.getValue('sizeCtl');
369    if(hgt){
370      textarea.style.height = hgt;
371    }else{
372      textarea.style.height = '300px';
373    }
374
375    var wrp = DokuCookie.getValue('wrapCtl');
376    if(wrp){
377      setWrap(textarea, wrp);
378    } // else use default value
379
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);});
389    ctl.appendChild(l);
390    ctl.appendChild(s);
391    ctl.appendChild(w);
392}
393
394/**
395 * This sets the vertical size of the editbox
396 */
397function sizeCtl(edid,val){
398  var textarea = $(edid);
399  var height = parseInt(textarea.style.height.substr(0,textarea.style.height.length-2));
400  height += val;
401  textarea.style.height = height+'px';
402
403  DokuCookie.setValue('sizeCtl',textarea.style.height);
404}
405
406/**
407 * Toggle the wrapping mode of a textarea
408 */
409function toggleWrap(edid){
410    var textarea = $(edid);
411    var wrap = textarea.getAttribute('wrap');
412    if(wrap && wrap.toLowerCase() == 'off'){
413        setWrap(textarea, 'soft');
414    }else{
415        setWrap(textarea, 'off');
416    }
417
418    DokuCookie.setValue('wrapCtl',textarea.getAttribute('wrap'));
419}
420
421/**
422 * Set the wrapping mode of a textarea
423 *
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
428 */
429function setWrap(textarea, wrapAttrValue){
430    textarea.setAttribute('wrap', wrapAttrValue);
431
432    // Fix display for mozilla
433    var parNod = textarea.parentNode;
434    var nxtSib = textarea.nextSibling;
435    parNod.removeChild(textarea);
436    parNod.insertBefore(textarea, nxtSib);
437}
438
439/**
440 * Handler to close all open Popups
441 */
442function closePopups(){
443  if(!document.getElementById){ return; }
444
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';
449    }
450  }
451}
452
453/**
454 * Looks for an element with the ID scroll__here at scrolls to it
455 */
456function scrollToMarker(){
457    var obj = $('scroll__here');
458    if(obj) obj.scrollIntoView();
459}
460
461/**
462 * Looks for an element with the ID focus__this at sets focus to it
463 */
464function focusMarker(){
465    var obj = $('focus__this');
466    if(obj) obj.focus();
467}
468
469/**
470 * Remove messages
471 */
472function cleanMsgArea(){
473    var elems = getElementsByClass('(success|info|error)',document,'div');
474    if(elems){
475        for(var i=0; i<elems.length; i++){
476            elems[i].style.display = 'none';
477        }
478    }
479}
480
481/**
482 * disable multiple revisions checkboxes if two are checked
483 *
484 * @author Anika Henke <anika@selfthinker.org>
485 */
486addInitEvent(function(){
487    var revForm = $('page__revisions');
488    if (!revForm) return;
489    var elems = revForm.elements;
490    var countTicks = 0;
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++;
496                else 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');
501                }
502            });
503        } else if(input1.type=='submit'){
504            input1.disabled = true;
505        }
506    }
507});
508
509/**
510 * Add the event handler to the actiondropdown
511 *
512 * @author Andreas Gohr <andi@splitbrain.org>
513 */
514addInitEvent(function(){
515    var selector = $('action__selector');
516    if(!selector) return;
517
518    addEvent(selector,'change',function(e){
519        this.form.submit();
520    });
521
522    $('action__selectorbtn').style.display = 'none';
523});
524
525/**
526 * Display error for Windows Shares on browsers other than IE
527 *
528 * @author Michael Klier <chi@chimeric.de>
529 */
530function checkWindowsShares() {
531    if(!LANG['nosmblinks']) return true;
532    var elems = getElementsByClass('windows',document,'a');
533    if(elems){
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']);
539                }
540            });
541        }
542    }
543}
544
545/**
546 * Add the event handler for the Windows Shares check
547 *
548 * @author Michael Klier <chi@chimeric.de>
549 */
550addInitEvent(function(){
551    checkWindowsShares();
552});
553
554/**
555 * Highlight the section when hovering over the appropriate section edit button
556 *
557 * @author Andreas Gohr <andi@splitbrain.org>
558 */
559addInitEvent(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){
564            var tgt = e.target;
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;
572            }
573        });
574
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/,'');
579            }
580            var secs = getElementsByClass('section_highlight');
581        });
582    }
583});
584