Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / HelpSource / scdoc.js
blob89731e611f62a48337c16f4fc59632dcd92c7cb8
1 function toggle_visibility(e) {
2     if(e.style.display == 'none') {
3         e.style.display = 'block';
4         return e;
5     } else {
6         e.style.display = 'none';
7         return undefined;
8     }
12 function createCookie(name,value,days) {
13     if (days) {
14         var date = new Date();
15         date.setTime(date.getTime()+(days*24*60*60*1000));
16         var expires = "; expires="+date.toGMTString();
17     }
18     else var expires = "";
19     document.cookie = name+"="+value+expires+"; path=/";
22 function readCookie(name) {
23     var nameEQ = name + "=";
24     var ca = document.cookie.split(';');
25     for(var i=0;i < ca.length;i++) {
26         var c = ca[i];
27         while (c.charAt(0)==' ') c = c.substring(1,c.length);
28         if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
29     }
30     return null;
34 var storage;
35 var sidetoc;
36 var toc;
37 var menubar;
38 var allItems;
39 function popOutTOC(original_toc, p0) {
40     var t = original_toc.cloneNode(true);
41     t.id = "sidetoc";
42     var c = document.getElementsByClassName("contents")[0];
43     var left = c.style.marginLeft;
44     c.style.marginLeft = "20.5em";
45     document.body.insertBefore(t,c);
47     t.getElementsByClassName("toc_search")[0].getElementsByTagName("input")[0].onkeyup = toc_search;
49     t.style.top = menubar.clientHeight;
50     t.style.maxHeight = "none";
51     t.style.display = "block";
53     var p = t.getElementsByClassName("popoutlink")[0];
54     p.innerHTML = "close";
55     p.onclick = function() {
56         t.parentNode.removeChild(t);
57         c.style.marginLeft = left;
58         p0.style.display = "";
59         sidetoc = null;
60         allItems = toc.getElementsByTagName("ul")[0].getElementsByTagName("li");
61         storage.popToc = "no";
62         return false;
63     }
64     var x = document.createElement("div");
65     x.id = "toctitle";
66     x.innerHTML = "Table of contents";
67     t.insertBefore(x,p.nextSibling);
68     p0.style.display = "none";
70     sidetoc = t;
71     allItems = t.getElementsByTagName("ul")[0].getElementsByTagName("li");
72     resize_handler();
73     storage.popToc = "yes";
76 function resize_handler() {
77     var height = window.innerHeight - menubar.clientHeight - 20;
78     if(sidetoc)
79         sidetoc.style.height = height;
80     if(toc)
81         toc.style.maxHeight = height * 0.75;
84 function addInheritedMethods() {
85     if(! /\/Classes\/[^\/]+/.test(window.location.pathname)) return; // skip this if not a class doc
86     var doc = docmap["Classes/"+document.title];
87     if(!doc) return;
88     if(doc.implementor) {
89         var sups = docmap["Classes/"+doc.implementor].superclasses;
90     } else {
91         var sups = doc.superclasses;
92     }
93     if(!sups) return;
94     var divs = [document.getElementById("inheritedclassmets"), document.getElementById("inheritedinstmets")];
95     for(var i=0;i<sups.length;i++) {
96         var s = docmap["Classes/"+sups[i]];
97         var d = [];
98         for(var j=0;j<2;j++) {
99             d[j] = document.createElement("ul");
100             d[j].className = "inheritedmets";
101             d[j].style.display = "none";
102         }
103         var mets = s.methods.sort();
104         for(var j=0;j<mets.length;j++) {
105             var m = mets[j];
106             if(doc.methods.indexOf("_"+m.slice(1))<0) { // ignore methods already documented in this helpfile
107                 var li = document.createElement("li");
108                 li.innerHTML = "<a href='"+helpRoot+"/"+s.path+".html#"+m.slice(1)+"'>"+m.slice(2)+"</a>";
109                 if(m[1]=="*") {
110                     d[0].appendChild(li);
111                 } else
112                 if(m[1]=="-") {
113                     d[1].appendChild(li);
114                 }
115             }
116         }
117         for(var j=0;j<2;j++) {
118             var count = d[j].childElementCount;
119             if(count) {
120                 var x = document.createElement("div");
121                 var show = "&#9658; show";
122                 var hide = "&#9660; hide";
123                 x.className = "inheritedmets_class";
124                 x.innerHTML = count+" methods from <a href='"+helpRoot+"/"+s.path+".html'>"+s.title+"</a> ";
125                 divs[j].appendChild(x);
126                 (function(z) {
127                     var a = document.createElement("a");
128                     a.className = "inheritedmets_toggle";
129                     a.setAttribute("href","#");
130                     a.innerHTML = show;
131                     a.onclick = function() {
132                         if(z.style.display == "none") {
133                             z.style.display = "block";
134                             a.innerHTML = hide;
135                         } else {
136                             z.style.display = "none";
137                             a.innerHTML = show;
138                         }
139                         return false;
140                     };
141                     x.appendChild(a);
142                 })(d[j]);
143                 divs[j].appendChild(d[j]);
144             }
145         }
146     }
149 function showAllSubclasses(a) {
150     var e = document.getElementById("hiddensubclasses");
151     e.style.display = "";
152     a.style.display = "none";
156 This key event handler selects the whole line when pressing shift/ctrl-enter with no selection.
157 But the problem is that it does not update the selection sent to the client.
158 This is probably because the WebView catches the key event before javascript does!
159 A fix might be to expose a function to JS that evaluates selection, and call it here.
160 Or can the WebView make sure that JS has responded to all key events before getting the selection?
162 function selectLine() {
163     var s =  window.getSelection();
164     var r = s.getRangeAt();
166     function findleft(p) {
167         var y, j;
168         while(p) {
169             if(p.nodeName == "BR")
170                 return [p,j];
171             if(p.childNodes.length>0) {
172                 for(var i=p.childNodes.length-1;i>=0;i--) {
173                     y = findleft(p.childNodes[i]);
174                     if(y) return y;
175                 }
176             }
177             p = p.previousSibling;
178         }
179         return null;
180     };
182     function findright(p) {
183         var y, j;
184         while(p) {
185             if(p.nodeName == "BR")
186                 return [p,j];
187             for(var i=0;i<p.childNodes.length;i++) {
188                 y = findright(p.childNodes[i]);
189                 if(y) return y;
190             }
191             p = p.nextSibling;
192         }
193         return null;
194     };
197     if(r.collapsed) {
198         var r2 = document.createRange();
199         var top = r.startContainer;
200         while(top && top.nodeName != "PRE")
201             top = top.parentNode;
203         var p = r.startContainer;
204         while(!p.previousSibling && p != top) {
205             p = p.parentNode;
206         }
207         if(p==top) {
208             r2.setStartBefore(top.firstChild);
209         } else {
210             var found = findleft(p.previousSibling);
211             if(found) {
212                 r2.setStartAfter(found[0]);
213             } else {
214                 r2.setStartBefore(top.firstChild);
215             }
216         }
217         var p = r.startContainer;
218         while(!p.nextSibling && p != top) {
219             p = p.parentNode;
220         }
221         if(p==top) {
222             r2.setEndAfter(top.lastChild);
223         } else {
224             var found = findright(p.nextSibling);
225             if(found) {
226                 r2.setEndBefore(found[0]);
227             } else {
228                 r2.setEndAfter(top.lastChild);
229             }
230         }
231         s.removeAllRanges();
232         s.addRange(r2);
233     }
236 function countChar(str,chr) {
237     var x = 0, a, b;
238     for(var i=0;i<str.length;i++) {
239         if(str[i]==chr) {
240             if(a==undefined) a = i;
241             b = i;
242             x++;
243         }
244     }
245     // return count, first occurence and last occurence
246     return [x,a,b];
249 var code_click_node;
250 var code_click_pos;
252 function selectParens(ev) {
253     var s =  window.getSelection();
254     var r = s.getRangeAt();
255     var r2 = document.createRange();
256     var j;
258     function countChars(char, counterChar, text, count, back)
259     {
260         var len = text.length;
261         var i;
262         if(back) {
263             for(i=len-1; i>=0; i--) {
264                 var c = text.charAt(i);
265                 if(c == char) count--;
266                 else if(c == counterChar) count++;
267                 if( count < 1 ) break;
268             }
269         }
270         else {
271             for(i=0; i<len; i++) {
272                 var c = text.charAt(i);
273                 if(c == char) count--;
274                 else if(c == counterChar) count++;
275                 if( count < 1 ) break;
276             }
277         }
278         return [count, i];
279     }
281     function advance(node, top, back) {
282         var childs = node.childNodes;
283         if(childs && childs.length) {
284             var child = back ? childs[childs.length-1] : childs[0];
285             return child;
286         }
287         else {
288             var next;
289             if(back) {
290                 while( node != top && !(next = node.previousSibling) )
291                     node = node.parentNode;
292             }
293             else {
294                 while( node != top && !(next = node.nextSibling) )
295                     node = node.parentNode;
296             }
297             return next;
298         }
299     }
301     function findChar(char, counterChar, top, node, back, count) {
302         do {
303             var text = node.nodeValue;
304             if(text) {
305                 var res = countChars(char, counterChar, text, count, back);
306                 count = res[0];
307                 if(count < 1) return [node, res[1]];
308             }
309             node = advance(node, top, back);
310         } while(node);
312         return null;
313     }
315     function findAdjacentChar(top, node, pos, chars) {
316         var ok = false;
317         var text, len;
318         var i;
320         // try right:
321         text = node.nodeValue;
322         len = text.length;
323         if(text && pos < len) {
324             if( (i = chars.indexOf(text[pos])) != -1 )
325                 return [chars[i], node, pos];
326         }
327         else {
328             var n = node;
329             do {
330                 n = advance(n, top, false);
331             } while (n && !(text = n.nodeValue));
332             if(n && (len=text.length)) {
333                 if( (i = chars.indexOf(text[0])) != -1 )
334                     return [chars[i], n, 0];
335             }
336         }
338         // try left:
339         text = node.nodeValue;
340         len = text.length;
341         if(text && pos > 0) {
342             if( (i = chars.indexOf(text[pos-1])) != -1 )
343                 return [chars[i], node, pos-1];
344         }
345         else {
346             n = node;
347             do {
348                 n = advance(n, top, true);
349             } while (n && !(text = n.nodeValue));
350             if(n && (len=text.length)) {
351                 if( (i = chars.indexOf(text[len-1])) != -1 )
352                     return [chars[i], n, len-1];
353             }
354         }
356         return null;
357     }
359     if(!code_click_node) return;
361     var top = ev.target;
362     while( top && top.className.indexOf("lang-sc") == -1 )
363         top = top.parentNode;
364     if(!top) return;
366     var pos = code_click_pos;
367     var node = code_click_node; code_click_node = null;
368     var char;
370     var startRes = findAdjacentChar(top, node, pos, "()");
371     if(startRes) {
372         char = startRes[0];
373         node = startRes[1];
374         pos = startRes[2];
375     }
376     else {
377         return;
378     }
380     back = char == ")";
381     counterChar = back ? "(" : ")";
382     var count = 1, countRes;
383     var rnode, rpos;
385     // try same node
386     var text = node.nodeValue;
387     if( back) text = text.substring(0, pos);
388     else text = text.substring(pos+1);
389     countRes = countChars(counterChar, char, text, count, back);
390     count = countRes[0];
391     if( count < 1 ) {
392         rpos = countRes[1];
393         if(!back) rpos += pos + 1;
394         rnode = node;
395     }
396     else {
397         // try other nodes
398         var n = advance(node, top, back);
399         res = findChar(counterChar, char, top, n, back, count);
400         if(res) {
401             rnode = res[0];
402             rpos = res[1];
403         }
404     }
406     if(rnode) {
407         if(back) {
408             r2.setStart(rnode, rpos);
409             r2.setEnd(node, pos+1);
410         }
411         else {
412             r2.setStart(node, pos);
413             r2.setEnd(rnode, rpos+1);
414         }
415         s.removeAllRanges();
416         s.addRange(r2);
417     }
420 escape_regexp = function(str) {
421   var specials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g"); // .*+?|()[]{}\
422   return str.replace(specials, "\\$&");
425 function toc_search(ev) {
426 //TODO: on enter, go to first match
427     var re = RegExp("^"+escape_regexp(ev.target.value),"i");
428     for(var i=0;i<allItems.length;i++) {
429         var li = allItems[i];
430         var a = li.firstChild;
431         if(re.test(a.innerHTML)) {
432             li.style.display = "";
433             var lev = li.className[3];
434             for(var i2 = i-1;i2>=0;i2--) {
435                 var e = allItems[i2];
436                 if(e.className[3]<lev) {
437                     e.style.display = "";
438                     lev -= 1;
439                 }
440                 if(e.className[3]==1) break;
441             }
442         } else {
443             li.style.display = "none";
444         }
445     }
448 function fixTOC() {
449     var x = document.getElementsByClassName("lang-sc");
450     for(var i=0;i<x.length;i++) {
451         var e = x[i];
453         // make all code examples editable!
454         e.setAttribute("contentEditable",true);
456         // select parenthesis on double-click
457         e.onclick = function(ev) {
458             var r =  window.getSelection().getRangeAt(0);
459             if(r.collapsed) {
460                 code_click_node = r.startContainer;
461                 code_click_pos = r.startOffset;
462             }
463         };
464         e.ondblclick = selectParens;
465     }
467     addInheritedMethods();
469     if('localStorage' in window) {
470         storage = window.localStorage;
471     } else {
472         storage = {};
473     }
475     var openMenu;
476     var inMenu = false;
478     var toggleMenu = function(e) {
479         if(openMenu) {
480             openMenu.style.display = 'none';
481         }
482         if(e != openMenu) {
483             e.style.display = 'block';
484             openMenu = e;
485         } else {
486             openMenu = undefined;
487         }
488         inMenu = true;
489     }
491     document.onclick = function(e) {
492         if(openMenu && !inMenu && e.target.id!="toc_search") {
493             openMenu.style.display = 'none';
494             openMenu = undefined;
495         }
496         inMenu = false;
497         return true;
498     }
500 // make header menu
501     var bar = document.getElementById("menubar");
502     menubar = bar;
503     var nav = ["Home","Browse","Search"];
504     var url = ["Help.html","Browse.html","Search.html"];
505     for(var i=0;i<nav.length;i++) {
506         var li = document.createElement("li");
507         li.className = "menuitem";
508         var a = document.createElement("a");
509         a.innerHTML = nav[i];
510         a.setAttribute("href",helpRoot+"/"+url[i]);
511         a.className = "navLink";
512         li.appendChild(a);
513         bar.appendChild(li);
514     }
516     var li = document.createElement("li");
517     li.className = "menuitem";
518     var a = document.createElement("a");
519     a.innerHTML = "Indexes &#9660;";
520     a.setAttribute("href","#");
521     var m1 = document.createElement("div");
522     m1.className = "submenu";
523     m1.style.display = "none";
524     a.onclick = function() {
525         toggleMenu(m1);
526         return false;
527     };
528     var nav = ["Documents","Classes","Methods"];
529     for(var i=0;i<nav.length;i++) {
530         var b = document.createElement("a");
531         b.setAttribute("href",helpRoot+"/Overviews/"+nav[i]+".html");
532         b.innerHTML = nav[i];
533         m1.appendChild(b);
534     }
535     li.appendChild(a);
536     li.appendChild(m1);
537     bar.appendChild(li);
539     var li = document.createElement("li");
540     li.className = "menuitem";
541     var x = document.createElement("span");
542     x.id = "topdoctitle";
543     x.appendChild(document.createTextNode(document.title));
544     x.onclick = function() {
545         scroll(0,0);
546         return false;
547     }
548     li.appendChild(x)
549     bar.appendChild(li);
551     var t = document.getElementById("toc");
552     toc = t;
553     if(t) {
554         var div = document.createElement("span");
555         div.className = "toc_search";
556         var ts = document.createElement("input");
557         ts.type = "text";
558         ts.id = "toc_search";
559         ts.value = "";
560         ts.style.border = "1px solid #ddd";
561         allItems = toc.getElementsByTagName("ul")[0].getElementsByTagName("li");
562         ts.onkeyup = toc_search;
563         div.appendChild(document.createTextNode("Filter:"));
564         div.appendChild(ts);
565         t.insertBefore(div,t.firstChild);
567         x.appendChild(document.createTextNode(" - "));
568         t.style.display = 'none';
570         var a = document.createElement("a");
571         a.setAttribute("href","#");
572         a.innerHTML = "Table of contents &#9660;";
573         li.appendChild(a);
574         a.onclick = function() {
575             ts.focus();
576             toggleMenu(t);
577             return false;
578         };
579         li.appendChild(t.parentNode.removeChild(t));
580         var p = document.createElement("a");
581         p.setAttribute("href","#");
582         p.className = "popoutlink";
583         p.innerHTML = "pop out";
584         p.onclick = function() {
585             if(!sidetoc)
586                 popOutTOC(t,a);
587             return false;
588         }
589         t.insertBefore(p,t.firstChild);
590         resize_handler();
591         if(storage.popToc == "yes") {
592             popOutTOC(t,a);
593         }
594     }
595     window.onresize = resize_handler;