a b/exseek/templates/igv/main.html
1
2
<!DOCTYPE html>
3
<html lang="en">
4
5
<head>
6
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
7
    <meta charset="utf-8">
8
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
9
    <meta name="viewport" content="width=device-width, initial-scale=1">
10
    <meta name="description" content="">
11
    <meta name="author" content="">
12
    <link rel="shortcut icon" href="https://igv.org/web/img/favicon.ico">
13
    <title>Integrative Genomics Viewer</title>
14
15
    <!-- Bootstrap 4 Dependancies - jQuery | Popper -->
16
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
17
    <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>
18
19
    <!-- Latest compiled and minified CSS -->
20
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
21
22
    <!-- Optional theme -->
23
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
24
25
    <!-- Latest compiled and minified JavaScript -->
26
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
27
28
    <!-- Global styles -->
29
    <style type="text/css">
30
    body {
31
            font-size: 11px;
32
        }
33
    p.panel-title {
34
        font-size: 14px;
35
        font-weight: bold;
36
        margin-bottom: 0.5em;
37
    }
38
    #sample-list {
39
        list-style-type: none;
40
        margin: 0;
41
        padding: 0;
42
    }
43
    #sample-list li{
44
        margin: 0;
45
    }
46
    </style>
47
48
    <!-- IGV JS -->
49
    <script src="https://igv.org/web/release/2.1.0/dist/igv.min.js"></script>
50
51
</head>
52
53
<body>
54
55
<script type="text/javascript">
56
    var dataTrackTypes = {"wig": 0, "alignment": 0};
57
    var tracks = {{ tracks_json }};
58
59
    var groups = [
60
    {% for group_name, group in groups.items() %}
61
        {
62
            "name": "{{ group_name }}",
63
            "color": "{{ group.color }}",
64
            "show": "{{ group.show }}"
65
        } {% if not loop.last %},{% endif %}
66
    {% endfor %}
67
    ];
68
    var showStrand = {"+": true, "-": true};
69
    var showGroup = {};
70
    for(let i = 0; i < groups.length; i ++)
71
        showGroup[groups[i].name] = groups[i].show;
72
    var sampleIds = [];
73
    var showSample = {};
74
    for(name in tracks){
75
        if(tracks[name].type in dataTrackTypes){
76
            if(!(tracks[name].sample_id in showSample))
77
                sampleIds.push(tracks[name].sample_id);
78
            showSample[tracks[name].sample_id] = tracks[name].show;
79
        }
80
    }
81
82
    applyForAllTracks = function(func){
83
        for(name in tracks){
84
            if(tracks[name].type in dataTrackTypes){
85
                func(tracks[name]);
86
            }
87
        }
88
    };
89
90
    findGroup = function(name){
91
        var found = [];
92
        groups.forEach(function(group){
93
            if(group.name == name)
94
                found.push(group);
95
        });
96
        if(found.length > 0)
97
            return found[0];
98
    }
99
100
    createTrackNavigator = function(){
101
        var trackNav = document.getElementById("track-nav");
102
        onToggleTrack = function(event){
103
            tracks[event.target.name].show = event.target.checked;
104
            toggleTracks();
105
        };
106
        var checkbox, label, ul, li;
107
        var trackNavCheckBoxes = {};
108
        ul = document.createElement("ul");
109
        ul.setAttribute("id", "sample-list");
110
        for(name in tracks){
111
            li = document.createElement("li");
112
            checkbox = document.createElement("input");
113
            checkbox.setAttribute("type", "checkbox");
114
            checkbox.setAttribute("name", name);
115
            if(tracks[name].show)
116
                checkbox.setAttribute("checked", "");
117
            checkbox.addEventListener("change", onToggleTrack);
118
            li.appendChild(checkbox);
119
            trackNavCheckBoxes[name] = checkbox;
120
121
            label = document.createElement("label");
122
            label.setAttribute("for", name);
123
            label.appendChild(document.createTextNode(name));
124
            li.appendChild(label);
125
            ul.appendChild(li);
126
        }
127
        trackNav.appendChild(ul);
128
        return trackNavCheckBoxes;
129
    };
130
131
    createTrackGroupSelector = function(){
132
        var tr, td, checkbox, label, color;
133
        var groupTable = document.getElementById("track-groups-table");
134
        onShowTrackGroupChecked = function(event){
135
            groups[event.target.name].show = event.target.checked;
136
            showTrackGroup(event.target);
137
        }
138
        onTrackColorGroupChanged = function(event){
139
            groups[event.target.name].show = event.target.value;
140
            setTrackColorGroup(event.target);
141
        }
142
        for(let i = 0; i < groups.length; i ++){
143
            var group = groups[i];
144
            tr = document.createElement("tr");
145
146
            checkbox = document.createElement("input");
147
            checkbox.setAttribute("type", "checkbox");
148
            checkbox.setAttribute("name", "show-group-" + i);
149
            if(group.show)
150
                checkbox.setAttribute("checked", "");
151
            checkbox.addEventListener("change", showTrackGroup);
152
            
153
            label = document.createElement("label");
154
            label.setAttribute("for", "show-group-" + i);
155
            label.innerHTML = group.name;
156
157
            td = document.createElement("td");
158
            td.appendChild(checkbox);
159
            td.appendChild(label);
160
            tr.appendChild(td);
161
162
            color = document.createElement("input");
163
            color.setAttribute("type", "color");
164
            color.setAttribute("name", "track-color-group-" + i);
165
            color.setAttribute("value", group.color);
166
            color.addEventListener("change", setTrackColorGroup);
167
            td = document.createElement("td");
168
            td.appendChild(color);
169
            tr.appendChild(td);
170
171
            groupTable.appendChild(tr);
172
        }
173
    };
