@datagrok/sequence-translator 1.2.7 → 1.3.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 +5 -5
- package/CHANGELOG.md +14 -0
- package/dist/package-test.js +2 -1
- package/dist/package-test.js.LICENSE.txt +8 -0
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +2 -1
- package/dist/package.js.LICENSE.txt +8 -0
- package/dist/package.js.map +1 -1
- package/files/pattern-app-data.json +80 -0
- package/package.json +22 -14
- package/src/{model → apps/common/model}/const.ts +1 -1
- package/src/{model/data-loading-utils → apps/common/model/data-loader}/const.ts +7 -2
- package/src/apps/common/model/data-loader/json-loader.ts +48 -0
- package/src/{model/data-loading-utils → apps/common/model/data-loader}/types.ts +13 -6
- package/src/{model → apps/common/model}/monomer-lib/lib-wrapper.ts +9 -12
- package/src/apps/common/model/oligo-toolkit-package.ts +30 -0
- package/src/{model → apps/common/model}/parsing-validation/format-detector.ts +5 -5
- package/src/{model → apps/common/model}/parsing-validation/format-handler.ts +18 -19
- package/src/{model → apps/common/model}/parsing-validation/sequence-validator.ts +1 -1
- package/src/apps/common/view/app-ui-base.ts +28 -0
- package/src/apps/common/view/combined-app-ui.ts +66 -0
- package/src/{view/utils → apps/common/view/components}/colored-input/colored-text-input.ts +1 -1
- package/src/{view/utils → apps/common/view/components}/draw-molecule.ts +1 -1
- package/src/{view/utils → apps/common/view/components}/molecule-img.ts +3 -3
- package/src/{view/const/ui.ts → apps/common/view/const.ts} +4 -4
- package/src/apps/common/view/isolated-app-ui.ts +43 -0
- package/src/{view/monomer-lib-viewer/viewer.ts → apps/common/view/monomer-lib-viewer.ts} +2 -2
- package/src/apps/common/view/utils.ts +29 -0
- package/src/apps/pattern/model/const.ts +121 -0
- package/src/apps/pattern/model/data-manager.ts +297 -0
- package/src/apps/pattern/model/event-bus.ts +487 -0
- package/src/apps/pattern/model/router.ts +46 -0
- package/src/apps/pattern/model/subscription-manager.ts +21 -0
- package/src/apps/pattern/model/translator.ts +94 -0
- package/src/apps/pattern/model/types.ts +52 -0
- package/src/apps/pattern/model/utils.ts +110 -0
- package/src/apps/pattern/view/components/bulk-convert/column-input.ts +79 -0
- package/src/apps/pattern/view/components/bulk-convert/table-controls.ts +38 -0
- package/src/apps/pattern/view/components/bulk-convert/table-input.ts +95 -0
- package/src/apps/pattern/view/components/edit-block-controls.ts +196 -0
- package/src/apps/pattern/view/components/left-section.ts +44 -0
- package/src/apps/pattern/view/components/load-block-controls.ts +200 -0
- package/src/apps/pattern/view/components/numeric-label-visibility-controls.ts +69 -0
- package/src/apps/pattern/view/components/right-section.ts +148 -0
- package/src/apps/pattern/view/components/strand-editor/dialog.ts +79 -0
- package/src/apps/pattern/view/components/strand-editor/header-controls.ts +105 -0
- package/src/apps/pattern/view/components/strand-editor/strand-controls.ts +159 -0
- package/src/apps/pattern/view/components/terminal-modification-editor.ts +127 -0
- package/src/apps/pattern/view/components/translation-examples-block.ts +139 -0
- package/src/{view/style/pattern-app.css → apps/pattern/view/style.css} +4 -0
- package/src/apps/pattern/view/svg-utils/const.ts +77 -0
- package/src/apps/pattern/view/svg-utils/legend-block.ts +92 -0
- package/src/apps/pattern/view/svg-utils/strands-block.ts +335 -0
- package/src/apps/pattern/view/svg-utils/svg-block-base.ts +37 -0
- package/src/apps/pattern/view/svg-utils/svg-display-manager.ts +44 -0
- package/src/apps/pattern/view/svg-utils/svg-element-factory.ts +94 -0
- package/src/apps/pattern/view/svg-utils/svg-renderer.ts +51 -0
- package/src/apps/pattern/view/svg-utils/text-dimensions-calculator.ts +29 -0
- package/src/apps/pattern/view/svg-utils/title-block.ts +53 -0
- package/src/apps/pattern/view/svg-utils/utils.ts +37 -0
- package/src/apps/pattern/view/types.ts +14 -0
- package/src/apps/pattern/view/ui.ts +61 -0
- package/src/{model/structure-app → apps/structure/model}/mol-transformations.ts +3 -3
- package/src/{model/structure-app → apps/structure/model}/monomer-code-parser.ts +9 -10
- package/src/{model/structure-app → apps/structure/model}/oligo-structure.ts +4 -4
- package/src/{model/structure-app → apps/structure/model}/sequence-to-molfile.ts +2 -2
- package/src/{view/apps/oligo-structure.ts → apps/structure/view/ui.ts} +31 -17
- package/src/{model/translator-app → apps/translator/model}/conversion-utils.ts +25 -7
- package/src/{model/translator-app → apps/translator/model}/format-converter.ts +7 -12
- package/src/{view/const/oligo-translator.ts → apps/translator/view/const.ts} +1 -1
- package/src/{view/apps/oligo-translator.ts → apps/translator/view/ui.ts} +88 -42
- package/src/demo/demo-st-ui.ts +12 -32
- package/src/package.ts +91 -55
- package/src/plugins/mermade.ts +9 -9
- package/src/polytool/const.ts +28 -0
- package/src/polytool/csv-to-json-monomer-lib-converter.ts +40 -0
- package/src/polytool/cyclized.ts +56 -0
- package/src/polytool/monomer-lib-handler.ts +115 -0
- package/src/polytool/pt-conversion.ts +307 -0
- package/src/polytool/pt-dialog.ts +115 -0
- package/src/polytool/pt-enumeration.ts +127 -0
- package/src/polytool/pt-rules.ts +73 -0
- package/src/polytool/utils.ts +27 -0
- package/src/tests/const.ts +5 -5
- package/src/tests/formats-support.ts +6 -6
- package/src/tests/formats-to-helm.ts +5 -5
- package/src/tests/helm-to-nucleotides.ts +5 -10
- package/tsconfig.json +3 -9
- package/webpack.config.js +3 -0
- package/files/axolabs-style.json +0 -97
- package/src/model/data-loading-utils/json-loader.ts +0 -38
- package/src/model/pattern-app/const.ts +0 -33
- package/src/model/pattern-app/draw-svg.ts +0 -193
- package/src/model/pattern-app/helpers.ts +0 -96
- package/src/model/pattern-app/oligo-pattern.ts +0 -111
- package/src/view/app-ui.ts +0 -193
- package/src/view/apps/oligo-pattern.ts +0 -759
- /package/src/{model → apps/common/model}/helpers.ts +0 -0
- /package/src/{model → apps/common/model}/monomer-lib/const.ts +0 -0
- /package/src/{view/utils → apps/common/view/components}/app-info-dialog.ts +0 -0
- /package/src/{view/utils → apps/common/view/components}/colored-input/input-painters.ts +0 -0
- /package/src/{view/style/colored-text-input.css → apps/common/view/components/colored-input/style.css} +0 -0
- /package/src/{view/utils → apps/common/view/components}/router.ts +0 -0
- /package/src/{model/structure-app → apps/structure/model}/const.ts +0 -0
- /package/src/{view/style/structure-app.css → apps/structure/view/style.css} +0 -0
- /package/src/{model/translator-app → apps/translator/model}/const.ts +0 -0
- /package/src/{view/style/translator-app.css → apps/translator/view/style.css} +0 -0
|
@@ -1,759 +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
|
-
|
|
6
|
-
import {axolabsStyleMap} from '../../model/data-loading-utils/json-loader';
|
|
7
|
-
import {
|
|
8
|
-
DEFAULT_PTO, DEFAULT_SEQUENCE_LENGTH, MAX_SEQUENCE_LENGTH, USER_STORAGE_KEY, SS, AS, STRAND_NAME, STRANDS, TERMINAL, TERMINAL_KEYS, THREE_PRIME, FIVE_PRIME, JSON_FIELD as FIELD
|
|
9
|
-
} from '../../model/pattern-app/const';
|
|
10
|
-
import {isOverhang} from '../../model/pattern-app/helpers';
|
|
11
|
-
import {generateExample, translateSequence, getShortName, isCurrentUserCreatedThisPattern, findDuplicates, addColumnWithIds, addColumnWithTranslatedSequences} from '../../model//pattern-app/oligo-pattern';
|
|
12
|
-
import {drawAxolabsPattern} from '../../model/pattern-app/draw-svg';
|
|
13
|
-
// todo: remove ts-ignore
|
|
14
|
-
//@ts-ignore
|
|
15
|
-
import * as svg from 'save-svg-as-png';
|
|
16
|
-
import $ from 'cash-dom';
|
|
17
|
-
|
|
18
|
-
type BooleanInput = DG.InputBase<boolean | null>;
|
|
19
|
-
type StringInput = DG.InputBase<string | null>;
|
|
20
|
-
|
|
21
|
-
export class PatternLayoutHandler {
|
|
22
|
-
get htmlDivElement() {
|
|
23
|
-
function updateModification(strand: string) {
|
|
24
|
-
modificationItems[strand].innerHTML = '';
|
|
25
|
-
ptoLinkages[strand] = ptoLinkages[strand].concat(Array(maxStrandLength[strand] - baseInputsObject[strand].length).fill(fullyPto));
|
|
26
|
-
baseInputsObject[strand] = baseInputsObject[strand].concat(Array(maxStrandLength[strand] - baseInputsObject[strand].length).fill(sequenceBase));
|
|
27
|
-
let nucleotideCounter = 0;
|
|
28
|
-
for (let i = 0; i < strandLengthInput[strand].value!; i++) {
|
|
29
|
-
ptoLinkages[strand][i] = ui.boolInput('', ptoLinkages[strand][i].value!, () => {
|
|
30
|
-
updateSvgScheme();
|
|
31
|
-
updateOutputExamples();
|
|
32
|
-
});
|
|
33
|
-
baseInputsObject[strand][i] = ui.choiceInput('', baseInputsObject[strand][i].value, baseChoices, (v: string) => {
|
|
34
|
-
if (!enumerateModifications.includes(v)) {
|
|
35
|
-
enumerateModifications.push(v);
|
|
36
|
-
isEnumerateModificationsDiv.append(
|
|
37
|
-
ui.divText('', {style: {width: '25px'}}),
|
|
38
|
-
ui.boolInput(v, true, (boolV: boolean) => {
|
|
39
|
-
if (boolV) {
|
|
40
|
-
if (!enumerateModifications.includes(v))
|
|
41
|
-
enumerateModifications.push(v);
|
|
42
|
-
} else {
|
|
43
|
-
const index = enumerateModifications.indexOf(v, 0);
|
|
44
|
-
if (index > -1)
|
|
45
|
-
enumerateModifications.splice(index, 1);
|
|
46
|
-
}
|
|
47
|
-
updateSvgScheme();
|
|
48
|
-
}).root,
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
updateModification(AS);
|
|
52
|
-
updateSvgScheme();
|
|
53
|
-
updateOutputExamples();
|
|
54
|
-
});
|
|
55
|
-
$(baseInputsObject[strand][i].root).addClass('st-pattern-choice-input');
|
|
56
|
-
if (!isOverhang(baseInputsObject[strand][i].value!))
|
|
57
|
-
nucleotideCounter++;
|
|
58
|
-
|
|
59
|
-
modificationItems[strand].append(
|
|
60
|
-
ui.divH([
|
|
61
|
-
ui.div([ui.label(isOverhang(baseInputsObject[strand][i].value!) ? '' : String(nucleotideCounter))],
|
|
62
|
-
{style: {width: '20px'}})!,
|
|
63
|
-
ui.block75([baseInputsObject[strand][i].root])!,
|
|
64
|
-
ui.div([ptoLinkages[strand][i]])!,
|
|
65
|
-
], {style: {alignItems: 'center'}}),
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function updateUiForNewSequenceLength() {
|
|
71
|
-
if (Object.values(strandLengthInput).every((input) => input.value! < MAX_SEQUENCE_LENGTH)) {
|
|
72
|
-
STRANDS.forEach((strand) => {
|
|
73
|
-
if (strandLengthInput[strand].value! > maxStrandLength[strand])
|
|
74
|
-
maxStrandLength[strand] = strandLengthInput[strand].value!;
|
|
75
|
-
updateModification(strand);
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
updateSvgScheme();
|
|
79
|
-
updateInputExamples();
|
|
80
|
-
updateOutputExamples();
|
|
81
|
-
} else {
|
|
82
|
-
ui.dialog('Out of range')
|
|
83
|
-
.add(ui.divText('Sequence length should be less than ' +
|
|
84
|
-
MAX_SEQUENCE_LENGTH.toString() + ' due to UI constrains.'))
|
|
85
|
-
.onOK(()=> {Object.values(strandLengthInput).every((input)=> input.value = 34)})
|
|
86
|
-
.onCancel(()=> {Object.values(strandLengthInput).every((input)=> input.value = 34)})
|
|
87
|
-
.showModal(false);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// todo: unify with updateBases
|
|
92
|
-
function updatePto(newPtoValue: boolean): void {
|
|
93
|
-
STRANDS.forEach((strand) => {
|
|
94
|
-
for (let i = 0; i < ptoLinkages[strand].length; i++)
|
|
95
|
-
ptoLinkages[strand][i].value = newPtoValue;
|
|
96
|
-
})
|
|
97
|
-
updateSvgScheme();
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function updateBases(newBasisValue: string): void {
|
|
101
|
-
STRANDS.forEach((strand) => {
|
|
102
|
-
for (let i = 0; i < baseInputsObject[strand].length; i++)
|
|
103
|
-
baseInputsObject[strand][i].value = newBasisValue;
|
|
104
|
-
})
|
|
105
|
-
updateSvgScheme();
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function updateInputExamples() {
|
|
109
|
-
STRANDS.forEach((s) => {
|
|
110
|
-
if (strandColumnInput[s].value === '')
|
|
111
|
-
inputExample[s].value = generateExample(strandLengthInput[s].value!, sequenceBase.value!);
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function updateOutputExamples() {
|
|
116
|
-
const conditions = [true, createAsStrand.value];
|
|
117
|
-
STRANDS.forEach((strand, i) => {
|
|
118
|
-
if (conditions[i]) {
|
|
119
|
-
outputExample[strand].value = translateSequence(inputExample[strand].value, baseInputsObject[strand], ptoLinkages[strand], terminalModification[strand][FIVE_PRIME], terminalModification[strand][THREE_PRIME], firstPto[strand].value!);
|
|
120
|
-
}
|
|
121
|
-
})
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function updateSvgScheme() {
|
|
125
|
-
svgDiv.innerHTML = '';
|
|
126
|
-
svgDiv.append(
|
|
127
|
-
ui.span([
|
|
128
|
-
|
|
129
|
-
// todo: refactor the funciton, reduce # of args
|
|
130
|
-
drawAxolabsPattern(
|
|
131
|
-
getShortName(saveAs.value),
|
|
132
|
-
createAsStrand.value!,
|
|
133
|
-
|
|
134
|
-
baseInputsObject[SS].slice(0, strandLengthInput[SS].value!).map((e) => e.value!),
|
|
135
|
-
baseInputsObject[AS].slice(0, strandLengthInput[AS].value!).map((e) => e.value!),
|
|
136
|
-
|
|
137
|
-
[firstPto[SS].value!].concat(ptoLinkages[SS].slice(0, strandLengthInput[SS].value!).map((e) => e.value!)),
|
|
138
|
-
[firstPto[AS].value!].concat(ptoLinkages[AS].slice(0, strandLengthInput[AS].value!).map((e) => e.value!)),
|
|
139
|
-
|
|
140
|
-
terminalModification[SS][THREE_PRIME].value,
|
|
141
|
-
terminalModification[SS][FIVE_PRIME].value,
|
|
142
|
-
|
|
143
|
-
terminalModification[AS][THREE_PRIME].value,
|
|
144
|
-
terminalModification[AS][FIVE_PRIME].value,
|
|
145
|
-
|
|
146
|
-
comment.value,
|
|
147
|
-
enumerateModifications,
|
|
148
|
-
),
|
|
149
|
-
]),
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// todo: rename
|
|
154
|
-
function detectDefaultBasis(array: string[]) {
|
|
155
|
-
const modeMap: {[index: string]: number} = {};
|
|
156
|
-
let maxEl = array[0];
|
|
157
|
-
let maxCount = 1;
|
|
158
|
-
for (let i = 0; i < array.length; i++) {
|
|
159
|
-
const el = array[i];
|
|
160
|
-
if (modeMap[el] === null)
|
|
161
|
-
modeMap[el] = 1;
|
|
162
|
-
else
|
|
163
|
-
modeMap[el]++;
|
|
164
|
-
if (modeMap[el] > maxCount) {
|
|
165
|
-
maxEl = el;
|
|
166
|
-
maxCount = modeMap[el];
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return maxEl;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
async function parsePatternAndUpdateUi(newName: string) {
|
|
173
|
-
const pi = DG.TaskBarProgressIndicator.create('Loading pattern...');
|
|
174
|
-
await grok.dapi.userDataStorage.get(USER_STORAGE_KEY, false).then((entities) => {
|
|
175
|
-
const obj = JSON.parse(entities[newName]);
|
|
176
|
-
sequenceBase.value = detectDefaultBasis(obj[FIELD.AS_BASES].concat(obj[FIELD.SS_BASES]));
|
|
177
|
-
createAsStrand.value = (obj[FIELD.AS_BASES].length > 0);
|
|
178
|
-
saveAs.value = newName;
|
|
179
|
-
|
|
180
|
-
let fields = [FIELD.SS_BASES, FIELD.AS_BASES];
|
|
181
|
-
STRANDS.forEach((strand, i) => {
|
|
182
|
-
baseInputsObject[strand] = [];
|
|
183
|
-
const field = fields[i];
|
|
184
|
-
for (let j = 0; j < obj[field].length; j++)
|
|
185
|
-
baseInputsObject[strand].push(ui.choiceInput('', obj[field][j], baseChoices));
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
fields = [FIELD.SS_PTO, FIELD.AS_PTO];
|
|
189
|
-
STRANDS.forEach((s, i) => {
|
|
190
|
-
const field = fields[i];
|
|
191
|
-
firstPto[s].value = obj[field][0];
|
|
192
|
-
ptoLinkages[s] = [];
|
|
193
|
-
for (let j = 1; j < obj[field].length; j++)
|
|
194
|
-
ptoLinkages[s].push(ui.boolInput('', obj[field][j]));
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
fields = [FIELD.SS_BASES, FIELD.AS_BASES];
|
|
198
|
-
STRANDS.forEach((strand, i) => {
|
|
199
|
-
strandLengthInput[strand].value = obj[fields[i]].length;
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
const field = [[FIELD.SS_3, FIELD.SS_5], [FIELD.AS_3, FIELD.AS_5]];
|
|
203
|
-
STRANDS.forEach((strand, i) => {
|
|
204
|
-
TERMINAL_KEYS.forEach((terminal, j) => {
|
|
205
|
-
terminalModification[strand][terminal].value = obj[field[i][j]];
|
|
206
|
-
})
|
|
207
|
-
})
|
|
208
|
-
comment.value = obj[FIELD.COMMENT];
|
|
209
|
-
});
|
|
210
|
-
pi.close();
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function allColumnValuesOfEqualLength(colName: string): boolean {
|
|
214
|
-
const col = tableInput.value!.getCol(colName);
|
|
215
|
-
let allLengthsAreTheSame = true;
|
|
216
|
-
for (let i = 1; i < col.length; i++) {
|
|
217
|
-
if (col.get(i - 1).length !== col.get(i).length && col.get(i).length !== 0) {
|
|
218
|
-
allLengthsAreTheSame = false;
|
|
219
|
-
break;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
if (!allLengthsAreTheSame) {
|
|
223
|
-
const dialog = ui.dialog('Sequences lengths mismatch');
|
|
224
|
-
$(dialog.getButton('OK')).hide();
|
|
225
|
-
dialog
|
|
226
|
-
.add(ui.divText('The sequence length should match the number of Raw sequences in the input file'))
|
|
227
|
-
.add(ui.divText('\'ADD COLUMN\' to see sequences lengths'))
|
|
228
|
-
.addButton('ADD COLUMN', () => {
|
|
229
|
-
tableInput.value!.columns.addNewInt('Sequences lengths in ' + colName).init((j: number) => col.get(j).length);
|
|
230
|
-
grok.shell.info('Column with lengths added to \'' + tableInput.value!.name + '\'');
|
|
231
|
-
dialog.close();
|
|
232
|
-
grok.shell.v = grok.shell.getTableView(tableInput.value!.name);
|
|
233
|
-
})
|
|
234
|
-
.show();
|
|
235
|
-
}
|
|
236
|
-
if (col.get(0).length !== strandLengthInput[SS].value) {
|
|
237
|
-
const d = ui.dialog('Length was updated by value to from imported file');
|
|
238
|
-
d.add(ui.divText('Latest modifications may not take effect during translation'))
|
|
239
|
-
.onOK(() => grok.shell.info('Lengths changed')).show();
|
|
240
|
-
}
|
|
241
|
-
return allLengthsAreTheSame;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
async function getCurrentUserName(): Promise<string> {
|
|
245
|
-
return await grok.dapi.users.current().then((user) => {
|
|
246
|
-
return ' (created by ' + user.friendlyName + ')';
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
async function postPatternToUserStorage() {
|
|
251
|
-
const currUserName = await getCurrentUserName();
|
|
252
|
-
saveAs.value = (saveAs.stringValue.includes('(created by ')) ?
|
|
253
|
-
getShortName(saveAs.value) + currUserName :
|
|
254
|
-
saveAs.stringValue + currUserName;
|
|
255
|
-
return grok.dapi.userDataStorage.postValue(
|
|
256
|
-
USER_STORAGE_KEY,
|
|
257
|
-
saveAs.value,
|
|
258
|
-
JSON.stringify({
|
|
259
|
-
[FIELD.SS_BASES]: baseInputsObject[SS].slice(0, strandLengthInput[SS].value!).map((e) => e.value),
|
|
260
|
-
[FIELD.AS_BASES]: baseInputsObject[AS].slice(0, strandLengthInput[AS].value!).map((e) => e.value),
|
|
261
|
-
[FIELD.SS_PTO]: [firstPto[SS].value].concat(ptoLinkages[SS].slice(0, strandLengthInput[SS].value!).map((e) => e.value)),
|
|
262
|
-
[FIELD.AS_PTO]: [firstPto[AS].value].concat(ptoLinkages[AS].slice(0, strandLengthInput[AS].value!).map((e) => e.value)),
|
|
263
|
-
[FIELD.SS_3]: terminalModification[SS][THREE_PRIME].value,
|
|
264
|
-
[FIELD.SS_5]:terminalModification[SS][FIVE_PRIME].value,
|
|
265
|
-
[FIELD.AS_3]: terminalModification[AS][THREE_PRIME].value,
|
|
266
|
-
[FIELD.AS_5]: terminalModification[AS][FIVE_PRIME].value,
|
|
267
|
-
[FIELD.COMMENT]: comment.value,
|
|
268
|
-
}),
|
|
269
|
-
false,
|
|
270
|
-
).then(() => grok.shell.info('Pattern \'' + saveAs.value + '\' was successfully uploaded!'));
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
async function updatePatternsList() {
|
|
274
|
-
grok.dapi.userDataStorage.get(USER_STORAGE_KEY, false).then(async (entities) => {
|
|
275
|
-
const lstMy: string[] = [];
|
|
276
|
-
const lstOthers: string[] = [];
|
|
277
|
-
|
|
278
|
-
// TODO: display short name, but use long for querying userdataStorage
|
|
279
|
-
for (const ent of Object.keys(entities)) {
|
|
280
|
-
if (await isCurrentUserCreatedThisPattern(ent))
|
|
281
|
-
lstOthers.push(ent);
|
|
282
|
-
else
|
|
283
|
-
lstMy.push(ent);//getShortName(ent));
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
let loadPattern = ui.choiceInput('Load pattern', '', lstMy, (v: string) => parsePatternAndUpdateUi(v));
|
|
287
|
-
|
|
288
|
-
const currentUserName = (await grok.dapi.users.current()).friendlyName;
|
|
289
|
-
const otherUsers = 'Other users';
|
|
290
|
-
|
|
291
|
-
const patternListChoiceInput = ui.choiceInput('', currentUserName, [currentUserName, otherUsers], (v: string) => {
|
|
292
|
-
const currentList = v === currentUserName ? lstMy : lstOthers;
|
|
293
|
-
loadPattern = ui.choiceInput('Load pattern', '', currentList, (v: string) => parsePatternAndUpdateUi(v));
|
|
294
|
-
|
|
295
|
-
loadPattern.root.append(patternListChoiceInput.input);
|
|
296
|
-
loadPattern.root.append(loadPattern.input);
|
|
297
|
-
// @ts-ignore
|
|
298
|
-
loadPattern.input.style.maxWidth = '120px';
|
|
299
|
-
loadPattern.input.style.marginLeft = '12px';
|
|
300
|
-
loadPattern.setTooltip('Apply Existing Pattern');
|
|
301
|
-
|
|
302
|
-
loadPatternDiv.innerHTML = '';
|
|
303
|
-
loadPatternDiv.append(loadPattern.root);
|
|
304
|
-
loadPattern.root.append(
|
|
305
|
-
ui.div([
|
|
306
|
-
ui.button(ui.iconFA('trash-alt', () => {}), async () => {
|
|
307
|
-
if (loadPattern.value === null)
|
|
308
|
-
grok.shell.warning('Choose pattern to delete');
|
|
309
|
-
else if (await isCurrentUserCreatedThisPattern(saveAs.value))
|
|
310
|
-
grok.shell.warning('Cannot delete pattern, created by other user');
|
|
311
|
-
else {
|
|
312
|
-
await grok.dapi.userDataStorage.remove(USER_STORAGE_KEY, loadPattern.value, false)
|
|
313
|
-
.then(() => grok.shell.info('Pattern \'' + loadPattern.value + '\' deleted'));
|
|
314
|
-
}
|
|
315
|
-
await updatePatternsList();
|
|
316
|
-
}),
|
|
317
|
-
], 'ui-input-options'),
|
|
318
|
-
);
|
|
319
|
-
});
|
|
320
|
-
patternListChoiceInput.input.style.maxWidth = '142px';
|
|
321
|
-
loadPattern.root.append(patternListChoiceInput.input);
|
|
322
|
-
loadPattern.root.append(loadPattern.input);
|
|
323
|
-
// @ts-ignore
|
|
324
|
-
loadPattern.input.style.maxWidth = '100px';
|
|
325
|
-
loadPattern.setTooltip('Apply Existing Pattern');
|
|
326
|
-
|
|
327
|
-
loadPatternDiv.innerHTML = '';
|
|
328
|
-
loadPatternDiv.append(loadPattern.root);
|
|
329
|
-
loadPattern.root.append(
|
|
330
|
-
ui.div([
|
|
331
|
-
ui.button(ui.iconFA('trash-alt', () => {}), async () => {
|
|
332
|
-
if (loadPattern.value === null)
|
|
333
|
-
grok.shell.warning('Choose pattern to delete');
|
|
334
|
-
else if (await isCurrentUserCreatedThisPattern(saveAs.value))
|
|
335
|
-
grok.shell.warning('Cannot delete pattern, created by other user');
|
|
336
|
-
else {
|
|
337
|
-
await grok.dapi.userDataStorage.remove(USER_STORAGE_KEY, loadPattern.value, false)
|
|
338
|
-
.then(() => grok.shell.info('Pattern \'' + loadPattern.value + '\' deleted'));
|
|
339
|
-
}
|
|
340
|
-
await updatePatternsList();
|
|
341
|
-
}),
|
|
342
|
-
], 'ui-input-options'),
|
|
343
|
-
);
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
async function savePattern() {
|
|
348
|
-
await grok.dapi.userDataStorage.get(USER_STORAGE_KEY, false)
|
|
349
|
-
.then((entities) => {
|
|
350
|
-
if (Object.keys(entities).includes(saveAs.value)) {
|
|
351
|
-
const dialog = ui.dialog('Pattern already exists');
|
|
352
|
-
$(dialog.getButton('OK')).hide();
|
|
353
|
-
dialog
|
|
354
|
-
.add(ui.divText('Pattern name \'' + saveAs.value + '\' already exists.'))
|
|
355
|
-
.add(ui.divText('Replace pattern?'))
|
|
356
|
-
.addButton('YES', async () => {
|
|
357
|
-
await grok.dapi.userDataStorage.remove(USER_STORAGE_KEY, saveAs.value, false)
|
|
358
|
-
.then(() => postPatternToUserStorage());
|
|
359
|
-
dialog.close();
|
|
360
|
-
})
|
|
361
|
-
.show();
|
|
362
|
-
} else
|
|
363
|
-
postPatternToUserStorage();
|
|
364
|
-
});
|
|
365
|
-
await updatePatternsList();
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
function validateStrandColumn(colName: string, strand: string): void {
|
|
369
|
-
const allLengthsAreTheSame: boolean = allColumnValuesOfEqualLength(colName);
|
|
370
|
-
const firstSequence = tableInput.value!.getCol(colName).get(0);
|
|
371
|
-
if (allLengthsAreTheSame && firstSequence.length !== strandLengthInput[strand].value)
|
|
372
|
-
strandLengthInput[strand].value = tableInput.value!.getCol(colName).get(0).length;
|
|
373
|
-
inputExample[strand].value = firstSequence;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
function validateIdsColumn(colName: string) {
|
|
377
|
-
const col = tableInput.value!.getCol(colName);
|
|
378
|
-
if (col.type !== DG.TYPE.INT)
|
|
379
|
-
grok.shell.error('Column should contain integers only');
|
|
380
|
-
//@ts-ignore
|
|
381
|
-
else if (col.categories.filter((e) => e !== '').length < col.toList().filter((e) => e !== '').length) {
|
|
382
|
-
const duplicates = findDuplicates(col.getRawData());
|
|
383
|
-
ui.dialog('Non-unique IDs')
|
|
384
|
-
.add(ui.divText('Press \'OK\' to select rows with non-unique values'))
|
|
385
|
-
.onOK(() => {
|
|
386
|
-
const selection = tableInput.value!.selection;
|
|
387
|
-
selection.init((i: number) => duplicates.indexOf(col.get(i)) > -1);
|
|
388
|
-
grok.shell.v = grok.shell.getTableView(tableInput.value!.name);
|
|
389
|
-
grok.shell.info('Rows are selected in table \'' + tableInput.value!.name + '\'');
|
|
390
|
-
})
|
|
391
|
-
.show();
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
const baseChoices: string[] = Object.keys(axolabsStyleMap);
|
|
396
|
-
const defaultBase: string = baseChoices[0];
|
|
397
|
-
const enumerateModifications = [defaultBase];
|
|
398
|
-
const sequenceBase = ui.choiceInput('Sequence basis', defaultBase, baseChoices, (v: string) => {
|
|
399
|
-
updateBases(v);
|
|
400
|
-
updateOutputExamples();
|
|
401
|
-
});
|
|
402
|
-
const fullyPto = ui.boolInput('Fully PTO', DEFAULT_PTO, (v: boolean) => {
|
|
403
|
-
STRANDS.forEach((s) => { firstPto[s].value = v; })
|
|
404
|
-
updatePto(v);
|
|
405
|
-
updateOutputExamples();
|
|
406
|
-
});
|
|
407
|
-
fullyPto.captionLabel.classList.add('ui-label-right');
|
|
408
|
-
fullyPto.captionLabel.style.textAlign = 'left';
|
|
409
|
-
fullyPto.captionLabel.style.maxWidth = '100px';
|
|
410
|
-
fullyPto.captionLabel.style.maxWidth = '100px';
|
|
411
|
-
fullyPto.captionLabel.style.minWidth = '40px';
|
|
412
|
-
fullyPto.captionLabel.style.width = 'auto';
|
|
413
|
-
|
|
414
|
-
const maxStrandLength = Object.fromEntries(STRANDS.map(
|
|
415
|
-
(strand) => [strand, DEFAULT_SEQUENCE_LENGTH]
|
|
416
|
-
));
|
|
417
|
-
// todo: remove vague legacy 'items' from name
|
|
418
|
-
const modificationItems = Object.fromEntries(STRANDS.map(
|
|
419
|
-
(strand) => [strand, ui.div([])]
|
|
420
|
-
));
|
|
421
|
-
const ptoLinkages = Object.fromEntries(STRANDS.map(
|
|
422
|
-
(strand) => [strand, Array<BooleanInput>(DEFAULT_SEQUENCE_LENGTH)
|
|
423
|
-
.fill(ui.boolInput('', DEFAULT_PTO))]
|
|
424
|
-
));
|
|
425
|
-
const baseInputsObject = Object.fromEntries(STRANDS.map(
|
|
426
|
-
(strand) => {
|
|
427
|
-
const choiceInputs = Array<StringInput>(DEFAULT_SEQUENCE_LENGTH)
|
|
428
|
-
.fill(ui.choiceInput('', defaultBase, baseChoices));
|
|
429
|
-
return [strand, choiceInputs];
|
|
430
|
-
}
|
|
431
|
-
));
|
|
432
|
-
const strandLengthInput = Object.fromEntries(STRANDS.map(
|
|
433
|
-
(strand) => {
|
|
434
|
-
const input = ui.intInput(`${STRAND_NAME[strand]} length`, DEFAULT_SEQUENCE_LENGTH, () => updateUiForNewSequenceLength());
|
|
435
|
-
input.setTooltip(`Length of ${STRAND_NAME[strand].toLowerCase()}, including overhangs`);
|
|
436
|
-
return [strand, input];
|
|
437
|
-
}));
|
|
438
|
-
const strandVar = Object.fromEntries(STRANDS.map((strand) => [strand, '']));
|
|
439
|
-
const inputExample = Object.fromEntries(STRANDS.map(
|
|
440
|
-
(strand) => [strand, ui.textInput(
|
|
441
|
-
``, generateExample(strandLengthInput[strand].value!, sequenceBase.value!))
|
|
442
|
-
]));
|
|
443
|
-
|
|
444
|
-
const strandColumnInput = Object.fromEntries(STRANDS.map((strand) => {
|
|
445
|
-
const input = ui.choiceInput(`${STRAND_NAME[strand]} column`, '', [], (colName: string) => {
|
|
446
|
-
validateStrandColumn(colName, strand);
|
|
447
|
-
strandVar[strand] = colName;
|
|
448
|
-
});
|
|
449
|
-
return [strand, input];
|
|
450
|
-
}));
|
|
451
|
-
|
|
452
|
-
const firstPto = Object.fromEntries(STRANDS.map((strand) => {
|
|
453
|
-
const input = ui.boolInput(`First ${strand} PTO`, fullyPto.value!, () => updateSvgScheme());
|
|
454
|
-
input.setTooltip(`ps linkage before first nucleotide of ${STRAND_NAME[strand].toLowerCase()}`);
|
|
455
|
-
|
|
456
|
-
input.captionLabel.classList.add('ui-label-right');
|
|
457
|
-
input.captionLabel.style.textAlign = 'left';
|
|
458
|
-
input.captionLabel.style.maxWidth = '100px';
|
|
459
|
-
input.captionLabel.style.minWidth = '40px';
|
|
460
|
-
input.captionLabel.style.width = 'auto';
|
|
461
|
-
|
|
462
|
-
return [strand, input];
|
|
463
|
-
}));
|
|
464
|
-
|
|
465
|
-
const terminalModification = Object.fromEntries(STRANDS.map((strand) => {
|
|
466
|
-
const inputs = Object.fromEntries(TERMINAL_KEYS.map((key) => {
|
|
467
|
-
const input = ui.stringInput(`${strand} ${TERMINAL[key]}\' Modification`, '', () => {
|
|
468
|
-
updateSvgScheme();
|
|
469
|
-
updateOutputExamples();
|
|
470
|
-
});
|
|
471
|
-
input.setTooltip(`Additional ${strand} ${TERMINAL[key]}\' Modification`);
|
|
472
|
-
return [key, input];
|
|
473
|
-
}));
|
|
474
|
-
return [strand, inputs];
|
|
475
|
-
}));
|
|
476
|
-
|
|
477
|
-
const outputExample = Object.fromEntries(STRANDS.map((strand) => {
|
|
478
|
-
const input = ui.textInput('', translateSequence(
|
|
479
|
-
inputExample[strand].value, baseInputsObject[strand], ptoLinkages[strand], terminalModification[strand][THREE_PRIME],terminalModification[strand][FIVE_PRIME], firstPto[strand].value!
|
|
480
|
-
));
|
|
481
|
-
input.input.style.minWidth = 'none';
|
|
482
|
-
input.input.style.flexGrow = '1';
|
|
483
|
-
$(input.root.lastChild).css('height', 'auto');
|
|
484
|
-
return [strand, input];
|
|
485
|
-
}));
|
|
486
|
-
|
|
487
|
-
const modificationSection = Object.fromEntries(STRANDS.map((strand) => {
|
|
488
|
-
const panel = ui.block([
|
|
489
|
-
ui.h1(`${STRAND_NAME[strand]}`),
|
|
490
|
-
ui.divH([
|
|
491
|
-
ui.div([ui.divText('#')], {style: {width: '20px'}})!,
|
|
492
|
-
ui.block75([ui.divText('Modification')])!,
|
|
493
|
-
ui.div([ui.divText('PTO')])!,
|
|
494
|
-
]),
|
|
495
|
-
modificationItems[strand],
|
|
496
|
-
], {style: {paddingTop: '12px'}});
|
|
497
|
-
return [strand, panel];
|
|
498
|
-
}));
|
|
499
|
-
|
|
500
|
-
STRANDS.forEach((s) => {
|
|
501
|
-
|
|
502
|
-
inputExample[s].input.style.resize = 'none';
|
|
503
|
-
outputExample[s].input.style.resize = 'none';
|
|
504
|
-
inputExample[s].input.style.minWidth = 'none';
|
|
505
|
-
inputExample[s].input.style.flexGrow = '1';
|
|
506
|
-
outputExample[s].input.style.minWidth = 'none';
|
|
507
|
-
outputExample[s].input.style.flexGrow = '1';
|
|
508
|
-
let options = ui.div([
|
|
509
|
-
ui.button(ui.iconFA('copy', () => {}), () => {
|
|
510
|
-
navigator.clipboard.writeText(outputExample[s].value).then(() =>
|
|
511
|
-
grok.shell.info('Sequence was copied to clipboard'));
|
|
512
|
-
}),
|
|
513
|
-
], 'ui-input-options');
|
|
514
|
-
options.style.height = 'inherit';
|
|
515
|
-
outputExample[s].root.append(
|
|
516
|
-
options
|
|
517
|
-
);
|
|
518
|
-
})
|
|
519
|
-
|
|
520
|
-
// const inputIdColumnDiv = ui.div([]);
|
|
521
|
-
const svgDiv = ui.div([]);
|
|
522
|
-
const asExampleDiv = ui.div([], 'ui-form ui-form-wide');
|
|
523
|
-
const loadPatternDiv = ui.div([]);
|
|
524
|
-
const asModificationDiv = ui.form([]);
|
|
525
|
-
const isEnumerateModificationsDiv = ui.divH([
|
|
526
|
-
ui.boolInput(defaultBase, true, (v: boolean) => {
|
|
527
|
-
if (v) {
|
|
528
|
-
if (!enumerateModifications.includes(defaultBase))
|
|
529
|
-
enumerateModifications.push(defaultBase);
|
|
530
|
-
} else {
|
|
531
|
-
const index = enumerateModifications.indexOf(defaultBase, 0);
|
|
532
|
-
if (index > -1)
|
|
533
|
-
enumerateModifications.splice(index, 1);
|
|
534
|
-
}
|
|
535
|
-
updateSvgScheme();
|
|
536
|
-
updateOutputExamples();
|
|
537
|
-
}).root,
|
|
538
|
-
]);
|
|
539
|
-
|
|
540
|
-
const asLengthDiv = ui.div([strandLengthInput[AS].root]);
|
|
541
|
-
|
|
542
|
-
function getTableInput(tableList: DG.DataFrame[]): DG.InputBase {
|
|
543
|
-
const tableInput = ui.tableInput('Tables', tableList[0], tableList, () => {
|
|
544
|
-
const table = tableInput.value;
|
|
545
|
-
if (table === null) {
|
|
546
|
-
console.warn('Table is null');
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
|
-
const tableName = table!.name;
|
|
550
|
-
if (!grok.shell.tableNames.includes(tableName)) {
|
|
551
|
-
const view = grok.shell.v;
|
|
552
|
-
grok.shell.addTableView(table!);
|
|
553
|
-
grok.shell.v = view;
|
|
554
|
-
}
|
|
555
|
-
const columnNames = table.columns.names();
|
|
556
|
-
|
|
557
|
-
STRANDS.forEach((strand) => {
|
|
558
|
-
const defaultColumn = columnNames[0];
|
|
559
|
-
validateStrandColumn(defaultColumn, strand);
|
|
560
|
-
strandVar[strand] = defaultColumn;
|
|
561
|
-
const input = ui.choiceInput(`${STRAND_NAME[strand]} column`, defaultColumn, columnNames, (colName: string) => {
|
|
562
|
-
validateStrandColumn(colName, strand);
|
|
563
|
-
strandVar[strand] = colName;
|
|
564
|
-
console.log(`clicked ${strand} var:`, strandVar[strand]);
|
|
565
|
-
});
|
|
566
|
-
$(strandColumnInput[strand].root).replaceWith(input.root);
|
|
567
|
-
})
|
|
568
|
-
|
|
569
|
-
idVar = columnNames[0];
|
|
570
|
-
// todo: unify with inputStrandColumn
|
|
571
|
-
const idInput = ui.choiceInput('ID column', columnNames[0], columnNames, (colName: string) => {
|
|
572
|
-
validateIdsColumn(colName);
|
|
573
|
-
idVar = colName;
|
|
574
|
-
});
|
|
575
|
-
$(inputIdColumn.root).replaceWith(idInput.root);
|
|
576
|
-
});
|
|
577
|
-
return tableInput;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
const tableInput = getTableInput([]);
|
|
581
|
-
|
|
582
|
-
// todo: unify with strandVar
|
|
583
|
-
let idVar = '';
|
|
584
|
-
const inputIdColumn = ui.choiceInput('ID column', '', [], (colName: string) => {
|
|
585
|
-
validateIdsColumn(colName);
|
|
586
|
-
idVar = colName;
|
|
587
|
-
});
|
|
588
|
-
// inputIdColumnDiv.append(inputIdColumn.root);
|
|
589
|
-
|
|
590
|
-
updatePatternsList();
|
|
591
|
-
|
|
592
|
-
const createAsStrand = ui.boolInput('Anti sense strand', true, (v: boolean) => {
|
|
593
|
-
modificationSection[AS].hidden = !v;
|
|
594
|
-
strandColumnInput[AS].root.hidden = !v;
|
|
595
|
-
asLengthDiv.hidden = !v;
|
|
596
|
-
asModificationDiv.hidden = !v;
|
|
597
|
-
asExampleDiv.hidden = !v;
|
|
598
|
-
firstPto[AS].root.hidden = !v;
|
|
599
|
-
updateSvgScheme();
|
|
600
|
-
});
|
|
601
|
-
createAsStrand.setTooltip('Create antisense strand sections on SVG and table to the right');
|
|
602
|
-
|
|
603
|
-
const saveAs = ui.textInput('Save as', 'Pattern name', () => updateSvgScheme());
|
|
604
|
-
saveAs.setTooltip('Name Of New Pattern');
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
TERMINAL_KEYS.forEach((terminal) => {
|
|
608
|
-
asModificationDiv.append(terminalModification[AS][terminal].root);
|
|
609
|
-
})
|
|
610
|
-
|
|
611
|
-
const comment = ui.textInput('Comment', '', () => updateSvgScheme());
|
|
612
|
-
|
|
613
|
-
const savePatternButton = ui.bigButton('Save', () => {
|
|
614
|
-
if (saveAs.value !== '')
|
|
615
|
-
savePattern().then(() => grok.shell.info('Pattern saved'));
|
|
616
|
-
else {
|
|
617
|
-
const name = ui.stringInput('Enter name', '');
|
|
618
|
-
ui.dialog('Pattern Name')
|
|
619
|
-
.add(name.root)
|
|
620
|
-
.onOK(() => {
|
|
621
|
-
saveAs.value = name.value;
|
|
622
|
-
savePattern().then(() => grok.shell.info('Pattern saved'));
|
|
623
|
-
})
|
|
624
|
-
.show();
|
|
625
|
-
}
|
|
626
|
-
});
|
|
627
|
-
saveAs.addOptions(savePatternButton);
|
|
628
|
-
|
|
629
|
-
const convertSequenceButton = ui.bigButton('Convert', () => {
|
|
630
|
-
const condition = [true, createAsStrand.value];
|
|
631
|
-
console.log(`strand vars:`, Object.values(strandVar));
|
|
632
|
-
if (STRANDS.some((s, i) => condition[i] && strandVar[s] === ''))
|
|
633
|
-
grok.shell.info('Please select table and columns on which to apply pattern');
|
|
634
|
-
else if (STRANDS.some((s) => strandLengthInput[s].value !== inputExample[s].value.length)) {
|
|
635
|
-
const dialog = ui.dialog('Length Mismatch');
|
|
636
|
-
$(dialog.getButton('OK')).hide();
|
|
637
|
-
dialog
|
|
638
|
-
.add(ui.divText('Length of sequences in columns doesn\'t match entered length. Update length value?'))
|
|
639
|
-
.addButton('YES', () => {
|
|
640
|
-
STRANDS.forEach((s) => {
|
|
641
|
-
strandLengthInput[s].value = tableInput.value!.getCol(strandColumnInput[s].value!).getString(0).length;
|
|
642
|
-
})
|
|
643
|
-
dialog.close();
|
|
644
|
-
})
|
|
645
|
-
.show();
|
|
646
|
-
} else {
|
|
647
|
-
if (idVar !== '')
|
|
648
|
-
addColumnWithIds(tableInput.value!.name, idVar, getShortName(saveAs.value));
|
|
649
|
-
const condition = [true, createAsStrand.value];
|
|
650
|
-
STRANDS.forEach((strand, i) => {
|
|
651
|
-
if (condition[i])
|
|
652
|
-
addColumnWithTranslatedSequences(
|
|
653
|
-
tableInput.value!.name, strandVar[strand], baseInputsObject[strand], ptoLinkages[strand],
|
|
654
|
-
terminalModification[strand][FIVE_PRIME], terminalModification[strand][THREE_PRIME], firstPto[strand].value!);
|
|
655
|
-
})
|
|
656
|
-
grok.shell.v = grok.shell.getTableView(tableInput.value!.name);
|
|
657
|
-
grok.shell.info(((createAsStrand.value) ? 'Columns were' : 'Column was') +
|
|
658
|
-
' added to table \'' + tableInput.value!.name + '\'');
|
|
659
|
-
updateOutputExamples();
|
|
660
|
-
}
|
|
661
|
-
});
|
|
662
|
-
|
|
663
|
-
asExampleDiv.append(inputExample[AS].root);
|
|
664
|
-
asExampleDiv.append(outputExample[AS].root);
|
|
665
|
-
|
|
666
|
-
updateUiForNewSequenceLength();
|
|
667
|
-
|
|
668
|
-
const exampleSection = ui.div([
|
|
669
|
-
ui.h1('Conversion preview'),
|
|
670
|
-
inputExample[SS].root,
|
|
671
|
-
outputExample[SS].root,
|
|
672
|
-
asExampleDiv,
|
|
673
|
-
], 'ui-form ui-form-wide');
|
|
674
|
-
|
|
675
|
-
const inputsSection = ui.block50([
|
|
676
|
-
ui.h1('Convert options'),
|
|
677
|
-
tableInput.root,
|
|
678
|
-
strandColumnInput[SS].root,
|
|
679
|
-
strandColumnInput[AS].root,
|
|
680
|
-
inputIdColumn.root,
|
|
681
|
-
ui.buttonsInput([
|
|
682
|
-
convertSequenceButton,
|
|
683
|
-
]),
|
|
684
|
-
]);
|
|
685
|
-
inputsSection.classList.add('ui-form');
|
|
686
|
-
|
|
687
|
-
const downloadButton = ui.link('Download', () => svg.saveSvgAsPng(document.getElementById('mySvg'), saveAs.value,
|
|
688
|
-
{backgroundColor: 'white'}), 'Download pattern as PNG image', '');
|
|
689
|
-
|
|
690
|
-
const editPattern = ui.link('Edit pattern', ()=>{
|
|
691
|
-
ui.dialog('Edit pattern')
|
|
692
|
-
.add(ui.divV([
|
|
693
|
-
ui.h1('PTO'),
|
|
694
|
-
ui.divH([
|
|
695
|
-
fullyPto.root,
|
|
696
|
-
firstPto[SS].root,
|
|
697
|
-
firstPto[AS].root,
|
|
698
|
-
], {style:{gap:'12px'}})
|
|
699
|
-
]))
|
|
700
|
-
.add(ui.divH([
|
|
701
|
-
modificationSection[SS],
|
|
702
|
-
modificationSection[AS],
|
|
703
|
-
], {style:{gap:'24px'}}))
|
|
704
|
-
.onOK(()=>{grok.shell.info('Saved')})
|
|
705
|
-
.show()
|
|
706
|
-
}, 'Edit pattern', '');
|
|
707
|
-
|
|
708
|
-
strandLengthInput[SS].addCaption('Length');
|
|
709
|
-
|
|
710
|
-
return ui.splitH([
|
|
711
|
-
ui.box(
|
|
712
|
-
ui.div([
|
|
713
|
-
ui.h1('Pattern'),
|
|
714
|
-
createAsStrand.root,
|
|
715
|
-
strandLengthInput[SS],
|
|
716
|
-
strandLengthInput[AS],
|
|
717
|
-
sequenceBase.root,
|
|
718
|
-
comment.root,
|
|
719
|
-
loadPatternDiv,
|
|
720
|
-
saveAs.root,
|
|
721
|
-
ui.h1('Convert'),
|
|
722
|
-
tableInput.root,
|
|
723
|
-
strandColumnInput[SS],
|
|
724
|
-
strandColumnInput[AS],
|
|
725
|
-
inputIdColumn.root,
|
|
726
|
-
ui.buttonsInput([
|
|
727
|
-
convertSequenceButton,
|
|
728
|
-
]),
|
|
729
|
-
], 'ui-form')
|
|
730
|
-
, {style:{maxWidth:'450px'}}),
|
|
731
|
-
ui.panel([
|
|
732
|
-
svgDiv,
|
|
733
|
-
isEnumerateModificationsDiv,
|
|
734
|
-
ui.divH([
|
|
735
|
-
downloadButton,
|
|
736
|
-
editPattern
|
|
737
|
-
], {style:{gap:'12px', marginTop:'12px'}}),
|
|
738
|
-
ui.divH([
|
|
739
|
-
ui.divV([
|
|
740
|
-
ui.h1('Sense strand'),
|
|
741
|
-
inputExample[SS].root,
|
|
742
|
-
outputExample[SS].root,
|
|
743
|
-
], 'ui-block'),
|
|
744
|
-
ui.divV([
|
|
745
|
-
ui.h1('Anti sense'),
|
|
746
|
-
inputExample[AS],
|
|
747
|
-
outputExample[AS]
|
|
748
|
-
], 'ui-block'),
|
|
749
|
-
], {style:{gap:'24px', marginTop:'24px'}}),
|
|
750
|
-
ui.h1('Additional modifications'),
|
|
751
|
-
ui.form([
|
|
752
|
-
terminalModification[SS][FIVE_PRIME],
|
|
753
|
-
terminalModification[SS][THREE_PRIME],
|
|
754
|
-
]),
|
|
755
|
-
asModificationDiv,
|
|
756
|
-
], {style: {overflowX: 'scroll', padding:'12px 24px'}})
|
|
757
|
-
], {}, true)
|
|
758
|
-
}
|
|
759
|
-
}
|