xref: /dokuwiki/lib/scripts/script.js (revision 886f80e7a9d4651ee3524837f0f5e430687ba6a5)
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_gecko  = ((clientPC.indexOf('gecko')!=-1) && (clientPC.indexOf('spoofer')==-1) &&
10                (clientPC.indexOf('khtml') == -1) && (clientPC.indexOf('netscape/7.0')==-1));
11var is_safari = ((clientPC.indexOf('AppleWebKit')!=-1) && (clientPC.indexOf('spoofer')==-1));
12var is_khtml  = (navigator.vendor == 'KDE' || ( document.childNodes && !document.all && !navigator.taintEnabled ));
13if (clientPC.indexOf('opera')!=-1) {
14    var is_opera = true;
15    var is_opera_preseven = (window.opera && !document.childNodes);
16    var is_opera_seven = (window.opera && document.childNodes);
17}
18
19/**
20 * Rewrite the accesskey tooltips to be more browser and OS specific.
21 *
22 * Accesskey tooltips are still only a best-guess of what will work
23 * on well known systems.
24 *
25 * @author Ben Coburn <btcoburn@silicodon.net>
26 */
27function updateAccessKeyTooltip() {
28  // determin tooltip text (order matters)
29  var tip = 'ALT+'; //default
30  if (domLib_isMac) { tip = 'CTRL+'; }
31  if (domLib_isOpera) { tip = 'SHIFT+ESC '; }
32  // add other cases here...
33
34  // do tooltip update
35  if (tip=='ALT+') { return; }
36  var exp = /\[ALT\+/i;
37  var rep = '['+tip;
38  var elements = domLib_getElementsByTagNames(['a', 'input', 'button']);
39  for (var i=0; i<elements.length; i++) {
40    if (elements[i].accessKey.length==1 && elements[i].title.length>0) {
41      elements[i].title = elements[i].title.replace(exp, rep);
42    }
43  }
44}
45
46/**
47 * Handy shortcut to document.getElementById
48 *
49 * This function was taken from the prototype library
50 *
51 * @link http://prototype.conio.net/
52 */
53function $() {
54  var elements = new Array();
55
56  for (var i = 0; i < arguments.length; i++) {
57    var element = arguments[i];
58    if (typeof element == 'string')
59      element = document.getElementById(element);
60
61    if (arguments.length == 1)
62      return element;
63
64    elements.push(element);
65  }
66
67  return elements;
68}
69
70/**
71 * Simple function to check if a global var is defined
72 *
73 * @author Kae Verens
74 * @link http://verens.com/archives/2005/07/25/isset-for-javascript/#comment-2835
75 */
76function isset(varname){
77  return(typeof(window[varname])!='undefined');
78}
79
80/**
81 * Select elements by their class name
82 *
83 * @author Dustin Diaz <dustin [at] dustindiaz [dot] com>
84 * @link   http://www.dustindiaz.com/getelementsbyclass/
85 */
86function getElementsByClass(searchClass,node,tag) {
87    var classElements = new Array();
88    if ( node == null )
89        node = document;
90    if ( tag == null )
91        tag = '*';
92    var els = node.getElementsByTagName(tag);
93    var elsLen = els.length;
94    var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
95    for (i = 0, j = 0; i < elsLen; i++) {
96        if ( pattern.test(els[i].className) ) {
97            classElements[j] = els[i];
98            j++;
99        }
100    }
101    return classElements;
102}
103
104/**
105 * Get the X offset of the top left corner of the given object
106 *
107 * @link http://www.quirksmode.org/index.html?/js/findpos.html
108 */
109function findPosX(object){
110  var curleft = 0;
111  var obj = $(object);
112  if (obj.offsetParent){
113    while (obj.offsetParent){
114      curleft += obj.offsetLeft;
115      obj = obj.offsetParent;
116    }
117  }
118  else if (obj.x){
119    curleft += obj.x;
120  }
121  return curleft;
122} //end findPosX function
123
124/**
125 * Get the Y offset of the top left corner of the given object
126 *
127 * @link http://www.quirksmode.org/index.html?/js/findpos.html
128 */
129function findPosY(object){
130  var curtop = 0;
131  var obj = $(object);
132  if (obj.offsetParent){
133    while (obj.offsetParent){
134      curtop += obj.offsetTop;
135      obj = obj.offsetParent;
136    }
137  }
138  else if (obj.y){
139    curtop += obj.y;
140  }
141  return curtop;
142} //end findPosY function
143
144/**
145 * Escape special chars in JavaScript
146 *
147 * @author Andreas Gohr <andi@splitbrain.org>
148 */
149function jsEscape(text){
150    var re=new RegExp("\\\\","g");
151    text=text.replace(re,"\\\\");
152    re=new RegExp("'","g");
153    text=text.replace(re,"\\'");
154    re=new RegExp('"',"g");
155    text=text.replace(re,'&quot;');
156    re=new RegExp("\\\\\\\\n","g");
157    text=text.replace(re,"\\n");
158    return text;
159}
160
161/**
162 * This function escapes some special chars
163 * @deprecated by above function
164 */
165function escapeQuotes(text) {
166  var re=new RegExp("'","g");
167  text=text.replace(re,"\\'");
168  re=new RegExp('"',"g");
169  text=text.replace(re,'&quot;');
170  re=new RegExp("\\n","g");
171  text=text.replace(re,"\\n");
172  return text;
173}
174
175/**
176 * Adds a node as the first childenode to the given parent
177 *
178 * @see appendChild()
179 */
180function prependChild(parent,element) {
181    if(!parent.firstChild){
182        parent.appendChild(element);
183    }else{
184        parent.insertBefore(element,parent.firstChild);
185    }
186}
187
188/**
189 * Prints a animated gif to show the search is performed
190 *
191 * Because we need to modify the DOM here before the document is loaded
192 * and parsed completely we have to rely on document.write()
193 *
194 * @author Andreas Gohr <andi@splitbrain.org>
195 */
196function showLoadBar(){
197
198  document.write('<img src="'+DOKU_BASE+'lib/images/loading.gif" '+
199                 'width="150" height="12" alt="..." />');
200
201  /* this does not work reliable in IE
202  obj = $(id);
203
204  if(obj){
205    obj.innerHTML = '<img src="'+DOKU_BASE+'lib/images/loading.gif" '+
206                    'width="150" height="12" alt="..." />';
207    obj.style.display="block";
208  }
209  */
210}
211
212/**
213 * Disables the animated gif to show the search is done
214 *
215 * @author Andreas Gohr <andi@splitbrain.org>
216 */
217function hideLoadBar(id){
218  obj = $(id);
219  if(obj) obj.style.display="none";
220}
221
222/**
223 * Adds the toggle switch to the TOC
224 */
225function addTocToggle() {
226    if(!document.getElementById) return;
227    var header = $('toc__header');
228  if(!header) return;
229
230  var showimg     = document.createElement('img');
231    showimg.id      = 'toc__show';
232  showimg.src     = DOKU_BASE+'lib/images/arrow_down.gif';
233  showimg.alt     = '+';
234    showimg.onclick = toggleToc;
235  showimg.style.display = 'none';
236
237    var hideimg     = document.createElement('img');
238    hideimg.id      = 'toc__hide';
239  hideimg.src     = DOKU_BASE+'lib/images/arrow_up.gif';
240  hideimg.alt     = '-';
241    hideimg.onclick = toggleToc;
242
243  prependChild(header,showimg);
244  prependChild(header,hideimg);
245}
246
247/**
248 * This toggles the visibility of the Table of Contents
249 */
250function toggleToc() {
251  var toc = $('toc__inside');
252  var showimg = $('toc__show');
253  var hideimg = $('toc__hide');
254  if(toc.style.display == 'none') {
255    toc.style.display      = '';
256    hideimg.style.display = '';
257    showimg.style.display = 'none';
258  } else {
259    toc.style.display      = 'none';
260    hideimg.style.display = 'none';
261    showimg.style.display = '';
262  }
263}
264
265/*
266 * This enables/disables checkboxes for acl-administration
267 *
268 * @author Frank Schubert <frank@schokilade.de>
269 */
270function checkAclLevel(){
271  if(document.getElementById) {
272    var scope = $('acl_scope').value;
273
274    //check for namespace
275    if( (scope.indexOf(":*") > 0) || (scope == "*") ){
276      document.getElementsByName('acl_checkbox[4]')[0].disabled=false;
277      document.getElementsByName('acl_checkbox[8]')[0].disabled=false;
278    }else{
279      document.getElementsByName('acl_checkbox[4]')[0].checked=false;
280      document.getElementsByName('acl_checkbox[8]')[0].checked=false;
281
282      document.getElementsByName('acl_checkbox[4]')[0].disabled=true;
283      document.getElementsByName('acl_checkbox[8]')[0].disabled=true;
284    }
285  }
286}
287
288/**
289 * insitu footnote addition
290 *
291 * provide a wrapper for domTT javascript library
292 * this function is placed in the onmouseover event of footnote references in the main page
293 *
294 * @author Chris Smith <chris [at] jalakai [dot] co [dot] uk>
295 */
296var currentFootnote = 0;
297function fnt(id, e, evt) {
298
299    if (currentFootnote && id != currentFootnote) {
300        domTT_close($('insitu__fn'+currentFootnote));
301    }
302
303    // does the footnote tooltip already exist?
304    var fnote = $('insitu__fn'+id);
305    var footnote;
306    if (!fnote) {
307        // if not create it...
308
309        // locate the footnote anchor element
310        var a = $( "fn__"+id );
311        if (!a){ return; }
312
313        // anchor parent is the footnote container, get its innerHTML
314        footnote = new String (a.parentNode.innerHTML);
315
316        // strip the leading footnote anchors and their comma separators
317        footnote = footnote.replace(/<a\s.*?href=\".*\#fnt__\d+\".*?<\/a>/gi, '');
318        footnote = footnote.replace(/^\s+(,\s+)+/,'');
319
320        // prefix ids on any elements with "insitu__" to ensure they remain unique
321        footnote = footnote.replace(/\bid=\"(.*?)\"/gi,'id="insitu__$1');
322    } else {
323        footnote = new String(fnt.innerHTML);
324    }
325
326    // activate the tooltip
327    domTT_activate(e, evt, 'content', footnote, 'type', 'velcro', 'id', 'insitu__fn'+id, 'styleClass', 'insitu-footnote JSpopup dokuwiki', 'maxWidth', document.body.offsetWidth*0.4);
328    currentFootnote = id;
329}
330
331
332/**
333 * Add the edit window size controls
334 */
335function initSizeCtl(ctlid,edid){
336    if(!document.getElementById){ return; }
337
338    var ctl      = $(ctlid);
339    var textarea = $(edid);
340    if(!ctl || !textarea) return;
341
342    var hgt = DokuCookie.getValue('sizeCtl');
343    if(hgt){
344      textarea.style.height = hgt;
345    }else{
346      textarea.style.height = '300px';
347    }
348
349    var l = document.createElement('img');
350    var s = document.createElement('img');
351    var w = document.createElement('img');
352    l.src = DOKU_BASE+'lib/images/larger.gif';
353    s.src = DOKU_BASE+'lib/images/smaller.gif';
354    w.src = DOKU_BASE+'lib/images/wrap.gif';
355    addEvent(l,'click',function(){sizeCtl(edid,100);});
356    addEvent(s,'click',function(){sizeCtl(edid,-100);});
357    addEvent(w,'click',function(){toggleWrap(edid);});
358    ctl.appendChild(l);
359    ctl.appendChild(s);
360    ctl.appendChild(w);
361}
362
363/**
364 * This sets the vertical size of the editbox
365 */
366function sizeCtl(edid,val){
367  var textarea = $(edid);
368  var height = parseInt(textarea.style.height.substr(0,textarea.style.height.length-2));
369  height += val;
370  textarea.style.height = height+'px';
371
372  DokuCookie.setValue('sizeCtl',textarea.style.height);
373}
374
375/**
376 * Toggle the wrapping mode of a textarea
377 *
378 * @author Fluffy Convict <fluffyconvict@hotmail.com>
379 * @link   http://news.hping.org/comp.lang.javascript.archive/12265.html
380 * @author <shutdown@flashmail.com>
381 * @link   https://bugzilla.mozilla.org/show_bug.cgi?id=302710#c2
382 */
383function toggleWrap(edid){
384    var txtarea = $(edid);
385    var wrap = txtarea.getAttribute('wrap');
386    if(wrap && wrap.toLowerCase() == 'off'){
387        txtarea.setAttribute('wrap', 'soft');
388    }else{
389        txtarea.setAttribute('wrap', 'off');
390    }
391    // Fix display for mozilla
392    var parNod = txtarea.parentNode;
393    var nxtSib = txtarea.nextSibling;
394    parNod.removeChild(txtarea);
395    parNod.insertBefore(txtarea, nxtSib);
396}
397
398/**
399 * Handler to close all open Popups
400 */
401function closePopups(){
402  if(!document.getElementById){ return; }
403
404  var divs = document.getElementsByTagName('div');
405  for(var i=0; i < divs.length; i++){
406    if(divs[i].className.indexOf('JSpopup') != -1){
407            divs[i].style.display = 'none';
408    }
409  }
410}
411
412/**
413 * Looks for an element with the ID scroll__here at scrolls to it
414 */
415function scrollToMarker(){
416    var obj = $('scroll__here');
417    if(obj) obj.scrollIntoView();
418}
419
420/**
421 * Looks for an element with the ID focus__this at sets focus to it
422 */
423function focusMarker(){
424    var obj = $('focus__this');
425    if(obj) obj.focus();
426}
427
428/**
429 * Remove messages
430 */
431function cleanMsgArea(){
432    var elems = getElementsByClass('(success|info|error)',document,'div');
433    if(elems){
434        for(var i=0; i<elems.length; i++){
435            elems[i].style.display = 'none';
436        }
437    }
438}
439