174
175
    updateTrackViews = function(items){
176
        for(name in tracks){
177
            if(!(tracks[name].type in dataTrackTypes))
178
                continue;
179
            igv.browser.findTracks("name", name).forEach(function(track){
180
                for(let i = 0; i < items.length; i ++){
181
                    if(items[i] == "height")
182
                        track.trackView.setTrackHeight(tracks[name].height);
183
                    else if(items[i] == "data_range"){
184
                        track.trackView.setDataRange(tracks[name].min, tracks[name].max, tracks[name].autoscale);
185
                        if(tracks[name].autoscale)
186
                            track.trackView.updateViews();
187
                    }else if(items[i] == "color"){
188
                        track.trackView.setColor(tracks[name].color);
189
                    }else{
190
                        track.trackView.track.logScale = tracks[name].logScale;
191
                        track.trackView.updateViews();
192
                    }
193
                }
194
            });
195
        }
196
    };
197
198
    // show or remove tracks according to track config
199
    toggleTracks = function(){
200
        var currentTrack;
201
        var showTrack;
202
        var track;
203
        for(name in tracks){
204
            track = tracks[name];
205
            currentTrack = igv.browser.findTracks("name", name);
206
            if(track.type in dataTrackTypes){
207
                showTrack = track.show && showGroup[track.group];
208
                if(track.type == "wig"){
209
                    showTrack = showTrack && showStrand[track.strand];
210
                }
211
            }else{
212
                showTrack = track.show;
213
            }
214
            if(showTrack && (currentTrack.length == 0)){
215
                igv.browser.loadTrack(track);
216
            }else if(!showTrack && (currentTrack.length > 0)){
217
                igv.browser.removeTrackByName(name);
218
            }
219
        }
220
    };
221
222
    showTracksWithStrand = function(element){
223
        var name, group;
224
        var strand = (element.name == "show-plus-track")? "+" : "-";
225
        showStrand[strand] = element.checked;
226
        toggleTracks();
227
    };
228
229
    showTrackGroup = function(element){
230
        var c = element.name.split("-");
231
        var group = groups[parseInt(c[c.length - 1])];
232
        showGroup[group.name] = element.checked;
233
        group.show = element.checked;
234
        toggleTracks();
235
    };
236
237
    setTrackColorGroup = function(element){
238
        var c = element.name.split("-");
239
        var group = groups[parseInt(c[c.length - 1])].name;
240
        var i;
241
        var trackView;
242
        for(name in tracks){
243
            if(tracks[name].group == group){
244
                tracks[name].color = element.value;
245
            }
246
        }
247
        updateTrackViews(["color"]);
248
    };
249
</script>
250
251
<div id="left-panel" style="position: fixed; margin-left: 5px; width: 300px; height: 100%; overflow: scroll">
252
    <div id="config-panel" style="border:1px solid lightgray; padding-left: 10px; padding-top: 10px;padding-bottom: 10px">
253
        <p class="panel-title">Track configuation</p>
254
        <table id="table-config">
255
            <tr><td>Minimum value: </td><td><input type="text" id="config-data-min" value="0" style="width: 50px"></td></tr>
256
            <tr><td>Maximum value: </td><td><input type="text" id="config-data-max" value="100" style="width: 50px"></td></tr>
257
            <tr><td>Autoscale: </td><td><input type="checkbox" id="config-autoscale" checked></td></tr>
258
            <tr><td>Track height: </td><td><input type="text" id="config-height" value="25" style="width: 50px"></td></tr>
259
        </table>
260
    </div>
261
    <div id="sequence-panel" style="border:1px solid lightgray; padding-left: 10px; padding-top: 10px;padding-bottom: 10px; margin-top: 5px">
262
        <p class="panel-title">Sequence</p> 
263
        <p id="sequence-locus"></p>
264
        <textarea id="sequence-box" rows="4" cols="40" readonly></textarea><br>
265
        <input type="checkbox" id="reverse-sequence" name="reverse-sequence" onchange="showSequence()">
266
        <label for="reverse-sequence">Reverse complement</label><br>
267
        <input type="button" id="show-sequence" value="Show sequence">
268
        <input type="button" id="copy-sequence" value="Copy sequence">
269
    </div>
270
    <div id="track-selection" style="border:1px solid lightgray; padding-left: 10px; padding-top: 10px;padding-bottom: 10px; margin-top: 5px">
271
        <p class="panel-title">Track selection</p>
272
        <input type="checkbox" onchange="showTracksWithStrand(this)" name="show-plus-track" checked>
