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