icn3d 3.54.3 → 3.54.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/icn3d.js CHANGED
@@ -2557,7 +2557,147 @@ var CIFTools;
2557
2557
  }
2558
2558
  return result(file);
2559
2559
  }
2560
+ /**
2561
+ * Small-molecule / crystallographic ("core") CIF support, e.g. files from COD.
2562
+ * Core CIF uses underscore names (_atom_site_label, _atom_site_fract_x) and fractional
2563
+ * coordinates, while iCn3D's loadCIF expects mmCIF names (_atom_site.label_atom_id)
2564
+ * and Cartesian coordinates. Without conversion getCategory("_atom_site") returns undefined.
2565
+ * This converts such a file to a minimal mmCIF text before it is parsed.
2566
+ */
2567
+ function isCoreCif(data) {
2568
+ return /^\s*_atom_site_fract_x\b/m.test(data) && !/^\s*_atom_site\./m.test(data);
2569
+ }
2570
+ // tokenize one CIF text keeping quotes; returns {tags:[...], loops:[{cols, rows}], singles:{}}
2571
+ function coreTokens(data) {
2572
+ var tokens = [], lines = data.split(/\r?\n/), i, line, m, re = /'([^']*)'(?=\s|$)|"([^"]*)"(?=\s|$)|(\S+)/g;
2573
+ for (i = 0; i < lines.length; i++) {
2574
+ line = lines[i];
2575
+ if (line.charAt(0) === ';') { // multi-line text field, skip it
2576
+ i++;
2577
+ while (i < lines.length && lines[i].charAt(0) !== ';') i++;
2578
+ tokens.push({ v: '?', t: 'text' });
2579
+ continue;
2580
+ }
2581
+ re.lastIndex = 0;
2582
+ while ((m = re.exec(line)) !== null) {
2583
+ if (m[3] !== undefined && m[3].charAt(0) === '#') break; // comment
2584
+ tokens.push(m[3] !== undefined ? { v: m[3] } : { v: (m[1] !== undefined ? m[1] : m[2]), t: 'quoted' });
2585
+ }
2586
+ }
2587
+ return tokens;
2588
+ }
2589
+ function coreToNumber(s) {
2590
+ var v = parseFloat(String(s).replace(/\(\d+\)$/, '')); // 0.7556(3) -> 0.7556
2591
+ return isNaN(v) ? 0 : v;
2592
+ }
2593
+ // symmetry operator like "1/2-x,y,1/2+z" -> function(frac) -> frac
2594
+ function coreSymop(str) {
2595
+ var parts = str.replace(/\s+/g, '').toLowerCase().split(','), rows = [], p, k, re = /([+-]?)(?:(\d+(?:\.\d+)?)(?:\/(\d+))?|([xyz]))/g, m, row, c;
2596
+ if (parts.length !== 3) return null;
2597
+ for (p = 0; p < 3; p++) {
2598
+ row = [0, 0, 0, 0]; // x y z const
2599
+ re.lastIndex = 0;
2600
+ while ((m = re.exec(parts[p])) !== null) {
2601
+ c = (m[1] === '-') ? -1 : 1;
2602
+ if (m[4]) { row['xyz'.indexOf(m[4])] += c; }
2603
+ else { row[3] += c * parseFloat(m[2]) / (m[3] ? parseFloat(m[3]) : 1); }
2604
+ }
2605
+ rows.push(row);
2606
+ }
2607
+ return function (f) {
2608
+ var out = [];
2609
+ for (k = 0; k < 3; k++) out.push(rows[k][0] * f[0] + rows[k][1] * f[1] + rows[k][2] * f[2] + rows[k][3]);
2610
+ return out;
2611
+ };
2612
+ }
2613
+ function convertCoreCif(data) {
2614
+ var tk = coreTokens(data), i = 0, n = tk.length, single = {}, loops = [], name = 'data_core', t, cols, rows, cur;
2615
+ function isTag(x) { return x && !x.t && x.v.charAt(0) === '_'; }
2616
+ while (i < n) {
2617
+ t = tk[i];
2618
+ if (!t.t && /^data_/i.test(t.v)) { name = t.v; i++; }
2619
+ else if (!t.t && /^loop_$/i.test(t.v)) {
2620
+ i++; cols = []; rows = [];
2621
+ while (i < n && isTag(tk[i])) cols.push(tk[i++].v.toLowerCase());
2622
+ cur = [];
2623
+ while (i < n && !isTag(tk[i]) && !(/^(loop_|data_)/i.test(tk[i].v) && !tk[i].t)) {
2624
+ cur.push(tk[i++].v);
2625
+ if (cur.length === cols.length) { rows.push(cur); cur = []; }
2626
+ }
2627
+ loops.push({ cols: cols, rows: rows });
2628
+ }
2629
+ else if (isTag(t)) { single[t.v.toLowerCase()] = (tk[i + 1] ? tk[i + 1].v : '?'); i += 2; }
2630
+ else i++;
2631
+ }
2632
+ var atoms = null, symops = [], l, c;
2633
+ for (l = 0; l < loops.length; l++) {
2634
+ if (loops[l].cols.indexOf('_atom_site_fract_x') >= 0) atoms = loops[l];
2635
+ c = loops[l].cols.indexOf('_symmetry_equiv_pos_as_xyz');
2636
+ if (c < 0) c = loops[l].cols.indexOf('_space_group_symop_operation_xyz');
2637
+ if (c >= 0) loops[l].rows.forEach(function (r) { var f = coreSymop(r[c]); if (f) symops.push(f); });
2638
+ }
2639
+ if (!atoms) return null;
2640
+ if (!symops.length) symops.push(coreSymop('x,y,z'));
2641
+
2642
+ // orthogonalization matrix (a along x, c* along z)
2643
+ var a = coreToNumber(single['_cell_length_a']), b = coreToNumber(single['_cell_length_b']), cc = coreToNumber(single['_cell_length_c']);
2644
+ var al = coreToNumber(single['_cell_angle_alpha'] || 90) * Math.PI / 180, be = coreToNumber(single['_cell_angle_beta'] || 90) * Math.PI / 180, ga = coreToNumber(single['_cell_angle_gamma'] || 90) * Math.PI / 180;
2645
+ var cosa = Math.cos(al), cosb = Math.cos(be), cosg = Math.cos(ga), sing = Math.sin(ga);
2646
+ var vol = Math.sqrt(1 - cosa * cosa - cosb * cosb - cosg * cosg + 2 * cosa * cosb * cosg);
2647
+ function toCart(f) {
2648
+ return [a * f[0] + b * cosg * f[1] + cc * cosb * f[2],
2649
+ b * sing * f[1] + cc * (cosa - cosb * cosg) / sing * f[2],
2650
+ cc * vol / sing * f[2]];
2651
+ }
2652
+ var col = {}; atoms.cols.forEach(function (x, k) { col[x] = k; });
2653
+ function get(r, key) { return (col[key] === undefined) ? '?' : r[col[key]]; }
2654
+
2655
+ var out = [name, '#', '_entry.id ' + name.substr(5), '#'];
2656
+ // core CIF "_cell_length_a" -> mmCIF "_cell.length_a" so iCn3D can parse the unit cell
2657
+ Object.keys(single).forEach(function (key) {
2658
+ if (/^_cell_/.test(key)) out.push(key.replace(/^_cell_/, '_cell.') + ' ' + String(single[key]).replace(/\(\d+\)$/, ''));
2659
+ });
2660
+ out.push('#');
2661
+ out.push(
2662
+ 'loop_', '_atom_site.group_PDB', '_atom_site.id', '_atom_site.type_symbol', '_atom_site.label_atom_id', '_atom_site.label_alt_id',
2663
+ '_atom_site.label_comp_id', '_atom_site.label_asym_id', '_atom_site.label_entity_id', '_atom_site.label_seq_id',
2664
+ '_atom_site.Cartn_x', '_atom_site.Cartn_y', '_atom_site.Cartn_z', '_atom_site.occupancy', '_atom_site.B_iso_or_equiv',
2665
+ '_atom_site.auth_seq_id', '_atom_site.auth_asym_id', '_atom_site.pdbx_PDB_model_num');
2666
+ var serial = 0, seen = [], r, s, f, g, k, dup, j, sym, el, lab, cart;
2667
+ for (r = 0; r < atoms.rows.length; r++) {
2668
+ var row = atoms.rows[r];
2669
+ if (get(row, '_atom_site_calc_flag') === 'dum') continue; // dummy atoms
2670
+ f = [coreToNumber(get(row, '_atom_site_fract_x')), coreToNumber(get(row, '_atom_site_fract_y')), coreToNumber(get(row, '_atom_site_fract_z'))];
2671
+ lab = get(row, '_atom_site_label');
2672
+ sym = get(row, '_atom_site_type_symbol'); if (sym === '?') sym = lab;
2673
+ el = (sym.match(/^[A-Za-z]{1,2}/) || ['X'])[0];
2674
+ el = el.charAt(0).toUpperCase() + el.substr(1).toLowerCase();
2675
+ var mySeen = [];
2676
+ for (s = 0; s < symops.length; s++) {
2677
+ g = symops[s](f).map(function (v) { return v - Math.floor(v + 1e-6); });
2678
+ dup = false;
2679
+ for (j = 0; j < mySeen.length; j++) {
2680
+ if (Math.abs(mySeen[j][0] - g[0]) < 1e-3 && Math.abs(mySeen[j][1] - g[1]) < 1e-3 && Math.abs(mySeen[j][2] - g[2]) < 1e-3) { dup = true; break; }
2681
+ }
2682
+ if (dup) continue;
2683
+ mySeen.push(g);
2684
+ cart = toCart(g);
2685
+ out.push(['HETATM', ++serial, el, lab, '.', 'MOL', 'A', 1, '.', cart[0].toFixed(3), cart[1].toFixed(3), cart[2].toFixed(3),
2686
+ coreToNumber(get(row, '_atom_site_occupancy') === '?' ? 1 : get(row, '_atom_site_occupancy')), '0.00', 1, 'A', 1].join(' '));
2687
+ }
2688
+ }
2689
+ out.push('#');
2690
+ return out.join('\n') + '\n';
2691
+ }
2560
2692
  function parse(data) {
2693
+ if (isCoreCif(data)) {
2694
+ var converted = convertCoreCif(data);
2695
+ if (converted) data = converted;
2696
+ }
2697
+ else {
2698
+ // "_cell_angle_alpha" -> "_cell.angle_alpha"
2699
+ data = data.replace(/^(\s*_cell)_(?=\w)/gm, '$1.');
2700
+ }
2561
2701
  return parseInternal(data);
2562
2702
  }
