But this time I just wanted to use client technologies and my source of data was
The first point was to fetch the data. I wrote a simple java parser which downloaded recursively all the profiles and groups starting from my own
. I ignored a few pages containing some xml errors:
. My
.
I then transformed this rdf file into JSON using the following XSLT stylesheet.
.
What is cool with JSON is that this format stores the data but and it is also a big javascript object which can be used just like a regular variable.
.
. Each time an icon/tag/profile/job/affiliation/group is clicked, a function
is ivoked. This function calls the
method where each icon is moved sightly to its goal. This function calls itself recursively until all icons have reached their goal. This page was tested using
.
<html
xmlns="
http://www.w3.org/1999/xhtml"
>
<head>
<title>Nature Network With SVG: Pierre Lindenbaum</title>
<script type="text/javascript" src="nn.js"/>
<script type="text/javascript"><![CDATA[
/** the HTML namespace */
var HTML={
"NS":"
http://www.w3.org/1999/xhtml"
};
/** the SVG namespace */
var SVG={
"NS":"
http://www.w3.org/2000/svg"
};
/** the XLINK namespace */
var XLINK={
"NS":"
http://www.w3.org/1999/xlink"
};
/** the html document */
var htmldoc=null;
/** the svg root */
var svgroot=null;
/** map uri2user */
var uri2user= new Array();
/** animation running */
var animated=false;
/** screen width */
var viewWidth=0.0;
/** ratio for animation */
var RATIO_ANIME=0.8;
/** time for animated */
var ELAPSED_TIME=10;
/** initialise the whole document */
function init()
{
htmldoc= this.document;
svgroot= document.getElementById("svgdoc");
var svgrect =document.getElementById("frameSVGRect");
viewWidth=this.innerWidth-30;
svgrect.setAttribute("width",viewWidth);
svgrect.setAttribute("height",this.innerHeight-100);
svgroot.setAttribute("width",1+viewWidth);
svgroot.setAttribute("height",1+this.innerHeight-100);
var v= network.profile;
var cosf= Math.cos;
var sinf= Math.sin;
//loop over users
for(var i=0;i< v.length;++i)
{
//initialize the uri2user array
uri2user[v[i].id]=v[i];
var angle= Math.random()*6.2;
v[i].pos.x= this.innerWidth/2.0 + this.innerWidth*cosf(angle);
v[i].pos.y= this.innerHeight/2.0 + this.innerHeight*sinf(angle);
var g = htmldoc.createElementNS(SVG.NS,"g");
v[i].g = g;
g.setAttribute("title",v[i].name+" "+v[i].job+" "+v[i].affiliation);
g.setAttribute("onmousedown","focusProfile(this);");
g.setAttribute("transform","translate(-50,-50)");
g.setAttribute("cursor","pointer");
g.setAttribute("id",v[i].id);
var r1= htmldoc.createElementNS(SVG.NS,"rect");
r1.setAttribute("x",0);
r1.setAttribute("y",0);
r1.setAttribute("width",41);
r1.setAttribute("height",50);
r1.setAttribute("fill","gray");
r1.setAttribute("stroke","black");
g.appendChild(r1);
if(v[i].img.length>0)
{
var img= htmldoc.createElementNS(SVG.NS,"image");
img.setAttributeNS(XLINK.NS,"xlink:href",v[i].img);
img.setAttribute("x",4);
img.setAttribute("y",4);
img.setAttribute("width",41-8);
img.setAttribute("height",50-8);
g.appendChild(img);
}
else
{
}
svgroot.appendChild(g);
}
focusProfileID("lindenb");
}
function focusProfile(g)
{
focusProfileID(g.id);
}
function createProfileShown()
{
var profileShown= new Array();
var v= network.profile;
for(var i=0;i< v.length;++i)
{
profileShown[v[i].id]=0;
}
return profileShown;
}
function focusProfileID( id)
{
if(animated) return;
showLayer("profileLayer");
var profileShown= createProfileShown();
profileShown[id]=1;
var user=uri2user[id];
if(user==null) return;
var a=null;
var tag = document.getElementById("profileImg");
if(user.img.length==0)
{
tag.setAttribute("src","
http://network.nature.com/images/user_generic_large.gif");
}
else
{
tag.setAttribute("src",user.img);
}
tag = document.getElementById("profileAnchor");
tag.setAttribute("href","
http://network.nature.com/profile/"+user.id);
tag.setAttribute("title","Go to
http://network.nature.com/profile/"+user.id);
tag.setAttribute("target",user.id);
tag = document.getElementById("profileName");
tag.innerHTML= user.name;
tag = document.getElementById("profileJob");
tag.innerHTML="";
a= htmldoc.createElementNS(HTML.NS,"a");
tag.appendChild(a);
a.setAttribute("href","javascript:focusJob(\""+user.job+"\");");
a.setAttribute("title",user.job);
a.appendChild(htmldoc.createTextNode(user.job));
tag = document.getElementById("profileAffiliation");
tag.innerHTML="";
a= htmldoc.createElementNS(HTML.NS,"a");
tag.appendChild(a);
a.setAttribute("href","javascript:focusAffiliation(\""+user.affiliation+"\");");
a.setAttribute("title",user.affiliation);
a.appendChild(htmldoc.createTextNode(user.affiliation));
tag = document.getElementById("profileWWW");
tag.innerHTML= ""
if(user.www.length>0)
{
a= htmldoc.createElementNS(HTML.NS,"a");
tag.appendChild(a);
a.setAttribute("href",user.www);
a.setAttribute("title",user.www);
a.setAttribute("target",user.www);
a.appendChild(htmldoc.createTextNode(user.www));
}
tag = document.getElementById("profileTags");
tag.innerHTML= ""
for(var i=0;i< user.tags.length;i++)
{
var a= htmldoc.createElementNS(HTML.NS,"a");
tag.appendChild(a);
a.setAttribute("href","javascript:focusTag(\""+user.tags[i]+"\");");
a.setAttribute("title",user.tags[i]);
a.appendChild(htmldoc.createTextNode(user.tags[i]));
if(i+1!= user.tags.length) tag.appendChild(htmldoc.createTextNode(" "));
}
var link=network.link;
for(var i=0;i< link.length;++i)
{
var L= link[i];
var other=null;
if(L[0]==id)
{
other =uri2user[L[1]];
}
else if(L[1]==id)
{
other =uri2user[L[0]];
}
if(other==null) continue;
profileShown[other.id]=1;
}
tag = document.getElementById("profileGroups");
tag.innerHTML= ""
var group=network.group;
for(var i=0;i< group.length;++i)
{
for(var j=0;j< group[i].members.length;++j)
{
if( group[i].members[j]==id)
{
var a= htmldoc.createElementNS(HTML.NS,"a");
tag.appendChild(a);
a.setAttribute("href","javascript:focusGroupID(\""+group[i].uri+"\");");
a.setAttribute("title",group[i].name);
a.appendChild(htmldoc.createTextNode(group[i].name));
tag.appendChild(htmldoc.createTextNode(" "));
break;
}
}
}
launchAnimation(profileShown);
}
function focusGroupID(id)
{
if(animated) return;
showLayer("groupLayer");
var group=network.group;
tag = document.getElementById("groupLayer");
tag.innerHTML= ""
for(var i=0;i< group.length;++i)
{
if(group[i].uri!=id) continue;
var e= htmldoc.createElementNS(HTML.NS,"b");
e.appendChild(htmldoc.createTextNode("Group: "));
tag.appendChild(e);
var e= htmldoc.createElementNS(HTML.NS,"a");
e.setAttribute("href","
http://network.nature.com/group/"+group[i].uri);
e.setAttribute("title","
http://network.nature.com/group/"+group[i].uri);
e.setAttribute("target",group[i].uri);
e.appendChild(htmldoc.createTextNode(group[i].name));
tag.appendChild(e);
var profileShown= createProfileShown();
for(var j=0;j< group[i].members.length;++j)
{
profileShown[group[i].members[j]]=1;
}
break;
}
launchAnimation(profileShown);
}
function focusTag(id)
{
if(animated) return;
showLayer("tagLayer");
tag = document.getElementById("tagLayer");
tag.innerHTML= ""
var e= htmldoc.createElementNS(HTML.NS,"b");
e.appendChild(htmldoc.createTextNode("Tag: "));
tag.appendChild(e);
tag.appendChild(htmldoc.createTextNode(id));
var profileShown= createProfileShown();
var v= network.profile;
id=id.toLowerCase();
for(var i=0;i< v.length;++i)
{
var t=v[i].tags;
for(var j=0;j< t.length;++j)
{
if(t[j].toLowerCase()==id)
{
profileShown[v[i].id]=1;
break;
}
}
}
launchAnimation(profileShown);
}
function focusJob(id)
{
if(animated) return;
showLayer("jobLayer");
tag = document.getElementById("jobLayer");
tag.innerHTML= ""
var e= htmldoc.createElementNS(HTML.NS,"b");
e.appendChild(htmldoc.createTextNode("Job : "));
tag.appendChild(e);
tag.appendChild(htmldoc.createTextNode(id));
focusOnProperty("job",id);
}
function focusAffiliation(id)
{
if(animated) return;
showLayer("affiliationLayer");
tag = document.getElementById("affiliationLayer");
tag.innerHTML= ""
var e= htmldoc.createElementNS(HTML.NS,"b");
e.appendChild(htmldoc.createTextNode("Affiliation : "));
tag.appendChild(e);
tag.appendChild(htmldoc.createTextNode(id));
focusOnProperty("affiliation",id);
}
function focusOnProperty(prop,id)
{
var profileShown= createProfileShown();
var v= network.profile;
id=id.toLowerCase();
for(var i=0;i< v.length;++i)
{
var p = v[i][prop].toLowerCase();
if(p==id)
{
profileShown[v[i].id]=1;
}
}
launchAnimation(profileShown);
}
function showLayer(id)
{
var a=["profileLayer","groupLayer","tagLayer","affiliationLayer","jobLayer"];
for(var i=0;i< a.length;i++)
{
document.getElementById(a[i]).style.visibility=(a[i]==id?"visible":"hidden");
}
}
function launchAnimation(profileShown)
{
if(animated) return;
var v= network.profile;
var x=5;
var y=5;
var cosf= Math.cos;
var sinf= Math.sin;
//loop over users
for(var i=0;i< v.length;++i)
{
if(profileShown[v[i].id]==1)
{
v[i].goal.x=x;
v[i].goal.y=y;
x+=(5+41);
if(x+41>viewWidth)
{
x=5;
y+=(5+50);
}
}
else
{
var angle= Math.random()*6.2;
v[i].goal.x= this.innerWidth/2.0 + this.innerWidth*cosf(angle);
v[i].goal.y= this.innerHeight/2.0 + this.innerHeight*sinf(angle);
}
}
animated=true;
setTimeout('nextStep()', ELAPSED_TIME);
}
function nextStep()
{
if(animated==false) return;
var continueAnimation=0;
var v= network.profile;
var sqrt= Math.sqrt;
for(var i=0;i< v.length;++i)
{
var curr=v[i];
var x0= curr.pos.x;
var y0= curr.pos.y;
var x1= curr.goal.x;
var y1= curr.goal.y;
var dx= x1-x0;
var dy= y1-y0;
curr.pos.x = x0+ (dx-dx*RATIO_ANIME);
curr.pos.y = y0+ (dy-dy*RATIO_ANIME);
dx= x1-x0;
dy= y1-y0;
var dist = sqrt(dx*dx+dy*dy);
if(dist<4)
{
curr.pos.x= x1;
curr.pos.y= y1;
}
else
{
continueAnimation++;
}
v[i].g.setAttribute("transform","translate("+ v[i].pos.x+","+v[i].pos.y+")");
}
if(continueAnimation>0)
{
setTimeout('nextStep()', ELAPSED_TIME);
}
else
{
animated=false;
}
}
]]></script>
</head>
<body onload="init()">
<div style="font-size:10pt;"><span style="font-size:18pt;"><b>The Nature Network</b></span> created by <a href="
http://plindenbaum.blogspot.com">Pierre Lindenbaum PhD 2007</a>. <cite>Learning a little more javascript and <a href="
http://en.wikipedia.org/wiki/JSON">JSON</a> I've used the <a href="nn.js">data</a> available from the <a href="
http://network.nature.com/">Nature Network</a> on May 13, 2007 to display an interactive map of the network based on <a href="
http://www.w3.org/Graphics/SVG/">SVG</a>, <a href="
http://en.wikipedia.org/wiki/JSON">JSON</a> and javascript</cite>. This page was tested on <a href="
http://www.mozilla.com/en-US/firefox/">Firefox 2</a></div>
<div id="layers" style="position:relative;width:100%; height:100px; background:lightgray;">
<div id="groupLayer" style="position:absolute; top:0px; left:0px;width:100%;visibility:hidden; "/>
<div id="tagLayer" style="position:absolute; top:0px; left:0px;width:100%;visibility:hidden; "/>
<div id="jobLayer" style="position:absolute; top:0px; left:0px;width:100%;visibility:hidden; "/>
<div id="affiliationLayer" style="position:absolute; top:0px; left:0px;width:100%;visibility:hidden; "/>
<div id="profileLayer" style="position:absolute; top:0px; left:0px;width:100%; font-size:9pt;">
<table width="100%"><tr>
<td width="33">
<a id="profileAnchor">
<img id="profileImg" width="33" height="42" src="
http://network.nature.com/images/user_generic_large.gif"/>
</a>
</td>
<td>
<b>Name :</b> <span id="profileName"></span>
<b>Job :</b> <span id="profileJob"></span>
<b>Affiliation :</b> <span id="profileAffiliation"></span>
<b>www :</b> <span id="profileWWW"></span>
<b>Tags :</b> <span id="profileTags"></span>
<b>Groups :</b> <span id="profileGroups"></span>
</td></tr></table></div>
</div><br/>
<svg id="svgdoc" xmlns="
http://www.w3.org/2000/svg" version="1.1" width="100" height="100">
<defs>
<linearGradient id = "gradient1" x1 = "50%" y1 = "0%" x2 = "50%" y2 = "100%">
<stop stop-color = "white" offset = "0%"/>
<stop stop-color = "black" offset = "100%"/>
</linearGradient>
</defs>
<rect id="frameSVGRect" x="0" y="0" width="100" height="100" fill="lightgray" stroke="black"/>
</svg>
<div>Pierre Lindenbaum: plindenbaum ( a t ) yahoo ( d o t ) fr </div>
<!-- google analytics -->
<script src="
http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "XXXX-2";
urchinTracker();
</script>
<!-- google analytics -->
</body>
</html>