icn3d 3.27.0 → 3.28.0

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.module.js CHANGED
@@ -9735,9 +9735,9 @@ class SetMenu {
9735
9735
 
9736
9736
  html += this.getMenuText('mn2_rotate90', 'Rotate 90°', undefined, undefined, 2);
9737
9737
  html += "<ul>";
9738
- html += this.getRadio('mn6_rotate90', 'mn6_rotatex', 'X-axis(Shift + Key M)', undefined, undefined, 2);
9739
- html += this.getRadio('mn6_rotate90', 'mn6_rotatey', 'Y-axis(Shift + Key J)', undefined, undefined, 2);
9740
- html += this.getRadio('mn6_rotate90', 'mn6_rotatez', 'Z-axis', undefined, undefined, 2);
9738
+ html += this.getRadio('mn6_rotate90', 'mn6_rotatex', 'rotate x', undefined, undefined, 2);
9739
+ html += this.getRadio('mn6_rotate90', 'mn6_rotatey', 'rotate y', undefined, undefined, 2);
9740
+ html += this.getRadio('mn6_rotate90', 'mn6_rotatez', 'rotate z', undefined, undefined, 2);
9741
9741
  html += "</ul>";
9742
9742
  html += "</li>";
9743
9743
  html += this.getMenuText('mn2_rotateauto', 'Auto Rotation', undefined, 1, 2);
@@ -12468,9 +12468,10 @@ class SetDialog {
12468
12468
 
12469
12469
  html += me.htmlCls.divStr + "dl_addtrack_tabs' style='border:0px;'>";
12470
12470
  html += "<ul>";
12471
+ html += "<li><a href='#" + me.pre + "tracktab2c'>Isoforms & Exons</a></li>";
12472
+ html += "<li><a href='#" + me.pre + "tracktab2b'>MSA</a></li>";
12471
12473
  html += "<li><a href='#" + me.pre + "tracktab1'>NCBI gi/Accession</a></li>";
12472
12474
  html += "<li><a href='#" + me.pre + "tracktab2'>FASTA</a></li>";
12473
- html += "<li><a href='#" + me.pre + "tracktab2b'>FASTA Alignment</a></li>";
12474
12475
  html += "<li><a href='#" + me.pre + "tracktab3'>BED File</a></li>";
12475
12476
  html += "<li><a href='#" + me.pre + "tracktab4'>Custom</a></li>";
12476
12477
  html += "<li><a href='#" + me.pre + "tracktab5'>Current Selection</a></li>";
@@ -12486,10 +12487,16 @@ class SetDialog {
12486
12487
  html += "</div>";
12487
12488
 
12488
12489
  html += me.htmlCls.divStr + "tracktab2b'>";
12489
- 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>";
12490
- html += "<b>FASTA alignment sequences</b>:<br>";
12490
+ // 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>";
12491
+ 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>";
12492
+
12493
+ html += "<b>Precalculated Multiple Sequence Alignment (MSA)</b>:<br>";
12491
12494
  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>";
12492
- html += "Position of the first residue in Sequences & Annotations window: " + me.htmlCls.inputTextStr + "id='" + me.pre + "fasta_startpos' value='1' size=2> <br><br>";
12495
+
12496
+ // html += "<b>Opion 1. Precalculated Multiple Sequence Alignment (MSA)</b>:<br>";
12497
+ // 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>";
12498
+ // html += "<b>Opion 2. NCBI Protein Accessions</b>: "+ me.htmlCls.inputTextStr + "id='" + me.pre + "track_acclist' size=60> <br><br>";
12499
+ 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>";
12493
12500
 
12494
12501
  html += "Color Sequence by: <select id='" + me.pre + "colorseqby'>";
12495
12502
  html += me.htmlCls.optionStr + "'identity' selected>Identity</option>";
@@ -12499,6 +12506,22 @@ class SetDialog {
12499
12506
  html += me.htmlCls.buttonStr + "addtrack_button2b'>Add Track(s)</button>";
12500
12507
  html += "</div>";
12501
12508
 
12509
+ html += me.htmlCls.divStr + "tracktab2c'>";
12510
+ html += "<div style='width:500px'>Note: Show exons for all isoforms of the protein in the same gene as specified below.</div><br>";
12511
+
12512
+ 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>";
12513
+
12514
+ 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>";
12515
+
12516
+ html += "Color Sequence by: <select id='" + me.pre + "colorseqby2'>";
12517
+ html += me.htmlCls.optionStr + "'identity' selected>Identity</option>";
12518
+ html += me.htmlCls.optionStr + "'conservation'>Conservation</option>";
12519
+ html += "</select> <br><br>";
12520
+
12521
+ html += me.htmlCls.buttonStr + "addtrack_button2c'>Show Isoforms & Exons</button>";
12522
+ html += "</div>";
12523
+
12524
+
12502
12525
  html += me.htmlCls.divStr + "tracktab3'>";
12503
12526
  html += "BED file: " + me.htmlCls.inputFileStr + "id='" + me.pre + "track_bed' size=16> <br><br>";
12504
12527
  html += me.htmlCls.buttonStr + "addtrack_button3'>Add Track</button>";
@@ -13820,6 +13843,8 @@ class Events {
13820
13843
 
13821
13844
  let esmData = await me.getAjaxPostPromise(esmUrl, esmfold_fasta, true, alertMess, undefined, true, 'text');
13822
13845
 
13846
+ ic.bResetAnno = true;
13847
+
13823
13848
  ic.bInputfile = true;
13824
13849
  ic.InputfileType = 'pdb';
13825
13850
  ic.InputfileData = (ic.InputfileData) ? ic.InputfileData + '\nENDMDL\n' + esmData : esmData;
@@ -15228,13 +15253,15 @@ class Events {
15228
15253
  });
15229
15254
 
15230
15255
 
15231
- $(document).on("click", ".icn3d-addtrack", function(e) { me.icn3d;
15256
+ $(document).on("click", ".icn3d-addtrack", function(e) { let ic = me.icn3d;
15232
15257
  e.stopImmediatePropagation();
15233
15258
  $("#" + me.pre + "anno_custom")[0].checked = true;
15234
15259
  $("[id^=" + me.pre + "custom]").show();
15235
15260
  //e.preventDefault();
15236
15261
  let chainid = $(this).attr('chainid');
15262
+ let geneid = ic.chainsGene[chainid].geneId;
15237
15263
  $("#" + me.pre + "track_chainid").val(chainid);
15264
+ $("#" + me.pre + "track_geneid").val(geneid);
15238
15265
  me.htmlCls.dialogCls.openDlg('dl_addtrack', 'Add track for Chain: ' + chainid);
15239
15266
  $( "#" + me.pre + "track_gi" ).focus();
15240
15267
  });
@@ -15474,7 +15501,7 @@ class AlignSeq {
15474
15501
  let bHighlightChain;
15475
15502
  let index = 0, prevResCnt2nd = 0;
15476
15503
  let firstChainid, oriChainid;
15477
- console.log("alignChainArray.length: " + alignChainArray.length);
15504
+
15478
15505
  // for(let i in ic.alnChains) {
15479
15506
  for (let m = 0, ml = alignChainArray.length; m < ml; ++m) {
15480
15507
  let i = alignChainArray[m];
@@ -33190,6 +33217,8 @@ class Alternate {
33190
33217
  // change the display atom when alternating
33191
33218
  //Show structures one by one.
33192
33219
  alternateStructures() { let ic = this.icn3d, me = ic.icn3dui;
33220
+ ic.bAlernate = true;
33221
+
33193
33222
  //ic.transformCls.zoominSelection();
33194
33223
 
33195
33224
  // default ic.ALTERNATE_STRUCTURE = -1
@@ -33304,11 +33333,11 @@ class Alternate {
33304
33333
 
33305
33334
  //ic.glycanCls.showGlycans();
33306
33335
 
33336
+ ic.opts['rotationcenter'] = 'highlight center';
33337
+
33307
33338
  ic.drawCls.draw();
33308
33339
 
33309
33340
  ic.bShowHighlight = true;
33310
- //ic.opts['rotationcenter'] = 'molecule center';
33311
- ic.opts['rotationcenter'] = 'highlight center';
33312
33341
  }
33313
33342
 
33314
33343
  alternateWrapper() { let ic = this.icn3d; ic.icn3dui;
@@ -36934,6 +36963,10 @@ class AnnoCddSite {
36934
36963
  let prevEmptyWidth = 0;
36935
36964
  let prevLineWidth = 0;
36936
36965
  let widthPerRes = 1;
36966
+
36967
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
36968
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
36969
+
36937
36970
  for(let i = 0, il = ic.giSeq[chnid].length; i < il; ++i) {
36938
36971
  html += ic.showSeqCls.insertGap(chnid, i, '-');
36939
36972
  if(resPosArray.indexOf(i) != -1) {
@@ -36966,6 +36999,9 @@ class AnnoCddSite {
36966
36999
  html += '<span>-</span>'; //'<span>-</span>';
36967
37000
  }
36968
37001
  }
37002
+
37003
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
37004
+
36969
37005
  htmlTmp = '<span class="icn3d-residueNum" title="residue count">&nbsp;' + resCnt.toString() + ' Residues</span>';
36970
37006
  htmlTmp += '</span>';
36971
37007
  htmlTmp += '<br>';
@@ -37123,6 +37159,9 @@ class AnnoCddSite {
37123
37159
  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>';
37124
37160
  html2 += htmlTmp3 + htmlTmp;
37125
37161
  let pre = type + index.toString();
37162
+
37163
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
37164
+
37126
37165
  for(let i = 0, il = ic.giSeq[chnid].length; i < il; ++i) {
37127
37166
  html += ic.showSeqCls.insertGap(chnid, i, '-');
37128
37167
 
@@ -37150,9 +37189,15 @@ class AnnoCddSite {
37150
37189
  html += '<span>-</span>'; //'<span>-</span>';
37151
37190
  }
37152
37191
  }
37192
+
37193
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
37194
+
37153
37195
  let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.chains[chnid]);
37154
37196
  let colorStr =(atom.color === undefined || atom.color.getHexString() === 'FFFFFF') ? 'DDDDDD' : atom.color.getHexString();
37155
37197
  let color =(atom.color !== undefined) ? colorStr : "CCCCCC";
37198
+
37199
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
37200
+
37156
37201
  if(me.cfg.blast_rep_id != chnid) { // regular
37157
37202
  for(let i = 0, il = fromArray.length; i < il; ++i) {
37158
37203
  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);
@@ -37229,6 +37274,10 @@ class AnnoCddSite {
37229
37274
  let prevEmptyWidth = 0;
37230
37275
  let prevLineWidth = 0;
37231
37276
  let widthPerRes = 1;
37277
+
37278
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
37279
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
37280
+
37232
37281
  for(let i = 0, il = ic.giSeq[chnid].length; i < il; ++i) {
37233
37282
  html += ic.showSeqCls.insertGap(chnid, i, '-');
37234
37283
  let resi = ic.ParserUtilsCls.getResi(chnid, i);
@@ -37304,6 +37353,9 @@ class AnnoCddSite {
37304
37353
  html += '<span>-</span>'; //'<span>-</span>';
37305
37354
  }
37306
37355
  }
37356
+
37357
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
37358
+
37307
37359
  htmlTmp = '<span class="icn3d-residueNum" title="residue count">&nbsp;' + resCnt.toString() + ' Residues</span>';
37308
37360
  htmlTmp += '</span>';
37309
37361
  htmlTmp += '<br>';
@@ -37468,6 +37520,9 @@ class AnnoContact {
37468
37520
  let prevLineWidth = 0;
37469
37521
  let widthPerRes = 1;
37470
37522
 
37523
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
37524
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
37525
+
37471
37526
  for(let i = 0, il = ic.giSeq[chnid].length; i < il; ++i) {
37472
37527
  html += ic.showSeqCls.insertGap(chnid, i, '-');
37473
37528
  let resi = ic.ParserUtilsCls.getResi(chnid, i);
@@ -37503,6 +37558,9 @@ class AnnoContact {
37503
37558
  html += '<span>-</span>'; //'<span>-</span>';
37504
37559
  }
37505
37560
  }
37561
+
37562
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
37563
+
37506
37564
  htmlTmp = '<span class="icn3d-residueNum" title="residue count">&nbsp;' + resCnt.toString() + ' Residues</span>';
37507
37565
  htmlTmp += '</span>';
37508
37566
  htmlTmp += '<br>';
@@ -37755,6 +37813,10 @@ class AnnoPTM {
37755
37813
  let prevEmptyWidth = 0;
37756
37814
  let prevLineWidth = 0;
37757
37815
  let widthPerRes = 1;
37816
+
37817
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
37818
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
37819
+
37758
37820
  for(let i = 0, il = ic.giSeq[chnid].length; i < il; ++i) {
37759
37821
  html += ic.showSeqCls.insertGap(chnid, i, '-');
37760
37822
  if(resPosArray.indexOf(i) != -1) {
@@ -37787,6 +37849,9 @@ class AnnoPTM {
37787
37849
  html += '<span>-</span>'; //'<span>-</span>';
37788
37850
  }
37789
37851
  }
37852
+
37853
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
37854
+
37790
37855
  htmlTmp = '<span class="icn3d-residueNum" title="residue count">&nbsp;' + resCnt.toString() + ' Residues</span>';
37791
37856
  htmlTmp += '</span>';
37792
37857
  htmlTmp += '<br>';
@@ -38066,6 +38131,9 @@ class AnnoDomain {
38066
38131
  html += htmlTmp2 + htmlTmp3 + htmlTmp;
38067
38132
  html2 += htmlTmp2 + htmlTmp3 + htmlTmp;
38068
38133
  let pre = 'domain3d' + index.toString();
38134
+
38135
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
38136
+
38069
38137
  for(let i = 0, il = ic.giSeq[chnid].length; i < il; ++i) {
38070
38138
  html += ic.showSeqCls.insertGap(chnid, i, '-');
38071
38139
  //if(i >= domainFrom && i <= domainTo) {
@@ -38085,9 +38153,15 @@ class AnnoDomain {
38085
38153
  html += '<span>-</span>'; //'<span>-</span>';
38086
38154
  }
38087
38155
  }
38156
+
38157
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
38158
+
38088
38159
  let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.chains[chnid]);
38089
38160
  let colorStr =(atom.color === undefined || atom.color.getHexString() === 'FFFFFF') ? 'DDDDDD' : atom.color.getHexString();
38090
38161
  let color =(atom.color !== undefined) ? colorStr : "CCCCCC";
38162
+
38163
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
38164
+
38091
38165
  if(me.cfg.blast_rep_id != chnid) { // regular
38092
38166
  for(let i = 0, il = fromArray.length; i < il; ++i) {
38093
38167
  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);
@@ -38355,9 +38429,18 @@ class AnnoSnpClinVar {
38355
38429
  let prevEmptyWidth = 0;
38356
38430
  let prevLineWidth = 0;
38357
38431
  let widthPerRes = 1;
38432
+
38433
+ if(bOverview) {
38434
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
38435
+ }
38436
+ else {
38437
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
38438
+ }
38439
+
38358
38440
  for(let i = 1, il = ic.giSeq[chnid].length; i <= il; ++i) {
38359
38441
  if(bOverview) {
38360
38442
  if(resi2index[i] !== undefined) {
38443
+
38361
38444
  // get the mouse over text
38362
38445
  let cFull = ic.giSeq[chnid][i-1];
38363
38446
  let c = cFull;
@@ -38382,7 +38465,8 @@ class AnnoSnpClinVar {
38382
38465
  }
38383
38466
  }
38384
38467
  html += ic.showSeqCls.insertGapOverview(chnid, i-1);
38385
- 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);
38468
+ let emptyWidth = Math.round(ic.seqAnnWidth *(i-1) /(ic.maxAnnoLength + ic.nTotalGap) - prevEmptyWidth - prevLineWidth);
38469
+ //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);
38386
38470
  //if(emptyWidth < 0) emptyWidth = 0;
38387
38471
  if(bClinvar) {
38388
38472
  if(snpTypeHash[i] == 'icn3d-clinvar' || snpTypeHash[i] == 'icn3d-clinvar-path') {
@@ -38611,6 +38695,10 @@ class AnnoSnpClinVar {
38611
38695
  }
38612
38696
  } // if(bOverview) {
38613
38697
  } // for
38698
+
38699
+ if(!bOverview) {
38700
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
38701
+ }
38614
38702
 
38615
38703
  //var end = bStartEndRes ? ic.chainsSeq[chnid][ic.giSeq[chnid].length - 1 - ic.matchedPos[chnid] ].resi : '';
38616
38704
  if(line == 1) {
@@ -40224,7 +40312,7 @@ class AddTrack {
40224
40312
  thisClass.alignSequenceToStructure(chainid, data, title);
40225
40313
  });
40226
40314
 
40227
- // FASTA Alignment
40315
+ // MSA
40228
40316
  me.myEventCls.onIds("#" + ic.pre + "addtrack_button2b", "click", async function(e) { let ic = thisClass.icn3d;
40229
40317
  e.stopImmediatePropagation();
40230
40318
  //e.preventDefault();
@@ -40232,159 +40320,47 @@ class AddTrack {
40232
40320
 
40233
40321
  let chainid = $("#" + ic.pre + "track_chainid").val();
40234
40322
  let startpos = $("#" + ic.pre + "fasta_startpos").val();
40323
+ if(!startpos) startpos = 1;
40324
+
40235
40325
  let colorseqby = $("#" + ic.pre + "colorseqby").val();
40236
40326
  let type =(colorseqby == 'identity') ? 'identity' : 'custom';
40237
40327
 
40238
40328
  let fastaList = $("#" + ic.pre + "track_fastaalign").val();
40239
- let fastaArray = fastaList.split('>');
40240
-
40241
- // the first array item is empty
40242
- // the second array item is the sequence of the structure, start with i = 2
40243
- let posFirst = fastaArray[1].indexOf('\n');
40244
- fastaArray[1].substr(0, posFirst);
40245
- let seqFirst = fastaArray[1].substr(posFirst + 1).replace(/\n/g, '');
40246
-
40247
- let trackTitleArray = [];
40248
- let trackSeqArray = [];
40249
- for(let i = 2, il = fastaArray.length; i < il; ++i) {
40250
- let pos = fastaArray[i].indexOf('\n');
40251
- let title = fastaArray[i].substr(0, pos);
40252
- trackTitleArray.push(title);
40253
- let seq = fastaArray[i].substr(pos + 1).replace(/\n/g, '');
40254
- trackSeqArray.push(seq);
40255
- }
40256
-
40257
- let startposGiSeq = undefined;
40258
- for(let i = 0, il = ic.giSeq[chainid].length; i < il; ++i) {
40259
- //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;
40260
- let pos = ic.ParserUtilsCls.getResi(chainid, i);
40261
-
40262
- if(pos != startpos) {
40263
- continue;
40264
- }
40265
- else {
40266
- startposGiSeq = i;
40267
- }
40268
- }
40269
-
40270
- if(startposGiSeq === undefined) alert("Please double check the start position before clicking \"Add Track\"");
40271
-
40272
-
40273
- // set up gap for the master seq
40274
- // don't count gaps in both ends
40275
- ic.targetGapHash = {};
40276
- let prevSeq = '-', prevPos = 0, from, to, cnt = 0, dashCnt = 0;
40277
- let bFound = false, seqStart = 0, seqEnd = 0;
40278
- for(let i = 0, il = seqFirst.length; i < il; ++i) {
40279
- if(seqFirst[i] == '-' && seqFirst[i] != prevSeq) { // start of gap
40280
- from = cnt;
40281
- dashCnt = 0;
40282
- }
40283
-
40284
- if(prevSeq == '-' && seqFirst[i] != prevSeq && cnt > 0) { // end of gap
40285
- to = prevPos;
40286
- ic.targetGapHash[from + startposGiSeq] = {'from': from + startposGiSeq, 'to': to + dashCnt - 1 + startposGiSeq};
40287
- }
40288
-
40289
- prevSeq = seqFirst[i];
40290
- prevPos = cnt;
40291
-
40292
- if(seqFirst[i] != '-') {
40293
- ++cnt;
40294
- seqEnd = i;
40295
-
40296
- if(!bFound) {
40297
- seqStart = i;
40298
- bFound = true;
40299
- }
40300
- }
40301
- else {
40302
- ++dashCnt;
40303
- }
40304
- }
40305
-
40306
- await ic.annotationCls.resetAnnoAll();
40307
-
40308
- let targetGapHashStr = '';
40309
- let cntTmp = 0;
40310
- for(let i in ic.targetGapHash) {
40311
- if(cntTmp > 0) targetGapHashStr += ' ';
40312
- targetGapHashStr += i + '_' + ic.targetGapHash[i].from + '_' + ic.targetGapHash[i].to;
40313
- ++cntTmp;
40314
- }
40315
-
40316
- me.htmlCls.clickMenuCls.setLogCmd("msa | " + targetGapHashStr, true);
40317
-
40318
- // add tracks
40319
- let resi2cntSameRes = {}; // count of same residue at each position
40320
- for(let j = 0, jl = trackSeqArray.length; j < jl; ++j) {
40321
- let resi = startpos; //startposGiSeq + 1;
40322
- let text = '';
40323
- for(let k = 0; k < startposGiSeq; ++k) {
40324
- if(ic.targetGapHash.hasOwnProperty(k)) {
40325
- for(let m = 0; m < ic.targetGapHash[k].to - ic.targetGapHash[k].from + 1; ++m) {
40326
- text += '-';
40327
- }
40328
- }
40329
-
40330
- text += '-';
40331
- }
40332
-
40333
- for(let k = seqStart; k <= seqEnd; ++k) {
40334
- //if(seqFirst[k] == '-') continue;
40335
-
40336
- if(j == 0) resi2cntSameRes[resi] = 0;
40337
-
40338
- text += trackSeqArray[j][k]; //ic.giSeq[chainid][i];
40339
-
40340
- if(seqFirst[k] != '-') {
40341
- if(seqFirst[k] == trackSeqArray[j][k]) ++resi2cntSameRes[resi];
40342
- ++resi;
40343
- }
40344
- }
40345
-
40346
- let title =(trackTitleArray[j].length < 20) ? trackTitleArray[j] : trackTitleArray[j].substr(0, 20) + '...';
40347
- let bMsa = true;
40348
- thisClass.showNewTrack(chainid, title, text, undefined, undefined, type, undefined, bMsa);
40349
-
40350
- me.htmlCls.clickMenuCls.setLogCmd("add track | chainid " + chainid + " | title " + title + " | text " + thisClass.simplifyText(text)
40351
- + " | type " + type + " | color 0 | msa 1", true);
40352
- }
40353
-
40354
- // set colot for the master seq
40355
- if(trackSeqArray.length > 0) {
40356
- if(ic.queryresi2score === undefined) ic.queryresi2score = {};
40357
- if(ic.queryresi2score[chainid] === undefined) ic.queryresi2score[chainid] = {};
40358
40329
 
40359
- let nSeq = trackSeqArray.length;
40360
- for(let resi in resi2cntSameRes) {
40361
- let score = parseInt(resi2cntSameRes[resi] / nSeq * 100);
40362
- ic.queryresi2score[chainid][resi] = score;
40363
- }
40330
+ if(fastaList) {
40331
+ await thisClass.addMsaTracks(chainid, startpos, type, fastaList);
40332
+ }
40333
+ });
40364
40334
 
40365
- let resiArray = Object.keys(resi2cntSameRes);
40366
- let start = Math.min.apply(null, resiArray);
40367
- let end = Math.max.apply(null, resiArray);
40335
+ // Gene table
40336
+ me.myEventCls.onIds("#" + ic.pre + "exons_table", "click", async function(e) { let ic = thisClass.icn3d;
40337
+ e.stopImmediatePropagation();
40338
+ //dialog.dialog( "close" );
40368
40339
 
40369
- let resiScoreStr = '';
40370
- for(let resi = start; resi <= end; ++resi) {
40371
- if(resi2cntSameRes.hasOwnProperty(resi)) {
40372
- resiScoreStr += Math.round(resi2cntSameRes[resi] / nSeq * 9); // max 9
40373
- }
40374
- else {
40375
- resiScoreStr += '_';
40376
- }
40377
- }
40340
+ let geneid = $("#" + ic.pre + "track_geneid").val().trim();
40341
+ window.open('https://www.ncbi.nlm.nih.gov/gene/' + geneid + '?report=gene_table', '_blank');
40342
+ });
40378
40343
 
40379
- ic.opts['color'] = 'align custom';
40380
- ic.setColorCls.setColorByOptions(ic.opts, ic.hAtoms);
40344
+ // Isoform Alignment
40345
+ me.myEventCls.onIds("#" + ic.pre + "addtrack_button2c", "click", async function(e) { let ic = thisClass.icn3d;
40346
+ e.stopImmediatePropagation();
40347
+ //e.preventDefault();
40348
+ dialog.dialog( "close" );
40381
40349
 
40382
- ic.hlUpdateCls.updateHlAll();
40350
+ let chainid = $("#" + ic.pre + "track_chainid").val();
40351
+ let geneid = $("#" + ic.pre + "track_geneid").val();
40352
+ if(!geneid) {
40353
+ alert("Please fill in the Gene ID...");
40354
+ return;
40355
+ }
40383
40356
 
40384
- ic.drawCls.draw();
40357
+ let startpos = $("#" + ic.pre + "fasta_startpos2").val();
40358
+ if(!startpos) startpos = 1;
40385
40359
 
40386
- me.htmlCls.clickMenuCls.setLogCmd('color align custom | ' + chainid + ' | range ' + start + '_' + end + ' | ' + resiScoreStr, true);
40387
- }
40360
+ let colorseqby = $("#" + ic.pre + "colorseqby2").val();
40361
+ let type =(colorseqby == 'identity') ? 'identity' : 'custom';
40362
+
40363
+ await thisClass.addExonTracks(chainid, geneid, startpos, type);
40388
40364
  });
40389
40365
 
40390
40366
  // BED file
@@ -40570,7 +40546,7 @@ class AddTrack {
40570
40546
 
40571
40547
  }
40572
40548
 
40573
- showNewTrack(chnid, title, text, cssColorArray, inTarget2queryHash, type, color, bMsa, fromArray, toArray) { let ic = this.icn3d, me = ic.icn3dui;
40549
+ showNewTrack(chnid, title, text, cssColorArray, inTarget2queryHash, type, color, bMsa, fromArray, toArray, seqStartLen, exonArray) { let ic = this.icn3d, me = ic.icn3dui;
40574
40550
  //if(ic.customTracks[chnid] === undefined) {
40575
40551
  // ic.customTracks[chnid] = {}
40576
40552
  //}
@@ -40619,22 +40595,28 @@ class AddTrack {
40619
40595
  $("#" + ic.pre + "tt_custom_" + chnid + "_" + simpTitle).width(divLength);
40620
40596
 
40621
40597
  let html = '<div id="' + ic.pre + 'giseq_sequence" class="icn3d-dl_sequence">';
40598
+ let htmlExon = html;
40622
40599
  let html2 = html;
40623
40600
  let html3 = html;
40601
+ let html3Exon = html;
40624
40602
 
40625
40603
  //var htmlTmp2 = '<div class="icn3d-seqTitle icn3d-link icn3d-blue" gi="' + chnid + '" anno="sequence" chain="' + chnid + '"><span style="white-space:nowrap;">' + title + '</span></div>';
40626
40604
  //var htmlTmp2 = '<div class="icn3d-seqTitle" gi="' + chnid + '" anno="sequence" chain="' + chnid + '"><span style="white-space:nowrap;">' + title + '</span></div>';
40627
40605
  let index = parseInt(Math.random()*10);
40628
40606
  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>';
40607
+ let htmlTmp2Exon = '<div class="icn3d-seqTitle" chain="' + chnid + '" title="Exons of ' + title + '">Exons </div>';
40608
+
40629
40609
  let htmlTmp3 = '<span class="icn3d-residueNum" title="residue count">' + resCnt.toString() + ' Pos</span>';
40630
40610
 
40631
40611
  html3 += htmlTmp2 + htmlTmp3 + '<br>';
40612
+ html3Exon += htmlTmp2Exon + htmlTmp3 + '<br>';
40632
40613
 
40633
40614
  let htmlTmp = '<span class="icn3d-seqLine">';
40634
40615
 
40635
40616
  html += htmlTmp2 + htmlTmp3 + htmlTmp;
40617
+ htmlExon += htmlTmp2Exon + htmlTmp3 + htmlTmp;
40636
40618
  html2 += htmlTmp2 + htmlTmp3 + htmlTmp;
40637
-
40619
+
40638
40620
  //var pre ='cst' + ic.customTracks[chnid].length;
40639
40621
  let posTmp = chnid.indexOf('_');
40640
40622
  //var pre ='cst' + chnid.substr(posTmp);
@@ -40649,10 +40631,34 @@ class AddTrack {
40649
40631
  let bIdentityColor =(type === 'identity') && text.indexOf('cannot-be-aligned') == -1 && text.indexOf('cannot be aligned') == -1 ? true : false;
40650
40632
 
40651
40633
  let parsedResn = {};
40652
- let gapCnt = 0, currResi = 1;
40634
+ let gapCnt = 0;
40653
40635
  htmlTmp2 = '';
40636
+
40637
+ // if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += ic.showSeqCls.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
40638
+ // if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqStartLen[chnid], '-');
40639
+
40640
+ let pos2exonColor = {}, pos2genome = {}, pos2exonIndex = {};
40641
+ let cnt = 0;
40642
+ if(exonArray) {
40643
+ for(let j = 0, jl = exonArray.length; j < jl; ++j) {
40644
+ let start = exonArray[j].resStart, end = exonArray[j].resEnd;
40645
+ let genStart = exonArray[j].genomeRange.split('-')[0];
40646
+
40647
+ for(let k = 0, kl = end - start + 1; k < kl; ++k) {
40648
+ let colorStr = this.getExonColor(start, end, cnt);
40649
+
40650
+ pos2exonColor[cnt] = colorStr;
40651
+ pos2genome[cnt] = (genStart + ic.exonOrder * k*3) + '-' + (genStart + ic.exonOrder * k*3 + ic.exonOrder * 2); // reverse order from large to small
40652
+ pos2exonIndex[cnt] = j;
40653
+
40654
+ ++cnt;
40655
+ }
40656
+ }
40657
+ }
40658
+
40659
+ cnt = 0;
40654
40660
  for(let i = 0, il = text.length; i < il; ++i) {
40655
- let resNum = i-gapCnt;
40661
+ let resNum = i - gapCnt - ((ic.seqStartLen && ic.seqStartLen[chnid]) ? ic.seqStartLen[chnid] : 0);
40656
40662
 
40657
40663
  if(!bMsa) {
40658
40664
  html += ic.showSeqCls.insertGap(chnid, i, '-');
@@ -40673,7 +40679,8 @@ class AddTrack {
40673
40679
  let identityColorStr =(c == resName) ? 'FF0000' : '0000FF';
40674
40680
 
40675
40681
  //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;
40676
- let pos = ic.baseResi[chnid] + currResi;
40682
+ // let pos = ic.baseResi[chnid] + currResi;
40683
+ let pos = ic.baseResi[chnid] + (i+1) - ((ic.seqStartLen && ic.seqStartLen[chnid]) ? ic.seqStartLen[chnid] : 0);
40677
40684
 
40678
40685
  if(inTarget2queryHash !== undefined) pos = ic.baseResi[chnid] + inTarget2queryHash[i] + 1; // 0-based
40679
40686
 
@@ -40704,9 +40711,13 @@ class AddTrack {
40704
40711
 
40705
40712
  html += '<span id="' + pre + '_' + ic.pre + chnid + '_' + pos + '" title="' + c + pos + '" class="icn3d-residue" ' + tmpStr + '>' + c + '</span>';
40706
40713
 
40714
+ let tmpStrExon = 'style="background-color:' + pos2exonColor[cnt] + '"';
40715
+ htmlExon += '<span id="' + pre + '_' + ic.pre + chnid + '_' + pos + '" title="' + c + pos + ', Exon ' + (pos2exonIndex[cnt] + 1) + ': ' + pos2genome[cnt] + '" class="icn3d-residue" ' + tmpStrExon + '>&nbsp;</span>';
40716
+
40707
40717
  htmlTmp2 += ic.showSeqCls.insertGapOverview(chnid, i);
40708
40718
 
40709
- 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);
40719
+ // 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);
40720
+ let emptyWidth = Math.round(ic.seqAnnWidth * i /(ic.maxAnnoLength + ic.nTotalGap) - prevEmptyWidth - prevLineWidth);
40710
40721
  if(emptyWidth < 0) emptyWidth = 0;
40711
40722
 
40712
40723
  htmlTmp2 += '<div style="display:inline-block; width:' + emptyWidth + 'px;">&nbsp;</div>';
@@ -40727,19 +40738,21 @@ class AddTrack {
40727
40738
 
40728
40739
  prevEmptyWidth += emptyWidth;
40729
40740
  prevLineWidth += widthPerRes;
40730
-
40731
- ++currResi;
40741
+ ++cnt;
40732
40742
  }
40733
40743
  else {
40734
40744
  if(bErrorMess) {
40735
- html += '<span>' + c + '</span>';
40745
+ html += '<span>' + c + '</span>';
40736
40746
  }
40737
40747
  else {
40738
40748
  html += '<span>-</span>';
40749
+ htmlExon += '<span></span>';
40739
40750
  }
40740
40751
  }
40741
40752
  }
40742
40753
 
40754
+ // if(ic.seqStartLen && ic.seqStartLen[chnid]) html += ic.showSeqCls.insertMulGap(ic.seqEndLen[chnid], '-');
40755
+
40743
40756
  if(fromArray !== undefined) {
40744
40757
  htmlTmp2 = '';
40745
40758
  let fromArray2 = [], toArray2 = [];
@@ -40765,13 +40778,77 @@ class AddTrack {
40765
40778
  let colorStr =(atom.color === undefined || atom.color.getHexString() === 'FFFFFF') ? 'DDDDDD' : atom.color.getHexString();
40766
40779
  let color =(atom.color !== undefined) ? colorStr : "CCCCCC";
40767
40780
 
40781
+ let cnt, prevCntTotal = 0;
40768
40782
  for(let i = 0, il = fromArray2.length; i < il; ++i) {
40769
40783
  htmlTmp2 += ic.showSeqCls.insertGapOverview(chnid, fromArray2[i]);
40770
40784
 
40771
- 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));
40785
+ let initialPos = (seqStartLen) ? fromArray2[i] : fromArray2[i] - ic.baseResi[chnid] - 1;
40786
+
40787
+ 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));
40788
+ if(emptyWidth < 0) emptyWidth = 0;
40789
+
40772
40790
  htmlTmp2 += '<div style="display:inline-block; width:' + emptyWidth + 'px;">&nbsp;</div>';
40773
40791
 
40774
- 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>';
40792
+ if(!exonArray) {
40793
+ 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>';
40794
+ }
40795
+ else {
40796
+ // determine how this range sits in the exon ranges in exonArray
40797
+ let startExon, endExon;
40798
+
40799
+ cnt = toArray[i] - fromArray[i] + 1;
40800
+ let from = prevCntTotal, to = prevCntTotal + cnt - 1;
40801
+
40802
+ prevCntTotal += cnt;
40803
+
40804
+ // fromArray2 was adjusted with gaps, no gaps in this case
40805
+ // let offset = fromArray2[i] - fromArray[i];
40806
+ // let emptyWidth = Math.round(ic.seqAnnWidth * offset /(ic.maxAnnoLength + ic.nTotalGap));
40807
+ // htmlTmp2 += '<div style="display:inline-block; width:' + emptyWidth + 'px;">&nbsp;</div>';
40808
+
40809
+ for(let j = 0, jl = exonArray.length; j < jl; ++j) {
40810
+ let start = exonArray[j].resStart, end = exonArray[j].resEnd;
40811
+
40812
+ if(from >= start && from <= end) {
40813
+ startExon = {exonIndex: j, rangeStart: start, rangeEnd: end, from: from, genomeRange: exonArray[j].genomeRange};
40814
+ }
40815
+
40816
+ if(to >= start && to <= end) {
40817
+ endExon = {exonIndex: j, rangeStart: start, rangeEnd: end, to: to, genomeRange: exonArray[j].genomeRange};
40818
+ }
40819
+ }
40820
+
40821
+ let startColorStr, endColorStr, colorGradient;
40822
+ if(startExon && endExon && startExon.exonIndex == endExon.exonIndex) { //
40823
+ startColorStr = this.getExonColor(startExon.rangeStart, startExon.rangeEnd, from);
40824
+ endColorStr = this.getExonColor(startExon.rangeStart, startExon.rangeEnd, to);
40825
+
40826
+ colorGradient = startColorStr + ' 0%, #FFF 50%, ' + endColorStr + ' 100%';
40827
+ htmlTmp2 += this.getExonHtml(startExon.exonIndex, colorGradient, startExon.from, endExon.to, startExon.genomeRange);
40828
+ }
40829
+ else {
40830
+ if(startExon) {
40831
+ startColorStr = this.getExonColor(startExon.rangeStart, startExon.rangeEnd, from);
40832
+
40833
+ colorGradient = startColorStr + ' 0%, #FFF 50%, #00F 100%';
40834
+ htmlTmp2 += this.getExonHtml(startExon.exonIndex, colorGradient, startExon.from, startExon.rangeEnd, startExon.genomeRange);
40835
+ }
40836
+
40837
+ if(startExon && endExon) {
40838
+ for(let j = startExon.exonIndex + 1; j < endExon.exonIndex; ++j) {
40839
+ colorGradient = '#F00 0%, #FFF 50%, #00F 100%';
40840
+ htmlTmp2 += this.getExonHtml(j, colorGradient, exonArray[j].resStart, exonArray[j].resEnd, exonArray[j].genomeRange);
40841
+ }
40842
+
40843
+ endColorStr = this.getExonColor(endExon.rangeStart, endExon.rangeEnd, to);
40844
+
40845
+ colorGradient = '#F00 0%, #FFF 50%, ' + endColorStr + ' 100%';
40846
+ htmlTmp2 += this.getExonHtml(endExon.exonIndex, colorGradient, endExon.rangeStart, endExon.to, endExon.genomeRange);
40847
+ }
40848
+ }
40849
+
40850
+ //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>';
40851
+ }
40775
40852
  }
40776
40853
  }
40777
40854
 
@@ -40783,12 +40860,37 @@ class AddTrack {
40783
40860
 
40784
40861
  html += htmlTmp;
40785
40862
  html2 += htmlTmp2 + htmlTmp;
40863
+ htmlExon += htmlTmp;
40786
40864
 
40787
40865
  html3 += '</div>';
40866
+ html3Exon += '</div>';
40867
+
40868
+ if(!exonArray) {
40869
+ $("#" + ic.pre + "dt_custom_" + chnid + "_" + simpTitle).html(html);
40870
+ $("#" + ic.pre + "ov_custom_" + chnid + "_" + simpTitle).html(html2);
40871
+ $("#" + ic.pre + "tt_custom_" + chnid + "_" + simpTitle).html(html3);
40872
+ }
40873
+ else {
40874
+ $("#" + ic.pre + "dt_custom_" + chnid + "_" + simpTitle).html(htmlExon + html);
40875
+ $("#" + ic.pre + "ov_custom_" + chnid + "_" + simpTitle).html(html2);
40876
+ $("#" + ic.pre + "tt_custom_" + chnid + "_" + simpTitle).html(html3Exon + html3);
40877
+ }
40878
+ }
40879
+
40880
+ getExonHtml(exonIndex, colorGradient, from, to, genomeRange) { let ic = this.icn3d; ic.icn3dui;
40881
+ 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>';
40882
+ }
40788
40883
 
40789
- $("#" + ic.pre + "dt_custom_" + chnid + "_" + simpTitle).html(html);
40790
- $("#" + ic.pre + "ov_custom_" + chnid + "_" + simpTitle).html(html2);
40791
- $("#" + ic.pre + "tt_custom_" + chnid + "_" + simpTitle).html(html3);
40884
+ getExonColor(start, end, pos) { let ic = this.icn3d; ic.icn3dui;
40885
+ let middle = ( start + end) * 0.5;
40886
+ if(pos < middle) {
40887
+ let gb = parseInt((pos - start) / (middle - start) * 255);
40888
+ return "rgb(255, " + gb + ", " + gb + ")";
40889
+ }
40890
+ else {
40891
+ let rg = parseInt((end - pos) / (end - middle) * 255);
40892
+ return "rgb(" + rg + ", " + rg + ", 255)";
40893
+ }
40792
40894
  }
40793
40895
 
40794
40896
  alignSequenceToStructure(chainid, data, title) { let ic = this.icn3d, me = ic.icn3dui;
@@ -41096,6 +41198,7 @@ class AddTrack {
41096
41198
  let result = this.getFullText(text);
41097
41199
  text = result.text;
41098
41200
  this.showNewTrack(chainid, title, text, undefined, undefined, type, color, bMsa);
41201
+
41099
41202
  return false;
41100
41203
  }
41101
41204
 
@@ -41218,6 +41321,722 @@ class AddTrack {
41218
41321
  }
41219
41322
  }
41220
41323
 
41324
+ async getMsa(acclist, firstAcc, chainSeq) { let ic = this.icn3d, me = ic.icn3dui;
41325
+ let trackTitleArray = [firstAcc], trackSeqArray = [];
41326
+ // get all seq
41327
+ let url = me.htmlCls.baseUrl + "/vastdyn/vastdyn.cgi?chainlist=" + acclist;
41328
+ let data = await me.getAjaxPromise(url, 'jsonp');
41329
+ let maxLen = 0, maxIndex = 0, index = 0;
41330
+ //let seqArray = [];
41331
+ for(let acc in data) {
41332
+ let seq = data[acc];
41333
+ //seqArray.push(seq);
41334
+
41335
+ let pos = acc.indexOf('.');
41336
+ if(pos != -1) {
41337
+ acc = acc.substr(0, pos);
41338
+ }
41339
+ trackTitleArray.push(acc);
41340
+
41341
+ if(seq.length > maxLen) {
41342
+ maxLen = seq.length;
41343
+ maxIndex = index;
41344
+ }
41345
+ ++index;
41346
+ }
41347
+
41348
+ // pairwise align each seq to the one with maxIndex
41349
+ url = me.htmlCls.baseUrl + 'pwaln/pwaln.fcgi?from=msa';
41350
+
41351
+ let accArray = acclist.split(',');
41352
+ // oroginal index, chain as the first one
41353
+ let acc2index = {};
41354
+ acc2index[firstAcc] = 0;
41355
+ for(let i = 0, il = accArray.length; i < il; ++i) {
41356
+ acc2index[accArray[i]] = i + 1;
41357
+ }
41358
+ let targetId = accArray[maxIndex];
41359
+ accArray.splice(maxIndex, 1);
41360
+
41361
+ let queries = (chainSeq) ? chainSeq : firstAcc;
41362
+ if(accArray.length > 0) queries += ',' + accArray.join(',');
41363
+
41364
+ let dataObj = {'targets': targetId, 'queries': queries};
41365
+ let alignData = await me.getAjaxPostPromise(url, dataObj);
41366
+
41367
+ if(!alignData.data) {
41368
+ console.log("The protein accessions " + targetId + "," + queries + " can not be aligned...");
41369
+ return;
41370
+ }
41371
+
41372
+ // get aligned length for each pair
41373
+ let index_alignLen = [];
41374
+ ic.qt_start_end = {};
41375
+ // target: targetId
41376
+ // queries: accArray
41377
+ let accArrayFound = [], querySeqArray = [];
41378
+ let firstKey = Object.keys(alignData.targets)[0];
41379
+ let targetSeq = alignData.targets[firstKey].seqdata;
41380
+
41381
+ //add firstAcc to accArray
41382
+ accArray.splice(0, 0, firstAcc);
41383
+
41384
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
41385
+ let query, target;
41386
+
41387
+ if(!alignData.data[index]) {
41388
+ continue;
41389
+ }
41390
+
41391
+ query = alignData.data[index].query;
41392
+ let acc;
41393
+ if(query.acc.length <= 5) { // PDB
41394
+ acc = query.acc.substr(0, 4) + '_' + query.acc.substr(4, 1);
41395
+ }
41396
+ else {
41397
+ acc = query.acc;
41398
+ }
41399
+
41400
+ if(index == 0) acc = firstAcc;
41401
+
41402
+ accArrayFound.push(acc);
41403
+
41404
+ firstKey = Object.keys(alignData.data[index].targets)[0];
41405
+ target = alignData.data[index].targets[firstKey];
41406
+
41407
+ target = target.hsps[0];
41408
+
41409
+ querySeqArray.push(query.seqdata);
41410
+ let alignLen = target.scores.num_ident * 100 + query.sz; // order by aligned seq length, then seq length
41411
+
41412
+ ic.qt_start_end[index] = [];
41413
+
41414
+ let segArray = target.segs;
41415
+ for(let i = 0, il = segArray.length; i < il; ++i) {
41416
+ let seg = segArray[i];
41417
+ let qt_start_end = {t_start: seg.orifrom, t_end: seg.orito, q_start: seg.from, q_end: seg.to};
41418
+ ic.qt_start_end[index].push(qt_start_end);
41419
+ }
41420
+
41421
+ index_alignLen.push({index: index, alignLen: alignLen});
41422
+ }
41423
+
41424
+ accArray = accArrayFound;
41425
+
41426
+ index_alignLen.sort(function(a,b){
41427
+ return b.alignLen - a.alignLen;
41428
+ });
41429
+
41430
+ // start and end of MSA
41431
+ let start_t = 9999, end_t = -1;
41432
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
41433
+ if(!ic.qt_start_end[index]) continue;
41434
+
41435
+ for(let i = 0, il = ic.qt_start_end[index].length; i < il; ++i) {
41436
+ let start1, end1;
41437
+
41438
+ start1 = ic.qt_start_end[index][i].t_start;
41439
+ end1 = ic.qt_start_end[index][i].t_end;
41440
+
41441
+ for(let j = start1; j <= end1; ++j) {
41442
+ if(j < start_t) start_t = j;
41443
+ if(j > end_t) end_t = j;
41444
+ }
41445
+ }
41446
+ }
41447
+
41448
+ // N- and C-terminal residues
41449
+ let maxNtermLen = start_t, maxCtermLen = targetSeq.length - (end_t + 1);
41450
+ let startArray = [], endArray = [];
41451
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
41452
+ if(!ic.qt_start_end[index]) continue;
41453
+
41454
+ let qPos = ic.qt_start_end[index][0].q_start;
41455
+ startArray.push(qPos);
41456
+ if(maxNtermLen < qPos) maxNtermLen = qPos;
41457
+
41458
+ let lastIndex = ic.qt_start_end[index].length - 1;
41459
+ qPos = ic.qt_start_end[index][lastIndex].q_end;
41460
+ endArray.push(qPos);
41461
+ let dist = querySeqArray[index].length - (qPos + 1);
41462
+ if(maxCtermLen < dist) maxCtermLen = dist;
41463
+ }
41464
+
41465
+ ic.msaSeq = {};
41466
+ // assign the template
41467
+ ic.msaSeq[targetId] = '';
41468
+
41469
+ for(let i = start_t; i <= end_t; ++i) {
41470
+ ic.msaSeq[targetId] += targetSeq[i];
41471
+ }
41472
+
41473
+ // progressively merge sequences, starting from most similar to least similar
41474
+ let alignedChainIndice = [0];
41475
+ for(let arrayIndex = 0, arrayIndexl = index_alignLen.length; arrayIndex < arrayIndexl; ++arrayIndex) {
41476
+ let index = index_alignLen[arrayIndex].index;
41477
+ alignedChainIndice.push(index);
41478
+
41479
+ ic.msaSeq[accArray[index]] = '';
41480
+
41481
+ // some proteins may not be aligned
41482
+ if(!querySeqArray[index]) continue;
41483
+
41484
+ ic.setSeqAlignCls.mergeTwoSeqForAllSimple(targetId, accArray, index, alignedChainIndice, start_t, end_t, querySeqArray);
41485
+ }
41486
+
41487
+ // add N-terminal seq
41488
+ let seqN = '', cnt;
41489
+ for(let i = 0; i < maxNtermLen - start_t; ++i) {
41490
+ seqN += '-';
41491
+ }
41492
+ for(let i = 0; i < start_t; ++i) {
41493
+ seqN += targetSeq[i];
41494
+ }
41495
+ ic.msaSeq[targetId] = seqN + ic.msaSeq[targetId];
41496
+
41497
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
41498
+ seqN = '';
41499
+ for(let i = 0; i < maxNtermLen - startArray[index]; ++i) {
41500
+ seqN += '-';
41501
+ }
41502
+ for(let i = 0; i < startArray[index]; ++i) {
41503
+ seqN += querySeqArray[index][i];
41504
+ }
41505
+
41506
+ ic.msaSeq[accArray[index]] = seqN + ic.msaSeq[accArray[index]];
41507
+ }
41508
+
41509
+ // add C-terminal seq
41510
+ for(let i = end_t + 1; i < targetSeq.length; ++i) {
41511
+ ic.msaSeq[targetId] += targetSeq[i];
41512
+ }
41513
+
41514
+ cnt = targetSeq.length - (end_t + 1);
41515
+ for(let i = 0; i < maxCtermLen - cnt; ++i) {
41516
+ ic.msaSeq[targetId] += '-';
41517
+ }
41518
+
41519
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
41520
+ for(let i = endArray[index] + 1; i < querySeqArray[index].length; ++i) {
41521
+ ic.msaSeq[accArray[index]] += querySeqArray[index][i];
41522
+ }
41523
+
41524
+ cnt = querySeqArray[index].length - (endArray[index] + 1);
41525
+ for(let i = 0; i < maxCtermLen - cnt; ++i) {
41526
+ ic.msaSeq[accArray[index]] += '-';
41527
+ }
41528
+ }
41529
+
41530
+ for(let acc in ic.msaSeq) {
41531
+ let index = acc2index[acc];
41532
+ trackSeqArray[index] = ic.msaSeq[acc];
41533
+ trackTitleArray[index] = acc;
41534
+ }
41535
+
41536
+ // some of the protein may not be aligned
41537
+ let trackTitleArrayFinal = [], trackSeqArrayFinal = [];
41538
+ for(let i = 0, il = trackSeqArray.length; i < il; ++i) {
41539
+ if(trackSeqArray[i]) {
41540
+ trackSeqArrayFinal.push(trackSeqArray[i]);
41541
+ trackTitleArrayFinal.push(trackTitleArray[i]);
41542
+ }
41543
+ }
41544
+
41545
+ let seqFirst = trackSeqArrayFinal[0];
41546
+
41547
+ trackSeqArrayFinal.splice(0, 1);
41548
+ trackTitleArrayFinal.splice(0, 1);
41549
+
41550
+ return {trackTitleArray: trackTitleArrayFinal, trackSeqArray: trackSeqArrayFinal, seqFirst: seqFirst};
41551
+ }
41552
+
41553
+ async getIsoformMsa(acclist, acc2exons) { let ic = this.icn3d, me = ic.icn3dui;
41554
+ let trackTitleArray = [], trackSeqArray = [];
41555
+ // get all seq
41556
+ let url = me.htmlCls.baseUrl + "/vastdyn/vastdyn.cgi?chainlist=" + acclist;
41557
+ let data = await me.getAjaxPromise(url, 'jsonp');
41558
+ let maxLen = 0, maxIndex = 0, index = 0;
41559
+ let accArray = [], querySeqArray = [];
41560
+ for(let acc in data) {
41561
+ let seq = data[acc];
41562
+ querySeqArray.push(seq);
41563
+
41564
+ let pos = acc.indexOf('.');
41565
+ if(pos != -1) {
41566
+ acc = acc.substr(0, pos);
41567
+ }
41568
+ accArray.push(acc);
41569
+
41570
+ if(seq.length > maxLen) {
41571
+ maxLen = seq.length;
41572
+ maxIndex = index;
41573
+ }
41574
+ ++index;
41575
+ }
41576
+
41577
+ // get aligned length for each pair
41578
+ ic.qt_start_end = {};
41579
+
41580
+ // use the genomic interval as the alignment template
41581
+ let targetId = 'genomeRes';
41582
+
41583
+ let acc2index = {};
41584
+
41585
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
41586
+ let acc = accArray[index];
41587
+
41588
+ acc2index[acc] = index;
41589
+
41590
+ ic.qt_start_end[index] = [];
41591
+
41592
+ let segArray = acc2exons[acc];
41593
+
41594
+ for(let i = 0, il = segArray.length; i < il; ++i) {
41595
+ let seg = segArray[i];
41596
+
41597
+ // mRNA has the reverse order, use negative to make the order right, then minus the offset
41598
+ let qt_start_end = {t_start: ic.exonOrder * seg.genResStart, t_end: ic.exonOrder * seg.genResEnd, q_start: seg.resStart, q_end: seg.resEnd};
41599
+ ic.qt_start_end[index].push(qt_start_end);
41600
+ }
41601
+ }
41602
+
41603
+ // start and end of MSA
41604
+ let start_t = 999999999, end_t = -999999999;
41605
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
41606
+ if(!ic.qt_start_end[index]) continue;
41607
+
41608
+ for(let i = 0, il = ic.qt_start_end[index].length; i < il; ++i) {
41609
+ let start1, end1;
41610
+
41611
+ start1 = ic.qt_start_end[index][i].t_start;
41612
+ end1 = ic.qt_start_end[index][i].t_end;
41613
+
41614
+ for(let j = start1; j <= end1; ++j) {
41615
+ if(j < start_t) start_t = j;
41616
+ if(j > end_t) end_t = j;
41617
+ }
41618
+ }
41619
+ }
41620
+
41621
+ // minus the offset start_t
41622
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
41623
+ let segArray = ic.qt_start_end[index];
41624
+ for(let i = 0, il = segArray.length; i < il; ++i) {
41625
+ let seg = segArray[i];
41626
+ seg.t_start -= start_t;
41627
+ seg.t_end -= start_t;
41628
+ }
41629
+ }
41630
+
41631
+ ic.msaSeq = {};
41632
+ // assign the template
41633
+ ic.msaSeq[targetId] = '';
41634
+
41635
+ let start_tFinal = 0;
41636
+ let end_tFinal = end_t - start_t;
41637
+
41638
+ for(let i = start_tFinal; i <= end_tFinal; ++i) {
41639
+ ic.msaSeq[targetId] += 'X'; // fake seq
41640
+ }
41641
+
41642
+ // progressively merge sequences, starting from most similar to least similar
41643
+ let alignedChainIndice = [0];
41644
+ for(let index = 0, indexl = accArray.length; index < indexl; ++index) {
41645
+ alignedChainIndice.push(index);
41646
+
41647
+ ic.msaSeq[accArray[index]] = '';
41648
+
41649
+ // some proteins may not be aligned
41650
+ if(!querySeqArray[index]) continue;
41651
+ ic.setSeqAlignCls.mergeTwoSeqForAllSimple(targetId, accArray, index, alignedChainIndice, start_tFinal, end_tFinal, querySeqArray);
41652
+ }
41653
+
41654
+ for(let acc in ic.msaSeq) {
41655
+ let index = acc2index[acc];
41656
+
41657
+ if(index !== undefined) {
41658
+ trackSeqArray[index] = ic.msaSeq[acc];
41659
+ trackTitleArray[index] = acc;
41660
+ }
41661
+ }
41662
+
41663
+ // remove introns in trackSeqArray
41664
+ let trackSeqArrayFinal = [];
41665
+ for(let i = 0, il = trackSeqArray.length; i < il; ++i) {
41666
+ trackSeqArrayFinal[i] = '';
41667
+ }
41668
+
41669
+ for(let j = 0, jl = trackSeqArray[maxIndex].length; j < jl; ++j) {
41670
+ let seq = trackSeqArray[maxIndex][j];
41671
+
41672
+ let bExon = (seq != '-') ? true : false;
41673
+ if(!bExon) {
41674
+ for(let i = 0, il = trackSeqArray.length; i < il; ++i) {
41675
+ if(trackSeqArray[i][j] != '-') {
41676
+ bExon = true;
41677
+ break;
41678
+ }
41679
+ }
41680
+ }
41681
+
41682
+ if(bExon) {
41683
+ for(let i = 0, il = trackSeqArray.length; i < il; ++i) {
41684
+ trackSeqArrayFinal[i] += trackSeqArray[i][j];
41685
+ }
41686
+ }
41687
+ }
41688
+
41689
+ return {trackTitleArray: trackTitleArray, trackSeqArray: trackSeqArrayFinal, maxIndex: maxIndex};
41690
+ }
41691
+
41692
+ async showMsaTracks(chainid, seqFirst, trackTitleArray, trackSeqArray, startpos, type, acc2exons) { let ic = this.icn3d; ic.icn3dui;
41693
+ //ic.startposGiSeq = undefined;
41694
+ for(let i = 0, il = ic.giSeq[chainid].length; i < il; ++i) {
41695
+ //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;
41696
+ let pos = ic.ParserUtilsCls.getResi(chainid, i);
41697
+
41698
+ if(pos != startpos) {
41699
+ continue;
41700
+ }
41701
+ else {
41702
+ ic.startposGiSeq = i;
41703
+ }
41704
+ }
41705
+
41706
+ if(ic.startposGiSeq === undefined) {
41707
+ alert("Please double check the start position before clicking \"Add Track\"");
41708
+ return;
41709
+ }
41710
+
41711
+ // set up gap for the master seq
41712
+ // don't count gaps in both ends
41713
+ ic.targetGapHash = {};
41714
+ let prevSeq = '-', prevPos = 0, from, to, cnt = 0, dashCnt = 0;
41715
+ let bFound = false, seqStart = 0, seqEnd = 0, seqLength = seqFirst.length;
41716
+ // add gaps to the N- and C-terminal
41717
+ if(!ic.seqStartLen) ic.seqStartLen = {};
41718
+ if(!ic.seqEndLen) ic.seqEndLen = {};
41719
+ for(let i = 0, il = seqFirst.length; i < il; ++i) {
41720
+ if(seqFirst[i] == '-' && seqFirst[i] != prevSeq) { // start of gap
41721
+ from = cnt;
41722
+ dashCnt = 0;
41723
+ }
41724
+
41725
+ if(prevSeq == '-' && seqFirst[i] != prevSeq && cnt > 0) { // end of gap
41726
+ to = prevPos;
41727
+ ic.targetGapHash[from + ic.startposGiSeq] = {'from': from + ic.startposGiSeq, 'to': to + dashCnt - 1 + ic.startposGiSeq};
41728
+ }
41729
+
41730
+ prevSeq = seqFirst[i];
41731
+ prevPos = cnt;
41732
+
41733
+ if(seqFirst[i] != '-') {
41734
+ ++cnt;
41735
+ seqEnd = i;
41736
+ ic.seqEndLen[chainid] = seqLength - 1 - seqEnd;
41737
+
41738
+ if(!bFound) {
41739
+ seqStart = i;
41740
+ ic.seqStartLen[chainid] = seqStart;
41741
+
41742
+ bFound = true;
41743
+ }
41744
+ }
41745
+ else {
41746
+ ++dashCnt;
41747
+ }
41748
+ }
41749
+
41750
+ // adjust the total length
41751
+ if(ic.maxAnnoLength < ic.maxAnnoLengthOri + ic.seqStartLen[chainid] + ic.seqEndLen[chainid]) {
41752
+ ic.maxAnnoLength = ic.maxAnnoLengthOri + ic.seqStartLen[chainid] + ic.seqEndLen[chainid];
41753
+ }
41754
+
41755
+ await ic.annotationCls.resetAnnoAll();
41756
+
41757
+ let targetGapHashStr = '';
41758
+ let cntTmp = 0;
41759
+ for(let i in ic.targetGapHash) {
41760
+ if(cntTmp > 0) targetGapHashStr += ' ';
41761
+ targetGapHashStr += i + '_' + ic.targetGapHash[i].from + '_' + ic.targetGapHash[i].to;
41762
+ ++cntTmp;
41763
+ }
41764
+
41765
+ //me.htmlCls.clickMenuCls.setLogCmd("msa | " + targetGapHashStr, true);
41766
+
41767
+ // add tracks
41768
+ let resi2cntSameRes = {}; // count of same residue at each position
41769
+ for(let j = 0, jl = trackSeqArray.length; j < jl; ++j) {
41770
+ let resi = startpos;
41771
+ let text = '';
41772
+ for(let k = 0; k < ic.startposGiSeq; ++k) {
41773
+ if(ic.targetGapHash.hasOwnProperty(k)) {
41774
+ for(let m = 0; m < ic.targetGapHash[k].to - ic.targetGapHash[k].from + 1; ++m) {
41775
+ text += '-';
41776
+ }
41777
+ }
41778
+
41779
+ text += '-';
41780
+ }
41781
+
41782
+ let resn, prevResn = '-';
41783
+ let fromArray = [], toArray = [];
41784
+ let bFound = false;
41785
+ let seqStartLen = 0;
41786
+ // for(let k = seqStart; k <= seqEnd; ++k) {
41787
+ for(let k = 0; k < seqLength; ++k) {
41788
+ //if(seqFirst[k] == '-') continue;
41789
+
41790
+ if(j == 0) resi2cntSameRes[resi] = 0;
41791
+
41792
+ resn = trackSeqArray[j][k];
41793
+
41794
+ if(prevResn == '-' && resn != '-') {
41795
+ fromArray.push(k);
41796
+ }
41797
+
41798
+ if(prevResn != '-' && resn == '-') {
41799
+ toArray.push(k - 1);
41800
+ }
41801
+
41802
+ if(resn != '-') {
41803
+ if(!bFound) {
41804
+ seqStartLen = k;
41805
+ bFound = true;
41806
+ }
41807
+ }
41808
+
41809
+ text += resn; //ic.giSeq[chainid][i];
41810
+
41811
+ if(seqFirst[k] != '-') {
41812
+ if(seqFirst[k] == trackSeqArray[j][k]) ++resi2cntSameRes[resi];
41813
+ ++resi;
41814
+ }
41815
+
41816
+ prevResn = resn;
41817
+ }
41818
+
41819
+ // last one
41820
+ if(prevResn != '-') {
41821
+ toArray.push(seqLength - 1);
41822
+ }
41823
+
41824
+ let title =(trackTitleArray[j].length < 20) ? trackTitleArray[j] : trackTitleArray[j].substr(0, 20) + '...';
41825
+ let bMsa = true;
41826
+ let exonArray = (acc2exons) ? acc2exons[trackTitleArray[j]] : undefined;
41827
+ this.showNewTrack(chainid, title, text, undefined, undefined, type, undefined, bMsa, fromArray, toArray, seqStartLen, exonArray);
41828
+ }
41829
+
41830
+ // set color for the master seq
41831
+ if(trackSeqArray.length > 0) {
41832
+ if(ic.queryresi2score === undefined) ic.queryresi2score = {};
41833
+ if(ic.queryresi2score[chainid] === undefined) ic.queryresi2score[chainid] = {};
41834
+
41835
+ let nSeq = trackSeqArray.length;
41836
+ for(let resi in resi2cntSameRes) {
41837
+ let score = parseInt(resi2cntSameRes[resi] / nSeq * 100);
41838
+ ic.queryresi2score[chainid][resi] = score;
41839
+ }
41840
+
41841
+ let resiArray = Object.keys(resi2cntSameRes);
41842
+ let start = Math.min.apply(null, resiArray);
41843
+ let end = Math.max.apply(null, resiArray);
41844
+
41845
+ let resiScoreStr = '';
41846
+ for(let resi = start; resi <= end; ++resi) {
41847
+ if(resi2cntSameRes.hasOwnProperty(resi)) {
41848
+ resiScoreStr += Math.round(resi2cntSameRes[resi] / nSeq * 9); // max 9
41849
+ }
41850
+ else {
41851
+ resiScoreStr += '_';
41852
+ }
41853
+ }
41854
+
41855
+ ic.opts['color'] = 'align custom';
41856
+ ic.setColorCls.setColorByOptions(ic.opts, ic.hAtoms);
41857
+
41858
+ ic.hlUpdateCls.updateHlAll();
41859
+
41860
+ ic.drawCls.draw();
41861
+
41862
+ //me.htmlCls.clickMenuCls.setLogCmd('color align custom | ' + chainid + ' | range ' + start + '_' + end + ' | ' + resiScoreStr, true);
41863
+ }
41864
+ }
41865
+
41866
+ processAccList(acclist) { let ic = this.icn3d; ic.icn3dui;
41867
+ // remove version from acc
41868
+ let accArray = acclist.split(',');
41869
+ let accHash = {};
41870
+ let acclistTmp = '';
41871
+ for(let i = 0, il = accArray.length; i < il; ++i) {
41872
+ let acc = accArray[i];
41873
+
41874
+ if(accHash.hasOwnProperty(acc)) {
41875
+ continue;
41876
+ }
41877
+ else {
41878
+ accHash[acc] = 1;
41879
+ }
41880
+
41881
+ let pos = acc.indexOf('.');
41882
+ if(pos != -1) {
41883
+ acclistTmp += acc.substr(0, pos);
41884
+ }
41885
+ else {
41886
+ acclistTmp += acc;
41887
+ }
41888
+
41889
+ if(i < accArray.length - 1) {
41890
+ acclistTmp += ',';
41891
+ }
41892
+ }
41893
+
41894
+ return acclistTmp;
41895
+ }
41896
+
41897
+ async addExonTracks(chainid, geneid, startpos, type) { let ic = this.icn3d, me = ic.icn3dui;
41898
+ let seqFirst, trackTitleArray = [], trackSeqArray = [];
41899
+
41900
+ // get acclist from geneid
41901
+ let url = me.htmlCls.baseUrl + "/vastdyn/vastdyn.cgi?geneid2isoforms=" + geneid;
41902
+ let data = await me.getAjaxPromise(url, 'jsonp');
41903
+ let accArray = data.acclist;
41904
+ let exons = data.exons;
41905
+ let acc2exons = {};
41906
+
41907
+ let acclist = '';
41908
+ ic.exonOrder = 1; // 1: increasing bp order; -1 decreasing bp order
41909
+ for(let i = 0, il = accArray.length; i < il; ++i) {
41910
+ let accOri = accArray[i];
41911
+ let pos = accOri.indexOf('.');
41912
+ let acc = (pos != -1) ? accOri.substr(0, pos) : accOri;
41913
+
41914
+ let cntTotal = 0, prevCntTotal = 0, rangeArray = [];
41915
+ for(let j = 0, jl = exons[accOri].length; j < jl; ++j) {
41916
+ let itemArray = exons[accOri][j].split('-');
41917
+ itemArray[0] = parseInt(itemArray[0]);
41918
+ itemArray[1] = parseInt(itemArray[1]);
41919
+ itemArray[2] = parseInt(itemArray[2]);
41920
+
41921
+ ic.exonOrder = (itemArray[0] < itemArray[1]) ? 1 : -1;
41922
+
41923
+ let genomeRange = itemArray[0] + '-' + itemArray[1];
41924
+ let cnt = (j == jl - 1) ? itemArray[2] - 3 : itemArray[2]; // The last one is stop codeon
41925
+ cntTotal += cnt;
41926
+
41927
+ let resStart = parseInt(prevCntTotal/3.0 + 0.5); // 0-based
41928
+ let resEnd = parseInt(cntTotal/3.0 + 0.5) - 1; // 0-based
41929
+
41930
+ let genResStart = parseInt(itemArray[0] / 3.0 + 0.5);
41931
+
41932
+ //let genResEnd = parseInt(itemArray[1] / 3.0 + 0.5); // some difference due to round
41933
+ let genResEnd = genResStart + ic.exonOrder * (resEnd - resStart);
41934
+
41935
+ rangeArray.push({genomeRange: genomeRange, genResStart: genResStart, genResEnd: genResEnd, resStart: resStart, resEnd: resEnd});
41936
+
41937
+ prevCntTotal = cntTotal;
41938
+ }
41939
+ acc2exons[acc] = rangeArray;
41940
+
41941
+ acclist += acc;
41942
+ if(i < il - 1) {
41943
+ acclist += ',';
41944
+ }
41945
+ }
41946
+
41947
+ let result = await this.getIsoformMsa(acclist, acc2exons);
41948
+ trackTitleArray = result.trackTitleArray;
41949
+ trackSeqArray = result.trackSeqArray;
41950
+ //seqFirst = result.seqFirst;
41951
+ let maxIndex = result.maxIndex;
41952
+
41953
+ let acclist2 = trackTitleArray[maxIndex];
41954
+ let structure = chainid.substr(0, chainid.indexOf('_'));
41955
+ let firstAcc;
41956
+ if(structure.length > 5) {
41957
+ if(ic.uniprot2acc && ic.uniprot2acc[structure]) structure = ic.uniprot2acc[structure];
41958
+ firstAcc = structure;
41959
+ }
41960
+ else {
41961
+ firstAcc = chainid;
41962
+ }
41963
+
41964
+ // get the sequence from iCn3D because a uniProt ID can not be retrieved in pwaln.fcgi
41965
+ if(structure.length > 5) {
41966
+ let chainSeq = '';
41967
+ for(let i = 0, il = ic.chainsSeq.length; i < il; ++i) {
41968
+ chainSeq += ic.chainsSeq[i].resn;
41969
+ }
41970
+
41971
+ result = await this.getMsa(acclist2, firstAcc, chainSeq);
41972
+ }
41973
+ else {
41974
+ result = await this.getMsa(acclist2, firstAcc);
41975
+ }
41976
+
41977
+ result.trackTitleArray;
41978
+ let trackSeqArray2 = result.trackSeqArray;
41979
+ seqFirst = result.seqFirst;
41980
+
41981
+ // merge trackTitleArray2[0] with trackSeqArray[maxIndex]
41982
+ let A = trackSeqArray[maxIndex], B = trackSeqArray2[0];
41983
+ let i = 0, j = 0;
41984
+
41985
+ let ALen = trackSeqArray.length;
41986
+
41987
+ while (i < A.length && j < B.length) {
41988
+ if(A[i] != B[j]) {
41989
+ if(A[i] == '-') {
41990
+ // inser "-" in B
41991
+ B = B.substr(0, j) + '-' + B.substr(j);
41992
+ seqFirst = seqFirst.substr(0, j) + '-' + seqFirst.substr(j);
41993
+ }
41994
+ else { //if(B[j] == '-') {
41995
+ // inser "-" in A
41996
+ for(let k = 0; k < ALen; ++k) {
41997
+ trackSeqArray[k] = trackSeqArray[k].substr(0, i) + '-' + trackSeqArray[k].substr(i);
41998
+ }
41999
+ }
42000
+ }
42001
+
42002
+ ++i;
42003
+ ++j;
42004
+ }
42005
+
42006
+ await this.showMsaTracks(chainid, seqFirst, trackTitleArray, trackSeqArray, startpos, type, acc2exons);
42007
+
42008
+ me.htmlCls.clickMenuCls.setLogCmd("add exon track | chainid " + chainid + " | geneid " + geneid + " | startpos " + startpos + " | type " + type, true);
42009
+ }
42010
+
42011
+ async addMsaTracks(chainid, startpos, type, fastaList) { let ic = this.icn3d, me = ic.icn3dui;
42012
+ let seqFirst, trackTitleArray = [], trackSeqArray = [];
42013
+
42014
+ let fastaArray = fastaList.split('>');
42015
+
42016
+ // the first array item is empty
42017
+ // the second array item is the sequence of the structure, start with i = 2
42018
+ let posFirst = fastaArray[1].indexOf('\n');
42019
+ //let titleFirst = fastaArray[1].substr(0, posFirst);
42020
+ seqFirst = fastaArray[1].substr(posFirst + 1).replace(/\n/g, '');
42021
+
42022
+ for(let i = 2, il = fastaArray.length; i < il; ++i) {
42023
+ let pos = fastaArray[i].indexOf('\n');
42024
+ let title = fastaArray[i].substr(0, pos);
42025
+ if(title.indexOf('|') != -1) {
42026
+ title = title.split('|')[1];
42027
+ // if(title.indexOf('.') != -1) {
42028
+ // title = title.split('.')[0];
42029
+ // }
42030
+ }
42031
+ trackTitleArray.push(title);
42032
+ let seq = fastaArray[i].substr(pos + 1).replace(/\n/g, '');
42033
+ trackSeqArray.push(seq);
42034
+ }
42035
+
42036
+ await this.showMsaTracks(chainid, seqFirst, trackTitleArray, trackSeqArray, startpos, type);
42037
+
42038
+ me.htmlCls.clickMenuCls.setLogCmd("add msa track | chainid " + chainid + " | startpos " + startpos + " | type " + type + " | fastaList " + fastaList , true);
42039
+ }
41221
42040
  }
41222
42041
 
41223
42042
  /**
@@ -41868,13 +42687,14 @@ class ShowAnno {
41868
42687
  } // if(me.cfg.mmdbid
41869
42688
  } // for(let i = 0
41870
42689
 
41871
- ic.maxAnnoLength = 1;
42690
+ ic.maxAnnoLengthOri = 1;
41872
42691
  for(let chainid in ic.chainsSeq) {
41873
42692
  // use protein or nucleotide as the max length
41874
- if(ic.chainsSeq[chainid].length > ic.maxAnnoLength && (ic.protein_chainid.hasOwnProperty(chainid) || nucleotide_chainid.hasOwnProperty(chainid)) ) {
41875
- ic.maxAnnoLength = ic.chainsSeq[chainid].length;
42693
+ if(ic.chainsSeq[chainid].length > ic.maxAnnoLengthOri && (ic.protein_chainid.hasOwnProperty(chainid) || nucleotide_chainid.hasOwnProperty(chainid)) ) {
42694
+ ic.maxAnnoLengthOri = ic.chainsSeq[chainid].length;
41876
42695
  }
41877
42696
  }
42697
+ ic.maxAnnoLength = ic.maxAnnoLengthOri;
41878
42698
  }
41879
42699
 
41880
42700
  return {'nucleotide_chainid': nucleotide_chainid, 'chemical_chainid': chemical_chainid, 'chemical_set': chemical_set};
@@ -41889,7 +42709,7 @@ class ShowAnno {
41889
42709
  let chemical_chainid = result.chemical_chainid;
41890
42710
  let chemical_set = result.chemical_set;
41891
42711
 
41892
- if(ic.bAnnoShown === undefined || !ic.bAnnoShown || ic.bResetAnno) { // ic.bResetAnno when loading another structure
42712
+ if(!ic.bAnnoShown || ic.bResetAnno) { // ic.bResetAnno when loading another structure
41893
42713
  // assign early to avoid load annotations twice
41894
42714
  ic.bAnnoShown = true;
41895
42715
 
@@ -42030,13 +42850,52 @@ class ShowAnno {
42030
42850
  let thisClass = this;
42031
42851
  let chnidBaseArray = $.map(ic.protein_chainid, function(v) { return v; });
42032
42852
  let index = 0;
42853
+
42854
+ // get geneid
42855
+ if(!ic.chainsGene) ic.chainsGene = {};
42856
+ for(let chnid in ic.protein_chainid) {
42857
+ let structure = chnid.substr(0, chnid.indexOf('_'));
42858
+ // UniProt or NCBI protein accession
42859
+ if(structure.length > 5) {
42860
+ let refseqid, url;
42861
+ if(ic.uniprot2acc && ic.uniprot2acc[structure]) {
42862
+ refseqid = ic.uniprot2acc[structure];
42863
+ }
42864
+ else {
42865
+ try {
42866
+ if(!ic.uniprot2acc) ic.uniprot2acc = {};
42867
+ url = me.htmlCls.baseUrl + "vastdyn/vastdyn.cgi?uniprot2refseq=" + structure;
42868
+ let result = await me.getAjaxPromise(url, 'jsonp');
42869
+ refseqid = (result && result.refseq) ? result.refseq : structure;
42870
+
42871
+ ic.uniprot2acc[structure] = refseqid;
42872
+ }
42873
+ catch {
42874
+ console.log("Problem in getting protein accession from UniProt ID...");
42875
+ refseqid = structure;
42876
+ }
42877
+ }
42878
+
42879
+ // get Gene info from protein name
42880
+ // url = me.htmlCls.baseUrl + "vastdyn/vastdyn.cgi?protein2gene=" + refseqid;
42881
+ // ic.chainsGene[chnid] = await me.getAjaxPromise(url, 'jsonp');
42882
+
42883
+ // get Gene info from uniprot
42884
+ url = "https://rest.uniprot.org/uniprotkb/search?format=json&fields=xref_geneid,gene_names&query=" + structure;
42885
+ let geneData = await me.getAjaxPromise(url, 'json');
42886
+ let geneId = (geneData.results[0] && geneData.results[0].uniProtKBCrossReferences[0]) ? geneData.results[0].uniProtKBCrossReferences[0].id : undefined;
42887
+ let geneSymbol = (geneData.results[0] && geneData.results[0].genes[0] && geneData.results[0].genes[0].geneName) ? geneData.results[0].genes[0].geneName.value : undefined;
42888
+ ic.chainsGene[chnid] = {geneId: geneId, geneSymbol: geneSymbol};
42889
+ }
42890
+ }
42891
+
42033
42892
  for(let chnid in ic.protein_chainid) {
42034
42893
  let buttonStyle = me.utilsCls.isMobile() ? 'none' : 'button';
42035
42894
  let fullProteinName = ic.showSeqCls.getProteinName(chnid);
42036
42895
  let proteinName = fullProteinName;
42037
42896
  //if(proteinName.length > 40) proteinName = proteinName.substr(0, 40) + "...";
42038
42897
  let categoryStr =(index == 0) ? "<span class='icn3d-annoLargeTitle'><b>Proteins</b>: </span><br><br>" : "";
42039
- 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>)" : '';
42898
+ 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>)" : '';
42040
42899
  let structure = chnid.substr(0, chnid.indexOf('_'));
42041
42900
  let chainLink = (structure.length > 5) ? '<a href="https://alphafold.ebi.ac.uk/entry/' + structure + '" target="_blank">' + chnid + '</a>' : chnid;
42042
42901
  let chainHtml = "<div id='" + ic.pre + "anno_" + chnid + "' class='icn3d-annotation'>" + categoryStr
@@ -42587,6 +43446,9 @@ class ShowSeq {
42587
43446
  html += htmlTmp + '<span class="icn3d-seqLine">';
42588
43447
  let helixCnt = 0, sheetCnt = 0;
42589
43448
  let savedSsName = '';
43449
+
43450
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqStartLen[chnid], ' ');
43451
+
42590
43452
  for(let i = 0, il = giSeq.length; i < il; ++i) {
42591
43453
  html += this.insertGap(chnid, i, '-');
42592
43454
  let currResi;
@@ -42644,18 +43506,25 @@ class ShowSeq {
42644
43506
  }
42645
43507
  html += '</span>';
42646
43508
  }
43509
+
43510
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqEndLen[chnid], ' ');
43511
+
42647
43512
  html += '<span class="icn3d-residueNum"></span>';
42648
43513
  html += '</span>';
42649
43514
  html += '<br>';
42650
43515
  html += '</div>';
42651
43516
  html3 += '</div>';
42652
43517
  }
43518
+
42653
43519
  // html to display secondary structures
42654
43520
  htmlTmp = '<div class="icn3d-residueLine" style="white-space:nowrap;">';
42655
43521
  htmlTmp += '<div class="icn3d-annoTitle" anno="0"></div>';
42656
43522
  htmlTmp += '<span class="icn3d-residueNum"></span>';
42657
43523
  html3 += htmlTmp + '<br>';
42658
43524
  html += htmlTmp + '<span class="icn3d-seqLine">';
43525
+
43526
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqStartLen[chnid], '-');
43527
+
42659
43528
  for(let i = 0, il = giSeq.length; i < il; ++i) {
42660
43529
  html += this.insertGap(chnid, i, '-');
42661
43530
  // 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;
@@ -42703,6 +43572,9 @@ class ShowSeq {
42703
43572
  html += '<span>-</span>'; //'<span>-</span>';
42704
43573
  }
42705
43574
  }
43575
+
43576
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqEndLen[chnid], '-');
43577
+
42706
43578
  html += '<span class="icn3d-residueNum"></span>';
42707
43579
  html += '</span>';
42708
43580
  html += '<br>';
@@ -42734,6 +43606,9 @@ class ShowSeq {
42734
43606
  html += htmlTmp + htmlTmp2;
42735
43607
  html2 += htmlTmp + htmlTmp2;
42736
43608
  let pos, nGap = 0;
43609
+
43610
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqStartLen[chnid], '-');
43611
+
42737
43612
  for(let i = 0, il = giSeq.length; i < il; ++i) {
42738
43613
  html += this.insertGap(chnid, i, '-');
42739
43614
  if(ic.targetGapHash !== undefined && ic.targetGapHash.hasOwnProperty(i)) nGap += ic.targetGapHash[i].to - ic.targetGapHash[i].from + 1;
@@ -42763,6 +43638,9 @@ class ShowSeq {
42763
43638
  html += '<span id="giseq_' + ic.pre + chnid + '_' + pos + '" title="' + cFull + pos + '" class="icn3d-residue" style="color:#' + color + '">' + c + '</span>';
42764
43639
  }
42765
43640
  }
43641
+
43642
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqEndLen[chnid], '-');
43643
+
42766
43644
  if(me.cfg.blast_rep_id == chnid) {
42767
43645
  // change color in 3D
42768
43646
  ic.opts['color'] = (ic.blastAcxn) ? 'confidence' : 'conservation';
@@ -42773,8 +43651,11 @@ class ShowSeq {
42773
43651
  // sequence, overview
42774
43652
  let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.chains[chnid]);
42775
43653
  let color =(atom.color) ? atom.color.getHexString() : "CCCCCC";
42776
- let width = Math.round(ic.seqAnnWidth * giSeq.length / ic.maxAnnoLength);
43654
+ let width = Math.round(ic.seqAnnWidth * giSeq.length / (ic.maxAnnoLength + ic.nTotalGap));
42777
43655
  if(width < 1) width = 1;
43656
+
43657
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += this.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
43658
+
42778
43659
  if(me.cfg.blast_rep_id != chnid) { // regular
42779
43660
  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>';
42780
43661
  }
@@ -42788,7 +43669,9 @@ class ShowSeq {
42788
43669
  }
42789
43670
  }
42790
43671
  toArray2.push(giSeq.length - 1);
43672
+
42791
43673
  html2 += '<div id="giseq_summary_' + ic.pre + chnid + '" class="icn3d-seqTitle icn3d-link" gi chain="' + chnid + '" style="width:' + width + 'px;">';
43674
+
42792
43675
  for(let i = 0, il = fromArray2.length; i < il; ++i) {
42793
43676
  html2 += this.insertGapOverview(chnid, fromArray2[i]);
42794
43677
  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>';
@@ -42920,6 +43803,9 @@ class ShowSeq {
42920
43803
  htmlTmp += '<span class="icn3d-residueNum"></span>';
42921
43804
  html3 += htmlTmp + '<br>';
42922
43805
  html += htmlTmp + '<span class="icn3d-seqLine">';
43806
+
43807
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqStartLen[chnid], '-');
43808
+
42923
43809
  for(let i = 0, il = giSeq.length; i < il; ++i) {
42924
43810
  html += this.insertGap(chnid, i, '-');
42925
43811
  //if(i >= ic.matchedPos[chnid] && i - ic.matchedPos[chnid] < ic.chainsSeq[chnid].length) {
@@ -42943,6 +43829,9 @@ class ShowSeq {
42943
43829
  // html += '<span></span>';
42944
43830
  // }
42945
43831
  }
43832
+
43833
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqEndLen[chnid], '-');
43834
+
42946
43835
  html += '<span class="icn3d-residueNum"></span>';
42947
43836
  html += '</span>';
42948
43837
  html += '<br>';
@@ -43590,26 +44479,43 @@ class ShowSeq {
43590
44479
  let html = '';
43591
44480
  //if(me.cfg.blast_rep_id == chnid && ic.targetGapHash!== undefined && ic.targetGapHash.hasOwnProperty(seqIndex)) {
43592
44481
  if(ic.targetGapHash!== undefined && ic.targetGapHash.hasOwnProperty(seqIndex)) {
43593
- for(let j = 0; j <(ic.targetGapHash[seqIndex].to - ic.targetGapHash[seqIndex].from + 1); ++j) {
43594
- if(bNohtml) {
43595
- html += text;
43596
- }
43597
- else {
43598
- html += '<span>' + text + '</span>';
43599
- }
43600
- }
44482
+ html += this.insertMulGap(ic.targetGapHash[seqIndex].to - ic.targetGapHash[seqIndex].from + 1, text, bNohtml);
43601
44483
  }
43602
44484
  return html;
43603
44485
  }
43604
- insertGapOverview(chnid, seqIndex) { let ic = this.icn3d, me = ic.icn3dui;
44486
+
44487
+ insertMulGap(n, text, bNohtml) { let ic = this.icn3d; ic.icn3dui;
44488
+ let html = '';
44489
+ for(let j = 0; j < n; ++j) {
44490
+ if(bNohtml) {
44491
+ html += text;
44492
+ }
44493
+ else {
44494
+ html += '<span>' + text + '</span>';
44495
+ }
44496
+ }
44497
+ return html;
44498
+ }
44499
+
44500
+ insertGapOverview(chnid, seqIndex) { let ic = this.icn3d; ic.icn3dui;
43605
44501
  let html2 = '';
43606
- if(me.cfg.blast_rep_id == chnid && ic.targetGapHash!== undefined && ic.targetGapHash.hasOwnProperty(seqIndex)) {
43607
- let width = ic.seqAnnWidth *(ic.targetGapHash[seqIndex].to - ic.targetGapHash[seqIndex].from + 1) /(ic.maxAnnoLength + ic.nTotalGap);
43608
- html2 += '<div style="display:inline-block; background-color:#333; width:' + width + 'px; height:3px;">&nbsp;</div>';
44502
+ // if(me.cfg.blast_rep_id == chnid && ic.targetGapHash !== undefined && ic.targetGapHash.hasOwnProperty(seqIndex)) {
44503
+ if(ic.targetGapHash !== undefined && ic.targetGapHash.hasOwnProperty(seqIndex)) {
44504
+ html2 += this.insertMulGapOverview(chnid, ic.targetGapHash[seqIndex].to - ic.targetGapHash[seqIndex].from + 1);
43609
44505
  }
43610
44506
  return html2;
43611
44507
  }
43612
44508
 
44509
+ insertMulGapOverview(chnid, n) { let ic = this.icn3d; ic.icn3dui;
44510
+ let html2 = '';
44511
+ let width = ic.seqAnnWidth * n /(ic.maxAnnoLength + ic.nTotalGap);
44512
+ width = parseInt(width);
44513
+
44514
+ // html2 += '<div style="display:inline-block; background-color:#333; width:' + width + 'px; height:3px;">&nbsp;</div>';
44515
+ html2 += '<div style="display:inline-block; width:' + width + 'px;">&nbsp;</div>';
44516
+ return html2;
44517
+ }
44518
+
43613
44519
  setAlternativeSeq(chnid, chnidBase) { let ic = this.icn3d; ic.icn3dui;
43614
44520
  //if(ic.chainsSeq[chnid] !== undefined) {
43615
44521
  let resArray = ic.chainsSeq[chnid];
@@ -50410,6 +51316,8 @@ class MmdbParser {
50410
51316
 
50411
51317
  if(data && data.uniprot) {
50412
51318
  me.cfg.afid = data.uniprot;
51319
+ if(!ic.uniprot2acc) ic.uniprot2acc = {};
51320
+ ic.uniprot2acc[data.uniprot] = refseqid;
50413
51321
  }
50414
51322
  else {
50415
51323
  alert('The accession ' + refseqid + ' can not be mapped to AlphaFold UniProt ID. It will be treated as a UniProt ID instead.');
@@ -56692,14 +57600,121 @@ class SetSeqAlign {
56692
57600
  result = this.getTemplatePosFromOriPos(chainid1, prevIndex1, end_t, bRealign);
56693
57601
  pos1 = result.pos1;
56694
57602
  pos2 = result.pos2;
56695
- //for(let i = pos1; i < pos2; ++i) {
56696
- for(let i = pos1; i <= pos2; ++i) {
57603
+ for(let i = pos1; i < pos2; ++i) {
57604
+ //for(let i = pos1; i <= pos2; ++i) {
56697
57605
  ic.alnChainsSeq[chainid2].push(gapResObject2);
56698
57606
  }
56699
57607
 
56700
57608
  return hAtoms;
56701
57609
  }
56702
57610
 
57611
+ // used for seq MSA
57612
+ mergeTwoSeqForAllSimple(targetId, chainidArray, index, alignedChainIndice, start_t, end_t, querySeqArray) { let ic = this.icn3d; ic.icn3dui;
57613
+ let chainid1 = targetId;
57614
+ let chainid2 = chainidArray[index];
57615
+
57616
+ let pos1, pos2, prevIndex1, prevIndex2;
57617
+
57618
+ for(let i = 0, il = ic.qt_start_end[index].length; i < il; ++i) {
57619
+ let start1, start2, end1, end2, resiStart1, start1Pos, end1Pos;
57620
+
57621
+ start1 = ic.qt_start_end[index][i].t_start;
57622
+ start2 = ic.qt_start_end[index][i].q_start;
57623
+ end1 = ic.qt_start_end[index][i].t_end;
57624
+ end2 = ic.qt_start_end[index][i].q_end;
57625
+
57626
+ // 1. before the mapped residues
57627
+ //resiStart1 = ic.ParserUtilsCls.getResi(chainid1, start1);
57628
+ resiStart1 = start1;
57629
+ start1Pos = start1;
57630
+ end1Pos = end1;
57631
+
57632
+ // if the mapping does not start from start_t, add gaps to the query seq
57633
+ if(i == 0) {
57634
+ pos1 = start_t;
57635
+ pos2 = start1Pos;
57636
+
57637
+ if(start1Pos > start_t) {
57638
+ for(let j = 0, jl = pos2 - pos1; j < jl; ++j) {
57639
+ ic.msaSeq[chainid2] += '-';
57640
+ }
57641
+ }
57642
+ }
57643
+ else {
57644
+ pos1 = prevIndex1;
57645
+ pos2 = start1;
57646
+ let notAlnLen1 = pos2 - (pos1 + 1);
57647
+ let notAlnLen2 = start2 - (prevIndex2 + 1);
57648
+
57649
+ // insert non-aligned residues in query seq
57650
+ // this.insertNotAlignRes(chainid2, prevIndex2+1, notAlnLen2, bRealign);
57651
+
57652
+ for(let j = 0, jl = notAlnLen2; j < jl; ++j) {
57653
+ let resn = querySeqArray[index][prevIndex2+1 + j];
57654
+ ic.msaSeq[chainid2] += resn;
57655
+ }
57656
+
57657
+ if(notAlnLen1 >= notAlnLen2) {
57658
+ // add gaps before the query sequence
57659
+ for(let j = 0, jl = notAlnLen1 - notAlnLen2; j < jl; ++j) {
57660
+ ic.msaSeq[chainid2] += '-';
57661
+ }
57662
+ }
57663
+ else {
57664
+ // check the number of gaps before resiStart1 (n), and insert 'notAlnLen2 - notAlnLen1 - n' gaps
57665
+ // this.addGapAllAlnChains(chainidArray, alignedChainIndice, chainid1, resiStart1, notAlnLen2 - notAlnLen1);
57666
+
57667
+ // let result = this.getResiPosInTemplate(chainid1, resi_t);
57668
+ // let nGap = result.ngap, pos_t = result.pos;
57669
+
57670
+ let pos_t = resiStart1; // position to add gap
57671
+
57672
+ // add gaps for all previously aligned sequences, not the current sequence, which is the last one
57673
+ for(let j = 0, jl = alignedChainIndice.length - 1; j < jl; ++j) {
57674
+ let chainidTmp = (j == 0) ? chainid1 : chainidArray[alignedChainIndice[j]];
57675
+
57676
+ for(let k = 0, kl = notAlnLen2 - notAlnLen1; k < kl; ++k) {
57677
+ //ic.msaSeq[chainidTmp].splice(pos_t, 0, '-');
57678
+ ic.msaSeq[chainidTmp] = ic.msaSeq[chainidTmp].substr(0, pos_t) + '-' + ic.msaSeq[chainidTmp].substr(pos_t);
57679
+ }
57680
+ }
57681
+ }
57682
+ }
57683
+
57684
+ // 2. In the mapped residues
57685
+ pos1 = start1Pos;
57686
+ pos2 = end1Pos;
57687
+
57688
+ let k = 0;
57689
+ for(let j = pos1; j <= pos2; ++j) {
57690
+ // inherit the gaps from the template
57691
+ if(ic.msaSeq[chainid1][j] == '-') {
57692
+ ic.msaSeq[chainid2] += '-';
57693
+ }
57694
+ else {
57695
+ //let resn1 = targetSeq[start1 + k];
57696
+ let resn2 = querySeqArray[index][start2 + k];
57697
+ //let resn2 = (querySeqArray[index]) ? querySeqArray[index][start2 + k] : '?';
57698
+
57699
+ ic.msaSeq[chainid2] += resn2;
57700
+
57701
+ ++k;
57702
+ }
57703
+ }
57704
+
57705
+ prevIndex1 = end1;
57706
+ prevIndex2 = end2;
57707
+ }
57708
+
57709
+ // add gaps at the end
57710
+ pos1 = prevIndex1;
57711
+ pos2 = end_t;
57712
+ for(let i = pos1; i < pos2; ++i) {
57713
+ //for(let i = pos1; i <= pos2; ++i) {
57714
+ ic.msaSeq[chainid2] += '-';
57715
+ }
57716
+ }
57717
+
56703
57718
  setSeqAlignForRealign(chainid_t, chainid, chainIndex) { let ic = this.icn3d, me = ic.icn3dui;
56704
57719
  //var chainid_t = ic.chainidArray[0];
56705
57720
 
@@ -61277,6 +62292,38 @@ class LoadScript {
61277
62292
 
61278
62293
  await ic.cartoon2dCls.draw2Dcartoon(type);
61279
62294
  }
62295
+ else if(command.indexOf('add msa track') == 0) {
62296
+ //add msa track | chainid " + chainid + " | startpos " + startpos + " | type " + type + " | fastaList " + fastaList
62297
+ let paraArray = command.split(' | ');
62298
+
62299
+ let chainid = paraArray[1].substr(8);
62300
+ let startpos = paraArray[2].substr(9);
62301
+ let type = paraArray[3].substr(5);
62302
+ let fastaList = paraArray[4].substr(10);
62303
+
62304
+ if($("#" + ic.pre + "anno_custom")[0]) {
62305
+ $("#" + ic.pre + "anno_custom")[0].checked = true;
62306
+ }
62307
+ $("[id^=" + ic.pre + "custom]").show();
62308
+
62309
+ await ic.addTrackCls.addMsaTracks(chainid, startpos, type, fastaList);
62310
+ }
62311
+ else if(command.indexOf('add exon track') == 0) {
62312
+ //add exon track | chainid " + chainid + " | geneid " + geneid + " | startpos " + startpos + " | type " + type
62313
+ let paraArray = command.split(' | ');
62314
+
62315
+ let chainid = paraArray[1].substr(8);
62316
+ let geneid = paraArray[2].substr(7);
62317
+ let startpos = paraArray[3].substr(9);
62318
+ let type = paraArray[4].substr(5);
62319
+
62320
+ if($("#" + ic.pre + "anno_custom")[0]) {
62321
+ $("#" + ic.pre + "anno_custom")[0].checked = true;
62322
+ }
62323
+ $("[id^=" + ic.pre + "custom]").show();
62324
+
62325
+ await ic.addTrackCls.addExonTracks(chainid, geneid, startpos, type);
62326
+ }
61280
62327
  else {
61281
62328
  await ic.applyCommandCls.applyCommand(ic.commands[i]);
61282
62329
  }
@@ -61989,12 +63036,18 @@ class SelectByCommand {
61989
63036
  residueStr = "*";
61990
63037
  }
61991
63038
  else if(colonPos2 != -1) {
61992
- refResStr = testStr.substr(colonPos2 + 5);
61993
- testStr = testStr.substr(0, colonPos2);
63039
+ refResStr = testStr.substr(colonPos2 + 5);
63040
+ testStr = testStr.substr(0, colonPos2);
63041
+
63042
+ // somehow sometimes refResStr or residueStr is rmpty
63043
+ if(!refResStr) continue;
61994
63044
  }
61995
63045
  else if(colonPos != -1) {
61996
- residueStr = testStr.substr(colonPos + 1);
61997
- testStr = testStr.substr(0, colonPos);
63046
+ residueStr = testStr.substr(colonPos + 1);
63047
+ testStr = testStr.substr(0, colonPos);
63048
+
63049
+ // somehow sometimes refResStr or residueStr is rmpty
63050
+ if(!residueStr) continue;
61998
63051
  }
61999
63052
 
62000
63053
  if(periodPos === -1) {
@@ -72383,7 +73436,7 @@ class iCn3DUI {
72383
73436
  //even when multiple iCn3D viewers are shown together.
72384
73437
  this.pre = this.cfg.divid + "_";
72385
73438
 
72386
- this.REVISION = '3.27.0';
73439
+ this.REVISION = '3.28.0';
72387
73440
 
72388
73441
  // In nodejs, iCn3D defines "window = {navigator: {}}"
72389
73442
  this.bNode = (Object.keys(window).length < 2) ? true : false;