@datagrok/helm 2.0.3 → 2.0.4

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/src/package.ts CHANGED
@@ -2,34 +2,34 @@
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 {NotationConverter} from '@datagrok-libraries/bio/src/utils/notation-converter';
5
+ import * as bio from '@datagrok-libraries/bio';
6
+
6
7
  import {createJsonMonomerLibFromSdf} from './utils';
7
8
  import {MONOMER_MANAGER_MAP, RGROUPS, RGROUP_CAP_GROUP_NAME, RGROUP_LABEL, SMILES} from './constants';
8
- import {printLeftOrCentered} from '@datagrok-libraries/bio/src/utils/cell-renderer';
9
+ import {delay} from '@datagrok-libraries/utils/src/test';
9
10
 
10
11
  export const _package = new DG.Package();
11
12
 
12
- const lru = new DG.LruCache<any, any>();
13
- const molfiles = new DG.LruCache<any, any>();
14
13
  const STORAGE_NAME = 'Libraries';
15
- let i = 0;
16
14
  const LIB_PATH = 'libraries/';
17
15
 
18
16
 
19
17
  //tags: init
20
18
  export async function initHelm(): Promise<void> {
21
- let grid = grok.shell.tv.grid;
22
- for (let j = 0; j <= i; j++) {
23
- await monomerManager(await grok.dapi.userDataStorage.getValue(STORAGE_NAME, j.toString(), true));
24
- }
25
- grid.invalidate();
26
-
27
- return new Promise((resolve, reject) => {
19
+ return new Promise(async (resolve, reject) => {
28
20
  // @ts-ignore
29
21
  dojo.ready(function() { resolve(null); });
22
+ await loadLibraries();
30
23
  });
31
24
  }
32
25
 
26
+ async function loadLibraries() {
27
+ let uploadedLibraries: string[] = Object.values(await grok.dapi.userDataStorage.get(STORAGE_NAME, true));
28
+ for (let i = 0; i < uploadedLibraries.length; ++i) {
29
+ await monomerManager(await grok.dapi.userDataStorage.getValue(STORAGE_NAME, uploadedLibraries[i], true));
30
+ }
31
+ }
32
+
33
33
  //name: helmCellRenderer
34
34
  //tags: cellRenderer
35
35
  //meta.cellType: helm
@@ -40,7 +40,7 @@ export function helmCellRenderer(): HelmCellRenderer {
40
40
  }
41
41
 
42
42
 
43
- export function webEditor(value: string) {
43
+ function webEditor(cell?: DG.GridCell, value?: string) {
44
44
  let view = ui.div();
45
45
  org.helm.webeditor.MolViewer.molscale = 0.8;
46
46
  let app = new scil.helm.App(view, {
@@ -66,15 +66,20 @@ export function webEditor(value: string) {
66
66
  app.structureview.resize(sizes.rightwidth, sizes.bottomheight + app.toolbarheight);
67
67
  app.mex.resize(sizes.topheight - 80);
68
68
  setTimeout(function() {
69
- app.canvas.helm.setSequence(value, 'HELM');
69
+ if (typeof cell !== 'undefined') {
70
+ app.canvas.helm.setSequence(cell.cell.value, 'HELM');
71
+ } else {
72
+ app.canvas.helm.setSequence(value, 'HELM');
73
+ }
70
74
  }, 200);
71
75
  //@ts-ignore
72
76
  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});
77
+ .add(view)
78
+ .onOK(() => {
79
+ if (typeof cell !== 'undefined') {
80
+ cell.cell.value = app.canvas.getHelm(true).replace(/<\/span>/g, '').replace(/<span style='background:#bbf;'>/g, '');
81
+ }
82
+ }).show({modal: true, fullScreen: true});
78
83
  }
79
84
 
80
85
 
@@ -83,9 +88,7 @@ export function webEditor(value: string) {
83
88
  //input: grid_cell cell
84
89
  export function editMoleculeCell(cell: DG.GridCell): void {
85
90
  if (cell.gridColumn.column.tags[DG.TAGS.UNITS] === 'helm') {
86
- let grid = grok.shell.tv.grid;
87
- webEditor(cell.cell.value);
88
- grid.invalidate();
91
+ webEditor(cell);
89
92
  }
90
93
  }
91
94
 
@@ -95,9 +98,9 @@ export function editMoleculeCell(cell: DG.GridCell): void {
95
98
  //input: string mol { semType: Macromolecule }
96
99
  export function openEditor(mol: string): void {
97
100
  let df = grok.shell.tv.grid.dataFrame;
98
- let converter = new NotationConverter(df.columns.bySemType('Macromolecule'));
101
+ let converter = new bio.NotationConverter(df.columns.bySemType('Macromolecule'));
99
102
  const resStr = converter.convertStringToHelm(mol, '/');
100
- webEditor(resStr);
103
+ webEditor(undefined, resStr);
101
104
  }
102
105
 
103
106
  //name: Properties
@@ -105,73 +108,58 @@ export function openEditor(mol: string): void {
105
108
  //input: string helmString {semType: Macromolecule}
106
109
  //output: widget result
107
110
  export async function propertiesPanel(helmString: string) {
108
- //const lru = await getLru();
109
- const result = lru.get(helmString).split(',');
111
+ let grid = grok.shell.tv.grid;
112
+ let parent = grid.root.parentElement;
113
+ const host = ui.div([]);
114
+ parent.appendChild(host);
115
+ let editor = new JSDraw2.Editor(host, {viewonly: true});
116
+ host.style.width = '0px';
117
+ host.style.height = '0px';
118
+ editor.setHelm(helmString);
119
+ let formula = editor.getFormula(true);
120
+ let molWeight = Math.round(editor.getMolWeight() * 100) / 100;
121
+ let coef = Math.round(editor.getExtinctionCoefficient(true) * 100) / 100;
122
+ parent.lastChild.remove();
110
123
  return new DG.Widget(
111
124
  ui.tableFromMap({
112
- 'formula': result[0].replace(/<sub>/g, '').replace(/<\/sub>/g, ''),
113
- 'molecular weight': result[1],
114
- 'extinction coefficient': result[2],
115
- /*'fasta': ui.wait(async () => ui.divText(await helmToFasta(helmString))),
116
- 'rna analogue sequence': ui.wait(async () => ui.divText(await helmToRNA(helmString))),
117
- 'smiles': ui.wait(async () => ui.divText(await helmToSmiles(helmString))),
118
- //'peptide analogue sequence': ui.wait(async () => ui.divText(await helmToPeptide(helmString))),*/
125
+ 'formula': formula.replace(/<sub>/g, '').replace(/<\/sub>/g, ''),
126
+ 'molecular weight': molWeight,
127
+ 'extinction coefficient': coef,
119
128
  })
120
129
  );
121
130
  }
122
131
 
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
- }
130
-
131
-
132
- //name: loadDialog
133
- export async function loadDialog() {
134
- let res = (await _package.files.list(`${LIB_PATH}`, false, '')).map(it => it.fileName);
135
- let FilesList = await ui.choiceInput('Monomer Libraries', ' ', res);
136
- let grid = grok.shell.tv.grid;
137
- ui.dialog('Load library from file')
138
- .add(FilesList)
139
- .onOK(async () => {
140
- await monomerManager(FilesList.value);
141
- grok.dapi.userDataStorage.postValue(STORAGE_NAME, i.toString(), FilesList.value, true);
142
- i += 1;
143
- grid.invalidate();
144
- }).show();
145
- };
146
132
 
147
133
  //name: Manage Libraries
148
134
  //tags: panel, widgets
149
135
  //input: column helmColumn {semType: Macromolecule}
150
136
  //output: widget result
151
- export async function libraryPanel(helmColumn: DG.Column) {
152
- //@ts-ignore
153
- let loadButton = ui.button('Load Library');
154
- loadButton.addEventListener('click', loadDialog);
137
+ export async function libraryPanel(helmColumn: DG.Column): Promise<DG.Widget> {
155
138
  //@ts-ignore
156
- let filesButton = ui.button('Manage');
157
- filesButton.addEventListener('click', manageFiles);
158
- let list = (await _package.files.list(`${LIB_PATH}`, false, '')).map(it => it.fileName);
159
- let usedLibraries = [];
160
- for (let j = 0; j <= i; j++) {
161
- usedLibraries.push(await grok.dapi.userDataStorage.getValue(STORAGE_NAME, j.toString(), true));
139
+ let filesButton: HTMLButtonElement = ui.button('Manage', manageFiles);
140
+ let divInputs: HTMLDivElement = ui.div();
141
+ let librariesList: string[] = (await _package.files.list(`${LIB_PATH}`, false, '')).map(it => it.fileName);
142
+ let uploadedLibraries: string[] = Object.values(await grok.dapi.userDataStorage.get(STORAGE_NAME, true));
143
+ for (let i = 0; i < uploadedLibraries.length; ++i) {
144
+ let libraryName: string = uploadedLibraries[i];
145
+ divInputs.append(ui.boolInput(libraryName, true, async v => {
146
+ org.helm.webeditor.Monomers.clear();
147
+ grok.dapi.userDataStorage.remove(STORAGE_NAME, libraryName, true);
148
+ await loadLibraries();
149
+ grok.shell.tv.grid.invalidate();
150
+ }).root);
151
+ }
152
+ let unusedLibraries: string[] = librariesList.filter(x => !uploadedLibraries.includes(x));
153
+ for (let i = 0; i < unusedLibraries.length; ++i) {
154
+ let libraryName: string = unusedLibraries[i];
155
+ divInputs.append(ui.boolInput(libraryName, false, v => {
156
+ monomerManager(libraryName);
157
+ grok.dapi.userDataStorage.postValue(STORAGE_NAME, libraryName, libraryName, true);
158
+ }).root);
162
159
  }
163
- let unusedLibraries = list.filter(x => !usedLibraries.includes(x));
164
160
  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
- ])
161
+ divInputs,
162
+ ui.divV([filesButton])
175
163
  ]));
176
164
  }
177
165
 
@@ -185,7 +173,6 @@ export async function manageFiles() {
185
173
  }
186
174
 
187
175
 
188
-
189
176
  async function accessServer(url: string, key: string) {
190
177
  const params: RequestInit = {
191
178
  method: 'GET',
@@ -271,7 +258,7 @@ export async function monomerManager(value: string) {
271
258
  dfSdf = await grok.functions.call('Chem:importSdf', {bytes: file});
272
259
  df = createJsonMonomerLibFromSdf(dfSdf[0]);
273
260
  } else {
274
- grok.shell.warning("Chem package is not installed");
261
+ grok.shell.warning('Chem package is not installed');
275
262
  }
276
263
  } else {
277
264
  const file = await _package.files.readAsText(`${LIB_PATH}${value}`);
@@ -296,6 +283,8 @@ export async function monomerManager(value: string) {
296
283
  }
297
284
  org.helm.webeditor.Monomers.addOneMonomer(m);
298
285
  });
286
+ let grid: DG.Grid = grok.shell.tv.grid;
287
+ grid.invalidate();
299
288
  }
300
289
 
301
290
  //name: helmColumnToSmiles
@@ -304,52 +293,45 @@ export function helmColumnToSmiles(helmColumn: DG.Column) {
304
293
  //todo: add column with smiles to col.dataFrame.
305
294
  }
306
295
 
307
- //name: getMolfile
308
- //input: string helmString {semType: Macromolecule}
309
- export async function getMolfile(helmString) {
310
- return molfiles.get(helmString);
311
- }
312
296
 
313
297
  function split(s: string, sep: string) {
314
298
  var ret = [];
315
- var frag = "";
299
+ var frag = '';
316
300
  var parentheses = 0;
317
301
  var bracket = 0;
318
302
  var braces = 0;
319
303
  var quote = 0;
320
304
  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;
305
+ var c = s.substring(i, i + 1);
306
+ if (c == sep && bracket == 0 && parentheses == 0 && braces == 0 && quote == 0) {
307
+ ret.push(frag);
308
+ frag = '';
309
+ } else {
310
+ frag += c;
311
+ if (quote > 0) {
312
+ if (c == '\\' && i + 1 < s.length) {
313
+ ++i;
314
+ var c2 = s.substring(i, i + 1);
315
+ frag += c2;
316
+ c += c2;
317
+ }
352
318
  }
319
+ if (c == '\"') {
320
+ if (!(i > 0 && s.substring(i - 1, i) == '\\'))
321
+ quote = quote == 0 ? 1 : 0;
322
+ } else if (c == '[')
323
+ ++bracket;
324
+ else if (c == ']')
325
+ --bracket;
326
+ else if (c == '(')
327
+ ++parentheses;
328
+ else if (c == ')')
329
+ --parentheses;
330
+ else if (c == '{')
331
+ ++braces;
332
+ else if (c == '}')
333
+ --braces;
334
+ }
353
335
  }
354
336
  ret.push(frag);
355
337
  return ret;
@@ -358,81 +340,86 @@ function split(s: string, sep: string) {
358
340
  function detachAnnotation(s: string) {
359
341
  var ret = _detachAppendix(s, '\"');
360
342
  if (ret.tag != null)
361
- return ret;
343
+ return ret;
362
344
 
363
345
  var r = _detachAppendix(s, '\'');
364
- return { tag: ret.tag, repeat: r.tag, str: r.str };
346
+ return {tag: ret.tag, repeat: r.tag, str: r.str};
365
347
  }
366
348
 
367
349
  function _detachAppendix(s: string, c: string) {
368
350
  var tag = null;
369
351
  //@ts-ignore
370
352
  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
- }
353
+ var p = s.length - 1;
354
+ while (p > 0) {
355
+ p = s.lastIndexOf(c, p - 1);
356
+ if (p <= 0 || s.substring(p - 1, p) != '\\')
357
+ break;
358
+ }
377
359
 
378
- if (p > 0 && p < s.length - 1) {
379
- tag = s.substring(p + 1, s.length - 1);
380
- s = s.substring(0, p);
381
- }
360
+ if (p > 0 && p < s.length - 1) {
361
+ tag = s.substring(p + 1, s.length - 1);
362
+ s = s.substring(0, p);
363
+ }
382
364
  }
383
365
  if (tag != null)
384
- tag = tag.replace(new RegExp("\\" + c, "g"), c);
385
- return { tag: unescape(tag), str: s };
366
+ tag = tag.replace(new RegExp('\\' + c, 'g'), c);
367
+ return {tag: unescape(tag), str: s};
386
368
  }
387
369
 
388
370
  function unescape(s: string) {
389
371
  //@ts-ignore
390
372
  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
- }
373
+ return s;
374
+
375
+ return s.replace(/[\\]./g, function(m) {
376
+ switch (m) {
377
+ case '\\r':
378
+ return '\r';
379
+ case '\\n':
380
+ return '\n';
381
+ case '\\t':
382
+ return '\t';
383
+ default:
384
+ return m.substring(1);
385
+ }
404
386
  });
405
387
  }
