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)
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
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.
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.
29 OLSR-Viz is inspired by Wi-viz: http://wiviz.natetrue.com
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
37 var cgi_url = "/cgi-bin/vizdata.sh";
40 var iconvariant = "-mini";
41 var nodes = new Array();
43 var newnodes = new Array();
44 var edges = new Array();
45 var iel = 220; // ideal edge length
46 var optsize = 10; // boundingbox around nodes
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;
70 /******* CALL TO SERVER ********/
71 function callToServer(URL) {
74 if (IFrameObj.document) {
76 IFrameDoc = IFrameObj.document;
78 else if (IFrameObj.contentDocument) {
80 IFrameDoc = IFrameObj.contentDocument;
82 else if (IFrameObj.contentWindow) {
84 IFrameDoc = IFrameObj.contentWindow.document;
91 IFrameDoc.location.replace(URL);
96 /******** EDGE CLASS ********/
98 this.getHTML = function()
102 if(this.n1.metric > maxmetric || this.n2.metric > maxmetric) {
108 dx = this.n2.x*scale - x;
109 dy = this.n2.y*scale - y;
111 x += xoff*scale + 75;
112 y += yoff*scale + 15;
114 imgtag = "<img src='/luci-static/resources/olsr-viz/dot_"
115 if (this.etx > 0 && this.etx < 2) {
116 imgtag += "good.gif'";
118 else if(this.etx > 2 && this.etx < 5) {
121 else if(this.etx > 5 && this.etx < 10) {
122 imgtag += "weak.gif'";
125 imgtag += "down.gif'";
127 imgtag += " alt='ETX: " + this.etx + "' title='ETX: " + this.etx + "' ";
129 d = Math.sqrt(dx*dx+dy*dy);
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' >";
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>";
147 this.isIdle = function()
149 return (now_secs - this.lastseen > idle_timeout);
152 this.isDead = function()
154 return (now_secs - this.lastseen > erase_timeout);
157 this.cleanup = function()
159 if(this.n1 && this.n1.weight) {
162 if(this.n2 && this.n2.weight) {
165 if(this.n1 && this.n2) {
166 delete this.n1.edges[n2.ip];
167 delete this.n2.edges[n1.ip];
174 // setup edges within node objects
176 this.n1.edges[n2.ip] = this;
178 this.n2.edges[n1.ip] = this;
183 function getEdgeKey(ip1,ip2)
187 key = ip2 + "-" + ip1;
190 key = ip1 + "-" + ip2;
195 function touch_edge(n1,n2,etx)
197 var key = getEdgeKey(n1.ip,n2.ip);
204 e.lastseen = now_secs;
208 /******** NODE CLASS ********/
210 this.getHTML = function()
214 if(this.metric > maxmetric) {
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>" : "")
240 this.isIdle = function()
242 return (now_secs - this.lastseen > idle_timeout);
245 this.isDead = function()
247 return (now_secs - this.lastseen > erase_timeout);
250 this.cleanup = function()
255 this.set_metric = function(metric) {
256 this.metric = metric;
260 this.set_desc = function(desc) {
265 this.update = function() {
266 this.lastseen = now_secs;
277 this.edges = new Array();
278 this.hna = new Array();
287 function touch_node(ip) {
293 // push and pop not supported in old ie. shit.
294 newnodes[newnodes.length] = n;
299 function place_new_nodes() {
301 for(i = 0;i<newnodes.length;i++){
303 if(n.placed){continue;}
304 if(sp = getCookie("node_"+n.ip)) {
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]);
311 // see if we find allredy placed nodes
312 ox=0,oy=0;dx=0,dy=0;c=0;
314 if(nodes[e] && nodes[e].placed){
320 dx += nodes[e].x - ox;
321 dy += nodes[e].y - oy;
327 n.x = ox + dx/c + Math.random()*iel/2-iel/4;
328 n.y = oy + dy/c + Math.random()*iel/2-iel/4;
333 n.x = Math.random()*400;
334 n.y = Math.random()*400;
343 /******** HNA CLASS ********/
344 function hna(gw,net,mask) {
351 function touch_hna(node,net,mask) {
354 h = new hna(node.ip,net,mask);
358 h.lastseen = now_secs;
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];
377 maindiv=document.getElementById(maindivid);
378 nodediv=document.getElementById(nodedivid);
379 edgediv=document.getElementById(edgedivid);
382 if((autosave = getCookie("prefs_autosave"))) {
383 auto_save = parseInt(autosave);
385 viz_autosave(auto_save);
387 // maximum metric of surrounding nodes
388 if(mmx = getCookie("prefs_maxmetric")) {
389 set_maxmetric(mmx,true,true);
393 if((savescale = getCookie("prefs_scale")) &&
394 (savescale = parseFloat(savescale))) {
395 set_scale(savescale,true);
400 if(val = getCookie("prefs_innerview")) {
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])))
407 maindiv.scrollLeft = iv[0] + "px";
408 maindiv.scrollHeight = iv[1] + "px";
416 // let cookie survive a month
418 exp.setTime(exp.getTime() + 2592000000);
419 // save node positions
422 if(nodes[ip].metric > maxmetric) {
425 setCookie("node_"+ip,nodes[ip].x+"x"+nodes[ip].y,exp);
429 setCookie("prefs_maxmetric",maxmetric,exp);
432 setCookie("prefs_scale",scale,exp);
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);
440 function viz_autosave(autosave)
442 auto_save = autosave;
444 document.body.onunload=viz_save;
447 deleteCookie("prefs_autosave");
464 function viz_update() {
466 clearTimeout(updateTimer);
468 now_secs = new Date().getTime()/1000;
469 callToServer(cgi_url);
472 function viz_callback() {
474 clearTimeout(updateTimer);
477 if(place_new_nodes() > 0 && auto_declump) {
481 updateTimer = setTimeout('viz_update()', 5000);
484 var refresh_running = false;
486 if(refresh_running) {
489 refresh_running = true;
495 for (var n in nodes) {
496 if(nodes[n].isDead()) {
501 nh += nodes[n].getHTML();
504 nodediv.innerHTML = nh;
509 for (var e in edges) {
510 if(edges[e].isDead()) {
515 nh += edges[e].getHTML();
518 edgediv.innerHTML = nh;
519 refresh_running = false;
522 function set_showdesc(doit)
525 if(!noupdate) refresh();
528 function set_autodeclump(doit)
535 clearTimeout(dclTimer);
539 function set_scale(inscale,noupdate)
542 inscale = parseFloat(document.getElementById("zoom").value/2);
544 scale = Math.round(inscale*100)/100;
545 if(!scale || scale<0.1) {
548 document.getElementById("zoom").value = scale*2;
549 if(!noupdate) refresh();
552 function set_maxmetric(inmetric,noupdate,noconfirm)
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;
558 document.getElementById("maxmetric").value = maxmetric;
559 if(!noupdate) refresh();
564 return Math.pow((iel*iel)/x,2);
568 return Math.pow((x*x)/iel,2);
572 var declump_running = false;
573 function declump(t) {
574 // clear declump timer
576 clearTimeout(dclTimer);
578 if(declump_running) {
581 declump_running = true;
585 for (var ip1 in nodes) {
590 nodes[ip1].x_next = nodes[ip1].x;
591 nodes[ip1].y_next = nodes[ip1].y;
592 nodes[ip1].randdisplace = 0;
594 for (var ip1 in nodes) {
595 if(nodes[ip1].metric > maxmetric || nodes[ip1].pinned) {
599 if (nodes[ip2].metric > maxmetric || ip1 == ip2) {
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);
607 nodes[ip1].fr_x += (dx/d) * fr(d);
608 nodes[ip1].fr_y += (dy/d) * fr(d);
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;
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) {
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);
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);
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) {
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;
652 nodes[ip].dx_last = dx;
653 nodes[ip].dy_last = dy;
654 nodes[ip].x_next += dx;
655 nodes[ip].y_next += dy;
657 if(!nodes[ip].x_next || !nodes[ip].y_next) {
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);
665 nodes[ip].x = nodes[ip].x_next;
666 nodes[ip].y = nodes[ip].y_next;
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);
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>";
686 dclTimer = setTimeout("declump()", dsum>ncount ? dcl_timeout : dcllow_timeout );
688 declump_running = false;
691 //Das Objekt, das gerade bewegt wird.
694 // Position, an der das Objekt angeklickt wurde.
702 function draginit() {
703 // Initialisierung der ãberwachung der Events
705 document.onmousemove = drag;
706 document.onmouseup = dragstop;
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;
723 function dragstop() {
724 //Wird aufgerufen, wenn ein Objekt nicht mehr bewegt werden soll.
735 function drag(ereignis) {
736 //Wird aufgerufen, wenn die Maus bewegt wird und bewegt bei Bedarf das Objekt.
738 posx = document.all ? window.event.clientX : ereignis.pageX;
739 posy = document.all ? window.event.clientY : ereignis.pageY;
743 n.x = (posx - dragx)/scale - xoff;
744 n.y = (posy - dragy)/scale - yoff;
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";
752 function debug_writeln(line)
754 document.getElementById('debug').innerHTML = line + "<br>" + document.getElementById('debug').innerHTML;
758 * Sets a Cookie with the given name and value.
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
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" : "");
779 * Gets the value of the specified cookie.
781 * name Name of the desired cookie.
783 * Returns a string containing value of specified cookie,
784 * or null if cookie does not exist.
787 function getCookie(name)
789 var results = document.cookie.match ( name + '=(.*?)(;|$)' );
791 return unescape(results[1]);
797 * Deletes the specified cookie.
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)
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";
813 function deleteAllCookies() {
814 cookies = document.cookie.split("; ");
815 for(i=0;i<cookies.length;i++) {
816 deleteCookie(cookies[i].split("=")[0]);