@datagrok/helm 2.0.3 → 2.0.6
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/dist/package-test.js +57543 -824
- package/dist/package.js +57339 -704
- package/package.json +5 -5
- package/src/__jest__/remote.test.ts +7 -6
- package/src/__jest__/test-node.ts +1 -1
- package/src/helm-web-editor.ts +45 -0
- package/src/package-test.ts +4 -3
- package/src/package.ts +327 -198
- package/src/tests/helm-tests.ts +24 -6
- package/{test-Helm-4f0c8bae6479-d7a9fa45.html → test-Helm-49ff04f38f57-48c80158.html} +6 -10
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
|
|
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 {
|
|
9
|
+
import {HelmWebEditor} from './helm-web-editor';
|
|
10
|
+
import {Observable, Subject} from 'rxjs';
|
|
9
11
|
|
|
10
12
|
export const _package = new DG.Package();
|
|
11
13
|
|
|
12
|
-
const lru = new DG.LruCache<any, any>();
|
|
13
|
-
const molfiles = new DG.LruCache<any, any>();
|
|
14
14
|
const STORAGE_NAME = 'Libraries';
|
|
15
|
-
let i = 0;
|
|
16
15
|
const LIB_PATH = 'libraries/';
|
|
17
16
|
|
|
18
|
-
|
|
19
17
|
//tags: init
|
|
20
18
|
export async function initHelm(): Promise<void> {
|
|
21
|
-
|
|
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
|
+
|
|
27
|
+
async function loadLibraries() {
|
|
28
|
+
let uploadedLibraries: string[] = Object.values(await grok.dapi.userDataStorage.get(STORAGE_NAME, true));
|
|
29
|
+
for (let i = 0; i < uploadedLibraries.length; ++i)
|
|
30
|
+
await monomerManager(uploadedLibraries[i]);
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
|
|
109
|
-
|
|
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':
|
|
113
|
-
'molecular weight':
|
|
114
|
-
'extinction coefficient':
|
|
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
|
-
|
|
158
|
-
let
|
|
159
|
-
let
|
|
160
|
-
for (let
|
|
161
|
-
|
|
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
|
-
|
|
166
|
-
|
|
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(
|
|
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,14 @@ export async function monomerManager(value: string) {
|
|
|
296
283
|
}
|
|
297
284
|
org.helm.webeditor.Monomers.addOneMonomer(m);
|
|
298
285
|
});
|
|
286
|
+
|
|
287
|
+
// Now after updating org.helm.webeditor.Monomers we update MonomerLib window singleton (fires onChanged event)
|
|
288
|
+
(getMonomerLibObj() as MonomerLib).update(getAllLibsData());
|
|
289
|
+
//grok.events.fireCustomEvent('monomersLibChanged', null);
|
|
290
|
+
|
|
291
|
+
// Obsolete
|
|
292
|
+
let grid: DG.Grid = grok.shell.tv.grid;
|
|
293
|
+
grid.invalidate();
|
|
299
294
|
}
|
|
300
295
|
|
|
301
296
|
//name: helmColumnToSmiles
|
|
@@ -304,52 +299,45 @@ export function helmColumnToSmiles(helmColumn: DG.Column) {
|
|
|
304
299
|
//todo: add column with smiles to col.dataFrame.
|
|
305
300
|
}
|
|
306
301
|
|
|
307
|
-
//name: getMolfile
|
|
308
|
-
//input: string helmString {semType: Macromolecule}
|
|
309
|
-
export async function getMolfile(helmString) {
|
|
310
|
-
return molfiles.get(helmString);
|
|
311
|
-
}
|
|
312
302
|
|
|
313
303
|
function split(s: string, sep: string) {
|
|
314
304
|
var ret = [];
|
|
315
|
-
var frag =
|
|
305
|
+
var frag = '';
|
|
316
306
|
var parentheses = 0;
|
|
317
307
|
var bracket = 0;
|
|
318
308
|
var braces = 0;
|
|
319
309
|
var quote = 0;
|
|
320
310
|
for (var i = 0; i < s.length; ++i) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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;
|
|
311
|
+
var c = s.substring(i, i + 1);
|
|
312
|
+
if (c == sep && bracket == 0 && parentheses == 0 && braces == 0 && quote == 0) {
|
|
313
|
+
ret.push(frag);
|
|
314
|
+
frag = '';
|
|
315
|
+
} else {
|
|
316
|
+
frag += c;
|
|
317
|
+
if (quote > 0) {
|
|
318
|
+
if (c == '\\' && i + 1 < s.length) {
|
|
319
|
+
++i;
|
|
320
|
+
var c2 = s.substring(i, i + 1);
|
|
321
|
+
frag += c2;
|
|
322
|
+
c += c2;
|
|
323
|
+
}
|
|
352
324
|
}
|
|
325
|
+
if (c == '\"') {
|
|
326
|
+
if (!(i > 0 && s.substring(i - 1, i) == '\\'))
|
|
327
|
+
quote = quote == 0 ? 1 : 0;
|
|
328
|
+
} else if (c == '[')
|
|
329
|
+
++bracket;
|
|
330
|
+
else if (c == ']')
|
|
331
|
+
--bracket;
|
|
332
|
+
else if (c == '(')
|
|
333
|
+
++parentheses;
|
|
334
|
+
else if (c == ')')
|
|
335
|
+
--parentheses;
|
|
336
|
+
else if (c == '{')
|
|
337
|
+
++braces;
|
|
338
|
+
else if (c == '}')
|
|
339
|
+
--braces;
|
|
340
|
+
}
|
|
353
341
|
}
|
|
354
342
|
ret.push(frag);
|
|
355
343
|
return ret;
|
|
@@ -358,81 +346,86 @@ function split(s: string, sep: string) {
|
|
|
358
346
|
function detachAnnotation(s: string) {
|
|
359
347
|
var ret = _detachAppendix(s, '\"');
|
|
360
348
|
if (ret.tag != null)
|
|
361
|
-
|
|
349
|
+
return ret;
|
|
362
350
|
|
|
363
351
|
var r = _detachAppendix(s, '\'');
|
|
364
|
-
return {
|
|
352
|
+
return {tag: ret.tag, repeat: r.tag, str: r.str};
|
|
365
353
|
}
|
|
366
354
|
|
|
367
355
|
function _detachAppendix(s: string, c: string) {
|
|
368
356
|
var tag = null;
|
|
369
357
|
//@ts-ignore
|
|
370
358
|
if (scil.Utils.endswith(s, c)) {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
359
|
+
var p = s.length - 1;
|
|
360
|
+
while (p > 0) {
|
|
361
|
+
p = s.lastIndexOf(c, p - 1);
|
|
362
|
+
if (p <= 0 || s.substring(p - 1, p) != '\\')
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
377
365
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
366
|
+
if (p > 0 && p < s.length - 1) {
|
|
367
|
+
tag = s.substring(p + 1, s.length - 1);
|
|
368
|
+
s = s.substring(0, p);
|
|
369
|
+
}
|
|
382
370
|
}
|
|
383
371
|
if (tag != null)
|
|
384
|
-
|
|
385
|
-
return {
|
|
372
|
+
tag = tag.replace(new RegExp('\\' + c, 'g'), c);
|
|
373
|
+
return {tag: unescape(tag), str: s};
|
|
386
374
|
}
|
|
387
375
|
|
|
388
376
|
function unescape(s: string) {
|
|
389
377
|
//@ts-ignore
|
|
390
378
|
if (scil.Utils.isNullOrEmpty(s))
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
return s.replace(/[\\]./g, function
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
379
|
+
return s;
|
|
380
|
+
|
|
381
|
+
return s.replace(/[\\]./g, function(m) {
|
|
382
|
+
switch (m) {
|
|
383
|
+
case '\\r':
|
|
384
|
+
return '\r';
|
|
385
|
+
case '\\n':
|
|
386
|
+
return '\n';
|
|
387
|
+
case '\\t':
|
|
388
|
+
return '\t';
|
|
389
|
+
default:
|
|
390
|
+
return m.substring(1);
|
|
391
|
+
}
|
|
404
392
|
});
|
|
405
393
|
}
|
|
406
394
|
|
|
407
|
-
|
|
395
|
+
//name: parseHelm
|
|
396
|
+
//input: string s
|
|
397
|
+
export function parseHelm(s: string) {
|
|
408
398
|
var sections = split(s, '$');
|
|
409
399
|
s = sections[0];
|
|
410
400
|
var monomers = [];
|
|
411
401
|
//@ts-ignore
|
|
412
402
|
if (!scil.Utils.isNullOrEmpty(s)) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
} else {
|
|
432
|
-
monomers.push(monomer);
|
|
433
|
-
}
|
|
403
|
+
var seqs = split(s, '|');
|
|
404
|
+
for (var i = 0; i < seqs.length; ++i) {
|
|
405
|
+
var e = detachAnnotation(seqs[i]);
|
|
406
|
+
s = e.str;
|
|
407
|
+
|
|
408
|
+
var p = s.indexOf('{');
|
|
409
|
+
|
|
410
|
+
s = s.substring(p + 1);
|
|
411
|
+
p = s.indexOf('}');
|
|
412
|
+
s = s.substring(0, p);
|
|
413
|
+
|
|
414
|
+
var ss = split(s, '.');
|
|
415
|
+
for (var monomer of ss) {
|
|
416
|
+
if (monomer.includes('(') && monomer.includes(')')) {
|
|
417
|
+
var elements = monomer.replace(/[()]/g, '').split('');
|
|
418
|
+
for (var el of elements) {
|
|
419
|
+
monomers.push(el);
|
|
434
420
|
}
|
|
421
|
+
} else if (monomer.includes('[') && monomer.includes(']')) {
|
|
422
|
+
var element = monomer.match(/(?<=\[).*?(?=\])/g, '');
|
|
423
|
+
monomers.push(element[0]);
|
|
424
|
+
} else {
|
|
425
|
+
monomers.push(monomer);
|
|
426
|
+
}
|
|
435
427
|
}
|
|
428
|
+
}
|
|
436
429
|
}
|
|
437
430
|
return monomers;
|
|
438
431
|
}
|
|
@@ -455,6 +448,89 @@ export function findMonomers(helmString: string) {
|
|
|
455
448
|
return new Set(split_string.filter(val => !monomer_names.includes(val)));
|
|
456
449
|
}
|
|
457
450
|
|
|
451
|
+
|
|
452
|
+
class MonomerLib implements bio.IMonomerLib {
|
|
453
|
+
private _monomers: { [type: string]: { [name: string]: bio.Monomer } };
|
|
454
|
+
private _onChanged = new Subject<any>();
|
|
455
|
+
|
|
456
|
+
get(monomerType: string, monomerName: string): bio.Monomer | null {
|
|
457
|
+
if (monomerType in this._monomers && monomerName in this._monomers[monomerType])
|
|
458
|
+
return this._monomers[monomerType][monomerName];
|
|
459
|
+
else
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
get onChanged(): Observable<any> {
|
|
464
|
+
return this._onChanged;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
public update(monomers: { [type: string]: { [name: string]: bio.Monomer } }): void {
|
|
468
|
+
this._monomers = monomers;
|
|
469
|
+
this._onChanged.next();
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function getAllLibsData(): { [type: string]: { [name: string]: bio.Monomer } } {
|
|
474
|
+
//@ts-ignore
|
|
475
|
+
const types = Object.keys(org.helm.webeditor.monomerTypeList());
|
|
476
|
+
//const monomers: any = [];
|
|
477
|
+
const monomers: { [type: string]: { [name: string]: bio.Monomer } } = {};
|
|
478
|
+
for (let i = 0; i < types.length; i++) {
|
|
479
|
+
monomers[types[i]] = new scil.helm.Monomers.getMonomerSet(types[i]);
|
|
480
|
+
//@ts-ignore
|
|
481
|
+
//monomers.push(new scil.helm.Monomers.getMonomerSet(types[i]));
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return monomers;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
//name: getMonomerLibObj
|
|
488
|
+
//description: Get object of IMonomerLib interface (singleton)
|
|
489
|
+
//output: object result
|
|
490
|
+
export function getMonomerLibObj(): bio.IMonomerLib {
|
|
491
|
+
if (!('monomerLib' in window)) {
|
|
492
|
+
window['monomerLib'] = new MonomerLib();
|
|
493
|
+
window['monomerLib'].update(getAllLibsData()); // TODO: refactor
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return window['monomerLib'];
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
//name: getMolfiles
|
|
500
|
+
//input: column col {semType: Macromolecule}
|
|
501
|
+
//output: column res
|
|
502
|
+
export function getMolfiles(col: DG.Column): DG.Column {
|
|
503
|
+
let grid = grok.shell.tv.grid;
|
|
504
|
+
let parent = grid.root.parentElement;
|
|
505
|
+
let res = DG.Column.string('mols', col.length);
|
|
506
|
+
const host = ui.div([]);
|
|
507
|
+
parent.appendChild(host);
|
|
508
|
+
let editor = new JSDraw2.Editor(host, {viewonly: true});
|
|
509
|
+
host.style.width = '0px';
|
|
510
|
+
host.style.height = '0px';
|
|
511
|
+
res.init((i) => {
|
|
512
|
+
editor.setHelm(col.get(i));
|
|
513
|
+
let mol = editor.getMolfile();
|
|
514
|
+
return mol;
|
|
515
|
+
});
|
|
516
|
+
parent.lastChild.remove();
|
|
517
|
+
return res;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function getParts(subParts: string[], s: string): string[] {
|
|
521
|
+
let j = 0;
|
|
522
|
+
let allParts: string[] = [];
|
|
523
|
+
for (let k = 0; k < subParts.length; ++k) {
|
|
524
|
+
let indexOfMonomer = s.indexOf(subParts[k]);
|
|
525
|
+
let helmBeforeMonomer = s.slice(j, indexOfMonomer);
|
|
526
|
+
allParts.push(helmBeforeMonomer);
|
|
527
|
+
allParts.push(subParts[k]);
|
|
528
|
+
s = s.substring(indexOfMonomer + subParts[k].length);
|
|
529
|
+
}
|
|
530
|
+
allParts.push(s);
|
|
531
|
+
return allParts;
|
|
532
|
+
}
|
|
533
|
+
|
|
458
534
|
class HelmCellRenderer extends DG.GridCellRenderer {
|
|
459
535
|
get name() { return 'helm'; }
|
|
460
536
|
|
|
@@ -464,6 +540,49 @@ class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
464
540
|
|
|
465
541
|
get defaultHeight(): number | null { return 100; }
|
|
466
542
|
|
|
543
|
+
onMouseMove(gridCell: DG.GridCell, e: MouseEvent): void {
|
|
544
|
+
const maxLengthWordsSum = gridCell.cell.column.temp['helm-sum-maxLengthWords'];
|
|
545
|
+
const maxIndex = Object.values(gridCell.cell.column.temp['helm-maxLengthWords']).length - 1;
|
|
546
|
+
const argsX = e.offsetX - gridCell.gridColumn.left + (gridCell.gridColumn.left - gridCell.bounds.x);
|
|
547
|
+
let left = 0;
|
|
548
|
+
let right = maxIndex;
|
|
549
|
+
let found = false;
|
|
550
|
+
maxLengthWordsSum[maxIndex + 1] = argsX + 1;
|
|
551
|
+
let mid = 0;
|
|
552
|
+
if (argsX > maxLengthWordsSum[0]) {
|
|
553
|
+
while (!found) {
|
|
554
|
+
mid = Math.floor((right + left) / 2);
|
|
555
|
+
if (argsX >= maxLengthWordsSum[mid] && argsX <= maxLengthWordsSum[mid + 1]) {
|
|
556
|
+
left = mid;
|
|
557
|
+
found = true;
|
|
558
|
+
} else if (argsX < maxLengthWordsSum[mid]) {
|
|
559
|
+
right = mid - 1;
|
|
560
|
+
} else if (argsX > maxLengthWordsSum[mid + 1]) {
|
|
561
|
+
left = mid + 1;
|
|
562
|
+
}
|
|
563
|
+
if (left == right) {
|
|
564
|
+
found = true;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
left = (argsX >= maxLengthWordsSum[left]) ? left + 1 : left;
|
|
569
|
+
const monomers = findMonomers(gridCell.cell.value);
|
|
570
|
+
let s: string = gridCell.cell.value ?? '';
|
|
571
|
+
let subParts: string[] = parseHelm(s);
|
|
572
|
+
let allParts: string[] = getParts(subParts, s);
|
|
573
|
+
let tooltipMessage: HTMLElement[] = [];
|
|
574
|
+
for (let i = 0; i < allParts.length; ++i) {
|
|
575
|
+
if (monomers.has(allParts[i]))
|
|
576
|
+
tooltipMessage[i] = ui.divV([
|
|
577
|
+
ui.divText(`Monomer ${allParts[i]} not found.`),
|
|
578
|
+
ui.divText('Open the Property Panel, then expand Manage Libraries')
|
|
579
|
+
]);
|
|
580
|
+
}
|
|
581
|
+
(((tooltipMessage[left]?.childNodes.length ?? 0) > 0))
|
|
582
|
+
? ui.tooltip.show(ui.div(tooltipMessage[left]), e.x + 16, e.y + 16)
|
|
583
|
+
: ui.tooltip.hide();
|
|
584
|
+
}
|
|
585
|
+
|
|
467
586
|
render(g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number,
|
|
468
587
|
gridCell: DG.GridCell, cellStyle: DG.GridCellStyle
|
|
469
588
|
) {
|
|
@@ -471,45 +590,47 @@ class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
471
590
|
const undefinedColor = 'rgb(100,100,100)';
|
|
472
591
|
const grayColor = '#808080';
|
|
473
592
|
const monomers = findMonomers(gridCell.cell.value);
|
|
593
|
+
let s: string = gridCell.cell.value ?? '';
|
|
594
|
+
let subParts: string[] = parseHelm(s);
|
|
474
595
|
if (monomers.size == 0) {
|
|
475
596
|
const host = ui.div([], {style: {width: `${w}px`, height: `${h}px`}});
|
|
476
597
|
host.setAttribute('dataformat', 'helm');
|
|
477
598
|
host.setAttribute('data', gridCell.cell.value);
|
|
478
599
|
gridCell.element = host;
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
600
|
+
//@ts-ignore
|
|
601
|
+
const canvas = new JSDraw2.Editor(host, {width: w, height: h, skin: 'w8', viewonly: true});
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
if (monomers.size > 0) {
|
|
605
|
+
w = grid ? Math.min(grid.canvas.width - x, w) : g.canvas.width - x;
|
|
606
|
+
g.save();
|
|
607
|
+
g.beginPath();
|
|
608
|
+
g.rect(x, y, w, h);
|
|
609
|
+
g.clip();
|
|
610
|
+
g.font = '12px monospace';
|
|
611
|
+
g.textBaseline = 'top';
|
|
612
|
+
let x1 = x;
|
|
613
|
+
let maxLengthWords: any = {};
|
|
614
|
+
let maxLengthWordSum: any = {};
|
|
615
|
+
let allParts: string[] = getParts(subParts, s);
|
|
616
|
+
for (let i = 0; i < allParts.length; ++i) {
|
|
617
|
+
maxLengthWords[i] = allParts[i].length * 7;
|
|
618
|
+
let color = monomers.has(allParts[i]) ? 'red' : grayColor;
|
|
619
|
+
g.fillStyle = undefinedColor;
|
|
620
|
+
x1 = bio.printLeftOrCentered(x1, y, w, h, g, allParts[i], color, 0, true, 1.0);
|
|
492
621
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
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;
|
|
622
|
+
|
|
623
|
+
maxLengthWordSum[0] = maxLengthWords[0];
|
|
624
|
+
for (let i = 1; i < allParts.length; i++) {
|
|
625
|
+
maxLengthWordSum[i] = maxLengthWordSum[i - 1] + maxLengthWords[i];
|
|
512
626
|
}
|
|
627
|
+
gridCell.cell.column.temp = {
|
|
628
|
+
'helm-sum-maxLengthWords': maxLengthWordSum,
|
|
629
|
+
'helm-maxLengthWords': maxLengthWords
|
|
630
|
+
};
|
|
631
|
+
g.restore();
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
513
634
|
}
|
|
514
635
|
}
|
|
515
636
|
|
|
@@ -517,5 +638,13 @@ class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
517
638
|
//input: string type
|
|
518
639
|
//output: string monomerSetJSON
|
|
519
640
|
export function getMonomerLib(type: string): string {
|
|
641
|
+
// TODO: deprecate
|
|
520
642
|
return JSON.stringify(scil.helm.Monomers.getMonomerSet(type));
|
|
521
643
|
}
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
//name: helmWebEditor
|
|
647
|
+
//output: object
|
|
648
|
+
export function helmWebEditor(): HelmWebEditor {
|
|
649
|
+
return new HelmWebEditor();
|
|
650
|
+
}
|