2563
2703
  Text.parse = parse;
@@ -59374,6 +59514,16 @@ class ClickMenu {
59374
59514
  thisClass.setLogCmd('set axis off', true);
59375
59515
  });
59376
59516
 
59517
+ me.myEventCls.onIds("#" + me.pre + "mn6_showucYes", "click", function(e) { let ic = me.icn3d; //e.preventDefault();
59518
+ ic.setOptionCls.setOption('unitcell', 'yes');
59519
+ thisClass.setLogCmd('set unitcell on', true);
59520
+ });
59521
+
59522
+ me.myEventCls.onIds("#" + me.pre + "mn6_showucNo", "click", function(e) { let ic = me.icn3d; //e.preventDefault();
59523
+ ic.setOptionCls.setOption('unitcell', 'no');
59524
+ thisClass.setLogCmd('set unitcell off', true);
59525
+ });
59526
+
59377
59527
  me.myEventCls.onIds("#" + me.pre + "mn6_symmetry", "click", async function(e) { let ic = me.icn3d; //e.preventDefault();
59378
59528
  ic.bAxisOnly = false;
59379
59529
  await ic.symdCls.retrieveSymmetry(Object.keys(ic.structures)[0]);
@@ -60693,6 +60843,15 @@ class SetMenu {
60693
60843
  html += "</ul>";
60694
60844
  html += "</li>";
60695
60845
 
60846
+ // if(me.cfg.mmcifid !== undefined) {
60847
+ html += this.getMenuText('mn2_unitcell', 'Unit Cell', undefined, undefined, 1);
60848
+ html += "<ul>";
60849
+ html += this.getRadio('mn6_showuc', 'mn6_showucYes', 'Show', undefined, undefined, 2);
60850
+ html += this.getRadio('mn6_showuc', 'mn6_showucNo', 'Hide', true, undefined, 2);
60851
+ html += "</ul>";
60852
+ html += "</li>";
60853
+ // }
60854
+
60696
60855
  html += this.getMenuSep();
60697
60856
 
60698
60857
  html += this.getMenuText('mn2_resetwrap', 'Reset', undefined, 1, 1);
@@ -81656,6 +81815,57 @@ class Axes {
81656
81815
 
81657
81816
  return axisPos;
81658
81817
  }
81818
+
81819
+ buildUnitcell() { let ic = this.icn3d, me = ic.icn3dui;
81820
+ if(!ic.cell) {
81821
+ var aaa = 1; //alert("Unit cell information is available for structures from mmCIF files. Please load a structure with 'mmcifid=', 'bcifid=', or from a mmCIF file.");
81822
+ return;
81823
+ }
81824
+
81825
+ let a = ic.cell.length_a;
81826
+ let b = ic.cell.length_b;
81827
+ let c = ic.cell.length_c;
81828
+ let alpha = ic.cell.angle_alpha;
81829
+ let beta = ic.cell.angle_beta;
81830
+ let gamma = ic.cell.angle_gamma;
81831
+
81832
+ const rad = d => d * Math.PI / 180;
81833
+ const ca = Math.cos(rad(alpha));
81834
+ const cb = Math.cos(rad(beta));
81835
+ const cg = Math.cos(rad(gamma));
81836
+ const sg = Math.sin(rad(gamma));
81837
+
81838
+ // Lattice vectors
81839
+ const va = [a, 0, 0];
81840
+ const vb = [b * cg, b * sg, 0];
81841
+ const cx = c * cb;
81842
+ const cy = c * (ca - cb * cg) / sg;
81843
+ const cz = Math.sqrt(c * c - cx * cx - cy * cy);
81844
+ const vc = [cx, cy, cz];
81845
+
81846
+ // 8 corners, indexed by bits (i, j, k) -> i*va + j*vb + k*vc
81847
+ const corner = (i, j, k) => [0, 1, 2].map(n => i * va[n] + j * vb[n] + k * vc[n]);
81848
+
81849
+ let color = me.htmlCls.ORANGE, radius = 0.1;
81850
+ let atomColor = me.parasCls.thr(color);
81851
+
81852
+ for (let i = 0; i < 2; i++) {
81853
+ for (let j = 0; j < 2; j++) {
81854
+ ic.analysisCls.addLine(corner(0, i, j)[0], corner(0, i, j)[1], corner(0, i, j)[2], corner(1, i, j)[0], corner(1, i, j)[1], corner(1, i, j)[2], color, false, 'unitcell', radius); // edges along a
81855
+ ic.analysisCls.addLine(corner(i, 0, j)[0], corner(i, 0, j)[1], corner(i, 0, j)[2], corner(i, 1, j)[0], corner(i, 1, j)[1], corner(i, 1, j)[2], color, false, 'unitcell', radius); // edges along b
81856
+ ic.analysisCls.addLine(corner(i, j, 0)[0], corner(i, j, 0)[1], corner(i, j, 0)[2], corner(i, j, 1)[0], corner(i, j, 1)[1], corner(i, j, 1)[2], color, false, 'unitcell', radius); // edges along c
81857
+ }
81858
+ }
81859
+
81860
+ // add sphere at each corner
81861
+ for (let i = 0; i < 2; i++) {
81862
+ for (let j = 0; j < 2; j++) {
81863
+ for (let k = 0; k < 2; k++) {
81864
+ ic.sphereCls.createSphereBase(new Vector3$1(corner(i, j, k)[0], corner(i, j, k)[1], corner(i, j, k)[2]), atomColor, radius, 1, false, false, 1);
81865
+ }
81866
+ }
81867
+ }
81868
+ }
81659
81869
  }
81660
81870
 
81661
81871
  /**
@@ -85652,7 +85862,7 @@ class ApplyOther {
85652
85862
  ic.applyCenterCls.applyCenterOptions(options);
85653
85863
 
85654
85864
  ic.axesCls.buildAllAxes(undefined, true);
85655
-
85865
+
85656
85866
  switch (options.axis.toLowerCase()) {
85657
85867
  case 'yes':
85658
85868
  ic.axis = true;
@@ -85663,6 +85873,19 @@ class ApplyOther {
85663
85873
  ic.axis = false;
85664
85874
  break;
85665
85875
  }
85876
+
85877
+ ic.lines['unitcell'] = []; // reset
85878
+
85879
+ switch (options.unitcell.toLowerCase()) {
85880
+ case 'yes':
85881
+ ic.unitcell = true;
85882
+ ic.axesCls.buildUnitcell();
85883
+
85884
+ break;
85885
+ case 'no':
85886
+ ic.unitcell = false;
85887
+ break;
85888
+ }
85666
85889
  switch (options.pk.toLowerCase()) {
85667
85890
  case 'atom':
85668
85891
  ic.pk = 1;
@@ -115545,7 +115768,8 @@ class ShowAnno {
115545
115768
  }
115546
115769
 
115547
115770
  let nucleotide_chainid = {}, chemical_chainid = {}, chemical_set = {};
115548
- //ic.protein_chainid = {};
115771
+ // ic.protein_chainid = {};
115772
+
115549
115773
 
115550
115774
  if(ic.bAnnoShown === undefined || !ic.bAnnoShown || ic.bResetAnno) { // ic.bResetAnno when loading another structure
115551
115775
  ic.protein_chainid = {};
@@ -115572,7 +115796,7 @@ class ShowAnno {
115572
115796
  dialogWidth =(me.cfg.notebook) ? me.htmlCls.WIDTH / 2 : $("#" + ic.pre + "dl_selectannotations").dialog( "option", "width" );
115573
115797
  }
115574
115798
  ic.seqAnnWidth = dialogWidth - 120 - 30*2 - 50; // title: 120px, start and end resi: 30px, extra space on the left and right: 50px
115575
-
115799
+
115576
115800
  for(let i = 0, il = chainArray.length; i < il; ++i) {
115577
115801
  if(!ic.chainsSeq[chainArray[i]]) continue; // skip empty chain
115578
115802
 
@@ -115682,8 +115906,8 @@ class ShowAnno {
115682
115906
 
115683
115907
  await ic.mmcifParserCls.downloadMmcifSymmetry(id, 'mmtfid');
115684
115908
  }
115685
-
115686
- await this.showAnnoSeqData(nucleotide_chainid, chemical_chainid, chemical_set);
115909
+
115910
+ await this.showAnnoSeqData(nucleotide_chainid, chemical_chainid, chemical_set);
115687
115911
  }
115688
115912
  }
115689
115913
  else if(me.cfg.blast_rep_id !== undefined && !ic.bSmithwm && !ic.bLocalSmithwm) { // align sequence to structure
@@ -126183,7 +126407,10 @@ class PdbParser {
126183
126407
 
126184
126408
  ic.saveFileCls.showTitle();
126185
126409
 
126186
- if(me.cfg.rotate !== undefined) ic.resizeCanvasCls.rotStruc(me.cfg.rotate, true);
126410
+ // if(me.cfg.rotate !== undefined) {
126411
+ if(me.cfg.rotate !== undefined && !(me.cfg.url && me.cfg.url.indexOf('membranome') != -1)) {
126412
+ ic.resizeCanvasCls.rotStruc(me.cfg.rotate, true);
126413
+ }
126187
126414
 
126188
126415
  if(bAppend && Object.keys(ic.structures).length == 1 && !me.bNode) {
126189
126416
  // show all
@@ -130436,6 +130663,7 @@ class ParserUtils {
130436
130663
  let angle = -0.5 * Math.PI;
130437
130664
  ic.transformCls.setRotation(axis, angle);
130438
130665
  }
130666
+
130439
130667
  //if(Object.keys(ic.structures).length > 1) {
130440
130668
  // $("#" + ic.pre + "alternate").show();
130441
130669
  //}
@@ -134398,6 +134626,16 @@ class LoadCIF {
134398
134626
  = m24Array = m31Array = m32Array = m33Array = m34Array = [];
134399
134627
  }
134400
134628
 
134629
+ if(block.getCategory("_cell")) {
134630
+ ic.cell = {};
134631
+ ic.cell.length_a = parseFloat(block.getCategory("_cell").getColumn("length_a").getString(0));
134632
+ ic.cell.length_b = parseFloat(block.getCategory("_cell").getColumn("length_b").getString(0));
134633
+ ic.cell.length_c = parseFloat(block.getCategory("_cell").getColumn("length_c").getString(0));
134634
+ ic.cell.angle_alpha = parseFloat(block.getCategory("_cell").getColumn("angle_alpha").getString(0));
134635
+ ic.cell.angle_beta = parseFloat(block.getCategory("_cell").getColumn("angle_beta").getString(0));
134636
+ ic.cell.angle_gamma = parseFloat(block.getCategory("_cell").getColumn("angle_gamma").getString(0));
134637
+ }
134638
+
134401
134639
  // if (record === 'ENDMDL') {
134402
134640
  // ++moleculeNum;
134403
134641
  // id = ic.defaultPdbId;
@@ -134510,6 +134748,7 @@ class LoadCIF {
134510
134748
  let sChain = {};
134511
134749
  let prevModelNum = '';
134512
134750
  if(!ic.molid2chain) ic.molid2chain = {};
134751
+
134513
134752
  for (let i = 0; i < atomSize; ++i) {
134514
134753
  let modelNum = modelNumArray.getString(i);
134515
134754
  if(i > 0 && modelNum != prevModelNum) {
@@ -134740,7 +134979,7 @@ class LoadCIF {
134740
134979
  // if(oriResidueNum !== prevOriResidueNum) {
134741
134980
  if(oriResidueNum !== prevOriResidueNum || chain + "_" + resn != prevResn || prevAutochain != autochain) {
134742
134981
  let residue = me.utilsCls.residueName2Abbr(resn);
134743
-
134982
+
134744
134983
  ic.residueId2Name[residueNum] = residue;
134745
134984
 
134746
134985
  if(serial !== 1 && prevResidueNum !== '') {
@@ -134865,7 +135104,7 @@ class LoadCIF {
134865
135104
 
134866
135105
  resiArray = oriResiArray = resnArray = chainArray = [];
134867
135106
  }
134868
-
135107
+
134869
135108
  this.setSeq(structure, sChain, mChainSeq, ligSeqHash);
134870
135109
  }
134871
135110
 
@@ -135067,17 +135306,22 @@ class LoadCIF {
135067
135306
  }
135068
135307
 
135069
135308
  setSeq(structure, sChain, mChainSeq, ligSeqHash) { let ic = this.icn3d; ic.icn3dui;
135309
+ // sometimes a chain appears in both ligSeqHash and mChainSeq, e.g., mmcif data for 1TOP
135310
+ /*
135070
135311
  for(let chain in sChain) {
135071
135312
  let chainNum = structure + '_' + chain;
135072
135313
 
135073
- if(ligSeqHash.hasOwnProperty(chain)) {
135314
+ if(mChainSeq.hasOwnProperty(chain)) {
135315
+ ic.chainsSeq[chainNum] = mChainSeq[chain];
135316
+ }
135317
+ else if(ligSeqHash.hasOwnProperty(chain)) {
135074
135318
  ic.chainsSeq[chainNum] = ligSeqHash[chain];
135075
135319
  }
135076
135320
  else {
135077
135321
  ic.chainsSeq[chainNum] = mChainSeq[chain];
135078
135322
  }
135079
135323
  }
135080
-
135324
+ */
135081
135325
  ic.loadPDBCls.setResidMapping();
135082
135326
  }
135083
135327
  }