273
        <label for="show-plus-track">Show tracks in + strand</label><br>
274
        <input type="checkbox" onchange="showTracksWithStrand(this)" name="show-minus-strand" checked>
275
        <label for="show-minus-track">Show tracks in - strand</label><br>
276
    </div>
277
    <div id="track-groups" style="border:1px solid lightgray; padding-left: 10px; padding-top: 10px;padding-bottom: 10px; margin-top: 5px">
278
        <p class="panel-title">Track groups</p>
279
        <table id="track-groups-table" border="0">
280
        {% for name, group in groups.items() %}
281
            <tr>
282
            <td><input type="checkbox" onchange="showTrackGroup(this)" name="show-group-{{ loop.index0 }}" checked>
283
            <label for="show-group-{{ loop.index0 }}">{{ name }}</label></td>
284
            <td><input type="color" onchange="setTrackColorGroup(this)" name="track-color-group-{{ loop.index0 }}" value="{{ group.color }}"></td>
285
            </tr>
286
        {% endfor %}
287
        </table>
288
    </div>
289
    <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;">
290
        <p class="panel-title">Track navigator</p>
291
    </div>
292
</div>
293
294
<div id="igv-div" style="margin-left: 310px; padding-top: 10px;padding-bottom: 10px; border:1px solid lightgray"></div>
295
296
<script type="text/javascript">
297
    document.addEventListener("DOMContentLoaded", function () {
298
299
        var options = {{ options_json }};
300
301
        if(window.location.hash.length > 1){
302
            options["locus"] = window.location.hash.substr(1);
303
        }
304
305
        var igvDiv = document.getElementById("igv-div");
306
307
        igv.createBrowser(igvDiv, options)
308
            .then(function (browser) {
309
                console.log("Created IGV browser");
310
                for(name in tracks){
311
                    if(tracks[name].show){
312
                        browser.loadTrack(tracks[name]);
313
                    }
314
                }
315
        });
316
        
317
318
        createTrackNavigator();
319
320
        showSequence = function(){
321
            var complement = {'A': 'T', 'C': 'G', 'G': 'C', 'T': 'A'};
322
            var referenceFrame;
323
324
            referenceFrame = igv.browser.genomicStateList[0].referenceFrame;
325
            var start = referenceFrame.start;
326
            var chrName = referenceFrame.chrName;
327
            var end = referenceFrame.start + 
328
                referenceFrame.bpPerPixel * (igv.browser.viewportContainerWidth() / igv.browser.genomicStateList.length);
329
            end = Math.floor(end);
330
            var locus;
331
            if((end - start) > 100000){
332
                alert("Sequence too long");
333
                return;
334
            }
335
336
            igv.browser.genome.sequence.getSequence(chrName, start, end).then(function(seq){
337
                seq = seq.toUpperCase();
338
                locus = "Locus: " + chrName + ":" + start + "-" + end;
339
                document.getElementById("sequence-locus").innerHTML = locus;
340
                if(document.getElementById("reverse-sequence").checked){
341
                    seq = seq.split("").map(function(cv){
342
                        return complement[cv];
343
                    }).join("");
344
                    seq = seq.split("").reverse().join("");
345
                }
346
                document.getElementById("sequence-box").value = seq;
347
            });
348
        }
349
350
        
351
        document.getElementById("show-sequence").addEventListener("click", function(event){
352
            showSequence();
353
        });
354
355
        document.getElementById("copy-sequence").addEventListener("click", function(event){
356
            showSequence();
357
            document.getElementById("sequence-box").select();
358
            document.execCommand('copy');
359
        });
360
361
        document.getElementById("config-data-min").addEventListener("change", function(event){
362
            var value = parseFloat(event.target.value);
363
            if(isNaN(value)){
364
                alert("Error: minimum value should be a number");
365
            }else{
366
                applyForAllTracks(function(track){ track.min = value; });
367
                updateTrackViews(["data_range"]);
368
            }
369
        });
370
371
        document.getElementById("config-data-max").addEventListener("change", function(event){
372
            var value = parseFloat(event.target.value);
373
            if(isNaN(value)){
374
                alert("Error: maximum value should be a number");
375
            }else{
376
                applyForAllTracks(function(track){ track.max = value; });
377
                updateTrackViews(["data_range"]);
378
            }
379
        });
380
381
        document.getElementById("config-autoscale").addEventListener("change", function(event){
382
            applyForAllTracks(function(track){
383
                if(!track.autoscale){
384
                    track.min = parseFloat(document.getElementById("config-data-min").value);
385
                    track.max = parseFloat(document.getElementById("config-data-max").value);
386
                }
387
                track.autoscale = event.target.checked;
388
            });
389
            updateTrackViews(["data_range"]);
390
        });
391
392
        document.getElementById("config-height").addEventListener("change", function(event){
393
            var value = parseInt(event.target.value);
394
            if(isNaN(value)){
395
                alert("Error: track height should be a number");
396
            }else {
397
                applyForAllTracks(function(track){ track.height = event.target.value; });
398
                updateTrackViews(["height"]);
399
            }
400
        });
401
402
    });
403
404
</script>
405
406
</body>
407
408
</html>