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.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
40329
 
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
-
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
+ }
40788
40879
 
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);
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
+ }
40883
+
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,726 @@ 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.chainsSeq[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 thisClass = this;
41899
+
41900
+ let seqFirst, trackTitleArray = [], trackSeqArray = [];
41901
+
41902
+ // get acclist from geneid
41903
+ let url = me.htmlCls.baseUrl + "/vastdyn/vastdyn.cgi?geneid2isoforms=" + geneid;
41904
+ let data = await me.getAjaxPromise(url, 'jsonp');
41905
+ let accArray = data.acclist;
41906
+ let exons = data.exons;
41907
+ let acc2exons = {};
41908
+
41909
+ let acclist = '';
41910
+ ic.exonOrder = 1; // 1: increasing bp order; -1 decreasing bp order
41911
+ for(let i = 0, il = accArray.length; i < il; ++i) {
41912
+ let accOri = accArray[i];
41913
+ let pos = accOri.indexOf('.');
41914
+ let acc = (pos != -1) ? accOri.substr(0, pos) : accOri;
41915
+
41916
+ let cntTotal = 0, prevCntTotal = 0, rangeArray = [];
41917
+ for(let j = 0, jl = exons[accOri].length; j < jl; ++j) {
41918
+ let itemArray = exons[accOri][j].split('-');
41919
+ itemArray[0] = parseInt(itemArray[0]);
41920
+ itemArray[1] = parseInt(itemArray[1]);
41921
+ itemArray[2] = parseInt(itemArray[2]);
41922
+
41923
+ ic.exonOrder = (itemArray[0] < itemArray[1]) ? 1 : -1;
41924
+
41925
+ let genomeRange = itemArray[0] + '-' + itemArray[1];
41926
+ let cnt = (j == jl - 1) ? itemArray[2] - 3 : itemArray[2]; // The last one is stop codeon
41927
+ cntTotal += cnt;
41928
+
41929
+ let resStart = parseInt(prevCntTotal/3.0 + 0.5); // 0-based
41930
+ let resEnd = parseInt(cntTotal/3.0 + 0.5) - 1; // 0-based
41931
+
41932
+ let genResStart = parseInt(itemArray[0] / 3.0 + 0.5);
41933
+
41934
+ //let genResEnd = parseInt(itemArray[1] / 3.0 + 0.5); // some difference due to round
41935
+ let genResEnd = genResStart + ic.exonOrder * (resEnd - resStart);
41936
+
41937
+ rangeArray.push({genomeRange: genomeRange, genResStart: genResStart, genResEnd: genResEnd, resStart: resStart, resEnd: resEnd});
41938
+
41939
+ prevCntTotal = cntTotal;
41940
+ }
41941
+ acc2exons[acc] = rangeArray;
41942
+
41943
+ acclist += acc;
41944
+ if(i < il - 1) {
41945
+ acclist += ',';
41946
+ }
41947
+ }
41948
+
41949
+ let result = await this.getIsoformMsa(acclist, acc2exons);
41950
+ trackTitleArray = result.trackTitleArray;
41951
+ trackSeqArray = result.trackSeqArray;
41952
+ //seqFirst = result.seqFirst;
41953
+ let maxIndex = result.maxIndex;
41954
+
41955
+ let acclist2 = trackTitleArray[maxIndex];
41956
+ let structure = chainid.substr(0, chainid.indexOf('_'));
41957
+ let firstAcc;
41958
+ if(structure.length > 5) {
41959
+ if(ic.uniprot2acc && ic.uniprot2acc[structure]) structure = ic.uniprot2acc[structure];
41960
+ firstAcc = structure;
41961
+ }
41962
+ else {
41963
+ firstAcc = chainid;
41964
+ }
41965
+
41966
+ // get the sequence from iCn3D because a uniProt ID can not be retrieved in pwaln.fcgi
41967
+ if(structure.length > 5) {
41968
+ let chainSeq = '';
41969
+ for(let i = 0, il = ic.chainsSeq.length; i < il; ++i) {
41970
+ chainSeq += ic.chainsSeq[i].resn;
41971
+ }
41972
+
41973
+ result = await this.getMsa(acclist2, firstAcc, chainSeq);
41974
+ }
41975
+ else {
41976
+ result = await this.getMsa(acclist2, firstAcc);
41977
+ }
41978
+
41979
+ result.trackTitleArray;
41980
+ let trackSeqArray2 = result.trackSeqArray;
41981
+ seqFirst = result.seqFirst;
41982
+
41983
+ // merge trackTitleArray2[0] with trackSeqArray[maxIndex]
41984
+ let A = trackSeqArray[maxIndex], B = trackSeqArray2[0];
41985
+ let i = 0, j = 0;
41986
+
41987
+ let ALen = trackSeqArray.length;
41988
+
41989
+ while (i < A.length && j < B.length) {
41990
+ if(A[i] != B[j]) {
41991
+ if(A[i] == '-') {
41992
+ // inser "-" in B
41993
+ B = B.substr(0, j) + '-' + B.substr(j);
41994
+ seqFirst = seqFirst.substr(0, j) + '-' + seqFirst.substr(j);
41995
+ }
41996
+ else { //if(B[j] == '-') {
41997
+ // inser "-" in A
41998
+ for(let k = 0; k < ALen; ++k) {
41999
+ trackSeqArray[k] = trackSeqArray[k].substr(0, i) + '-' + trackSeqArray[k].substr(i);
42000
+ }
42001
+ }
42002
+ }
42003
+
42004
+ ++i;
42005
+ ++j;
42006
+ }
42007
+
42008
+ await thisClass.showMsaTracks(chainid, seqFirst, trackTitleArray, trackSeqArray, startpos, type, acc2exons);
42009
+
42010
+ me.htmlCls.clickMenuCls.setLogCmd("add exon track | chainid " + chainid + " | geneid " + geneid + " | startpos " + startpos + " | type " + type, true);
42011
+ }
42012
+
42013
+ async addMsaTracks(chainid, startpos, type, fastaList) { let ic = this.icn3d, me = ic.icn3dui;
42014
+ let thisClass = this;
42015
+
42016
+ let seqFirst, trackTitleArray = [], trackSeqArray = [];
42017
+
42018
+ let fastaArray = fastaList.split('>');
42019
+
42020
+ // the first array item is empty
42021
+ // the second array item is the sequence of the structure, start with i = 2
42022
+ let posFirst = fastaArray[1].indexOf('\n');
42023
+ //let titleFirst = fastaArray[1].substr(0, posFirst);
42024
+ seqFirst = fastaArray[1].substr(posFirst + 1).replace(/\n/g, '');
42025
+
42026
+ for(let i = 2, il = fastaArray.length; i < il; ++i) {
42027
+ let pos = fastaArray[i].indexOf('\n');
42028
+ let title = fastaArray[i].substr(0, pos);
42029
+ if(title.indexOf('|') != -1) {
42030
+ title = title.split('|')[1];
42031
+ // if(title.indexOf('.') != -1) {
42032
+ // title = title.split('.')[0];
42033
+ // }
42034
+ }
42035
+ trackTitleArray.push(title);
42036
+ let seq = fastaArray[i].substr(pos + 1).replace(/\n/g, '');
42037
+ trackSeqArray.push(seq);
42038
+ }
42039
+
42040
+ await thisClass.showMsaTracks(chainid, seqFirst, trackTitleArray, trackSeqArray, startpos, type);
42041
+
42042
+ me.htmlCls.clickMenuCls.setLogCmd("add msa track | chainid " + chainid + " | startpos " + startpos + " | type " + type + " | fastaList " + fastaList , true);
42043
+ }
41221
42044
  }
