/**
* This component uses the D3 library and specifically its implementation of the force algorithm to
* represent a network of protein interactions.
*
* @class
* @extends Biojs
*
* @author Gustavo A. Salazar
* @version 0.9.1_beta
* @category 1
*
* @requires jQuery Core 1.7.2
* @dependency
*
* @requires D3
* @dependency
*
* @requires InteractionsD3 CSS
* @dependency
*
* @param {Object} options An object with the options for the InteractionsD3 component.
*
* @option {string} target
* Identifier of the DIV tag where the component should be displayed.
* @option {string} width
* Width of the SVG element, if given in percentage it will use it on proportion of the container
* @option {string} height
* Height of the SVG element, if given in percentage it will use it on proportion of the container
* @option {string} radius
* Radius of the nodes representing the proteins
* @option {string} enableEdges
* Force the proteins to stay in the defined area of the SVG
*
* @example
* var instance = new Biojs.InteractionsD3({
* target: "YourOwnDivId",
* });
* for (var pid=1;pid<=15;pid++)
* instance.addProtein({ "id":pid,"name":pid,"showLegend":false,"typeLegend":"id","organism":"human"+pid%3,"features":{"f1":"val1","f2":"val2","f3":"val3"}});
*
* for (var pid=1;pid<=30;pid++)
* instance.addInteraction(Math.floor((Math.random()*15)+1),Math.floor((Math.random()*15)+1) ,{score:Math.random()});
* instance.restart();
*/
Biojs.InteractionsD3 = Biojs.extend (
/** @lends Biojs.InteractionsD3# */
{
force:null,
vis:null,
interactions:[],
interactionsA:{},
proteins:[],
proteinsA:{},
node_drag:null,
foci: [],
organisms: {},
//Transformation values
tTranslate:null,
tScale:null,
constructor: function (options) {
var self = this;
self.force =null;
self.vis =null;
self.interactions=[];
self.interactionsA={};
self.proteins=[];
self.proteinsA={};
self.node_drag=null;
self.foci=[];
self.organisms={};
this._container = $("#"+self.opt.target);
this._container.empty();
$(this._container).addClass("graphNetwork");
var width = $(this._container).width(),
height = $(this._container).height();
if (self.opt.width.indexOf("%")!=-1)
width = width*(self.opt.width.substring(0, self.opt.width.length-1)*1)/100.0;
else
width=self.opt.width*1;
self.opt.width=width;
if (self.opt.height.indexOf("%")!=-1)
height = height*(self.opt.height.substring(0, self.opt.height.length-1)*1)/100.0;
else
height=self.opt.height*1;
self.opt.height=height;
this._container.width(width);
this._container.height(height);
self.zoom=d3.behavior.zoom().
scaleExtent([(self.opt.enableEdges)?1:0.05, 10])
.on("zoom", redraw);
self.vis = d3.select("#"+self.opt.target).append("svg")
.attr("width", width)
.attr("height", height)
.attr("pointer-events", "all")
.call(self.zoom)
.append('svg:g');
self.vis.append('svg:rect')
.attr('width', width*20)
.attr('height', height*20)
.attr('x', -width*10)
.attr('y', -height*10)
.attr('fill', 'white')
.attr('stroke','white');
self.rect= self.vis.append('svg:rect')
.attr("class", "frame")
.attr('width', width)
.attr('height', height)
.attr('fill', 'white')
.attr('stroke','white')
.attr("stroke-dasharray","5,5");
self.perspective=d3.select("#"+self.opt.target + " svg").append('svg:g');
function redraw(x,y,scaleP) {
var trans=null,scale=null;
if (typeof x!="undefined" && typeof y!="undefined"){
trans=[x,y];
scale = scaleP;
}else{
trans=d3.event.translate;
scale = d3.event.scale;
}
self.tTranslate=trans;
self.tScale=scale;
if (self.opt.enableEdges) {
if(scale<1)scale=1;
d3.behavior.zoom().scaleExtent([1, Infinity]);
if (trans[0]>0)trans[0]=0;
if (trans[1]>0)trans[1]=0;
var W = self.rect[0][0].width.animVal.value, H= self.rect[0][0].height.animVal.value;
var Ws = W*scale, Hs = H*scale;
if (Ws 1e-2) && (k < 50)) {
self.force.tick();
k = k + 1;
}
},
/**
*
* allows to resize the SVG element updating the gravity points
* @param {string} width value of width to be assign to the SVG
* @param {string} height value of height to be assign to the SVG
*
* @example
* instance.setSize(400,400);
* instance.restart();
*/
setSize:function(width,height){
var self =this;
self.opt.width=width;
self.opt.height=height;
d3.select("#"+self.opt.target+" svg")
.attr('width', width)
.attr('height', height);
d3.select("#"+self.opt.target+" .frame")
.attr('width', width)
.attr('height', height);
self._container.width(width);
self._container.height(height);
var numberOfOrganism =Object.keys(self.organisms).length;
self.foci=[];
for (var i=0; ib[1]){
return -1;
}else
return 1;
return a[0]-b[0];
});
},
_paintLegend:function(legend,type){
var self = this;
legend.filter(function(d) { return d[0]== "label" && d[1]==type; }).append("text")
.attr("x", self.opt.width - 6)
.attr("y", 7)
.attr("dy", ".35em")
.style("text-anchor", "end")
.style("font-size", "1.2em")
.text(type+":");
if (type.indexOf("Resize By")==0){
legend.filter(function(d) { return d[0]!="label" && d[1]==type; }).append("path")
.attr("class", "figure")
.attr("d", function(d) {
var h=2*self.opt.radius*Math.sqrt(d[0][2]);
return "M0,0L0,10M0,5L"+h+",5M"+h+",0L"+h+",10 ";
})
.attr("transform", function(d) {
return "translate(" + (self.opt.width - 18 - 2*self.opt.radius*Math.sqrt(d[0][2])) + "," + 0 + ")";
})
.style("fill", "transparent")
.style("stroke", "black");
legend.filter(function(d) { return d[0]!="label" && d[1]== type; }).append("text")
.attr("x", function(d) {
return (self.opt.width - 22 - 5*self.opt.radius);
})
.attr("y", 7)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return (d[0][1]*1.0).toFixed(2); });
}else{
legend.filter(function(d) { return d[0]!="label" && d[1]==type; }).append("rect")
.attr("x", self.opt.width - 18)
.attr("width", 13)
.attr("height", 13)
.style("fill", function(d,i) {
if (typeof d[2]== "undefined")
return self.colors[i];
return d[2];
});
legend.filter(function(d) { return d[0]!="label" && d[1]== type; }).append("text")
.attr("x", self.opt.width - 24)
.attr("y", 7)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d[0]; });
}
},
_paintLegends: function(){
var self = this;
var w=18 + self.longestLegend*7 + 10;
var legendBlock = self.perspective.insert("g",".link")
.attr("class", "legendBlock");
self._sortLegends();
legendBlock.append("rect")
.attr("x", self.opt.width -w)
.attr("height", 6 + self.legends.length *16)
.attr("width", w)
.style("fill", "#ddd")
.style("fill-opacity","0.4");
var legend = legendBlock.selectAll(".mainLegend")
.data(self.legends)
.enter().insert("g")
.attr("class", "mainLegend")
.attr("transform", function(d, i) {
return "translate(0," + (3 + i * 16) + ")";
});
for (var i=0; i< self.legendTypes.length; i++)
self._paintLegend(legend,self.legendTypes[i]);
},
longestLegend:4,
legendTypes:[],
/**
* Adds a legend to the graphic
*
* @example
* instance.addLegends(["Legend red"],"Color","#FF0000");
* instance.restart();
*/
addLegends:function(legends,type,color){
var self = this;
if (self.legends==null) self.legends=[],self.legendTypes=[];
if (legends==null) {
self.legends = null;
self.legendTypes=[];
self.longestLegend=4;
return;
}
if (type=="Resize By")
type = type+ " "+legends[0];
if (self.legendTypes.indexOf(type)==-1) {
self.legends.push(["label",type]);
self.legendTypes.push(type);
if (type.length>self.longestLegend)
self.longestLegend=type.length;
}
if (type.indexOf("Resize By")==0){ //is a size label
self.legends.push([legends,type]);
} else //is a color label
for (var i=0;iself.longestLegend)
self.longestLegend=legends[i].length;
}
},
_paintZoomLegend: function(){
var self = this;
var w=70;
var legendBlock = self.perspective.insert("g",".link")
.attr("class", "legendZoomBlock");
legendBlock.append("rect")
.attr("x", 0)
.attr("height", 22)
.attr("width", w)
.style("fill", "#fff")
.style("fill-opacity","0.0");
self.zoomLegend = legendBlock.selectAll(".mainLegend")
.data(["1.00 :",self.tScale])
.enter().insert("g")
.attr("class", "mainLegend")
.attr("transform", function(d,i){
return "translate("+i*30+",15)";
})
.append("text");
self._refreshZoomLegend();
// for (var i=0; i< self.legendTypes.length; i++)
// self._paintLegend(legend,self.legendTypes[i]);
},
_refreshZoomLegend: function(){
var self = this;
self.zoomLegend.text(function(d,i) {
return (i==0)?d:(self.tScale*1.0).toFixed(2);
}).attr("font-size", function(d,i){
return (i==0)?"1em":((self.tScale*1<1)?"0.8em":"1.5em");
}); },
/**
* Hides the elements on the graphic that match the selector.
* Check the CSS3 selectors documentation to build a selector string
*
* @param {string} selector a string to represent a set of elements. Check the CSS3 selectors documentation to build a selector string
*
* @example
* instance.hide("[id = node_10]");
*/
hide: function(selector){
var self=this;
self.vis.selectAll(selector).attr("visibility", 'hidden');
self.vis.selectAll(selector).selectAll(" .legend").attr("visibility", 'hidden');
},
/**
* Shows the elements on the graphic that match the selector.
* Check the CSS3 selectors documentation to build a selector string
*
* @param {string} selector a string to represent a set of elements. Check the CSS3 selectors documentation to build a selector string
*
* @example
* instance.show("[id = node_10]");
*/
show: function(selector){
var self=this;
self.vis.selectAll(selector).attr("visibility", 'visible');
self.vis.selectAll(selector).selectAll(" .legend").attr("visibility",function(d) { return (d.showLegend)?"visible":"hidden";});
},
/**
* Highlight the elements on the graphic that match the selector.
* Check the CSS3 selectors documentation to build a selector string
*
* @param {string} selector a string to represent a set of elements. Check the CSS3 selectors documentation to build a selector string
*
* @example
* instance.highlight("[id *= node_1]");
*/
highlight: function(selector){
var self=this;
self.vis.selectAll(selector).style("stroke", '#0f0');
},
/**
* Set the fill's color of the elements on the graphic that match the selector.
* Check the CSS3 selectors documentation to build a selector string
*
* @param {string} selector a string to represent a set of elements. Check the CSS3 selectors documentation to build a selector string
* @param {string} color a color in web format eg. #FF0000
*
* @example
* instance.setFillColor(".figure","#FF0000");
*/
setFillColor: function(selector,color){
var self=this;
self.vis.selectAll(selector).style("fill", color);
},
/**
* Set the stroke's color of the elements on the graphic that match the selector.
* Check the CSS3 selectors documentation to build a selector string
*
* @param {string} selector a string to represent a set of elements. Check the CSS3 selectors documentation to build a selector string
* @param {string} color a color in web format eg. #FF0000
*
* @example
* instance.setColor("[id *= node_2]","#FF0000");
*/
setColor: function(selector,color){
var self=this;
self.vis.selectAll(selector).style("stroke", color);
},
/**
* If the protein has a fixed position in the graphic it gets released, or viceversa other wise
*
* @param {string} protein the id of the protein to swap is position on the graphic
*
* @example
* instance.swapFixed("3");
*/
swapFixed: function(protein){
var self=this;
var nodes=self.force.nodes();
nodes.forEach(function(d, i) {
if (d.id==protein)
d.fixed = !d.fixed;
});
},
/**
* Shows the legend(id) of the protein
*
* @param {string} protein the id of the protein to swap the visibility of the legend
*
* @example
* instance.swapShowLegend("#node_5 .legend");
*/
showLegend: function(selector,typeLegend){
var self=this;
self.vis.selectAll(selector).selectAll(".legend").attr("visibility", "visible").text(function(d) {
d.typeLegend=typeLegend;
if (d.typeLegend=="id")
return d.id;
else //if (d.typeLegend.indexOf("features.")==0)
return d.features[d.typeLegend];
// else
// return d[d.typeLegend];
});
// self.restart();
},
/**
* Scales the area of a protein
*
* @param {string} protein the id of the protein to scale
* @param {integer} scale value to scale a node
*
* @example
* instance.setSizeScale("#figure_1",4);
*/
setSizeScale: function(selector,scale){
var self=this;
self.vis.selectAll(selector).attr("d", d3.svg.symbol()
.size(function(d) {
d.size=scale;
return (2*self.opt.radius)*(2*self.opt.radius)*scale;
})
.type(function(d) {
return d3.svg.symbolTypes[self._figuresOrder[self.organisms[d.organism]]];
})
);
},
/**
* Scales the size of the proteins which value has been modify by other means
*
* @param {string} selector a CSS3 selector to choose the nodes to resize
*
* @example
* for (var i=0;i