@datagrok/helm 2.0.0 → 2.0.3

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.
@@ -15,6 +15,9 @@
15
15
  <script type="text/javascript">
16
16
  dojo.ready(function () {
17
17
  var jsd = new JSDraw("Div2", { width: 800, height: 400, skin: "w8", viewonly: true });
18
+ var mol = jsd.getMolfile();
19
+ var m = new JSDraw2.Mol();
20
+ console.log(JSDraw2.FormulaParser.getAtomStats(m.setMolfile(mol)));
18
21
  });
19
22
  </script>
20
23
  </body>
package/package.json CHANGED
@@ -1,17 +1,19 @@
1
1
  {
2
2
  "name": "@datagrok/helm",
3
- "beta": false,
4
3
  "friendlyName": "Helm",
5
- "version": "2.0.0",
4
+ "version": "2.0.3",
5
+ "author": {
6
+ "name": "Oleksandra Serhiienko",
7
+ "email": "oserhiienko@datagrok.ai"
8
+ },
6
9
  "description": "",
7
10
  "dependencies": {
8
- "datagrok-api": "^1.4.12",
9
- "cash-dom": "latest",
10
- "dayjs": "latest",
11
- "@datagrok-libraries/utils": "^1.4.0",
12
- "@datagrok-libraries/bio": "^2.8.4",
13
- "ts-loader": "^9.2.5",
14
- "typescript": "^4.4.2"
11
+ "datagrok-api": "^1.6.0",
12
+ "cash-dom": "^8.1.1",
13
+ "dayjs": "^1.10.6",
14
+ "@datagrok-libraries/utils": "^1.6.2",
15
+ "@datagrok-libraries/bio": "^3.1.1"
16
+
15
17
  },
16
18
  "sources": [
17
19
  "css/helm.css",
@@ -21,21 +23,24 @@
21
23
  "helm/JSDraw/Pistoia.HELM-uncompressed.js"
22
24
  ],
23
25
  "devDependencies": {
24
- "@typescript-eslint/eslint-plugin": "latest",
25
- "@typescript-eslint/parser": "latest",
26
- "eslint": "latest",
27
- "eslint-config-google": "latest",
28
- "ts-loader": "latest",
29
- "typescript": "latest",
30
- "webpack": "latest",
31
- "webpack-cli": "latest",
32
26
  "@types/jest": "^27.0.0",
33
27
  "jest": "^27.0.0",
34
- "jest-html-reporter": "^3.5.0",
28
+ "jest-html-reporter": "^3.6.0",
29
+ "path": "^0.12.7",
35
30
  "puppeteer": "^13.7.0",
36
31
  "ts-jest": "^27.0.0",
32
+ "ts-loader": "^9.2.6",
33
+ "typescript": "^4.4.4",
34
+ "webpack": "^5.59.1",
35
+ "webpack-cli": "^4.9.1",
36
+ "@typescript-eslint/eslint-plugin": "^5.11.0",
37
+ "@typescript-eslint/parser": "^5.11.0",
38
+ "eslint": "^8.18.0",
39
+ "eslint-config-google": "^0.14.0",
37
40
  "@types/js-yaml": "^4.0.5",
38
- "@types/node-fetch": "^2.6.1"
41
+ "js-yaml": "^4.1.0",
42
+ "@types/node-fetch": "^2.6.2",
43
+ "node-fetch": "^2.6.7"
39
44
  },
40
45
  "scripts": {
41
46
  "link-all": "npm link datagrok-api @datagrok-libraries/utils @datagrok-libraries/bio",
@@ -51,7 +56,7 @@
51
56
  "release-helm-public": "grok publish public --release",
52
57
  "lint": "eslint \"./src/**/*.ts\"",
53
58
  "lint-fix": "eslint \"./src/**/*.ts\" --fix",
54
- "test": "set HOST=dev && jest",
59
+ "test": "jest",
55
60
  "test-dev": "set HOST=dev && jest",
56
61
  "test-local": "set HOST=localhost && jest"
57
62
  },
@@ -5,7 +5,7 @@
5
5
  import * as utils from './test-node';
6
6
  import puppeteer from 'puppeteer';
7
7
 
8
- const P_START_TIMEOUT: number = 100000;
8
+ const P_START_TIMEOUT: number = 3600000;
9
9
  let browser: puppeteer.Browser;
10
10
  let page: puppeteer.Page;
11
11
 
@@ -51,10 +51,10 @@ it('TEST', async () => {
51
51
  let failReport = '';
52
52
  for (let i = 0; i < df.rowCount; i++) {
53
53
  if (cStatus.get(i)) {
54
- passReport += `Test result : ${targetPackage}.${cCat.get(i)}.${cName.get(i)} : ${cMessage.get(i)}\n`;
54
+ passReport += `Test result : Success : ${targetPackage}.${cCat.get(i)}.${cName.get(i)} : ${cMessage.get(i)}\n`;
55
55
  } else {
56
56
  failed = true;
57
- failReport += `Test result : ${targetPackage}.${cCat.get(i)}.${cName.get(i)} : ${cMessage.get(i)}\n`;
57
+ failReport += `Test result : Failed : ${targetPackage}.${cCat.get(i)}.${cName.get(i)} : ${cMessage.get(i)}\n`;
58
58
  }
59
59
  }
60
60
  resolve({failReport, passReport, failed});
@@ -65,4 +65,4 @@ it('TEST', async () => {
65
65
  console.log(r.passReport);
66
66
  // @ts-ignore
67
67
  expect(r.failed).checkOutput(false, r.failReport);
68
- }, 100000);
68
+ }, 3600000);
@@ -66,6 +66,7 @@ export async function getBrowserPage(puppeteer: any): Promise<{browser: any, pag
66
66
  });
