@datagrok/helm 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/package-test.js +1 -1
  3. package/dist/package-test.js.map +1 -1
  4. package/dist/package.js +1 -1
  5. package/dist/package.js.map +1 -1
  6. package/package.json +15 -16
  7. package/src/cell-renderer.ts +13 -36
  8. package/src/constants.ts +0 -11
  9. package/src/helm-helper.ts +67 -17
  10. package/src/helm-monomer-placer.ts +8 -26
  11. package/src/helm-web-editor.ts +33 -3
  12. package/src/package-test.ts +12 -4
  13. package/src/package-utils.ts +160 -0
  14. package/src/package.ts +123 -197
  15. package/src/tests/findMonomers-tests.ts +2 -0
  16. package/src/tests/get-all-parts-tests.ts +5 -0
  17. package/src/tests/{get-molfiles.ts → get-molfiles-tests.ts} +3 -1
  18. package/src/tests/get-monomer-tests.ts +279 -0
  19. package/src/tests/helm-service-tests.ts +4 -0
  20. package/src/tests/helm-tests.ts +8 -1
  21. package/src/tests/helm-web-editor-tests.ts +55 -0
  22. package/src/tests/parse-helm-tests.ts +98 -0
  23. package/src/tests/properties-widget-tests.ts +33 -4
  24. package/src/tests/renderers-tests.ts +3 -1
  25. package/src/tests/utils.ts +6 -0
  26. package/src/types/index.ts +5 -0
  27. package/src/utils/get-hovered.ts +9 -21
  28. package/src/utils/get-monomer-dummy.ts +149 -0
  29. package/src/utils/get-monomer-of-library.ts +90 -0
  30. package/src/utils/get-monomer.ts +113 -0
  31. package/src/utils/helm-grid-cell-renderer.ts +12 -7
  32. package/src/utils/helm-service.ts +19 -45
  33. package/src/utils/index.ts +11 -17
  34. package/src/widgets/properties-widget.ts +29 -9
  35. package/tsconfig.json +1 -1
  36. package/vendor/helm-web-editor.development.js +39640 -0
  37. package/vendor/helm-web-editor.production.js +2 -0
  38. package/vendor/helm-web-editor.production.js.map +1 -0
  39. package/webpack.config.js +14 -11
  40. package/helm/JSDraw/Pistoia.HELM-uncompressed.js +0 -9694
  41. package/helm/JSDraw/Pistoia.HELM.js +0 -27
  42. package/helm/JSDraw/ReadMe.txt +0 -8
  43. package/helm/JSDraw/Scilligence.JSDraw2.Lite-uncompressed.js +0 -31126
  44. package/helm/JSDraw/Scilligence.JSDraw2.Lite.js +0 -12
  45. package/helm/JSDraw/Scilligence.JSDraw2.Resources.js +0 -762
  46. package/helm/JSDraw/dojo.js +0 -250
  47. package/helm/JSDraw/test.html +0 -24
  48. package/src/utils/dummy-monomer.ts +0 -69
@@ -2,15 +2,17 @@ import * as ui from 'datagrok-api/ui';
2
2
  import * as DG from 'datagrok-api/dg';
3
3
  import * as grok from 'datagrok-api/grok';
4
4
 
