<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<link rel="shortcut icon" href="https://igv.org/web/img/favicon.ico">
<title>Integrative Genomics Viewer</title>
<!-- Bootstrap 4 Dependancies - jQuery | Popper -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<!-- Global styles -->
<style type="text/css">
body {
font-size: 11px;
}
p.panel-title {
font-size: 14px;
font-weight: bold;
margin-bottom: 0.5em;
}
#sample-list {
list-style-type: none;
margin: 0;
padding: 0;
}
#sample-list li{
margin: 0;
}
</style>
<!-- IGV JS -->
<script src="https://igv.org/web/release/2.1.0/dist/igv.min.js"></script>
</head>
<body>
<script type="text/javascript">
var dataTrackTypes = {"wig": 0, "alignment": 0};
var tracks = {{ tracks_json }};
var groups = [
{% for group_name, group in groups.items() %}
{
"name": "{{ group_name }}",
"color": "{{ group.color }}",
"show": "{{ group.show }}"
} {% if not loop.last %},{% endif %}
{% endfor %}
];
var showStrand = {"+": true, "-": true};
var showGroup = {};
for(let i = 0; i < groups.length; i ++)
showGroup[groups[i].name] = groups[i].show;
var sampleIds = [];
var showSample = {};
for(name in tracks){
if(tracks[name].type in dataTrackTypes){
if(!(tracks[name].sample_id in showSample))
sampleIds.push(tracks[name].sample_id);
showSample[tracks[name].sample_id] = tracks[name].show;
}
}
applyForAllTracks = function(func){
for(name in tracks){
if(tracks[name].type in dataTrackTypes){
func(tracks[name]);
}
}
};
findGroup = function(name){
var found = [];
groups.forEach(function(group){
if(group.name == name)
found.push(group);
});
if(found.length > 0)
return found[0];
}
createTrackNavigator = function(){
var trackNav = document.getElementById("track-nav");
onToggleTrack = function(event){
tracks[event.target.name].show = event.target.checked;
toggleTracks();
};
var checkbox, label, ul, li;
var trackNavCheckBoxes = {};
ul = document.createElement("ul");
ul.setAttribute("id", "sample-list");
for(name in tracks){
li = document.createElement("li");
checkbox = document.createElement("input");
checkbox.setAttribute("type", "checkbox");
checkbox.setAttribute("name", name);
if(tracks[name].show)
checkbox.setAttribute("checked", "");
checkbox.addEventListener("change", onToggleTrack);
li.appendChild(checkbox);
trackNavCheckBoxes[name] = checkbox;
label = document.createElement("label");
label.setAttribute("for", name);
label.appendChild(document.createTextNode(name));
li.appendChild(label);
ul.appendChild(li);
}
trackNav.appendChild(ul);
return trackNavCheckBoxes;
};
createTrackGroupSelector = function(){
var tr, td, checkbox, label, color;
var groupTable = document.getElementById("track-groups-table");
onShowTrackGroupChecked = function(event){
groups[event.target.name].show = event.target.checked;
showTrackGroup(event.target);
}
onTrackColorGroupChanged = function(event){
groups[event.target.name].show = event.target.value;
setTrackColorGroup(event.target);
}
for(let i = 0; i < groups.length; i ++){
var group = groups[i];
tr = document.createElement("tr");
checkbox = document.createElement("input");
checkbox.setAttribute("type", "checkbox");
checkbox.setAttribute("name", "show-group-" + i);
if(group.show)
checkbox.setAttribute("checked", "");
checkbox.addEventListener("change", showTrackGroup);
label = document.createElement("label");
label.setAttribute("for", "show-group-" + i);
label.innerHTML = group.name;
td = document.createElement("td");
td.appendChild(checkbox);
td.appendChild(label);
tr.appendChild(td);
color = document.createElement("input");
color.setAttribute("type", "color");
color.setAttribute("name", "track-color-group-" + i);
color.setAttribute("value", group.color);
color.addEventListener("change", setTrackColorGroup);
td = document.createElement("td");
td.appendChild(color);
tr.appendChild(td);
groupTable.appendChild(tr);
}
};
updateTrackViews = function(items){
for(name in tracks){
if(!(tracks[name].type in dataTrackTypes))
continue;
igv.browser.findTracks("name", name).forEach(function(track){
for(let i = 0; i < items.length; i ++){
if(items[i] == "height")
track.trackView.setTrackHeight(tracks[name].height);
else if(items[i] == "data_range"){
track.trackView.setDataRange(tracks[name].min, tracks[name].max, tracks[name].autoscale);
if(tracks[name].autoscale)
track.trackView.updateViews();
}else if(items[i] == "color"){
track.trackView.setColor(tracks[name].color);
}else{
track.trackView.track.logScale = tracks[name].logScale;
track.trackView.updateViews();
}
}
});
}
};
// show or remove tracks according to track config
toggleTracks = function(){
var currentTrack;
var showTrack;
var track;
for(name in tracks){
track = tracks[name];
currentTrack = igv.browser.findTracks("name", name);
if(track.type in dataTrackTypes){
showTrack = track.show && showGroup[track.group];
if(track.type == "wig"){
showTrack = showTrack && showStrand[track.strand];
}
}else{
showTrack = track.show;
}
if(showTrack && (currentTrack.length == 0)){
igv.browser.loadTrack(track);
}else if(!showTrack && (currentTrack.length > 0)){
igv.browser.removeTrackByName(name);
}
}
};
showTracksWithStrand = function(element){
var name, group;
var strand = (element.name == "show-plus-track")? "+" : "-";
showStrand[strand] = element.checked;
toggleTracks();
};
showTrackGroup = function(element){
var c = element.name.split("-");
var group = groups[parseInt(c[c.length - 1])];
showGroup[group.name] = element.checked;
group.show = element.checked;
toggleTracks();
};
setTrackColorGroup = function(element){
var c = element.name.split("-");
var group = groups[parseInt(c[c.length - 1])].name;
var i;
var trackView;
for(name in tracks){
if(tracks[name].group == group){
tracks[name].color = element.value;
}
}
updateTrackViews(["color"]);
};
</script>
<div id="left-panel" style="position: fixed; margin-left: 5px; width: 300px; height: 100%; overflow: scroll">
<div id="config-panel" style="border:1px solid lightgray; padding-left: 10px; padding-top: 10px;padding-bottom: 10px">
<p class="panel-title">Track configuation</p>
<table id="table-config">
<tr><td>Minimum value: </td><td><input type="text" id="config-data-min" value="0" style="width: 50px"></td></tr>
<tr><td>Maximum value: </td><td><input type="text" id="config-data-max" value="100" style="width: 50px"></td></tr>
<tr><td>Autoscale: </td><td><input type="checkbox" id="config-autoscale" checked></td></tr>
<tr><td>Track height: </td><td><input type="text" id="config-height" value="25" style="width: 50px"></td></tr>
</table>
</div>
<div id="sequence-panel" style="border:1px solid lightgray; padding-left: 10px; padding-top: 10px;padding-bottom: 10px; margin-top: 5px">
<p class="panel-title">Sequence</p>
<p id="sequence-locus"></p>
<textarea id="sequence-box" rows="4" cols="40" readonly></textarea><br>
<input type="checkbox" id="reverse-sequence" name="reverse-sequence" onchange="showSequence()">
<label for="reverse-sequence">Reverse complement</label><br>
<input type="button" id="show-sequence" value="Show sequence">
<input type="button" id="copy-sequence" value="Copy sequence">
</div>
<div id="track-selection" style="border:1px solid lightgray; padding-left: 10px; padding-top: 10px;padding-bottom: 10px; margin-top: 5px">
<p class="panel-title">Track selection</p>
<input type="checkbox" onchange="showTracksWithStrand(this)" name="show-plus-track" checked>
<label for="show-plus-track">Show tracks in + strand</label><br>
<input type="checkbox" onchange="showTracksWithStrand(this)" name="show-minus-strand" checked>
<label for="show-minus-track">Show tracks in - strand</label><br>
</div>
<div id="track-groups" style="border:1px solid lightgray; padding-left: 10px; padding-top: 10px;padding-bottom: 10px; margin-top: 5px">
<p class="panel-title">Track groups</p>
<table id="track-groups-table" border="0">
{% for name, group in groups.items() %}
<tr>
<td><input type="checkbox" onchange="showTrackGroup(this)" name="show-group-{{ loop.index0 }}" checked>
<label for="show-group-{{ loop.index0 }}">{{ name }}</label></td>
<td><input type="color" onchange="setTrackColorGroup(this)" name="track-color-group-{{ loop.index0 }}" value="{{ group.color }}"></td>
</tr>
{% endfor %}
</table>
</div>
<div id="track-nav" style="border:1px solid lightgray; padding-left: 10px; padding-top: 10px;padding-bottom: 10px; margin-top: 5px; overflow: scroll; height:400px;">
<p class="panel-title">Track navigator</p>
</div>
</div>
<div id="igv-div" style="margin-left: 310px; padding-top: 10px;padding-bottom: 10px; border:1px solid lightgray"></div>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function () {
var options = {{ options_json }};
if(window.location.hash.length > 1){
options["locus"] = window.location.hash.substr(1);
}
var igvDiv = document.getElementById("igv-div");
igv.createBrowser(igvDiv, options)
.then(function (browser) {
console.log("Created IGV browser");
for(name in tracks){
if(tracks[name].show){
browser.loadTrack(tracks[name]);
}
}
});
createTrackNavigator();
showSequence = function(){
var complement = {'A': 'T', 'C': 'G', 'G': 'C', 'T': 'A'};
var referenceFrame;
referenceFrame = igv.browser.genomicStateList[0].referenceFrame;
var start = referenceFrame.start;
var chrName = referenceFrame.chrName;
var end = referenceFrame.start +
referenceFrame.bpPerPixel * (igv.browser.viewportContainerWidth() / igv.browser.genomicStateList.length);
end = Math.floor(end);
var locus;
if((end - start) > 100000){
alert("Sequence too long");
return;
}
igv.browser.genome.sequence.getSequence(chrName, start, end).then(function(seq){
seq = seq.toUpperCase();
locus = "Locus: " + chrName + ":" + start + "-" + end;
document.getElementById("sequence-locus").innerHTML = locus;
if(document.getElementById("reverse-sequence").checked){
seq = seq.split("").map(function(cv){
return complement[cv];
}).join("");
seq = seq.split("").reverse().join("");
}
document.getElementById("sequence-box").value = seq;
});
}
document.getElementById("show-sequence").addEventListener("click", function(event){
showSequence();
});
document.getElementById("copy-sequence").addEventListener("click", function(event){
showSequence();
document.getElementById("sequence-box").select();
document.execCommand('copy');
});
document.getElementById("config-data-min").addEventListener("change", function(event){
var value = parseFloat(event.target.value);
if(isNaN(value)){
alert("Error: minimum value should be a number");
}else{
applyForAllTracks(function(track){ track.min = value; });
updateTrackViews(["data_range"]);
}
});
document.getElementById("config-data-max").addEventListener("change", function(event){
var value = parseFloat(event.target.value);
if(isNaN(value)){
alert("Error: maximum value should be a number");
}else{
applyForAllTracks(function(track){ track.max = value; });
updateTrackViews(["data_range"]);
}
});
document.getElementById("config-autoscale").addEventListener("change", function(event){
applyForAllTracks(function(track){
if(!track.autoscale){
track.min = parseFloat(document.getElementById("config-data-min").value);
track.max = parseFloat(document.getElementById("config-data-max").value);
}
track.autoscale = event.target.checked;
});
updateTrackViews(["data_range"]);
});
document.getElementById("config-height").addEventListener("change", function(event){
var value = parseInt(event.target.value);
if(isNaN(value)){
alert("Error: track height should be a number");
}else {
applyForAllTracks(function(track){ track.height = event.target.value; });
updateTrackViews(["height"]);
}
});
});
</script>
</body>
</html>