406
388
 
407
- function parseHelm(s: string) {
389
+ //name: parseHelm
390
+ //input: string s
391
+ export function parseHelm(s: string) {
408
392
  var sections = split(s, '$');
409
393
  s = sections[0];
410
394
  var monomers = [];
411
395
  //@ts-ignore
412
396
  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
- }
397
+ var seqs = split(s, '|');
398
+ for (var i = 0; i < seqs.length; ++i) {
399
+ var e = detachAnnotation(seqs[i]);
400
+ s = e.str;
401
+
402
+ var p = s.indexOf('{');
403
+
404
+ s = s.substring(p + 1);
405
+ p = s.indexOf('}');
406
+ s = s.substring(0, p);
407
+
408
+ var ss = split(s, '.');
409
+ for (var monomer of ss) {
410
+ if (monomer.includes('(') && monomer.includes(')')) {
411
+ var elements = monomer.replace(/[()]/g, '').split('');
412
+ for (var el of elements) {
413
+ monomers.push(el);
434
414
  }
415
+ } else if (monomer.includes('[') && monomer.includes(']')) {
416
+ var element = monomer.match(/(?<=\[).*?(?=\])/g, '');
417
+ monomers.push(element[0]);
418
+ } else {
419
+ monomers.push(monomer);
420
+ }
435
421
  }
422
+ }
436
423
  }