@@ -136016,6 +136260,14 @@ class ApplyCommand {
136016
136260
  ic.opts['axis'] = 'no';
136017
136261
  ic.pc1 = false;
136018
136262
  }
136263
+ else if(command == 'set unitcell on') {
136264
+ ic.opts['unitcell'] = 'yes';
136265
+ ic.drawCls.draw();
136266
+ }
136267
+ else if(command == 'set unitcell off') {
136268
+ ic.opts['unitcell'] = 'no';
136269
+ ic.drawCls.draw();
136270
+ }
136019
136271
  else if(command == 'set fog on') {
136020
136272
  ic.opts['fog'] = 'yes';
136021
136273
  ic.fogCls.setFog(true);
@@ -146498,9 +146750,10 @@ class Diagram2d {
146498
146750
  // get the residues in the selection
146499
146751
  let ncbiresid = data;
146500
146752
  let resid = ic.ncbi2resid[ncbiresid];
146501
- let resi = resid.substr(resid.lastIndexOf('_') + 1);
146502
146753
 
146503
- if(ic.ig2ddgm_chainid && ic.resid2refnum && ic.resid2refnum[resid]) {
146754
+ if(resid && ic.ig2ddgm_chainid && ic.resid2refnum && ic.resid2refnum[resid]) {
146755
+ let resi = resid.substr(resid.lastIndexOf('_') + 1);
146756
+
146504
146757
  let refnumLabel = ic.resid2refnum[resid];
146505
146758
  let refnumStr = ic.refnumCls.rmStrandFromRefnumlabel(refnumLabel);
146506
146759
 
@@ -150273,7 +150526,8 @@ class SaveFile {
150273
150526
  idName = "PDB ID";
150274
150527
  }
150275
150528
  else if(bAlphaFold) {
150276
- idName = "AlphaFold ID";
150529
+ // idName = "AlphaFold ID";
150530
+ idName = "ID";
150277
150531
  }
150278
150532
 
150279
150533
  if(structureidArray.length > 1) {
@@ -153194,6 +153448,7 @@ class iCn3D {
153194
153448
  this.radiusArraySphere = new Array();
153195
153449
 
153196
153450
  this.axis = false; // used to turn on and off xyz axes
153451
+ this.unitcell = false; // used to turn on and off unit cell
153197
153452
 
153198
153453
  // pk
153199
153454
  //If its value is 1, selecting an atom will select the atom. If its value is 2, selecting an atom
@@ -153315,6 +153570,7 @@ class iCn3D {
153315
153570
  this.optsOri['clbonds'] = 'yes'; //yes, no
153316
153571
  this.optsOri['rotationcenter'] = 'molecule center'; //molecule center, pick center, display center
153317
153572
  this.optsOri['axis'] = 'no'; //yes, no
153573
+ this.optsOri['unitcell'] = 'no'; //yes, no
153318
153574
  this.optsOri['fog'] = 'no'; //yes, no
153319
153575
  this.optsOri['slab'] = 'no'; //yes, no
153320
153576
  this.optsOri['pk'] = 'residue'; //no, atom, residue, strand, chain
@@ -153732,7 +153988,7 @@ class iCn3DUI {
153732
153988
  //even when multiple iCn3D viewers are shown together.
153733
153989
  this.pre = this.cfg.divid + "_";
153734
153990
 
153735
- this.REVISION = '3.54.1';
153991
+ this.REVISION = '3.54.2';
153736
153992
 
153737
153993
  // In nodejs, iCn3D defines "window = {navigator: {}}", and added window = {navigator: {}, "__THREE__":"177"}
153738
153994
  this.bNode = (Object.keys(window).length < 3) ? true : false;