applications: Add luci-olsr-viz (ported from fff-1.7.4)
[project/luci.git] / applications / luci-olsr-viz / htdocs / luci-static / resources / olsr-viz.js
1 /*
2 Copyright (c) 2006, Lorenz Schori <lo@znerol.ch>
3 All rights reserved (Naja: Ich hab' trotzdem was geaendert. Sven-Ola). (Naja:
4 diese Rechte garantiert dir die BSD-Lizenz ja ausdrücklich. Lorenz)
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8
9 - Redistributions of source code must retain the above copyright notice, this
10   list of conditions and the following disclaimer.
11 - Redistributions in binary form must reproduce the above copyright notice,
12   this list of conditions and the following disclaimer in the documentation
13   and/or other materials provided with the distribution.
14 - Neither the name of the <ORGANIZATION> nor the names of its contributors may
15   be used to endorse or promote products derived from this software without
16   specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
22 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 OLSR-Viz is inspired by Wi-viz: http://wiviz.natetrue.com
30
31 Changes:
32 2007-10-04: Added hostname display option -- Stefan Katerkamp <stefan@katerkamp.de>.
33 2007-10-04: Optimized display by moving presentation css out of js -- lo
34 2010-12-11: Changed some paths to make it work with Kamikaze and Luci -- soma
35 */
36
37 var cgi_url = "/cgi-bin/vizdata.sh";
38
39 var maxmetric = 3;
40 var iconvariant = "-mini";
41 var nodes = new Array();
42 var ncount = 0;
43 var newnodes = new Array();
44 var edges = new Array();
45 var iel = 220; // ideal edge length
46 var optsize = 10; // boundingbox around nodes
47
48 var vwidth = 0;
49 var vheight = 0;
50
51 var xoff = 0;
52 var yoff = 0;
53 var scale = 1.0;
54
55 var idle_timeout = 15;
56 var erase_timeout = 60;
57 var dcl_timeout = 250;
58 var dcllow_timeout = 500;
59 var auto_declump = true;
60 var showdesc = true;
61 var auto_save = 1;
62 var now_secs = 5;
63
64 // dom elements
65 var IFrameObj;
66 var maindiv;
67 var nodediv;
68 var edgediv;
69
70 /******* CALL TO SERVER ********/
71 function callToServer(URL) {
72         var IFrameDoc;
73
74         if (IFrameObj.document) {
75                 // For IE5 + opera
76                 IFrameDoc = IFrameObj.document;
77         }
78         else if (IFrameObj.contentDocument) {
79                 // For NS6
80                 IFrameDoc = IFrameObj.contentDocument;
81         }
82         else if (IFrameObj.contentWindow) {
83                 // For IE5.5 and IE6
84                 IFrameDoc = IFrameObj.contentWindow.document;
85         }
86         else {
87                 // opera? hmmmm
88                 return true;
89         }
90
91         IFrameDoc.location.replace(URL);
92         return false;
93 }
94
95
96 /******** EDGE CLASS ********/
97 function edge(n1,n2){
98         this.getHTML = function()
99         {
100                 var nh = "";
101
102                 if(this.n1.metric > maxmetric || this.n2.metric > maxmetric) {
103                         return "";
104                 }
105
106                 x = this.n1.x*scale;
107                 y = this.n1.y*scale;
108                 dx = this.n2.x*scale - x;
109                 dy = this.n2.y*scale - y;
110
111                 x += xoff*scale + 75;
112                 y += yoff*scale + 15;
113
114                 imgtag = "<img src='/luci-static/resources/olsr-viz/dot_"
115                 if (this.etx > 0 && this.etx < 2) {
116                         imgtag += "good.gif'";
117                 }
118                 else if(this.etx > 2 && this.etx < 5) {
119                         imgtag += "ok.gif'";
120                 }
121                 else if(this.etx > 5 && this.etx < 10) {
122                         imgtag += "weak.gif'";
123                 }
124                 else {
125                         imgtag += "down.gif'";
126                 }
127                 imgtag += " alt='ETX: " + this.etx + "' title='ETX: " + this.etx + "' ";
128
129                 d = Math.sqrt(dx*dx+dy*dy);
130
131                 for (j = 0; j < d; j += 15) {
132                         nh += imgtag + "style='top:"
133                         + parseInt(y+dy * j / d) + "px; left:"
134                         + parseInt(x+dx * j / d) + "px; "
135                         + "width: 4px; height: 4px; position: absolute; z-index: 2' >";
136                 }
137
138                 nh += "<div style='top:"
139                 + parseInt(y+dy * 0.5 - 5) + "px; left:"
140                 + parseInt(x+dx * 0.5 - 24) + "px; "
141                 + "position: absolute; z-index: 3; width: 48px; text-align: center;' >"
142                 + "<span class='label etx' >" + this.etx + "</span></div>";
143
144                 return nh;
145         }
146
147         this.isIdle = function()
148         {
149                 return (now_secs - this.lastseen > idle_timeout);
150         }
151
152         this.isDead = function()
153         {
154                 return (now_secs - this.lastseen > erase_timeout);
155         }
156
157         this.cleanup = function()
158         {
159                 if(this.n1 && this.n1.weight) {
160                         this.n1.weight--;
161                 }
162                 if(this.n2 && this.n2.weight) {
163                         this.n2.weight--;
164                 }
165                 if(this.n1 && this.n2) {
166                         delete this.n1.edges[n2.ip];
167                         delete this.n2.edges[n1.ip];
168                 }
169         }
170
171         this.n1 = n1;
172         this.n2 = n2;
173
174         // setup edges within node objects
175         this.n1.weight++;
176         this.n1.edges[n2.ip] = this;
177         this.n2.weight++;
178         this.n2.edges[n1.ip] = this;
179
180         return this;
181 }
182
183 function getEdgeKey(ip1,ip2)
184 {
185         key = "";
186         if(ip1 > ip2) {
187                 key = ip2 + "-" + ip1;
188         }
189         else {
190                 key = ip1 + "-" + ip2;
191         }
192         return key;
193 }
194
195 function touch_edge(n1,n2,etx)
196 {
197         var key = getEdgeKey(n1.ip,n2.ip);
198         var e = edges[key];
199         if(!e) {
200                 e = new edge(n1,n2);
201                 edges[key] = e;
202         }
203         e.etx = etx;
204         e.lastseen = now_secs;
205         return e;
206 }
207
208 /******** NODE CLASS ********/
209 function node(ip) {
210         this.getHTML = function()
211         {
212                 var nh;
213
214                 if(this.metric > maxmetric) {
215                         return "";
216                 }
217                 var igw = 0;
218                 for(h in this.hna) {
219                         if(h == "0.0.0.0") {
220                                 igw = 1;
221                                 break;
222                         }
223                 }
224                 nh =
225                         "<div id='node_" + this.ip + "' onmousedown='dragstart(this)' style="
226                         + "'top: " + parseInt((this.y+yoff)*scale) + "px; "
227                         + "left: " + parseInt((this.x+xoff)*scale) + "px; "
228                         + "width: 150px; height: 1px; z-index: 4; "
229                         + "position: absolute; background-color: transparent;' >"
230                         + "<div><img src='/luci-static/resources/olsr-viz/node"+(igw ? "-hna" : "")+iconvariant + ".gif'"
231                         + " alt='node " + this.ip + "' style='border: none;'><br>"
232                         +       "<a href='http://" + this.ip + "/'>"
233                         +       "<span class='label ip'>" + this.ip + "</span></a>"
234                         +       (showdesc && this.desc != "" ? 
235                                                 "<br><span class='label desc'>" + this.desc + "</span>" : "")
236                         + "</div></div>";
237                 return nh;
238         }
239
240         this.isIdle = function()
241         {
242                 return (now_secs - this.lastseen > idle_timeout);
243         }
244
245         this.isDead = function()
246         {
247                 return (now_secs - this.lastseen > erase_timeout);
248         }
249
250         this.cleanup = function()
251         {
252                 ncount--;
253         }
254         
255         this.set_metric = function(metric) {
256                 this.metric = metric;
257                 return this;
258         }
259         
260         this.set_desc = function(desc) {
261                 this.desc = desc
262                 return this;
263         }
264         
265         this.update = function() {
266                 this.lastseen = now_secs;
267                 return this;
268         }
269         
270         this.ip = ip;
271         this.x = 0;
272         this.y = 0;
273         this.dx_last=0;
274         this.dy_last=0;
275         this.placed = false;
276         this.weight = 0;
277         this.edges = new Array();
278         this.hna = new Array();
279         this.pinned = false;
280         this.metric = 999;
281         this.desc = "";
282
283         ncount++;
284         return this;
285 }
286
287 function touch_node(ip) {
288         n = nodes[ip];
289         if(!n) {
290                 n = new node(ip);
291                 nodes[ip] = n;
292                 // newnodes.push(n);
293                 // push and pop not supported in old ie. shit.
294                 newnodes[newnodes.length] = n;
295         }
296         return n;
297 }
298
299 function place_new_nodes() {
300         var nc = 0;
301         for(i = 0;i<newnodes.length;i++){
302                 n = newnodes[i];
303                 if(n.placed){continue;}
304                 if(sp = getCookie("node_"+n.ip)) {
305                         xy = sp.split("x");
306                         debug_writeln("sp: "+sp+" xy[0]: "+xy[0]+" xy[1]: "+xy[1]);
307                         n.x = parseFloat(xy[0]);
308                         n.y = parseFloat(xy[1]);
309                 }
310                 else if(n.weight>1){
311                         // see if we find allredy placed nodes
312                         ox=0,oy=0;dx=0,dy=0;c=0;
313                         for(e in n.edges){
314                                 if(nodes[e] && nodes[e].placed){
315                                         if(!ox && !oy) {
316                                                 ox = nodes[e].x;
317                                                 oy = nodes[e].y;
318                                         }
319                                         else {
320                                                 dx += nodes[e].x - ox;
321                                                 dy += nodes[e].y - oy;
322                                         }
323                                         c++;
324                                 }
325                         }
326                         if(c>0) {
327                                 n.x = ox + dx/c + Math.random()*iel/2-iel/4;
328                                 n.y = oy + dy/c + Math.random()*iel/2-iel/4;
329                         }
330                 }
331                 else {
332                         // beginn somewhere
333                         n.x = Math.random()*400;
334                         n.y = Math.random()*400;
335                 }
336                 n.placed = true;
337                 nc++;
338         }
339         newnodes.length=0;
340         return nc;
341 }
342
343 /******** HNA CLASS ********/
344 function hna(gw,net,mask) {
345         this.gw = gw;
346         this.net = net;
347         this.mask = mask;
348         return this;
349 }
350
351 function touch_hna(node,net,mask) {
352         h = node.hna[net];
353         if(!h) {
354                 h = new hna(node.ip,net,mask);
355                 node.hna[net] = h;
356         }
357
358         h.lastseen = now_secs;
359         return h;
360 }
361
362 /******** VIZ SETUP AND SETTINGS ********/
363 function viz_setup(iframeid,maindivid,nodedivid,edgedivid) {
364         // assign a reference to the
365         // object to our global variable IFrameObj.
366         IFrameObj=document.getElementById(iframeid);
367         if (document.frames) {
368                 // this is for IE5 Mac, because it will only
369                 // allow access to the document object
370                 // of the IFrame if we access it through
371                 // the document.frames array
372                 IFrameObj = document.frames[iframeid];
373         }
374
375         draginit();
376
377         maindiv=document.getElementById(maindivid);
378         nodediv=document.getElementById(nodedivid);
379         edgediv=document.getElementById(edgedivid);
380
381         // autosave on exit?
382         if((autosave = getCookie("prefs_autosave"))) {
383                 auto_save = parseInt(autosave);
384         }
385         viz_autosave(auto_save);
386
387         // maximum metric of surrounding nodes
388         if(mmx = getCookie("prefs_maxmetric")) {
389                 set_maxmetric(mmx,true,true);
390         }
391
392         // scale of view
393         if((savescale = getCookie("prefs_scale")) &&
394                  (savescale = parseFloat(savescale))) {
395                         set_scale(savescale,true);
396         }
397
398         // scroll - FIXME
399         /*
400         if(val = getCookie("prefs_innerview")) {
401                 iv = val.split("x");
402                 if (iv[0] && (iv[0] = parseInt(iv[0])) &&
403                                 iv[1] && (iv[2] = parseInt(iv[2])) &&
404                                 iv[3] && (iv[3] = parseInt(iv[3])) &&
405                                 iv[4] && (iv[4] = parseInt(iv[4])))
406                 {
407                         maindiv.scrollLeft = iv[0] + "px";
408                         maindiv.scrollHeight = iv[1] + "px";
409                 }
410         }
411         */
412 }
413
414 function viz_save()
415 {
416         // let cookie survive a month
417         exp = new Date();
418         exp.setTime(exp.getTime() + 2592000000);
419         // save node positions
420         for(ip in nodes)
421         {
422                 if(nodes[ip].metric > maxmetric) {
423                         continue;
424                 }
425                 setCookie("node_"+ip,nodes[ip].x+"x"+nodes[ip].y,exp);
426         }
427
428         // save maxmetric
429         setCookie("prefs_maxmetric",maxmetric,exp);
430
431         // save zooming
432         setCookie("prefs_scale",scale,exp);
433
434         // save scroll - FIXME
435         setCookie("prefs_innerview",
436                 parseInt(maindiv.scrollLeft)+"x"+parseInt(maindiv.scrollTop)+"x"+
437                 parseInt(vwidth*scale)+"x"+parseInt(vheight*scale),exp);
438 }
439
440 function viz_autosave(autosave)
441 {
442         auto_save = autosave;
443         if(auto_save) {
444                 document.body.onunload=viz_save;
445         }
446         else {
447                 deleteCookie("prefs_autosave");
448         }
449 }
450
451 function viz_reset()
452 {
453         deleteAllCookies();
454         for(ip in nodes) {
455                 delete nodes[ip];
456         }
457         for(e in edges) {
458                 delete edges[e];
459         }
460         viz_update();
461 }
462
463 var updateTimer = 0;
464 function viz_update() {
465         if (updateTimer) {
466                 clearTimeout(updateTimer);
467         }
468         now_secs = new Date().getTime()/1000;
469         callToServer(cgi_url);
470 }
471
472 function viz_callback() {
473         if (updateTimer) {
474                 clearTimeout(updateTimer);
475         }
476
477         if(place_new_nodes() > 0 && auto_declump) {
478                 declump();
479         }
480         refresh();
481         updateTimer = setTimeout('viz_update()', 5000);
482 }
483
484 var refresh_running = false;
485 function refresh() {
486         if(refresh_running) {
487                 return;
488         }
489         refresh_running = true;
490
491         var nh = "";
492
493         // refresh nodes
494         nh = "";
495         for (var n in nodes) {
496                 if(nodes[n].isDead()) {
497                         nodes[n].cleanup();
498                         delete nodes[n];
499                 }
500                 else {
501                         nh += nodes[n].getHTML();
502                 }
503         }
504         nodediv.innerHTML = nh;
505
506         // refresh edges
507
508                 nh = "";
509         for (var e in edges) {
510                 if(edges[e].isDead()) {
511                         edges[e].cleanup();
512                         delete edges[e];
513                 }
514                 else {
515                         nh += edges[e].getHTML();
516                 }
517         }
518         edgediv.innerHTML = nh;
519         refresh_running = false;
520 }
521
522 function set_showdesc(doit)
523 {
524         showdesc = doit;
525         if(!noupdate) refresh();
526 }
527
528 function set_autodeclump(doit)
529 {
530         auto_declump = doit;
531         if(doit) {
532                 declump();
533         }
534         else {
535                 clearTimeout(dclTimer);
536         }
537 }
538
539 function set_scale(inscale,noupdate)
540 {
541         if(!inscale) {
542                 inscale = parseFloat(document.getElementById("zoom").value/2);
543         }
544         scale = Math.round(inscale*100)/100;
545         if(!scale || scale<0.1) {
546                 scale = 0.1;
547         }
548         document.getElementById("zoom").value = scale*2;
549         if(!noupdate) refresh();
550 }
551
552 function set_maxmetric(inmetric,noupdate,noconfirm)
553 {
554         inmetric = parseInt(inmetric);
555         if(inmetric > 0 || !noconfirm || confirm("warning. setting the maximum metric to zero can lead to expensive calculations if you are connected to a network with many nodes. do you want to proceed?")) {
556                 maxmetric = inmetric;
557         }
558         document.getElementById("maxmetric").value = maxmetric;
559         if(!noupdate) refresh();
560 }
561
562 // k = area / nodes
563 function fr(x) {
564         return Math.pow((iel*iel)/x,2);
565 }
566
567 function fa(x) {
568         return Math.pow((x*x)/iel,2);
569 }
570
571 var dclTimer = 0;
572 var declump_running = false;
573 function declump(t) {
574         // clear declump timer
575         if(dclTimer) {
576                 clearTimeout(dclTimer);
577         }
578         if(declump_running) {
579                 return;
580         }
581         declump_running = true;
582
583         // nodes
584         nc = 0;
585         for (var ip1 in nodes) {
586                 nodes[ip1].fr_x=0;
587                 nodes[ip1].fr_y=0;
588                 nodes[ip1].fa_x=0;
589                 nodes[ip1].fa_y=0;
590                 nodes[ip1].x_next = nodes[ip1].x;
591                 nodes[ip1].y_next = nodes[ip1].y;
592                 nodes[ip1].randdisplace = 0;
593         }
594         for (var ip1 in nodes) {
595                 if(nodes[ip1].metric > maxmetric || nodes[ip1].pinned) {
596                         continue;
597                 }
598                 for (ip2 in nodes) {
599                         if (nodes[ip2].metric > maxmetric || ip1 == ip2) {
600                                 continue;
601                         }
602                         dx = (nodes[ip1].x_next - nodes[ip2].x_next);
603                         dy = (nodes[ip1].y_next - nodes[ip2].y_next);
604                         d = Math.sqrt(dx*dx+dy*dy);
605                         d = Math.max(d-optsize,(d+optsize)/optsize);
606
607                         nodes[ip1].fr_x += (dx/d) * fr(d);
608                         nodes[ip1].fr_y += (dy/d) * fr(d);
609                 }
610
611                 dx = nodes[ip1].fr_x;
612                 dy = nodes[ip1].fr_y;
613                 d = Math.sqrt(dx*dx+dy*dy);
614                 md = Math.min(d,iel/nodes[ip1].weight);
615                 nodes[ip1].x_next += (dx / d) * md;
616                 nodes[ip1].y_next += (dy / d) * md;
617                 nc++;
618         }
619
620         // edges
621         ec = 0;
622         for (var e in edges) {
623                 if (!edges[e].n1 || !edges[e].n2 ||
624                         edges[e].n1.metric > maxmetric || edges[e].n2.metric > maxmetric) {
625                         continue;
626                 }
627                 dx = (edges[e].n1.x_next - edges[e].n2.x_next);
628                 dy = (edges[e].n1.y_next - edges[e].n2.y_next);
629                 d = Math.sqrt(dx*dx+dy*dy);
630 //              d = Math.max(d-optsize,(d+optsize)/optsize);
631
632                 edges[e].n1.fa_x -= (dx/d) * fa(d);
633                 edges[e].n1.fa_y -= (dy/d) * fa(d);
634                 edges[e].n2.fa_x += (dx/d) * fa(d);
635                 edges[e].n2.fa_y += (dy/d) * fa(d);
636                 ec++;
637         }
638
639         // displacement
640         xmin=-20;ymin=-20;xmax=20;ymax=20;dsum=0;
641         for (var ip in nodes) {
642                 if(nodes[ip].metric > maxmetric || nodes[ip].pinned) {
643                         continue;
644                 }
645
646                 dx = nodes[ip].fa_x;
647                 dy = nodes[ip].fa_y;
648                 d = Math.sqrt(dx*dx+dy*dy);
649                 dx = (dx / d) * Math.min(d,iel/nodes[ip].weight) * 0.75 + nodes[ip].dx_last * 0.25;
650                 dy = (dy / d) * Math.min(d,iel/nodes[ip].weight) * 0.75 + nodes[ip].dy_last * 0.25;
651
652                 nodes[ip].dx_last = dx;
653                 nodes[ip].dy_last = dy;
654                 nodes[ip].x_next += dx;
655                 nodes[ip].y_next += dy;
656
657                 if(!nodes[ip].x_next || !nodes[ip].y_next) {
658                         continue;
659                 }
660
661                 dx = (nodes[ip].x - nodes[ip].x_next);
662                 dy = (nodes[ip].y - nodes[ip].y_next);
663                 dsum += Math.sqrt(dx*dx+dy*dy);
664
665                 nodes[ip].x = nodes[ip].x_next;
666                 nodes[ip].y = nodes[ip].y_next;
667
668                 xmin = Math.min(xmin,nodes[ip].x);
669                 xmax = Math.max(xmax,nodes[ip].x);
670                 ymin = Math.min(ymin,nodes[ip].y);
671                 ymax = Math.max(ymax,nodes[ip].y);
672         }
673         vwidth=(xmax-xmin);
674         vheight=(ymax-ymin);
675
676         xoff=-xmin;
677         yoff=-ymin;
678         /*
679         document.getElementById('debug').innerHTML = "<br>" +
680                 "offset: " + xoff + "x" + yoff + " dsum: " + dsum + "<br>" +
681                 "nc: " + nc + " ec: " + ec + "xmax: " + xmax + " xmin: " + xmin +       "<br>" +
682                 "optsize: " + optsize + "<br>";
683                 */
684         refresh();
685         if(auto_declump) {
686                 dclTimer = setTimeout("declump()", dsum>ncount ? dcl_timeout : dcllow_timeout );
687         }
688         declump_running = false;
689 }
690
691 //Das Objekt, das gerade bewegt wird.
692 var dragip = null;
693
694 // Position, an der das Objekt angeklickt wurde.
695 var dragx = 0;
696 var dragy = 0;
697
698 // Mausposition
699 var posx = 0;
700 var posy = 0;
701
702 function draginit() {
703         // Initialisierung der ãberwachung der Events
704
705         document.onmousemove = drag;
706         document.onmouseup = dragstop;
707 }
708
709
710 function dragstart(element) {
711         //Wird aufgerufen, wenn ein Objekt bewegt werden soll.
712         dragip = element.id.split("_")[1];
713         dragx = posx - element.offsetLeft;
714         dragy = posy - element.offsetTop;
715
716         n = nodes[dragip];
717         if(n) {
718                 n.pinned = true;
719         }
720 }
721
722
723 function dragstop() {
724         //Wird aufgerufen, wenn ein Objekt nicht mehr bewegt werden soll.
725
726         n = nodes[dragip];
727         if(n) {
728                 n.pinned = false;
729         }
730         refresh();
731         dragip=null;
732 }
733
734
735 function drag(ereignis) {
736         //Wird aufgerufen, wenn die Maus bewegt wird und bewegt bei Bedarf das Objekt.
737
738         posx = document.all ? window.event.clientX : ereignis.pageX;
739         posy = document.all ? window.event.clientY : ereignis.pageY;
740         if(dragip != null) {
741                 n = nodes[dragip];
742                 if(n) {
743                         n.x = (posx - dragx)/scale - xoff;
744                         n.y = (posy - dragy)/scale - yoff;
745                 }
746                 e = document.getElementById('node_'+dragip);
747                 e.style.left = parseInt((n.x+xoff)*scale) + "px";
748                 e.style.top = parseInt((n.y+yoff)*scale) + "px";
749         }
750 }
751
752 function debug_writeln(line)
753 {
754         document.getElementById('debug').innerHTML = line + "<br>" + document.getElementById('debug').innerHTML;
755 }
756
757 /**
758  * Sets a Cookie with the given name and value.
759  *
760  * name                  Name of the cookie
761  * value                        Value of the cookie
762  * [expires]    Expiration date of the cookie (default: end of current session)
763  * [path]                Path where the cookie is valid (default: path of calling document)
764  * [domain]      Domain where the cookie is valid
765  *                                                      (default: domain of calling document)
766  * [secure]      Boolean value indicating if the cookie transmission requires a
767  *                                                      secure transmission
768  */
769
770 function setCookie(name, value, expires, path, domain, secure) {
771         document.cookie= name + "=" + escape(value) +
772                 ((expires) ? "; expires=" + expires.toGMTString() : "") +
773                 ((path) ? "; path=" + path : "") +
774                 ((domain) ? "; domain=" + domain : "") +
775                 ((secure) ? "; secure" : "");
776 }
777
778 /**
779  * Gets the value of the specified cookie.
780  *
781  * name Name of the desired cookie.
782  *
783  * Returns a string containing value of specified cookie,
784  *       or null if cookie does not exist.
785  */
786
787 function getCookie(name)
788 {
789         var results = document.cookie.match ( name + '=(.*?)(;|$)' );
790         if (results) {
791                 return unescape(results[1]);
792         }
793         return null;
794 }
795
796 /**
797  * Deletes the specified cookie.
798  *
799  * name         name of the cookie
800  * [path]       path of the cookie (must be same as path used to create cookie)
801  * [domain]     domain of the cookie (must be same as domain used to create cookie)
802  */
803
804 function deleteCookie(name, path, domain) {
805         if (getCookie(name)) {
806                 document.cookie = name + "=" +
807                         ((path) ? "; path=" + path : "") +
808                         ((domain) ? "; domain=" + domain : "") +
809                         "; expires=Thu, 01-Jan-70 00:00:01 GMT";
810         }
811 }
812
813 function deleteAllCookies() {
814         cookies = document.cookie.split("; ");
815         for(i=0;i<cookies.length;i++) {
816                 deleteCookie(cookies[i].split("=")[0]);
817         }
818 }