5
- import {after, before, category, expect, expectObject, test} from '@datagrok-libraries/utils/src/test';
5
+ import {after, before, category, expect, expectObject, test, timeout} from '@datagrok-libraries/utils/src/test';
6
6
  import {ALPHABET, NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
7
7
  import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
8
8
  import {
9
9
  getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
10
10
  } from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
11
11
  import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
12
+ import {generateLongSequence} from '@datagrok-libraries/bio/src/utils/generator';
12
13
 
13
- import {getPropertiesDict} from '../widgets/properties-widget';
14
+ import {initHelmMainPackage} from './utils';
15
+ import {getPropertiesDict, SeqPropertiesError} from '../widgets/properties-widget';
14
16
 
15
17
  const enum MolProps {
16
18
  formula = 'formula',
@@ -25,6 +27,7 @@ const enum Tests {
25
27
  separatorGaps = 'separator-gaps',
26
28
  fastaPt = 'fasta-PT',
27
29
  fastaPtMsa = 'fasta-PT-MSA',
30
+ fastaRna = 'fasta-RNA',
28
31
  fastaDna = 'fasta-DNA',
29
32
  }
30
33
 
@@ -56,6 +59,11 @@ const TestsData: {
56
59
  units: NOTATION.FASTA, alphabet: ALPHABET.PT,
57
60
  res: {[MolProps.formula]: 'C38H58N10O12S', [MolProps.mw]: 878.99, [MolProps.extinctCoef]: 1.55}
58
61
  },
62
+ [Tests.fastaRna]: {
63
+ seq: 'ACGUAUUCC',
64
+ units: NOTATION.FASTA, alphabet: ALPHABET.RNA,
65
+ res: {[MolProps.formula]: 'C84H107N30O65P9', [MolProps.mw]: 2855.67, [MolProps.extinctCoef]: 96.27}
66
+ },
59
67
  [Tests.fastaDna]: {
60
68
  seq: 'ACGTATTCC',
61
69
  units: NOTATION.FASTA, alphabet: ALPHABET.DNA,
@@ -69,9 +77,14 @@ category('properties-widget', () => {
69
77
  let userLibSettings: any = null;
70
78
 
71
79
  before(async () => {
80
+ await initHelmMainPackage();
81
+
72
82
  monomerLibHelper = await getMonomerLibHelper();
73
83
  userLibSettings = getUserLibSettings();
84
+
74
85
  await setUserLibSettingsForTests();
86
+ await timeout(async () => { await monomerLibHelper.awaitLoaded(); },
87
+ 5000, 'MonomerLibHelper.awaitLoaded() timeout');
75
88
  await monomerLibHelper.loadLibraries(true); // load default libraries
76
89
  });
77
90
 
@@ -85,6 +98,22 @@ category('properties-widget', () => {
85
98
  testPropertiesDict(testData.seq, testData.units, testData.separator, testData.alphabet, testData.res);
86
99
  });
87
100
  }
101
+
102
+ test('too-long', async () => {
103
+ const colList = generateLongSequence();
104
+ const df = DG.DataFrame.fromColumns(colList);
105
+ const col = df.getCol('MSA');
106
+
107
+ const sh = SeqHandler.forColumn(col);
108
+ let errCatched = true;
109
+ try {
110
+ const _actPropDict = getPropertiesDict(col.get(0), sh);
111
+ const k = 11;
112
+ } catch (err: any) {
113
+ if (err instanceof SeqPropertiesError) errCatched = true;
114
+ }
115
+ expect(errCatched, true);
116
+ });
88
117
  });
89
118
 
90
119
  function testPropertiesDict(
@@ -95,7 +124,7 @@ function testPropertiesDict(
95
124
  col.setTag(DG.TAGS.UNITS, units);
96
125
  if (separator) col.setTag(bioTAGS.separator, separator);
97
126
  if (alphabet) col.setTag(bioTAGS.alphabet, alphabet);
98
- const uh = SeqHandler.forColumn(col);
99
- const actPropDict = getPropertiesDict(seq, uh);
127
+ const sh = SeqHandler.forColumn(col);
128
+ const actPropDict = getPropertiesDict(seq, sh);
100
129
  expectObject(actPropDict, expPropDict);
101
130
  }
@@ -15,7 +15,7 @@ import {
15
15
  getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
16
16
  } from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
17
17
 
18
- import {awaitGrid} from './utils';
18
+ import {awaitGrid, initHelmMainPackage} from './utils';
19
19
  import {TAGS as helmTAGS} from '../constants';
20
20
 
21
21
  import {_package} from '../package-test';
@@ -26,6 +26,8 @@ category('renderers', () => {
26
26
  let userLibSettings: UserLibSettings;
27
27
 
28
28
  before(async () => {
29
+ await initHelmMainPackage();
30
+
29
31
  monomerLibHelper = await getMonomerLibHelper();
30
32
  userLibSettings = await getUserLibSettings();
31
33
 
@@ -3,6 +3,12 @@ import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import {delay, testEvent} from '@datagrok-libraries/utils/src/test';
6
+ import {getHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
7
+
8
+
9
+ export async function initHelmMainPackage(): Promise<void> {
10
+ await getHelmHelper();
11
+ }
6
12
 
7
13
  export async function awaitGrid(grid: DG.Grid, timeout: number = 5000): Promise<void> {
8
14
  await delay(100);
@@ -0,0 +1,5 @@
1
+ import {HelmType, JSDraw2ModuleType, OrgType, ScilModuleType} from '@datagrok-libraries/bio/src/helm/types';
2
+
3
+ export type ScilModule = ScilModuleType;
4
+ export type JSDraw2HelmModule = JSDraw2ModuleType<HelmType>;
5
+ export type OrgHelmModule = OrgType;
@@ -2,15 +2,14 @@ import * as grok from 'datagrok-api/grok';
2
2
  import * as DG from 'datagrok-api/dg';
3
3
  import * as ui from 'datagrok-api/ui';
4
4
 
5
- import * as org from 'org';
6
- import * as JSDraw2 from 'JSDraw2';
5
+ import {HelmType, PolymerType, Atom, Mol} from '@datagrok-libraries/bio/src/helm/types';
7
6
 
8
- import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
7
+ import {helmTypeToPolymerType} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
9
8
 
10
9
  import {HelmMonomerPlacer, ISeqMonomer} from '../helm-monomer-placer';
11
10
 
12
11
  export function getHoveredMonomerFromEditorMol(
13
- argsX: number, argsY: number, gridCell: DG.GridCell, mol: JSDraw2.IEditorMol
12
+ argsX: number, argsY: number, gridCell: DG.GridCell, mol: Mol<HelmType>
14
13
  ): ISeqMonomer | null {
15
14
  let hoveredSeqMonomer: ISeqMonomer | null = null;
16
15
 
@@ -80,32 +79,21 @@ export function getHoveredMonomerFallback(
80
79
  }
81
80
  left = (argsX >= sumLengths[left]) ? left : left - 1; // correct left to between sumLengths
82
81
  if (left >= 0)
83
- hoveredSeqMonomer = getSeqMonomerFromHelm(allParts[0], allParts[left], helmPlacer.monomerLib);
82
+ hoveredSeqMonomer = getSeqMonomerFromHelm(allParts[0], allParts[left]);
84
83
  return hoveredSeqMonomer;
85
84
  }
86
85
 
87
- function getSeqMonomerFromHelmAtom(atom: JSDraw2.IEditorMolAtom): ISeqMonomer {
88
- let polymerType: string | undefined = undefined;
89
- switch (atom.bio.type) {
90
- case 'HELM_BASE':
91
- case 'HELM_SUGAR': // r - ribose, d - deoxyribose
92
- case 'HELM_LINKER': // p - phosphate
93
- polymerType = 'RNA';
94
- break;
95
- case 'HELM_AA':
96
- polymerType = 'PEPTIDE';
97
- break;
98
- default:
99
- polymerType = 'PEPTIDE';
100
- }
86
+ function getSeqMonomerFromHelmAtom(atom: Atom<HelmType>): ISeqMonomer {
87
+ const polymerType = helmTypeToPolymerType(atom.bio.type);
101
88
  return {symbol: atom.elem, polymerType: polymerType};
102
89
  }
103
90
 
104
91
  function getSeqMonomerFromHelm(
105
- helmPrefix: string, symbol: string, monomerLib: IMonomerLib
92
+ helmPrefix: string, symbol: string
106
93
  ): ISeqMonomer {
107
94
  let resSeqMonomer: ISeqMonomer | undefined = undefined;
108
- for (const polymerType of monomerLib.getPolymerTypes()) {
95
+ const polymerTypeList: PolymerType[] = ['RNA', 'PEPTIDE', 'CHEM', 'BLOB', 'G'];
96
+ for (const polymerType of polymerTypeList) {
109
97
  if (helmPrefix.startsWith(polymerType))
110
98
  resSeqMonomer = {symbol: symbol, polymerType: polymerType};
111
99
  }
@@ -0,0 +1,149 @@
1
+ import * as grok from 'datagrok-api/grok';
2
+ import * as ui from 'datagrok-api/ui';
3
+ import * as DG from 'datagrok-api/dg';
4
+
5
+ import {
6
+ HelmType, IWebEditorMonomer, MonomerType, PolymerType, WebEditorRGroups
7
+ } from '@datagrok-libraries/bio/src/helm/types';
8
+
9
+ import {Monomer} from '@datagrok-libraries/bio/src/types/index';
10
+ import {
11
+ HELM_REQUIRED_FIELD as REQ, HELM_OPTIONAL_FIELDS as OPT, HELM_RGROUP_FIELDS as RGP,
12
+ } from '@datagrok-libraries/bio/src/utils/const';
13
+
14
+ export function getRS(smiles: string) {
15
+ const newS = smiles.match(/(?<=\[)[^\][]*(?=])/gm);
16
+ const res: { [name: string]: string } = {};
17
+ let el = '';
18
+ let digit;
19
+ if (!!newS) {
20
+ for (let i = 0; i < newS.length; i++) {
21
+ if (newS[i] != null) {
22
+ if (/\d/.test(newS[i])) {
23
+ digit = newS[i][newS[i].length - 1];
24
+ newS[i] = newS[i].replace(/[0-9]/g, '');
25
+ for (let j = 0; j < newS[i].length; j++) {
26
+ if (newS[i][j] != ':')
27
+ el += newS[i][j];
28
+ }
29
+ res['R' + digit] = el;
30
+ el = '';
31
+ }
32
+ }
33
+ }
34
+ }
35
+ return res;
36
+ }
37
+
38
+ /* eslint-disable camelcase */
39
+ export abstract class DummyWebEditorMonomer implements IWebEditorMonomer {
40
+ //@formatter:off
41
+ public abstract get backgroundcolor(): string | undefined;
42
+ public abstract get linecolor(): string | undefined;
43
+ public abstract get textcolor(): string | undefined;
44
+ //@formatter:on
45
+
46
+ get issmiles(): boolean { return !!this.smiles; }
47
+
48
+ /** R-Group index os single digit only is allowed in Pistoia code */
49
+ public readonly at: WebEditorRGroups = {
50
+ R1: 'H', R2: 'H', R3: 'H', R4: 'H', R5: 'H', R6: 'H', R7: 'H', R8: 'H', R9: 'H'
51
+ };
52
+
53
+ public get rs(): number { return Object.keys(this.at).length; }
54
+
55
+ protected constructor(
56
+ public readonly biotype: string,
57
+ /** symbol */ public readonly id: string,
58
+ /** name */ public readonly n: string | undefined = 'missing',
59
+ /** molfile */ public readonly m: string | undefined = undefined,
60
+ /* Pistoia.HELM deletes .type and .mt in Monomers.addOneMonomer() */
61
+ /** polymer type */ public readonly type: PolymerType | undefined = undefined,
62
+ /** monomer type */ public readonly mt: MonomerType | undefined = undefined,
63
+ public readonly smiles?: string,
64
+ ) {
65
+ if (!this.id)
66
+ throw new Error('Invalid arg undefined [id].');
67
+
68
+ // return new Proxy(this, {
69
+ // get: (target: any, prop: string | symbol, _receiver: any) => {
70
+ // const logPrefix = `${this.toLog()}.proxy.get( '${String(prop)}' )`;
71
+ // switch (prop) {
72
+ // case 'id':
73
+ // case 'at':
74
+ // case 'na':
75
+ // case 'nature':
76
+ // case 'backgroundcolor':
77
+ // case 'linecolor':
78
+ // case 'textcolor': {
79
+ // // these attributes are known and handled
80
+ // return target[prop];
81
+ // }
82
+ // case 'rs': {
83
+ // /* R-Group count (?), 2 is default for monomer "?" */
84
+ // throw new Error('Not supported get "rs" attribute.');
85
+ // }
86
+ // default:
87
+ // // Unexpected attribute requested
88
+ // _package.logger.warning(`${logPrefix}`);
89
+ // return target[prop];
90
+ // }
91
+ // },
92
+ // set: (target, prop, value: any, _receiver: any): boolean => {
93
+ // throw new Error(`Not supported set '${String(prop)}' attribute, any.`);
94
+ // },
95
+ // apply: (target: any, thisArg: any, argArray: any[]): any | undefined => {
96
+ // throw new Error(`Not supported call (apply) '${thisArg.toString()}' method, any.`);
97
+ // }
98
+ // });
99
+ }
100
+
101
+ private static objCounter: number = -1;
102
+ private readonly objId: number = ++DummyWebEditorMonomer.objCounter;
103
+
104
+ private readonly className: string = 'DummyWebEditorMonomer';
105
+
106
+ protected toLog(): string {
107
+ return `Helm: ${this.className}<${this.objId}>`;
108
+ }
109
+ }
110
+
111
+ export class GapWebEditorMonomer extends DummyWebEditorMonomer {
112
+ public readonly backgroundcolor: string = '#FFFFFF';
113
+ public readonly linecolor: string = '#808080';
114
+ public readonly textcolor: string = '#808080';
115
+
116
+ constructor(biotype: string, id: string) {
117
+ super(biotype, id, 'gap');
118
+ }
119
+ }
120
+
121
+ export class AmbiguousWebEditorMonomer extends DummyWebEditorMonomer {
122
+ public readonly backgroundcolor: string = '#808080';
123
+ public readonly linecolor: string = '#000000';
124
+ public readonly textcolor: string = '#000000';
125
+
126
+ constructor(biotype: string, id: string) {
127
+ super(biotype, id, 'ambiguous');
128
+ }
129
+ }
130
+
131
+ export class MissingWebEditorMonomer extends DummyWebEditorMonomer {
132
+ public readonly backgroundcolor: string = '#FF4444';
133
+ public readonly linecolor: string = '#800000';
134
+ public readonly textcolor: string = '#FFFFFF';
135
+
136
+ constructor(biotype: string, id: string) {
137
+ super(biotype, id, 'missing');
138
+ }
139
+ }
140
+
141
+ export class BrokenWebEditorMonomer extends DummyWebEditorMonomer {
142
+ public readonly backgroundcolor: string = '#FFFF44';
143
+ public readonly linecolor: string = '#800000';
144
+ public readonly textcolor: string = '#000000';
145
+
146
+ constructor(biotype: string, id: string) {
147
+ super(biotype, id, 'broken');
148
+ }
149
+ }
@@ -0,0 +1,90 @@
1
+ import * as grok from 'datagrok-api/grok';
2
+ import * as ui from 'datagrok-api/ui';
3
+ import * as DG from 'datagrok-api/dg';
4
+
5
+ import {
6
+ HelmType, IWebEditorMonomer, IMonomerColors, MonomerType, PolymerType, WebEditorRGroups
7
+ } from '@datagrok-libraries/bio/src/helm/types';
8
+ import {Monomer} from '@datagrok-libraries/bio/src/types';
9
+ import {
10
+ HELM_OPTIONAL_FIELDS as OPT, HELM_REQUIRED_FIELD as REQ, HELM_RGROUP_FIELDS as RGP
11
+ } from '@datagrok-libraries/bio/src/utils/const';
12
+ import {BrokenWebEditorMonomer, getRS, MissingWebEditorMonomer} from './get-monomer-dummy';
13
+
14
+ function getMonomerColors(monomer: Monomer): IMonomerColors | null {
15
+ const currentMonomerSchema = 'default';
16
+ let monomerSchema: string = currentMonomerSchema;
17
+ if (!monomer.meta || !monomer.meta.colors) return null;
18
+
19
+ const monomerColors: { [colorSchemaName: string]: any } = monomer.meta.colors;
20
+
21
+ if (!(currentMonomerSchema in monomerColors)) monomerSchema = 'default';
22
+
23
+ const res = monomerColors[monomerSchema];
24
+
25
+ return !res ? null : {
26
+ textcolor: res.text ?? res.textColor,
27
+ linecolor: res.line ?? res.lineColor,
28
+ backgroundcolor: res.background ?? res.backgroundColor
29
+ } as IMonomerColors;
30
+ }
31
+
32
+ export class LibraryWebEditorMonomer implements IWebEditorMonomer {
33
+ public get rs(): number { return Object.keys(this.at).length; }
34
+
35
+ public get issmiles(): boolean { return !!this.smiles; }
36
+
37
+ public linecolor?: string;
38
+ public backgroundcolor?: string;
39
+ public textcolor?: string;
40
+
41
+ /* eslint-disable max-params */
42
+ protected constructor(
43
+ public readonly id: string,
44
+ public readonly m: string,
45
+ public readonly n: string,
46
+ public readonly na: string | undefined,
47
+ public readonly type: PolymerType,
48
+ public readonly mt: MonomerType,
49
+ public readonly at: WebEditorRGroups,
50
+ public readonly smiles?: string,
51
+ ) /* eslint-enable max-params */ {}
52
+
53
+ static fromMonomer(biotype: HelmType, monomer: Monomer): IWebEditorMonomer {
54
+ let at: WebEditorRGroups = {};
55
+ const symbol = monomer[REQ.SYMBOL];
56
+ const smiles = monomer[REQ.SMILES];
57
+ if (monomer.rgroups.length > 0) {
58
+ monomer.rgroups.forEach((it) => {
59
+ at[it[RGP.LABEL]] = it[RGP.CAP_GROUP_NAME];
60
+ });
61
+ } else if (smiles) {
62
+ // Generate R-Groups from SMILES
63
+ at = getRS(smiles);
64
+ } else if (!monomer.lib) {
65
+ // missing
66
+ return new MissingWebEditorMonomer(biotype, symbol);
67
+ } else {
68
+ // broken
69
+ return new BrokenWebEditorMonomer(biotype, symbol);
70
+ }
71
+
72
+ const res = new LibraryWebEditorMonomer(
73
+ monomer[REQ.SYMBOL],
74
+ monomer[REQ.MOLFILE],
75
+ monomer[REQ.NAME],
76
+ monomer[OPT.NATURAL_ANALOG],
77
+ monomer[REQ.POLYMER_TYPE],
78
+ monomer[REQ.MONOMER_TYPE],
79
+ at);
80
+
81
+ const colors = getMonomerColors(monomer);
82
+ if (colors) {
83
+ res.textcolor = colors?.textcolor;
84
+ res.linecolor = colors?.linecolor;
85
+ res.backgroundcolor = colors?.backgroundcolor;
86
+ }
87
+
88
+ return res;
89
+ }
90
+ }
@@ -0,0 +1,113 @@
1
+ import * as grok from 'datagrok-api/grok';
2
+ import * as ui from 'datagrok-api/ui';
3
+ import * as DG from 'datagrok-api/dg';
4
+
5
+ import {ILogger} from '@datagrok-libraries/bio/src/utils/logger';
6
+ import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types';
7
+ import {Atom, HelmType, IWebEditorMonomer, GetMonomerResType} from '@datagrok-libraries/bio/src/helm/types';
8
+ import {helmTypeToPolymerType} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
9
+ import {
10
+ HELM_REQUIRED_FIELD as REQ
11
+ } from '@datagrok-libraries/bio/src/utils/const';
12
+ import {HelmTypes} from '@datagrok-libraries/bio/src/helm/consts';
13
+
14
+ import {AmbiguousWebEditorMonomer, GapWebEditorMonomer, MissingWebEditorMonomer} from './get-monomer-dummy';
15
+ import {LibraryWebEditorMonomer} from './get-monomer-of-library';
16
+ import {OrgHelmModule, ScilModule} from '../types';
17
+
18
+ import {_package} from '../package';
19
+
20
+ declare const org: OrgHelmModule;
21
+ declare const scil: ScilModule;
22
+
23
+ const monomerRe = /[\w()]+/;
24
+ //** Do not mess with monomer symbol with parenthesis enclosed in square brackets */
25
+ const ambMonomerRe = RegExp(String.raw`\(${monomerRe}(,${monomerRe})+\)`);
26
+
27
+ type GetMonomerOverridingFunc = (
28
+ a: Atom<HelmType> | HelmType, name: string | undefined, monomerLib: IMonomerLib,
29
+ originalGetMonomer: (a: Atom<HelmType> | HelmType, name: string) => GetMonomerResType) => GetMonomerResType;
30
+
31
+ export function getMonomerOverrideAndLogAlert(
32
+ monomerLib: IMonomerLib, getMonomerOverriding: GetMonomerOverridingFunc, trigger: () => void, logger: ILogger,
33
+ ): void {
34
+ const logPrefix = 'Helm: overrideGetMonomerAndAlert()';
35
+ const monomers = org.helm.webeditor.Monomers;
36
+ const getMonomerOriginal = monomers.getMonomer.bind(monomers);
37
+ const alertOriginal = scil.Utils.alert;
38
+ try {
39
+ org.helm.webeditor.Monomers.getMonomer =
40
+ (a: Atom<HelmType> | HelmType, name?: string): GetMonomerResType => {
41
+ return getMonomerOverriding(a, name, monomerLib, getMonomerOriginal);
42
+ };
43
+ // Preventing alert message box for missing monomers with compressed Scilligence.JSDraw2.Lite.js
44
+ scil.Utils.alert = (s: string): void => {
45
+ logger.warning(`${logPrefix}, scil.Utils.alert() s = 's'.`);
46
+ };
47
+ trigger();
48
+ } finally {
49
+ monomers.getMonomer = getMonomerOriginal;
50
+ scil.Utils.alert = alertOriginal;
51
+ }
52
+ }
53
+
54
+ /** Inputs logic */
55
+ export function getMonomerHandleArgs(
56
+ a: Atom<HelmType> | HelmType, name?: string
57
+ ): [/** biotype */ HelmType, /** elem */ string] {
58
+ let biotype: HelmType;
59
+ let elem: string;
60
+ if ((a as Atom<HelmType>).T === 'ATOM') {
61
+ biotype = (a as Atom<HelmType>).biotype()!;
62
+ elem = (a as Atom<HelmType>).elem;
63
+ } else {
64
+ biotype = a as HelmType;
65
+ elem = org.helm.webeditor.IO.trimBracket(name!);
66
+ }
67
+ return [biotype, elem];
68
+ }
69
+
70
+
71
+ /** Substitutes {@link org.helm.webeditor.Monomers.getMonomer()} */
72
+ export function getWebEditorMonomer(
73
+ monomerLib: IMonomerLib,
74
+ a: Atom<HelmType> | HelmType, argName?: string,
75
+ ): IWebEditorMonomer | null {
76
+ const [biotype, elem] = getMonomerHandleArgs(a, argName);
77
+ const pt = helmTypeToPolymerType(biotype);
78
+
79
+ /** Get or create {@link Monomer} object (in case it is missing in monomer library current config) */
80
+ let m: Monomer | null = monomerLib.getMonomer(pt, elem);
81
+ if (m && biotype == HelmTypes.LINKER && m[REQ.RGROUPS].length != 2) {
82
+ // Web Editor expects null
83
+ return null;
84
+ }
85
+ if (m && biotype == HelmTypes.SUGAR && m[REQ.RGROUPS].length != 3) {
86
+ // Web Editor expects null
87
+ return null;
88
+ }
89
+ if (!m /* && biotype != HelmTypes.LINKER*/)
90
+ m = monomerLib.addMissingMonomer(pt, elem);
91
+
92
+ /** Get or create {@link org,helm.WebEditorMonomer} */
93
+ let resWem: IWebEditorMonomer | null = m.wem ?? null;
94
+ if (!resWem) {
95
+ if (elem === '*')
96
+ resWem = m.wem = new GapWebEditorMonomer(biotype, elem);
97
+ else if (
98
+ (biotype === 'HELM_NUCLETIDE' && elem === 'N') ||
99
+ (biotype === 'HELM_AA' && elem === 'X') ||
100
+ (biotype === 'HELM_CHEM' && false) || // TODO: Ambiguous monomer for CHEM
101
+ ambMonomerRe.test(elem) // e.g. (A,R,_)
102
+ )
103
+ resWem = m.wem = new AmbiguousWebEditorMonomer(biotype, elem);
104
+ else if (!m.lib)
105
+ resWem = m.wem = new MissingWebEditorMonomer(biotype, elem);
106
+
107
+
108
+ if (!resWem)
109
+ resWem = m.wem = LibraryWebEditorMonomer.fromMonomer(biotype, m);
110
+ }
111
+
112
+ return resWem;
113
+ }
@@ -2,9 +2,9 @@ import * as grok from 'datagrok-api/grok';
2
2
  import * as DG from 'datagrok-api/dg';
3
3
  import * as ui from 'datagrok-api/ui';
4
4
 
5
- import * as JSDraw2 from 'JSDraw2';
6
5
  import wu from 'wu';
7
6
 
7
+ import {HelmType, Mol} from '@datagrok-libraries/bio/src/helm/types';
8
8
  import {
9
9
  CellRendererBackAsyncBase, RenderServiceBase
10
10
  } from '@datagrok-libraries/bio/src/utils/cell-renderer-async-base';
@@ -16,7 +16,6 @@ import {_getHelmService} from '../package-utils';
16
16
  import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
17
17
  import {ILogger} from '@datagrok-libraries/bio/src/utils/logger';
18
18
  import {getGridCellRendererBack} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
19
- import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
20
19
 
21
20
  import {_package, getMonomerLib} from '../package';
22
21
 
@@ -44,15 +43,19 @@ class WrapLogger implements ILogger {
44
43
 
45
44
  export class HelmGridCellRendererBack extends CellRendererBackAsyncBase<HelmProps, HelmAux> {
46
45
  private _auxList: (HelmAux | null)[];
47
- private readonly monomerLib: IMonomerLib;
48
46
 
49
47
  constructor(
50
48
  gridCol: DG.GridColumn | null,
51
49
  tableCol: DG.Column<string>,
52
50
  ) {
53
51
  super(gridCol, tableCol, new WrapLogger(_package.logger) /* _package.logger */, true);
54
- this.monomerLib = getMonomerLib();
55
- this.subs.push(this.monomerLib.onChanged.subscribe(this.monomerLibOnChanged.bind(this)));
52
+
53
+ const monomerLib = getMonomerLib();
54
+ if (!monomerLib)
55
+ throw new Error('Helm package is not initialized yet.');
56
+ this.subs.push(monomerLib.onChanged.subscribe(() => {
57
+ this.reset();
58
+ }));
56
59
  }
57
60
 
58
61
  protected override reset(): void {
@@ -138,7 +141,7 @@ export class HelmGridCellRendererBack extends CellRendererBackAsyncBase<HelmProp
138
141
  const argsX = (e.offsetX - gcb.x) * dpr;
139
142
  const argsY = (e.offsetY - gcb.y) * dpr;
140
143
 
141
- const editorMol: JSDraw2.IEditorMol | null = aux.mol;
144
+ const editorMol: Mol<HelmType> | null = aux.mol;
142
145
  if (!editorMol) {
143
146
  this.logger.warning(`${logPrefix}, editorMol of the cell not found.`);
144
147
  return; // The gridCell is not rendered yet
@@ -146,7 +149,9 @@ export class HelmGridCellRendererBack extends CellRendererBackAsyncBase<HelmProp
146
149
  const seqMonomer: ISeqMonomer | null = getHoveredMonomerFromEditorMol(argsX, argsY, gridCell, editorMol);
147
150
 
148
151
  if (seqMonomer) {
149
- const tooltipEl = this.monomerLib.getTooltip(seqMonomer.polymerType, seqMonomer.symbol);
152
+ const monomerLib = getMonomerLib();
153
+ const tooltipEl = monomerLib ? monomerLib.getTooltip(seqMonomer.polymerType, seqMonomer.symbol) :
154
+ ui.divText('Monomer library is not available');
150
155
  ui.tooltip.show(tooltipEl, e.x + 16, e.y + 16);
151
156
  } else {
152
157
  // Tooltip for missing monomers