@datagrok/helm 2.1.19 → 2.1.21
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/CHANGELOG.md +2 -0
- package/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/files/tests/sample_HELM-types.csv +5 -0
- package/package.json +10 -7
- package/src/cell-renderer.ts +84 -45
- package/src/helm-monomer-placer.ts +17 -8
- package/src/package.ts +2 -2
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Helm,Protein,RNA,DNA
|
|
2
|
+
PEPTIDE1{I.H.A.N.T.Thr_PO3H2.Aca.D-Tyr_Et}$$$$,IHANTK,ACGU,ACGT
|
|
3
|
+
RNA1{r(A)p.r(C)p.r(G)p.r(U)p}$$$$,KHAQTRT,CGUG,CGGT
|
|
4
|
+
RNA1{d(A)p.d(C)p.d(G)p.d(U)p}$$$$,IHANTKT,CCGU,ACCT
|
|
5
|
+
RNA1{d(A)p.d(C)p.d(G)p.d(U)p}|PEPTIDE1{I.H.A.N.T.Thr_PO3H2}$$$$,PQRHLF,UAAA,CTAA
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagrok/helm",
|
|
3
3
|
"friendlyName": "Helm",
|
|
4
|
-
"version": "2.1.
|
|
4
|
+
"version": "2.1.21",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Oleksandra Serhiienko",
|
|
7
7
|
"email": "oserhiienko@datagrok.ai"
|
|
@@ -15,15 +15,16 @@
|
|
|
15
15
|
"helm/JSDraw/Pistoia.HELM-uncompressed.js"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@datagrok-libraries/bio": "^5.
|
|
18
|
+
"@datagrok-libraries/bio": "^5.39.6",
|
|
19
19
|
"@datagrok-libraries/utils": "^4.0.17",
|
|
20
20
|
"cash-dom": "^8.1.1",
|
|
21
21
|
"datagrok-api": "^1.10.2",
|
|
22
22
|
"dayjs": "^1.10.6",
|
|
23
|
-
"rxjs": "^6.5.5"
|
|
23
|
+
"rxjs": "^6.5.5",
|
|
24
|
+
"wu": "latest"
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|
|
26
|
-
"@
|
|
27
|
+
"@types/wu": "latest",
|
|
27
28
|
"path": "^0.12.7",
|
|
28
29
|
"source-map-loader": "^4.0.1",
|
|
29
30
|
"ts-loader": "^9.2.6",
|
|
@@ -33,10 +34,11 @@
|
|
|
33
34
|
"@typescript-eslint/eslint-plugin": "^5.11.0",
|
|
34
35
|
"@typescript-eslint/parser": "^5.11.0",
|
|
35
36
|
"eslint": "^8.18.0",
|
|
36
|
-
"eslint-config-google": "^0.14.0"
|
|
37
|
+
"eslint-config-google": "^0.14.0",
|
|
38
|
+
"@datagrok/bio": "^2.11.8"
|
|
37
39
|
},
|
|
38
40
|
"scripts": {
|
|
39
|
-
"link-all": "npm link datagrok-api
|
|
41
|
+
"link-all": "npm link datagrok-api @datagrok-libraries/utils @datagrok-libraries/bio",
|
|
40
42
|
"debug-helm": "webpack && grok publish",
|
|
41
43
|
"release-helm": "webpack && grok publish --release",
|
|
42
44
|
"build-helm": "webpack",
|
|
@@ -51,7 +53,8 @@
|
|
|
51
53
|
"lint-fix": "eslint \"./src/**/*.ts\" --fix",
|
|
52
54
|
"test": "grok test",
|
|
53
55
|
"test-dev": "grok test --host dev",
|
|
54
|
-
"test-local": "grok test --host localhost"
|
|
56
|
+
"test-local": "grok test --host localhost",
|
|
57
|
+
"build-all": "npm --prefix ./../../js-api run build && npm --prefix ./../../libraries/utils run build && npm --prefix ./../../libraries/bio run build && npm run build"
|
|
55
58
|
},
|
|
56
59
|
"canEdit": [
|
|
57
60
|
"Developers"
|
package/src/cell-renderer.ts
CHANGED
|
@@ -8,7 +8,8 @@ import {printLeftOrCentered} from '@datagrok-libraries/bio/src/utils/cell-render
|
|
|
8
8
|
import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
|
|
9
9
|
|
|
10
10
|
import {findMonomers, parseHelm} from './utils';
|
|
11
|
-
import {IEditor, HelmMonomerPlacer} from './helm-monomer-placer';
|
|
11
|
+
import {IEditor, HelmMonomerPlacer, IEditorMolAtom, ISeqMonomer} from './helm-monomer-placer';
|
|
12
|
+
import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
|
|
12
13
|
|
|
13
14
|
// import {_package} from './package'; // NullError: method not found: '_package' on null
|
|
14
15
|
|
|
@@ -24,10 +25,41 @@ const helmGapStartRe = /\{(\*\.)+/g;
|
|
|
24
25
|
const helmGapIntRe = /\.(\*\.)+/g;
|
|
25
26
|
const helmGapEndRe = /(\.\*)+\}/g;
|
|
26
27
|
|
|
28
|
+
function getSeqMonomerFromHelm(
|
|
29
|
+
helmPrefix: string, symbol: string, monomerLib: IMonomerLib
|
|
30
|
+
): ISeqMonomer {
|
|
31
|
+
let resSeqMonomer: ISeqMonomer | undefined = undefined;
|
|
32
|
+
for (const polymerType of monomerLib.getPolymerTypes()) {
|
|
33
|
+
if (helmPrefix.startsWith(polymerType))
|
|
34
|
+
resSeqMonomer = {symbol: symbol, polymerType: polymerType};
|
|
35
|
+
}
|
|
36
|
+
if (!resSeqMonomer)
|
|
37
|
+
resSeqMonomer = {symbol: symbol};
|
|
38
|
+
return resSeqMonomer;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getSeqMonomerFromHelmAtom(atom: IEditorMolAtom): ISeqMonomer {
|
|
42
|
+
let polymerType: string | undefined = undefined;
|
|
43
|
+
// @ts-ignore
|
|
44
|
+
switch (atom.bio.type) {
|
|
45
|
+
case 'HELM_BASE':
|
|
46
|
+
case 'HELM_SUGAR': // r - ribose, d - deoxyribose
|
|
47
|
+
case 'HELM_LINKER': // p - phosphate
|
|
48
|
+
polymerType = 'RNA';
|
|
49
|
+
break;
|
|
50
|
+
case 'HELM_AA':
|
|
51
|
+
polymerType = 'PEPTIDE';
|
|
52
|
+
break;
|
|
53
|
+
default:
|
|
54
|
+
polymerType = 'PEPTIDE';
|
|
55
|
+
}
|
|
56
|
+
return {symbol: atom.elem, polymerType: polymerType};
|
|
57
|
+
}
|
|
58
|
+
|
|
27
59
|
function getHoveredMonomerFromEditor(
|
|
28
60
|
argsX: number, argsY: number, gridCell: DG.GridCell, editor: IEditor
|
|
29
|
-
):
|
|
30
|
-
let
|
|
61
|
+
): ISeqMonomer | null {
|
|
62
|
+
let hoveredSeqMonomer: ISeqMonomer | null = null;
|
|
31
63
|
|
|
32
64
|
/** @return {[number, number]} [atom, distance] */
|
|
33
65
|
function getNearest(excluded: (number | undefined)[]): [number | undefined, number | undefined] {
|
|
@@ -64,21 +96,23 @@ function getHoveredMonomerFromEditor(
|
|
|
64
96
|
// }
|
|
65
97
|
|
|
66
98
|
if (firstAtomI !== undefined && firstDistance !== undefined) {
|
|
99
|
+
const firstAtom = editor.m.atoms[firstAtomI];
|
|
100
|
+
const firstSeqMonomer = getSeqMonomerFromHelmAtom(firstAtom);
|
|
67
101
|
if (secondAtomI !== undefined && secondDistance !== undefined) {
|
|
68
102
|
if (firstDistance < secondDistance * 0.45)
|
|
69
|
-
|
|
103
|
+
hoveredSeqMonomer = firstSeqMonomer;
|
|
70
104
|
} else {
|
|
71
105
|
if (firstDistance < 0.35 * gridCell.bounds.height)
|
|
72
|
-
|
|
106
|
+
hoveredSeqMonomer = firstSeqMonomer;
|
|
73
107
|
}
|
|
74
108
|
}
|
|
75
|
-
return
|
|
109
|
+
return hoveredSeqMonomer;
|
|
76
110
|
}
|
|
77
111
|
|
|
78
112
|
function getHoveredMonomerFallback(
|
|
79
113
|
argsX: number, _argsY: number, gridCell: DG.GridCell, helmPlacer: HelmMonomerPlacer
|
|
80
|
-
):
|
|
81
|
-
let
|
|
114
|
+
): ISeqMonomer | null {
|
|
115
|
+
let hoveredSeqMonomer: ISeqMonomer | null = null;
|
|
82
116
|
const [allParts, lengths, sumLengths] = helmPlacer.getCellAllPartsLengths(gridCell.tableRowIndex!);
|
|
83
117
|
const maxIndex = Object.values(lengths).length - 1;
|
|
84
118
|
let left = 0;
|
|
@@ -106,38 +140,39 @@ function getHoveredMonomerFallback(
|
|
|
106
140
|
}
|
|
107
141
|
left = (argsX >= sumLengths[left]) ? left : left - 1; // correct left to between sumLengths
|
|
108
142
|
if (left >= 0)
|
|
109
|
-
|
|
110
|
-
return
|
|
143
|
+
hoveredSeqMonomer = getSeqMonomerFromHelm(allParts[0], allParts[left], helmPlacer.monomerLib);
|
|
144
|
+
return hoveredSeqMonomer;
|
|
111
145
|
}
|
|
112
146
|
|
|
113
|
-
// window.dojox.gfx.svg.Text.prototype.getTextWidth hangs
|
|
114
|
-
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
147
|
+
// patch window.dojox.gfx.svg.Text.prototype.getTextWidth hangs
|
|
148
|
+
/** get the text width in pixels */
|
|
149
|
+
// @ts-ignore
|
|
150
|
+
window.dojox.gfx.svg.Text.prototype.getTextWidth = function() {
|
|
151
|
+
const rawNode = this.rawNode;
|
|
152
|
+
const oldParent = rawNode.parentNode;
|
|
153
|
+
const _measurementNode = rawNode.cloneNode(true);
|
|
154
|
+
_measurementNode.style.visibility = 'hidden';
|
|
155
|
+
|
|
156
|
+
// solution to the "orphan issue" in FF
|
|
157
|
+
let _width = 0;
|
|
158
|
+
const _text = _measurementNode.firstChild.nodeValue;
|
|
159
|
+
oldParent.appendChild(_measurementNode);
|
|
160
|
+
|
|
161
|
+
// solution to the "orphan issue" in Opera
|
|
162
|
+
// (nodeValue == "" hangs firefox)
|
|
163
|
+
if (_text != '') {
|
|
164
|
+
let watchdogCounter = 100;
|
|
165
|
+
while (!_width && --watchdogCounter > 0) { // <-- hangs
|
|
166
|
+
//Yang: work around svgweb bug 417 -- http://code.google.com/p/svgweb/issues/detail?id=417
|
|
167
|
+
if (_measurementNode.getBBox)
|
|
168
|
+
_width = parseInt(_measurementNode.getBBox().width);
|
|
169
|
+
else
|
|
170
|
+
_width = 68;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
oldParent.removeChild(_measurementNode);
|
|
174
|
+
return _width;
|
|
175
|
+
};
|
|
141
176
|
|
|
142
177
|
/** Helm cell renderer in case of no missed monomer draws with JSDraw2.Editor (webeditor),
|
|
143
178
|
* in case of missed monomers presented, draws linear sequences aligned in width per monomer.
|
|
@@ -163,9 +198,9 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
163
198
|
|
|
164
199
|
const helmPlacer = HelmMonomerPlacer.getOrCreate(tableCol);
|
|
165
200
|
const editor: IEditor | null = helmPlacer.getEditor(gridCell.tableRowIndex!);
|
|
166
|
-
const
|
|
201
|
+
const seqMonomer: ISeqMonomer | null = editor ? getHoveredMonomerFromEditor(argsX, argsY, gridCell, editor) :
|
|
167
202
|
getHoveredMonomerFallback(argsX, argsY, gridCell, helmPlacer);
|
|
168
|
-
if (!
|
|
203
|
+
if (!seqMonomer) {
|
|
169
204
|
ui.tooltip.hide();
|
|
170
205
|
return;
|
|
171
206
|
}
|
|
@@ -176,18 +211,18 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
176
211
|
const monomerList = parseHelm(seq);
|
|
177
212
|
const missedMonomers = findMonomers(monomerList);
|
|
178
213
|
|
|
179
|
-
if (missedMonomers.has(
|
|
214
|
+
if (missedMonomers.has(seqMonomer.symbol)) {
|
|
180
215
|
ui.tooltip.show(ui.divV([
|
|
181
|
-
ui.divText(`Monomer ${
|
|
216
|
+
ui.divText(`Monomer ${seqMonomer.symbol} not found.`),
|
|
182
217
|
ui.divText('Open the Context Panel, then expand Manage Libraries'),
|
|
183
218
|
]), e.x + 16, e.y + 16);
|
|
184
219
|
} else {
|
|
185
|
-
const monomer = helmPlacer.getMonomer(
|
|
220
|
+
const monomer = helmPlacer.getMonomer(seqMonomer);
|
|
186
221
|
if (monomer) {
|
|
187
222
|
const options = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
|
|
188
223
|
const monomerSvg = grok.chem.svgMol(monomer.smiles, undefined, undefined, options);
|
|
189
224
|
ui.tooltip.show(ui.divV([
|
|
190
|
-
ui.divText(
|
|
225
|
+
ui.divText(seqMonomer.symbol),
|
|
191
226
|
monomerSvg,
|
|
192
227
|
]), e.x + 16, e.y + 16);
|
|
193
228
|
}
|
|
@@ -218,6 +253,9 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
218
253
|
} catch (err: any) {
|
|
219
254
|
const errMsg: string = errorToConsole(err);
|
|
220
255
|
console.error('Helm: HelmCellRenderer.onMouseMove() error:\n' + errMsg);
|
|
256
|
+
} finally {
|
|
257
|
+
e.preventDefault();
|
|
258
|
+
e.stopPropagation();
|
|
221
259
|
}
|
|
222
260
|
}
|
|
223
261
|
|
|
@@ -246,7 +284,8 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
246
284
|
|
|
247
285
|
if (missedMonomers.size == 0) {
|
|
248
286
|
// Recreate host to avoid hanging in window.dojox.gfx.svg.Text.prototype.getTextWidth
|
|
249
|
-
const host = gridCell.element = ui.div([],
|
|
287
|
+
const host = gridCell.element = ui.div([],
|
|
288
|
+
{style: {width: `${w - 2}px`, height: `${h - 2}px`, margin: `${1}px`, backgroundColor: '#FFE0E0'}});
|
|
250
289
|
host.setAttribute('dataformat', 'helm');
|
|
251
290
|
host.setAttribute('data', seq /* gaps skipped */);
|
|
252
291
|
|
|
@@ -35,12 +35,17 @@ export interface IEditorPoint {
|
|
|
35
35
|
get y(): number;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
export interface ISeqMonomer {
|
|
39
|
+
symbol: string,
|
|
40
|
+
polymerType?: string
|
|
41
|
+
}
|
|
42
|
+
|
|
38
43
|
export class HelmMonomerPlacer {
|
|
39
|
-
private _allPartsList: (string[] | null)[];
|
|
40
|
-
private _lengthsList: (number[] | null)[];
|
|
41
|
-
private _editorList: (IEditor | null)[];
|
|
44
|
+
private readonly _allPartsList: (string[] | null)[];
|
|
45
|
+
private readonly _lengthsList: (number[] | null)[];
|
|
46
|
+
private readonly _editorList: (IEditor | null)[];
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
public readonly monomerLib: IMonomerLib;
|
|
44
49
|
|
|
45
50
|
public monomerCharWidth: number = 7;
|
|
46
51
|
public leftPadding: number = 5;
|
|
@@ -95,11 +100,15 @@ export class HelmMonomerPlacer {
|
|
|
95
100
|
return seq ? getParts(monomerList, seq) : [];
|
|
96
101
|
}
|
|
97
102
|
|
|
98
|
-
getMonomer(
|
|
103
|
+
getMonomer(monomer: ISeqMonomer): Monomer | null {
|
|
99
104
|
let res: Monomer | null = null;
|
|
100
|
-
|
|
101
|
-
res = this.monomerLib.getMonomer(polymerType,
|
|
102
|
-
|
|
105
|
+
if (monomer.polymerType)
|
|
106
|
+
res = this.monomerLib.getMonomer(monomer.polymerType, monomer.symbol);
|
|
107
|
+
else {
|
|
108
|
+
for (const polymerType of this.monomerLib.getPolymerTypes()) {
|
|
109
|
+
res = this.monomerLib.getMonomer(polymerType, monomer.symbol);
|
|
110
|
+
if (res) break;
|
|
111
|
+
}
|
|
103
112
|
}
|
|
104
113
|
return res;
|
|
105
114
|
}
|
package/src/package.ts
CHANGED
|
@@ -112,7 +112,7 @@ function rewriteLibraries() {
|
|
|
112
112
|
//name: helmCellRenderer
|
|
113
113
|
//tags: cellRenderer
|
|
114
114
|
//meta.cellType: helm
|
|
115
|
-
//meta.columnTags: units=helm
|
|
115
|
+
//meta.columnTags: quality=Macromolecule, units=helm
|
|
116
116
|
//output: grid_cell_renderer result
|
|
117
117
|
export function helmCellRenderer(): HelmCellRenderer {
|
|
118
118
|
return new HelmCellRenderer();
|
|
@@ -283,7 +283,7 @@ export function getMolfiles(col: DG.Column): DG.Column {
|
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
//name: helmWebEditor
|
|
286
|
-
//output: object
|
|
286
|
+
//output: object result
|
|
287
287
|
export function helmWebEditor(): HelmWebEditor {
|
|
288
288
|
return new HelmWebEditor();
|
|
289
289
|
}
|