icn3d 3.27.0 → 3.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/icn3d.js CHANGED
@@ -8834,9 +8834,9 @@ class SetMenu {
8834
8834
 
8835
8835
  html += this.getMenuText('mn2_rotate90', 'Rotate 90°', undefined, undefined, 2);
8836
8836
  html += "<ul>";
8837
- html += this.getRadio('mn6_rotate90', 'mn6_rotatex', 'X-axis(Shift + Key M)', undefined, undefined, 2);
8838
- html += this.getRadio('mn6_rotate90', 'mn6_rotatey', 'Y-axis(Shift + Key J)', undefined, undefined, 2);
8839
- html += this.getRadio('mn6_rotate90', 'mn6_rotatez', 'Z-axis', undefined, undefined, 2);
8837
+ html += this.getRadio('mn6_rotate90', 'mn6_rotatex', 'rotate x', undefined, undefined, 2);
8838
+ html += this.getRadio('mn6_rotate90', 'mn6_rotatey', 'rotate y', undefined, undefined, 2);
8839
+ html += this.getRadio('mn6_rotate90', 'mn6_rotatez', 'rotate z', undefined, undefined, 2);
8840
8840
  html += "</ul>";
8841
8841
  html += "</li>";
8842
8842
  html += this.getMenuText('mn2_rotateauto', 'Auto Rotation', undefined, 1, 2);
@@ -11567,9 +11567,10 @@ class SetDialog {
11567
11567
 
11568
11568
  html += me.htmlCls.divStr + "dl_addtrack_tabs' style='border:0px;'>";
11569
11569
  html += "<ul>";
11570
+ html += "<li><a href='#" + me.pre + "tracktab2c'>Isoforms & Exons</a></li>";
11571
+ html += "<li><a href='#" + me.pre + "tracktab2b'>MSA</a></li>";
11570
11572
  html += "<li><a href='#" + me.pre + "tracktab1'>NCBI gi/Accession</a></li>";
11571
11573
  html += "<li><a href='#" + me.pre + "tracktab2'>FASTA</a></li>";
11572
- html += "<li><a href='#" + me.pre + "tracktab2b'>FASTA Alignment</a></li>";
11573
11574
  html += "<li><a href='#" + me.pre + "tracktab3'>BED File</a></li>";
11574
11575
  html += "<li><a href='#" + me.pre + "tracktab4'>Custom</a></li>";
11575
11576
  html += "<li><a href='#" + me.pre + "tracktab5'>Current Selection</a></li>";
@@ -11585,10 +11586,16 @@ class SetDialog {
11585
11586
  html += "</div>";
11586
11587
 
11587
11588
  html += me.htmlCls.divStr + "tracktab2b'>";
11588
- html += "<div style='width:600px'>The full protein sequences with gaps are listed one by one. The sequence of the structure is listed at the top. If there are non-gap residues(e.g., from RefSeq) outside of the sequence of the structure, please remove them. Each sequence has a title line starting with \">\".</div><br>";
11589
- html += "<b>FASTA alignment sequences</b>:<br>";
11589
+ // html += "<div style='width:600px'>The full protein sequences with gaps are listed one by one. The sequence of the structure is listed at the top. If there are non-gap residues(e.g., from RefSeq) outside of the sequence of the structure, please remove them. Each sequence has a title line starting with \">\".</div><br>";
11590
+ html += "<div style='width:600px'>Note: The full protein sequences with gaps in MSA are listed one by one. The sequence of the structure is listed at the top. Each sequence has a title line starting with \">\".</div><br>";
11591
+
11592
+ html += "<b>Precalculated Multiple Sequence Alignment (MSA)</b>:<br>";
11590
11593
  html += "<textarea id='" + me.pre + "track_fastaalign' rows='5' style='width: 100%; height: " +(2*me.htmlCls.LOG_HEIGHT) + "px; padding: 0px; border: 0px;'></textarea><br><br>";
11591
- html += "Position of the first residue in Sequences & Annotations window: " + me.htmlCls.inputTextStr + "id='" + me.pre + "fasta_startpos' value='1' size=2> <br><br>";
11594
+
11595
+ // html += "<b>Opion 1. Precalculated Multiple Sequence Alignment (MSA)</b>:<br>";
11596
+ // html += "<textarea id='" + me.pre + "track_fastaalign' rows='5' style='width: 100%; height: " +(2*me.htmlCls.LOG_HEIGHT) + "px; padding: 0px; border: 0px;'></textarea><br><br>";
11597
+ // html += "<b>Opion 2. NCBI Protein Accessions</b>: "+ me.htmlCls.inputTextStr + "id='" + me.pre + "track_acclist' size=60> <br><br>";
11598
+ html += "<b>Position of the first residue in Sequences & Annotations window</b>: " + me.htmlCls.inputTextStr + "id='" + me.pre + "fasta_startpos' value='1' size=2> <br><br>";
11592
11599
 
11593
11600
  html += "Color Sequence by: <select id='" + me.pre + "colorseqby'>";
11594
11601
  html += me.htmlCls.optionStr + "'identity' selected>Identity</option>";
@@ -11598,6 +11605,22 @@ class SetDialog {
11598
11605
  html += me.htmlCls.buttonStr + "addtrack_button2b'>Add Track(s)</button>";
11599
11606
  html += "</div>";
11600
11607
 
11608
+ html += me.htmlCls.divStr + "tracktab2c'>";
11609
+ html += "<div style='width:500px'>Note: Show exons for all isoforms of the protein in the same gene as specified below.</div><br>";
11610
+
11611
+ html += "<b><a href='https://www.ncbi.nlm.nih.gov/gene' target='_blank'>NCBI Gene</a> ID</b>: "+ me.htmlCls.inputTextStr + "id='" + me.pre + "track_geneid' size=20>" + me.htmlCls.space3 + me.htmlCls.buttonStr + "exons_table'>Exons & Introns in Gene Table</button><br><br>";
11612
+
11613
+ html += "<b>Position of the first residue in Sequences & Annotations window</b>: " + me.htmlCls.inputTextStr + "id='" + me.pre + "fasta_startpos2' value='1' size=2> <br><br>";
11614
+
11615
+ html += "Color Sequence by: <select id='" + me.pre + "colorseqby2'>";
11616
+ html += me.htmlCls.optionStr + "'identity' selected>Identity</option>";
11617
+ html += me.htmlCls.optionStr + "'conservation'>Conservation</option>";
11618
+ html += "</select> <br><br>";
11619
+
11620
+ html += me.htmlCls.buttonStr + "addtrack_button2c'>Show Isoforms & Exons</button>";
11621
+ html += "</div>";
11622
+
11623
+
11601
11624
  html += me.htmlCls.divStr + "tracktab3'>";
11602
11625
  html += "BED file: " + me.htmlCls.inputFileStr + "id='" + me.pre + "track_bed' size=16> <br><br>";
11603
11626
  html += me.htmlCls.buttonStr + "addtrack_button3'>Add Track</button>";
@@ -12919,6 +12942,8 @@ class Events {
12919
12942
 
12920
12943
  let esmData = await me.getAjaxPostPromise(esmUrl, esmfold_fasta, true, alertMess, undefined, true, 'text');
12921
12944
 
12945
+ ic.bResetAnno = true;
12946
+
12922
12947
  ic.bInputfile = true;
12923
12948
  ic.InputfileType = 'pdb';
12924
12949
  ic.InputfileData = (ic.InputfileData) ? ic.InputfileData + '\nENDMDL\n' + esmData : esmData;
@@ -14327,13 +14352,15 @@ class Events {
14327
14352
  });
14328
14353
 
14329
14354
 
14330
- $(document).on("click", ".icn3d-addtrack", function(e) { me.icn3d;
14355
+ $(document).on("click", ".icn3d-addtrack", function(e) { let ic = me.icn3d;
14331
14356
  e.stopImmediatePropagation();
14332
14357
  $("#" + me.pre + "anno_custom")[0].checked = true;
14333
14358
  $("[id^=" + me.pre + "custom]").show();
14334
14359
  //e.preventDefault();
14335
14360
  let chainid = $(this).attr('chainid');
14361
+ let geneid = ic.chainsGene[chainid].geneId;
14336
14362
  $("#" + me.pre + "track_chainid").val(chainid);
14363
+ $("#" + me.pre + "track_geneid").val(geneid);
14337
14364
  me.htmlCls.dialogCls.openDlg('dl_addtrack', 'Add track for Chain: ' + chainid);
14338
14365
  $( "#" + me.pre + "track_gi" ).focus();
14339
14366
  });
@@ -14573,7 +14600,7 @@ class AlignSeq {
14573
14600
  let bHighlightChain;
14574
14601
  let index = 0, prevResCnt2nd = 0;
14575
14602
  let firstChainid, oriChainid;
14576
- console.log("alignChainArray.length: " + alignChainArray.length);
14603
+
14577
14604
  // for(let i in ic.alnChains) {
14578
14605
  for (let m = 0, ml = alignChainArray.length; m < ml; ++m) {
14579
14606
  let i = alignChainArray[m];
@@ -32289,6 +32316,8 @@ class Alternate {
32289
32316
  // change the display atom when alternating
32290
32317
  //Show structures one by one.
32291
32318
  alternateStructures() { let ic = this.icn3d, me = ic.icn3dui;
32319
+ ic.bAlernate = true;
32320
+
32292
32321
  //ic.transformCls.zoominSelection();
32293
32322
 
32294
32323
  // default ic.ALTERNATE_STRUCTURE = -1
@@ -32403,11 +32432,11 @@ class Alternate {
32403
32432
 
32404
32433
  //ic.glycanCls.showGlycans();
32405
32434
 
32435
+ ic.opts['rotationcenter'] = 'highlight center';
32436
+
32406
32437
  ic.drawCls.draw();
32407
32438
 
32408
32439
  ic.bShowHighlight = true;
32409
- //ic.opts['rotationcenter'] = 'molecule center';
32410
- ic.opts['rotationcenter'] = 'highlight center';
32411
32440
  }
32412
32441
 
32413
32442
  alternateWrapper() { let ic = this.icn3d; ic.icn3dui;
@@ -36033,6 +36062,10 @@ class AnnoCddSite {
36033
36062
  let prevEmptyWidth = 0;
36034
36063
  let prevLineWidth = 0;
36035
36064
  let widthPerRes = 1;
36065
+
36066
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
36067
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
36068
+
36036
36069
  for(let i = 0, il = ic.giSeq[chnid].length; i < il; ++i) {
36037
36070
  html += ic.showSeqCls.insertGap(chnid, i, '-');
36038
36071
  if(resPosArray.indexOf(i) != -1) {
@@ -36065,6 +36098,9 @@ class AnnoCddSite {
36065
36098
  html += '<span>-</span>'; //'<span>-</span>';
36066
36099
  }
36067
36100
  }
36101
+
36102
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
36103
+
36068
36104
  htmlTmp = '<span class="icn3d-residueNum" title="residue count">&nbsp;' + resCnt.toString() + ' Residues</span>';
36069
36105
  htmlTmp += '</span>';
36070
36106
  htmlTmp += '<br>';
@@ -36222,6 +36258,9 @@ class AnnoCddSite {
36222
36258
  html2 += '<div style="width:' + titleSpace + 'px!important;" class="icn3d-seqTitle ' + linkStr + '" ' + type + '="' + acc + '" from="' + fromArray + '" to="' + toArray + '" shorttitle="' + title + '" index="' + index + '" setname="' + setname + '" anno="sequence" chain="' + chnid + '" title="' + fulltitle + '">' + title + ' </div>';
36223
36259
  html2 += htmlTmp3 + htmlTmp;
36224
36260
  let pre = type + index.toString();
36261
+
36262
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
36263
+
36225
36264
  for(let i = 0, il = ic.giSeq[chnid].length; i < il; ++i) {
36226
36265
  html += ic.showSeqCls.insertGap(chnid, i, '-');
36227
36266
 
@@ -36249,9 +36288,15 @@ class AnnoCddSite {
36249
36288
  html += '<span>-</span>'; //'<span>-</span>';
36250
36289
  }
36251
36290
  }
36291
+
36292
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
36293
+
36252
36294
  let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.chains[chnid]);
36253
36295
  let colorStr =(atom.color === undefined || atom.color.getHexString() === 'FFFFFF') ? 'DDDDDD' : atom.color.getHexString();
36254
36296
  let color =(atom.color !== undefined) ? colorStr : "CCCCCC";
36297
+
36298
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
36299
+
36255
36300
  if(me.cfg.blast_rep_id != chnid) { // regular
36256
36301
  for(let i = 0, il = fromArray.length; i < il; ++i) {
36257
36302
  let emptyWidth =(i == 0) ? Math.round(ic.seqAnnWidth *(fromArray[i] - ic.baseResi[chnid] - 1) / ic.maxAnnoLength) : Math.round(ic.seqAnnWidth *(fromArray[i] - toArray[i-1] - 1) / ic.maxAnnoLength);
@@ -36328,6 +36373,10 @@ class AnnoCddSite {
36328
36373
  let prevEmptyWidth = 0;
36329
36374
  let prevLineWidth = 0;
36330
36375
  let widthPerRes = 1;
36376
+
36377
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
36378
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
36379
+
36331
36380
  for(let i = 0, il = ic.giSeq[chnid].length; i < il; ++i) {
36332
36381
  html += ic.showSeqCls.insertGap(chnid, i, '-');
36333
36382
  let resi = ic.ParserUtilsCls.getResi(chnid, i);
@@ -36403,6 +36452,9 @@ class AnnoCddSite {
36403
36452
  html += '<span>-</span>'; //'<span>-</span>';
36404
36453
  }
36405
36454
  }
36455
+
36456
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
36457
+
36406
36458
  htmlTmp = '<span class="icn3d-residueNum" title="residue count">&nbsp;' + resCnt.toString() + ' Residues</span>';
36407
36459
  htmlTmp += '</span>';
36408
36460
  htmlTmp += '<br>';
@@ -36567,6 +36619,9 @@ class AnnoContact {
36567
36619
  let prevLineWidth = 0;
36568
36620
  let widthPerRes = 1;
36569
36621
 
36622
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
36623
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
36624
+
36570
36625
  for(let i = 0, il = ic.giSeq[chnid].length; i < il; ++i) {
36571
36626
  html += ic.showSeqCls.insertGap(chnid, i, '-');
36572
36627
  let resi = ic.ParserUtilsCls.getResi(chnid, i);
@@ -36602,6 +36657,9 @@ class AnnoContact {
36602
36657
  html += '<span>-</span>'; //'<span>-</span>';
36603
36658
  }
36604
36659
  }
36660
+
36661
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
36662
+
36605
36663
  htmlTmp = '<span class="icn3d-residueNum" title="residue count">&nbsp;' + resCnt.toString() + ' Residues</span>';
36606
36664
  htmlTmp += '</span>';
36607
36665
  htmlTmp += '<br>';
@@ -36854,6 +36912,10 @@ class AnnoPTM {
36854
36912
  let prevEmptyWidth = 0;
36855
36913
  let prevLineWidth = 0;
36856
36914
  let widthPerRes = 1;
36915
+
36916
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
36917
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
36918
+
36857
36919
  for(let i = 0, il = ic.giSeq[chnid].length; i < il; ++i) {
36858
36920
  html += ic.showSeqCls.insertGap(chnid, i, '-');
36859
36921
  if(resPosArray.indexOf(i) != -1) {
@@ -36886,6 +36948,9 @@ class AnnoPTM {
36886
36948
  html += '<span>-</span>'; //'<span>-</span>';
36887
36949
  }
36888
36950
  }
36951
+
36952
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
36953
+
36889
36954
  htmlTmp = '<span class="icn3d-residueNum" title="residue count">&nbsp;' + resCnt.toString() + ' Residues</span>';
36890
36955
  htmlTmp += '</span>';
36891
36956
  htmlTmp += '<br>';
@@ -37165,6 +37230,9 @@ class AnnoDomain {
37165
37230
  html += htmlTmp2 + htmlTmp3 + htmlTmp;
37166
37231
  html2 += htmlTmp2 + htmlTmp3 + htmlTmp;
37167
37232
  let pre = 'domain3d' + index.toString();
37233
+
37234
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
37235
+
37168
37236
  for(let i = 0, il = ic.giSeq[chnid].length; i < il; ++i) {
37169
37237
  html += ic.showSeqCls.insertGap(chnid, i, '-');
37170
37238
  //if(i >= domainFrom && i <= domainTo) {
@@ -37184,9 +37252,15 @@ class AnnoDomain {
37184
37252
  html += '<span>-</span>'; //'<span>-</span>';
37185
37253
  }
37186
37254
  }
37255
+
37256
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
37257
+
37187
37258
  let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.chains[chnid]);
37188
37259
  let colorStr =(atom.color === undefined || atom.color.getHexString() === 'FFFFFF') ? 'DDDDDD' : atom.color.getHexString();
37189
37260
  let color =(atom.color !== undefined) ? colorStr : "CCCCCC";
37261
+
37262
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
37263
+
37190
37264
  if(me.cfg.blast_rep_id != chnid) { // regular
37191
37265
  for(let i = 0, il = fromArray.length; i < il; ++i) {
37192
37266
  let emptyWidth =(i == 0) ? Math.round(ic.seqAnnWidth *(fromArray[i] - ic.baseResi[chnid] - 1) / ic.maxAnnoLength) : Math.round(ic.seqAnnWidth *(fromArray[i] - toArray[i-1] - 1) / ic.maxAnnoLength);
@@ -37454,9 +37528,18 @@ class AnnoSnpClinVar {
37454
37528
  let prevEmptyWidth = 0;
37455
37529
  let prevLineWidth = 0;
37456
37530
  let widthPerRes = 1;
37531
+
37532
+ if(bOverview) {
37533
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
37534
+ }
37535
+ else {
37536
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
37537
+ }
37538
+
37457
37539
  for(let i = 1, il = ic.giSeq[chnid].length; i <= il; ++i) {
37458
37540
  if(bOverview) {
37459
37541
  if(resi2index[i] !== undefined) {
37542
+
37460
37543
  // get the mouse over text
37461
37544
  let cFull = ic.giSeq[chnid][i-1];
37462
37545
  let c = cFull;
@@ -37481,7 +37564,8 @@ class AnnoSnpClinVar {
37481
37564
  }
37482
37565
  }
37483
37566
  html += ic.showSeqCls.insertGapOverview(chnid, i-1);
37484
- let emptyWidth =(me.cfg.blast_rep_id == chnid) ? Math.round(ic.seqAnnWidth *(i-1) /(ic.maxAnnoLength + ic.nTotalGap) - prevEmptyWidth - prevLineWidth) : Math.round(ic.seqAnnWidth *(i-1) / ic.maxAnnoLength - prevEmptyWidth - prevLineWidth);
37567
+ let emptyWidth = Math.round(ic.seqAnnWidth *(i-1) /(ic.maxAnnoLength + ic.nTotalGap) - prevEmptyWidth - prevLineWidth);
37568
+ //let emptyWidth =(me.cfg.blast_rep_id == chnid) ? Math.round(ic.seqAnnWidth *(i-1) /(ic.maxAnnoLength + ic.nTotalGap) - prevEmptyWidth - prevLineWidth) : Math.round(ic.seqAnnWidth *(i-1) / ic.maxAnnoLength - prevEmptyWidth - prevLineWidth);
37485
37569
  //if(emptyWidth < 0) emptyWidth = 0;
37486
37570
  if(bClinvar) {
37487
37571
  if(snpTypeHash[i] == 'icn3d-clinvar' || snpTypeHash[i] == 'icn3d-clinvar-path') {
@@ -37710,6 +37794,10 @@ class AnnoSnpClinVar {
37710
37794
  }
37711
37795
  } // if(bOverview) {
37712
37796
  } // for
37797
+
37798
+ if(!bOverview) {
37799
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
37800
+ }
37713
37801
 
37714
37802
  //var end = bStartEndRes ? ic.chainsSeq[chnid][ic.giSeq[chnid].length - 1 - ic.matchedPos[chnid] ].resi : '';
37715
37803
  if(line == 1) {
@@ -39323,7 +39411,7 @@ class AddTrack {
39323
39411
  thisClass.alignSequenceToStructure(chainid, data, title);
39324
39412
  });
39325
39413
 
39326
- // FASTA Alignment
39414
+ // MSA
39327
39415
  me.myEventCls.onIds("#" + ic.pre + "addtrack_button2b", "click", async function(e) { let ic = thisClass.icn3d;
39328
39416
  e.stopImmediatePropagation();
39329
39417
  //e.preventDefault();
@@ -39331,159 +39419,47 @@ class AddTrack {
39331
39419
 
39332
39420
  let chainid = $("#" + ic.pre + "track_chainid").val();
39333
39421
  let startpos = $("#" + ic.pre + "fasta_startpos").val();
39422
+ if(!startpos) startpos = 1;
39423
+
39334
39424
  let colorseqby = $("#" + ic.pre + "colorseqby").val();
39335
39425
  let type =(colorseqby == 'identity') ? 'identity' : 'custom';
39336
39426
 
39337
39427
  let fastaList = $("#" + ic.pre + "track_fastaalign").val();
39338
- let fastaArray = fastaList.split('>');
39339
-
39340
- // the first array item is empty
39341
- // the second array item is the sequence of the structure, start with i = 2
39342
- let posFirst = fastaArray[1].indexOf('\n');
39343
- fastaArray[1].substr(0, posFirst);
39344
- let seqFirst = fastaArray[1].substr(posFirst + 1).replace(/\n/g, '');
39345
-
39346
- let trackTitleArray = [];
39347
- let trackSeqArray = [];
39348
- for(let i = 2, il = fastaArray.length; i < il; ++i) {
39349
- let pos = fastaArray[i].indexOf('\n');
39350
- let title = fastaArray[i].substr(0, pos);
39351
- trackTitleArray.push(title);
39352
- let seq = fastaArray[i].substr(pos + 1).replace(/\n/g, '');
39353
- trackSeqArray.push(seq);
39354
- }
39355
-
39356
- let startposGiSeq = undefined;
39357
- for(let i = 0, il = ic.giSeq[chainid].length; i < il; ++i) {
39358
- //let pos =(i >= ic.matchedPos[chainid] && i - ic.matchedPos[chainid] < ic.chainsSeq[chainid].length) ? ic.chainsSeq[chainid][i - ic.matchedPos[chainid]].resi : ic.baseResi[chainid] + 1 + i;
39359
- let pos = ic.ParserUtilsCls.getResi(chainid, i);
39360
-
39361
- if(pos != startpos) {
39362
- continue;
39363
- }
39364
- else {
39365
- startposGiSeq = i;
39366
- }
39367
- }
39368
-
39369
- if(startposGiSeq === undefined) var aaa = 1; //alert("Please double check the start position before clicking \"Add Track\"");
39370
-
39371
-
39372
- // set up gap for the master seq
39373
- // don't count gaps in both ends
39374
- ic.targetGapHash = {};
39375
- let prevSeq = '-', prevPos = 0, from, to, cnt = 0, dashCnt = 0;
39376
- let bFound = false, seqStart = 0, seqEnd = 0;
39377
- for(let i = 0, il = seqFirst.length; i < il; ++i) {
39378
- if(seqFirst[i] == '-' && seqFirst[i] != prevSeq) { // start of gap
39379
- from = cnt;
39380
- dashCnt = 0;
39381
- }
39382
-
39383
- if(prevSeq == '-' && seqFirst[i] != prevSeq && cnt > 0) { // end of gap
39384
- to = prevPos;
39385
- ic.targetGapHash[from + startposGiSeq] = {'from': from + startposGiSeq, 'to': to + dashCnt - 1 + startposGiSeq};
39386
- }
39387
-
39388
- prevSeq = seqFirst[i];
39389
- prevPos = cnt;
39390
-
39391
- if(seqFirst[i] != '-') {
39392
- ++cnt;
39393
- seqEnd = i;
39394
-
39395
- if(!bFound) {
39396
- seqStart = i;
39397
- bFound = true;
39398
- }
39399
- }
39400
- else {
39401
- ++dashCnt;
39402
- }
39403
- }
39404
-
39405
- await ic.annotationCls.resetAnnoAll();
39406
-
39407
- let targetGapHashStr = '';
39408
- let cntTmp = 0;
39409
- for(let i in ic.targetGapHash) {
39410
- if(cntTmp > 0) targetGapHashStr += ' ';
39411
- targetGapHashStr += i + '_' + ic.targetGapHash[i].from + '_' + ic.targetGapHash[i].to;
39412
- ++cntTmp;
39413
- }
39414
-
39415
- me.htmlCls.clickMenuCls.setLogCmd("msa | " + targetGapHashStr, true);
39416
-
39417
- // add tracks
39418
- let resi2cntSameRes = {}; // count of same residue at each position
39419
- for(let j = 0, jl = trackSeqArray.length; j < jl; ++j) {
39420
- let resi = startpos; //startposGiSeq + 1;
39421
- let text = '';
39422
- for(let k = 0; k < startposGiSeq; ++k) {
39423
- if(ic.targetGapHash.hasOwnProperty(k)) {
39424
- for(let m = 0; m < ic.targetGapHash[k].to - ic.targetGapHash[k].from + 1; ++m) {
39425
- text += '-';
39426
- }
39427
- }
39428
-
39429
- text += '-';
39430
- }
39431
-
39432
- for(let k = seqStart; k <= seqEnd; ++k) {
39433
- //if(seqFirst[k] == '-') continue;
39434
39428
 
39435
- if(j == 0) resi2cntSameRes[resi] = 0;
39436
-
39437
- text += trackSeqArray[j][k]; //ic.giSeq[chainid][i];
39438
-
39439
- if(seqFirst[k] != '-') {
39440
- if(seqFirst[k] == trackSeqArray[j][k]) ++resi2cntSameRes[resi];
39441
- ++resi;
39442
- }
39443
- }
39444
-
39445
- let title =(trackTitleArray[j].length < 20) ? trackTitleArray[j] : trackTitleArray[j].substr(0, 20) + '...';
39446
- let bMsa = true;
39447
- thisClass.showNewTrack(chainid, title, text, undefined, undefined, type, undefined, bMsa);
39448
-
39449
- me.htmlCls.clickMenuCls.setLogCmd("add track | chainid " + chainid + " | title " + title + " | text " + thisClass.simplifyText(text)
39450
- + " | type " + type + " | color 0 | msa 1", true);
39451
- }
39452
-
39453
- // set colot for the master seq
39454
- if(trackSeqArray.length > 0) {
39455
- if(ic.queryresi2score === undefined) ic.queryresi2score = {};
39456
- if(ic.queryresi2score[chainid] === undefined) ic.queryresi2score[chainid] = {};
39457
-
39458
- let nSeq = trackSeqArray.length;
39459
- for(let resi in resi2cntSameRes) {
39460
- let score = parseInt(resi2cntSameRes[resi] / nSeq * 100);
39461
- ic.queryresi2score[chainid][resi] = score;
39462
- }
39429
+ if(fastaList) {
39430
+ await thisClass.addMsaTracks(chainid, startpos, type, fastaList);
39431
+ }
39432
+ });
39463
39433
 
39464
- let resiArray = Object.keys(resi2cntSameRes);
39465
- let start = Math.min.apply(null, resiArray);
39466
- let end = Math.max.apply(null, resiArray);
39434
+ // Gene table
39435
+ me.myEventCls.onIds("#" + ic.pre + "exons_table", "click", async function(e) { let ic = thisClass.icn3d;
39436
+ e.stopImmediatePropagation();
39437
+ //dialog.dialog( "close" );
39467
39438
 
39468
- let resiScoreStr = '';
39469
- for(let resi = start; resi <= end; ++resi) {
39470
- if(resi2cntSameRes.hasOwnProperty(resi)) {
39471
- resiScoreStr += Math.round(resi2cntSameRes[resi] / nSeq * 9); // max 9
39472
- }
39473
- else {
39474
- resiScoreStr += '_';
39475
- }
39476
- }
39439
+ let geneid = $("#" + ic.pre + "track_geneid").val().trim();
39440
+ window.open('https://www.ncbi.nlm.nih.gov/gene/' + geneid + '?report=gene_table', '_blank');
39441
+ });
39477
39442
 
39478
- ic.opts['color'] = 'align custom';
39479
- ic.setColorCls.setColorByOptions(ic.opts, ic.hAtoms);
39443
+ // Isoform Alignment
39444
+ me.myEventCls.onIds("#" + ic.pre + "addtrack_button2c", "click", async function(e) { let ic = thisClass.icn3d;
39445
+ e.stopImmediatePropagation();
39446
+ //e.preventDefault();
39447
+ dialog.dialog( "close" );
39480
39448
 
39481
- ic.hlUpdateCls.updateHlAll();
39449
+ let chainid = $("#" + ic.pre + "track_chainid").val();
39450
+ let geneid = $("#" + ic.pre + "track_geneid").val();
39451
+ if(!geneid) {
39452
+ var aaa = 1; //alert("Please fill in the Gene ID...");
39453
+ return;
39454
+ }
39482
39455
 
39483
- ic.drawCls.draw();
39456
+ let startpos = $("#" + ic.pre + "fasta_startpos2").val();
39457
+ if(!startpos) startpos = 1;
39484
39458
 
39485
- me.htmlCls.clickMenuCls.setLogCmd('color align custom | ' + chainid + ' | range ' + start + '_' + end + ' | ' + resiScoreStr, true);
39486
- }
39459
+ let colorseqby = $("#" + ic.pre + "colorseqby2").val();
39460
+ let type =(colorseqby == 'identity') ? 'identity' : 'custom';
39461
+
39462
+ await thisClass.addExonTracks(chainid, geneid, startpos, type);
39487
39463
  });
39488
39464
 
39489
39465
  // BED file
@@ -39669,7 +39645,7 @@ class AddTrack {
39669
39645
 
39670
39646
  }
39671
39647
 
39672
- showNewTrack(chnid, title, text, cssColorArray, inTarget2queryHash, type, color, bMsa, fromArray, toArray) { let ic = this.icn3d, me = ic.icn3dui;
39648
+ showNewTrack(chnid, title, text, cssColorArray, inTarget2queryHash, type, color, bMsa, fromArray, toArray, seqStartLen, exonArray) { let ic = this.icn3d, me = ic.icn3dui;
39673
39649
  //if(ic.customTracks[chnid] === undefined) {
39674
39650
  // ic.customTracks[chnid] = {}
39675
39651
  //}
@@ -39718,22 +39694,28 @@ class AddTrack {
39718
39694
  $("#" + ic.pre + "tt_custom_" + chnid + "_" + simpTitle).width(divLength);
39719
39695
 
39720
39696
  let html = '<div id="' + ic.pre + 'giseq_sequence" class="icn3d-dl_sequence">';
39697
+ let htmlExon = html;
39721
39698
  let html2 = html;
39722
39699
  let html3 = html;
39700
+ let html3Exon = html;
39723
39701
 
39724
39702
  //var htmlTmp2 = '<div class="icn3d-seqTitle icn3d-link icn3d-blue" gi="' + chnid + '" anno="sequence" chain="' + chnid + '"><span style="white-space:nowrap;">' + title + '</span></div>';
39725
39703
  //var htmlTmp2 = '<div class="icn3d-seqTitle" gi="' + chnid + '" anno="sequence" chain="' + chnid + '"><span style="white-space:nowrap;">' + title + '</span></div>';
39726
39704
  let index = parseInt(Math.random()*10);
39727
39705
  let htmlTmp2 = '<div class="icn3d-seqTitle icn3d-link icn3d-blue" custom="' +(index+1).toString() + '" from="' + fromArray + '" to="' + toArray + '" shorttitle="' + simpTitle + '" index="' + index + '" setname="' + chnid + '_custom_' +(index+1).toString() + '" anno="sequence" chain="' + chnid + '" title="' + title + '">' + simpTitle + ' </div>';
39706
+ let htmlTmp2Exon = '<div class="icn3d-seqTitle" chain="' + chnid + '" title="Exons of ' + title + '">Exons </div>';
39707
+
39728
39708
  let htmlTmp3 = '<span class="icn3d-residueNum" title="residue count">' + resCnt.toString() + ' Pos</span>';
39729
39709
 
39730
39710
  html3 += htmlTmp2 + htmlTmp3 + '<br>';
39711
+ html3Exon += htmlTmp2Exon + htmlTmp3 + '<br>';
39731
39712
 
39732
39713
  let htmlTmp = '<span class="icn3d-seqLine">';
39733
39714
 
39734
39715
  html += htmlTmp2 + htmlTmp3 + htmlTmp;
39716
+ htmlExon += htmlTmp2Exon + htmlTmp3 + htmlTmp;
39735
39717
  html2 += htmlTmp2 + htmlTmp3 + htmlTmp;
39736
-
39718
+
39737
39719
  //var pre ='cst' + ic.customTracks[chnid].length;
39738
39720
  let posTmp = chnid.indexOf('_');
39739
39721
  //var pre ='cst' + chnid.substr(posTmp);
@@ -39748,10 +39730,34 @@ class AddTrack {
39748
39730
  let bIdentityColor =(type === 'identity') && text.indexOf('cannot-be-aligned') == -1 && text.indexOf('cannot be aligned') == -1 ? true : false;
39749
39731
 
39750
39732
  let parsedResn = {};
39751
- let gapCnt = 0, currResi = 1;
39733
+ let gapCnt = 0;
39752
39734
  htmlTmp2 = '';
39735
+
39736
+ // if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
39737
+ // if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
39738
+
39739
+ let pos2exonColor = {}, pos2genome = {}, pos2exonIndex = {};
39740
+ let cnt = 0;
39741
+ if(exonArray) {
39742
+ for(let j = 0, jl = exonArray.length; j < jl; ++j) {
39743
+ let start = exonArray[j].resStart, end = exonArray[j].resEnd;
39744
+ let genStart = exonArray[j].genomeRange.split('-')[0];
39745
+
39746
+ for(let k = 0, kl = end - start + 1; k < kl; ++k) {
39747
+ let colorStr = this.getExonColor(start, end, cnt);
39748
+
39749
+ pos2exonColor[cnt] = colorStr;
39750
+ pos2genome[cnt] = (genStart + ic.exonOrder * k*3) + '-' + (genStart + ic.exonOrder * k*3 + ic.exonOrder * 2); // reverse order from large to small
39751
+ pos2exonIndex[cnt] = j;
39752
+
39753
+ ++cnt;
39754
+ }
39755
+ }
39756
+ }
39757
+
39758
+ cnt = 0;
39753
39759
  for(let i = 0, il = text.length; i < il; ++i) {
39754
- let resNum = i-gapCnt;
39760
+ let resNum = i - gapCnt - ((ic.seqStartLen && ic.seqStartLen[chnid]) ? ic.seqStartLen[chnid] : 0);
39755
39761
 
39756
39762
  if(!bMsa) {
39757
39763
  html += ic.showSeqCls.insertGap(chnid, i, '-');
@@ -39772,7 +39778,8 @@ class AddTrack {
39772
39778
  let identityColorStr =(c == resName) ? 'FF0000' : '0000FF';
39773
39779
 
39774
39780
  //var pos =(resNum >= ic.matchedPos[chnid] && resNum - ic.matchedPos[chnid] < ic.chainsSeq[chnid].length) ? ic.chainsSeq[chnid][resNum - ic.matchedPos[chnid]].resi : ic.baseResi[chnid] + 1 + resNum;
39775
- let pos = ic.baseResi[chnid] + currResi;
39781
+ // let pos = ic.baseResi[chnid] + currResi;
39782
+ let pos = ic.baseResi[chnid] + (i+1) - ((ic.seqStartLen && ic.seqStartLen[chnid]) ? ic.seqStartLen[chnid] : 0);
39776
39783
 
39777
39784
  if(inTarget2queryHash !== undefined) pos = ic.baseResi[chnid] + inTarget2queryHash[i] + 1; // 0-based
39778
39785
 
@@ -39803,9 +39810,13 @@ class AddTrack {
39803
39810
 
39804
39811
  html += '<span id="' + pre + '_' + ic.pre + chnid + '_' + pos + '" title="' + c + pos + '" class="icn3d-residue" ' + tmpStr + '>' + c + '</span>';
39805
39812
 
39813
+ let tmpStrExon = 'style="background-color:' + pos2exonColor[cnt] + '"';
39814
+ htmlExon += '<span id="' + pre + '_' + ic.pre + chnid + '_' + pos + '" title="' + c + pos + ', Exon ' + (pos2exonIndex[cnt] + 1) + ': ' + pos2genome[cnt] + '" class="icn3d-residue" ' + tmpStrExon + '>&nbsp;</span>';
39815
+
39806
39816
  htmlTmp2 += ic.showSeqCls.insertGapOverview(chnid, i);
39807
39817
 
39808
- let emptyWidth =(me.cfg.blast_rep_id == chnid) ? Math.round(ic.seqAnnWidth * i /(ic.maxAnnoLength + ic.nTotalGap) - prevEmptyWidth - prevLineWidth) : Math.round(ic.seqAnnWidth * i / ic.maxAnnoLength - prevEmptyWidth - prevLineWidth);
39818
+ // let emptyWidth =(me.cfg.blast_rep_id == chnid) ? Math.round(ic.seqAnnWidth * i /(ic.maxAnnoLength + ic.nTotalGap) - prevEmptyWidth - prevLineWidth) : Math.round(ic.seqAnnWidth * i / ic.maxAnnoLength - prevEmptyWidth - prevLineWidth);
39819
+ let emptyWidth = Math.round(ic.seqAnnWidth * i /(ic.maxAnnoLength + ic.nTotalGap) - prevEmptyWidth - prevLineWidth);
39809
39820
  if(emptyWidth < 0) emptyWidth = 0;
39810
39821
 
39811
39822
  htmlTmp2 += '<div style="display:inline-block; width:' + emptyWidth + 'px;">&nbsp;</div>';
@@ -39826,19 +39837,21 @@ class AddTrack {
39826
39837
 
39827
39838
  prevEmptyWidth += emptyWidth;
39828
39839
  prevLineWidth += widthPerRes;
39829
-
39830
- ++currResi;
39840
+ ++cnt;
39831
39841
  }
39832
39842
  else {
39833
39843
  if(bErrorMess) {
39834
- html += '<span>' + c + '</span>';
39844
+ html += '<span>' + c + '</span>';
39835
39845
  }
39836
39846
  else {
39837
39847
  html += '<span>-</span>';
39848
+ htmlExon += '<span></span>';
39838
39849
  }
39839
39850
  }
39840
39851
  }
39841
39852
 
39853
+ // if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
39854
+
39842
39855
  if(fromArray !== undefined) {
39843
39856
  htmlTmp2 = '';
39844
39857
  let fromArray2 = [], toArray2 = [];
@@ -39864,13 +39877,77 @@ class AddTrack {
39864
39877
  let colorStr =(atom.color === undefined || atom.color.getHexString() === 'FFFFFF') ? 'DDDDDD' : atom.color.getHexString();
39865
39878
  let color =(atom.color !== undefined) ? colorStr : "CCCCCC";
39866
39879
 
39880
+ let cnt, prevCntTotal = 0;
39867
39881
  for(let i = 0, il = fromArray2.length; i < il; ++i) {
39868
39882
  htmlTmp2 += ic.showSeqCls.insertGapOverview(chnid, fromArray2[i]);
39869
39883
 
39870
- let emptyWidth =(i == 0) ? Math.round(ic.seqAnnWidth *(fromArray2[i] - ic.baseResi[chnid] - 1) /(ic.maxAnnoLength + ic.nTotalGap)) : Math.round(ic.seqAnnWidth *(fromArray2[i] - toArray2[i-1] - 1) /(ic.maxAnnoLength + ic.nTotalGap));
39884
+ let initialPos = (seqStartLen) ? fromArray2[i] : fromArray2[i] - ic.baseResi[chnid] - 1;
39885
+
39886
+ let emptyWidth =(i == 0) ? Math.round(ic.seqAnnWidth * initialPos /(ic.maxAnnoLength + ic.nTotalGap)) : Math.round(ic.seqAnnWidth *(fromArray2[i] - toArray2[i-1] - 1) /(ic.maxAnnoLength + ic.nTotalGap));
39887
+ if(emptyWidth < 0) emptyWidth = 0;
39888
+
39871
39889
  htmlTmp2 += '<div style="display:inline-block; width:' + emptyWidth + 'px;">&nbsp;</div>';
39872
39890
 
39873
- htmlTmp2 += '<div style="display:inline-block; color:white!important; font-weight:bold; background-color:#' + color + '; width:' + Math.round(ic.seqAnnWidth *(toArray2[i] - fromArray2[i] + 1) /(ic.maxAnnoLength + ic.nTotalGap)) + 'px;" class="icn3d-seqTitle icn3d-link icn3d-blue" custom="' +(index+1).toString() + '" from="' + fromArray2 + '" to="' + toArray2 + '" shorttitle="' + simpTitle + '" index="' + index + '" setname="' + chnid + '_custom_' +(index+1).toString() + '" id="' + chnid + '_custom_' + index + '" anno="sequence" chain="' + chnid + '" title="' + title + '">' + title + '</div>';
39891
+ if(!exonArray) {
39892
+ htmlTmp2 += '<div style="display:inline-block; color:white!important; font-weight:bold; background-color:#' + color + '; width:' + Math.round(ic.seqAnnWidth *(toArray2[i] - fromArray2[i] + 1) /(ic.maxAnnoLength + ic.nTotalGap)) + 'px;" class="icn3d-seqTitle icn3d-link icn3d-blue" custom="' +(index+1).toString() + '" from="' + fromArray2 + '" to="' + toArray2 + '" shorttitle="' + simpTitle + '" index="' + index + '" setname="' + chnid + '_custom_' +(index+1).toString() + '" id="' + chnid + '_custom_' + index + '" anno="sequence" chain="' + chnid + '" title="' + title + '">' + title + '</div>';
39893
+ }
39894
+ else {
39895
+ // determine how this range sits in the exon ranges in exonArray
39896
+ let startExon, endExon;
39897
+
39898
+ cnt = toArray[i] - fromArray[i] + 1;
39899
+ let from = prevCntTotal, to = prevCntTotal + cnt - 1;
39900
+
39901
+ prevCntTotal += cnt;
39902
+
39903
+ // fromArray2 was adjusted with gaps, no gaps in this case
39904
+ // let offset = fromArray2[i] - fromArray[i];
39905
+ // let emptyWidth = Math.round(ic.seqAnnWidth * offset /(ic.maxAnnoLength + ic.nTotalGap));
39906
+ // htmlTmp2 += '<div style="display:inline-block; width:' + emptyWidth + 'px;">&nbsp;</div>';
39907
+
39908
+ for(let j = 0, jl = exonArray.length; j < jl; ++j) {
39909
+ let start = exonArray[j].resStart, end = exonArray[j].resEnd;
39910
+
39911
+ if(from >= start && from <= end) {
39912
+ startExon = {exonIndex: j, rangeStart: start, rangeEnd: end, from: from, genomeRange: exonArray[j].genomeRange};
39913
+ }
39914
+
39915
+ if(to >= start && to <= end) {
39916
+ endExon = {exonIndex: j, rangeStart: start, rangeEnd: end, to: to, genomeRange: exonArray[j].genomeRange};
39917
+ }
39918
+ }
39919
+
39920
+ let startColorStr, endColorStr, colorGradient;
39921
+ if(startExon && endExon && startExon.exonIndex == endExon.exonIndex) { //
39922
+ startColorStr = this.getExonColor(startExon.rangeStart, startExon.rangeEnd, from);
39923
+ endColorStr = this.getExonColor(startExon.rangeStart, startExon.rangeEnd, to);
39924
+
39925
+ colorGradient = startColorStr + ' 0%, #FFF 50%, ' + endColorStr + ' 100%';
39926
+ htmlTmp2 += this.getExonHtml(startExon.exonIndex, colorGradient, startExon.from, endExon.to, startExon.genomeRange);
39927
+ }
39928
+ else {
39929
+ if(startExon) {
39930
+ startColorStr = this.getExonColor(startExon.rangeStart, startExon.rangeEnd, from);
39931
+
39932
+ colorGradient = startColorStr + ' 0%, #FFF 50%, #00F 100%';
39933
+ htmlTmp2 += this.getExonHtml(startExon.exonIndex, colorGradient, startExon.from, startExon.rangeEnd, startExon.genomeRange);
39934
+ }
39935
+
39936
+ if(startExon && endExon) {
39937
+ for(let j = startExon.exonIndex + 1; j < endExon.exonIndex; ++j) {
39938
+ colorGradient = '#F00 0%, #FFF 50%, #00F 100%';
39939
+ htmlTmp2 += this.getExonHtml(j, colorGradient, exonArray[j].resStart, exonArray[j].resEnd, exonArray[j].genomeRange);
39940
+ }
39941
+
39942
+ endColorStr = this.getExonColor(endExon.rangeStart, endExon.rangeEnd, to);
39943
+
39944
+ colorGradient = '#F00 0%, #FFF 50%, ' + endColorStr + ' 100%';
39945
+ htmlTmp2 += this.getExonHtml(endExon.exonIndex, colorGradient, endExon.rangeStart, endExon.to, endExon.genomeRange);
39946
+ }
39947
+ }
39948
+
39949
+ //htmlTmp2 += '<div style="display:inline-block; color:white!important; font-weight:bold; background-color:#' + color + '; width:' + Math.round(ic.seqAnnWidth *(toArray2[i] - fromArray2[i] + 1) /(ic.maxAnnoLength + ic.nTotalGap)) + 'px;" class="icn3d-seqTitle icn3d-link icn3d-blue" custom="' +(index+1).toString() + '" from="' + fromArray2 + '" to="' + toArray2 + '" shorttitle="' + simpTitle + '" index="' + index + '" setname="' + chnid + '_custom_' +(index+1).toString() + '" id="' + chnid + '_custom_' + index + '" anno="sequence" chain="' + chnid + '" title="' + title + '">' + title + '</div>';
39950
+ }
39874
39951
  }
39875
39952
  }
39876
39953
 
@@ -39882,12 +39959,37 @@ class AddTrack {
39882
39959
 
39883
39960
  html += htmlTmp;
39884
39961
  html2 += htmlTmp2 + htmlTmp;
39962
+ htmlExon += htmlTmp;
39885
39963
 
39886
39964
  html3 += '</div>';
39965
+ html3Exon += '</div>';
39966
+
39967
+ if(!exonArray) {
39968
+ $("#" + ic.pre + "dt_custom_" + chnid + "_" + simpTitle).html(html);
39969
+ $("#" + ic.pre + "ov_custom_" + chnid + "_" + simpTitle).html(html2);
39970
+ $("#" + ic.pre + "tt_custom_" + chnid + "_" + simpTitle).html(html3);
39971
+ }
39972
+ else {
39973
+ $("#" + ic.pre + "dt_custom_" + chnid + "_" + simpTitle).html(htmlExon + html);
39974
+ $("#" + ic.pre + "ov_custom_" + chnid + "_" + simpTitle).html(html2);
39975
+ $("#" + ic.pre + "tt_custom_" + chnid + "_" + simpTitle).html(html3Exon + html3);
39976
+ }
39977
+ }
39887
39978
 
39888
- $("#" + ic.pre + "dt_custom_" + chnid + "_" + simpTitle).html(html);
39889
- $("#" + ic.pre + "ov_custom_" + chnid + "_" + simpTitle).html(html2);
39890
- $("#" + ic.pre + "tt_custom_" + chnid + "_" + simpTitle).html(html3);
39979
+ getExonHtml(exonIndex, colorGradient, from, to, genomeRange) { let ic = this.icn3d; ic.icn3dui;
39980
+ return '<div style="display:inline-block; color:white!important; width:' + Math.round(ic.seqAnnWidth *(to - from + 1) /(ic.maxAnnoLength + ic.nTotalGap)) + 'px;" class="icn3d-seqTitle icn3d-link icn3d-blue" title="Exon ' + (exonIndex + 1) + ': ' + genomeRange + ' genomic interval" anno="sequence" title="' + (exonIndex + 1) + '"><div style="height: 12px; border: 1px solid #000; background: linear-gradient(to right, ' + colorGradient + ');"></div></div>';
39981
+ }
39982
+
39983
+ getExonColor(start, end, pos) { let ic = this.icn3d; ic.icn3dui;
39984
+ let middle = ( start + end) * 0.5;
39985
+ if(pos < middle) {
39986
+ let gb = parseInt((pos - start) / (middle - start) * 255);
39987
+ return "rgb(255, " + gb + ", " + gb + ")";
39988
+ }
39989
+ else {
39990
+ let rg = parseInt((end - pos) / (end - middle) * 255);
39991
+ return "rgb(" + rg + ", " + rg + ", 255)";
39992
+ }
39891
39993
  }
39892
39994
 
39893
39995
  alignSequenceToStructure(chainid, data, title) { let ic = this.icn3d, me = ic.icn3dui;
@@ -40195,6 +40297,7 @@ class AddTrack {
40195
40297
  let result = this.getFullText(text);
40196
40298
  text = result.text;
40197
40299
  this.showNewTrack(chainid, title, text, undefined, undefined, type, color, bMsa);
40300
+
40198
40301
  return false;
40199
40302
  }
40200
40303
 
@@ -40317,6 +40420,726 @@ class AddTrack {
40317
40420
  }
40318
40421
  }
40319
40422
 
40423
+ async getMsa(acclist, firstAcc, chainSeq) { let ic = this.icn3d, me = ic.icn3dui;
40424
+ let trackTitleArray = [firstAcc], trackSeqArray = [];
40425
+ // get all seq
40426
+ let url = me.htmlCls.baseUrl + "/vastdyn/vastdyn.cgi?chainlist=" + acclist;
40427
+ let data = await me.getAjaxPromise(url, 'jsonp');
40428
+ let maxLen = 0, maxIndex = 0, index = 0;
40429
+ //let seqArray = [];
40430
+ for(let acc in data) {
40431
+ let seq = data[acc];
40432
+ //seqArray.push(seq);
40433
+
40434
+ let pos = acc.indexOf('.');
40435
+ if(pos != -1) {
40436
+ acc = acc.substr(0, pos);
40437
+ }
40438
+ trackTitleArray.push(acc);
40439
+
40440
+ if(seq.length > maxLen) {
40441
+ maxLen = seq.length;
40442
+ maxIndex = index;
40443
+ }
40444
+ ++index;
40445
+ }
40446
+
40447
+ // pairwise align each seq to the one with maxIndex
40448
+ url = me.htmlCls.baseUrl + 'pwaln/pwaln.fcgi?from=msa';
40449
+
40450
+ let accArray = acclist.split(',');
40451
+ // oroginal index, chain as the first one
40452
+ let acc2index = {};
40453
+ acc2index[firstAcc] = 0;
40454
+ for(let i = 0, il = accArray.length; i < il; ++i) {
40455
+ acc2index[accArray[i]] = i + 1;
40456
+ }
40457
+ let targetId = accArray[maxIndex];
40458
+ accArray.splice(maxIndex, 1);
40459
+
40460
+ let queries = (chainSeq) ? chainSeq : firstAcc;
40461
+ if(accArray.length > 0) queries += ',' + accArray.join(',');
40462
+
40463
+ let dataObj = {'targets': targetId, 'queries': queries};
40464
+ let alignData = await me.getAjaxPostPromise(url, dataObj);
40465
+
40466
+ if(!alignData.data) {
40467
+ console.log("The protein accessions " + targetId + "," + queries + " can not be aligned...");
40468
+ return;
40469
+ }
40470
+
40471
+ // get aligned length for each pair
40472
+ let index_alignLen = [];
40473
+ ic.qt_start_end = {};
40474
+ // target: targetId
40475
+ // queries: accArray
40476
+ let accArrayFound = [], querySeqArray = [];
40477
+ let firstKey = Object.keys(alignData.targets)[0];
40478
+ let targetSeq = alignData.targets[firstKey].seqdata;
40479
+
40480
+ //add firstAcc to accArray
40481
+ accArray.splice(0, 0, firstAcc);
40482
+
40483
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
40484
+ let query, target;
40485
+
40486
+ if(!alignData.data[index]) {
40487
+ continue;
40488
+ }
40489
+
40490
+ query = alignData.data[index].query;
40491
+ let acc;
40492
+ if(query.acc.length <= 5) { // PDB
40493
+ acc = query.acc.substr(0, 4) + '_' + query.acc.substr(4, 1);
40494
+ }
40495
+ else {
40496
+ acc = query.acc;
40497
+ }
40498
+
40499
+ if(index == 0) acc = firstAcc;
40500
+
40501
+ accArrayFound.push(acc);
40502
+
40503
+ firstKey = Object.keys(alignData.data[index].targets)[0];
40504
+ target = alignData.data[index].targets[firstKey];
40505
+
40506
+ target = target.hsps[0];
40507
+
40508
+ querySeqArray.push(query.seqdata);
40509
+ let alignLen = target.scores.num_ident * 100 + query.sz; // order by aligned seq length, then seq length
40510
+
40511
+ ic.qt_start_end[index] = [];
40512
+
40513
+ let segArray = target.segs;
40514
+ for(let i = 0, il = segArray.length; i < il; ++i) {
40515
+ let seg = segArray[i];
40516
+ let qt_start_end = {t_start: seg.orifrom, t_end: seg.orito, q_start: seg.from, q_end: seg.to};
40517
+ ic.qt_start_end[index].push(qt_start_end);
40518
+ }
40519
+
40520
+ index_alignLen.push({index: index, alignLen: alignLen});
40521
+ }
40522
+
40523
+ accArray = accArrayFound;
40524
+
40525
+ index_alignLen.sort(function(a,b){
40526
+ return b.alignLen - a.alignLen;
40527
+ });
40528
+
40529
+ // start and end of MSA
40530
+ let start_t = 9999, end_t = -1;
40531
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
40532
+ if(!ic.qt_start_end[index]) continue;
40533
+
40534
+ for(let i = 0, il = ic.qt_start_end[index].length; i < il; ++i) {
40535
+ let start1, end1;
40536
+
40537
+ start1 = ic.qt_start_end[index][i].t_start;
40538
+ end1 = ic.qt_start_end[index][i].t_end;
40539
+
40540
+ for(let j = start1; j <= end1; ++j) {
40541
+ if(j < start_t) start_t = j;
40542
+ if(j > end_t) end_t = j;
40543
+ }
40544
+ }
40545
+ }
40546
+
40547
+ // N- and C-terminal residues
40548
+ let maxNtermLen = start_t, maxCtermLen = targetSeq.length - (end_t + 1);
40549
+ let startArray = [], endArray = [];
40550
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
40551
+ if(!ic.qt_start_end[index]) continue;
40552
+
40553
+ let qPos = ic.qt_start_end[index][0].q_start;
40554
+ startArray.push(qPos);
40555
+ if(maxNtermLen < qPos) maxNtermLen = qPos;
40556
+
40557
+ let lastIndex = ic.qt_start_end[index].length - 1;
40558
+ qPos = ic.qt_start_end[index][lastIndex].q_end;
40559
+ endArray.push(qPos);
40560
+ let dist = querySeqArray[index].length - (qPos + 1);
40561
+ if(maxCtermLen < dist) maxCtermLen = dist;
40562
+ }
40563
+
40564
+ ic.msaSeq = {};
40565
+ // assign the template
40566
+ ic.msaSeq[targetId] = '';
40567
+
40568
+ for(let i = start_t; i <= end_t; ++i) {
40569
+ ic.msaSeq[targetId] += targetSeq[i];
40570
+ }
40571
+
40572
+ // progressively merge sequences, starting from most similar to least similar
40573
+ let alignedChainIndice = [0];
40574
+ for(let arrayIndex = 0, arrayIndexl = index_alignLen.length; arrayIndex < arrayIndexl; ++arrayIndex) {
40575
+ let index = index_alignLen[arrayIndex].index;
40576
+ alignedChainIndice.push(index);
40577
+
40578
+ ic.msaSeq[accArray[index]] = '';
40579
+
40580
+ // some proteins may not be aligned
40581
+ if(!querySeqArray[index]) continue;
40582
+
40583
+ ic.setSeqAlignCls.mergeTwoSeqForAllSimple(targetId, accArray, index, alignedChainIndice, start_t, end_t, querySeqArray);
40584
+ }
40585
+
40586
+ // add N-terminal seq
40587
+ let seqN = '', cnt;
40588
+ for(let i = 0; i < maxNtermLen - start_t; ++i) {
40589
+ seqN += '-';
40590
+ }
40591
+ for(let i = 0; i < start_t; ++i) {
40592
+ seqN += targetSeq[i];
40593
+ }
40594
+ ic.msaSeq[targetId] = seqN + ic.msaSeq[targetId];
40595
+
40596
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
40597
+ seqN = '';
40598
+ for(let i = 0; i < maxNtermLen - startArray[index]; ++i) {
40599
+ seqN += '-';
40600
+ }
40601
+ for(let i = 0; i < startArray[index]; ++i) {
40602
+ seqN += querySeqArray[index][i];
40603
+ }
40604
+
40605
+ ic.msaSeq[accArray[index]] = seqN + ic.msaSeq[accArray[index]];
40606
+ }
40607
+
40608
+ // add C-terminal seq
40609
+ for(let i = end_t + 1; i < targetSeq.length; ++i) {
40610
+ ic.msaSeq[targetId] += targetSeq[i];
40611
+ }
40612
+
40613
+ cnt = targetSeq.length - (end_t + 1);
40614
+ for(let i = 0; i < maxCtermLen - cnt; ++i) {
40615
+ ic.msaSeq[targetId] += '-';
40616
+ }
40617
+
40618
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
40619
+ for(let i = endArray[index] + 1; i < querySeqArray[index].length; ++i) {
40620
+ ic.msaSeq[accArray[index]] += querySeqArray[index][i];
40621
+ }
40622
+
40623
+ cnt = querySeqArray[index].length - (endArray[index] + 1);
40624
+ for(let i = 0; i < maxCtermLen - cnt; ++i) {
40625
+ ic.msaSeq[accArray[index]] += '-';
40626
+ }
40627
+ }
40628
+
40629
+ for(let acc in ic.msaSeq) {
40630
+ let index = acc2index[acc];
40631
+ trackSeqArray[index] = ic.msaSeq[acc];
40632
+ trackTitleArray[index] = acc;
40633
+ }
40634
+
40635
+ // some of the protein may not be aligned
40636
+ let trackTitleArrayFinal = [], trackSeqArrayFinal = [];
40637
+ for(let i = 0, il = trackSeqArray.length; i < il; ++i) {
40638
+ if(trackSeqArray[i]) {
40639
+ trackSeqArrayFinal.push(trackSeqArray[i]);
40640
+ trackTitleArrayFinal.push(trackTitleArray[i]);
40641
+ }
40642
+ }
40643
+
40644
+ let seqFirst = trackSeqArrayFinal[0];
40645
+
40646
+ trackSeqArrayFinal.splice(0, 1);
40647
+ trackTitleArrayFinal.splice(0, 1);
40648
+
40649
+ return {trackTitleArray: trackTitleArrayFinal, trackSeqArray: trackSeqArrayFinal, seqFirst: seqFirst};
40650
+ }
40651
+
40652
+ async getIsoformMsa(acclist, acc2exons) { let ic = this.icn3d, me = ic.icn3dui;
40653
+ let trackTitleArray = [], trackSeqArray = [];
40654
+ // get all seq
40655
+ let url = me.htmlCls.baseUrl + "/vastdyn/vastdyn.cgi?chainlist=" + acclist;
40656
+ let data = await me.getAjaxPromise(url, 'jsonp');
40657
+ let maxLen = 0, maxIndex = 0, index = 0;
40658
+ let accArray = [], querySeqArray = [];
40659
+ for(let acc in data) {
40660
+ let seq = data[acc];
40661
+ querySeqArray.push(seq);
40662
+
40663
+ let pos = acc.indexOf('.');
40664
+ if(pos != -1) {
40665
+ acc = acc.substr(0, pos);
40666
+ }
40667
+ accArray.push(acc);
40668
+
40669
+ if(seq.length > maxLen) {
40670
+ maxLen = seq.length;
40671
+ maxIndex = index;
40672
+ }
40673
+ ++index;
40674
+ }
40675
+
40676
+ // get aligned length for each pair
40677
+ ic.qt_start_end = {};
40678
+
40679
+ // use the genomic interval as the alignment template
40680
+ let targetId = 'genomeRes';
40681
+
40682
+ let acc2index = {};
40683
+
40684
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
40685
+ let acc = accArray[index];
40686
+
40687
+ acc2index[acc] = index;
40688
+
40689
+ ic.qt_start_end[index] = [];
40690
+
40691
+ let segArray = acc2exons[acc];
40692
+
40693
+ for(let i = 0, il = segArray.length; i < il; ++i) {
40694
+ let seg = segArray[i];
40695
+
40696
+ // mRNA has the reverse order, use negative to make the order right, then minus the offset
40697
+ let qt_start_end = {t_start: ic.exonOrder * seg.genResStart, t_end: ic.exonOrder * seg.genResEnd, q_start: seg.resStart, q_end: seg.resEnd};
40698
+ ic.qt_start_end[index].push(qt_start_end);
40699
+ }
40700
+ }
40701
+
40702
+ // start and end of MSA
40703
+ let start_t = 999999999, end_t = -999999999;
40704
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
40705
+ if(!ic.qt_start_end[index]) continue;
40706
+
40707
+ for(let i = 0, il = ic.qt_start_end[index].length; i < il; ++i) {
40708
+ let start1, end1;
40709
+
40710
+ start1 = ic.qt_start_end[index][i].t_start;
40711
+ end1 = ic.qt_start_end[index][i].t_end;
40712
+
40713
+ for(let j = start1; j <= end1; ++j) {
40714
+ if(j < start_t) start_t = j;
40715
+ if(j > end_t) end_t = j;
40716
+ }
40717
+ }
40718
+ }
40719
+
40720
+ // minus the offset start_t
40721
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
40722
+ let segArray = ic.qt_start_end[index];
40723
+ for(let i = 0, il = segArray.length; i < il; ++i) {
40724
+ let seg = segArray[i];
40725
+ seg.t_start -= start_t;
40726
+ seg.t_end -= start_t;
40727
+ }
40728
+ }
40729
+
40730
+ ic.msaSeq = {};
40731
+ // assign the template
40732
+ ic.msaSeq[targetId] = '';
40733
+
40734
+ let start_tFinal = 0;
40735
+ let end_tFinal = end_t - start_t;
40736
+
40737
+ for(let i = start_tFinal; i <= end_tFinal; ++i) {
40738
+ ic.msaSeq[targetId] += 'X'; // fake seq
40739
+ }
40740
+
40741
+ // progressively merge sequences, starting from most similar to least similar
40742
+ let alignedChainIndice = [0];
40743
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
40744
+ alignedChainIndice.push(index);
40745
+
40746
+ ic.msaSeq[accArray[index]] = '';
40747
+
40748
+ // some proteins may not be aligned
40749
+ if(!querySeqArray[index]) continue;
40750
+ ic.setSeqAlignCls.mergeTwoSeqForAllSimple(targetId, accArray, index, alignedChainIndice, start_tFinal, end_tFinal, querySeqArray);
40751
+ }
40752
+
40753
+ for(let acc in ic.msaSeq) {
40754
+ let index = acc2index[acc];
40755
+
40756
+ if(index !== undefined) {
40757
+ trackSeqArray[index] = ic.msaSeq[acc];
40758
+ trackTitleArray[index] = acc;
40759
+ }
40760
+ }
40761
+
40762
+ // remove introns in trackSeqArray
40763
+ let trackSeqArrayFinal = [];
40764
+ for(let i = 0, il = trackSeqArray.length; i < il; ++i) {
40765
+ trackSeqArrayFinal[i] = '';
40766
+ }
40767
+
40768
+ for(let j = 0, jl = trackSeqArray[maxIndex].length; j < jl; ++j) {
40769
+ let seq = trackSeqArray[maxIndex][j];
40770
+
40771
+ let bExon = (seq != '-') ? true : false;
40772
+ if(!bExon) {
40773
+ for(let i = 0, il = trackSeqArray.length; i < il; ++i) {
40774
+ if(trackSeqArray[i][j] != '-') {
40775
+ bExon = true;
40776
+ break;
40777
+ }
40778
+ }
40779
+ }
40780
+
40781
+ if(bExon) {
40782
+ for(let i = 0, il = trackSeqArray.length; i < il; ++i) {
40783
+ trackSeqArrayFinal[i] += trackSeqArray[i][j];
40784
+ }
40785
+ }
40786
+ }
40787
+
40788
+ return {trackTitleArray: trackTitleArray, trackSeqArray: trackSeqArrayFinal, maxIndex: maxIndex};
40789
+ }
40790
+
40791
+ async showMsaTracks(chainid, seqFirst, trackTitleArray, trackSeqArray, startpos, type, acc2exons) { let ic = this.icn3d; ic.icn3dui;
40792
+ //ic.startposGiSeq = undefined;
40793
+ for(let i = 0, il = ic.chainsSeq[chainid].length; i < il; ++i) {
40794
+ //let pos =(i >= ic.matchedPos[chainid] && i - ic.matchedPos[chainid] < ic.chainsSeq, [chainid].length) ? ic.chainsSeq[chainid][i - ic.matchedPos[chainid]].resi : ic.baseResi[chainid] + 1 + i;
40795
+ let pos = ic.ParserUtilsCls.getResi(chainid, i);
40796
+
40797
+ if(pos != startpos) {
40798
+ continue;
40799
+ }
40800
+ else {
40801
+ ic.startposGiSeq = i;
40802
+ }
40803
+ }
40804
+
40805
+ if(ic.startposGiSeq === undefined) {
40806
+ var aaa = 1; //alert("Please double check the start position before clicking \"Add Track\"");
40807
+ return;
40808
+ }
40809
+
40810
+ // set up gap for the master seq
40811
+ // don't count gaps in both ends
40812
+ ic.targetGapHash = {};
40813
+ let prevSeq = '-', prevPos = 0, from, to, cnt = 0, dashCnt = 0;
40814
+ let bFound = false, seqStart = 0, seqEnd = 0, seqLength = seqFirst.length;
40815
+ // add gaps to the N- and C-terminal
40816
+ if(!ic.seqStartLen) ic.seqStartLen = {};
40817
+ if(!ic.seqEndLen) ic.seqEndLen = {};
40818
+ for(let i = 0, il = seqFirst.length; i < il; ++i) {
40819
+ if(seqFirst[i] == '-' && seqFirst[i] != prevSeq) { // start of gap
40820
+ from = cnt;
40821
+ dashCnt = 0;
40822
+ }
40823
+
40824
+ if(prevSeq == '-' && seqFirst[i] != prevSeq && cnt > 0) { // end of gap
40825
+ to = prevPos;
40826
+ ic.targetGapHash[from + ic.startposGiSeq] = {'from': from + ic.startposGiSeq, 'to': to + dashCnt - 1 + ic.startposGiSeq};
40827
+ }
40828
+
40829
+ prevSeq = seqFirst[i];
40830
+ prevPos = cnt;
40831
+
40832
+ if(seqFirst[i] != '-') {
40833
+ ++cnt;
40834
+ seqEnd = i;
40835
+ ic.seqEndLen[chainid] = seqLength - 1 - seqEnd;
40836
+
40837
+ if(!bFound) {
40838
+ seqStart = i;
40839
+ ic.seqStartLen[chainid] = seqStart;
40840
+
40841
+ bFound = true;
40842
+ }
40843
+ }
40844
+ else {
40845
+ ++dashCnt;
40846
+ }
40847
+ }
40848
+
40849
+ // adjust the total length
40850
+ if(ic.maxAnnoLength < ic.maxAnnoLengthOri + ic.seqStartLen[chainid] + ic.seqEndLen[chainid]) {
40851
+ ic.maxAnnoLength = ic.maxAnnoLengthOri + ic.seqStartLen[chainid] + ic.seqEndLen[chainid];
40852
+ }
40853
+
40854
+ await ic.annotationCls.resetAnnoAll();
40855
+
40856
+ let targetGapHashStr = '';
40857
+ let cntTmp = 0;
40858
+ for(let i in ic.targetGapHash) {
40859
+ if(cntTmp > 0) targetGapHashStr += ' ';
40860
+ targetGapHashStr += i + '_' + ic.targetGapHash[i].from + '_' + ic.targetGapHash[i].to;
40861
+ ++cntTmp;
40862
+ }
40863
+
40864
+ //me.htmlCls.clickMenuCls.setLogCmd("msa | " + targetGapHashStr, true);
40865
+
40866
+ // add tracks
40867
+ let resi2cntSameRes = {}; // count of same residue at each position
40868
+ for(let j = 0, jl = trackSeqArray.length; j < jl; ++j) {
40869
+ let resi = startpos;
40870
+ let text = '';
40871
+ for(let k = 0; k < ic.startposGiSeq; ++k) {
40872
+ if(ic.targetGapHash.hasOwnProperty(k)) {
40873
+ for(let m = 0; m < ic.targetGapHash[k].to - ic.targetGapHash[k].from + 1; ++m) {
40874
+ text += '-';
40875
+ }
40876
+ }
40877
+
40878
+ text += '-';
40879
+ }
40880
+
40881
+ let resn, prevResn = '-';
40882
+ let fromArray = [], toArray = [];
40883
+ let bFound = false;
40884
+ let seqStartLen = 0;
40885
+ // for(let k = seqStart; k <= seqEnd; ++k) {
40886
+ for(let k = 0; k < seqLength; ++k) {
40887
+ //if(seqFirst[k] == '-') continue;
40888
+
40889
+ if(j == 0) resi2cntSameRes[resi] = 0;
40890
+
40891
+ resn = trackSeqArray[j][k];
40892
+
40893
+ if(prevResn == '-' && resn != '-') {
40894
+ fromArray.push(k);
40895
+ }
40896
+
40897
+ if(prevResn != '-' && resn == '-') {
40898
+ toArray.push(k - 1);
40899
+ }
40900
+
40901
+ if(resn != '-') {
40902
+ if(!bFound) {
40903
+ seqStartLen = k;
40904
+ bFound = true;
40905
+ }
40906
+ }
40907
+
40908
+ text += resn; //ic.giSeq[chainid][i];
40909
+
40910
+ if(seqFirst[k] != '-') {
40911
+ if(seqFirst[k] == trackSeqArray[j][k]) ++resi2cntSameRes[resi];
40912
+ ++resi;
40913
+ }
40914
+
40915
+ prevResn = resn;
40916
+ }
40917
+
40918
+ // last one
40919
+ if(prevResn != '-') {
40920
+ toArray.push(seqLength - 1);
40921
+ }
40922
+
40923
+ let title =(trackTitleArray[j].length < 20) ? trackTitleArray[j] : trackTitleArray[j].substr(0, 20) + '...';
40924
+ let bMsa = true;
40925
+ let exonArray = (acc2exons) ? acc2exons[trackTitleArray[j]] : undefined;
40926
+ this.showNewTrack(chainid, title, text, undefined, undefined, type, undefined, bMsa, fromArray, toArray, seqStartLen, exonArray);
40927
+ }
40928
+
40929
+ // set color for the master seq
40930
+ if(trackSeqArray.length > 0) {
40931
+ if(ic.queryresi2score === undefined) ic.queryresi2score = {};
40932
+ if(ic.queryresi2score[chainid] === undefined) ic.queryresi2score[chainid] = {};
40933
+
40934
+ let nSeq = trackSeqArray.length;
40935
+ for(let resi in resi2cntSameRes) {
40936
+ let score = parseInt(resi2cntSameRes[resi] / nSeq * 100);
40937
+ ic.queryresi2score[chainid][resi] = score;
40938
+ }
40939
+
40940
+ let resiArray = Object.keys(resi2cntSameRes);
40941
+ let start = Math.min.apply(null, resiArray);
40942
+ let end = Math.max.apply(null, resiArray);
40943
+
40944
+ let resiScoreStr = '';
40945
+ for(let resi = start; resi <= end; ++resi) {
40946
+ if(resi2cntSameRes.hasOwnProperty(resi)) {
40947
+ resiScoreStr += Math.round(resi2cntSameRes[resi] / nSeq * 9); // max 9
40948
+ }
40949
+ else {
40950
+ resiScoreStr += '_';
40951
+ }
40952
+ }
40953
+
40954
+ ic.opts['color'] = 'align custom';
40955
+ ic.setColorCls.setColorByOptions(ic.opts, ic.hAtoms);
40956
+
40957
+ ic.hlUpdateCls.updateHlAll();
40958
+
40959
+ ic.drawCls.draw();
40960
+
40961
+ //me.htmlCls.clickMenuCls.setLogCmd('color align custom | ' + chainid + ' | range ' + start + '_' + end + ' | ' + resiScoreStr, true);
40962
+ }
40963
+ }
40964
+
40965
+ processAccList(acclist) { let ic = this.icn3d; ic.icn3dui;
40966
+ // remove version from acc
40967
+ let accArray = acclist.split(',');
40968
+ let accHash = {};
40969
+ let acclistTmp = '';
40970
+ for(let i = 0, il = accArray.length; i < il; ++i) {
40971
+ let acc = accArray[i];
40972
+
40973
+ if(accHash.hasOwnProperty(acc)) {
40974
+ continue;
40975
+ }
40976
+ else {
40977
+ accHash[acc] = 1;
40978
+ }
40979
+
40980
+ let pos = acc.indexOf('.');
40981
+ if(pos != -1) {
40982
+ acclistTmp += acc.substr(0, pos);
40983
+ }
40984
+ else {
40985
+ acclistTmp += acc;
40986
+ }
40987
+
40988
+ if(i < accArray.length - 1) {
40989
+ acclistTmp += ',';
40990
+ }
40991
+ }
40992
+
40993
+ return acclistTmp;
40994
+ }
40995
+
40996
+ async addExonTracks(chainid, geneid, startpos, type) { let ic = this.icn3d, me = ic.icn3dui;
40997
+ let thisClass = this;
40998
+
40999
+ let seqFirst, trackTitleArray = [], trackSeqArray = [];
41000
+
41001
+ // get acclist from geneid
41002
+ let url = me.htmlCls.baseUrl + "/vastdyn/vastdyn.cgi?geneid2isoforms=" + geneid;
41003
+ let data = await me.getAjaxPromise(url, 'jsonp');
41004
+ let accArray = data.acclist;
41005
+ let exons = data.exons;
41006
+ let acc2exons = {};
41007
+
41008
+ let acclist = '';
41009
+ ic.exonOrder = 1; // 1: increasing bp order; -1 decreasing bp order
41010
+ for(let i = 0, il = accArray.length; i < il; ++i) {
41011
+ let accOri = accArray[i];
41012
+ let pos = accOri.indexOf('.');
41013
+ let acc = (pos != -1) ? accOri.substr(0, pos) : accOri;
41014
+
41015
+ let cntTotal = 0, prevCntTotal = 0, rangeArray = [];
41016
+ for(let j = 0, jl = exons[accOri].length; j < jl; ++j) {
41017
+ let itemArray = exons[accOri][j].split('-');
41018
+ itemArray[0] = parseInt(itemArray[0]);
41019
+ itemArray[1] = parseInt(itemArray[1]);
41020
+ itemArray[2] = parseInt(itemArray[2]);
41021
+
41022
+ ic.exonOrder = (itemArray[0] < itemArray[1]) ? 1 : -1;
41023
+
41024
+ let genomeRange = itemArray[0] + '-' + itemArray[1];
41025
+ let cnt = (j == jl - 1) ? itemArray[2] - 3 : itemArray[2]; // The last one is stop codeon
41026
+ cntTotal += cnt;
41027
+
41028
+ let resStart = parseInt(prevCntTotal/3.0 + 0.5); // 0-based
41029
+ let resEnd = parseInt(cntTotal/3.0 + 0.5) - 1; // 0-based
41030
+
41031
+ let genResStart = parseInt(itemArray[0] / 3.0 + 0.5);
41032
+
41033
+ //let genResEnd = parseInt(itemArray[1] / 3.0 + 0.5); // some difference due to round
41034
+ let genResEnd = genResStart + ic.exonOrder * (resEnd - resStart);
41035
+
41036
+ rangeArray.push({genomeRange: genomeRange, genResStart: genResStart, genResEnd: genResEnd, resStart: resStart, resEnd: resEnd});
41037
+
41038
+ prevCntTotal = cntTotal;
41039
+ }
41040
+ acc2exons[acc] = rangeArray;
41041
+
41042
+ acclist += acc;
41043
+ if(i < il - 1) {
41044
+ acclist += ',';
41045
+ }
41046
+ }
41047
+
41048
+ let result = await this.getIsoformMsa(acclist, acc2exons);
41049
+ trackTitleArray = result.trackTitleArray;
41050
+ trackSeqArray = result.trackSeqArray;
41051
+ //seqFirst = result.seqFirst;
41052
+ let maxIndex = result.maxIndex;
41053
+
41054
+ let acclist2 = trackTitleArray[maxIndex];
41055
+ let structure = chainid.substr(0, chainid.indexOf('_'));
41056
+ let firstAcc;
41057
+ if(structure.length > 5) {
41058
+ if(ic.uniprot2acc && ic.uniprot2acc[structure]) structure = ic.uniprot2acc[structure];
41059
+ firstAcc = structure;
41060
+ }
41061
+ else {
41062
+ firstAcc = chainid;
41063
+ }
41064
+
41065
+ // get the sequence from iCn3D because a uniProt ID can not be retrieved in pwaln.fcgi
41066
+ if(structure.length > 5) {
41067
+ let chainSeq = '';
41068
+ for(let i = 0, il = ic.chainsSeq.length; i < il; ++i) {
41069
+ chainSeq += ic.chainsSeq[i].resn;
41070
+ }
41071
+
41072
+ result = await this.getMsa(acclist2, firstAcc, chainSeq);
41073
+ }
41074
+ else {
41075
+ result = await this.getMsa(acclist2, firstAcc);
41076
+ }
41077
+
41078
+ result.trackTitleArray;
41079
+ let trackSeqArray2 = result.trackSeqArray;
41080
+ seqFirst = result.seqFirst;
41081
+
41082
+ // merge trackTitleArray2[0] with trackSeqArray[maxIndex]
41083
+ let A = trackSeqArray[maxIndex], B = trackSeqArray2[0];
41084
+ let i = 0, j = 0;
41085
+
41086
+ let ALen = trackSeqArray.length;
41087
+
41088
+ while (i < A.length && j < B.length) {
41089
+ if(A[i] != B[j]) {
41090
+ if(A[i] == '-') {
41091
+ // inser "-" in B
41092
+ B = B.substr(0, j) + '-' + B.substr(j);
41093
+ seqFirst = seqFirst.substr(0, j) + '-' + seqFirst.substr(j);
41094
+ }
41095
+ else { //if(B[j] == '-') {
41096
+ // inser "-" in A
41097
+ for(let k = 0; k < ALen; ++k) {
41098
+ trackSeqArray[k] = trackSeqArray[k].substr(0, i) + '-' + trackSeqArray[k].substr(i);
41099
+ }
41100
+ }
41101
+ }
41102
+
41103
+ ++i;
41104
+ ++j;
41105
+ }
41106
+
41107
+ await thisClass.showMsaTracks(chainid, seqFirst, trackTitleArray, trackSeqArray, startpos, type, acc2exons);
41108
+
41109
+ me.htmlCls.clickMenuCls.setLogCmd("add exon track | chainid " + chainid + " | geneid " + geneid + " | startpos " + startpos + " | type " + type, true);
41110
+ }
41111
+
41112
+ async addMsaTracks(chainid, startpos, type, fastaList) { let ic = this.icn3d, me = ic.icn3dui;
41113
+ let thisClass = this;
41114
+
41115
+ let seqFirst, trackTitleArray = [], trackSeqArray = [];
41116
+
41117
+ let fastaArray = fastaList.split('>');
41118
+
41119
+ // the first array item is empty
41120
+ // the second array item is the sequence of the structure, start with i = 2
41121
+ let posFirst = fastaArray[1].indexOf('\n');
41122
+ //let titleFirst = fastaArray[1].substr(0, posFirst);
41123
+ seqFirst = fastaArray[1].substr(posFirst + 1).replace(/\n/g, '');
41124
+
41125
+ for(let i = 2, il = fastaArray.length; i < il; ++i) {
41126
+ let pos = fastaArray[i].indexOf('\n');
41127
+ let title = fastaArray[i].substr(0, pos);
41128
+ if(title.indexOf('|') != -1) {
41129
+ title = title.split('|')[1];
41130
+ // if(title.indexOf('.') != -1) {
41131
+ // title = title.split('.')[0];
41132
+ // }
41133
+ }
41134
+ trackTitleArray.push(title);
41135
+ let seq = fastaArray[i].substr(pos + 1).replace(/\n/g, '');
41136
+ trackSeqArray.push(seq);
41137
+ }
41138
+
41139
+ await thisClass.showMsaTracks(chainid, seqFirst, trackTitleArray, trackSeqArray, startpos, type);
41140
+
41141
+ me.htmlCls.clickMenuCls.setLogCmd("add msa track | chainid " + chainid + " | startpos " + startpos + " | type " + type + " | fastaList " + fastaList , true);
41142
+ }
40320
41143
  }
40321
41144
 
40322
41145
  /**
@@ -40967,13 +41790,14 @@ class ShowAnno {
40967
41790
  } // if(me.cfg.mmdbid
40968
41791
  } // for(let i = 0
40969
41792
 
40970
- ic.maxAnnoLength = 1;
41793
+ ic.maxAnnoLengthOri = 1;
40971
41794
  for(let chainid in ic.chainsSeq) {
40972
41795
  // use protein or nucleotide as the max length
40973
- if(ic.chainsSeq[chainid].length > ic.maxAnnoLength && (ic.protein_chainid.hasOwnProperty(chainid) || nucleotide_chainid.hasOwnProperty(chainid)) ) {
40974
- ic.maxAnnoLength = ic.chainsSeq[chainid].length;
41796
+ if(ic.chainsSeq[chainid].length > ic.maxAnnoLengthOri && (ic.protein_chainid.hasOwnProperty(chainid) || nucleotide_chainid.hasOwnProperty(chainid)) ) {
41797
+ ic.maxAnnoLengthOri = ic.chainsSeq[chainid].length;
40975
41798
  }
40976
41799
  }
41800
+ ic.maxAnnoLength = ic.maxAnnoLengthOri;
40977
41801
  }
40978
41802
 
40979
41803
  return {'nucleotide_chainid': nucleotide_chainid, 'chemical_chainid': chemical_chainid, 'chemical_set': chemical_set};
@@ -40988,7 +41812,7 @@ class ShowAnno {
40988
41812
  let chemical_chainid = result.chemical_chainid;
40989
41813
  let chemical_set = result.chemical_set;
40990
41814
 
40991
- if(ic.bAnnoShown === undefined || !ic.bAnnoShown || ic.bResetAnno) { // ic.bResetAnno when loading another structure
41815
+ if(!ic.bAnnoShown || ic.bResetAnno) { // ic.bResetAnno when loading another structure
40992
41816
  // assign early to avoid load annotations twice
40993
41817
  ic.bAnnoShown = true;
40994
41818
 
@@ -41129,13 +41953,52 @@ class ShowAnno {
41129
41953
  let thisClass = this;
41130
41954
  let chnidBaseArray = $.map(ic.protein_chainid, function(v) { return v; });
41131
41955
  let index = 0;
41956
+
41957
+ // get geneid
41958
+ if(!ic.chainsGene) ic.chainsGene = {};
41959
+ for(let chnid in ic.protein_chainid) {
41960
+ let structure = chnid.substr(0, chnid.indexOf('_'));
41961
+ // UniProt or NCBI protein accession
41962
+ if(structure.length > 5) {
41963
+ let refseqid, url;
41964
+ if(ic.uniprot2acc && ic.uniprot2acc[structure]) {
41965
+ refseqid = ic.uniprot2acc[structure];
41966
+ }
41967
+ else {
41968
+ try {
41969
+ if(!ic.uniprot2acc) ic.uniprot2acc = {};
41970
+ url = me.htmlCls.baseUrl + "vastdyn/vastdyn.cgi?uniprot2refseq=" + structure;
41971
+ let result = await me.getAjaxPromise(url, 'jsonp');
41972
+ refseqid = (result && result.refseq) ? result.refseq : structure;
41973
+
41974
+ ic.uniprot2acc[structure] = refseqid;
41975
+ }
41976
+ catch {
41977
+ console.log("Problem in getting protein accession from UniProt ID...");
41978
+ refseqid = structure;
41979
+ }
41980
+ }
41981
+
41982
+ // get Gene info from protein name
41983
+ // url = me.htmlCls.baseUrl + "vastdyn/vastdyn.cgi?protein2gene=" + refseqid;
41984
+ // ic.chainsGene[chnid] = await me.getAjaxPromise(url, 'jsonp');
41985
+
41986
+ // get Gene info from uniprot
41987
+ url = "https://rest.uniprot.org/uniprotkb/search?format=json&fields=xref_geneid,gene_names&query=" + structure;
41988
+ let geneData = await me.getAjaxPromise(url, 'json');
41989
+ let geneId = (geneData.results[0] && geneData.results[0].uniProtKBCrossReferences && geneData.results[0].uniProtKBCrossReferences[0]) ? geneData.results[0].uniProtKBCrossReferences[0].id : undefined;
41990
+ let geneSymbol = (geneData.results[0] && geneData.results[0].genes && geneData.results[0].genes[0] && geneData.results[0].genes[0].geneName) ? geneData.results[0].genes[0].geneName.value : undefined;
41991
+ ic.chainsGene[chnid] = {geneId: geneId, geneSymbol: geneSymbol};
41992
+ }
41993
+ }
41994
+
41132
41995
  for(let chnid in ic.protein_chainid) {
41133
41996
  let buttonStyle = me.utilsCls.isMobile() ? 'none' : 'button';
41134
41997
  let fullProteinName = ic.showSeqCls.getProteinName(chnid);
41135
41998
  let proteinName = fullProteinName;
41136
41999
  //if(proteinName.length > 40) proteinName = proteinName.substr(0, 40) + "...";
41137
42000
  let categoryStr =(index == 0) ? "<span class='icn3d-annoLargeTitle'><b>Proteins</b>: </span><br><br>" : "";
41138
- let geneLink =(ic.chainsGene[chnid] && ic.chainsGene[chnid].geneId) ? "(Gene: <a href='https://www.ncbi.nlm.nih.gov/gene/" + ic.chainsGene[chnid].geneId + "' target='_blank' title='" + ic.chainsGene[chnid].geneDesc + "'>" + ic.chainsGene[chnid].geneSymbol + "</a>)" : '';
42001
+ let geneLink =(ic.chainsGene[chnid] && ic.chainsGene[chnid].geneId) ? "(Gene: <a href='https://www.ncbi.nlm.nih.gov/gene/" + ic.chainsGene[chnid].geneId + "?report=gene_table' target='_blank' title='" + ic.chainsGene[chnid].geneDesc + "'>" + ic.chainsGene[chnid].geneSymbol + "</a>)" : '';
41139
42002
  let structure = chnid.substr(0, chnid.indexOf('_'));
41140
42003
  let chainLink = (structure.length > 5) ? '<a href="https://alphafold.ebi.ac.uk/entry/' + structure + '" target="_blank">' + chnid + '</a>' : chnid;
41141
42004
  let chainHtml = "<div id='" + ic.pre + "anno_" + chnid + "' class='icn3d-annotation'>" + categoryStr
@@ -41686,6 +42549,9 @@ class ShowSeq {
41686
42549
  html += htmlTmp + '<span class="icn3d-seqLine">';
41687
42550
  let helixCnt = 0, sheetCnt = 0;
41688
42551
  let savedSsName = '';
42552
+
42553
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqStartLen[chnid], ' ');
42554
+
41689
42555
  for(let i = 0, il = giSeq.length; i < il; ++i) {
41690
42556
  html += this.insertGap(chnid, i, '-');
41691
42557
  let currResi;
@@ -41743,18 +42609,25 @@ class ShowSeq {
41743
42609
  }
41744
42610
  html += '</span>';
41745
42611
  }
42612
+
42613
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqEndLen[chnid], ' ');
42614
+
41746
42615
  html += '<span class="icn3d-residueNum"></span>';
41747
42616
  html += '</span>';
41748
42617
  html += '<br>';
41749
42618
  html += '</div>';
41750
42619
  html3 += '</div>';
41751
42620
  }
42621
+
41752
42622
  // html to display secondary structures
41753
42623
  htmlTmp = '<div class="icn3d-residueLine" style="white-space:nowrap;">';
41754
42624
  htmlTmp += '<div class="icn3d-annoTitle" anno="0"></div>';
41755
42625
  htmlTmp += '<span class="icn3d-residueNum"></span>';
41756
42626
  html3 += htmlTmp + '<br>';
41757
42627
  html += htmlTmp + '<span class="icn3d-seqLine">';
42628
+
42629
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqStartLen[chnid], '-');
42630
+
41758
42631
  for(let i = 0, il = giSeq.length; i < il; ++i) {
41759
42632
  html += this.insertGap(chnid, i, '-');
41760
42633
  // let resi =(i >= ic.matchedPos[chnid] && i - ic.matchedPos[chnid] < ic.chainsSeq[chnid].length) ? ic.chainsSeq[chnid][i - ic.matchedPos[chnid]].resi : ic.baseResi[chnid] + 1 + i;
@@ -41802,6 +42675,9 @@ class ShowSeq {
41802
42675
  html += '<span>-</span>'; //'<span>-</span>';
41803
42676
  }
41804
42677
  }
42678
+
42679
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqEndLen[chnid], '-');
42680
+
41805
42681
  html += '<span class="icn3d-residueNum"></span>';
41806
42682
  html += '</span>';
41807
42683
  html += '<br>';
@@ -41833,6 +42709,9 @@ class ShowSeq {
41833
42709
  html += htmlTmp + htmlTmp2;
41834
42710
  html2 += htmlTmp + htmlTmp2;
41835
42711
  let pos, nGap = 0;
42712
+
42713
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqStartLen[chnid], '-');
42714
+
41836
42715
  for(let i = 0, il = giSeq.length; i < il; ++i) {
41837
42716
  html += this.insertGap(chnid, i, '-');
41838
42717
  if(ic.targetGapHash !== undefined && ic.targetGapHash.hasOwnProperty(i)) nGap += ic.targetGapHash[i].to - ic.targetGapHash[i].from + 1;
@@ -41862,6 +42741,9 @@ class ShowSeq {
41862
42741
  html += '<span id="giseq_' + ic.pre + chnid + '_' + pos + '" title="' + cFull + pos + '" class="icn3d-residue" style="color:#' + color + '">' + c + '</span>';
41863
42742
  }
41864
42743
  }
42744
+
42745
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqEndLen[chnid], '-');
42746
+
41865
42747
  if(me.cfg.blast_rep_id == chnid) {
41866
42748
  // change color in 3D
41867
42749
  ic.opts['color'] = (ic.blastAcxn) ? 'confidence' : 'conservation';
@@ -41872,8 +42754,11 @@ class ShowSeq {
41872
42754
  // sequence, overview
41873
42755
  let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.chains[chnid]);
41874
42756
  let color =(atom.color) ? atom.color.getHexString() : "CCCCCC";
41875
- let width = Math.round(ic.seqAnnWidth * giSeq.length / ic.maxAnnoLength);
42757
+ let width = Math.round(ic.seqAnnWidth * giSeq.length / (ic.maxAnnoLength + ic.nTotalGap));
41876
42758
  if(width < 1) width = 1;
42759
+
42760
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += this.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
42761
+
41877
42762
  if(me.cfg.blast_rep_id != chnid) { // regular
41878
42763
  html2 += '<div id="giseq_summary_' + ic.pre + chnid + '" class="icn3d-seqTitle icn3d-link" gi chain="' + chnid + '" style="display:inline-block; color:white; font-weight:bold; background-color:#' + color + '; width:' + width + 'px;">' + chnid + '</div>';
41879
42764
  }
@@ -41887,7 +42772,9 @@ class ShowSeq {
41887
42772
  }
41888
42773
  }
41889
42774
  toArray2.push(giSeq.length - 1);
42775
+
41890
42776
  html2 += '<div id="giseq_summary_' + ic.pre + chnid + '" class="icn3d-seqTitle icn3d-link" gi chain="' + chnid + '" style="width:' + width + 'px;">';
42777
+
41891
42778
  for(let i = 0, il = fromArray2.length; i < il; ++i) {
41892
42779
  html2 += this.insertGapOverview(chnid, fromArray2[i]);
41893
42780
  html2 += '<div style="display:inline-block; color:white!important; font-weight:bold; background-color:#' + color + '; width:' + Math.round(ic.seqAnnWidth *(toArray2[i] - fromArray2[i] + 1) /(ic.maxAnnoLength + ic.nTotalGap)) + 'px;" class="icn3d-seqTitle icn3d-link icn3d-blue" anno="sequence" gi chain="' + chnid + '" title="' + chnid + '">' + chnid + '</div>';
@@ -42019,6 +42906,9 @@ class ShowSeq {
42019
42906
  htmlTmp += '<span class="icn3d-residueNum"></span>';
42020
42907
  html3 += htmlTmp + '<br>';
42021
42908
  html += htmlTmp + '<span class="icn3d-seqLine">';
42909
+
42910
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqStartLen[chnid], '-');
42911
+
42022
42912
  for(let i = 0, il = giSeq.length; i < il; ++i) {
42023
42913
  html += this.insertGap(chnid, i, '-');
42024
42914
  //if(i >= ic.matchedPos[chnid] && i - ic.matchedPos[chnid] < ic.chainsSeq[chnid].length) {
@@ -42042,6 +42932,9 @@ class ShowSeq {
42042
42932
  // html += '<span></span>';
42043
42933
  // }
42044
42934
  }
42935
+
42936
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqEndLen[chnid], '-');
42937
+
42045
42938
  html += '<span class="icn3d-residueNum"></span>';
42046
42939
  html += '</span>';
42047
42940
  html += '<br>';
@@ -42689,26 +43582,43 @@ class ShowSeq {
42689
43582
  let html = '';
42690
43583
  //if(me.cfg.blast_rep_id == chnid && ic.targetGapHash!== undefined && ic.targetGapHash.hasOwnProperty(seqIndex)) {
42691
43584
  if(ic.targetGapHash!== undefined && ic.targetGapHash.hasOwnProperty(seqIndex)) {
42692
- for(let j = 0; j <(ic.targetGapHash[seqIndex].to - ic.targetGapHash[seqIndex].from + 1); ++j) {
42693
- if(bNohtml) {
42694
- html += text;
42695
- }
42696
- else {
42697
- html += '<span>' + text + '</span>';
42698
- }
42699
- }
43585
+ html += this.insertMulGap(ic.targetGapHash[seqIndex].to - ic.targetGapHash[seqIndex].from + 1, text, bNohtml);
42700
43586
  }
42701
43587
  return html;
42702
43588
  }
42703
- insertGapOverview(chnid, seqIndex) { let ic = this.icn3d, me = ic.icn3dui;
43589
+
43590
+ insertMulGap(n, text, bNohtml) { let ic = this.icn3d; ic.icn3dui;
43591
+ let html = '';
43592
+ for(let j = 0; j < n; ++j) {
43593
+ if(bNohtml) {
43594
+ html += text;
43595
+ }
43596
+ else {
43597
+ html += '<span>' + text + '</span>';
43598
+ }
43599
+ }
43600
+ return html;
43601
+ }
43602
+
43603
+ insertGapOverview(chnid, seqIndex) { let ic = this.icn3d; ic.icn3dui;
42704
43604
  let html2 = '';
42705
- if(me.cfg.blast_rep_id == chnid && ic.targetGapHash!== undefined && ic.targetGapHash.hasOwnProperty(seqIndex)) {
42706
- let width = ic.seqAnnWidth *(ic.targetGapHash[seqIndex].to - ic.targetGapHash[seqIndex].from + 1) /(ic.maxAnnoLength + ic.nTotalGap);
42707
- html2 += '<div style="display:inline-block; background-color:#333; width:' + width + 'px; height:3px;">&nbsp;</div>';
43605
+ // if(me.cfg.blast_rep_id == chnid && ic.targetGapHash !== undefined && ic.targetGapHash.hasOwnProperty(seqIndex)) {
43606
+ if(ic.targetGapHash !== undefined && ic.targetGapHash.hasOwnProperty(seqIndex)) {
43607
+ html2 += this.insertMulGapOverview(chnid, ic.targetGapHash[seqIndex].to - ic.targetGapHash[seqIndex].from + 1);
42708
43608
  }
42709
43609
  return html2;
42710
43610
  }
42711
43611
 
43612
+ insertMulGapOverview(chnid, n) { let ic = this.icn3d; ic.icn3dui;
43613
+ let html2 = '';
43614
+ let width = ic.seqAnnWidth * n /(ic.maxAnnoLength + ic.nTotalGap);
43615
+ width = parseInt(width);
43616
+
43617
+ // html2 += '<div style="display:inline-block; background-color:#333; width:' + width + 'px; height:3px;">&nbsp;</div>';
43618
+ html2 += '<div style="display:inline-block; width:' + width + 'px;">&nbsp;</div>';
43619
+ return html2;
43620
+ }
43621
+
42712
43622
  setAlternativeSeq(chnid, chnidBase) { let ic = this.icn3d; ic.icn3dui;
42713
43623
  //if(ic.chainsSeq[chnid] !== undefined) {
42714
43624
  let resArray = ic.chainsSeq[chnid];
@@ -49509,6 +50419,8 @@ class MmdbParser {
49509
50419
 
49510
50420
  if(data && data.uniprot) {
49511
50421
  me.cfg.afid = data.uniprot;
50422
+ if(!ic.uniprot2acc) ic.uniprot2acc = {};
50423
+ ic.uniprot2acc[data.uniprot] = refseqid;
49512
50424
  }
49513
50425
  else {
49514
50426
  var aaa = 1; //alert('The accession ' + refseqid + ' can not be mapped to AlphaFold UniProt ID. It will be treated as a UniProt ID instead.');
@@ -55791,14 +56703,121 @@ class SetSeqAlign {
55791
56703
  result = this.getTemplatePosFromOriPos(chainid1, prevIndex1, end_t, bRealign);
55792
56704
  pos1 = result.pos1;
55793
56705
  pos2 = result.pos2;
55794
- //for(let i = pos1; i < pos2; ++i) {
55795
- for(let i = pos1; i <= pos2; ++i) {
56706
+ for(let i = pos1; i < pos2; ++i) {
56707
+ //for(let i = pos1; i <= pos2; ++i) {
55796
56708
  ic.alnChainsSeq[chainid2].push(gapResObject2);
55797
56709
  }
55798
56710
 
55799
56711
  return hAtoms;
55800
56712
  }
55801
56713
 
56714
+ // used for seq MSA
56715
+ mergeTwoSeqForAllSimple(targetId, chainidArray, index, alignedChainIndice, start_t, end_t, querySeqArray) { let ic = this.icn3d; ic.icn3dui;
56716
+ let chainid1 = targetId;
56717
+ let chainid2 = chainidArray[index];
56718
+
56719
+ let pos1, pos2, prevIndex1, prevIndex2;
56720
+
56721
+ for(let i = 0, il = ic.qt_start_end[index].length; i < il; ++i) {
56722
+ let start1, start2, end1, end2, resiStart1, start1Pos, end1Pos;
56723
+
56724
+ start1 = ic.qt_start_end[index][i].t_start;
56725
+ start2 = ic.qt_start_end[index][i].q_start;
56726
+ end1 = ic.qt_start_end[index][i].t_end;
56727
+ end2 = ic.qt_start_end[index][i].q_end;
56728
+
56729
+ // 1. before the mapped residues
56730
+ //resiStart1 = ic.ParserUtilsCls.getResi(chainid1, start1);
56731
+ resiStart1 = start1;
56732
+ start1Pos = start1;
56733
+ end1Pos = end1;
56734
+
56735
+ // if the mapping does not start from start_t, add gaps to the query seq
56736
+ if(i == 0) {
56737
+ pos1 = start_t;
56738
+ pos2 = start1Pos;
56739
+
56740
+ if(start1Pos > start_t) {
56741
+ for(let j = 0, jl = pos2 - pos1; j < jl; ++j) {
56742
+ ic.msaSeq[chainid2] += '-';
56743
+ }
56744
+ }
56745
+ }
56746
+ else {
56747
+ pos1 = prevIndex1;
56748
+ pos2 = start1;
56749
+ let notAlnLen1 = pos2 - (pos1 + 1);
56750
+ let notAlnLen2 = start2 - (prevIndex2 + 1);
56751
+
56752
+ // insert non-aligned residues in query seq
56753
+ // this.insertNotAlignRes(chainid2, prevIndex2+1, notAlnLen2, bRealign);
56754
+
56755
+ for(let j = 0, jl = notAlnLen2; j < jl; ++j) {
56756
+ let resn = querySeqArray[index][prevIndex2+1 + j];
56757
+ ic.msaSeq[chainid2] += resn;
56758
+ }
56759
+
56760
+ if(notAlnLen1 >= notAlnLen2) {
56761
+ // add gaps before the query sequence
56762
+ for(let j = 0, jl = notAlnLen1 - notAlnLen2; j < jl; ++j) {
56763
+ ic.msaSeq[chainid2] += '-';
56764
+ }
56765
+ }
56766
+ else {
56767
+ // check the number of gaps before resiStart1 (n), and insert 'notAlnLen2 - notAlnLen1 - n' gaps
56768
+ // this.addGapAllAlnChains(chainidArray, alignedChainIndice, chainid1, resiStart1, notAlnLen2 - notAlnLen1);
56769
+
56770
+ // let result = this.getResiPosInTemplate(chainid1, resi_t);
56771
+ // let nGap = result.ngap, pos_t = result.pos;
56772
+
56773
+ let pos_t = resiStart1; // position to add gap
56774
+
56775
+ // add gaps for all previously aligned sequences, not the current sequence, which is the last one
56776
+ for(let j = 0, jl = alignedChainIndice.length - 1; j < jl; ++j) {
56777
+ let chainidTmp = (j == 0) ? chainid1 : chainidArray[alignedChainIndice[j]];
56778
+
56779
+ for(let k = 0, kl = notAlnLen2 - notAlnLen1; k < kl; ++k) {
56780
+ //ic.msaSeq[chainidTmp].splice(pos_t, 0, '-');
56781
+ ic.msaSeq[chainidTmp] = ic.msaSeq[chainidTmp].substr(0, pos_t) + '-' + ic.msaSeq[chainidTmp].substr(pos_t);
56782
+ }
56783
+ }
56784
+ }
56785
+ }
56786
+
56787
+ // 2. In the mapped residues
56788
+ pos1 = start1Pos;
56789
+ pos2 = end1Pos;
56790
+
56791
+ let k = 0;
56792
+ for(let j = pos1; j <= pos2; ++j) {
56793
+ // inherit the gaps from the template
56794
+ if(ic.msaSeq[chainid1][j] == '-') {
56795
+ ic.msaSeq[chainid2] += '-';
56796
+ }
56797
+ else {
56798
+ //let resn1 = targetSeq[start1 + k];
56799
+ let resn2 = querySeqArray[index][start2 + k];
56800
+ //let resn2 = (querySeqArray[index]) ? querySeqArray[index][start2 + k] : '?';
56801
+
56802
+ ic.msaSeq[chainid2] += resn2;
56803
+
56804
+ ++k;
56805
+ }
56806
+ }
56807
+
56808
+ prevIndex1 = end1;
56809
+ prevIndex2 = end2;
56810
+ }
56811
+
56812
+ // add gaps at the end
56813
+ pos1 = prevIndex1;
56814
+ pos2 = end_t;
56815
+ for(let i = pos1; i < pos2; ++i) {
56816
+ //for(let i = pos1; i <= pos2; ++i) {
56817
+ ic.msaSeq[chainid2] += '-';
56818
+ }
56819
+ }
56820
+
55802
56821
  setSeqAlignForRealign(chainid_t, chainid, chainIndex) { let ic = this.icn3d, me = ic.icn3dui;
55803
56822
  //var chainid_t = ic.chainidArray[0];
55804
56823
 
@@ -60376,6 +61395,38 @@ class LoadScript {
60376
61395
 
60377
61396
  await ic.cartoon2dCls.draw2Dcartoon(type);
60378
61397
  }
61398
+ else if(command.indexOf('add msa track') == 0) {
61399
+ //add msa track | chainid " + chainid + " | startpos " + startpos + " | type " + type + " | fastaList " + fastaList
61400
+ let paraArray = command.split(' | ');
61401
+
61402
+ let chainid = paraArray[1].substr(8);
61403
+ let startpos = paraArray[2].substr(9);
61404
+ let type = paraArray[3].substr(5);
61405
+ let fastaList = paraArray[4].substr(10);
61406
+
61407
+ if($("#" + ic.pre + "anno_custom")[0]) {
61408
+ $("#" + ic.pre + "anno_custom")[0].checked = true;
61409
+ }
61410
+ $("[id^=" + ic.pre + "custom]").show();
61411
+
61412
+ await ic.addTrackCls.addMsaTracks(chainid, startpos, type, fastaList);
61413
+ }
61414
+ else if(command.indexOf('add exon track') == 0) {
61415
+ //add exon track | chainid " + chainid + " | geneid " + geneid + " | startpos " + startpos + " | type " + type
61416
+ let paraArray = command.split(' | ');
61417
+
61418
+ let chainid = paraArray[1].substr(8);
61419
+ let geneid = paraArray[2].substr(7);
61420
+ let startpos = paraArray[3].substr(9);
61421
+ let type = paraArray[4].substr(5);
61422
+
61423
+ if($("#" + ic.pre + "anno_custom")[0]) {
61424
+ $("#" + ic.pre + "anno_custom")[0].checked = true;
61425
+ }
61426
+ $("[id^=" + ic.pre + "custom]").show();
61427
+
61428
+ await ic.addTrackCls.addExonTracks(chainid, geneid, startpos, type);
61429
+ }
60379
61430
  else {
60380
61431
  await ic.applyCommandCls.applyCommand(ic.commands[i]);
60381
61432
  }
@@ -61088,12 +62139,18 @@ class SelectByCommand {
61088
62139
  residueStr = "*";
61089
62140
  }
61090
62141
  else if(colonPos2 != -1) {
61091
- refResStr = testStr.substr(colonPos2 + 5);
61092
- testStr = testStr.substr(0, colonPos2);
62142
+ refResStr = testStr.substr(colonPos2 + 5);
62143
+ testStr = testStr.substr(0, colonPos2);
62144
+
62145
+ // somehow sometimes refResStr or residueStr is rmpty
62146
+ if(!refResStr) continue;
61093
62147
  }
61094
62148
  else if(colonPos != -1) {
61095
- residueStr = testStr.substr(colonPos + 1);
61096
- testStr = testStr.substr(0, colonPos);
62149
+ residueStr = testStr.substr(colonPos + 1);
62150
+ testStr = testStr.substr(0, colonPos);
62151
+
62152
+ // somehow sometimes refResStr or residueStr is rmpty
62153
+ if(!residueStr) continue;
61097
62154
  }
61098
62155
 
61099
62156
  if(periodPos === -1) {
@@ -71482,7 +72539,7 @@ class iCn3DUI {
71482
72539
  //even when multiple iCn3D viewers are shown together.
71483
72540
  this.pre = this.cfg.divid + "_";
71484
72541
 
71485
- this.REVISION = '3.27.0';
72542
+ this.REVISION = '3.28.0';
71486
72543
 
71487
72544
  // In nodejs, iCn3D defines "window = {navigator: {}}"
71488
72545
  this.bNode = (Object.keys(window).length < 2) ? true : false;