282 lines
8.9 KiB
Vue
282 lines
8.9 KiB
Vue
|
|
<template>
|
||
|
|
<div id="app">
|
||
|
|
<main role="main" class="container-fluid">
|
||
|
|
<div style="padding-top: 64px">
|
||
|
|
<div>
|
||
|
|
<ul
|
||
|
|
class="navbar-nav mr-auto"
|
||
|
|
style="list-style:none; padding-left:0; margin:0;"
|
||
|
|
>
|
||
|
|
<li
|
||
|
|
class="nav-item dropdown"
|
||
|
|
style="position: relative; display: inline-block;"
|
||
|
|
>
|
||
|
|
<a
|
||
|
|
href="#"
|
||
|
|
id="igv-example-api-dropdown"
|
||
|
|
@click.prevent="toggleDropdown"
|
||
|
|
aria-haspopup="true"
|
||
|
|
:aria-expanded="isDropdownOpen.toString()"
|
||
|
|
style="color: black; cursor: pointer; user-select: none;"
|
||
|
|
>Tracks</a
|
||
|
|
>
|
||
|
|
<ul
|
||
|
|
v-show="isDropdownOpen"
|
||
|
|
class="dropdown-menu"
|
||
|
|
style="width:350px; position: absolute; top: 100%; left: 0; background: white; border: 1px solid #ccc; box-shadow: 0 2px 5px rgba(0,0,0,0.15); padding: 5px 0; margin: 0; list-style:none; z-index: 1000;"
|
||
|
|
>
|
||
|
|
<li>
|
||
|
|
<a
|
||
|
|
href="#"
|
||
|
|
@click.prevent="loadCopyNumberTrack"
|
||
|
|
style="display:block; padding: 8px 20px; color: black; text-decoration: none;"
|
||
|
|
>Copy Number</a
|
||
|
|
>
|
||
|
|
</li>
|
||
|
|
<li>
|
||
|
|
<a
|
||
|
|
href="#"
|
||
|
|
@click.prevent="loadDbSnpTrack"
|
||
|
|
style="display:block; padding: 8px 20px; color: black; text-decoration: none;"
|
||
|
|
>dbSNP 137 (bed tabix)</a
|
||
|
|
>
|
||
|
|
</li>
|
||
|
|
<li>
|
||
|
|
<a
|
||
|
|
href="#"
|
||
|
|
@click.prevent="loadBigWigTrack"
|
||
|
|
style="display:block; padding: 8px 20px; color: black; text-decoration: none;"
|
||
|
|
>Encode bigwig</a
|
||
|
|
>
|
||
|
|
</li>
|
||
|
|
<li>
|
||
|
|
<a
|
||
|
|
href="#"
|
||
|
|
@click.prevent="loadBamTrack"
|
||
|
|
style="display:block; padding: 8px 20px; color: black; text-decoration: none;"
|
||
|
|
>1KG Bam (HG02450)</a
|
||
|
|
>
|
||
|
|
</li>
|
||
|
|
</ul>
|
||
|
|
</li>
|
||
|
|
</ul>
|
||
|
|
</div>
|
||
|
|
<!-- 탭 콘텐츠 -->
|
||
|
|
<div class="tab-content" id="viewerTabContent">
|
||
|
|
<div
|
||
|
|
class="tab-pane fade show active"
|
||
|
|
id="igv-viewer"
|
||
|
|
role="tabpanel"
|
||
|
|
>
|
||
|
|
<div style="padding-top: 20px">
|
||
|
|
이 예제는 드롭다운 메뉴에서 동적으로 트랙을 추가하는 igv.js API의
|
||
|
|
사용을 보여줍니다.
|
||
|
|
위의 메뉴에서 'CopyNumber'를 선택하면 다음과 같은 호출이 실행됩니다.
|
||
|
|
<p>
|
||
|
|
<pre>
|
||
|
|
igv.browser.loadTrack({
|
||
|
|
url: 'https://s3.amazonaws.com/igv.org.demo/GBM-TP.seg.gz',
|
||
|
|
name: 'GBM Copy # (TCGA Broad GDAC)'});
|
||
|
|
</pre>
|
||
|
|
</p>
|
||
|
|
자세한 내용은
|
||
|
|
<a href="https://github.com/igvteam/igv.js/wiki">개발자 위키</a>를
|
||
|
|
참조하세요.
|
||
|
|
</div>
|
||
|
|
<div id="igvDiv" style="padding-top: 20px"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</main>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
export default {
|
||
|
|
name: "App",
|
||
|
|
data() {
|
||
|
|
return {
|
||
|
|
browser: null,
|
||
|
|
isDropdownOpen: false,
|
||
|
|
};
|
||
|
|
},
|
||
|
|
mounted() {
|
||
|
|
// 외부 클릭시 드롭다운 닫기
|
||
|
|
document.addEventListener("click", this.handleClickOutside);
|
||
|
|
this.initializeIGV();
|
||
|
|
},
|
||
|
|
beforeUnmount() {
|
||
|
|
document.removeEventListener("click", this.handleClickOutside);
|
||
|
|
},
|
||
|
|
methods: {
|
||
|
|
toggleDropdown() {
|
||
|
|
this.isDropdownOpen = !this.isDropdownOpen;
|
||
|
|
},
|
||
|
|
closeDropdown() {
|
||
|
|
this.isDropdownOpen = false;
|
||
|
|
},
|
||
|
|
handleClickOutside(event) {
|
||
|
|
const dropdown = this.$el.querySelector("#igv-example-api-dropdown");
|
||
|
|
const menu = this.$el.querySelector(".dropdown-menu");
|
||
|
|
if (
|
||
|
|
dropdown &&
|
||
|
|
menu &&
|
||
|
|
!dropdown.contains(event.target) &&
|
||
|
|
!menu.contains(event.target)
|
||
|
|
) {
|
||
|
|
this.closeDropdown();
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
async initializeIGV() {
|
||
|
|
await this.waitForIGV();
|
||
|
|
await this.$nextTick();
|
||
|
|
|
||
|
|
const igvDiv = document.getElementById("igvDiv");
|
||
|
|
if (!igvDiv) {
|
||
|
|
console.error("❌ #igvDiv가 존재하지 않습니다.");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const options = {
|
||
|
|
locus: "chr1:155,160,475-155,184,282",
|
||
|
|
genome: "hg19",
|
||
|
|
};
|
||
|
|
|
||
|
|
try {
|
||
|
|
this.browser = await igv.createBrowser(igvDiv, options);
|
||
|
|
window.igv = { browser: this.browser };
|
||
|
|
this.addBaseClickEvent();
|
||
|
|
this.browser.on("locuschange", this.addBaseClickEvent);
|
||
|
|
this.browser.on("trackclick", this.addBaseClickEvent);
|
||
|
|
} catch (error) {
|
||
|
|
console.error("IGV 브라우저 생성 중 오류:", error);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
waitForIGV() {
|
||
|
|
return new Promise((resolve) => {
|
||
|
|
const checkIGV = () => {
|
||
|
|
if (typeof igv !== "undefined") {
|
||
|
|
resolve();
|
||
|
|
} else {
|
||
|
|
setTimeout(checkIGV, 100);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
checkIGV();
|
||
|
|
});
|
||
|
|
},
|
||
|
|
addBaseClickEvent() {
|
||
|
|
setTimeout(() => {
|
||
|
|
const texts = document.querySelectorAll("#igvDiv text");
|
||
|
|
texts.forEach((text) => {
|
||
|
|
const base = text.textContent;
|
||
|
|
if (["A", "T", "C", "G"].includes(base)) {
|
||
|
|
text.style.cursor = "pointer";
|
||
|
|
text.onclick = () => {
|
||
|
|
text.textContent = this.getComplement(text.textContent);
|
||
|
|
};
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}, 300);
|
||
|
|
},
|
||
|
|
getComplement(base) {
|
||
|
|
switch (base) {
|
||
|
|
case "A":
|
||
|
|
return "T";
|
||
|
|
case "T":
|
||
|
|
return "A";
|
||
|
|
case "C":
|
||
|
|
return "G";
|
||
|
|
case "G":
|
||
|
|
return "C";
|
||
|
|
default:
|
||
|
|
return base;
|
||
|
|
}
|
||
|
|
},
|
||
|
|
loadCopyNumberTrack() {
|
||
|
|
if (this.browser) {
|
||
|
|
this.browser.loadTrackList = this.browser.loadTrackList.bind(this.browser);
|
||
|
|
this.browser.loadTrackList([
|
||
|
|
{
|
||
|
|
url: "https://s3.amazonaws.com/igv.org.demo/GBM-TP.seg.gz",
|
||
|
|
indexed: false,
|
||
|
|
isLog: true,
|
||
|
|
name: "GBM Copy # (TCGA Broad GDAC)",
|
||
|
|
},
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
this.closeDropdown();
|
||
|
|
},
|
||
|
|
loadDbSnpTrack() {
|
||
|
|
if (this.browser) {
|
||
|
|
this.browser.loadTrackList = this.browser.loadTrackList.bind(this.browser);
|
||
|
|
this.browser.loadTrackList([
|
||
|
|
{
|
||
|
|
type: "annotation",
|
||
|
|
format: "bed",
|
||
|
|
url: "https://data.broadinstitute.org/igvdata/annotations/hg19/dbSnp/snp137.hg19.bed.gz",
|
||
|
|
indexURL:
|
||
|
|
"https://data.broadinstitute.org/igvdata/annotations/hg19/dbSnp/snp137.hg19.bed.gz.tbi",
|
||
|
|
visibilityWindow: 200000,
|
||
|
|
name: "dbSNP 137",
|
||
|
|
},
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
this.closeDropdown();
|
||
|
|
},
|
||
|
|
loadBigWigTrack() {
|
||
|
|
if (this.browser) {
|
||
|
|
this.browser.loadTrackList = this.browser.loadTrackList.bind(this.browser);
|
||
|
|
this.browser.loadTrackList([
|
||
|
|
{
|
||
|
|
type: "wig",
|
||
|
|
format: "bigwig",
|
||
|
|
url: "https://s3.amazonaws.com/igv.broadinstitute.org/data/hg19/encode/wgEncodeBroadHistoneGm12878H3k4me3StdSig.bigWig",
|
||
|
|
name: "Gm12878H3k4me3",
|
||
|
|
},
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
this.closeDropdown();
|
||
|
|
},
|
||
|
|
loadBamTrack() {
|
||
|
|
if (this.browser) {
|
||
|
|
this.browser.loadTrackList = this.browser.loadTrackList.bind(this.browser);
|
||
|
|
this.browser.loadTrackList([
|
||
|
|
{
|
||
|
|
type: "alignment",
|
||
|
|
format: "bam",
|
||
|
|
url: "https://1000genomes.s3.amazonaws.com/phase3/data/HG02450/alignment/HG02450.mapped.ILLUMINA.bwa.ACB.low_coverage.20120522.bam",
|
||
|
|
indexURL:
|
||
|
|
"https://1000genomes.s3.amazonaws.com/phase3/data/HG02450/alignment/HG02450.mapped.ILLUMINA.bwa.ACB.low_coverage.20120522.bam.bai",
|
||
|
|
name: "HG02450",
|
||
|
|
},
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
this.closeDropdown();
|
||
|
|
},
|
||
|
|
},
|
||
|
|
};
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style>
|
||
|
|
#app {
|
||
|
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||
|
|
-webkit-font-smoothing: antialiased;
|
||
|
|
-moz-osx-font-smoothing: grayscale;
|
||
|
|
color: #2c3e50;
|
||
|
|
}
|
||
|
|
|
||
|
|
pre {
|
||
|
|
padding: 10px;
|
||
|
|
border-radius: 4px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.nav-tabs {
|
||
|
|
margin-bottom: 20px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.tab-content {
|
||
|
|
padding-top: 20px;
|
||
|
|
}
|
||
|
|
</style>
|
||
|
|
|