67
67
 
68
68
  let page = await browser.newPage();
69
+ await page.setDefaultNavigationTimeout(0);
69
70
  await page.goto(`${url}/oauth/`);
70
71
  await page.setCookie({name: 'auth', value: token});
71
72
  await page.evaluate((token: any) => {
@@ -73,8 +74,8 @@ export async function getBrowserPage(puppeteer: any): Promise<{browser: any, pag
73
74
  }, token);
74
75
  await page.goto(url);
75
76
  try {
76
- await page.waitForSelector('.grok-preloader');
77
- await page.waitForFunction(() => document.querySelector('.grok-preloader') == null, {timeout: 100000});
77
+ await page.waitForSelector('.grok-preloader', { timeout: 1800000 });
78
+ await page.waitForFunction(() => document.querySelector('.grok-preloader') == null, {timeout: 3600000});
78
79
  } catch (error) {
79
80
  throw error;
80
81
  }
@@ -11,7 +11,6 @@ export {tests};
11
11
  //input: string category {optional: true}
12
12
  //input: string test {optional: true}
13
13
  //output: dataframe result
14
- //top-menu: Tools | Dev | JS API Tests
15
14
  export async function test(category: string, test: string): Promise<DG.DataFrame> {
16
15
  const data = await runTests({category, test});
17
16
  return DG.DataFrame.fromObjects(data)!;
package/src/package.ts CHANGED
@@ -2,16 +2,15 @@
2
2
  import * as grok from 'datagrok-api/grok';
3
3
  import * as ui from 'datagrok-api/ui';
4
4
  import * as DG from 'datagrok-api/dg';
5
- //import {lru} from '../../Bio/src/utils/cell-renderer';
6
- import {NotationConverter, NOTATION} from '@datagrok-libraries/bio/src/utils/notation-converter';
7
- import {WebLogo} from '@datagrok-libraries/bio/src/viewers/web-logo';
5
+ import {NotationConverter} from '@datagrok-libraries/bio/src/utils/notation-converter';
8
6
  import {createJsonMonomerLibFromSdf} from './utils';
9
7
  import {MONOMER_MANAGER_MAP, RGROUPS, RGROUP_CAP_GROUP_NAME, RGROUP_LABEL, SMILES} from './constants';
10
- //import {ConverterFunc, DfReaderFunc} from '../../Bio/src/tests/types';
8
+ import {printLeftOrCentered} from '@datagrok-libraries/bio/src/utils/cell-renderer';
11
9
 
12
10
  export const _package = new DG.Package();
13
11
 
14
12
  const lru = new DG.LruCache<any, any>();
13
+ const molfiles = new DG.LruCache<any, any>();
15
14
  const STORAGE_NAME = 'Libraries';
16
15
  let i = 0;
17
16
  const LIB_PATH = 'libraries/';
@@ -24,22 +23,24 @@ export async function initHelm(): Promise<void> {
24
23
  await monomerManager(await grok.dapi.userDataStorage.getValue(STORAGE_NAME, j.toString(), true));
25
24
  }
26
25
  grid.invalidate();
26
+
27
+ return new Promise((resolve, reject) => {
28
+ // @ts-ignore
29
+ dojo.ready(function() { resolve(null); });
30
+ });
27
31
  }
28
32
 
29
- //name: initHelp
30
- export function initHelp(){
31
- return;
33
+ //name: helmCellRenderer
34
+ //tags: cellRenderer
35
+ //meta.cellType: helm
36
+ //meta.columnTags: units=helm
37
+ //output: grid_cell_renderer result
38
+ export function helmCellRenderer(): HelmCellRenderer {
39
+ return new HelmCellRenderer();
32
40
  }
33
41
 
34
- //name: getLru
35
- export async function getLru() {
36
- return await grok.functions.call('Bio:Lru');
37
- }
38
42
 
39
- //tags: cellEditor
40
- //description: Macromolecule
41
- //input: grid_cell cell
42
- export function editMoleculeCell(cell: DG.GridCell): void {
43
+ export function webEditor(value: string) {
43
44
  let view = ui.div();
44
45
  org.helm.webeditor.MolViewer.molscale = 0.8;
45
46
  let app = new scil.helm.App(view, {
@@ -64,55 +65,72 @@ export function editMoleculeCell(cell: DG.GridCell): void {
64
65
  scil.apply(app.properties.parent.style, s);
65
66
  app.structureview.resize(sizes.rightwidth, sizes.bottomheight + app.toolbarheight);
66
67
  app.mex.resize(sizes.topheight - 80);
67
- if (cell.gridColumn.column.tags[DG.TAGS.UNITS] === 'HELM') {
68
- setTimeout(function() {
69
- app.canvas.helm.setSequence(cell.cell.value, 'HELM');
70
- }, 200);
71
- //@ts-ignore
72
- ui.dialog({showHeader: false, showFooter: true})
73
- .add(view)
74
- .onOK(() => {
75
- const val = app.canvas.getHelm(true).replace(/<\/span>/g, '').replace(/<span style='background:#bbf;'>/g, '');
76
- cell.cell.value = val;
77
- }).show({modal: true, fullScreen: true});
78
- } else if (cell.gridColumn.column.tags[DG.TAGS.UNITS] === 'fasta:SEQ:PT') {
79
- const converter = new NotationConverter(cell.gridColumn.column);
80
- const resStr = converter.convertStringToHelm(cell.cell.value, '-');
81
- setTimeout(function() {
82
- app.canvas.helm.setSequence(resStr, 'HELM');
83
- }, 200);
84
- //@ts-ignore
85
- ui.dialog({showHeader: false, showFooter: true})
86
- .add(view)
87
- .onOK(() => {
88
- cell.cell.value;
89
- }).show({modal: true, fullScreen: true});
68
+ setTimeout(function() {
69
+ app.canvas.helm.setSequence(value, 'HELM');
70
+ }, 200);
71
+ //@ts-ignore
72
+ ui.dialog({showHeader: false, showFooter: true})
73
+ .add(view)
74
+ .onOK(() => {
75
+ const val = app.canvas.getHelm(true).replace(/<\/span>/g, '').replace(/<span style='background:#bbf;'>/g, '');
76
+ value = val;
77
+ }).show({modal: true, fullScreen: true});
78
+ }
79
+
80
+
81
+ //tags: cellEditor
82
+ //description: Macromolecule
83
+ //input: grid_cell cell
84
+ export function editMoleculeCell(cell: DG.GridCell): void {
85
+ if (cell.gridColumn.column.tags[DG.TAGS.UNITS] === 'helm') {
86
+ let grid = grok.shell.tv.grid;
87
+ webEditor(cell.cell.value);
88
+ grid.invalidate();
90
89
  }
91
90
  }
92
91
 
93
- //name: Details
92
+ //name: Open Helm Web Editor
93
+ //description: Adds editor
94
+ //meta.action: Open Helm Web Editor
95
+ //input: string mol { semType: Macromolecule }
96
+ export function openEditor(mol: string): void {
97
+ let df = grok.shell.tv.grid.dataFrame;
98
+ let converter = new NotationConverter(df.columns.bySemType('Macromolecule'));
99
+ const resStr = converter.convertStringToHelm(mol, '/');
100
+ webEditor(resStr);
101
+ }
102
+
103
+ //name: Properties
94
104
  //tags: panel, widgets
95
105
  //input: string helmString {semType: Macromolecule}
96
106
  //output: widget result
97
- export async function detailsPanel(helmString: string) {
98
- const lru = await getLru();
107
+ export async function propertiesPanel(helmString: string) {
108
+ //const lru = await getLru();
99
109
  const result = lru.get(helmString).split(',');
100
110
  return new DG.Widget(
101
111
  ui.tableFromMap({
102
112
  'formula': result[0].replace(/<sub>/g, '').replace(/<\/sub>/g, ''),
103
113
  'molecular weight': result[1],
104
114
  'extinction coefficient': result[2],
105
- 'molfile': result[3],
106
- 'fasta': ui.wait(async () => ui.divText(await helmToFasta(helmString))),
115
+ /*'fasta': ui.wait(async () => ui.divText(await helmToFasta(helmString))),
107
116
  'rna analogue sequence': ui.wait(async () => ui.divText(await helmToRNA(helmString))),
108
117
  'smiles': ui.wait(async () => ui.divText(await helmToSmiles(helmString))),
109
- //'peptide analogue sequence': ui.wait(async () => ui.divText(await helmToPeptide(helmString))),
118
+ //'peptide analogue sequence': ui.wait(async () => ui.divText(await helmToPeptide(helmString))),*/
110
119
  })
111
120
  );
112
121
  }
113
122
 
123
+ //name: Molfile
124
+ //tags: panel, widgets
125
+ //input: string helmString {semType: Macromolecule}
126
+ //output: widget result
127
+ export async function molfilePanel(helmString: string) {
128
+ return ui.textInput('', await getMolfile(helmString));
129
+ }
114
130
 
115
- async function loadDialog() {
131
+
132
+ //name: loadDialog
133
+ export async function loadDialog() {
116
134
  let res = (await _package.files.list(`${LIB_PATH}`, false, '')).map(it => it.fileName);
117
135
  let FilesList = await ui.choiceInput('Monomer Libraries', ' ', res);
118
136
  let grid = grok.shell.tv.grid;
@@ -134,21 +152,40 @@ export async function libraryPanel(helmColumn: DG.Column) {
134
152
  //@ts-ignore
135
153
  let loadButton = ui.button('Load Library');
136
154
  loadButton.addEventListener('click', loadDialog);
155
+ //@ts-ignore
156
+ let filesButton = ui.button('Manage');
157
+ filesButton.addEventListener('click', manageFiles);
137
158
  let list = (await _package.files.list(`${LIB_PATH}`, false, '')).map(it => it.fileName);
138
159
  let usedLibraries = [];
139
160
  for (let j = 0; j <= i; j++) {
140
161
  usedLibraries.push(await grok.dapi.userDataStorage.getValue(STORAGE_NAME, j.toString(), true));
141
162
  }
142
163
  let unusedLibraries = list.filter(x => !usedLibraries.includes(x));
143
- return new DG.Widget(
144
- ui.tableFromMap({
145
- 'Uploaded libraries': ui.divText(usedLibraries.toString()),
146
- 'Not used libraries': ui.divText(unusedLibraries.toString()),
147
- 'Upload new library': ui.divH([loadButton]),
148
- })
149
- );
164
+ return new DG.Widget(ui.splitV([
165
+ ui.splitH([
166
+ ui.tableFromMap({
167
+ 'Uploaded libraries': ui.divText(usedLibraries.toString()),
168
+ 'Not used libraries': ui.divText(unusedLibraries.toString()),
169
+ })
170
+ ]),
171
+ ui.splitH([
172
+ ui.divH([loadButton]),
173
+ ui.divH([filesButton]),
174
+ ])
175
+ ]));
150
176
  }
151
177
 
178
+ //name: manageFiles
179
+ export async function manageFiles() {
180
+ const a = ui.dialog({title: 'Manage files'})
181
+ //@ts-ignore
182
+ .add(ui.fileBrowser({path: 'System:AppData/Helm/libraries'}).root)
183
+ .addButton('OK', () => a.close())
184
+ .show();
185
+ }
186
+
187
+
188
+
152
189
  async function accessServer(url: string, key: string) {
153
190
  const params: RequestInit = {
154
191
  method: 'GET',
@@ -220,12 +257,22 @@ function getRS(smiles: string) {
220
257
  return res;
221
258
  }
222
259
 
223
- async function monomerManager(value: string) {
260
+ //name: monomerManager
261
+ //input: string value
262
+ export async function monomerManager(value: string) {
224
263
  let df: any[];
264
+ let file;
265
+ let dfSdf;
225
266
  if (value.endsWith('.sdf')) {
226
- const file = await _package.files.readAsBytes(`${LIB_PATH}${value}`);
227
- const dfSdf = await grok.functions.call('Chem:importSdf', {bytes: file});
228
- df = createJsonMonomerLibFromSdf(dfSdf[0]);
267
+ const funcList: DG.Func[] = DG.Func.find({package: 'Chem', name: 'importSdf'});
268
+ console.debug(`Helm: initHelm() funcList.length = ${funcList.length}`);
269
+ if (funcList.length === 1) {
270
+ file = await _package.files.readAsBytes(`${LIB_PATH}${value}`);
271
+ dfSdf = await grok.functions.call('Chem:importSdf', {bytes: file});
272
+ df = createJsonMonomerLibFromSdf(dfSdf[0]);
273
+ } else {
274
+ grok.shell.warning("Chem package is not installed");
275
+ }
229
276
  } else {
230
277
  const file = await _package.files.readAsText(`${LIB_PATH}${value}`);
231
278
  df = JSON.parse(file);
@@ -257,28 +304,161 @@ export function helmColumnToSmiles(helmColumn: DG.Column) {
257
304
  //todo: add column with smiles to col.dataFrame.
258
305
  }
259
306
 
307
+ //name: getMolfile
308
+ //input: string helmString {semType: Macromolecule}
309
+ export async function getMolfile(helmString) {
310
+ return molfiles.get(helmString);
311
+ }
312
+
313
+ function split(s: string, sep: string) {
314
+ var ret = [];
315
+ var frag = "";
316
+ var parentheses = 0;
317
+ var bracket = 0;
318
+ var braces = 0;
319
+ var quote = 0;
320
+ for (var i = 0; i < s.length; ++i) {
321
+ var c = s.substring(i, i + 1);
322
+ if (c == sep && bracket == 0 && parentheses == 0 && braces == 0 && quote == 0) {
323
+ ret.push(frag);
324
+ frag = "";
325
+ }
326
+ else {
327
+ frag += c;
328
+ if (quote > 0) {
329
+ if (c == '\\' && i + 1 < s.length) {
330
+ ++i;
331
+ var c2 = s.substring(i, i + 1);
332
+ frag += c2;
333
+ c += c2;
334
+ }
335
+ }
336
+ if (c == '\"') {
337
+ if (!(i > 0 && s.substring(i - 1, i) == '\\'))
338
+ quote = quote == 0 ? 1 : 0;
339
+ }
340
+ else if (c == '[')
341
+ ++bracket;
342
+ else if (c == ']')
343
+ --bracket;
344
+ else if (c == '(')
345
+ ++parentheses;
346
+ else if (c == ')')
347
+ --parentheses;
348
+ else if (c == '{')
349
+ ++braces;
350
+ else if (c == '}')
351
+ --braces;
352
+ }
353
+ }
354
+ ret.push(frag);
355
+ return ret;
356
+ }
357
+
358
+ function detachAnnotation(s: string) {
359
+ var ret = _detachAppendix(s, '\"');
360
+ if (ret.tag != null)
361
+ return ret;
362
+
363
+ var r = _detachAppendix(s, '\'');
364
+ return { tag: ret.tag, repeat: r.tag, str: r.str };
365
+ }
366
+
367
+ function _detachAppendix(s: string, c: string) {
368
+ var tag = null;
369
+ //@ts-ignore
370
+ if (scil.Utils.endswith(s, c)) {
371
+ var p = s.length - 1;
372
+ while (p > 0) {
373
+ p = s.lastIndexOf(c, p - 1);
374
+ if (p <= 0 || s.substring(p - 1, p) != '\\')
375
+ break;
376
+ }
377
+
378
+ if (p > 0 && p < s.length - 1) {
379
+ tag = s.substring(p + 1, s.length - 1);
380
+ s = s.substring(0, p);
381
+ }
382
+ }
383
+ if (tag != null)
384
+ tag = tag.replace(new RegExp("\\" + c, "g"), c);
385
+ return { tag: unescape(tag), str: s };
386
+ }
387
+
388
+ function unescape(s: string) {
389
+ //@ts-ignore
390
+ if (scil.Utils.isNullOrEmpty(s))
391
+ return s;
392
+
393
+ return s.replace(/[\\]./g, function (m) {
394
+ switch (m) {
395
+ case "\\r":
396
+ return "\r";
397
+ case "\\n":
398
+ return "\n";
399
+ case "\\t":
400
+ return "\t";
401
+ default:
402
+ return m.substring(1);
403
+ }
404
+ });
405
+ }
406
+
407
+ function parseHelm(s: string) {
408
+ var sections = split(s, '$');
409
+ s = sections[0];
410
+ var monomers = [];
411
+ //@ts-ignore
412
+ if (!scil.Utils.isNullOrEmpty(s)) {
413
+ var seqs = split(s, '|');
414
+ for (var i = 0; i < seqs.length; ++i) {
415
+ var e = detachAnnotation(seqs[i]);
416
+ s = e.str;
417
+
418
+ var p = s.indexOf("{");
419
+
420
+ s = s.substring(p + 1);
421
+ p = s.indexOf('}');
422
+ s = s.substring(0, p);
423
+
424
+ var ss = split(s, '.');
425
+ for (var monomer of ss) {
426
+ if (monomer.includes('(') && monomer.includes(')')) {
427
+ var elements = monomer.replace(/[()]/g, "").split("");
428
+ for (var el of elements) {
429
+ monomers.push(el);
430
+ }
431
+ } else {
432
+ monomers.push(monomer);
433
+ }
434
+ }
435
+ }
436
+ }
437
+ return monomers;
438
+ }
439
+
260
440
  //name: findMonomers
261
- //input: string helmString { semType: Macromolecule }
262
- export async function findMonomers(helmString: string) {
441
+ //input: string helmString
442
+ export function findMonomers(helmString: string) {
443
+ //@ts-ignore
263
444
  const types = Object.keys(org.helm.webeditor.monomerTypeList());
264
- const monomers = [];
265
- const monomerNames = [];
266
- for (let i = 0; i < types.length; i++) {
267
- // Synonym to overcome eslint error
268
- const GetMonomerSet = scil.helm.Monomers.getMonomerSet;
269
- monomers.push(new GetMonomerSet(types[i]));
270
- Object.keys(monomers[i]).forEach((k) => {
271
- monomerNames.push(monomers[i][k].id);
445
+ const monomers: any = [];
446
+ const monomer_names: any = [];
447
+ for (var i = 0; i < types.length; i++) {
448
+ //@ts-ignore
449
+ monomers.push(new scil.helm.Monomers.getMonomerSet(types[i]));
450
+ Object.keys(monomers[i]).forEach(k => {
451
+ monomer_names.push(monomers[i][k].id);
272
452
  });
273
453
  }
274
- const splitString = WebLogo.splitterAsHelm(helmString);
275
- return new Set(splitString.filter((val) => !monomerNames.includes(val)));
454
+ const split_string = parseHelm(helmString);
455
+ return new Set(split_string.filter(val => !monomer_names.includes(val)));
276
456
  }
277
457
 
278
458
  class HelmCellRenderer extends DG.GridCellRenderer {
279
- get name() { return 'macromolecule'; }
459
+ get name() { return 'helm'; }
280
460
 
281
- get cellType() { return 'macromolecule'; }
461
+ get cellType() { return 'helm'; }
282
462
 
283
463
  get defaultWidth(): number | null { return 400; }
284
464
 
@@ -287,17 +467,55 @@ class HelmCellRenderer extends DG.GridCellRenderer {
287
467
  render(g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number,
288
468
  gridCell: DG.GridCell, cellStyle: DG.GridCellStyle
289
469
  ) {
290
- const host = ui.div([], {style: {width: `${w}px`, height: `${h}px`}});
291
- host.setAttribute('dataformat', 'helm');
292
- host.setAttribute('data', gridCell.cell.value);
293
-
294
- gridCell.element = host;
295
- const canvas = new JSDraw2.Editor(host, {width: w, height: h, skin: 'w8', viewonly: true});
296
- const formula = canvas.getFormula(true);
297
- const molWeight = Math.round(canvas.getMolWeight() * 100) / 100;
298
- const coef = Math.round(canvas.getExtinctionCoefficient(true) * 100) / 100;
299
- const molfile = canvas.getMolfile();
300
- const result = formula + ', ' + molWeight + ', ' + coef + ', ' + molfile;
301
- lru.set(gridCell.cell.value, result);
470
+ const grid = gridCell.gridRow !== -1 ? gridCell.grid : undefined;
471
+ const undefinedColor = 'rgb(100,100,100)';
472
+ const grayColor = '#808080';
473
+ const monomers = findMonomers(gridCell.cell.value);
474
+ if (monomers.size == 0) {
475
+ const host = ui.div([], {style: {width: `${w}px`, height: `${h}px`}});
476
+ host.setAttribute('dataformat', 'helm');
477
+ host.setAttribute('data', gridCell.cell.value);
478
+ gridCell.element = host;
479
+ //@ts-ignore
480
+ const canvas = new JSDraw2.Editor(host, {width: w, height: h, skin: 'w8', viewonly: true});
481
+ const formula = canvas.getFormula(true);
482
+ if (!formula) {
483
+ gridCell.element = ui.divText(gridCell.cell.value, {style: {color: 'red'}});
484
+ }
485
+ const molWeight = Math.round(canvas.getMolWeight() * 100) / 100;
486
+ const coef = Math.round(canvas.getExtinctionCoefficient(true) * 100) / 100;
487
+ const molfile = canvas.getMolfile();
488
+ const result = formula + ', ' + molWeight + ', ' + coef;
489
+ lru.set(gridCell.cell.value, result);
490
+ molfiles.set(gridCell.cell.value, molfile);
491
+ return;
492
+ }
493
+ if (monomers.size > 0) {
494
+ w = grid ? Math.min(grid.canvas.width - x, w) : g.canvas.width - x;
495
+ g.save();
496
+ g.beginPath();
497
+ g.rect(x, y, w, h);
498
+ g.clip();
499
+ g.font = '12px monospace';
500
+ g.textBaseline = 'top';
501
+ let x1 = x;
502
+ const s: string = gridCell.cell.value ?? '';
503
+ let subParts: string[] = parseHelm(s);
504
+ subParts.forEach((amino, index) => {
505
+ let color = monomers.has(amino) ? 'red' : grayColor;
506
+ g.fillStyle = undefinedColor;
507
+ let last = index === subParts.length - 1;
508
+ x1 = printLeftOrCentered(x1, y, w, h, g, amino, color, 0, true, 1.0, '/', last);
509
+ });
510
+ g.restore();
511
+ return;
512
+ }
302
513
  }
303
514
  }
515
+
516
+ //name: getMonomerLib
517
+ //input: string type
518
+ //output: string monomerSetJSON
519
+ export function getMonomerLib(type: string): string {
520
+ return JSON.stringify(scil.helm.Monomers.getMonomerSet(type));
521
+ }