@datagrok/helm 2.1.15 → 2.1.17
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/.eslintrc.json +11 -22
- package/CHANGELOG.md +22 -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/package.json +3 -3
- package/src/cell-renderer.ts +20 -21
- package/src/constants.ts +21 -21
- package/src/helm-monomer-placer.ts +38 -15
- package/src/helm-web-editor.ts +3 -0
- package/src/package.ts +61 -44
- package/src/utils.ts +3 -3
- package/tsconfig.json +1 -1
- package/webpack.config.js +2 -2
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.17",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Oleksandra Serhiienko",
|
|
7
7
|
"email": "oserhiienko@datagrok.ai"
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"helm/JSDraw/Pistoia.HELM-uncompressed.js"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@datagrok-libraries/bio": "^5.
|
|
19
|
-
"@datagrok-libraries/utils": "^
|
|
18
|
+
"@datagrok-libraries/bio": "^5.38.0",
|
|
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",
|
package/src/cell-renderer.ts
CHANGED
|
@@ -9,7 +9,6 @@ import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
|
|
|
9
9
|
|
|
10
10
|
import {findMonomers, parseHelm} from './utils';
|
|
11
11
|
import {HelmMonomerPlacer} from './helm-monomer-placer';
|
|
12
|
-
import {TAGS as helmTAGS} from './constants';
|
|
13
12
|
|
|
14
13
|
const enum tempTAGS {
|
|
15
14
|
helmSumMaxLengthWords = 'helm-sum-maxLengthWords',
|
|
@@ -45,7 +44,7 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
45
44
|
if (!tableCol) return;
|
|
46
45
|
|
|
47
46
|
const helmPlacer = HelmMonomerPlacer.getOrCreate(tableCol);
|
|
48
|
-
const [allParts, lengths, sumLengths] = helmPlacer.getCellAllPartsLengths(gridCell.tableRowIndex);
|
|
47
|
+
const [allParts, lengths, sumLengths] = helmPlacer.getCellAllPartsLengths(gridCell.tableRowIndex!);
|
|
49
48
|
|
|
50
49
|
const maxIndex = Object.values(lengths).length - 1;
|
|
51
50
|
const argsX = e.offsetX - gridCell.bounds.x;
|
|
@@ -61,11 +60,11 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
61
60
|
if (argsX >= sumLengths[mid] && argsX <= sumLengths[mid + 1]) {
|
|
62
61
|
left = mid;
|
|
63
62
|
found = true;
|
|
64
|
-
} else if (argsX < sumLengths[mid])
|
|
63
|
+
} else if (argsX < sumLengths[mid])
|
|
65
64
|
right = mid - 1;
|
|
66
|
-
|
|
65
|
+
else if (argsX > sumLengths[mid + 1])
|
|
67
66
|
left = mid + 1;
|
|
68
|
-
|
|
67
|
+
|
|
69
68
|
if (left == right)
|
|
70
69
|
found = true;
|
|
71
70
|
|
|
@@ -74,7 +73,9 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
74
73
|
}
|
|
75
74
|
left = (argsX >= sumLengths[left]) ? left : left - 1; // correct left to between sumLengths
|
|
76
75
|
|
|
77
|
-
const seq: string = gridCell.cell.value
|
|
76
|
+
const seq: string = !gridCell.cell.value ? '' : gridCell.cell.value
|
|
77
|
+
.replaceAll(helmGapStartRe, '{').replaceAll(helmGapIntRe, '.').replaceAll(helmGapEndRe, '}')
|
|
78
|
+
.replace('{*}', '{}');
|
|
78
79
|
const monomerList = parseHelm(seq);
|
|
79
80
|
const monomers = new Set<string>(monomerList);
|
|
80
81
|
const missedMonomers = findMonomers(monomerList);
|
|
@@ -110,27 +111,31 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
110
111
|
render(g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number,
|
|
111
112
|
gridCell: DG.GridCell, cellStyle: DG.GridCellStyle
|
|
112
113
|
) {
|
|
113
|
-
/* Can not do anything without tableColumn containing temp */
|
|
114
|
-
let tableCol: DG.Column | null = null;
|
|
115
|
-
try { tableCol = gridCell.tableColumn; } catch { }
|
|
116
|
-
if (!tableCol) return;
|
|
117
|
-
|
|
118
114
|
g.save();
|
|
119
115
|
try {
|
|
116
|
+
/* Can not do anything without tableColumn containing temp */
|
|
117
|
+
let tableCol: DG.Column | null = null;
|
|
118
|
+
try { tableCol = gridCell.tableColumn; } catch { }
|
|
119
|
+
if (!tableCol) return;
|
|
120
|
+
|
|
120
121
|
const grid = gridCell.gridRow !== -1 ? gridCell.grid : undefined;
|
|
121
122
|
const missedColor = 'red';
|
|
122
123
|
const monomerColor: string = '#404040';
|
|
123
124
|
const frameColor: string = '#C0C0C0';
|
|
124
125
|
|
|
125
|
-
const seq = gridCell.cell.value
|
|
126
|
-
|
|
126
|
+
const seq: string = !gridCell.cell.value ? '' : gridCell.cell.value
|
|
127
|
+
.replaceAll(helmGapStartRe, '{').replaceAll(helmGapIntRe, '.').replaceAll(helmGapEndRe, '}')
|
|
128
|
+
.replace('{*}', '{}');
|
|
129
|
+
const monomerList = parseHelm(seq);
|
|
127
130
|
const monomers: Set<string> = new Set<string>(monomerList);
|
|
128
131
|
const missedMonomers: Set<string> = findMonomers(monomerList);
|
|
132
|
+
const helmPlacer = HelmMonomerPlacer.getOrCreate(tableCol);
|
|
129
133
|
|
|
130
134
|
if (missedMonomers.size == 0) {
|
|
135
|
+
helmPlacer.skipCell(gridCell.tableRowIndex!);
|
|
131
136
|
const host = ui.div([], {style: {width: `${w}px`, height: `${h}px`}});
|
|
132
137
|
host.setAttribute('dataformat', 'helm');
|
|
133
|
-
host.setAttribute('data',
|
|
138
|
+
host.setAttribute('data', seq /* gaps skipped */);
|
|
134
139
|
gridCell.element = host;
|
|
135
140
|
//@ts-ignore
|
|
136
141
|
const canvas = new JSDraw2.Editor(host, {width: w, height: h, skin: 'w8', viewonly: true});
|
|
@@ -153,8 +158,7 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
153
158
|
g.transform(1, 0, 0, 1, x, y);
|
|
154
159
|
g.font = '12px monospace';
|
|
155
160
|
g.textBaseline = 'top';
|
|
156
|
-
const
|
|
157
|
-
const [allParts, lengths, sumLengths] = helmPlacer.getCellAllPartsLengths(gridCell.tableRow.idx);
|
|
161
|
+
const [allParts, lengths, sumLengths] = helmPlacer.getCellAllPartsLengths(gridCell.tableRowIndex!);
|
|
158
162
|
|
|
159
163
|
for (let i = 0; i < allParts.length; ++i) {
|
|
160
164
|
const part: string = allParts[i];
|
|
@@ -167,11 +171,6 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
167
171
|
printLeftOrCentered(sumLengths[i], 0, w, h, g, allParts[i], color, 0, true, 1.0);
|
|
168
172
|
}
|
|
169
173
|
}
|
|
170
|
-
} catch (err: any) {
|
|
171
|
-
const errMsg = err instanceof Error ? err.message : err.toString();
|
|
172
|
-
const errStack = err instanceof Error ? err.stack : undefined;
|
|
173
|
-
tableCol.setTag(helmTAGS.cellRendererRenderError, JSON.stringify({message: errMsg, stack: errStack}));
|
|
174
|
-
throw err;
|
|
175
174
|
} finally {
|
|
176
175
|
g.restore();
|
|
177
176
|
}
|
package/src/constants.ts
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
export const jsonSdfMonomerLibDict = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
2
|
+
'monomerType': null,
|
|
3
|
+
'smiles': null,
|
|
4
|
+
'name': 'MonomerName',
|
|
5
|
+
'author': null,
|
|
6
|
+
'molfile': 'molecule',
|
|
7
|
+
'naturalAnalog': 'MonomerNaturalAnalogCode',
|
|
8
|
+
'rgroups': 'MonomerCaps',
|
|
9
|
+
'createDate': null,
|
|
10
|
+
'id': null,
|
|
11
|
+
'polymerType': 'MonomerType',
|
|
12
|
+
'symbol': 'MonomerCode'
|
|
13
|
+
};
|
|
14
14
|
|
|
15
15
|
export type WebEditorMonomer = {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
/** symbol */ id: string,
|
|
17
|
+
/** name */ n: string,
|
|
18
|
+
/** natural analog */ na?: string,
|
|
19
|
+
/** polymer type */type: string,
|
|
20
|
+
/** monomer type */ mt: string,
|
|
21
|
+
/** molfile */ m: string,
|
|
22
|
+
/** substituents */ at: { [group: string]: string },
|
|
23
|
+
/** number of substituents */ rs: number
|
|
24
|
+
};
|
|
25
25
|
|
|
26
26
|
export const SMILES = 'smiles';
|
|
27
27
|
export const RGROUPS = 'rgroups';
|
|
@@ -14,8 +14,8 @@ export const enum Temps {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export class HelmMonomerPlacer {
|
|
17
|
-
private _allPartsList: string[][] = null;
|
|
18
|
-
private _lengthsList: number[][] = null;
|
|
17
|
+
private _allPartsList: (string[] | null)[] | null = null;
|
|
18
|
+
private _lengthsList: (number[] | null) [] | null = null;
|
|
19
19
|
|
|
20
20
|
private monomerLib: IMonomerLib;
|
|
21
21
|
|
|
@@ -25,15 +25,25 @@ export class HelmMonomerPlacer {
|
|
|
25
25
|
constructor(public readonly col: DG.Column<string>) {
|
|
26
26
|
this.col.dataFrame.onDataChanged.subscribe();
|
|
27
27
|
this.monomerLib = getMonomerLib();
|
|
28
|
+
this.monomerLib.onChanged.subscribe(this.monomerLibOnChanged.bind(this));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public skipCell(rowIdx: number): void {
|
|
32
|
+
if (this._allPartsList === null)
|
|
33
|
+
this._allPartsList = new Array<string[] | null>(this.col.length).fill(null);
|
|
34
|
+
if (this._lengthsList === null)
|
|
35
|
+
this._lengthsList = new Array<number[] | null>(this.col.length).fill(null);
|
|
36
|
+
|
|
37
|
+
this._allPartsList[rowIdx] = [];
|
|
38
|
+
this._lengthsList[rowIdx] = [];
|
|
28
39
|
}
|
|
29
40
|
|
|
30
41
|
/** @param rowIdx Row index of the table {@link DG.DataFrame}, HelmMonomerPlacer is {@link DG.Column} based */
|
|
31
42
|
public getCellAllPartsLengths(rowIdx: number): [string[], number[], number[]] {
|
|
32
43
|
if (this._allPartsList === null)
|
|
33
|
-
this._allPartsList = new Array<string[]>(this.col.length).fill(null);
|
|
34
|
-
|
|
44
|
+
this._allPartsList = new Array<string[] | null>(this.col.length).fill(null);
|
|
35
45
|
if (this._lengthsList === null)
|
|
36
|
-
this._lengthsList = new Array<number[]>(this.col.length).fill(null);
|
|
46
|
+
this._lengthsList = new Array<number[] | null>(this.col.length).fill(null);
|
|
37
47
|
|
|
38
48
|
const [allParts, lengths] = this.getCellMonomerLengthsForSeq(rowIdx);
|
|
39
49
|
|
|
@@ -45,12 +55,17 @@ export class HelmMonomerPlacer {
|
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
private getCellMonomerLengthsForSeq(rowIdx: number): [string[], number[]] {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
let allParts: string[] | null = this._allPartsList![rowIdx];
|
|
59
|
+
if (allParts === null)
|
|
60
|
+
allParts = this._allPartsList![rowIdx] = this.getAllParts(rowIdx);
|
|
61
|
+
|
|
62
|
+
let lengths: number[] | null = this._lengthsList![rowIdx];
|
|
63
|
+
if (lengths === null) {
|
|
64
|
+
lengths = this._lengthsList![rowIdx] = new Array<number>(allParts.length);
|
|
65
|
+
for (const [part, partI] of wu.enumerate(allParts)) {
|
|
66
|
+
const partWidth: number = part.length * this.monomerCharWidth;
|
|
67
|
+
lengths[partI] = partWidth;
|
|
68
|
+
}
|
|
54
69
|
}
|
|
55
70
|
|
|
56
71
|
return [allParts, lengths];
|
|
@@ -58,17 +73,25 @@ export class HelmMonomerPlacer {
|
|
|
58
73
|
|
|
59
74
|
getAllParts(rowIdx: number): string[] {
|
|
60
75
|
const seq: string | null = this.col.get(rowIdx);
|
|
61
|
-
const
|
|
62
|
-
return seq ? getParts(
|
|
76
|
+
const monomerList: string[] = seq ? parseHelm(seq) : [];
|
|
77
|
+
return seq ? getParts(monomerList, seq) : [];
|
|
63
78
|
}
|
|
64
79
|
|
|
65
80
|
getMonomer(monomerSymbol: any): Monomer | null {
|
|
66
|
-
let res: Monomer = null;
|
|
81
|
+
let res: Monomer | null = null;
|
|
67
82
|
for (const polymerType of this.monomerLib.getPolymerTypes()) {
|
|
68
83
|
res = this.monomerLib.getMonomer(polymerType, monomerSymbol);
|
|
69
84
|
if (res) break;
|
|
70
85
|
}
|
|
71
|
-
return res
|
|
86
|
+
return res;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// -- Handle events --
|
|
90
|
+
|
|
91
|
+
private monomerLibOnChanged(_value: any): void {
|
|
92
|
+
this._lengthsList = null;
|
|
93
|
+
this._allPartsList = null;
|
|
94
|
+
// TODO: Invalidate all grids of this.col.dataFrame
|
|
72
95
|
}
|
|
73
96
|
|
|
74
97
|
public static getOrCreate(col: DG.Column<string>): HelmMonomerPlacer {
|
package/src/helm-web-editor.ts
CHANGED
|
@@ -7,6 +7,7 @@ export class HelmWebEditor {
|
|
|
7
7
|
editor: any;
|
|
8
8
|
w = 200;
|
|
9
9
|
h = 100;
|
|
10
|
+
|
|
10
11
|
constructor() {
|
|
11
12
|
this.host = ui.div([], {style: {width: `${this.w}px`, height: `${this.h}px`}});
|
|
12
13
|
this.editor = new JSDraw2.Editor(this.host, {width: this.w, height: this.h, viewonly: true});
|
|
@@ -18,7 +19,9 @@ export class HelmWebEditor {
|
|
|
18
19
|
|
|
19
20
|
createWebEditor(value: string) {
|
|
20
21
|
const editorView = ui.div();
|
|
22
|
+
// @ts-ignore
|
|
21
23
|
org.helm.webeditor.MolViewer.molscale = 0.8;
|
|
24
|
+
// @ts-ignore
|
|
22
25
|
const webEditor = new scil.helm.App(editorView, {
|
|
23
26
|
showabout: false,
|
|
24
27
|
mexfontsize: '90%',
|
package/src/package.ts
CHANGED
|
@@ -7,14 +7,15 @@ import {WebEditorMonomer, RGROUP_CAP_GROUP_NAME, RGROUP_LABEL, SMILES} from './c
|
|
|
7
7
|
import {HelmWebEditor} from './helm-web-editor';
|
|
8
8
|
import {HelmCellRenderer} from './cell-renderer';
|
|
9
9
|
import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types';
|
|
10
|
-
import {
|
|
11
|
-
import {findMonomers} from './utils';
|
|
10
|
+
import {GapSymbols, UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
11
|
+
import {findMonomers, parseHelm} from './utils';
|
|
12
12
|
import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
|
|
13
13
|
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
14
14
|
|
|
15
|
-
export const _package = new DG.Package();
|
|
16
15
|
let monomerLib: IMonomerLib | null = null;
|
|
17
16
|
|
|
17
|
+
export const _package = new DG.Package();
|
|
18
|
+
|
|
18
19
|
//tags: init
|
|
19
20
|
export async function initHelm(): Promise<void> {
|
|
20
21
|
return Promise.all([
|
|
@@ -24,17 +25,17 @@ export async function initHelm(): Promise<void> {
|
|
|
24
25
|
}),
|
|
25
26
|
grok.functions.call('Bio:getBioLib'),
|
|
26
27
|
])
|
|
27
|
-
.then(([_, lib]: [
|
|
28
|
+
.then(([_, lib]: [unknown, IMonomerLib]) => {
|
|
28
29
|
monomerLib = lib;
|
|
29
30
|
rewriteLibraries(); // initHelm()
|
|
30
31
|
monomerLib.onChanged.subscribe((_) => {
|
|
31
32
|
try {
|
|
32
33
|
rewriteLibraries(); // initHelm()
|
|
33
34
|
|
|
34
|
-
const monTypeList: string[] = monomerLib
|
|
35
|
+
const monTypeList: string[] = monomerLib!.getPolymerTypes();
|
|
35
36
|
const msgStr: string = 'Monomer lib updated:<br />' + (
|
|
36
37
|
monTypeList.length == 0 ? 'empty' : monTypeList.map((monType) => {
|
|
37
|
-
return `${monType} ${monomerLib
|
|
38
|
+
return `${monType} ${monomerLib!.getMonomerSymbolsByType(monType).length}`;
|
|
38
39
|
}).join('<br />'));
|
|
39
40
|
|
|
40
41
|
grok.shell.info(msgStr);
|
|
@@ -55,16 +56,17 @@ export async function initHelm(): Promise<void> {
|
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
export function getMonomerLib(): IMonomerLib {
|
|
58
|
-
return monomerLib
|
|
59
|
+
return monomerLib!;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
function rewriteLibraries() {
|
|
63
|
+
// @ts-ignore
|
|
62
64
|
org.helm.webeditor.Monomers.clear();
|
|
63
|
-
monomerLib
|
|
64
|
-
const monomerSymbols = monomerLib
|
|
65
|
+
monomerLib!.getPolymerTypes().forEach((polymerType) => {
|
|
66
|
+
const monomerSymbols = monomerLib!.getMonomerSymbolsByType(polymerType);
|
|
65
67
|
monomerSymbols.forEach((monomerSymbol) => {
|
|
66
68
|
let isBroken = false;
|
|
67
|
-
const monomer: Monomer = monomerLib
|
|
69
|
+
const monomer: Monomer = monomerLib!.getMonomer(polymerType, monomerSymbol)!;
|
|
68
70
|
const webEditorMonomer: WebEditorMonomer = {
|
|
69
71
|
id: monomerSymbol,
|
|
70
72
|
m: monomer.molfile,
|
|
@@ -78,7 +80,7 @@ function rewriteLibraries() {
|
|
|
78
80
|
|
|
79
81
|
if (monomer.rgroups.length > 0) {
|
|
80
82
|
webEditorMonomer.rs = monomer.rgroups.length;
|
|
81
|
-
const at = {};
|
|
83
|
+
const at: { [prop: string]: any } = {};
|
|
82
84
|
monomer.rgroups.forEach((it) => {
|
|
83
85
|
at[it[RGROUP_LABEL]] = it[RGROUP_CAP_GROUP_NAME];
|
|
84
86
|
});
|
|
@@ -86,12 +88,13 @@ function rewriteLibraries() {
|
|
|
86
88
|
} else if (monomer[SMILES] != null) {
|
|
87
89
|
webEditorMonomer.rs = Object.keys(getRS(monomer[SMILES].toString())).length;
|
|
88
90
|
webEditorMonomer.at = getRS(monomer[SMILES].toString());
|
|
89
|
-
} else
|
|
91
|
+
} else
|
|
90
92
|
isBroken = true;
|
|
91
|
-
}
|
|
92
93
|
|
|
93
|
-
if (!isBroken)
|
|
94
|
+
if (!isBroken) {
|
|
95
|
+
// @ts-ignore
|
|
94
96
|
org.helm.webeditor.Monomers.addOneMonomer(webEditorMonomer);
|
|
97
|
+
}
|
|
95
98
|
});
|
|
96
99
|
});
|
|
97
100
|
|
|
@@ -110,10 +113,16 @@ export function helmCellRenderer(): HelmCellRenderer {
|
|
|
110
113
|
}
|
|
111
114
|
|
|
112
115
|
function checkMonomersAndOpenWebEditor(cell?: DG.Cell, value?: string, units?: string) {
|
|
113
|
-
const cellValue =
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
const cellValue: string = !!cell && units === undefined ? cell.value : value;
|
|
117
|
+
const monomerList: string[] = parseHelm(cellValue);
|
|
118
|
+
const monomers = findMonomers(monomerList);
|
|
119
|
+
if (monomers.size === 0)
|
|
120
|
+
webEditor(cell, value, units);
|
|
121
|
+
else if (monomers.size === 1 && monomers.has(GapSymbols[NOTATION.HELM]))
|
|
122
|
+
grok.shell.warning(`WebEditor doesn't support Helm with gaps '${GapSymbols[NOTATION.HELM]}'.`);
|
|
123
|
+
else {
|
|
124
|
+
grok.shell.warning(
|
|
125
|
+
`Monomers ${Array.from(monomers).map((m) => `'${m}'`).join(', ')} are absent! <br/>` +
|
|
117
126
|
`Please, upload the monomer library! <br/>` +
|
|
118
127
|
`<a href="https://datagrok.ai/help/domains/bio/macromolecules" target="_blank">Learn more</a>`);
|
|
119
128
|
}
|
|
@@ -133,13 +142,14 @@ export function editMoleculeCell(cell: DG.GridCell): void {
|
|
|
133
142
|
//input: string mol { semType: Macromolecule }
|
|
134
143
|
export function openEditor(mol: string): void {
|
|
135
144
|
const df = grok.shell.tv.grid.dataFrame;
|
|
136
|
-
const col = df.columns.bySemType('Macromolecule')
|
|
145
|
+
const col = df.columns.bySemType('Macromolecule')!;
|
|
146
|
+
const colUh = UnitsHandler.getOrCreate(col);
|
|
137
147
|
const colUnits = col.getTag(DG.TAGS.UNITS);
|
|
138
148
|
if (colUnits === NOTATION.HELM)
|
|
139
149
|
checkMonomersAndOpenWebEditor(df.currentCell, undefined, undefined);
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
checkMonomersAndOpenWebEditor(df.currentCell,
|
|
150
|
+
const convert = colUh.getConverter(NOTATION.HELM);
|
|
151
|
+
const helmMol = convert(mol);
|
|
152
|
+
checkMonomersAndOpenWebEditor(df.currentCell, helmMol, col.getTag(DG.TAGS.UNITS));
|
|
143
153
|
}
|
|
144
154
|
|
|
145
155
|
//name: Properties
|
|
@@ -148,7 +158,7 @@ export function openEditor(mol: string): void {
|
|
|
148
158
|
//output: widget result
|
|
149
159
|
export async function propertiesPanel(helmString: string) {
|
|
150
160
|
const grid = grok.shell.tv.grid;
|
|
151
|
-
const parent = grid.root.parentElement
|
|
161
|
+
const parent = grid.root.parentElement!;
|
|
152
162
|
const host = ui.div([]);
|
|
153
163
|
parent.appendChild(host);
|
|
154
164
|
const editor = new JSDraw2.Editor(host, {viewonly: true});
|
|
@@ -158,7 +168,7 @@ export async function propertiesPanel(helmString: string) {
|
|
|
158
168
|
const formula = editor.getFormula(true);
|
|
159
169
|
const molWeight = Math.round(editor.getMolWeight() * 100) / 100;
|
|
160
170
|
const coef = Math.round(editor.getExtinctionCoefficient(true) * 100) / 100;
|
|
161
|
-
parent.lastChild
|
|
171
|
+
parent.lastChild!.remove();
|
|
162
172
|
return new DG.Widget(
|
|
163
173
|
ui.tableFromMap({
|
|
164
174
|
'formula': formula.replace(/<sub>/g, '').replace(/<\/sub>/g, ''),
|
|
@@ -171,8 +181,11 @@ export async function propertiesPanel(helmString: string) {
|
|
|
171
181
|
function webEditor(cell?: DG.Cell, value?: string, units?: string) {
|
|
172
182
|
const view = ui.div();
|
|
173
183
|
const df = grok.shell.tv.grid.dataFrame;
|
|
174
|
-
const
|
|
184
|
+
const col = df.columns.bySemType('Macromolecule')!;
|
|
185
|
+
const uh = UnitsHandler.getOrCreate(col);
|
|
186
|
+
// @ts-ignore
|
|
175
187
|
org.helm.webeditor.MolViewer.molscale = 0.8;
|
|
188
|
+
// @ts-ignore
|
|
176
189
|
const app = new scil.helm.App(view, {
|
|
177
190
|
showabout: false,
|
|
178
191
|
mexfontsize: '90%',
|
|
@@ -196,7 +209,7 @@ function webEditor(cell?: DG.Cell, value?: string, units?: string) {
|
|
|
196
209
|
app.structureview.resize(sizes.rightwidth, sizes.bottomheight + app.toolbarheight);
|
|
197
210
|
app.mex.resize(sizes.topheight - 80);
|
|
198
211
|
setTimeout(() => {
|
|
199
|
-
if (
|
|
212
|
+
if (!!cell && units === undefined)
|
|
200
213
|
app.canvas.helm.setSequence(cell.value, 'HELM');
|
|
201
214
|
else
|
|
202
215
|
app.canvas.helm.setSequence(value, 'HELM');
|
|
@@ -207,31 +220,35 @@ function webEditor(cell?: DG.Cell, value?: string, units?: string) {
|
|
|
207
220
|
.onOK(() => {
|
|
208
221
|
const helmValue = app.canvas.getHelm(true).replace(/<\/span>/g, '')
|
|
209
222
|
.replace(/<span style='background:#bbf;'>/g, '');
|
|
210
|
-
if (
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
223
|
+
if (!!cell) {
|
|
224
|
+
if (units === undefined)
|
|
225
|
+
cell.value = helmValue;
|
|
226
|
+
else {
|
|
227
|
+
const convertedRes = uh.convertHelmToFastaSeparator(helmValue, units!);
|
|
228
|
+
cell.value = convertedRes;
|
|
229
|
+
}
|
|
215
230
|
}
|
|
216
231
|
}).show({modal: true, fullScreen: true});
|
|
217
232
|
}
|
|
218
233
|
|
|
219
234
|
function getRS(smiles: string) {
|
|
220
235
|
const newS = smiles.match(/(?<=\[)[^\][]*(?=])/gm);
|
|
221
|
-
const res = {};
|
|
236
|
+
const res: { [name: string]: string } = {};
|
|
222
237
|
let el = '';
|
|
223
238
|
let digit;
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
239
|
+
if (!!newS) {
|
|
240
|
+
for (let i = 0; i < newS.length; i++) {
|
|
241
|
+
if (newS[i] != null) {
|
|
242
|
+
if (/\d/.test(newS[i])) {
|
|
243
|
+
digit = newS[i][newS[i].length - 1];
|
|
244
|
+
newS[i] = newS[i].replace(/[0-9]/g, '');
|
|
245
|
+
for (let j = 0; j < newS[i].length; j++) {
|
|
246
|
+
if (newS[i][j] != ':')
|
|
247
|
+
el += newS[i][j];
|
|
248
|
+
}
|
|
249
|
+
res['R' + digit] = el;
|
|
250
|
+
el = '';
|
|
232
251
|
}
|
|
233
|
-
res['R' + digit] = el;
|
|
234
|
-
el = '';
|
|
235
252
|
}
|
|
236
253
|
}
|
|
237
254
|
}
|
|
@@ -243,7 +260,7 @@ function getRS(smiles: string) {
|
|
|
243
260
|
//output: column res
|
|
244
261
|
export function getMolfiles(col: DG.Column): DG.Column {
|
|
245
262
|
const grid = grok.shell.tv.grid;
|
|
246
|
-
const parent = grid.root.parentElement
|
|
263
|
+
const parent = grid.root.parentElement!;
|
|
247
264
|
const res = DG.Column.string('mols', col.length);
|
|
248
265
|
const host = ui.div([]);
|
|
249
266
|
parent.appendChild(host);
|
|
@@ -255,7 +272,7 @@ export function getMolfiles(col: DG.Column): DG.Column {
|
|
|
255
272
|
const mol = editor.getMolfile();
|
|
256
273
|
return mol;
|
|
257
274
|
});
|
|
258
|
-
parent.lastChild
|
|
275
|
+
parent.lastChild!.remove();
|
|
259
276
|
return res;
|
|
260
277
|
}
|
|
261
278
|
|
package/src/utils.ts
CHANGED
|
@@ -24,7 +24,7 @@ export function getParts(subParts: string[], s: string): string[] {
|
|
|
24
24
|
return allParts;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function parseHelm(s: string) {
|
|
27
|
+
export function parseHelm(s: string): string[] {
|
|
28
28
|
const sections = split(s, '$');
|
|
29
29
|
s = sections[0];
|
|
30
30
|
const monomers = [];
|
|
@@ -181,8 +181,8 @@ function _detachAppendix(s: string, c: string) {
|
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
if (tag != null)
|
|
184
|
-
tag = tag.replace(new RegExp('\\' + c, 'g'), c);
|
|
185
|
-
return {tag:
|
|
184
|
+
tag = unescape(tag.replace(new RegExp('\\' + c, 'g'), c));
|
|
185
|
+
return {tag: tag, str: s};
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
function unescape(s: string) {
|
package/tsconfig.json
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
|
26
26
|
|
|
27
27
|
/* Strict Type-Checking Options */
|
|
28
|
-
"strict":
|
|
28
|
+
"strict": true, /* Enable all strict type-checking options. */
|
|
29
29
|
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
|
30
30
|
// "strictNullChecks": true, /* Enable strict null checks. */
|
|
31
31
|
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
package/webpack.config.js
CHANGED
|
@@ -13,11 +13,11 @@ module.exports = {
|
|
|
13
13
|
},
|
|
14
14
|
resolve: {
|
|
15
15
|
fallback: {'url': false},
|
|
16
|
-
extensions: ['.
|
|
16
|
+
extensions: ['.ts', '.tsx', '.js', '.wasm', '.mjs', '.json'],
|
|
17
17
|
},
|
|
18
18
|
module: {
|
|
19
19
|
rules: [
|
|
20
|
-
{test: /\.js$/, enforce: 'pre', use: ['source-map-loader']},
|
|
20
|
+
{test: /\.js$/, enforce: 'pre', use: ['source-map-loader'], exclude: /node_modules/},
|
|
21
21
|
{test: /\.ts(x?)$/, use: 'ts-loader', exclude: /node_modules/},
|
|
22
22
|
{test: /\.css$/, use: ['style-loader', 'css-loader'], exclude: /node_modules/},
|
|
23
23
|
],
|