437
424
  return monomers;
438
425
  }
@@ -455,6 +442,41 @@ export function findMonomers(helmString: string) {
455
442
  return new Set(split_string.filter(val => !monomer_names.includes(val)));
456
443
  }
457
444
 
445
+ //name: getMolfiles
446
+ //input: column col {semType: Macromolecule}
447
+ //output: column res
448
+ export function getMolfiles(col: DG.Column): DG.Column {
449
+ let grid = grok.shell.tv.grid;
450
+ let parent = grid.root.parentElement;
451
+ let res = DG.Column.string('mols', col.length);
452
+ const host = ui.div([]);
453
+ parent.appendChild(host);
454
+ let editor = new JSDraw2.Editor(host, {viewonly: true});
455
+ host.style.width = '0px';
456
+ host.style.height = '0px';
457
+ res.init((i) => {
458
+ editor.setHelm(col.get(i));
459
+ let mol = editor.getMolfile();
460
+ return mol;
461
+ });
462
+ parent.lastChild.remove();
463
+ return res;
464
+ }
465
+
466
+ function getParts(subParts: string[], s: string): string[] {
467
+ let j = 0;
468
+ let allParts: string[] = [];
469
+ for (let k = 0; k < subParts.length; ++k) {
470
+ let indexOfMonomer = s.indexOf(subParts[k]);
471
+ let helmBeforeMonomer = s.slice(j, indexOfMonomer);
472
+ allParts.push(helmBeforeMonomer);
473
+ allParts.push(subParts[k]);
474
+ s = s.substring(indexOfMonomer + subParts[k].length);
475
+ }
476
+ allParts.push(s);
477
+ return allParts;
478
+ }
479
+
458
480
  class HelmCellRenderer extends DG.GridCellRenderer {
459
481
  get name() { return 'helm'; }
460
482
 
@@ -464,6 +486,49 @@ class HelmCellRenderer extends DG.GridCellRenderer {
464
486
 
465
487
  get defaultHeight(): number | null { return 100; }
466
488
 
489
+ onMouseMove(gridCell: DG.GridCell, e: MouseEvent): void {
490
+ const maxLengthWordsSum = gridCell.cell.column.temp['helm-sum-maxLengthWords'];
491
+ const maxIndex = Object.values(gridCell.cell.column.temp['helm-maxLengthWords']).length - 1;
492
+ const argsX = e.offsetX - gridCell.gridColumn.left + (gridCell.gridColumn.left - gridCell.bounds.x);
493
+ let left = 0;
494
+ let right = maxIndex;
495
+ let found = false;
496
+ maxLengthWordsSum[maxIndex + 1] = argsX + 1;
497
+ let mid = 0;
498
+ if (argsX > maxLengthWordsSum[0]) {
499
+ while (!found) {
500
+ mid = Math.floor((right + left) / 2);
501
+ if (argsX >= maxLengthWordsSum[mid] && argsX <= maxLengthWordsSum[mid + 1]) {
502
+ left = mid;
503
+ found = true;
504
+ } else if (argsX < maxLengthWordsSum[mid]) {
505
+ right = mid - 1;
506
+ } else if (argsX > maxLengthWordsSum[mid + 1]) {
507
+ left = mid + 1;
508
+ }
509
+ if (left == right) {
510
+ found = true;
511
+ }
512
+ }
513
+ }
514
+ left = (argsX >= maxLengthWordsSum[left]) ? left + 1 : left;
515
+ const monomers = findMonomers(gridCell.cell.value);
516
+ let s: string = gridCell.cell.value ?? '';
517
+ let subParts: string[] = parseHelm(s);
518
+ let allParts: string[] = getParts(subParts, s);
519
+ let tooltipMessage: HTMLElement[] = [];
520
+ for (let i = 0; i < allParts.length; ++i) {
521
+ if (monomers.has(allParts[i]))
522
+ tooltipMessage[i] = ui.divV([
523
+ ui.divText(`Monomer ${allParts[i]} not found.`),
524
+ ui.divText('Open the Property Panel, then expand Manage Libraries')
525
+ ]);
526
+ }
527
+ (((tooltipMessage[left]?.childNodes.length ?? 0) > 0))
528
+ ? ui.tooltip.show(ui.div(tooltipMessage[left]), e.x + 16, e.y + 16)
529
+ : ui.tooltip.hide();
530
+ }
531
+
467
532
  render(g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number,
468
533
  gridCell: DG.GridCell, cellStyle: DG.GridCellStyle
469
534
  ) {
@@ -471,45 +536,47 @@ class HelmCellRenderer extends DG.GridCellRenderer {
471
536
  const undefinedColor = 'rgb(100,100,100)';
472
537
  const grayColor = '#808080';
473
538
  const monomers = findMonomers(gridCell.cell.value);
539
+ let s: string = gridCell.cell.value ?? '';
540
+ let subParts: string[] = parseHelm(s);
474
541
  if (monomers.size == 0) {
475
542
  const host = ui.div([], {style: {width: `${w}px`, height: `${h}px`}});
476
543
  host.setAttribute('dataformat', 'helm');
477
544
  host.setAttribute('data', gridCell.cell.value);
478
545
  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;
546
+ //@ts-ignore
547
+ const canvas = new JSDraw2.Editor(host, {width: w, height: h, skin: 'w8', viewonly: true});
548
+ return;
549
+ }
550
+ if (monomers.size > 0) {
551
+ w = grid ? Math.min(grid.canvas.width - x, w) : g.canvas.width - x;
552
+ g.save();
553
+ g.beginPath();
554
+ g.rect(x, y, w, h);
555
+ g.clip();
556
+ g.font = '12px monospace';
557
+ g.textBaseline = 'top';
558
+ let x1 = x;
559
+ let maxLengthWords: any = {};
560
+ let maxLengthWordSum: any = {};
561
+ let allParts: string[] = getParts(subParts, s);
562
+ for (let i = 0; i < allParts.length; ++i) {
563
+ maxLengthWords[i] = allParts[i].length * 7;
564
+ let color = monomers.has(allParts[i]) ? 'red' : grayColor;
565
+ g.fillStyle = undefinedColor;
566
+ x1 = bio.printLeftOrCentered(x1, y, w, h, g, allParts[i], color, 0, true, 1.0);
492
567
  }
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;
568
+
569
+ maxLengthWordSum[0] = maxLengthWords[0];
570
+ for (let i = 1; i < allParts.length; i++) {
571
+ maxLengthWordSum[i] = maxLengthWordSum[i - 1] + maxLengthWords[i];
512
572
  }
573
+ gridCell.cell.column.temp = {
574
+ 'helm-sum-maxLengthWords': maxLengthWordSum,
575
+ 'helm-maxLengthWords': maxLengthWords
576
+ };
577
+ g.restore();
578
+ return;
579
+ }
513
580
  }
514
581
  }
515
582
 
@@ -1,7 +1,7 @@
1
1
  import {after, before, category, delay, expect, test} from '@datagrok-libraries/utils/src/test';
2
2
  //import {findMonomers, helmToFasta, helmToPeptide, helmToRNA, initHelm} from '../package';
3
3
  import {_package} from '../package-test';
4
- import {loadDialog, manageFiles, monomerManager} from '../package';
4
+ import {manageFiles, parseHelm} from '../package';
5
5
  import * as DG from 'datagrok-api/dg';
6
6
  import * as grok from 'datagrok-api/grok';
7
7
 
@@ -46,16 +46,14 @@ category('Helm', () => {
46
46
  // expect(col.tags[DG.TAGS.UNITS], 'HELM');
47
47
  // });
48
48
 
49
-
50
49
  test('manageFiles', async () => {
51
50
  return await manageFiles();
52
51
  });
53
52
 
54
- test('loadDialog', async() => {
55
- return await grok.functions.call('Helm:loadDialog');
56
- });
57
-
53
+
58
54
  test('monomerManager', async() => {
55
+ const df: DG.DataFrame = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/test.csv'));
56
+ grok.shell.addTableView(df);
59
57
  await grok.functions.call('Helm:monomerManager', {value: 'HELMCoreLibrary.json'});
60
58
  const checkName = '2-Chloroadenine';
61
59
  let flag = false;
@@ -73,4 +71,24 @@ category('Helm', () => {
73
71
  expect(flag, true);
74
72
  });
75
73
 
74
+ test('parseHelm', async() => {
75
+ const expectedResults = [
76
+ ['meI', 'hHis', 'Aca', 'N', 'T', 'dK', 'Thr_PO3H2', 'D-Tyr_Et', 'Aze', 'dV', 'E', 'Phe_4Me'],
77
+ ['A', 'C', 'T', 'G', 'W', 'E', 'Q'],
78
+ ['R', 'U', 'P', 'T', 'A'],
79
+ ['A', '*', 'G', 'C']
80
+ ];
81
+ const helmStrings = [
82
+ 'PEPTIDE1{meI.hHis.Aca.N.T.dK.Thr_PO3H2.Aca.D-Tyr_Et.Aze.dV.E.N.dV.Phe_4Me}$$$',
83
+ 'PEPTIDE1{A.C.T.G.C.T.W.G.T.W.E.C.W.C.Q.W}|PEPTIDE2{A.C.T.G.C.T.W.G.T.W.E.Q}$PEPTIDE1,PEPTIDE1,5:R3-14:R3|PEPTIDE2,PEPTIDE1,2:R3-12:R3$$$',
84
+ 'RNA1{R(U)P.R(T)P}|RNA2{P.R(A)P.R(A)}$RNA1,RNA2,2:pair-6:pair|RNA1,RNA2,5:pair-3:pair$$$',
85
+ 'PEPTIDE1{A.*.G.C}$$$$V2.0'
86
+ ];
87
+ for (let idx = 0; idx < helmStrings.length; ++idx) {
88
+ let monomerSet = new Set(parseHelm(helmStrings[idx]));
89
+ let monomerArray = Array.from(monomerSet);
90
+ expect(JSON.stringify(monomerArray), JSON.stringify(expectedResults[idx]));
91
+ }
92
+ });
93
+
76
94
  });