@datagrok/sequence-translator 1.0.17 → 1.1.0
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 +4 -3
- package/CHANGELOG.md +3 -0
- package/detectors.js +8 -0
- package/dist/package-test.js +2 -73079
- package/dist/package-test.js.map +1 -0
- package/dist/package.js +2 -72284
- package/dist/package.js.map +1 -0
- package/files/axolabs-style.json +97 -0
- package/files/codes-to-symbols.json +66 -0
- package/files/formats-to-helm.json +59 -0
- package/files/linkers.json +22 -0
- package/files/monomer-lib.json +1094 -0
- package/link-bio +7 -0
- package/package.json +30 -28
- package/scripts/build-monomer-lib.py +391 -122
- package/src/demo/demo-st-ui.ts +71 -0
- package/src/demo/handle-error.ts +12 -0
- package/src/model/axolabs/axolabs-tab.ts +111 -0
- package/src/model/axolabs/const.ts +33 -0
- package/src/{axolabs-tab → model/axolabs}/draw-svg.ts +1 -1
- package/src/{axolabs-tab → model/axolabs}/helpers.ts +7 -5
- package/src/model/const.ts +19 -0
- package/src/model/data-loading-utils/const.ts +8 -0
- package/src/model/data-loading-utils/json-loader.ts +38 -0
- package/src/model/data-loading-utils/types.ts +30 -0
- package/src/model/format-translation/const.ts +8 -0
- package/src/model/format-translation/conversion-utils.ts +48 -0
- package/src/model/format-translation/format-converter.ts +107 -0
- package/src/model/helpers.ts +12 -0
- package/src/model/monomer-lib/const.ts +3 -0
- package/src/model/monomer-lib/lib-wrapper.ts +106 -0
- package/src/model/parsing-validation/format-detector.ts +57 -0
- package/src/model/parsing-validation/sequence-validator.ts +52 -0
- package/src/model/sequence-to-structure-utils/const.ts +1 -0
- package/src/{utils/structures-works → model/sequence-to-structure-utils}/mol-transformations.ts +33 -41
- package/src/model/sequence-to-structure-utils/monomer-code-parser.ts +92 -0
- package/src/model/sequence-to-structure-utils/sdf-tab.ts +94 -0
- package/src/model/sequence-to-structure-utils/sequence-to-molfile.ts +409 -0
- package/src/package.ts +104 -92
- package/src/tests/const.ts +17 -0
- package/src/tests/smiles-tests.ts +32 -457
- package/src/view/const/main-tab.ts +3 -0
- package/src/view/const/view.ts +10 -0
- package/src/view/css/axolabs-tab.css +1 -0
- package/src/view/css/colored-text-input.css +27 -0
- package/src/view/css/main-tab.css +46 -0
- package/src/view/css/sdf-tab.css +39 -0
- package/src/view/monomer-lib-viewer/viewer.ts +22 -0
- package/src/view/tabs/axolabs.ts +720 -0
- package/src/view/tabs/main.ts +174 -0
- package/src/view/tabs/sdf.ts +173 -0
- package/src/view/utils/app-info-dialog.ts +18 -0
- package/src/view/utils/colored-input/colored-text-input.ts +56 -0
- package/src/view/utils/colored-input/input-painters.ts +44 -0
- package/src/view/utils/draw-molecule.ts +86 -0
- package/src/view/utils/molecule-img.ts +106 -0
- package/src/view/view.ts +129 -0
- package/tsconfig.json +12 -18
- package/webpack.config.js +17 -4
- package/README.md +0 -84
- package/css/style.css +0 -18
- package/img/Sequence Translator Axolabs.png +0 -0
- package/jest.config.js +0 -33
- package/setup-unlink-clean.cmd +0 -14
- package/setup-unlink-clean.sh +0 -21
- package/setup.cmd +0 -14
- package/setup.sh +0 -37
- package/src/__jest__/remote.test.ts +0 -77
- package/src/__jest__/test-node.ts +0 -97
- package/src/apps/oligo-sd-file-app.ts +0 -58
- package/src/autostart/calculations.ts +0 -40
- package/src/autostart/constants.ts +0 -37
- package/src/autostart/registration.ts +0 -306
- package/src/axolabs-tab/axolabs-tab.ts +0 -873
- package/src/axolabs-tab/define-pattern.ts +0 -874
- package/src/hardcode-to-be-eliminated/ICDs.ts +0 -3
- package/src/hardcode-to-be-eliminated/IDPs.ts +0 -3
- package/src/hardcode-to-be-eliminated/const.ts +0 -5
- package/src/hardcode-to-be-eliminated/constants.ts +0 -101
- package/src/hardcode-to-be-eliminated/converters.ts +0 -323
- package/src/hardcode-to-be-eliminated/map.ts +0 -720
- package/src/hardcode-to-be-eliminated/salts.ts +0 -2
- package/src/hardcode-to-be-eliminated/sources.ts +0 -3
- package/src/hardcode-to-be-eliminated/users.ts +0 -3
- package/src/main-tab/main-tab.ts +0 -210
- package/src/sdf-tab/sdf-tab.ts +0 -163
- package/src/sdf-tab/sequence-codes-tools.ts +0 -347
- package/src/utils/const.ts +0 -0
- package/src/utils/helpers.ts +0 -28
- package/src/utils/parse.ts +0 -27
- package/src/utils/sdf-add-columns.ts +0 -118
- package/src/utils/sdf-save-table.ts +0 -56
- package/src/utils/structures-works/draw-molecule.ts +0 -84
- package/src/utils/structures-works/from-monomers.ts +0 -266
- package/test-SequenceTranslator-6288c2fbe346-695b7b55.html +0 -259
- package/vendors/openchemlib-full.js +0 -293
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
map, SYNTHESIZERS, TECHNOLOGIES, MODIFICATIONS, DELIMITER, gcrsCodesWithoutSmiles, NUCLEOTIDES
|
|
3
|
-
} from '../hardcode-to-be-eliminated/map';
|
|
4
|
-
import {sortByStringLengthInDescendingOrder} from '../utils/helpers';
|
|
5
|
-
import {
|
|
6
|
-
asoGapmersNucleotidesToBioSpring, asoGapmersNucleotidesToGcrs,
|
|
7
|
-
asoGapmersBioSpringToNucleotides, asoGapmersBioSpringToGcrs, gcrsToMermade12, siRnaNucleotideToBioSpringSenseStrand,
|
|
8
|
-
siRnaNucleotideToAxolabsSenseStrand, siRnaNucleotidesToGcrs, siRnaBioSpringToNucleotides,
|
|
9
|
-
siRnaBioSpringToAxolabs, siRnaBioSpringToGcrs, siRnaAxolabsToNucleotides,
|
|
10
|
-
siRnaAxolabsToBioSpring, siRnaAxolabsToGcrs, siRnaGcrsToNucleotides,
|
|
11
|
-
siRnaGcrsToBioSpring, siRnaGcrsToAxolabs, gcrsToNucleotides, gcrsToLcms
|
|
12
|
-
} from '../hardcode-to-be-eliminated/converters';
|
|
13
|
-
|
|
14
|
-
const noTranslationTableAvailable = 'No translation table available';
|
|
15
|
-
export const undefinedInputSequence = 'Type of input sequence is undefined';
|
|
16
|
-
|
|
17
|
-
export function getFormat(sequence: string): string | null {
|
|
18
|
-
const possibleSynthesizers = getListOfPossibleSynthesizersByFirstMatchedCode(sequence);
|
|
19
|
-
|
|
20
|
-
if (possibleSynthesizers.length == 0)
|
|
21
|
-
return null;
|
|
22
|
-
|
|
23
|
-
let outputIndex = 0;
|
|
24
|
-
|
|
25
|
-
const firstUniqueCharacters = ['r', 'd'];
|
|
26
|
-
|
|
27
|
-
possibleSynthesizers.forEach((synthesizer) => {
|
|
28
|
-
const codes = getAllCodesOfSynthesizer(synthesizer);
|
|
29
|
-
while (outputIndex < sequence.length) {
|
|
30
|
-
const matchedCode = codes.find((c) => c == sequence.slice(outputIndex, outputIndex + c.length));
|
|
31
|
-
|
|
32
|
-
if (matchedCode == null)
|
|
33
|
-
break;
|
|
34
|
-
|
|
35
|
-
if ( // for mistake pattern 'rAA'
|
|
36
|
-
outputIndex > 1 &&
|
|
37
|
-
NUCLEOTIDES.includes(sequence[outputIndex]) &&
|
|
38
|
-
firstUniqueCharacters.includes(sequence[outputIndex - 2])
|
|
39
|
-
) break;
|
|
40
|
-
|
|
41
|
-
if ( // for mistake pattern 'ArA'
|
|
42
|
-
firstUniqueCharacters.includes(sequence[outputIndex + 1]) &&
|
|
43
|
-
NUCLEOTIDES.includes(sequence[outputIndex])
|
|
44
|
-
) {
|
|
45
|
-
outputIndex++;
|
|
46
|
-
break;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
outputIndex += matchedCode.length;
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const indexOfFirstNotValidChar = (outputIndex == sequence.length) ? -1 : outputIndex;
|
|
54
|
-
if (indexOfFirstNotValidChar != -1)
|
|
55
|
-
return possibleSynthesizers[0];
|
|
56
|
-
|
|
57
|
-
const possibleTechnologies = getListOfPossibleTechnologiesByFirstMatchedCode(sequence, possibleSynthesizers[0]);
|
|
58
|
-
|
|
59
|
-
if (possibleTechnologies.length == 0)
|
|
60
|
-
return null;
|
|
61
|
-
|
|
62
|
-
outputIndex = 0;
|
|
63
|
-
|
|
64
|
-
possibleTechnologies.forEach((technology: string) => {
|
|
65
|
-
const codes = Object.keys(map[possibleSynthesizers[0]][technology]);
|
|
66
|
-
while (outputIndex < sequence.length) {
|
|
67
|
-
const matchedCode = codes.find((c) => c == sequence.slice(outputIndex, outputIndex + c.length));
|
|
68
|
-
|
|
69
|
-
if (matchedCode == null)
|
|
70
|
-
break;
|
|
71
|
-
|
|
72
|
-
if ( // for mistake pattern 'rAA'
|
|
73
|
-
outputIndex > 1 &&
|
|
74
|
-
NUCLEOTIDES.includes(sequence[outputIndex]) &&
|
|
75
|
-
firstUniqueCharacters.includes(sequence[outputIndex - 2])
|
|
76
|
-
) break;
|
|
77
|
-
|
|
78
|
-
if ( // for mistake pattern 'ArA'
|
|
79
|
-
firstUniqueCharacters.includes(sequence[outputIndex + 1]) &&
|
|
80
|
-
NUCLEOTIDES.includes(sequence[outputIndex])
|
|
81
|
-
) {
|
|
82
|
-
outputIndex++;
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
outputIndex += matchedCode.length;
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
return possibleSynthesizers[0];
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
export function isValidSequence(sequence: string, format: string | null): {
|
|
95
|
-
indexOfFirstNotValidChar: number,
|
|
96
|
-
synthesizer: string[] | null,
|
|
97
|
-
// technology: string[] | null
|
|
98
|
-
} {
|
|
99
|
-
const possibleSynthesizers = format == null ?
|
|
100
|
-
getListOfPossibleSynthesizersByFirstMatchedCode(sequence) :
|
|
101
|
-
[format];
|
|
102
|
-
|
|
103
|
-
// if (possibleSynthesizers.length > 1) {
|
|
104
|
-
// const synthesizer = ui.choiceInput('Choose synthesizer from list: ', possibleSynthesizers[0],
|
|
105
|
-
// possibleSynthesizers);
|
|
106
|
-
// ui.dialog('Choose Synthesizer')
|
|
107
|
-
// .add(ui.panel([synthesizer.root], {style: {fontWeight: 'bold'}}))
|
|
108
|
-
// .onOK(() => possibleSynthesizers = [synthesizer.value])
|
|
109
|
-
// .onCancel(() => {
|
|
110
|
-
// possibleSynthesizers = [possibleSynthesizers[0]];
|
|
111
|
-
// grok.shell.warning('Input sequence is expected to be in format ' + possibleSynthesizers[0]);
|
|
112
|
-
// })
|
|
113
|
-
// .show();
|
|
114
|
-
// } else if (possibleSynthesizers.length == 0)
|
|
115
|
-
if (possibleSynthesizers.length == 0)
|
|
116
|
-
return {indexOfFirstNotValidChar: 0, synthesizer: null};//, technology: null};
|
|
117
|
-
|
|
118
|
-
const outputIndices = Array(possibleSynthesizers.length).fill(0);
|
|
119
|
-
|
|
120
|
-
const firstUniqueCharacters = ['r', 'd'];
|
|
121
|
-
possibleSynthesizers.forEach(function(synthesizer, i) {
|
|
122
|
-
const codes = sortByStringLengthInDescendingOrder(getAllCodesOfSynthesizer(synthesizer));
|
|
123
|
-
while (outputIndices[i] < sequence.length) {
|
|
124
|
-
const matchedCode = codes.find((c) => c == sequence.slice(outputIndices[i], outputIndices[i] + c.length));
|
|
125
|
-
|
|
126
|
-
if (matchedCode == null)
|
|
127
|
-
break;
|
|
128
|
-
|
|
129
|
-
if ( // for mistake pattern 'rAA'
|
|
130
|
-
outputIndices[i] > 1 &&
|
|
131
|
-
NUCLEOTIDES.includes(sequence[outputIndices[i]]) &&
|
|
132
|
-
firstUniqueCharacters.includes(sequence[outputIndices[i] - 2])
|
|
133
|
-
) break;
|
|
134
|
-
|
|
135
|
-
if ( // for mistake pattern 'ArA'
|
|
136
|
-
firstUniqueCharacters.includes(sequence[outputIndices[i] + 1]) &&
|
|
137
|
-
NUCLEOTIDES.includes(sequence[outputIndices[i]])
|
|
138
|
-
) {
|
|
139
|
-
outputIndices[i]++;
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
outputIndices[i] += matchedCode.length;
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
const outputIndex = Math.max(...outputIndices);
|
|
148
|
-
const synthesizer = possibleSynthesizers[outputIndices.indexOf(outputIndex)];
|
|
149
|
-
const indexOfFirstNotValidChar = (outputIndex == sequence.length) ? -1 : outputIndex;
|
|
150
|
-
if (indexOfFirstNotValidChar != -1) {
|
|
151
|
-
return {
|
|
152
|
-
indexOfFirstNotValidChar: indexOfFirstNotValidChar,
|
|
153
|
-
synthesizer: [synthesizer],
|
|
154
|
-
// technology: null,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// const possibleTechnologies =
|
|
159
|
-
// getListOfPossibleTechnologiesByFirstMatchedCode(sequence, possibleSynthesizers[outputIndex]);
|
|
160
|
-
|
|
161
|
-
// if (possibleTechnologies.length > 1) {
|
|
162
|
-
// const technology = ui.choiceInput('Choose technology from list: ', possibleTechnologies[0],
|
|
163
|
-
// possibleTechnologies);
|
|
164
|
-
// ui.dialog('Choose Technology')
|
|
165
|
-
// .add(ui.panel([technology.root], {style: {fontWeight: 'bold'}}))
|
|
166
|
-
// .onOK(() => possibleTechnologies = [technology.value])
|
|
167
|
-
// .onCancel(() => {
|
|
168
|
-
// possibleTechnologies = [possibleTechnologies[0]];
|
|
169
|
-
// grok.shell.warning('Input sequence is expected to be in format ' + possibleTechnologies[0]);
|
|
170
|
-
// })
|
|
171
|
-
// .show();
|
|
172
|
-
// } else if (possibleTechnologies.length == 0)
|
|
173
|
-
// if (possibleTechnologies.length == 0)
|
|
174
|
-
// return {indexOfFirstNotValidChar: 0, synthesizer: [possibleSynthesizers[3]], technology: null};
|
|
175
|
-
|
|
176
|
-
// outputIndex = 0;
|
|
177
|
-
|
|
178
|
-
// possibleTechnologies.forEach((technology: string) => {
|
|
179
|
-
// const codes = Object.keys(map[possibleSynthesizers[0]][technology]);
|
|
180
|
-
// while (outputIndex < sequence.length) {
|
|
181
|
-
// const matchedCode = codes.find((c) => c == sequence.slice(outputIndex, outputIndex + c.length));
|
|
182
|
-
|
|
183
|
-
// if (matchedCode == null)
|
|
184
|
-
// break;
|
|
185
|
-
|
|
186
|
-
// if ( // for mistake pattern 'rAA'
|
|
187
|
-
// outputIndex > 1 &&
|
|
188
|
-
// nucleotides.includes(sequence[outputIndex]) &&
|
|
189
|
-
// firstUniqueCharacters.includes(sequence[outputIndex - 2])
|
|
190
|
-
// ) break;
|
|
191
|
-
|
|
192
|
-
// if ( // for mistake pattern 'ArA'
|
|
193
|
-
// firstUniqueCharacters.includes(sequence[outputIndex + 1]) &&
|
|
194
|
-
// nucleotides.includes(sequence[outputIndex])
|
|
195
|
-
// ) {
|
|
196
|
-
// outputIndex++;
|
|
197
|
-
// break;
|
|
198
|
-
// }
|
|
199
|
-
|
|
200
|
-
// outputIndex += matchedCode.length;
|
|
201
|
-
// }
|
|
202
|
-
// });
|
|
203
|
-
|
|
204
|
-
return {
|
|
205
|
-
indexOfFirstNotValidChar: indexOfFirstNotValidChar,
|
|
206
|
-
synthesizer: [synthesizer],
|
|
207
|
-
// technology: [possibleTechnologies[0]],
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export function getAllCodesOfSynthesizer(synthesizer: string): string[] {
|
|
212
|
-
let codes: string[] = [];
|
|
213
|
-
for (const technology of Object.keys(map[synthesizer]))
|
|
214
|
-
codes = codes.concat(Object.keys(map[synthesizer][technology]));
|
|
215
|
-
return codes.concat(Object.keys(MODIFICATIONS)).concat(DELIMITER);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function getListOfPossibleSynthesizersByFirstMatchedCode(sequence: string): string[] {
|
|
219
|
-
let synthesizers: string[] = [];
|
|
220
|
-
Object.keys(map).forEach((synthesizer: string) => {
|
|
221
|
-
let codes = sortByStringLengthInDescendingOrder(getAllCodesOfSynthesizer(synthesizer));
|
|
222
|
-
if (synthesizer == 'Janssen GCRS Codes')
|
|
223
|
-
codes = codes.concat(gcrsCodesWithoutSmiles);
|
|
224
|
-
//TODO: get first non-dropdown code when there are two modifications
|
|
225
|
-
let start = 0;
|
|
226
|
-
for (let i = 0; i < sequence.length; i++) {
|
|
227
|
-
if (sequence[i] == ')' && i != sequence.length - 1) {
|
|
228
|
-
start = i + 1;
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
if (gcrsCodesWithoutSmiles.some((s: string) => s == sequence.slice(start, start + s.length)))
|
|
233
|
-
synthesizers = ['Janssen GCRS Codes'];
|
|
234
|
-
if (codes.some((s: string) => s == sequence.slice(start, start + s.length)))
|
|
235
|
-
synthesizers.push(synthesizer);
|
|
236
|
-
});
|
|
237
|
-
return synthesizers;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function getListOfPossibleTechnologiesByFirstMatchedCode(sequence: string, synthesizer: string): string[] {
|
|
241
|
-
const technologies: string[] = [];
|
|
242
|
-
Object.keys(map[synthesizer]).forEach((technology: string) => {
|
|
243
|
-
const codes = Object.keys(map[synthesizer][technology]).concat(Object.keys(MODIFICATIONS));
|
|
244
|
-
if (codes.some((s) => s == sequence.slice(0, s.length)))
|
|
245
|
-
technologies.push(technology);
|
|
246
|
-
});
|
|
247
|
-
return technologies;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
export function convertSequence(sequence: string, output: {
|
|
251
|
-
indexOfFirstNotValidChar: number, synthesizer: string[] | null}) {//, technology: string[] | null}) {
|
|
252
|
-
if (output.indexOfFirstNotValidChar != -1) {
|
|
253
|
-
return {
|
|
254
|
-
// type: '',
|
|
255
|
-
indexOfFirstNotValidChar: JSON.stringify(output),
|
|
256
|
-
Error: undefinedInputSequence,
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
if (output.synthesizer!.includes(SYNTHESIZERS.RAW_NUCLEOTIDES)) {//&& output.technology!.includes(TECHNOLOGIES.DNA)) {
|
|
260
|
-
return {
|
|
261
|
-
type: SYNTHESIZERS.RAW_NUCLEOTIDES, // + ' ' + TECHNOLOGIES.DNA,
|
|
262
|
-
Nucleotides: sequence,
|
|
263
|
-
BioSpring: asoGapmersNucleotidesToBioSpring(sequence),
|
|
264
|
-
GCRS: asoGapmersNucleotidesToGcrs(sequence),
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
if (output.synthesizer!.includes(SYNTHESIZERS.BIOSPRING)) {
|
|
268
|
-
// && output.technology!.includes(TECHNOLOGIES.ASO_GAPMERS)) {
|
|
269
|
-
return {
|
|
270
|
-
type: SYNTHESIZERS.BIOSPRING + ' ' + TECHNOLOGIES.ASO_GAPMERS,
|
|
271
|
-
Nucleotides: asoGapmersBioSpringToNucleotides(sequence),
|
|
272
|
-
BioSpring: sequence,
|
|
273
|
-
GCRS: asoGapmersBioSpringToGcrs(sequence),
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
if (output.synthesizer!.includes(SYNTHESIZERS.GCRS)) { // && output.technology!.includes(TECHNOLOGIES.ASO_GAPMERS)) {
|
|
277
|
-
return {
|
|
278
|
-
type: SYNTHESIZERS.GCRS + ' ' + TECHNOLOGIES.ASO_GAPMERS,
|
|
279
|
-
Nucleotides: gcrsToNucleotides(sequence),
|
|
280
|
-
BioSpring: siRnaGcrsToBioSpring(sequence),
|
|
281
|
-
Axolabs: siRnaGcrsToAxolabs(sequence),
|
|
282
|
-
Mermade12: gcrsToMermade12(sequence),
|
|
283
|
-
GCRS: sequence,
|
|
284
|
-
LCMS: gcrsToLcms(sequence),
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
if (output.synthesizer!.includes(SYNTHESIZERS.RAW_NUCLEOTIDES)) {
|
|
288
|
-
// && output.technology!.includes(TECHNOLOGIES.RNA)) {
|
|
289
|
-
return {
|
|
290
|
-
type: SYNTHESIZERS.RAW_NUCLEOTIDES + ' ' + TECHNOLOGIES.RNA,
|
|
291
|
-
Nucleotides: sequence,
|
|
292
|
-
BioSpring: siRnaNucleotideToBioSpringSenseStrand(sequence),
|
|
293
|
-
Axolabs: siRnaNucleotideToAxolabsSenseStrand(sequence),
|
|
294
|
-
GCRS: siRnaNucleotidesToGcrs(sequence),
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
if (output.synthesizer!.includes(SYNTHESIZERS.BIOSPRING)) { // && output.technology!.includes(TECHNOLOGIES.SI_RNA)) {
|
|
298
|
-
return {
|
|
299
|
-
type: SYNTHESIZERS.BIOSPRING + ' ' + TECHNOLOGIES.SI_RNA,
|
|
300
|
-
Nucleotides: siRnaBioSpringToNucleotides(sequence),
|
|
301
|
-
BioSpring: sequence,
|
|
302
|
-
Axolabs: siRnaBioSpringToAxolabs(sequence),
|
|
303
|
-
GCRS: siRnaBioSpringToGcrs(sequence),
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
if (output.synthesizer!.includes(SYNTHESIZERS.AXOLABS)) {
|
|
307
|
-
return {
|
|
308
|
-
type: SYNTHESIZERS.AXOLABS + ' ' + TECHNOLOGIES.SI_RNA,
|
|
309
|
-
Nucleotides: siRnaAxolabsToNucleotides(sequence),
|
|
310
|
-
BioSpring: siRnaAxolabsToBioSpring(sequence),
|
|
311
|
-
Axolabs: sequence,
|
|
312
|
-
GCRS: siRnaAxolabsToGcrs(sequence),
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
if (output.synthesizer!.includes(SYNTHESIZERS.GCRS)) { // && output.technology!.includes(TECHNOLOGIES.SI_RNA)) {
|
|
316
|
-
return {
|
|
317
|
-
type: SYNTHESIZERS.GCRS + ' ' + TECHNOLOGIES.SI_RNA,
|
|
318
|
-
Nucleotides: siRnaGcrsToNucleotides(sequence),
|
|
319
|
-
BioSpring: siRnaGcrsToBioSpring(sequence),
|
|
320
|
-
Axolabs: siRnaGcrsToAxolabs(sequence),
|
|
321
|
-
MM12: gcrsToMermade12(sequence),
|
|
322
|
-
GCRS: sequence,
|
|
323
|
-
LCMS: gcrsToLcms(sequence),
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
if (output.synthesizer!.includes(SYNTHESIZERS.GCRS)) {
|
|
327
|
-
return {
|
|
328
|
-
type: SYNTHESIZERS.GCRS,
|
|
329
|
-
Nucleotides: gcrsToNucleotides(sequence),
|
|
330
|
-
GCRS: sequence,
|
|
331
|
-
Mermade12: gcrsToMermade12(sequence),
|
|
332
|
-
LCMS: gcrsToLcms(sequence),
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
if (output.synthesizer!.includes(SYNTHESIZERS.MERMADE_12)) {
|
|
336
|
-
return {
|
|
337
|
-
type: SYNTHESIZERS.MERMADE_12,
|
|
338
|
-
Nucleotides: noTranslationTableAvailable,
|
|
339
|
-
GCRS: noTranslationTableAvailable,
|
|
340
|
-
Mermade12: sequence,
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
return {
|
|
344
|
-
type: undefinedInputSequence,
|
|
345
|
-
Nucleotides: undefinedInputSequence,
|
|
346
|
-
};
|
|
347
|
-
}
|
package/src/utils/const.ts
DELETED
|
File without changes
|
package/src/utils/helpers.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import * as DG from 'datagrok-api/dg';
|
|
2
|
-
|
|
3
|
-
export function sortByStringLengthInDescendingOrder(array: string[]): string[] {
|
|
4
|
-
return array.sort(function(a, b) { return b.length - a.length; });
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function stringify(items: string[]): string {
|
|
8
|
-
return '["' + items.join('", "') + '"]';
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function download(name: string, href: string): void {
|
|
12
|
-
const element = document.createElement('a');
|
|
13
|
-
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + href);
|
|
14
|
-
element.setAttribute('download', name);
|
|
15
|
-
element.click();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function removeEmptyRows(t: DG.DataFrame, colToCheck: DG.Column): DG.DataFrame {
|
|
19
|
-
for (let i = t.rowCount - 1; i > -1; i--) {
|
|
20
|
-
if (colToCheck.getString(i) === '')
|
|
21
|
-
t.rows.removeAt(i, 1, false);
|
|
22
|
-
}
|
|
23
|
-
return t;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function differenceOfTwoArrays(a: string[], b: string[]): string[] {
|
|
27
|
-
return a.filter((x) => !b.includes(x));
|
|
28
|
-
}
|
package/src/utils/parse.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
export const CELL_STRUCTURE = {
|
|
2
|
-
DUPLEX: {
|
|
3
|
-
BEFORE_SS: 'SS ',
|
|
4
|
-
BEFORE_AS: '\r\nAS ',
|
|
5
|
-
},
|
|
6
|
-
TRIPLEX_OR_DIMER: {
|
|
7
|
-
BEFORE_SS: 'SS ',
|
|
8
|
-
BEFORE_AS1: '\r\nAS1 ',
|
|
9
|
-
BEFORE_AS2: '\r\nAS2 ',
|
|
10
|
-
},
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export function parseStrandsFromDuplexCell(s: string): { SS: string, AS: string } {
|
|
14
|
-
const arr = s
|
|
15
|
-
.slice(CELL_STRUCTURE.DUPLEX.BEFORE_SS.length)
|
|
16
|
-
.split(CELL_STRUCTURE.DUPLEX.BEFORE_AS);
|
|
17
|
-
return {SS: arr[0], AS: arr[1]};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function parseStrandsFromTriplexOrDimerCell(s: string): { SS: string, AS1: string, AS2: string } {
|
|
21
|
-
const arr1 = s
|
|
22
|
-
.slice(CELL_STRUCTURE.TRIPLEX_OR_DIMER.BEFORE_SS.length)
|
|
23
|
-
.split(CELL_STRUCTURE.TRIPLEX_OR_DIMER.BEFORE_AS1);
|
|
24
|
-
const arr2 = arr1[1]
|
|
25
|
-
.split(CELL_STRUCTURE.TRIPLEX_OR_DIMER.BEFORE_AS2);
|
|
26
|
-
return {SS: arr1[0], AS1: arr2[0], AS2: arr2[1]};
|
|
27
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import * as DG from 'datagrok-api/dg';
|
|
2
|
-
import {COL_NAMES, GENERATED_COL_NAMES, SEQUENCE_TYPES} from '../autostart/constants';
|
|
3
|
-
import * as grok from 'datagrok-api/grok';
|
|
4
|
-
import {removeEmptyRows} from '../utils/helpers';
|
|
5
|
-
import {parseStrandsFromDuplexCell, parseStrandsFromTriplexOrDimerCell} from './parse';
|
|
6
|
-
import {isValidSequence} from '../sdf-tab/sequence-codes-tools';
|
|
7
|
-
import {batchMolWeight, molecularWeight, saltMass, saltMolWeigth} from '../autostart/calculations';
|
|
8
|
-
import {weightsObj} from '../hardcode-to-be-eliminated/map';
|
|
9
|
-
|
|
10
|
-
export class SdfColumnsExistsError extends Error {
|
|
11
|
-
constructor(message: string) {
|
|
12
|
-
super();
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function sdfAddColumns(
|
|
17
|
-
df: DG.DataFrame, saltNamesList: string[], saltsMolWeightList: number[], onError: (rowI: number, err: any) => void
|
|
18
|
-
): DG.DataFrame {
|
|
19
|
-
const sequenceCol = df.getCol(COL_NAMES.SEQUENCE);
|
|
20
|
-
const saltCol = df.getCol(COL_NAMES.SALT);
|
|
21
|
-
const equivalentsCol = df.getCol(COL_NAMES.EQUIVALENTS);
|
|
22
|
-
const typeCol = df.getCol(COL_NAMES.TYPE);
|
|
23
|
-
const chemistryNameCol = df.getCol(COL_NAMES.CHEMISTRY_NAME);
|
|
24
|
-
|
|
25
|
-
if (GENERATED_COL_NAMES.some((colName) => df.columns.contains(colName)))
|
|
26
|
-
throw new SdfColumnsExistsError('Columns already exist');
|
|
27
|
-
|
|
28
|
-
df = removeEmptyRows(df, sequenceCol);
|
|
29
|
-
|
|
30
|
-
df.columns.addNewString(COL_NAMES.COMPOUND_NAME).init((i: number) => {
|
|
31
|
-
let res: string = '';
|
|
32
|
-
try {
|
|
33
|
-
res = ([SEQUENCE_TYPES.DUPLEX, SEQUENCE_TYPES.DIMER, SEQUENCE_TYPES.TRIPLEX].includes(typeCol.get(i))) ?
|
|
34
|
-
chemistryNameCol.get(i) :
|
|
35
|
-
sequenceCol.get(i);
|
|
36
|
-
} catch (err) {
|
|
37
|
-
onError(i, err);
|
|
38
|
-
}
|
|
39
|
-
return res;
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
df.columns.addNewString(COL_NAMES.COMPOUND_COMMENTS).init((i: number) => {
|
|
43
|
-
let res: string = '';
|
|
44
|
-
try {
|
|
45
|
-
if ([SEQUENCE_TYPES.SENSE_STRAND, SEQUENCE_TYPES.ANTISENSE_STRAND].includes(typeCol.get(i))) {
|
|
46
|
-
res = sequenceCol.get(i);
|
|
47
|
-
} else if (typeCol.get(i) == SEQUENCE_TYPES.DUPLEX) {
|
|
48
|
-
const obj = parseStrandsFromDuplexCell(sequenceCol.get(i));
|
|
49
|
-
res = `${chemistryNameCol.get(i)}; duplex of SS: ${obj.SS} and AS: ${obj.AS}`;
|
|
50
|
-
} else if ([SEQUENCE_TYPES.DIMER, SEQUENCE_TYPES.TRIPLEX].includes(typeCol.get(i))) {
|
|
51
|
-
const obj = parseStrandsFromTriplexOrDimerCell(sequenceCol.get(i));
|
|
52
|
-
res = `${chemistryNameCol.get(i)}; duplex of SS: ${obj.SS} and AS1: ${obj.AS1} and AS2: ${obj.AS2}`;
|
|
53
|
-
}
|
|
54
|
-
} catch (err) {
|
|
55
|
-
onError(i, err);
|
|
56
|
-
}
|
|
57
|
-
return res;
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
df.columns.addNewFloat(COL_NAMES.COMPOUND_MOL_WEIGHT).init((i: number) => {
|
|
61
|
-
let res: number = Number.NaN;
|
|
62
|
-
try {
|
|
63
|
-
if ([SEQUENCE_TYPES.SENSE_STRAND, SEQUENCE_TYPES.ANTISENSE_STRAND].includes(typeCol.get(i))) {
|
|
64
|
-
res = (isValidSequence(sequenceCol.get(i), null).indexOfFirstNotValidChar == -1) ?
|
|
65
|
-
molecularWeight(sequenceCol.get(i), weightsObj) :
|
|
66
|
-
DG.FLOAT_NULL;
|
|
67
|
-
} else if (typeCol.get(i) == SEQUENCE_TYPES.DUPLEX) {
|
|
68
|
-
const obj = parseStrandsFromDuplexCell(sequenceCol.get(i));
|
|
69
|
-
res = (Object.values(obj).every((seq) => isValidSequence(seq, null).indexOfFirstNotValidChar == -1)) ?
|
|
70
|
-
molecularWeight(obj.SS, weightsObj) + molecularWeight(obj.AS, weightsObj) :
|
|
71
|
-
DG.FLOAT_NULL;
|
|
72
|
-
} else if ([SEQUENCE_TYPES.DIMER, SEQUENCE_TYPES.TRIPLEX].includes(typeCol.get(i))) {
|
|
73
|
-
const obj = parseStrandsFromTriplexOrDimerCell(sequenceCol.get(i));
|
|
74
|
-
res = (Object.values(obj).every((seq) => isValidSequence(seq, null).indexOfFirstNotValidChar == -1)) ?
|
|
75
|
-
molecularWeight(obj.SS, weightsObj) + molecularWeight(obj.AS1, weightsObj) +
|
|
76
|
-
molecularWeight(obj.AS2, weightsObj) :
|
|
77
|
-
DG.FLOAT_NULL;
|
|
78
|
-
}
|
|
79
|
-
} catch (err) {
|
|
80
|
-
onError(i, err);
|
|
81
|
-
}
|
|
82
|
-
return res;
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
df.columns.addNewFloat(COL_NAMES.SALT_MASS).init((i: number) => {
|
|
86
|
-
let res: number = Number.NaN;
|
|
87
|
-
try {
|
|
88
|
-
res = saltMass(saltNamesList, saltsMolWeightList, equivalentsCol, i, saltCol);
|
|
89
|
-
} catch (err) {
|
|
90
|
-
onError(i, err);
|
|
91
|
-
}
|
|
92
|
-
return res;
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
df.columns.addNewFloat(COL_NAMES.SALT_MOL_WEIGHT).init((i: number) => {
|
|
96
|
-
let res: number = Number.NaN;
|
|
97
|
-
try {
|
|
98
|
-
res = saltMolWeigth(saltNamesList, saltCol, saltsMolWeightList, i);
|
|
99
|
-
} catch (err) {
|
|
100
|
-
onError(i, err);
|
|
101
|
-
}
|
|
102
|
-
return res;
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
const compoundMolWeightCol = df.getCol(COL_NAMES.COMPOUND_MOL_WEIGHT);
|
|
106
|
-
const saltMassCol = df.getCol(COL_NAMES.SALT_MASS);
|
|
107
|
-
df.columns.addNewFloat(COL_NAMES.BATCH_MOL_WEIGHT).init((i: number) => {
|
|
108
|
-
let res: number = Number.NaN;
|
|
109
|
-
try {
|
|
110
|
-
res = batchMolWeight(compoundMolWeightCol, saltMassCol, i);
|
|
111
|
-
} catch (err) {
|
|
112
|
-
onError(i, err);
|
|
113
|
-
}
|
|
114
|
-
return res;
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
return df;
|
|
118
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import * as DG from 'datagrok-api/dg';
|
|
2
|
-
import {COL_NAMES, GENERATED_COL_NAMES, SEQUENCE_TYPES} from '../autostart/constants';
|
|
3
|
-
import {differenceOfTwoArrays, download} from '../utils/helpers';
|
|
4
|
-
import * as grok from 'datagrok-api/grok';
|
|
5
|
-
import {SYNTHESIZERS} from '../hardcode-to-be-eliminated/map';
|
|
6
|
-
import {sequenceToMolV3000} from '../utils/structures-works/from-monomers';
|
|
7
|
-
import {parseStrandsFromDuplexCell, parseStrandsFromTriplexOrDimerCell} from './parse';
|
|
8
|
-
import {linkStrandsV3000} from '../utils/structures-works/mol-transformations';
|
|
9
|
-
|
|
10
|
-
export async function sdfSaveTable(table: DG.DataFrame, onError: (rowI: number, err: any) => void) {
|
|
11
|
-
if (GENERATED_COL_NAMES.some((colName) => !table.columns.contains(colName))) {
|
|
12
|
-
const absentColNames = differenceOfTwoArrays(GENERATED_COL_NAMES, table.columns.names()).join(`', '`);
|
|
13
|
-
grok.shell.warning(`File saved without columns '${absentColNames}'`);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const sequenceCol = table.getCol(COL_NAMES.SEQUENCE);
|
|
17
|
-
const typeCol = table.getCol(COL_NAMES.TYPE);
|
|
18
|
-
|
|
19
|
-
let resultStr = '';
|
|
20
|
-
const rowCount = table.rowCount;
|
|
21
|
-
for (let i = 0; i < rowCount; i++) {
|
|
22
|
-
try {
|
|
23
|
-
let rowStr = '';
|
|
24
|
-
const format = SYNTHESIZERS.GCRS; //getFormat(sequenceCol.get(i))!;
|
|
25
|
-
if (typeCol.get(i) == SEQUENCE_TYPES.SENSE_STRAND) {
|
|
26
|
-
rowStr += `${sequenceToMolV3000(sequenceCol.get(i), false, true, format)}\n> <Sequence>\nSense Strand\n\n`;
|
|
27
|
-
} else if (typeCol.get(i) == SEQUENCE_TYPES.ANTISENSE_STRAND) {
|
|
28
|
-
rowStr += `${sequenceToMolV3000(sequenceCol.get(i), true, true, format)}\n> <Sequence>\nAnti Sense\n\n`;
|
|
29
|
-
} else if (typeCol.get(i) == SEQUENCE_TYPES.DUPLEX) {
|
|
30
|
-
const obj = parseStrandsFromDuplexCell(sequenceCol.get(i));
|
|
31
|
-
const as = `${sequenceToMolV3000(obj.AS, true, true, format)}\n> <Sequence>\nAnti Sense\n\n`;
|
|
32
|
-
const ss = `${sequenceToMolV3000(obj.SS, false, true, format)}\n> <Sequence>\nSense Strand\n\n`;
|
|
33
|
-
rowStr += `${linkStrandsV3000({senseStrands: [ss], antiStrands: [as]}, true)}\n\n`;
|
|
34
|
-
} else if ([SEQUENCE_TYPES.TRIPLEX, SEQUENCE_TYPES.DIMER].includes(typeCol.get(i))) {
|
|
35
|
-
const obj = parseStrandsFromTriplexOrDimerCell(sequenceCol.get(i));
|
|
36
|
-
const as1 = `${sequenceToMolV3000(obj.AS1, true, true, format)}\n> <Sequence>\nAnti Sense\n\n`;
|
|
37
|
-
const as2 = `${sequenceToMolV3000(obj.AS2, true, true, format)}\n> <Sequence>\nAnti Sense\n\n`;
|
|
38
|
-
const ss = `${sequenceToMolV3000(obj.SS, false, true, format)}\n> <Sequence>\nSense Strand\n\n`;
|
|
39
|
-
rowStr += `${linkStrandsV3000({senseStrands: [ss], antiStrands: [as1, as2]}, true)}\n\n`;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
for (const col of table.columns) {
|
|
43
|
-
if (col.name != COL_NAMES.SEQUENCE)
|
|
44
|
-
rowStr += `> <${col.name}>\n${col.get(i)}\n\n`;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
rowStr += '$$$$\n';
|
|
48
|
-
|
|
49
|
-
resultStr += rowStr;
|
|
50
|
-
} catch (err: any) {
|
|
51
|
-
onError(i, err);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
download(`${table.name}.sdf`, encodeURIComponent(resultStr));
|
|
56
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/* Do not change these import lines to match external modules in webpack configuration */
|
|
2
|
-
import * as grok from 'datagrok-api/grok';
|
|
3
|
-
import * as ui from 'datagrok-api/ui';
|
|
4
|
-
import * as DG from 'datagrok-api/dg';
|
|
5
|
-
import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
|
|
6
|
-
|
|
7
|
-
import $ from 'cash-dom';
|
|
8
|
-
|
|
9
|
-
import {extractAtomDataV3000} from './mol-transformations';
|
|
10
|
-
|
|
11
|
-
/** Draw molecule on the canvas and append it to the specified div, with the
|
|
12
|
-
* option of zoom-in */
|
|
13
|
-
export async function drawMolecule(
|
|
14
|
-
moleculeImgDiv: HTMLDivElement,
|
|
15
|
-
canvasWidth: number, canvasHeight: number,
|
|
16
|
-
molfile: string
|
|
17
|
-
): Promise<void> {
|
|
18
|
-
// clear the div's content if any
|
|
19
|
-
async function drawMolfileOnCanvas(canvas: HTMLCanvasElement): Promise<void> {
|
|
20
|
-
await grok.functions.call('Chem:canvasMol', {
|
|
21
|
-
x: 0, y: 0, w: canvas.width, h: canvas.height, canvas: canvas,
|
|
22
|
-
molString: molfile, scaffoldMolString: '',
|
|
23
|
-
options: {normalizeDepiction: false, straightenDepiction: false}
|
|
24
|
-
});
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
async function drawZoomedInMolecule(): Promise<void> {
|
|
28
|
-
try {
|
|
29
|
-
const dialogDivStyle = {
|
|
30
|
-
overflowX: 'scroll',
|
|
31
|
-
};
|
|
32
|
-
const dialogDiv = ui.div([], {style: dialogDivStyle});
|
|
33
|
-
|
|
34
|
-
// dialogDiv size required, but now available before dialog show()
|
|
35
|
-
const atomCoordinates = extractAtomDataV3000(molfile);
|
|
36
|
-
// const cw: number = $(window).width() * 0.80; // dialogDiv.clientWidth
|
|
37
|
-
const clientHeight: number = $(window).height() * 0.70; // dialogDiv.clientHeight
|
|
38
|
-
const molWidth: number = Math.max(...atomCoordinates.x) - Math.min(...atomCoordinates.x);
|
|
39
|
-
const molHeight: number = Math.max(...atomCoordinates.y) - Math.min(...atomCoordinates.y);
|
|
40
|
-
|
|
41
|
-
// const wR: number = cw / molWidth;
|
|
42
|
-
const hR: number = clientHeight / molHeight;
|
|
43
|
-
const r: number = hR; // Math.max(wR, hR);
|
|
44
|
-
const dialogCanvasWidth = r * molWidth;
|
|
45
|
-
const dialogCanvasHeight = r * molHeight;
|
|
46
|
-
|
|
47
|
-
const dialogCanvas = ui.canvas(
|
|
48
|
-
dialogCanvasWidth * window.devicePixelRatio, dialogCanvasHeight * window.devicePixelRatio
|
|
49
|
-
);
|
|
50
|
-
dialogCanvas.style.width = `${dialogCanvasWidth}px`;
|
|
51
|
-
dialogCanvas.style.height = `${dialogCanvasHeight}px`;
|
|
52
|
-
await drawMolfileOnCanvas(dialogCanvas);
|
|
53
|
-
|
|
54
|
-
dialogDiv.appendChild(dialogCanvas);
|
|
55
|
-
ui.dialog('Molecule')
|
|
56
|
-
.add(dialogDiv)
|
|
57
|
-
.showModal(true);
|
|
58
|
-
} catch (err) {
|
|
59
|
-
const errStr = errorToConsole(err);
|
|
60
|
-
console.error(errStr);
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// clear div's content if any
|
|
65
|
-
moleculeImgDiv.innerHTML = '';
|
|
66
|
-
|
|
67
|
-
if (molfile !== '') {
|
|
68
|
-
const canvas = ui.canvas(canvasWidth * window.devicePixelRatio, canvasHeight * window.devicePixelRatio);
|
|
69
|
-
|
|
70
|
-
// Draw zoomed-out molecule
|
|
71
|
-
canvas.style.width = `${canvasWidth}px`;
|
|
72
|
-
canvas.style.height = `${canvasHeight}px`;
|
|
73
|
-
canvas.style.borderStyle = 'solid';
|
|
74
|
-
canvas.style.borderColor = 'blue';
|
|
75
|
-
drawMolfileOnCanvas(canvas);
|
|
76
|
-
|
|
77
|
-
// Dialog with zoomed-in molecule
|
|
78
|
-
$(canvas).on('click', drawZoomedInMolecule);
|
|
79
|
-
$(canvas).on('mouseover', () => $(canvas).css('cursor', 'grab')); // for some reason 'zoom-in' value wouldn't work
|
|
80
|
-
$(canvas).on('mouseout', () => $(canvas).css('cursor', 'default'));
|
|
81
|
-
|
|
82
|
-
moleculeImgDiv.append(canvas);
|
|
83
|
-
}
|
|
84
|
-
}
|