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