41222
42045
 
41223
42046
  /**
@@ -41868,13 +42691,14 @@ class ShowAnno {
41868
42691
  } // if(me.cfg.mmdbid
41869
42692
  } // for(let i = 0
41870
42693
 
41871
- ic.maxAnnoLength = 1;
42694
+ ic.maxAnnoLengthOri = 1;
41872
42695
  for(let chainid in ic.chainsSeq) {
41873
42696
  // 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;
42697
+ if(ic.chainsSeq[chainid].length > ic.maxAnnoLengthOri && (ic.protein_chainid.hasOwnProperty(chainid) || nucleotide_chainid.hasOwnProperty(chainid)) ) {
42698
+ ic.maxAnnoLengthOri = ic.chainsSeq[chainid].length;
41876
42699
  }
41877
42700
  }
42701
+ ic.maxAnnoLength = ic.maxAnnoLengthOri;
41878
42702
  }
41879
42703
 
41880
42704
  return {'nucleotide_chainid': nucleotide_chainid, 'chemical_chainid': chemical_chainid, 'chemical_set': chemical_set};
@@ -41889,7 +42713,7 @@ class ShowAnno {
41889
42713
  let chemical_chainid = result.chemical_chainid;
41890
42714
  let chemical_set = result.chemical_set;
41891
42715
 
41892
- if(ic.bAnnoShown === undefined || !ic.bAnnoShown || ic.bResetAnno) { // ic.bResetAnno when loading another structure
42716
+ if(!ic.bAnnoShown || ic.bResetAnno) { // ic.bResetAnno when loading another structure
41893
42717
  // assign early to avoid load annotations twice
41894
42718
  ic.bAnnoShown = true;
41895
42719
 
@@ -42030,13 +42854,52 @@ class ShowAnno {
42030
42854
  let thisClass = this;
42031
42855
  let chnidBaseArray = $.map(ic.protein_chainid, function(v) { return v; });
42032
42856
  let index = 0;
42857
+
42858
+ // get geneid
42859
+ if(!ic.chainsGene) ic.chainsGene = {};
42860
+ for(let chnid in ic.protein_chainid) {
42861
+ let structure = chnid.substr(0, chnid.indexOf('_'));
42862
+ // UniProt or NCBI protein accession
42863
+ if(structure.length > 5) {
42864
+ let refseqid, url;
42865
+ if(ic.uniprot2acc && ic.uniprot2acc[structure]) {
42866
+ refseqid = ic.uniprot2acc[structure];
42867
+ }
42868
+ else {
42869
+ try {
42870
+ if(!ic.uniprot2acc) ic.uniprot2acc = {};
42871
+ url = me.htmlCls.baseUrl + "vastdyn/vastdyn.cgi?uniprot2refseq=" + structure;
42872
+ let result = await me.getAjaxPromise(url, 'jsonp');
42873
+ refseqid = (result && result.refseq) ? result.refseq : structure;
42874
+
42875
+ ic.uniprot2acc[structure] = refseqid;
42876
+ }
42877
+ catch {
42878
+ console.log("Problem in getting protein accession from UniProt ID...");
42879
+ refseqid = structure;
42880
+ }
42881
+ }
42882
+
42883
+ // get Gene info from protein name
42884
+ // url = me.htmlCls.baseUrl + "vastdyn/vastdyn.cgi?protein2gene=" + refseqid;
42885
+ // ic.chainsGene[chnid] = await me.getAjaxPromise(url, 'jsonp');
42886
+
42887
+ // get Gene info from uniprot
42888
+ url = "https://rest.uniprot.org/uniprotkb/search?format=json&fields=xref_geneid,gene_names&query=" + structure;
42889
+ let geneData = await me.getAjaxPromise(url, 'json');
42890
+ let geneId = (geneData.results[0] && geneData.results[0].uniProtKBCrossReferences && geneData.results[0].uniProtKBCrossReferences[0]) ? geneData.results[0].uniProtKBCrossReferences[0].id : undefined;
42891
+ 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;
42892
+ ic.chainsGene[chnid] = {geneId: geneId, geneSymbol: geneSymbol};
42893
+ }
42894
+ }
42895
+
42033
42896
  for(let chnid in ic.protein_chainid) {
42034
42897
  let buttonStyle = me.utilsCls.isMobile() ? 'none' : 'button';
42035
42898
  let fullProteinName = ic.showSeqCls.getProteinName(chnid);
42036
42899
  let proteinName = fullProteinName;
42037
42900
  //if(proteinName.length > 40) proteinName = proteinName.substr(0, 40) + "...";
42038
42901
  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>)" : '';
42902
+ 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
42903
  let structure = chnid.substr(0, chnid.indexOf('_'));
42041
42904
  let chainLink = (structure.length > 5) ? '<a href="https://alphafold.ebi.ac.uk/entry/' + structure + '" target="_blank">' + chnid + '</a>' : chnid;
42042
42905
  let chainHtml = "<div id='" + ic.pre + "anno_" + chnid + "' class='icn3d-annotation'>" + categoryStr
@@ -42587,6 +43450,9 @@ class ShowSeq {
42587
43450
  html += htmlTmp + '<span class="icn3d-seqLine">';
42588
43451
  let helixCnt = 0, sheetCnt = 0;
42589
43452
  let savedSsName = '';
43453
+
43454
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqStartLen[chnid], ' ');
43455
+
42590
43456
  for(let i = 0, il = giSeq.length; i < il; ++i) {
42591
43457
  html += this.insertGap(chnid, i, '-');
42592
43458
  let currResi;
@@ -42644,18 +43510,25 @@ class ShowSeq {
42644
43510
  }
42645
43511
  html += '</span>';
42646
43512
  }
43513
+
43514
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqEndLen[chnid], ' ');
43515
+
42647
43516
  html += '<span class="icn3d-residueNum"></span>';
42648
43517
  html += '</span>';
42649
43518
  html += '<br>';
42650
43519
  html += '</div>';
42651
43520
  html3 += '</div>';
42652
43521
  }
43522
+
42653
43523
  // html to display secondary structures
42654
43524
  htmlTmp = '<div class="icn3d-residueLine" style="white-space:nowrap;">';
42655
43525
  htmlTmp += '<div class="icn3d-annoTitle" anno="0"></div>';
42656
43526
  htmlTmp += '<span class="icn3d-residueNum"></span>';
42657
43527
  html3 += htmlTmp + '<br>';
42658
43528
  html += htmlTmp + '<span class="icn3d-seqLine">';
43529
+
43530
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqStartLen[chnid], '-');
43531
+
42659
43532
  for(let i = 0, il = giSeq.length; i < il; ++i) {
42660
43533
  html += this.insertGap(chnid, i, '-');
42661
43534
  // 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 +43576,9 @@ class ShowSeq {
42703
43576
  html += '<span>-</span>'; //'<span>-</span>';
42704
43577
  }
42705
43578
  }
43579
+
43580
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqEndLen[chnid], '-');
43581
+
42706
43582
  html += '<span class="icn3d-residueNum"></span>';
42707
43583
  html += '</span>';
42708
43584
  html += '<br>';
@@ -42734,6 +43610,9 @@ class ShowSeq {
42734
43610
  html += htmlTmp + htmlTmp2;
42735
43611
  html2 += htmlTmp + htmlTmp2;
42736
43612
  let pos, nGap = 0;
43613
+
43614
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqStartLen[chnid], '-');
43615
+
42737
43616
  for(let i = 0, il = giSeq.length; i < il; ++i) {
42738
43617
  html += this.insertGap(chnid, i, '-');
42739
43618
  if(ic.targetGapHash !== undefined && ic.targetGapHash.hasOwnProperty(i)) nGap += ic.targetGapHash[i].to - ic.targetGapHash[i].from + 1;
@@ -42763,6 +43642,9 @@ class ShowSeq {
42763
43642
  html += '<span id="giseq_' + ic.pre + chnid + '_' + pos + '" title="' + cFull + pos + '" class="icn3d-residue" style="color:#' + color + '">' + c + '</span>';
42764
43643
  }
42765
43644
  }
43645
+
43646
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqEndLen[chnid], '-');
43647
+
42766
43648
  if(me.cfg.blast_rep_id == chnid) {
42767
43649
  // change color in 3D
42768
43650
  ic.opts['color'] = (ic.blastAcxn) ? 'confidence' : 'conservation';
@@ -42773,8 +43655,11 @@ class ShowSeq {
42773
43655
  // sequence, overview
42774
43656
  let atom = ic.firstAtomObjCls.getFirstCalphaAtomObj(ic.chains[chnid]);
42775
43657
  let color =(atom.color) ? atom.color.getHexString() : "CCCCCC";
42776
- let width = Math.round(ic.seqAnnWidth * giSeq.length / ic.maxAnnoLength);
43658
+ let width = Math.round(ic.seqAnnWidth * giSeq.length / (ic.maxAnnoLength + ic.nTotalGap));
42777
43659
  if(width < 1) width = 1;
43660
+
43661
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html2 += this.insertMulGapOverview(chnid, ic.seqStartLen[chnid]);
43662
+
42778
43663
  if(me.cfg.blast_rep_id != chnid) { // regular
42779
43664
  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
43665
  }
@@ -42788,7 +43673,9 @@ class ShowSeq {
42788
43673
  }
42789
43674
  }
42790
43675
  toArray2.push(giSeq.length - 1);
43676
+
42791
43677
  html2 += '<div id="giseq_summary_' + ic.pre + chnid + '" class="icn3d-seqTitle icn3d-link" gi chain="' + chnid + '" style="width:' + width + 'px;">';
43678
+
42792
43679
  for(let i = 0, il = fromArray2.length; i < il; ++i) {
42793
43680
  html2 += this.insertGapOverview(chnid, fromArray2[i]);
42794
43681
  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 +43807,9 @@ class ShowSeq {
42920
43807
  htmlTmp += '<span class="icn3d-residueNum"></span>';
42921
43808
  html3 += htmlTmp + '<br>';
42922
43809
  html += htmlTmp + '<span class="icn3d-seqLine">';
43810
+
43811
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqStartLen[chnid], '-');
43812
+
42923
43813
  for(let i = 0, il = giSeq.length; i < il; ++i) {
42924
43814
  html += this.insertGap(chnid, i, '-');
42925
43815
  //if(i >= ic.matchedPos[chnid] && i - ic.matchedPos[chnid] < ic.chainsSeq[chnid].length) {
@@ -42943,6 +43833,9 @@ class ShowSeq {
42943
43833
  // html += '<span></span>';
42944
43834
  // }
42945
43835
  }
43836
+
43837
+ if(ic.seqStartLen && ic.seqStartLen[chnid]) html += this.insertMulGap(ic.seqEndLen[chnid], '-');
43838
+
42946
43839
  html += '<span class="icn3d-residueNum"></span>';
42947
43840
  html += '</span>';
42948
43841
  html += '<br>';
@@ -43590,26 +44483,43 @@ class ShowSeq {
43590
44483
  let html = '';
43591
44484
  //if(me.cfg.blast_rep_id == chnid && ic.targetGapHash!== undefined && ic.targetGapHash.hasOwnProperty(seqIndex)) {
43592
44485
  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
- }
44486
+ html += this.insertMulGap(ic.targetGapHash[seqIndex].to - ic.targetGapHash[seqIndex].from + 1, text, bNohtml);
43601
44487
  }
43602
44488
  return html;
43603
44489
  }
43604
- insertGapOverview(chnid, seqIndex) { let ic = this.icn3d, me = ic.icn3dui;
44490
+
44491
+ insertMulGap(n, text, bNohtml) { let ic = this.icn3d; ic.icn3dui;
44492
+ let html = '';
44493
+ for(let j = 0; j < n; ++j) {
44494
+ if(bNohtml) {
44495
+ html += text;
44496
+ }
44497
+ else {
44498
+ html += '<span>' + text + '</span>';
44499
+ }
44500
+ }
44501
+ return html;
44502
+ }
44503
+
44504
+ insertGapOverview(chnid, seqIndex) { let ic = this.icn3d; ic.icn3dui;
43605
44505
  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>';
44506
+ // if(me.cfg.blast_rep_id == chnid && ic.targetGapHash !== undefined && ic.targetGapHash.hasOwnProperty(seqIndex)) {
44507
+ if(ic.targetGapHash !== undefined && ic.targetGapHash.hasOwnProperty(seqIndex)) {
44508
+ html2 += this.insertMulGapOverview(chnid, ic.targetGapHash[seqIndex].to - ic.targetGapHash[seqIndex].from + 1);
43609
44509
  }
43610
44510
  return html2;
43611
44511
  }
43612
44512
 
44513
+ insertMulGapOverview(chnid, n) { let ic = this.icn3d; ic.icn3dui;
44514
+ let html2 = '';
44515
+ let width = ic.seqAnnWidth * n /(ic.maxAnnoLength + ic.nTotalGap);
44516
+ width = parseInt(width);
44517
+
44518
+ // html2 += '<div style="display:inline-block; background-color:#333; width:' + width + 'px; height:3px;">&nbsp;</div>';
44519
+ html2 += '<div style="display:inline-block; width:' + width + 'px;">&nbsp;</div>';
44520
+ return html2;
44521
+ }
44522
+
43613
44523
  setAlternativeSeq(chnid, chnidBase) { let ic = this.icn3d; ic.icn3dui;
43614
44524
  //if(ic.chainsSeq[chnid] !== undefined) {
43615
44525
  let resArray = ic.chainsSeq[chnid];
@@ -50410,6 +51320,8 @@ class MmdbParser {
50410
51320
 
50411
51321
  if(data && data.uniprot) {
50412
51322
  me.cfg.afid = data.uniprot;
51323
+ if(!ic.uniprot2acc) ic.uniprot2acc = {};
51324
+ ic.uniprot2acc[data.uniprot] = refseqid;
50413
51325
  }
50414
51326
  else {
50415
51327
  alert('The accession ' + refseqid + ' can not be mapped to AlphaFold UniProt ID. It will be treated as a UniProt ID instead.');
@@ -56692,14 +57604,121 @@ class SetSeqAlign {
56692
57604
  result = this.getTemplatePosFromOriPos(chainid1, prevIndex1, end_t, bRealign);
56693
57605
  pos1 = result.pos1;
56694
57606
  pos2 = result.pos2;
56695
- //for(let i = pos1; i < pos2; ++i) {
56696
- for(let i = pos1; i <= pos2; ++i) {
57607
+ for(let i = pos1; i < pos2; ++i) {
57608
+ //for(let i = pos1; i <= pos2; ++i) {
56697
57609
  ic.alnChainsSeq[chainid2].push(gapResObject2);
56698
57610
  }
56699
57611
 
56700
57612
  return hAtoms;
56701
57613
  }
56702
57614
 
57615
+ // used for seq MSA
57616
+ mergeTwoSeqForAllSimple(targetId, chainidArray, index, alignedChainIndice, start_t, end_t, querySeqArray) { let ic = this.icn3d; ic.icn3dui;
57617
+ let chainid1 = targetId;
57618
+ let chainid2 = chainidArray[index];
57619
+
57620
+ let pos1, pos2, prevIndex1, prevIndex2;
57621
+
57622
+ for(let i = 0, il = ic.qt_start_end[index].length; i < il; ++i) {
57623
+ let start1, start2, end1, end2, resiStart1, start1Pos, end1Pos;
57624
+
57625
+ start1 = ic.qt_start_end[index][i].t_start;
57626
+ start2 = ic.qt_start_end[index][i].q_start;
57627
+ end1 = ic.qt_start_end[index][i].t_end;
57628
+ end2 = ic.qt_start_end[index][i].q_end;
57629
+
57630
+ // 1. before the mapped residues
57631
+ //resiStart1 = ic.ParserUtilsCls.getResi(chainid1, start1);
57632
+ resiStart1 = start1;
57633
+ start1Pos = start1;
57634
+ end1Pos = end1;
57635
+
57636
+ // if the mapping does not start from start_t, add gaps to the query seq
57637
+ if(i == 0) {
57638
+ pos1 = start_t;
57639
+ pos2 = start1Pos;
57640
+
57641
+ if(start1Pos > start_t) {
57642
+ for(let j = 0, jl = pos2 - pos1; j < jl; ++j) {
57643
+ ic.msaSeq[chainid2] += '-';
57644
+ }
57645
+ }
57646
+ }
57647
+ else {
57648
+ pos1 = prevIndex1;
57649
+ pos2 = start1;
57650
+ let notAlnLen1 = pos2 - (pos1 + 1);
57651
+ let notAlnLen2 = start2 - (prevIndex2 + 1);
57652
+
57653
+ // insert non-aligned residues in query seq
57654
+ // this.insertNotAlignRes(chainid2, prevIndex2+1, notAlnLen2, bRealign);
57655
+
57656
+ for(let j = 0, jl = notAlnLen2; j < jl; ++j) {
57657
+ let resn = querySeqArray[index][prevIndex2+1 + j];
57658
+ ic.msaSeq[chainid2] += resn;
57659
+ }
57660
+
57661
+ if(notAlnLen1 >= notAlnLen2) {
57662
+ // add gaps before the query sequence
57663
+ for(let j = 0, jl = notAlnLen1 - notAlnLen2; j < jl; ++j) {
57664
+ ic.msaSeq[chainid2] += '-';
57665
+ }
57666
+ }
57667
+ else {
57668
+ // check the number of gaps before resiStart1 (n), and insert 'notAlnLen2 - notAlnLen1 - n' gaps
57669
+ // this.addGapAllAlnChains(chainidArray, alignedChainIndice, chainid1, resiStart1, notAlnLen2 - notAlnLen1);
57670
+
57671
+ // let result = this.getResiPosInTemplate(chainid1, resi_t);
57672
+ // let nGap = result.ngap, pos_t = result.pos;
57673
+
57674
+ let pos_t = resiStart1; // position to add gap
57675
+
57676
+ // add gaps for all previously aligned sequences, not the current sequence, which is the last one
57677
+ for(let j = 0, jl = alignedChainIndice.length - 1; j < jl; ++j) {
57678
+ let chainidTmp = (j == 0) ? chainid1 : chainidArray[alignedChainIndice[j]];
57679
+
57680
+ for(let k = 0, kl = notAlnLen2 - notAlnLen1; k < kl; ++k) {
57681
+ //ic.msaSeq[chainidTmp].splice(pos_t, 0, '-');
57682
+ ic.msaSeq[chainidTmp] = ic.msaSeq[chainidTmp].substr(0, pos_t) + '-' + ic.msaSeq[chainidTmp].substr(pos_t);
57683
+ }
57684
+ }
57685
+ }
57686
+ }
57687
+
57688
+ // 2. In the mapped residues
57689
+ pos1 = start1Pos;
57690
+ pos2 = end1Pos;
57691
+
57692
+ let k = 0;
57693
+ for(let j = pos1; j <= pos2; ++j) {
57694
+ // inherit the gaps from the template
57695
+ if(ic.msaSeq[chainid1][j] == '-') {
57696
+ ic.msaSeq[chainid2] += '-';
57697
+ }
57698
+ else {
57699
+ //let resn1 = targetSeq[start1 + k];
57700
+ let resn2 = querySeqArray[index][start2 + k];
57701
+ //let resn2 = (querySeqArray[index]) ? querySeqArray[index][start2 + k] : '?';
57702
+
57703
+ ic.msaSeq[chainid2] += resn2;
57704
+
57705
+ ++k;
57706
+ }
57707
+ }
57708
+
57709
+ prevIndex1 = end1;
57710
+ prevIndex2 = end2;
57711
+ }
57712
+
57713
+ // add gaps at the end
57714
+ pos1 = prevIndex1;
57715
+ pos2 = end_t;
57716
+ for(let i = pos1; i < pos2; ++i) {
57717
+ //for(let i = pos1; i <= pos2; ++i) {
57718
+ ic.msaSeq[chainid2] += '-';
57719
+ }
57720
+ }
57721
+
56703
57722
  setSeqAlignForRealign(chainid_t, chainid, chainIndex) { let ic = this.icn3d, me = ic.icn3dui;
56704
57723
  //var chainid_t = ic.chainidArray[0];
56705
57724
 
@@ -61277,6 +62296,38 @@ class LoadScript {
61277
62296
 
61278
62297
  await ic.cartoon2dCls.draw2Dcartoon(type);
61279
62298
  }
62299
+ else if(command.indexOf('add msa track') == 0) {
62300
+ //add msa track | chainid " + chainid + " | startpos " + startpos + " | type " + type + " | fastaList " + fastaList
62301
+ let paraArray = command.split(' | ');
62302
+
62303
+ let chainid = paraArray[1].substr(8);
62304
+ let startpos = paraArray[2].substr(9);
62305
+ let type = paraArray[3].substr(5);
62306
+ let fastaList = paraArray[4].substr(10);
62307
+
62308
+ if($("#" + ic.pre + "anno_custom")[0]) {
62309
+ $("#" + ic.pre + "anno_custom")[0].checked = true;
62310
+ }
62311
+ $("[id^=" + ic.pre + "custom]").show();
62312
+
62313
+ await ic.addTrackCls.addMsaTracks(chainid, startpos, type, fastaList);
62314
+ }
62315
+ else if(command.indexOf('add exon track') == 0) {
62316
+ //add exon track | chainid " + chainid + " | geneid " + geneid + " | startpos " + startpos + " | type " + type
62317
+ let paraArray = command.split(' | ');
62318
+
62319
+ let chainid = paraArray[1].substr(8);
62320
+ let geneid = paraArray[2].substr(7);
62321
+ let startpos = paraArray[3].substr(9);
62322
+ let type = paraArray[4].substr(5);
62323
+
62324
+ if($("#" + ic.pre + "anno_custom")[0]) {
62325
+ $("#" + ic.pre + "anno_custom")[0].checked = true;
62326
+ }
62327
+ $("[id^=" + ic.pre + "custom]").show();
62328
+
62329
+ await ic.addTrackCls.addExonTracks(chainid, geneid, startpos, type);
62330
+ }
61280
62331
  else {
61281
62332
  await ic.applyCommandCls.applyCommand(ic.commands[i]);
61282
62333
  }
@@ -61989,12 +63040,18 @@ class SelectByCommand {
61989
63040
  residueStr = "*";
61990
63041
  }
61991
63042
  else if(colonPos2 != -1) {
61992
- refResStr = testStr.substr(colonPos2 + 5);
61993
- testStr = testStr.substr(0, colonPos2);
63043
+ refResStr = testStr.substr(colonPos2 + 5);
63044
+ testStr = testStr.substr(0, colonPos2);
63045
+
63046
+ // somehow sometimes refResStr or residueStr is rmpty
63047
+ if(!refResStr) continue;
61994
63048
  }
61995
63049
  else if(colonPos != -1) {
61996
- residueStr = testStr.substr(colonPos + 1);
61997
- testStr = testStr.substr(0, colonPos);
63050
+ residueStr = testStr.substr(colonPos + 1);
63051
+ testStr = testStr.substr(0, colonPos);
63052
+
63053
+ // somehow sometimes refResStr or residueStr is rmpty
63054
+ if(!residueStr) continue;
61998
63055
  }
61999
63056
 
62000
63057
  if(periodPos === -1) {
@@ -72383,7 +73440,7 @@ class iCn3DUI {
72383
73440
  //even when multiple iCn3D viewers are shown together.
72384
73441
  this.pre = this.cfg.divid + "_";
72385
73442
 
72386
- this.REVISION = '3.27.0';
73443
+ this.REVISION = '3.28.0';
72387
73444
 
72388
73445
  // In nodejs, iCn3D defines "window = {navigator: {}}"
72389
73446
  this.bNode = (Object.keys(window).length < 2) ? true : false;