xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-polyfills/0.1.41/polyfill.min.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
.extra-tip{
position: fixed;
max-width: 200px;
left: -9999px;
background-color: white;
border: 1px solid gray;
border-radius: 5px;
font: 12px;
overflow-wrap: break-word;
word-wrap: break-word;
}
.extra-tip > .extra-tip-content{
padding: 5px;
}
.extra-tip .extra-tip-content a{
text-decoration: none;
}
.extra-tip > .extra-tip-arrow{
position: absolute;
height: 15px;
width: 100%;
top: 100%;
/* indicate area when needed */
background-color: rgba(200,100,0,0);
}
.extra-tip .extra-tip-wrapper{
position: absolute;
left: 50%;
}
.extra-tip .extra-tip-wrapper:before,
.extra-tip .extra-tip-wrapper:after{
content: " ";
width: 0;
height: 0;
top: 100%;
position: absolute;
border: solid transparent;
}
.extra-tip .extra-tip-wrapper:before{
border-width: 10px;
border-top-color: gray;
left: -8px;
}
.extra-tip .extra-tip-wrapper:after{
border-width: 8px;
border-top-color: white;
left: -6px;
}
table{ border-collapse: collapse; }
table td{ border: 1px solid #000; padding: 5px; }
.highlight{ background-color: #fac; }
</style>
</head>
<body>
<table id="mutated">
<thead></thead>
<tbody>
<tr>
<td>TCGA-C5-A2LT-01A</td>
<td>dolor sit amet</td>
<td>consectetur adipisicing </td>
</tr>
<tr>
<td>TCGA-FU-A3HZ-01A</td>
<td>architecto beatae cum</td>
<td>tenetur quisquam</td>
</tr>
<tr>
<td>TCGA-FU-A23L-01A</td>
<td>nam sunt repellendus</td>
<td>hic inventore nisi</td>
</tr>
<tr>
<td>TCGA-EK-A2PK-01A</td>
<td>Obcaecati ducimus</td>
<td>ab molestias sit</td>
</tr>
<tr>
<td>TCGA-VS-AA62-01A</td>
<td>Obcaecati ducimus</td>
<td>ab molestias sit</td>
</tr>
</tbody>
</table>
<div id="container"></div>
<script>
d3.svg.mutate = function(id, rData, tableID){
var svgWidth = 650,
svgHeight = 180,
margin = {top: 20, right: 70, bottom: 20, left: 50},
width = svgWidth - margin.left - margin.right,
// height = svgHeight - margin.top - margin.bottom,
pointPlotHeight = 90,
barPlotHeight = 30,
pointRadius = 4,
barHeight1 = 12,
barHeight2 = barHeight1 + 10,
widthPerLetter,
tooltip,
mutateTypeColors = {
"inframe" : "#8b4513",
"others" : "#8b00c9",
"splice site" : "#393b79",
"frameshift" : "#000000",
"missense" : "#008000"
},
pfamColors = [
"#6b6ecf", "#9c9ede", "#637939",
"#8ca252", "#b5cf6b", "#cedb9c", "#8c6d31", "#bd9e39",
"#e7ba52", "#e7cb94", "#843c39", "#ad494a", "#d6616b",
"#e7969c", "#7b4173", "#a55194", "#ce6dbd", "#de9ed6",
"#3182bd", "#6baed6", "#9ecae1", "#c6dbef", "#e6550d",
"#fd8d3c", "#fdae6b", "#fdd0a2", "#31a354", "#74c476",
"#a1d99b", "#c7e9c0", "#756bb1", "#9e9ac8", "#bcbddc",
"#393b79", "#5254a3"
];
// auto calculate letter width, if fail assign it with 11
try{ widthPerLetter = averageLetterWith(); }catch(err){ widthPerLetter = 11; }
var table = document.getElementById(tableID);
var xScale = d3.scale.linear().range([0, width]),
yScale = d3.scale.linear().range([0, pointPlotHeight]),
xAxis = d3.svg.axis().orient("bottom").scale(xScale),
yAxis = d3.svg.axis().orient("left");
var svg = d3.select("#" + id)
.append("svg")
.attr({
width : svgWidth,
height : svgHeight,
version : 1.1,
"shape-rendering" : "crispEdges",
xmlns : "https://www.w3.org/2000/svg"
})
.style("font", "12px arial, sans-serif");
var gPlotMain = svg.append("g").attr({
class: "gPlotMain",
transform: "translate(" + [margin.left, margin.top] + ")"
}),
gYAxis = gPlotMain.append("g").attr({
class: "axis gYAxis",
transform: "translate(" + -pointRadius +",0)"
}),
gPoints = gPlotMain.append("g").attr({
class: "gPoints",
transform: "translate(0," + pointPlotHeight + ")"
}),
gBars = gPlotMain.append("g").attr({
class: "gBars",
transform: "translate(0," + pointPlotHeight + ")"
}),
gXAxis = gPlotMain.append("g").attr({
class: "axis gXAxis",
transform: "translate(0," + (pointPlotHeight + barPlotHeight) + ")"
});
// data processing
var yMax = (rData.point && rData.point.length > 0) ?
d3.max(rData.point, function(d){ return d.y; }) : 1,
xMax = d3.max(rData.domain, function(d){ return d.end; });
// Forcing all search sample strings to upper case format
// before comparing with table cell textContent
try{ // in case there is no point at all: rData.point === undefined
rData.point.forEach(function(d){
d.sample = d.sample.map(function(e){ return e.toUpperCase(); });
});
}catch(error){
console.log("rData.point: " + error.message + " :-)");
}
xScale.domain([0, xMax]);
yScale.domain([0, yMax]);
yAxis.scale(yScale.copy().range([pointPlotHeight, 0]));
gXAxis.call(xAxis).call(styleAxis).call(xAxisMaxLabel);
if(yMax <= 9){ yAxis.ticks(yMax); }else{ yAxis.ticks(5); }
gYAxis.call(yAxis).call(styleAxis)
.append("text")
.attr({
x : -pointPlotHeight * 0.5,
y : -25,
transform : "rotate(-90)",
"text-anchor" : "middle"
})
.text("# Mutations");
// begin to draw
if(rData.point && rData.point.length > 0){
gPoints
.selectAll("g")
.data(rData.point)
.enter()
.append("g")
.each(function(d){
var g = d3.select(this);
g.attr("transform",
"translate(" + [xScale(d.x), -yScale(d.y)] + ")");
g.append("line").attr({
y2 : yScale(d.y) + 0.5 * barPlotHeight,
stroke : "#000",
"stroke-width": 1
});
g.append("circle").attr({
r : pointRadius,
fill : mutateTypeColors[d.mutation.toLowerCase()] || "red",
"shape-rendering" : "auto"
})
.on("mouseenter", pointOnMouseEnter)
.on("mouseleave", pointOnMouseLeave);
});
} // END IF :-)
var longBanner, otherBannners = [], otherBannerType = [];
rData.domain.forEach(function(d){
if(d.end === xMax && d.start === 0){
longBanner = d;
}else{ otherBannners.push(d); }
});
otherBannners
.sort(function(a, b){ return a.start - b.start; })
.forEach(function(d){
if(otherBannerType.indexOf(d.pfam) === -1){
otherBannerType.push(d.pfam);
}
});
// domain and other protein banners:
gBars
.append("g")
.datum(longBanner)
.on("mouseenter", barOnMouseEnter)
.on("mouseleave", barOnMouseLeave)
.attr("class", "domain")
.attr("transform", "translate(" +
[0, (barPlotHeight - barHeight1) * 0.5 ] + ")")
.append("rect")
.attr({
width : xScale(xMax),
height : barHeight1,
fill : "#BABDB6",
stroke : "#000",
"stroke-width": 0
});
gBars
.append("g")
.selectAll("g")
.data(otherBannners)
.enter()
.append("g")
.on("mouseenter", barOnMouseEnter)
.on("mouseleave", barOnMouseLeave)
.attr("class", "protein")
.each(function(d, i){
var g = d3.select(this), barWidth = xScale(d.end - d.start);
g.attr("transform", "translate(" +
[xScale(d.start), (barPlotHeight - barHeight2) * 0.5] + ")");
g.append("rect")
.attr({
width : barWidth,
height : barHeight2,
fill : pfamColors[otherBannerType.indexOf(d.pfam)],
stroke : "#000",
"stroke-width": 0
});
g.append("text")
.text(getLabel(barWidth, d.desc, widthPerLetter))
.attr({
x : 0.5 * barWidth,
y : 0.5 * barHeight2,
fill : "#fff",
"text-anchor" : "middle",
"alignment-baseline": "central",
"pointer-events" : "none"
});
});
tooltip = document.getElementById("extra-tip");
if(!tooltip){
tooltip = d3.select("body")
.append("div")
.attr({id: "extra-tip", class: "extra-tip"})
.html('<div class="extra-tip-content"></div><div class="extra-tip-arrow"><div class="extra-tip-wrapper"></div></div>')
.on("mouseleave", function(){ this.style.left = "-9999px"; })
.node();
}else{
d3.select(tooltip)
.on("mouseleave", function(){ this.style.left = "-9999px"; });
}
function pointOnMouseEnter(d){
var x = d3.event.clientX, y = d3.event.clientY;
var html = "<strong>" + d.y + " mutation</strong></br>" +
"AA change: " + d.change;
d3.select(tooltip).select("div:first-child").html(html);
d3.select(this).transition().attr("r", pointRadius * 1.5);
tooltip.style.left = x - 0.5 * tooltip.offsetWidth + "px";
tooltip.style.top = y - tooltip.offsetHeight - 20 + "px";
// highlight table row if matched
if(table && d.sample && d.sample.length){
d3.select(table)
.selectAll("tr > td:first-child")
.each(function(){
if(d.sample.indexOf(this.textContent.trim().toUpperCase()) !== -1){
this.parentNode.classList.add("highlight");
}
});
}
}
function pointOnMouseLeave(d){
d3.select(this).transition().attr("r", pointRadius);
tooltip.style.left = "-99999px";
// remove highlight class from all table row
if(table){
d3.select(table)
.selectAll("tr")
.classed("highlight", false);
}
}
function barOnMouseEnter(d){
var x = d3.event.clientX,
y = d3.event.clientY,
rCoordinate = d3.mouse(this);
d3.select(tooltip)
.select("div:first-child")
.html(d.desc + " (" + d.start + " - " + d.end + ") <br />");
d.link.forEach(function(e){
d3.select(tooltip)
.select("div:first-child")
.append("a")
.text(" " + e.name + " ")
.attr({ href: e.address, target: "_blank" })
.style({ display: "inline-block", margin: "2px" });
});
d3.select(this).select("rect").attr("stroke-width", 1);
if(this.classList.contains("domain")){
tooltip.style.left = x - (0.5 * tooltip.offsetWidth) + "px";
}else{
tooltip.style.left = (x - rCoordinate[0]) +
(0.5 * xScale(d.end - d.start)) -
(0.5 * tooltip.offsetWidth) - 2 + "px";
}
tooltip.style.top = (y - rCoordinate[1]) -
tooltip.offsetHeight - 10 + "px";
}
function barOnMouseLeave(d){
d3.select(this).select("rect").attr("stroke-width", 0);
if(d3.event.toElement.tagName !== "DIV"){
tooltip.style.left = "-99999px";
}
}
function styleAxis(s){
s.select(".domain")
.attr({ fill: "none", stroke: "#000", "stroke-width": 1});
s.selectAll(".tick line")
.attr({ stroke: "#000", "stroke-width": 1 });
}
function xAxisMaxLabel(s){
var lastTick = s.select("path").node().previousElementSibling,
lastTickNumber = xScale.ticks().slice().pop();
// Remove the last tick if the max-label will collision with it.
if(xScale(xMax - lastTickNumber) < (widthPerLetter * lastTickNumber.toString().length / 2)){
d3.select(lastTick).select("text").remove();
}
// minic the normal tick layout
s.insert("g", "path")
.attr({
class: "tick",
transform: "translate(" + xScale.range()[1] + ",0)"
})
.append("text")
.text(xMax + "aa")
.attr({
y : yAxis.outerTickSize() + yAxis.tickPadding(),
dy: ".71em",
// "text-anchor": "middle"
});
}
function averageLetterWith(){
var str = "WWWWWWMMMMMMEUMM",
text = svg.append("text").html(str),
width = Math.ceil(text.node().getBBox().width / str.length);
text.remove();
return width;
}
function getLabel(barWidth, str, letterWidth, maxTextLength){
if(!maxTextLength){ maxTextLength = 11; }
if(barWidth < letterWidth * 1.5){ return ""; }
else{
var length = Math.floor(barWidth / letterWidth);
return str.slice(0, Math.min(length, maxTextLength));
}
}
}; // mutate END
d3.json("protein_seq.json", function(error, rData){
if(error){ throw error; }
d3.svg.mutate("container", rData, "mutated");
});
</script>
</body>
https://cdnjs.cloudflare.com/ajax/libs/js-polyfills/0.1.41/polyfill.min.js
https://d3js.org/d3.v3.min.js