@datagrok/sequence-translator 1.0.15 → 1.0.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 +16 -3
- package/detectors.js +0 -28
- package/dist/package-test.js +4372 -3957
- package/dist/package.js +3875 -3460
- package/package.json +4 -2
- package/setup-unlink-clean.sh +21 -0
- package/src/apps/oligo-sd-file-app.ts +58 -0
- package/src/autostart/calculations.ts +11 -8
- package/src/autostart/constants.ts +0 -12
- package/src/autostart/registration.ts +194 -157
- package/src/{axolabs/define-pattern.ts → axolabs-tab/axolabs-tab.ts} +2 -2
- package/src/axolabs-tab/define-pattern.ts +874 -0
- package/src/{axolabs → axolabs-tab}/draw-svg.ts +1 -1
- package/src/{axolabs → axolabs-tab}/helpers.ts +2 -2
- package/src/{autostart → hardcode-to-be-eliminated}/ICDs.ts +0 -0
- package/src/{autostart → hardcode-to-be-eliminated}/IDPs.ts +0 -0
- package/src/{structures-works → hardcode-to-be-eliminated}/const.ts +0 -0
- package/src/{axolabs → hardcode-to-be-eliminated}/constants.ts +0 -0
- package/src/{structures-works → hardcode-to-be-eliminated}/converters.ts +1 -1
- package/src/{structures-works → hardcode-to-be-eliminated}/map.ts +2 -2
- package/src/{autostart → hardcode-to-be-eliminated}/salts.ts +0 -0
- package/src/{autostart → hardcode-to-be-eliminated}/sources.ts +0 -0
- package/src/{autostart → hardcode-to-be-eliminated}/users.ts +0 -0
- package/src/{main/main-view.ts → main-tab/main-tab.ts} +28 -80
- package/src/package.ts +77 -13
- package/src/sdf-tab/sdf-tab.ts +163 -0
- package/src/{structures-works → sdf-tab}/sequence-codes-tools.ts +8 -5
- package/src/tests/smiles-tests.ts +2 -2
- package/src/utils/const.ts +0 -0
- package/src/{helpers.ts → utils/helpers.ts} +3 -3
- package/src/utils/parse.ts +27 -0
- package/src/utils/sdf-add-columns.ts +118 -0
- package/src/utils/sdf-save-table.ts +56 -0
- package/src/utils/structures-works/draw-molecule.ts +84 -0
- package/src/{structures-works → utils/structures-works}/from-monomers.ts +15 -16
- package/src/{structures-works → utils/structures-works}/mol-transformations.ts +34 -52
- package/{test-SequenceTranslator-91c83d8913ff-f94596bc.html → test-SequenceTranslator-6288c2fbe346-695b7b55.html} +10 -10
- package/src/structures-works/save-sense-antisense.ts +0 -91
|
@@ -0,0 +1,874 @@
|
|
|
1
|
+
import * as grok from 'datagrok-api/grok';
|
|
2
|
+
import * as ui from 'datagrok-api/ui';
|
|
3
|
+
import * as DG from 'datagrok-api/dg';
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
import * as svg from 'save-svg-as-png';
|
|
6
|
+
import $ from 'cash-dom';
|
|
7
|
+
|
|
8
|
+
import {drawAxolabsPattern} from './draw-svg';
|
|
9
|
+
import {AXOLABS_MAP} from '../hardcode-to-be-eliminated/constants';
|
|
10
|
+
import {isOverhang} from './helpers';
|
|
11
|
+
|
|
12
|
+
const baseChoices: string[] = Object.keys(AXOLABS_MAP);
|
|
13
|
+
const defaultBase: string = baseChoices[0];
|
|
14
|
+
const defaultPto: boolean = true;
|
|
15
|
+
const defaultSequenceLength: number = 23;
|
|
16
|
+
const maximalValidSequenceLength: number = 35;
|
|
17
|
+
const userStorageKey: string = 'SequenceTranslator';
|
|
18
|
+
const exampleMinWidth: string = '400px';
|
|
19
|
+
|
|
20
|
+
function generateExample(sequenceLength: number, sequenceBasis: string): string {
|
|
21
|
+
const uniqueSymbols = AXOLABS_MAP[sequenceBasis].symbols.join('');
|
|
22
|
+
return uniqueSymbols.repeat(Math.floor(sequenceLength / 4)) + uniqueSymbols.slice(0, sequenceLength % 4);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function findDuplicates(data: Int32Array | Float32Array | Float64Array | Uint32Array): number[] {
|
|
26
|
+
return Array.from(new Set(data)).filter((value) => data.indexOf(value) !== data.lastIndexOf(value));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function isCurrentUserCreatedThisPattern(patternName: string): Promise<boolean> {
|
|
30
|
+
return await grok.dapi.users.current().then((user) => {
|
|
31
|
+
const [firstName, lastName] = getUserName(patternName);
|
|
32
|
+
return (user.firstName != firstName || user.lastName != lastName);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getShortName(patternName: string): string {
|
|
37
|
+
let first = patternName.length + 1;
|
|
38
|
+
for (let i = 0; i < patternName.length; i++) {
|
|
39
|
+
if (patternName[i] === '(') {
|
|
40
|
+
first = i;
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return patternName.slice(0, first - 1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getUserName(patternName: string): string[] {
|
|
48
|
+
let first = -1;
|
|
49
|
+
for (let i = 0; i < patternName.length; i++) {
|
|
50
|
+
if (patternName[i] === '(') {
|
|
51
|
+
first = i;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return (first === -1) ? ['', ''] : patternName.slice(first + 9, patternName.length - 1).split(' ').slice(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function translateSequence(
|
|
59
|
+
sequence: string,
|
|
60
|
+
bases: DG.InputBase[],
|
|
61
|
+
ptoLinkages: DG.InputBase[],
|
|
62
|
+
startModification: DG.InputBase,
|
|
63
|
+
endModification: DG.InputBase,
|
|
64
|
+
firstPtoExist: boolean
|
|
65
|
+
): string {
|
|
66
|
+
let i: number = -1;
|
|
67
|
+
let mainSequence = sequence.replace(/[AUGC]/g, function(x: string) {
|
|
68
|
+
i++;
|
|
69
|
+
const indexOfSymbol = AXOLABS_MAP['RNA']['symbols'].indexOf(x);
|
|
70
|
+
let symbol = AXOLABS_MAP[bases[i].value]['symbols'][indexOfSymbol];
|
|
71
|
+
if (isOverhang(bases[i].value)) {
|
|
72
|
+
if (i < sequence.length / 2 && !isOverhang(bases[i + 1].value))
|
|
73
|
+
symbol = symbol + x + 'f';
|
|
74
|
+
else if (i > sequence.length / 2 && !isOverhang(bases[i - 1].value))
|
|
75
|
+
symbol = x + 'f' + symbol;
|
|
76
|
+
}
|
|
77
|
+
return (ptoLinkages[i].value) ? symbol + 's' : symbol;
|
|
78
|
+
});
|
|
79
|
+
if (mainSequence.slice(0, 5).split('mU').length === 3)
|
|
80
|
+
mainSequence = '(uu)' + mainSequence.slice(4);
|
|
81
|
+
if (mainSequence.slice(mainSequence.length - 7).split('mU').length === 3)
|
|
82
|
+
mainSequence = mainSequence.slice(0, mainSequence.length - 4) + '(uu)';
|
|
83
|
+
return startModification.value + (firstPtoExist ? 's' : '') + mainSequence + endModification.value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function addColumnWithIds(tableName: string, columnName: string, patternName: string): DG.Column<string> {
|
|
87
|
+
const nameOfNewColumn = 'ID ' + patternName;
|
|
88
|
+
const columns = grok.shell.table(tableName).columns;
|
|
89
|
+
if (columns.contains(nameOfNewColumn))
|
|
90
|
+
columns.remove(nameOfNewColumn);
|
|
91
|
+
const columnWithIds = columns.byName(columnName);
|
|
92
|
+
return columns.addNewString(nameOfNewColumn).init((i: number) => {
|
|
93
|
+
return (columnWithIds.getString(i) === '') ? '' : columnWithIds.get(i) + '_' + patternName;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function addColumnWithTranslatedSequences(
|
|
98
|
+
tableName: string,
|
|
99
|
+
columnName: string,
|
|
100
|
+
bases: DG.InputBase[],
|
|
101
|
+
ptoLinkages: DG.InputBase[],
|
|
102
|
+
startModification: DG.InputBase,
|
|
103
|
+
endModification: DG.InputBase,
|
|
104
|
+
firstPtoExist: boolean
|
|
105
|
+
): DG.Column<string> {
|
|
106
|
+
const nameOfNewColumn = 'Axolabs ' + columnName;
|
|
107
|
+
const columns = grok.shell.table(tableName).columns;
|
|
108
|
+
if (columns.contains(nameOfNewColumn))
|
|
109
|
+
columns.remove(nameOfNewColumn);
|
|
110
|
+
const columnWithInputSequences = columns.byName(columnName);
|
|
111
|
+
return columns.addNewString(nameOfNewColumn).init((i: number) => {
|
|
112
|
+
return (columnWithInputSequences.getString(i) === '') ? '' :
|
|
113
|
+
translateSequence(columnWithInputSequences.getString(i), bases, ptoLinkages, startModification, endModification,
|
|
114
|
+
firstPtoExist);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function getAxolabsTab(): HTMLDivElement {
|
|
119
|
+
const enumerateModifications = [defaultBase];
|
|
120
|
+
let maximalSsLength = defaultSequenceLength;
|
|
121
|
+
let maximalAsLength = defaultSequenceLength;
|
|
122
|
+
|
|
123
|
+
function updateAsModification() {
|
|
124
|
+
asModificationItems.innerHTML = '';
|
|
125
|
+
asPtoLinkages = asPtoLinkages.concat(Array(maximalAsLength - asBases.length).fill(fullyPto));
|
|
126
|
+
asBases = asBases.concat(Array(maximalAsLength - asBases.length).fill(sequenceBase));
|
|
127
|
+
let nucleotideCounter = 0;
|
|
128
|
+
for (let i = 0; i < asLength.value!; i++) {
|
|
129
|
+
asPtoLinkages[i] = ui.boolInput('', asPtoLinkages[i].value, () => {
|
|
130
|
+
updateSvgScheme();
|
|
131
|
+
updateOutputExamples();
|
|
132
|
+
});
|
|
133
|
+
asBases[i] = ui.choiceInput('', asBases[i].value, baseChoices, (v: string) => {
|
|
134
|
+
if (!enumerateModifications.includes(v)) {
|
|
135
|
+
enumerateModifications.push(v);
|
|
136
|
+
isEnumerateModificationsDiv.append(
|
|
137
|
+
ui.divText('', {style: {width: '25px'}}),
|
|
138
|
+
ui.boolInput(v, true, (boolV: boolean) => {
|
|
139
|
+
if (boolV) {
|
|
140
|
+
if (!enumerateModifications.includes(v))
|
|
141
|
+
enumerateModifications.push(v);
|
|
142
|
+
} else {
|
|
143
|
+
const index = enumerateModifications.indexOf(v, 0);
|
|
144
|
+
if (index > -1)
|
|
145
|
+
enumerateModifications.splice(index, 1);
|
|
146
|
+
}
|
|
147
|
+
updateSvgScheme();
|
|
148
|
+
}).root,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
updateAsModification();
|
|
152
|
+
updateSvgScheme();
|
|
153
|
+
updateOutputExamples();
|
|
154
|
+
});
|
|
155
|
+
if (!isOverhang(asBases[i].value))
|
|
156
|
+
nucleotideCounter++;
|
|
157
|
+
|
|
158
|
+
asModificationItems.append(
|
|
159
|
+
ui.divH([
|
|
160
|
+
ui.div([ui.label(isOverhang(asBases[i].value) ? '' : String(nucleotideCounter))],
|
|
161
|
+
{style: {width: '20px'}})!,
|
|
162
|
+
ui.block75([asBases[i]])!,
|
|
163
|
+
ui.div([asPtoLinkages[i]])!,
|
|
164
|
+
], {style: {alignItems: 'center'}}),
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function updateSsModification() {
|
|
170
|
+
ssModificationItems.innerHTML = '';
|
|
171
|
+
ssPtoLinkages = ssPtoLinkages.concat(Array(maximalSsLength - ssBases.length).fill(fullyPto));
|
|
172
|
+
ssBases = ssBases.concat(Array(maximalSsLength - ssBases.length).fill(sequenceBase));
|
|
173
|
+
let nucleotideCounter = 0;
|
|
174
|
+
for (let i = 0; i < ssLength.value!; i++) {
|
|
175
|
+
ssPtoLinkages[i] = ui.boolInput('', ssPtoLinkages[i].value, () => {
|
|
176
|
+
updateSvgScheme();
|
|
177
|
+
updateOutputExamples();
|
|
178
|
+
});
|
|
179
|
+
ssBases[i] = ui.choiceInput('', ssBases[i].value, baseChoices, (v: string) => {
|
|
180
|
+
if (!enumerateModifications.includes(v)) {
|
|
181
|
+
enumerateModifications.push(v);
|
|
182
|
+
isEnumerateModificationsDiv.append(
|
|
183
|
+
ui.divText('', {style: {width: '25px'}}),
|
|
184
|
+
ui.boolInput(v, true, (boolV: boolean) => {
|
|
185
|
+
if (boolV) {
|
|
186
|
+
if (!enumerateModifications.includes(v))
|
|
187
|
+
enumerateModifications.push(v);
|
|
188
|
+
} else {
|
|
189
|
+
const index = enumerateModifications.indexOf(v, 0);
|
|
190
|
+
if (index > -1)
|
|
191
|
+
enumerateModifications.splice(index, 1);
|
|
192
|
+
}
|
|
193
|
+
updateSvgScheme();
|
|
194
|
+
}).root,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
updateSsModification();
|
|
198
|
+
updateSvgScheme();
|
|
199
|
+
updateOutputExamples();
|
|
200
|
+
});
|
|
201
|
+
if (!isOverhang(ssBases[i].value))
|
|
202
|
+
nucleotideCounter++;
|
|
203
|
+
|
|
204
|
+
ssModificationItems.append(
|
|
205
|
+
ui.divH([
|
|
206
|
+
ui.div([ui.label(isOverhang(ssBases[i].value) ? '' : String(nucleotideCounter))],
|
|
207
|
+
{style: {width: '20px'}})!,
|
|
208
|
+
ui.block75([ssBases[i]])!,
|
|
209
|
+
ui.div([ssPtoLinkages[i]])!,
|
|
210
|
+
], {style: {alignItems: 'center'}}),
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function updateUiForNewSequenceLength() {
|
|
216
|
+
if (ssLength.value! < maximalValidSequenceLength && asLength.value! < maximalValidSequenceLength) {
|
|
217
|
+
if (ssLength.value! > maximalSsLength)
|
|
218
|
+
maximalSsLength = ssLength.value!;
|
|
219
|
+
if (asLength.value! > maximalAsLength)
|
|
220
|
+
maximalAsLength = asLength.value!;
|
|
221
|
+
updateSsModification();
|
|
222
|
+
updateAsModification();
|
|
223
|
+
updateSvgScheme();
|
|
224
|
+
updateInputExamples();
|
|
225
|
+
updateOutputExamples();
|
|
226
|
+
} else {
|
|
227
|
+
ui.dialog('Sequence length is out of range')
|
|
228
|
+
.add(ui.divText('Sequence length should be less than ' +
|
|
229
|
+
maximalValidSequenceLength.toString() + ' due to UI constrains.'))
|
|
230
|
+
.add(ui.divText('Please change sequence length in order to define new pattern.'))
|
|
231
|
+
.show();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function updatePto(newPtoValue: boolean): void {
|
|
236
|
+
for (let i = 0; i < ssPtoLinkages.length; i++)
|
|
237
|
+
ssPtoLinkages[i].value = newPtoValue;
|
|
238
|
+
|
|
239
|
+
for (let i = 0; i < asPtoLinkages.length; i++)
|
|
240
|
+
asPtoLinkages[i].value = newPtoValue;
|
|
241
|
+
|
|
242
|
+
updateSvgScheme();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function updateBases(newBasisValue: string): void {
|
|
246
|
+
for (let i = 0; i < ssBases.length; i++)
|
|
247
|
+
ssBases[i].value = newBasisValue;
|
|
248
|
+
|
|
249
|
+
for (let i = 0; i < asBases.length; i++)
|
|
250
|
+
asBases[i].value = newBasisValue;
|
|
251
|
+
|
|
252
|
+
updateSvgScheme();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function updateInputExamples() {
|
|
256
|
+
if (inputSsColumn.value === '')
|
|
257
|
+
ssInputExample.value = generateExample(ssLength.value!, sequenceBase.value!);
|
|
258
|
+
if (createAsStrand.value && inputAsColumn.value === '')
|
|
259
|
+
asInputExample.value = generateExample(asLength.value!, sequenceBase.value!);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function updateOutputExamples() {
|
|
263
|
+
ssOutputExample.value = translateSequence(
|
|
264
|
+
ssInputExample.value, ssBases, ssPtoLinkages, ssFiveModification, ssThreeModification, firstSsPto.value!);
|
|
265
|
+
if (createAsStrand.value) {
|
|
266
|
+
asOutputExample.value = translateSequence(
|
|
267
|
+
asInputExample.value, asBases, asPtoLinkages, asFiveModification, asThreeModification, firstAsPto.value!);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function updateSvgScheme() {
|
|
272
|
+
svgDiv.innerHTML = '';
|
|
273
|
+
svgDiv.append(
|
|
274
|
+
ui.span([
|
|
275
|
+
drawAxolabsPattern(
|
|
276
|
+
getShortName(saveAs.value),
|
|
277
|
+
createAsStrand.value!,
|
|
278
|
+
ssBases.slice(0, ssLength.value!).map((e) => e.value),
|
|
279
|
+
asBases.slice(0, asLength.value!).map((e) => e.value),
|
|
280
|
+
[firstSsPto.value!].concat(ssPtoLinkages.slice(0, ssLength.value!).map((e) => e.value)),
|
|
281
|
+
[firstAsPto.value!].concat(asPtoLinkages.slice(0, asLength.value!).map((e) => e.value)),
|
|
282
|
+
ssThreeModification.value,
|
|
283
|
+
ssFiveModification.value,
|
|
284
|
+
asThreeModification.value,
|
|
285
|
+
asFiveModification.value,
|
|
286
|
+
comment.value,
|
|
287
|
+
enumerateModifications,
|
|
288
|
+
),
|
|
289
|
+
]),
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function detectDefaultBasis(array: string[]) {
|
|
294
|
+
const modeMap: {[index: string]: number} = {};
|
|
295
|
+
let maxEl = array[0];
|
|
296
|
+
let maxCount = 1;
|
|
297
|
+
for (let i = 0; i < array.length; i++) {
|
|
298
|
+
const el = array[i];
|
|
299
|
+
if (modeMap[el] === null)
|
|
300
|
+
modeMap[el] = 1;
|
|
301
|
+
else
|
|
302
|
+
modeMap[el]++;
|
|
303
|
+
if (modeMap[el] > maxCount) {
|
|
304
|
+
maxEl = el;
|
|
305
|
+
maxCount = modeMap[el];
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return maxEl;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async function parsePatternAndUpdateUi(newName: string) {
|
|
312
|
+
const pi = DG.TaskBarProgressIndicator.create('Loading pattern...');
|
|
313
|
+
await grok.dapi.userDataStorage.get(userStorageKey, false).then((entities) => {
|
|
314
|
+
const obj = JSON.parse(entities[newName]);
|
|
315
|
+
sequenceBase.value = detectDefaultBasis(obj['asBases'].concat(obj['ssBases']));
|
|
316
|
+
createAsStrand.value = (obj['asBases'].length > 0);
|
|
317
|
+
saveAs.value = newName;
|
|
318
|
+
|
|
319
|
+
ssBases = [];
|
|
320
|
+
for (let i = 0; i < obj['ssBases'].length; i++)
|
|
321
|
+
ssBases.push(ui.choiceInput('', obj['ssBases'][i], baseChoices));
|
|
322
|
+
|
|
323
|
+
asBases = [];
|
|
324
|
+
for (let i = 0; i < obj['asBases'].length; i++)
|
|
325
|
+
asBases.push(ui.choiceInput('', obj['asBases'][i], baseChoices));
|
|
326
|
+
|
|
327
|
+
firstSsPto.value = obj['ssPtoLinkages'][0];
|
|
328
|
+
ssPtoLinkages = [];
|
|
329
|
+
for (let i = 1; i < obj['ssPtoLinkages'].length; i++)
|
|
330
|
+
ssPtoLinkages.push(ui.boolInput('', obj['ssPtoLinkages'][i]));
|
|
331
|
+
|
|
332
|
+
firstAsPto.value = obj['asPtoLinkages'][0];
|
|
333
|
+
asPtoLinkages = [];
|
|
334
|
+
for (let i = 1; i < obj['asPtoLinkages'].length; i++)
|
|
335
|
+
asPtoLinkages.push(ui.boolInput('', obj['asPtoLinkages'][i]));
|
|
336
|
+
|
|
337
|
+
ssLength.value = obj['ssBases'].length;
|
|
338
|
+
asLength.value = obj['asBases'].length;
|
|
339
|
+
|
|
340
|
+
ssThreeModification.value = obj['ssThreeModification'];
|
|
341
|
+
ssFiveModification.value = obj['ssFiveModification'];
|
|
342
|
+
asThreeModification.value = obj['asThreeModification'];
|
|
343
|
+
asFiveModification.value = obj['asFiveModification'];
|
|
344
|
+
comment.value = obj['comment'];
|
|
345
|
+
});
|
|
346
|
+
pi.close();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function checkWhetherAllValuesInColumnHaveTheSameLength(colName: string): boolean {
|
|
350
|
+
const col = tables.value!.getCol(colName);
|
|
351
|
+
let allLengthsAreTheSame = true;
|
|
352
|
+
for (let i = 1; i < col.length; i++) {
|
|
353
|
+
if (col.get(i - 1).length != col.get(i).length && col.get(i).length != 0) {
|
|
354
|
+
allLengthsAreTheSame = false;
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (!allLengthsAreTheSame) {
|
|
359
|
+
const dialog = ui.dialog('Sequences lengths mismatch');
|
|
360
|
+
$(dialog.getButton('OK')).hide();
|
|
361
|
+
dialog
|
|
362
|
+
.add(ui.divText('The sequence length should match the number of Raw sequences in the input file'))
|
|
363
|
+
.add(ui.divText('\'ADD COLUMN\' to see sequences lengths'))
|
|
364
|
+
.addButton('ADD COLUMN', () => {
|
|
365
|
+
tables.value!.columns.addNewInt('Sequences lengths in ' + colName).init((j: number) => col.get(j).length);
|
|
366
|
+
grok.shell.info('Column with lengths added to \'' + tables.value!.name + '\'');
|
|
367
|
+
dialog.close();
|
|
368
|
+
grok.shell.v = grok.shell.getTableView(tables.value!.name);
|
|
369
|
+
})
|
|
370
|
+
.show();
|
|
371
|
+
}
|
|
372
|
+
if (col.get(0) != ssLength.value) {
|
|
373
|
+
const d = ui.dialog('Length was updated by value to from imported file');
|
|
374
|
+
d.add(ui.divText('Latest modifications may not take effect during translation'))
|
|
375
|
+
.onOK(() => grok.shell.info('Lengths changed')).show();
|
|
376
|
+
}
|
|
377
|
+
return allLengthsAreTheSame;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
async function getCurrentUserName(): Promise<string> {
|
|
381
|
+
return await grok.dapi.users.current().then((user) => {
|
|
382
|
+
return ' (created by ' + user.firstName + ' ' + user.lastName + ')';
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async function postPatternToUserStorage() {
|
|
387
|
+
const currUserName = await getCurrentUserName();
|
|
388
|
+
saveAs.value = (saveAs.stringValue.includes('(created by ')) ?
|
|
389
|
+
getShortName(saveAs.value) + currUserName :
|
|
390
|
+
saveAs.stringValue + currUserName;
|
|
391
|
+
return grok.dapi.userDataStorage.postValue(
|
|
392
|
+
userStorageKey,
|
|
393
|
+
saveAs.value,
|
|
394
|
+
JSON.stringify({
|
|
395
|
+
'ssBases': ssBases.slice(0, ssLength.value!).map((e) => e.value),
|
|
396
|
+
'asBases': asBases.slice(0, asLength.value!).map((e) => e.value),
|
|
397
|
+
'ssPtoLinkages': [firstSsPto.value].concat(ssPtoLinkages.slice(0, ssLength.value!).map((e) => e.value)),
|
|
398
|
+
'asPtoLinkages': [firstAsPto.value].concat(asPtoLinkages.slice(0, asLength.value!).map((e) => e.value)),
|
|
399
|
+
'ssThreeModification': ssThreeModification.value,
|
|
400
|
+
'ssFiveModification': ssFiveModification.value,
|
|
401
|
+
'asThreeModification': asThreeModification.value,
|
|
402
|
+
'asFiveModification': asFiveModification.value,
|
|
403
|
+
'comment': comment.value,
|
|
404
|
+
}),
|
|
405
|
+
false,
|
|
406
|
+
).then(() => grok.shell.info('Pattern \'' + saveAs.value + '\' was successfully uploaded!'));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async function updatePatternsList() {
|
|
410
|
+
grok.dapi.userDataStorage.get(userStorageKey, false).then(async (entities) => {
|
|
411
|
+
const lstMy: string[] = [];
|
|
412
|
+
const lstOthers: string[] = [];
|
|
413
|
+
|
|
414
|
+
// TODO: display short name, but use long for querying userdataStorage
|
|
415
|
+
for (const ent of Object.keys(entities)) {
|
|
416
|
+
if (await isCurrentUserCreatedThisPattern(ent))
|
|
417
|
+
lstOthers.push(ent);
|
|
418
|
+
else
|
|
419
|
+
lstMy.push(ent);//getShortName(ent));
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
let loadPattern = ui.choiceInput('Load Pattern', '', lstMy, (v: string) => parsePatternAndUpdateUi(v));
|
|
423
|
+
|
|
424
|
+
const myOrOthersPatternList = ui.choiceInput('', 'Mine', ['Mine', 'Others'], (v: string) => {
|
|
425
|
+
const currentList = v === 'Mine' ? lstMy : lstOthers;
|
|
426
|
+
loadPattern = ui.choiceInput('Load Pattern', '', currentList, (v: string) => parsePatternAndUpdateUi(v));
|
|
427
|
+
|
|
428
|
+
loadPattern.root.append(myOrOthersPatternList.input);
|
|
429
|
+
loadPattern.root.append(loadPattern.input);
|
|
430
|
+
// @ts-ignore
|
|
431
|
+
loadPattern.input.style.maxWidth = '100px';
|
|
432
|
+
loadPattern.setTooltip('Apply Existing Pattern');
|
|
433
|
+
|
|
434
|
+
loadPatternDiv.innerHTML = '';
|
|
435
|
+
loadPatternDiv.append(loadPattern.root);
|
|
436
|
+
loadPattern.root.append(
|
|
437
|
+
ui.div([
|
|
438
|
+
ui.button(ui.iconFA('trash-alt', () => {}), async () => {
|
|
439
|
+
if (loadPattern.value === null)
|
|
440
|
+
grok.shell.warning('Choose pattern to delete');
|
|
441
|
+
else if (await isCurrentUserCreatedThisPattern(saveAs.value))
|
|
442
|
+
grok.shell.warning('Cannot delete pattern, created by other user');
|
|
443
|
+
else {
|
|
444
|
+
await grok.dapi.userDataStorage.remove(userStorageKey, loadPattern.value, false)
|
|
445
|
+
.then(() => grok.shell.info('Pattern \'' + loadPattern.value + '\' deleted'));
|
|
446
|
+
}
|
|
447
|
+
await updatePatternsList();
|
|
448
|
+
}),
|
|
449
|
+
], 'ui-input-options'),
|
|
450
|
+
);
|
|
451
|
+
});
|
|
452
|
+
loadPattern.root.append(myOrOthersPatternList.input);
|
|
453
|
+
loadPattern.root.append(loadPattern.input);
|
|
454
|
+
// @ts-ignore
|
|
455
|
+
loadPattern.input.style.maxWidth = '100px';
|
|
456
|
+
loadPattern.setTooltip('Apply Existing Pattern');
|
|
457
|
+
|
|
458
|
+
loadPatternDiv.innerHTML = '';
|
|
459
|
+
loadPatternDiv.append(loadPattern.root);
|
|
460
|
+
loadPattern.root.append(
|
|
461
|
+
ui.div([
|
|
462
|
+
ui.button(ui.iconFA('trash-alt', () => {}), async () => {
|
|
463
|
+
if (loadPattern.value === null)
|
|
464
|
+
grok.shell.warning('Choose pattern to delete');
|
|
465
|
+
else if (await isCurrentUserCreatedThisPattern(saveAs.value))
|
|
466
|
+
grok.shell.warning('Cannot delete pattern, created by other user');
|
|
467
|
+
else {
|
|
468
|
+
await grok.dapi.userDataStorage.remove(userStorageKey, loadPattern.value, false)
|
|
469
|
+
.then(() => grok.shell.info('Pattern \'' + loadPattern.value + '\' deleted'));
|
|
470
|
+
}
|
|
471
|
+
await updatePatternsList();
|
|
472
|
+
}),
|
|
473
|
+
], 'ui-input-options'),
|
|
474
|
+
);
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
async function savePattern() {
|
|
479
|
+
await grok.dapi.userDataStorage.get(userStorageKey, false)
|
|
480
|
+
.then((entities) => {
|
|
481
|
+
if (Object.keys(entities).includes(saveAs.value)) {
|
|
482
|
+
const dialog = ui.dialog('Pattern already exists');
|
|
483
|
+
$(dialog.getButton('OK')).hide();
|
|
484
|
+
dialog
|
|
485
|
+
.add(ui.divText('Pattern name \'' + saveAs.value + '\' already exists.'))
|
|
486
|
+
.add(ui.divText('Replace pattern?'))
|
|
487
|
+
.addButton('YES', async () => {
|
|
488
|
+
await grok.dapi.userDataStorage.remove(userStorageKey, saveAs.value, false)
|
|
489
|
+
.then(() => postPatternToUserStorage());
|
|
490
|
+
dialog.close();
|
|
491
|
+
})
|
|
492
|
+
.show();
|
|
493
|
+
} else
|
|
494
|
+
postPatternToUserStorage();
|
|
495
|
+
});
|
|
496
|
+
await updatePatternsList();
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const inputSsColumnDiv = ui.div([]);
|
|
500
|
+
const inputAsColumnDiv = ui.div([]);
|
|
501
|
+
const inputIdColumnDiv = ui.div([]);
|
|
502
|
+
const ssModificationItems = ui.div([]);
|
|
503
|
+
const asModificationItems = ui.div([]);
|
|
504
|
+
const svgDiv = ui.div([]);
|
|
505
|
+
const asExampleDiv = ui.div([]);
|
|
506
|
+
const appAxolabsDescription = ui.div([]);
|
|
507
|
+
const loadPatternDiv = ui.div([]);
|
|
508
|
+
const asModificationDiv = ui.div([]);
|
|
509
|
+
const firstAsPtoDiv = ui.div([]);
|
|
510
|
+
const isEnumerateModificationsDiv = ui.divH([
|
|
511
|
+
ui.boolInput(defaultBase, true, (v: boolean) => {
|
|
512
|
+
if (v) {
|
|
513
|
+
if (!enumerateModifications.includes(defaultBase))
|
|
514
|
+
enumerateModifications.push(defaultBase);
|
|
515
|
+
} else {
|
|
516
|
+
const index = enumerateModifications.indexOf(defaultBase, 0);
|
|
517
|
+
if (index > -1)
|
|
518
|
+
enumerateModifications.splice(index, 1);
|
|
519
|
+
}
|
|
520
|
+
updateSvgScheme();
|
|
521
|
+
updateOutputExamples();
|
|
522
|
+
}).root,
|
|
523
|
+
]);
|
|
524
|
+
|
|
525
|
+
let ssBases = Array(defaultSequenceLength).fill(ui.choiceInput('', defaultBase, baseChoices));
|
|
526
|
+
let asBases = Array(defaultSequenceLength).fill(ui.choiceInput('', defaultBase, baseChoices));
|
|
527
|
+
let ssPtoLinkages = Array(defaultSequenceLength).fill(ui.boolInput('', defaultPto));
|
|
528
|
+
let asPtoLinkages = Array(defaultSequenceLength).fill(ui.boolInput('', defaultPto));
|
|
529
|
+
|
|
530
|
+
const ssLength = ui.intInput('SS Length', defaultSequenceLength, () => updateUiForNewSequenceLength());
|
|
531
|
+
ssLength.setTooltip('Length of sense strand, including overhangs');
|
|
532
|
+
const asLength = ui.intInput('AS Length', defaultSequenceLength, () => updateUiForNewSequenceLength());
|
|
533
|
+
asLength.setTooltip('Length of sense strand, including overhangs');
|
|
534
|
+
const asLengthDiv = ui.div([asLength.root]);
|
|
535
|
+
|
|
536
|
+
function validateSsColumn(colName: string): void {
|
|
537
|
+
const allLengthsAreTheSame: boolean = checkWhetherAllValuesInColumnHaveTheSameLength(colName);
|
|
538
|
+
const firstSequence = tables.value!.getCol(colName).get(0);
|
|
539
|
+
if (allLengthsAreTheSame && firstSequence.length != ssLength.value)
|
|
540
|
+
ssLength.value = tables.value!.getCol(colName).get(0).length;
|
|
541
|
+
ssInputExample.value = firstSequence;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function validateAsColumn(colName: string): void {
|
|
545
|
+
const allLengthsAreTheSame: boolean = checkWhetherAllValuesInColumnHaveTheSameLength(colName);
|
|
546
|
+
const firstSequence = tables.value!.getCol(colName).get(0);
|
|
547
|
+
if (allLengthsAreTheSame && firstSequence.length != asLength.value)
|
|
548
|
+
asLength.value = tables.value!.getCol(colName).get(0).length;
|
|
549
|
+
asInputExample.value = firstSequence;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function validateIdsColumn(colName: string) {
|
|
553
|
+
const col = tables.value!.getCol(colName);
|
|
554
|
+
if (col.type != DG.TYPE.INT)
|
|
555
|
+
grok.shell.error('Column should contain integers only');
|
|
556
|
+
else if (col.categories.filter((e) => e != '').length < col.toList().filter((e) => e != '').length) {
|
|
557
|
+
const duplicates = findDuplicates(col.getRawData());
|
|
558
|
+
ui.dialog('Non-unique IDs')
|
|
559
|
+
.add(ui.divText('Press \'OK\' to select rows with non-unique values'))
|
|
560
|
+
.onOK(() => {
|
|
561
|
+
const selection = tables.value!.selection;
|
|
562
|
+
selection.init((i: number) => duplicates.indexOf(col.get(i)) > -1);
|
|
563
|
+
grok.shell.v = grok.shell.getTableView(tables.value!.name);
|
|
564
|
+
grok.shell.info('Rows are selected in table \'' + tables.value!.name + '\'');
|
|
565
|
+
})
|
|
566
|
+
.show();
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const tables = ui.tableInput('Tables', grok.shell.tables[0], grok.shell.tables, (t: DG.DataFrame) => {
|
|
571
|
+
const inputSsColumn = ui.choiceInput('SS Column', '', t.columns.names(), (colName: string) => {
|
|
572
|
+
validateSsColumn(colName);
|
|
573
|
+
ssVar = colName;
|
|
574
|
+
});
|
|
575
|
+
inputSsColumnDiv.innerHTML = '';
|
|
576
|
+
inputSsColumnDiv.append(inputSsColumn.root);
|
|
577
|
+
const inputAsColumn = ui.choiceInput('AS Column', '', t.columns.names(), (colName: string) => {
|
|
578
|
+
validateAsColumn(colName);
|
|
579
|
+
asVar = colName;
|
|
580
|
+
});
|
|
581
|
+
inputAsColumnDiv.innerHTML = '';
|
|
582
|
+
inputAsColumnDiv.append(inputAsColumn.root);
|
|
583
|
+
const inputIdColumn = ui.choiceInput('ID Column', '', t.columns.names(), (colName: string) => {
|
|
584
|
+
validateIdsColumn(colName);
|
|
585
|
+
idVar = colName;
|
|
586
|
+
});
|
|
587
|
+
inputIdColumnDiv.innerHTML = '';
|
|
588
|
+
inputIdColumnDiv.append(inputIdColumn.root);
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
let ssVar = '';
|
|
592
|
+
const inputSsColumn = ui.choiceInput('SS Column', '', [], (colName: string) => {
|
|
593
|
+
validateSsColumn(colName);
|
|
594
|
+
ssVar = colName;
|
|
595
|
+
});
|
|
596
|
+
inputSsColumnDiv.append(inputSsColumn.root);
|
|
597
|
+
let asVar = '';
|
|
598
|
+
const inputAsColumn = ui.choiceInput('AS Column', '', [], (colName: string) => {
|
|
599
|
+
validateAsColumn(colName);
|
|
600
|
+
asVar = colName;
|
|
601
|
+
});
|
|
602
|
+
inputAsColumnDiv.append(inputAsColumn.root);
|
|
603
|
+
let idVar = '';
|
|
604
|
+
const inputIdColumn = ui.choiceInput('ID Column', '', [], (colName: string) => {
|
|
605
|
+
validateIdsColumn(colName);
|
|
606
|
+
idVar = colName;
|
|
607
|
+
});
|
|
608
|
+
inputIdColumnDiv.append(inputIdColumn.root);
|
|
609
|
+
|
|
610
|
+
updatePatternsList();
|
|
611
|
+
|
|
612
|
+
const sequenceBase = ui.choiceInput('Sequence Basis', defaultBase, baseChoices, (v: string) => {
|
|
613
|
+
updateBases(v);
|
|
614
|
+
updateOutputExamples();
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
const fullyPto = ui.boolInput('Fully PTO', defaultPto, (v: boolean) => {
|
|
618
|
+
firstSsPto.value = v;
|
|
619
|
+
firstAsPto.value = v;
|
|
620
|
+
updatePto(v);
|
|
621
|
+
updateOutputExamples();
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
const firstSsPto = ui.boolInput('First SS PTO', fullyPto.value!, () => updateSvgScheme());
|
|
625
|
+
firstSsPto.setTooltip('ps linkage before first nucleotide of sense strand');
|
|
626
|
+
const firstAsPto = ui.boolInput('First AS PTO', fullyPto.value!, () => updateSvgScheme());
|
|
627
|
+
firstAsPto.setTooltip('ps linkage before first nucleotide of antisense strand');
|
|
628
|
+
firstAsPtoDiv.append(firstAsPto.root);
|
|
629
|
+
|
|
630
|
+
const createAsStrand = ui.boolInput('Create AS Strand', true, (v: boolean) => {
|
|
631
|
+
asModificationSection.hidden = (!v);
|
|
632
|
+
inputAsColumnDiv.hidden = (!v);
|
|
633
|
+
asLengthDiv.hidden = (!v);
|
|
634
|
+
asModificationDiv.hidden = (!v);
|
|
635
|
+
asExampleDiv.hidden = (!v);
|
|
636
|
+
firstAsPtoDiv.hidden = (!v);
|
|
637
|
+
updateSvgScheme();
|
|
638
|
+
});
|
|
639
|
+
createAsStrand.setTooltip('Create antisense strand sections on SVG and table to the right');
|
|
640
|
+
|
|
641
|
+
const saveAs = ui.textInput('Save As', 'Pattern Name', () => updateSvgScheme());
|
|
642
|
+
saveAs.setTooltip('Name Of New Pattern');
|
|
643
|
+
|
|
644
|
+
const ssThreeModification = ui.stringInput('SS 3\' Modification', '', () => {
|
|
645
|
+
updateSvgScheme();
|
|
646
|
+
updateOutputExamples();
|
|
647
|
+
});
|
|
648
|
+
ssThreeModification.setTooltip('Additional SS 3\' Modification');
|
|
649
|
+
|
|
650
|
+
const ssFiveModification = ui.stringInput('SS 5\' Modification', '', () => {
|
|
651
|
+
updateSvgScheme();
|
|
652
|
+
updateOutputExamples();
|
|
653
|
+
});
|
|
654
|
+
ssFiveModification.setTooltip('Additional SS 5\' Modification');
|
|
655
|
+
|
|
656
|
+
const asThreeModification = ui.stringInput('AS 3\' Modification', '', () => {
|
|
657
|
+
updateSvgScheme();
|
|
658
|
+
updateOutputExamples();
|
|
659
|
+
});
|
|
660
|
+
asThreeModification.setTooltip('Additional AS 3\' Modification');
|
|
661
|
+
|
|
662
|
+
const asFiveModification = ui.stringInput('AS 5\' Modification', '', () => {
|
|
663
|
+
updateSvgScheme();
|
|
664
|
+
updateOutputExamples();
|
|
665
|
+
});
|
|
666
|
+
asFiveModification.setTooltip('Additional AS 5\' Modification');
|
|
667
|
+
|
|
668
|
+
asModificationDiv.append(asThreeModification.root);
|
|
669
|
+
asModificationDiv.append(asFiveModification.root);
|
|
670
|
+
|
|
671
|
+
const comment = ui.textInput('Comment', '', () => updateSvgScheme());
|
|
672
|
+
|
|
673
|
+
const savePatternButton = ui.button('Save', () => {
|
|
674
|
+
if (saveAs.value != '')
|
|
675
|
+
savePattern().then((r) => grok.shell.info('Pattern saved'));
|
|
676
|
+
else {
|
|
677
|
+
const name = ui.stringInput('Enter Name', '');
|
|
678
|
+
ui.dialog('Pattern Name')
|
|
679
|
+
.add(name.root)
|
|
680
|
+
.onOK(() => {
|
|
681
|
+
saveAs.value = name.value;
|
|
682
|
+
savePattern().then((r) => grok.shell.info('Pattern saved'));
|
|
683
|
+
})
|
|
684
|
+
.show();
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
const convertSequenceButton = ui.button('Convert Sequences', () => {
|
|
689
|
+
if (ssVar === '' || (createAsStrand.value && asVar === ''))
|
|
690
|
+
grok.shell.info('Please select table and columns on which to apply pattern');
|
|
691
|
+
else if (ssLength.value != ssInputExample.value.length || asLength.value != asInputExample.value.length) {
|
|
692
|
+
const dialog = ui.dialog('Length Mismatch');
|
|
693
|
+
$(dialog.getButton('OK')).hide();
|
|
694
|
+
dialog
|
|
695
|
+
.add(ui.divText('Length of sequences in columns doesn\'t match entered length. Update length value?'))
|
|
696
|
+
.addButton('YES', () => {
|
|
697
|
+
ssLength.value = tables.value!.getCol(inputSsColumn.value!).getString(0).length;
|
|
698
|
+
asLength.value = tables.value!.getCol(inputAsColumn.value!).getString(0).length;
|
|
699
|
+
dialog.close();
|
|
700
|
+
})
|
|
701
|
+
.show();
|
|
702
|
+
} else {
|
|
703
|
+
if (idVar != '')
|
|
704
|
+
addColumnWithIds(tables.value!.name, idVar, getShortName(saveAs.value));
|
|
705
|
+
addColumnWithTranslatedSequences(
|
|
706
|
+
tables.value!.name, ssVar, ssBases, ssPtoLinkages,
|
|
707
|
+
ssFiveModification, ssThreeModification, firstSsPto.value!);
|
|
708
|
+
if (createAsStrand.value) {
|
|
709
|
+
addColumnWithTranslatedSequences(
|
|
710
|
+
tables.value!.name, asVar, asBases, asPtoLinkages,
|
|
711
|
+
asFiveModification, asThreeModification, firstAsPto.value!);
|
|
712
|
+
}
|
|
713
|
+
grok.shell.v = grok.shell.getTableView(tables.value!.name);
|
|
714
|
+
grok.shell.info(((createAsStrand.value) ? 'Columns were' : 'Column was') +
|
|
715
|
+
' added to table \'' + tables.value!.name + '\'');
|
|
716
|
+
updateOutputExamples();
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
const ssInputExample = ui.textInput('Sense Strand', generateExample(ssLength.value!, sequenceBase.value!));
|
|
721
|
+
const ssOutputExample = ui.textInput(' ', translateSequence(
|
|
722
|
+
ssInputExample.value, ssBases, ssPtoLinkages, ssThreeModification, ssFiveModification, firstSsPto.value!));
|
|
723
|
+
(ssInputExample.input as HTMLElement).style.resize = 'none';
|
|
724
|
+
(ssInputExample.input as HTMLElement).style.minWidth = exampleMinWidth;
|
|
725
|
+
(ssOutputExample.input as HTMLElement).style.resize = 'none';
|
|
726
|
+
(ssOutputExample.input as HTMLElement).style.minWidth = exampleMinWidth;
|
|
727
|
+
// @ts-ignore
|
|
728
|
+
ssOutputExample.input.disabled = 'true';
|
|
729
|
+
ssOutputExample.root.append(
|
|
730
|
+
ui.div([
|
|
731
|
+
ui.button(ui.iconFA('copy', () => {}), () => {
|
|
732
|
+
navigator.clipboard.writeText(ssOutputExample.value).then(() =>
|
|
733
|
+
grok.shell.info('Sequence was copied to clipboard'));
|
|
734
|
+
}),
|
|
735
|
+
], 'ui-input-options'),
|
|
736
|
+
);
|
|
737
|
+
|
|
738
|
+
const asInputExample = ui.textInput('Antisense Strand', generateExample(asLength.value!, sequenceBase.value!));
|
|
739
|
+
const asOutputExample = ui.textInput(' ', translateSequence(
|
|
740
|
+
asInputExample.value, asBases, asPtoLinkages, asFiveModification, asThreeModification, firstSsPto.value!));
|
|
741
|
+
(asInputExample.input as HTMLElement).style.resize = 'none';
|
|
742
|
+
(asInputExample.input as HTMLElement).style.minWidth = exampleMinWidth;
|
|
743
|
+
(asOutputExample.input as HTMLElement).style.resize = 'none';
|
|
744
|
+
(asOutputExample.input as HTMLElement).style.minWidth = exampleMinWidth;
|
|
745
|
+
// @ts-ignore
|
|
746
|
+
asOutputExample.input.disabled = 'true';
|
|
747
|
+
asOutputExample.root.append(
|
|
748
|
+
ui.div([
|
|
749
|
+
ui.button(ui.iconFA('copy', () => {}), () => {
|
|
750
|
+
navigator.clipboard.writeText(asOutputExample.value).then(() =>
|
|
751
|
+
grok.shell.info('Sequence was copied to clipboard'));
|
|
752
|
+
}),
|
|
753
|
+
], 'ui-input-options'),
|
|
754
|
+
);
|
|
755
|
+
asExampleDiv.append(asInputExample.root);
|
|
756
|
+
asExampleDiv.append(asOutputExample.root);
|
|
757
|
+
|
|
758
|
+
updateUiForNewSequenceLength();
|
|
759
|
+
|
|
760
|
+
const exampleSection = ui.div([
|
|
761
|
+
ui.h1('Example'),
|
|
762
|
+
ssInputExample.root,
|
|
763
|
+
ssOutputExample.root,
|
|
764
|
+
asExampleDiv,
|
|
765
|
+
], 'ui-form');
|
|
766
|
+
|
|
767
|
+
const inputsSection = ui.div([
|
|
768
|
+
ui.h1('Inputs'),
|
|
769
|
+
ui.divH([
|
|
770
|
+
tables.root,
|
|
771
|
+
inputSsColumnDiv,
|
|
772
|
+
]),
|
|
773
|
+
ui.divH([
|
|
774
|
+
inputAsColumnDiv,
|
|
775
|
+
inputIdColumnDiv,
|
|
776
|
+
]),
|
|
777
|
+
ui.buttonsInput([
|
|
778
|
+
convertSequenceButton,
|
|
779
|
+
]),
|
|
780
|
+
], 'ui-form');
|
|
781
|
+
|
|
782
|
+
const downloadButton = ui.button('Download', () => svg.saveSvgAsPng(document.getElementById('mySvg'), saveAs.value,
|
|
783
|
+
{backgroundColor: 'white'}));
|
|
784
|
+
|
|
785
|
+
const mainSection = ui.panel([
|
|
786
|
+
ui.block([
|
|
787
|
+
svgDiv,
|
|
788
|
+
], {style: {overflowX: 'scroll'}}),
|
|
789
|
+
downloadButton,
|
|
790
|
+
isEnumerateModificationsDiv,
|
|
791
|
+
ui.div([
|
|
792
|
+
ui.div([
|
|
793
|
+
ui.divH([
|
|
794
|
+
ui.h1('Pattern'),
|
|
795
|
+
ui.div([
|
|
796
|
+
ui.iconFA('question-circle', () => {
|
|
797
|
+
appAxolabsDescription.innerHTML = '';
|
|
798
|
+
appAxolabsDescription.append(info);
|
|
799
|
+
}),
|
|
800
|
+
], {style: {padding: '2px'}}),
|
|
801
|
+
]),
|
|
802
|
+
ui.divH([
|
|
803
|
+
ui.div([
|
|
804
|
+
ssLength.root,
|
|
805
|
+
asLengthDiv,
|
|
806
|
+
sequenceBase.root,
|
|
807
|
+
comment.root,
|
|
808
|
+
loadPatternDiv,
|
|
809
|
+
saveAs.root,
|
|
810
|
+
ui.buttonsInput([
|
|
811
|
+
savePatternButton,
|
|
812
|
+
]),
|
|
813
|
+
], 'ui-form'),
|
|
814
|
+
ui.div([
|
|
815
|
+
createAsStrand.root,
|
|
816
|
+
fullyPto.root,
|
|
817
|
+
firstSsPto.root,
|
|
818
|
+
firstAsPtoDiv,
|
|
819
|
+
ssFiveModification.root,
|
|
820
|
+
ssThreeModification.root,
|
|
821
|
+
asModificationDiv,
|
|
822
|
+
], 'ui-form'),
|
|
823
|
+
], 'ui-form'),
|
|
824
|
+
], 'ui-form'),
|
|
825
|
+
inputsSection,
|
|
826
|
+
exampleSection,
|
|
827
|
+
], {style: {flexWrap: 'wrap'}}),
|
|
828
|
+
]);
|
|
829
|
+
|
|
830
|
+
const ssModificationSection = ui.panel([
|
|
831
|
+
ui.h1('Sense Strand'),
|
|
832
|
+
ui.divH([
|
|
833
|
+
ui.div([ui.divText('#')], {style: {width: '20px'}})!,
|
|
834
|
+
ui.block75([ui.divText('Modification')])!,
|
|
835
|
+
ui.div([ui.divText('PTO')], {style: {paddingRight: '8px'}})!,
|
|
836
|
+
]),
|
|
837
|
+
ssModificationItems,
|
|
838
|
+
])!;
|
|
839
|
+
|
|
840
|
+
const asModificationSection = ui.panel([
|
|
841
|
+
ui.h1('Antisense Strand'),
|
|
842
|
+
ui.divH([
|
|
843
|
+
ui.div([ui.divText('#')], {style: {width: '20px'}})!,
|
|
844
|
+
ui.block75([ui.divText('Modification')])!,
|
|
845
|
+
ui.div([ui.divText('PTO')], {style: {paddingRight: '8px'}})!,
|
|
846
|
+
]),
|
|
847
|
+
asModificationItems,
|
|
848
|
+
])!;
|
|
849
|
+
|
|
850
|
+
const info = ui.info(
|
|
851
|
+
[
|
|
852
|
+
ui.divText('\n How to define new pattern:', {style: {'font-weight': 'bolder'}}),
|
|
853
|
+
ui.divText('1. Choose table and columns with sense and antisense strands'),
|
|
854
|
+
ui.divText('2. Choose lengths of both strands by editing checkboxes below'),
|
|
855
|
+
ui.divText('3. Choose basis and PTO status for each nucleotide'),
|
|
856
|
+
ui.divText('4. Set additional modifications for sequence edges'),
|
|
857
|
+
ui.divText('5. Press \'Convert Sequences\' button'),
|
|
858
|
+
ui.divText('This will add the result column(s) to the right of the table'),
|
|
859
|
+
], 'Create and apply Axolabs translation patterns.',
|
|
860
|
+
);
|
|
861
|
+
|
|
862
|
+
return ui.splitH([
|
|
863
|
+
ui.div([
|
|
864
|
+
appAxolabsDescription,
|
|
865
|
+
mainSection!,
|
|
866
|
+
])!,
|
|
867
|
+
ui.box(
|
|
868
|
+
ui.divH([
|
|
869
|
+
ssModificationSection,
|
|
870
|
+
asModificationSection,
|
|
871
|
+
]), {style: {maxWidth: '360px'}},
|
|
872
|
+
),
|
|
873
|
+
]);
|
|
874
|
+
}
|