xref: /dokuwiki/lib/scripts/script.js (revision d4ff305907ac195652cfacfc12a1c52cb8a3f9e0)
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
204    var obj          = document.createElement('span');
205    obj.id           = 'toc__toggle';
206    obj.innerHTML    = '<span>&minus;</span>';
207    obj.className    = 'toc_close';
208    obj.style.cursor = 'pointer';
209
210    prependChild(header,obj);
211    obj.parentNode.onclick = toggleToc;
212    try {
213       obj.parentNode.style.cursor = 'pointer';
214       obj.parentNode.style.cursor = 'hand';
215    }catch(e){}
216}
217
218/**
219 * This toggles the visibility of the Table of Contents
220 */
221function toggleToc() {
222  var toc = $('toc__inside');
223  var obj = $('toc__toggle');
224  if(toc.style.display == 'none') {
225    toc.style.display   = '';
226    obj.innerHTML       = '<span>&minus;</span>';
227    obj.className       = 'toc_close';
228  } else {
229    toc.style.display   = 'none';
230    obj.innerHTML       = '<span>+</span>';
231    obj.className       = 'toc_open';
232  }
233}
234
235/**
236 * This enables/disables checkboxes for acl-administration
237 *
238 * @author Frank Schubert <frank@schokilade.de>
239 */
240function checkAclLevel(){
241  if(document.getElementById) {
242    var scope = $('acl_scope').value;
243
244    //check for namespace
245    if( (scope.indexOf(":*") > 0) || (scope == "*") ){
246      document.getElementsByName('acl_checkbox[4]')[0].disabled=false;
247      document.getElementsByName('acl_checkbox[8]')[0].disabled=false;
248    }else{
249      document.getElementsByName('acl_checkbox[4]')[0].checked=false;
250      document.getElementsByName('acl_checkbox[8]')[0].checked=false;
251
252      document.getElementsByName('acl_checkbox[4]')[0].disabled=true;
253      document.getElementsByName('acl_checkbox[8]')[0].disabled=true;
254    }
255  }
256}
257
258/**
259 * Display an insitu footnote popup
260 *
261 * @author Andreas Gohr <andi@splitbrain.org>
262 * @author Chris Smith <chris@jalakai.co.uk>
263 */
264function footnote(e){
265    var obj = e.target;
266    var id = obj.id.substr(5);
267
268    // get or create the footnote popup div
269    var fndiv = $('insitu__fn');
270    if(!fndiv){
271        fndiv = document.createElement('div');
272        fndiv.id        = 'insitu__fn';
273        fndiv.className = 'insitu-footnote JSpopup dokuwiki';
274
275        // autoclose on mouseout - ignoring bubbled up events
276        addEvent(fndiv,'mouseout',function(e){
277            if(e.target != fndiv){
278                e.stopPropagation();
279                return;
280            }
281            // check if the element was really left
282            if(e.pageX){        // Mozilla
283                var bx1 = findPosX(fndiv);
284                var bx2 = bx1 + fndiv.offsetWidth;
285                var by1 = findPosY(fndiv);
286                var by2 = by1 + fndiv.offsetHeight;
287                var x = e.pageX;
288                var y = e.pageY;
289                if(x > bx1 && x < bx2 && y > by1 && y < by2){
290                    // we're still inside boundaries
291                    e.stopPropagation();
292                    return;
293                }
294            }else{              // IE
295                if(e.offsetX > 0 && e.offsetX < fndiv.offsetWidth-1 &&
296                   e.offsetY > 0 && e.offsetY < fndiv.offsetHeight-1){
297                    // we're still inside boundaries
298                    e.stopPropagation();
299                    return;
300                }
301            }
302            // okay, hide it
303            fndiv.style.display='none';
304        });
305        document.body.appendChild(fndiv);
306    }
307
308    // locate the footnote anchor element
309    var a = $( "fn__"+id );
310    if (!a){ return; }
311
312    // anchor parent is the footnote container, get its innerHTML
313    var content = new String (a.parentNode.parentNode.innerHTML);
314
315    // strip the leading content anchors and their comma separators
316    content = content.replace(/<sup>.*<\/sup>/gi, '');
317    content = content.replace(/^\s+(,\s+)+/,'');
318
319    // prefix ids on any elements with "insitu__" to ensure they remain unique
320    content = content.replace(/\bid=\"(.*?)\"/gi,'id="insitu__$1');
321
322    // now put the content into the wrapper
323    fndiv.innerHTML = content;
324
325    // position the div and make it visible
326    var x; var y;
327    if(e.pageX){        // Mozilla
328        x = e.pageX;
329        y = e.pageY;
330    }else{              // IE
331        x = e.offsetX;
332        y = e.offsetY;
333    }
334    fndiv.style.position = 'absolute';
335    fndiv.style.left = (x+2)+'px';
336    fndiv.style.top  = (y+2)+'px';
337    fndiv.style.display = '';
338}
339
340/**
341 * Add the event handlers to footnotes
342 *
343 * @author Andreas Gohr <andi@splitbrain.org>
344 */
345addInitEvent(function(){
346    var elems = getElementsByClass('fn_top',null,'a');
347    for(var i=0; i<elems.length; i++){
348        addEvent(elems[i],'mouseover',function(e){footnote(e);});
349    }
350});
351
352/**
353 * Add the edit window size controls
354 */
355function initSizeCtl(ctlid,edid){
356    if(!document.getElementById){ return; }
357
358    var ctl      = $(ctlid);
359    var textarea = $(edid);
360    if(!ctl || !textarea) return;
361
362    var hgt = DokuCookie.getValue('sizeCtl');
363    if(hgt){
364      textarea.style.height = hgt;
365    }else{
366      textarea.style.height = '300px';
367    }
368
369    var wrp = DokuCookie.getValue('wrapCtl');
370    if(wrp){
371      setWrap(textarea, wrp);
372    } // else use default value
373
374    var l = document.createElement('img');
375    var s = document.createElement('img');
376    var w = document.createElement('img');
377    l.src = DOKU_BASE+'lib/images/larger.gif';
378    s.src = DOKU_BASE+'lib/images/smaller.gif';
379    w.src = DOKU_BASE+'lib/images/wrap.gif';
380    addEvent(l,'click',function(){sizeCtl(edid,100);});
381    addEvent(s,'click',function(){sizeCtl(edid,-100);});
382    addEvent(w,'click',function(){toggleWrap(edid);});
383    ctl.appendChild(l);
384    ctl.appendChild(s);
385    ctl.appendChild(w);
386}
387
388/**
389 * This sets the vertical size of the editbox
390 */
391function sizeCtl(edid,val){
392  var textarea = $(edid);
393  var height = parseInt(textarea.style.height.substr(0,textarea.style.height.length-2));
394  height += val;
395  textarea.style.height = height+'px';
396
397  DokuCookie.setValue('sizeCtl',textarea.style.height);
398}
399
400/**
401 * Toggle the wrapping mode of a textarea
402 */
403function toggleWrap(edid){
404    var textarea = $(edid);
405    var wrap = textarea.getAttribute('wrap');
406    if(wrap && wrap.toLowerCase() == 'off'){
407        setWrap(textarea, 'soft');
408    }else{
409        setWrap(textarea, 'off');
410    }
411
412    DokuCookie.setValue('wrapCtl',textarea.getAttribute('wrap'));
413}
414
415/**
416 * Set the wrapping mode of a textarea
417 *
418 * @author Fluffy Convict <fluffyconvict@hotmail.com>
419 * @author <shutdown@flashmail.com>
420 * @link   http://news.hping.org/comp.lang.javascript.archive/12265.html
421 * @link   https://bugzilla.mozilla.org/show_bug.cgi?id=41464
422 */
423function setWrap(textarea, wrapAttrValue){
424    textarea.setAttribute('wrap', wrapAttrValue);
425
426    // Fix display for mozilla
427    var parNod = textarea.parentNode;
428    var nxtSib = textarea.nextSibling;
429    parNod.removeChild(textarea);
430    parNod.insertBefore(textarea, nxtSib);
431}
432
433/**
434 * Handler to close all open Popups
435 */
436function closePopups(){
437  if(!document.getElementById){ return; }
438
439  var divs = document.getElementsByTagName('div');
440  for(var i=0; i < divs.length; i++){
441    if(divs[i].className.indexOf('JSpopup') != -1){
442            divs[i].style.display = 'none';
443    }
444  }
445}
446
447/**
448 * Looks for an element with the ID scroll__here at scrolls to it
449 */
450function scrollToMarker(){
451    var obj = $('scroll__here');
452    if(obj) obj.scrollIntoView();
453}
454
455/**
456 * Looks for an element with the ID focus__this at sets focus to it
457 */
458function focusMarker(){
459    var obj = $('focus__this');
460    if(obj) obj.focus();
461}
462
463/**
464 * Remove messages
465 */
466function cleanMsgArea(){
467    var elems = getElementsByClass('(success|info|error)',document,'div');
468    if(elems){
469        for(var i=0; i<elems.length; i++){
470            elems[i].style.display = 'none';
471        }
472    }
473}
474
475/**
476 * disable multiple revisions checkboxes if two are checked
477 *
478 * @author Anika Henke <anika@selfthinker.org>
479 */
480addInitEvent(function(){
481    var revForm = $('page__revisions');
482    if (!revForm) return;
483    var elems = revForm.elements;
484    var countTicks = 0;
485    for (var i=0; i<elems.length; i++) {
486        var input1 = elems[i];
487        if (input1.type=='checkbox') {
488            addEvent(input1,'click',function(e){
489                if (this.checked) countTicks++;
490                else countTicks--;
491                for (var j=0; j<elems.length; j++) {
492                    var input2 = elems[j];
493                    if (countTicks >= 2) input2.disabled = (input2.type=='checkbox' && !input2.checked);
494                    else input2.disabled = (input2.type!='checkbox');
495                }
496            });
497        } else if(input1.type=='submit'){
498            input1.disabled = true;
499        }
500    }
501});
502
503/**
504 * Add the event handler to the actiondropdown
505 *
506 * @author Andreas Gohr <andi@splitbrain.org>
507 */
508addInitEvent(function(){
509    var selector = $('action__selector');
510    if(!selector) return;
511
512    addEvent(selector,'change',function(e){
513        this.form.submit();
514    });
515
516    $('action__selectorbtn').style.display = 'none';
517});
518
519/**
520 * Display error for Windows Shares on browsers other than IE
521 *
522 * @author Michael Klier <chi@chimeric.de>
523 */
524function checkWindowsShares() {
525    if(!LANG['nosmblinks']) return true;
526    var elems = getElementsByClass('windows',document,'a');
527    if(elems){
528        for(var i=0; i<elems.length; i++){
529            var share = elems[i];
530            addEvent(share,'click',function(){
531                if(document.all == null) {
532                    alert(LANG['nosmblinks']);
533                }
534            });
535        }
536    }
537}
538
539/**
540 * Add the event handler for the Windows Shares check
541 *
542 * @author Michael Klier <chi@chimeric.de>
543 */
544addInitEvent(function(){
545    checkWindowsShares();
546});
547
548/**
549 * Highlight the section when hovering over the appropriate section edit button
550 *
551 * @author Andreas Gohr <andi@splitbrain.org>
552 */
553addInitEvent(function(){
554    var btns = getElementsByClass('btn_secedit',document,'form');
555    for(var i=0; i<btns.length; i++){
556        addEvent(btns[i],'mouseover',function(e){
557            var tgt = e.target;
558            if(tgt.form) tgt = tgt.form;
559            tgt = tgt.parentNode.previousSibling;
560            if(tgt.nodeName != "DIV") tgt = tgt.previousSibling;
561            if(tgt.nodeName != "DIV") return;
562            tgt.className += ' section_highlight';
563        });
564
565        addEvent(btns[i],'mouseout',function(e){
566            var secs = getElementsByClass('section_highlight',document,'div');
567            for(var j=0; j<secs.length; j++){
568                secs[j].className = secs[j].className.replace(/ section_highlight/,'');
569            }
570        });
571    }
572});
573