@datagrok/sequence-translator 0.0.4 → 0.0.5

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/src/package.ts CHANGED
@@ -3,36 +3,38 @@ import * as grok from 'datagrok-api/grok';
3
3
  import * as ui from 'datagrok-api/ui';
4
4
  import * as DG from 'datagrok-api/dg';
5
5
  import * as OCL from 'openchemlib/full.js';
6
- import $ from "cash-dom";
7
- import {defineAxolabsPattern} from "./defineAxolabsPattern";
8
- import {map, stadardPhosphateLinkSmiles, SYNTHESIZERS, TECHNOLOGIES, MODIFICATIONS} from "./map";
6
+ import $ from 'cash-dom';
7
+ import {defineAxolabsPattern} from './defineAxolabsPattern';
8
+ import {saveSenseAntiSense} from './save-sense-antisense';
9
+ import {map, stadardPhosphateLinkSmiles, SYNTHESIZERS, TECHNOLOGIES, MODIFICATIONS} from './map';
9
10
 
10
- export let _package = new DG.Package();
11
+ export const _package = new DG.Package();
11
12
 
12
- const defaultInput = "AGGTCCTCTTGACTTAGGCC";
13
- const undefinedInputSequence = "Type of input sequence is undefined";
14
- const noTranslationTableAvailable = "No translation table available";
13
+ const defaultInput = 'AGGTCCTCTTGACTTAGGCC';
14
+ const undefinedInputSequence = 'Type of input sequence is undefined';
15
+ const noTranslationTableAvailable = 'No translation table available';
15
16
  const sequenceWasCopied = 'Copied';
16
17
  const tooltipSequence = 'Copy sequence';
17
18
 
18
- function getAllCodesOfSynthesizer(synthesizer: string) {
19
+ function getAllCodesOfSynthesizer(synthesizer: string): string[] {
19
20
  let codes: string[] = [];
20
- for (let technology of Object.keys(map[synthesizer]))
21
+ for (const technology of Object.keys(map[synthesizer]))
21
22
  codes = codes.concat(Object.keys(map[synthesizer][technology]));
22
23
  return codes.concat(Object.keys(MODIFICATIONS));
23
24
  }
24
25
 
25
26
  function getListOfPossibleSynthesizersByFirstMatchedCode(sequence: string): string[] {
26
- let synthesizers: string[] = [];
27
+ const synthesizers: string[] = [];
27
28
  Object.keys(map).forEach((synthesizer: string) => {
28
29
  const codes = getAllCodesOfSynthesizer(synthesizer);
29
30
  //TODO: get first non-dropdown code when there are two modifications
30
31
  let start = 0;
31
- for (let i = 0; i < sequence.length; i++)
32
+ for (let i = 0; i < sequence.length; i++) {
32
33
  if (sequence[i] == ')') {
33
34
  start = i + 1;
34
35
  break;
35
36
  }
37
+ }
36
38
  if (codes.some((s: string) => s == sequence.slice(start, start + s.length)))
37
39
  synthesizers.push(synthesizer);
38
40
  });
@@ -40,7 +42,7 @@ function getListOfPossibleSynthesizersByFirstMatchedCode(sequence: string): stri
40
42
  }
41
43
 
42
44
  function getListOfPossibleTechnologiesByFirstMatchedCode(sequence: string, synthesizer: string): string[] {
43
- let technologies: string[] = [];
45
+ const technologies: string[] = [];
44
46
  Object.keys(map[synthesizer]).forEach((technology: string) => {
45
47
  const codes = Object.keys(map[synthesizer][technology]).concat(Object.keys(MODIFICATIONS));
46
48
  if (codes.some((s) => s == sequence.slice(0, s.length)))
@@ -49,32 +51,36 @@ function getListOfPossibleTechnologiesByFirstMatchedCode(sequence: string, synth
49
51
  return technologies;
50
52
  }
51
53
 
52
- function isValidSequence(sequence: string) {
53
- let possibleSynthesizers = getListOfPossibleSynthesizersByFirstMatchedCode(sequence);
54
+ export function isValidSequence(sequence: string): {
55
+ indexOfFirstNotValidCharacter: number,
56
+ expectedSynthesizer: string | null,
57
+ expectedTechnology: string | null
58
+ } {
59
+ const possibleSynthesizers = getListOfPossibleSynthesizersByFirstMatchedCode(sequence);
54
60
  if (possibleSynthesizers.length == 0)
55
- return { indexOfFirstNotValidCharacter: 0, expectedType: null };
61
+ return {indexOfFirstNotValidCharacter: 0, expectedSynthesizer: null, expectedTechnology: null};
56
62
 
57
63
  let outputIndices = Array(possibleSynthesizers.length).fill(0);
58
64
 
59
- const firstUniqueCharacters = ['r', 'd'], nucleotides = ["A", "U", "T", "C", "G"];
65
+ const firstUniqueCharacters = ['r', 'd'];
66
+ const nucleotides = ['A', 'U', 'T', 'C', 'G'];
60
67
 
61
68
  possibleSynthesizers.forEach((synthesizer, synthesizerIndex) => {
62
- let codes = getAllCodesOfSynthesizer(synthesizer);
69
+ const codes = getAllCodesOfSynthesizer(synthesizer);
63
70
  while (outputIndices[synthesizerIndex] < sequence.length) {
64
-
65
- let matchedCode = codes
71
+ const matchedCode = codes
66
72
  .find((c) => c == sequence.slice(outputIndices[synthesizerIndex], outputIndices[synthesizerIndex] + c.length));
67
73
 
68
74
  if (matchedCode == null)
69
75
  break;
70
76
 
71
- if ( // for mistake pattern 'rAA'
77
+ if ( // for mistake pattern 'rAA'
72
78
  outputIndices[synthesizerIndex] > 1 &&
73
79
  nucleotides.includes(sequence[outputIndices[synthesizerIndex]]) &&
74
80
  firstUniqueCharacters.includes(sequence[outputIndices[synthesizerIndex] - 2])
75
81
  ) break;
76
82
 
77
- if ( // for mistake pattern 'ArA'
83
+ if ( // for mistake pattern 'ArA'
78
84
  firstUniqueCharacters.includes(sequence[outputIndices[synthesizerIndex] + 1]) &&
79
85
  nucleotides.includes(sequence[outputIndices[synthesizerIndex]])
80
86
  ) {
@@ -92,16 +98,17 @@ function isValidSequence(sequence: string) {
92
98
  if (indexOfFirstNotValidCharacter != -1)
93
99
  return {
94
100
  indexOfFirstNotValidCharacter: indexOfFirstNotValidCharacter,
95
- expectedType: expectedSynthesizer
101
+ expectedSynthesizer: expectedSynthesizer,
102
+ expectedTechnology: null
96
103
  };
97
104
 
98
105
  let possibleTechnologies = getListOfPossibleTechnologiesByFirstMatchedCode(sequence, expectedSynthesizer);
99
106
  if (possibleTechnologies.length == 0)
100
- return { indexOfFirstNotValidCharacter: 0, expectedRepresentation: null };
107
+ return { indexOfFirstNotValidCharacter: 0, expectedSynthesizer: null, expectedTechnology: null };
101
108
 
102
109
  outputIndices = Array(possibleTechnologies.length).fill(0);
103
110
 
104
- possibleTechnologies.forEach((technology, technologyIndex) => {
111
+ possibleTechnologies.forEach((technology: string, technologyIndex: number) => {
105
112
  let codes = Object.keys(map[expectedSynthesizer][technology]);
106
113
  while (outputIndices[technologyIndex] < sequence.length) {
107
114
 
@@ -111,7 +118,7 @@ function isValidSequence(sequence: string) {
111
118
  if (matchedCode == null)
112
119
  break;
113
120
 
114
- if ( // for mistake pattern 'rAA'
121
+ if ( // for mistake pattern 'rAA'
115
122
  outputIndices[technologyIndex] > 1 &&
116
123
  nucleotides.includes(sequence[outputIndices[technologyIndex]]) &&
117
124
  firstUniqueCharacters.includes(sequence[outputIndices[technologyIndex] - 2])
@@ -134,7 +141,8 @@ function isValidSequence(sequence: string) {
134
141
 
135
142
  return {
136
143
  indexOfFirstNotValidCharacter: indexOfFirstNotValidCharacter,
137
- expectedType: expectedSynthesizer + ' ' + expectedTechnology
144
+ expectedSynthesizer: expectedSynthesizer,
145
+ expectedTechnology: expectedTechnology
138
146
  };
139
147
  }
140
148
 
@@ -142,39 +150,51 @@ function sortByStringLengthInDescendingOrder(array: string[]): string[] {
142
150
  return array.sort(function(a: string, b: string) { return b.length - a.length; });
143
151
  }
144
152
 
145
- function getObjectWithCodesAndSmiles() {
146
- let obj: {[code: string]: string} = {};
147
- for (let synthesizer of Object.keys(map))
148
- for (let technology of Object.keys(map[synthesizer]))
153
+ function getObjectWithCodesAndSmiles(sequence: string) {
154
+ const obj: { [code: string]: string } = {};
155
+ for (const synthesizer of Object.keys(map))
156
+ for (const technology of Object.keys(map[synthesizer]))
149
157
  for (let code of Object.keys(map[synthesizer][technology]))
150
158
  obj[code] = map[synthesizer][technology][code].SMILES;
159
+ // TODO: create object based from synthesizer type to avoid key(codes) duplicates
160
+ const output = isValidSequence(sequence);
161
+ if (output.expectedSynthesizer == SYNTHESIZERS.MERMADE_12)
162
+ obj['g'] = map[SYNTHESIZERS.MERMADE_12][TECHNOLOGIES.SI_RNA]['g'].SMILES;
163
+ else if (output.expectedSynthesizer == SYNTHESIZERS.AXOLABS)
164
+ obj['g'] = map[SYNTHESIZERS.AXOLABS][TECHNOLOGIES.SI_RNA]['g'].SMILES;
151
165
  return obj;
152
166
  }
153
167
 
154
- export function sequenceToSmiles(sequence: string) {
155
- const obj = getObjectWithCodesAndSmiles();
168
+ export function sequenceToSmiles(sequence: string, inverted: boolean = false): string {
169
+ const obj = getObjectWithCodesAndSmiles(sequence);
156
170
  let codes = sortByStringLengthInDescendingOrder(Object.keys(obj));
157
- let i = 0, smiles = '', codesList = [];
171
+ let i = 0;
172
+ let smiles = '';
173
+ const codesList = [];
158
174
  const links = ['s', 'ps', '*'];
159
- const includesStandardLinkAlready = ["e", "h", "g", "f", "i", "l", "k", "j"];
175
+ const includesStandardLinkAlready = ['e', 'h', /*'g',*/ 'f', 'i', 'l', 'k', 'j'];
160
176
  const dropdowns = Object.keys(MODIFICATIONS);
161
177
  codes = codes.concat(dropdowns);
162
178
  while (i < sequence.length) {
163
- let code = codes.find((s: string) => s == sequence.slice(i, i + s.length))!;
179
+ const code = codes.find((s: string) => s == sequence.slice(i, i + s.length))!;
164
180
  i += code.length;
165
- codesList.push(code);
181
+ inverted ? codesList.unshift(code) : codesList.push(code);
166
182
  }
167
183
  for (let i = 0; i < codesList.length; i++) {
168
184
  if (dropdowns.includes(codesList[i])) {
169
- smiles += (i >= codesList.length / 2) ?
170
- MODIFICATIONS[codesList[i]].right :
171
- MODIFICATIONS[codesList[i]].left;
185
+ if (i == codesList.length -1 || (i < codesList.length - 1 && links.includes(codesList[i + 1]))) {
186
+ smiles += (i >= codesList.length / 2) ?
187
+ MODIFICATIONS[codesList[i]].right:
188
+ MODIFICATIONS[codesList[i]].left;
189
+ } else if (i < codesList.length - 1) {
190
+ smiles += (i >= codesList.length / 2) ?
191
+ MODIFICATIONS[codesList[i]].right + stadardPhosphateLinkSmiles:
192
+ MODIFICATIONS[codesList[i]].left + stadardPhosphateLinkSmiles;
193
+ }
172
194
  } else {
173
- if (links.includes(codesList[i]) && i > 1 && !includesStandardLinkAlready.includes(codesList[i - 1]))
174
- smiles = smiles.slice(0, smiles.length - stadardPhosphateLinkSmiles.length + 1);
175
- else if (links.includes(codesList[i]) ||
195
+ if (links.includes(codesList[i]) ||
176
196
  includesStandardLinkAlready.includes(codesList[i]) ||
177
- (i < codesList.length - 1 && (links.includes(codesList[i + 1]) || dropdowns.includes(codesList[i + 1])))
197
+ (i < codesList.length - 1 && links.includes(codesList[i + 1]))
178
198
  )
179
199
  smiles += obj[codesList[i]];
180
200
  else
@@ -197,126 +217,221 @@ export function sequenceToSmiles(sequence: string) {
197
217
 
198
218
  //name: Sequence Translator
199
219
  //tags: app
200
- export function sequenceTranslator() {
201
-
202
- let windows = grok.shell.windows;
220
+ export function sequenceTranslator(): void {
221
+ const windows = grok.shell.windows;
203
222
  windows.showProperties = false;
204
223
  windows.showToolbox = false;
205
224
  windows.showHelp = false;
206
225
 
207
- function updateTableAndMolecule(sequence: string) {
208
- moleculeSvgDiv.innerHTML = "";
209
- outputTableDiv.innerHTML = "";
210
- let pi = DG.TaskBarProgressIndicator.create('Rendering table and molecule...');
226
+ function updateTableAndMolecule(sequence: string): void {
227
+ moleculeSvgDiv.innerHTML = '';
228
+ outputTableDiv.innerHTML = '';
229
+ const pi = DG.TaskBarProgressIndicator.create('Rendering table and molecule...');
230
+ let errorsExist = false;
211
231
  try {
212
- let outputSequenceObj = convertSequence(sequence);
213
- let tableRows = [];
214
- for (let key of Object.keys(outputSequenceObj).slice(1)) {
232
+ const outputSequenceObj = convertSequence(sequence);
233
+ const tableRows = [];
234
+
235
+ for (const key of Object.keys(outputSequenceObj).slice(1)) {
236
+ let indexOfFirstNotValidCharacter = ('indexOfFirstNotValidCharacter' in outputSequenceObj) ?
237
+ JSON.parse(outputSequenceObj.indexOfFirstNotValidCharacter!).indexOfFirstNotValidCharacter :
238
+ -1;
239
+ if ('indexOfFirstNotValidCharacter' in outputSequenceObj) {
240
+ let indexOfFirstNotValidCharacter = ('indexOfFirstNotValidCharacter' in outputSequenceObj) ?
241
+ JSON.parse(outputSequenceObj.indexOfFirstNotValidCharacter!).indexOfFirstNotValidCharacter :
242
+ -1;
243
+ if (indexOfFirstNotValidCharacter != -1)
244
+ errorsExist = true;
245
+ }
246
+
215
247
  tableRows.push({
216
248
  'key': key,
217
- 'value': ("indexOfFirstNotValidCharacter" in outputSequenceObj) ?
249
+ 'value': ('indexOfFirstNotValidCharacter' in outputSequenceObj) ?
218
250
  ui.divH([
219
- ui.divText(sequence.slice(0, JSON.parse(outputSequenceObj.indexOfFirstNotValidCharacter!).indexOfFirstNotValidCharacter), {style: {color: "grey"}}),
251
+ ui.divText(sequence.slice(0, indexOfFirstNotValidCharacter), {style: {color: 'grey'}}),
220
252
  ui.tooltip.bind(
221
- ui.divText(sequence.slice(JSON.parse(outputSequenceObj.indexOfFirstNotValidCharacter!).indexOfFirstNotValidCharacter), {style: {color: "red"}}),
222
- "Expected format: " + JSON.parse(outputSequenceObj.indexOfFirstNotValidCharacter!).expectedType + ". Press 'SHOW CODES' button to see tables with valid codes"
223
- )
253
+ ui.divText(sequence.slice(indexOfFirstNotValidCharacter), {style: {color: 'red'}}),
254
+ 'Expected format: ' + JSON.parse(outputSequenceObj.indexOfFirstNotValidCharacter!).expectedSynthesizer + '. See tables with valid codes on the right'
255
+ ),
224
256
  ]) : //@ts-ignore
225
257
  ui.link(outputSequenceObj[key], () => navigator.clipboard.writeText(outputSequenceObj[key]).then(() => grok.shell.info(sequenceWasCopied)), tooltipSequence, '')
226
- })
258
+ });
259
+ }
260
+
261
+ if (errorsExist) {
262
+ const expectedSynthesizer = JSON.parse(outputSequenceObj.indexOfFirstNotValidCharacter!).expectedSynthesizer.slice(0, -6);
263
+ asoGapmersGrid.onCellPrepare(function (gc) {
264
+ gc.style.backColor = (gc.gridColumn.name == expectedSynthesizer) ? 0xFFF00000 : 0xFFFFFFFF;
265
+ });
266
+ omeAndFluoroGrid.onCellPrepare(function (gc) {
267
+ gc.style.backColor = (gc.gridColumn.name == expectedSynthesizer) ? 0xFFF00000 : 0xFFFFFFFF;
268
+ });
269
+ switchInput.enabled = true;
270
+ } else {
271
+ asoGapmersGrid.onCellPrepare(function (gc) {
272
+ gc.style.backColor = 0xFFFFFFFF;
273
+ });
274
+ omeAndFluoroGrid.onCellPrepare(function (gc) {
275
+ gc.style.backColor = 0xFFFFFFFF;
276
+ });
227
277
  }
278
+
228
279
  outputTableDiv.append(
229
- ui.div([DG.HtmlTable.create(tableRows, (item: { key: string; value: string; }) => [item.key, item.value], ['Code', 'Sequence']).root], 'table')
280
+ ui.div([
281
+ DG.HtmlTable.create(tableRows, (item: { key: string; value: string; }) => [item.key, item.value], ['Code', 'Sequence']).root
282
+ ], 'table')
230
283
  );
231
284
  semTypeOfInputSequence.textContent = 'Detected input type: ' + outputSequenceObj.type;
232
285
 
233
- let width = $(window).width();
234
- const canvas = ui.canvas(width, Math.round(width / 2));
235
- let smiles = sequenceToSmiles(inputSequenceField.value.replace(/\s/g, ''));
236
- // @ts-ignore
237
- OCL.StructureView.drawMolecule(canvas, OCL.Molecule.fromSmiles(smiles), { suppressChiralText: true });
238
- if (outputSequenceObj.type != undefinedInputSequence)
286
+ if (outputSequenceObj.type != undefinedInputSequence && outputSequenceObj.Error != undefinedInputSequence) {
287
+ let canvas = ui.canvas(300, 170);
288
+ canvas.addEventListener("click", () => {
289
+ let canv = ui.canvas($(window).width(), $(window).height());
290
+ const smiles = sequenceToSmiles(inputSequenceField.value.replace(/\s/g, ''));
291
+ // @ts-ignore
292
+ OCL.StructureView.drawMolecule(canv, OCL.Molecule.fromSmiles(smiles), { suppressChiralText: true });
293
+ ui.dialog('Molecule')
294
+ .add(canv)
295
+ .showModal(true);
296
+ });
297
+ $(canvas).on('mouseover', () => $(canvas).css('cursor', 'zoom-in'));
298
+ $(canvas).on('mouseout', () => $(canvas).css('cursor', 'default'));
299
+ const smiles = sequenceToSmiles(inputSequenceField.value.replace(/\s/g, ''));
300
+ // @ts-ignore
301
+ OCL.StructureView.drawMolecule(canvas, OCL.Molecule.fromSmiles(smiles), { suppressChiralText: true });
239
302
  moleculeSvgDiv.append(canvas);
303
+ } else
304
+ moleculeSvgDiv.innerHTML = '';
240
305
  } finally {
241
306
  pi.close();
242
307
  }
243
308
  }
244
309
 
245
- let semTypeOfInputSequence = ui.divText('');
246
- let moleculeSvgDiv = ui.block([]);
247
- let outputTableDiv = ui.div([], 'table');
248
- let inputSequenceField = ui.textInput("", defaultInput, (sequence: string) => updateTableAndMolecule(sequence));
249
- updateTableAndMolecule(defaultInput);
250
-
251
- let tablesWithCodes = ui.divV([
252
- DG.HtmlTable.create(Object.keys(MODIFICATIONS), (item: string) => [item], ['Overhang modification']).root,
253
- ui.div([], {style: {height: '30px'}})
254
- ]);
255
- for (let synthesizer of Object.keys(map)) {
256
- for (let technology of Object.keys(map[synthesizer])) {
257
- let tableRows = [];
258
- for (let [key, value] of Object.entries(map[synthesizer][technology]))
259
- tableRows.push({'name': value.name, 'code': key});
260
- tablesWithCodes.append(
261
- DG.HtmlTable.create(
262
- tableRows,
263
- (item: {name: string; code: string;}) => [item['name'], item['code']],
264
- [synthesizer + ' ' + technology, 'Code']
265
- ).root,
266
- ui.div([], {style: {height: '30px'}})
267
- );
268
- }
269
- }
270
- let showCodesButton = ui.button('SHOW CODES', () => ui.dialog('Codes').add(tablesWithCodes).show());
271
- let copySmiles = ui.button(
272
- 'COPY SMILES',
273
- () => navigator.clipboard.writeText(sequenceToSmiles(inputSequenceField.value.replace(/\s/g, '')))
274
- .then(() => grok.shell.info(sequenceWasCopied))
310
+ const semTypeOfInputSequence = ui.divText('');
311
+ const moleculeSvgDiv = ui.block([]);
312
+ const outputTableDiv = ui.div([], 'table');
313
+ const inputSequenceField = ui.textInput('', defaultInput, (sequence: string) => updateTableAndMolecule(sequence));
314
+
315
+ const asoDf = DG.DataFrame.fromObjects([
316
+ { Name: "2'MOE-5Me-rU", BioSpring: "5", "Janssen GCRS": "moeT" },
317
+ { Name: "2'MOE-rA", BioSpring: "6", "Janssen GCRS": "moeA" },
318
+ { Name: "2'MOE-5Me-rC", BioSpring: "7", "Janssen GCRS": "moe5mC" },
319
+ { Name: "2'MOE-rG", BioSpring: "8", "Janssen GCRS": "moeG" },
320
+ { Name: "5-Methyl-dC", BioSpring: "9", "Janssen GCRS": "5mC" },
321
+ { Name: "ps linkage", BioSpring: "*", "Janssen GCRS": "ps" },
322
+ { Name: "dA", BioSpring: "A", "Janssen GCRS": "A, dA" },
323
+ { Name: "dC", BioSpring: "C", "Janssen GCRS": "C, dC" },
324
+ { Name: "dG", BioSpring: "G", "Janssen GCRS": "G, dG" },
325
+ { Name: "dT", BioSpring: "T", "Janssen GCRS": "T, dT" },
326
+ { Name: "rA", BioSpring: "", "Janssen GCRS": "rA" },
327
+ { Name: "rC", BioSpring: "", "Janssen GCRS": "rC" },
328
+ { Name: "rG", BioSpring: "", "Janssen GCRS": "rG" },
329
+ { Name: "rU", BioSpring: "", "Janssen GCRS": "rU" }
330
+ ])!;
331
+ const asoGapmersGrid = DG.Viewer.grid(
332
+ asoDf, { showRowHeader: false, showCellTooltip: false }
275
333
  );
276
- let saveMolFileButton = ui.bigButton('SAVE MOL FILE', () => {
277
- let smiles = sequenceToSmiles(inputSequenceField.value.replace(/\s/g, ''));
278
- let result = `${OCL.Molecule.fromSmiles(smiles).toMolfile()}\n`;
279
- let element = document.createElement('a');
280
- element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result));
281
- element.setAttribute('download', inputSequenceField.value.replace(/\s/g, '') + '.mol');
282
- element.click();
334
+
335
+ asoDf.onCurrentCellChanged.subscribe((_) => {
336
+ navigator.clipboard.writeText(asoDf.currentCell.value).then(() => grok.shell.info('Copied'))
283
337
  });
284
338
 
339
+ let omeAndFluoroGrid = DG.Viewer.grid(
340
+ DG.DataFrame.fromObjects([
341
+ { Name: "2'-fluoro-U", BioSpring: "1", Axolabs: "Uf", "Janssen GCRS": "fU" },
342
+ { Name: "2'-fluoro-A", BioSpring: "2", Axolabs: "Af", "Janssen GCRS": "fA" },
343
+ { Name: "2'-fluoro-C", BioSpring: "3", Axolabs: "Cf", "Janssen GCRS": "fC" },
344
+ { Name: "2'-fluoro-G", BioSpring: "4", Axolabs: "Gf", "Janssen GCRS": "fG" },
345
+ { Name: "2'OMe-rU", BioSpring: "5", Axolabs: "u", "Janssen GCRS": "mU" },
346
+ { Name: "2'OMe-rA", BioSpring: "6", Axolabs: "a", "Janssen GCRS": "mA" },
347
+ { Name: "2'OMe-rC", BioSpring: "7", Axolabs: "c", "Janssen GCRS": "mC" },
348
+ { Name: "2'OMe-rG", BioSpring: "8", Axolabs: "g", "Janssen GCRS": "mG" },
349
+ { Name: "ps linkage", BioSpring: "*", Axolabs: "s", "Janssen GCRS": "ps" }
350
+ ])!, { showRowHeader: false, showCellTooltip: false }
351
+ );
352
+
353
+ const overhangModificationsGrid = DG.Viewer.grid(
354
+ DG.DataFrame.fromObjects([
355
+ { Name: "(invabasic)" },
356
+ { Name: "(GalNAc-2-JNJ)" }
357
+ ])!, { showRowHeader: false, showCellTooltip: false }
358
+ );
359
+ updateTableAndMolecule(defaultInput);
360
+
285
361
  const appMainDescription = ui.info([
286
- ui.divText('\n How to convert one sequence:',{style:{'font-weight':'bolder'}}),
287
- ui.divText("Paste sequence into the text field below"),
362
+ ui.divText('How to convert one sequence:',{style:{'font-weight':'bolder'}}),
363
+ ui.divText('Paste sequence into the text field below'),
288
364
  ui.divText('\n How to convert many sequences:',{style:{'font-weight':'bolder'}}),
289
- ui.divText("1. Drag & drop an Excel or CSV file with sequences into Datagrok. The platform will automatically detect columns with sequences"),
365
+ ui.divText('1. Drag & drop an Excel or CSV file with sequences into Datagrok. The platform will automatically detect columns with sequences'),
290
366
  ui.divText('2. Right-click on the column header, then see the \'Convert\' menu'),
291
- ui.divText("This will add the result column to the right of the table"),
367
+ ui.divText('This will add the result column to the right of the table'),
292
368
  ], 'Convert oligonucleotide sequences between Nucleotides, BioSpring, Axolabs, Mermade 12 and GCRS representations.'
293
369
  );
294
370
 
295
- let v = grok.shell.newView('Sequence Translator', [
296
- ui.tabControl({
297
- 'MAIN': ui.div([
298
- appMainDescription,
299
- ui.panel([
300
- ui.div([
301
- ui.h1('Input sequence'),
371
+ const codesTablesDiv = ui.splitV([
372
+ ui.box(ui.h2('ASO Gapmers'), { style: {maxHeight: '40px'} }),
373
+ asoGapmersGrid.root,
374
+ ui.box(ui.h2("2'-OMe and 2'-F siRNA"), { style: {maxHeight: '40px'} }),
375
+ omeAndFluoroGrid.root,
376
+ ui.box(ui.h2('Overhang modifications'), { style: {maxHeight: '40px'} }),
377
+ overhangModificationsGrid.root
378
+ ], { style: { maxWidth: '350px' } });
379
+
380
+ const tabControl = ui.tabControl({
381
+ 'MAIN': ui.box(
382
+ ui.splitH([
383
+ ui.splitV([
384
+ ui.panel([
385
+ appMainDescription,
302
386
  ui.div([
303
- inputSequenceField.root
304
- ],'input-base')
305
- ], 'sequenceInput'),
306
- semTypeOfInputSequence,
307
- ui.block([
308
- ui.h1('Output'),
309
- outputTableDiv
310
- ]),
311
- moleculeSvgDiv,
312
- ui.divH([saveMolFileButton, showCodesButton, copySmiles])
313
- ], 'sequence')
314
- ]),
315
- 'AXOLABS': defineAxolabsPattern()
316
- })
317
- ]);
387
+ ui.h1('Input sequence'),
388
+ ui.div([
389
+ inputSequenceField.root,
390
+ ], 'input-base'),
391
+ ], 'sequenceInput'),
392
+ semTypeOfInputSequence,
393
+ ui.block([
394
+ ui.h1('Output'),
395
+ outputTableDiv,
396
+ ]),
397
+ moleculeSvgDiv,
398
+ ], 'sequence'),
399
+ ]),
400
+ codesTablesDiv,
401
+ ], {style: {height: '100%', width: '100%'}}),
402
+ ),
403
+ 'AXOLABS': defineAxolabsPattern(),
404
+ 'SDF': saveSenseAntiSense(),
405
+ });
406
+
407
+ let v = grok.shell.newView('Sequence Translator', [tabControl]);
318
408
  v.box = true;
319
409
 
410
+ const switchInput = ui.switchInput('Codes', true, (v: boolean) => (v) ?
411
+ $(codesTablesDiv).show() :
412
+ $(codesTablesDiv).hide()
413
+ );
414
+
415
+ const topPanel = [
416
+ ui.iconFA('download', () => {
417
+ const smiles = sequenceToSmiles(inputSequenceField.value.replace(/\s/g, ''));
418
+ const result = `${OCL.Molecule.fromSmiles(smiles).toMolfile()}\n`;
419
+ const element = document.createElement('a');
420
+ element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result));
421
+ element.setAttribute('download', inputSequenceField.value.replace(/\s/g, '') + '.mol');
422
+ element.click();
423
+ }, 'Save .mol file'),
424
+ ui.iconFA('copy', () => {
425
+ navigator.clipboard.writeText(sequenceToSmiles(inputSequenceField.value.replace(/\s/g, '')))
426
+ .then(() => grok.shell.info(sequenceWasCopied));
427
+ }, 'Copy SMILES'),
428
+ switchInput.root,
429
+ ];
430
+
431
+ tabControl.onTabChanged.subscribe((_) =>
432
+ v.setRibbonPanels([(tabControl.currentPane.name == 'MAIN') ? topPanel : []]));
433
+ v.setRibbonPanels([topPanel]);
434
+
320
435
  $('.sequence')
321
436
  .children().css('padding','5px 0');
322
437
  $('.sequenceInput .input-base')
@@ -325,7 +440,7 @@ export function sequenceTranslator() {
325
440
  .css('resize','none')
326
441
  .css('min-height','50px')
327
442
  .css('width','100%')
328
- .attr("spellcheck", "false");
443
+ .attr('spellcheck', 'false');
329
444
  $('.sequenceInput select')
330
445
  .css('width','100%');
331
446
  }
@@ -336,24 +451,25 @@ function convertSequence(text: string) {
336
451
  let output = isValidSequence(seq);
337
452
  if (output.indexOfFirstNotValidCharacter != -1)
338
453
  return {
454
+ // type: '',
339
455
  indexOfFirstNotValidCharacter: JSON.stringify(output),
340
456
  Error: undefinedInputSequence
341
457
  };
342
- if (output.expectedType == SYNTHESIZERS.RAW_NUCLEOTIDES + ' ' + TECHNOLOGIES.DNA)
458
+ if (output.expectedSynthesizer == SYNTHESIZERS.RAW_NUCLEOTIDES && output.expectedTechnology == TECHNOLOGIES.DNA)
343
459
  return {
344
460
  type: SYNTHESIZERS.RAW_NUCLEOTIDES + ' ' + TECHNOLOGIES.DNA,
345
461
  Nucleotides: seq,
346
462
  BioSpring: asoGapmersNucleotidesToBioSpring(seq),
347
463
  GCRS: asoGapmersNucleotidesToGcrs(seq)
348
464
  };
349
- if (output.expectedType == SYNTHESIZERS.BIOSPRING + ' ' + TECHNOLOGIES.ASO_GAPMERS)
465
+ if (output.expectedSynthesizer == SYNTHESIZERS.BIOSPRING && output.expectedTechnology == TECHNOLOGIES.ASO_GAPMERS)
350
466
  return {
351
467
  type: SYNTHESIZERS.BIOSPRING + ' ' + TECHNOLOGIES.ASO_GAPMERS,
352
468
  Nucleotides: asoGapmersBioSpringToNucleotides(seq),
353
469
  BioSpring: seq,
354
470
  GCRS: asoGapmersBioSpringToGcrs(seq)
355
471
  };
356
- if (output.expectedType == SYNTHESIZERS.GCRS + ' ' + TECHNOLOGIES.ASO_GAPMERS)
472
+ if (output.expectedSynthesizer == SYNTHESIZERS.GCRS && output.expectedTechnology == TECHNOLOGIES.ASO_GAPMERS)
357
473
  return {
358
474
  type: SYNTHESIZERS.GCRS + ' ' + TECHNOLOGIES.ASO_GAPMERS,
359
475
  Nucleotides: asoGapmersGcrsToNucleotides(seq),
@@ -361,7 +477,7 @@ function convertSequence(text: string) {
361
477
  Mermade12: gcrsToMermade12(seq),
362
478
  GCRS: seq
363
479
  };
364
- if (output.expectedType == SYNTHESIZERS.RAW_NUCLEOTIDES + ' ' + TECHNOLOGIES.RNA)
480
+ if (output.expectedSynthesizer == SYNTHESIZERS.RAW_NUCLEOTIDES && output.expectedTechnology == TECHNOLOGIES.RNA)
365
481
  return {
366
482
  type: SYNTHESIZERS.RAW_NUCLEOTIDES + ' ' + TECHNOLOGIES.RNA,
367
483
  Nucleotides: seq,
@@ -369,7 +485,7 @@ function convertSequence(text: string) {
369
485
  Axolabs: siRnaNucleotideToAxolabsSenseStrand(seq),
370
486
  GCRS: siRnaNucleotidesToGcrs(seq)
371
487
  };
372
- if (output.expectedType == SYNTHESIZERS.BIOSPRING + ' ' + TECHNOLOGIES.SI_RNA)
488
+ if (output.expectedSynthesizer == SYNTHESIZERS.BIOSPRING && output.expectedTechnology == TECHNOLOGIES.SI_RNA)
373
489
  return {
374
490
  type: SYNTHESIZERS.BIOSPRING + ' ' + TECHNOLOGIES.SI_RNA,
375
491
  Nucleotides: siRnaBioSpringToNucleotides(seq),
@@ -377,7 +493,7 @@ function convertSequence(text: string) {
377
493
  Axolabs: siRnaBioSpringToAxolabs(seq),
378
494
  GCRS: siRnaBioSpringToGcrs(seq)
379
495
  };
380
- if (output.expectedType == SYNTHESIZERS.AXOLABS + ' ' + TECHNOLOGIES.SI_RNA)
496
+ if (output.expectedSynthesizer == SYNTHESIZERS.AXOLABS && output.expectedTechnology == TECHNOLOGIES.SI_RNA)
381
497
  return {
382
498
  type: SYNTHESIZERS.AXOLABS + ' ' + TECHNOLOGIES.SI_RNA,
383
499
  Nucleotides: siRnaAxolabsToNucleotides(seq),
@@ -385,7 +501,7 @@ function convertSequence(text: string) {
385
501
  Axolabs: seq,
386
502
  GCRS: siRnaAxolabsToGcrs(seq)
387
503
  };
388
- if (output.expectedType == SYNTHESIZERS.GCRS + ' ' + TECHNOLOGIES.SI_RNA)
504
+ if (output.expectedSynthesizer == SYNTHESIZERS.GCRS && output.expectedTechnology == TECHNOLOGIES.SI_RNA)
389
505
  return {
390
506
  type: SYNTHESIZERS.GCRS + ' ' + TECHNOLOGIES.SI_RNA,
391
507
  Nucleotides: siRnaGcrsToNucleotides(seq),
@@ -394,14 +510,14 @@ function convertSequence(text: string) {
394
510
  MM12: gcrsToMermade12(seq),
395
511
  GCRS: seq
396
512
  };
397
- if (output.expectedType == SYNTHESIZERS.GCRS)
513
+ if (output.expectedSynthesizer == SYNTHESIZERS.GCRS)
398
514
  return {
399
515
  type: SYNTHESIZERS.GCRS,
400
516
  Nucleotides: gcrsToNucleotides(seq),
401
517
  GCRS: seq,
402
518
  Mermade12: gcrsToMermade12(seq)
403
519
  }
404
- if (output.expectedType == SYNTHESIZERS.MERMADE_12)
520
+ if (output.expectedSynthesizer == SYNTHESIZERS.MERMADE_12)
405
521
  return {
406
522
  type: SYNTHESIZERS.MERMADE_12,
407
523
  Nucleotides: noTranslationTableAvailable,
@@ -417,47 +533,47 @@ function convertSequence(text: string) {
417
533
  //name: asoGapmersNucleotidesToBioSpring
418
534
  //input: string nucleotides {semType: DNA nucleotides}
419
535
  //output: string result {semType: BioSpring / Gapmers}
420
- export function asoGapmersNucleotidesToBioSpring(nucleotides: string) {
536
+ export function asoGapmersNucleotidesToBioSpring(nucleotides: string): string {
421
537
  let count: number = -1;
422
- const objForEdges: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "T": "5*", "A": "6*", "C": "7*", "G": "8*"};
423
- const objForCenter: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "T": "T*", "A": "A*", "C": "9*", "G": "G*"};
538
+ const objForEdges: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'T': '5*', 'A': '6*', 'C': '7*', 'G': '8*'};
539
+ const objForCenter: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'T': 'T*', 'A': 'A*', 'C': '9*', 'G': 'G*'};
424
540
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|A|T|C|G)/g, function (x: string) {
425
541
  count++;
426
542
  return (count > 4 && count < 15) ? objForCenter[x] : objForEdges[x];
427
- }).slice(0, (nucleotides.endsWith("(invabasic)") || nucleotides.endsWith("(GalNAc-2-JNJ)")) ? nucleotides.length : 2 * count + 1);
543
+ }).slice(0, (nucleotides.endsWith('(invabasic)') || nucleotides.endsWith('(GalNAc-2-JNJ)')) ? nucleotides.length : 2 * count + 1);
428
544
  }
429
545
 
430
546
  //name: asoGapmersNucleotidesToGcrs
431
547
  //input: string nucleotides {semType: DNA nucleotides}
432
548
  //output: string result {semType: GCRS / Gapmers}
433
- export function asoGapmersNucleotidesToGcrs(nucleotides: string) {
549
+ export function asoGapmersNucleotidesToGcrs(nucleotides: string): string {
434
550
  let count: number = -1;
435
- const objForEdges: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "T": "moeUnps", "A": "moeAnps", "C": "moe5mCnps", "G": "moeGnps"};
436
- const objForCenter: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "C": "5mCps", "A": "Aps", "T": "Tps", "G": "Gps"};
551
+ const objForEdges: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'T': 'moeUnps', 'A': 'moeAnps', 'C': 'moe5mCnps', 'G': 'moeGnps'};
552
+ const objForCenter: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'C': '5mCps', 'A': 'Aps', 'T': 'Tps', 'G': 'Gps'};
437
553
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|A|T|C|G)/g, function (x: string) {
438
554
  count++;
439
555
  if (count < 5) return (count == 4) ? objForEdges[x].slice(0, -3) + 'ps' : objForEdges[x];
440
556
  if (count < 15) return (count == 14) ? objForCenter[x].slice(0, -2) + 'nps' : objForCenter[x];
441
557
  return objForEdges[x];
442
- }).slice(0, (nucleotides.endsWith("(invabasic)") || nucleotides.endsWith("(GalNAc-2-JNJ)")) ? nucleotides.length : -3);
558
+ }).slice(0, (nucleotides.endsWith('(invabasic)') || nucleotides.endsWith('(GalNAc-2-JNJ)')) ? nucleotides.length : -3);
443
559
  }
444
560
 
445
561
  //name: asoGapmersBioSpringToNucleotides
446
562
  //input: string nucleotides {semType: BioSpring / Gapmers}
447
563
  //output: string result {semType: DNA nucleotides}
448
- export function asoGapmersBioSpringToNucleotides(nucleotides: string) {
449
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "*": "", "5": "T", "6": "A", "7": "C", "8": "G", "9": "C"};
564
+ export function asoGapmersBioSpringToNucleotides(nucleotides: string): string {
565
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', '*': '', '5': 'T', '6': 'A', '7': 'C', '8': 'G', '9': 'C'};
450
566
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|\*|5|6|7|8|9)/g, function (x: string) {return obj[x];});
451
567
  }
452
568
 
453
569
  //name: asoGapmersBioSpringToGcrs
454
570
  //input: string nucleotides {semType: BioSpring / Gapmers}
455
571
  //output: string result {semType: GCRS / Gapmers}
456
- export function asoGapmersBioSpringToGcrs(nucleotides: string) {
572
+ export function asoGapmersBioSpringToGcrs(nucleotides: string): string {
457
573
  let count: number = -1;
458
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)",
459
- "5*": "moeUnps", "6*": "moeAnps", "7*": "moe5mCnps", "8*": "moeGnps", "9*": "5mCps", "A*": "Aps", "T*": "Tps",
460
- "G*": "Gps", "C*": "Cps", "5": "moeU", "6": "moeA", "7": "moe5mC", "8": "moeG"
574
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)',
575
+ '5*': 'moeUnps', '6*': 'moeAnps', '7*': 'moe5mCnps', '8*': 'moeGnps', '9*': '5mCps', 'A*': 'Aps', 'T*': 'Tps',
576
+ 'G*': 'Gps', 'C*': 'Cps', '5': 'moeU', '6': 'moeA', '7': 'moe5mC', '8': 'moeG'
461
577
  };
462
578
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|5\*|6\*|7\*|8\*|9\*|A\*|T\*|G\*|C\*|5|6|7|8)/g, function (x: string) {
463
579
  count++;
@@ -468,9 +584,9 @@ export function asoGapmersBioSpringToGcrs(nucleotides: string) {
468
584
  //name: asoGapmersGcrsToBioSpring
469
585
  //input: string nucleotides {semType: GCRS / Gapmers}
470
586
  //output: string result {semType: BioSpring / Gapmers}
471
- export function asoGapmersGcrsToBioSpring(nucleotides: string) {
472
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)",
473
- "moeT": "5", "moeA": "6", "moe5mC": "7", "moeG": "8", "moeU": "5", "5mC": "9", "nps": "*", "ps": "*", "U": "T"
587
+ export function asoGapmersGcrsToBioSpring(nucleotides: string): string {
588
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)',
589
+ 'moeT': '5', 'moeA': '6', 'moe5mC': '7', 'moeG': '8', 'moeU': '5', '5mC': '9', 'nps': '*', 'ps': '*', 'U': 'T'
474
590
  };
475
591
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|moeT|moeA|moe5mC|moeG|moeU|5mC|nps|ps|U)/g, function (x: string) {return obj[x];});
476
592
  }
@@ -479,7 +595,7 @@ export function asoGapmersGcrsToBioSpring(nucleotides: string) {
479
595
  //input: string nucleotides {semType: GCRS / Gapmers}
480
596
  //output: string result {semType: DNA nucleotides}
481
597
  export function asoGapmersGcrsToNucleotides(nucleotides: string) {
482
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "moe": "", "5m": "", "n": "", "ps": "", "U": "T"};
598
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'moe': '', '5m': '', 'n': '', 'ps': '', 'U': 'T'};
483
599
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|moe|5m|n|ps|U)/g, function (x: string) {return obj[x];});
484
600
  }
485
601
 
@@ -487,7 +603,7 @@ export function asoGapmersGcrsToNucleotides(nucleotides: string) {
487
603
  //input: string nucleotides {semType: BioSpring / siRNA}
488
604
  //output: string result {semType: RNA nucleotides}
489
605
  export function siRnaBioSpringToNucleotides(nucleotides: string) {
490
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "1": "U", "2": "A", "3": "C", "4": "G", "5": "U", "6": "A", "7": "C", "8": "G", "*": ""};
606
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', '1': 'U', '2': 'A', '3': 'C', '4': 'G', '5': 'U', '6': 'A', '7': 'C', '8': 'G', '*': ''};
491
607
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|1|2|3|4|5|6|7|8|\*)/g, function (x: string) {return obj[x];});
492
608
  }
493
609
 
@@ -495,7 +611,7 @@ export function siRnaBioSpringToNucleotides(nucleotides: string) {
495
611
  //input: string nucleotides {semType: BioSpring / siRNA}
496
612
  //output: string result {semType: Axolabs / siRNA}
497
613
  export function siRnaBioSpringToAxolabs(nucleotides: string) {
498
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "1": "Uf", "2": "Af", "3": "Cf", "4": "Gf", "5": "u", "6": "a", "7": "c", "8": "g", "*": "s"};
614
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', '1': 'Uf', '2': 'Af', '3': 'Cf', '4': 'Gf', '5': 'u', '6': 'a', '7': 'c', '8': 'g', '*': 's'};
499
615
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|1|2|3|4|5|6|7|8|\*)/g, function (x: string) {return obj[x];});
500
616
  }
501
617
 
@@ -503,7 +619,7 @@ export function siRnaBioSpringToAxolabs(nucleotides: string) {
503
619
  //input: string nucleotides {semType: BioSpring / siRNA}
504
620
  //output: string result {semType: GCRS}
505
621
  export function siRnaBioSpringToGcrs(nucleotides: string) {
506
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "1": "fU", "2": "fA", "3": "fC", "4": "fG", "5": "mU", "6": "mA", "7": "mC", "8": "mG", "*": "ps"};
622
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', '1': 'fU', '2': 'fA', '3': 'fC', '4': 'fG', '5': 'mU', '6': 'mA', '7': 'mC', '8': 'mG', '*': 'ps'};
507
623
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|1|2|3|4|5|6|7|8|\*)/g, function (x: string) {return obj[x];});
508
624
  }
509
625
 
@@ -511,8 +627,8 @@ export function siRnaBioSpringToGcrs(nucleotides: string) {
511
627
  //input: string nucleotides {semType: Axolabs / siRNA}
512
628
  //output: string result {semType: GCRS}
513
629
  export function siRnaAxolabsToGcrs(nucleotides: string) {
514
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)",
515
- "Uf": "fU", "Af": "fA", "Cf": "fC", "Gf": "fG", "u": "mU", "a": "mA", "c": "mC", "g": "mG", "s": "ps"
630
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)',
631
+ 'Uf': 'fU', 'Af': 'fA', 'Cf': 'fC', 'Gf': 'fG', 'u': 'mU', 'a': 'mA', 'c': 'mC', 'g': 'mG', 's': 'ps'
516
632
  };
517
633
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|Uf|Af|Cf|Gf|u|a|c|g|s)/g, function (x: string) {return obj[x];});
518
634
  }
@@ -521,8 +637,8 @@ export function siRnaAxolabsToGcrs(nucleotides: string) {
521
637
  //input: string nucleotides {semType: Axolabs / siRNA}
522
638
  //output: string result {semType: BioSpring / siRNA}
523
639
  export function siRnaAxolabsToBioSpring(nucleotides: string) {
524
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)",
525
- "Uf": "1", "Af": "2", "Cf": "3", "Gf": "4", "u": "5", "a": "6", "c": "7", "g": "8", "s": "*"
640
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)',
641
+ 'Uf': '1', 'Af': '2', 'Cf': '3', 'Gf': '4', 'u': '5', 'a': '6', 'c': '7', 'g': '8', 's': '*'
526
642
  };
527
643
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|Uf|Af|Cf|Gf|u|a|c|g|s)/g, function (x: string) {return obj[x];});
528
644
  }
@@ -531,8 +647,8 @@ export function siRnaAxolabsToBioSpring(nucleotides: string) {
531
647
  //input: string nucleotides {semType: Axolabs / siRNA}
532
648
  //output: string result {semType: RNA nucleotides}
533
649
  export function siRnaAxolabsToNucleotides(nucleotides: string) {
534
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)",
535
- "Uf": "U", "Af": "A", "Cf": "C", "Gf": "G", "u": "U", "a": "A", "c": "C", "g": "G", "s": ""
650
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)',
651
+ 'Uf': 'U', 'Af': 'A', 'Cf': 'C', 'Gf': 'G', 'u': 'U', 'a': 'A', 'c': 'C', 'g': 'G', 's': ''
536
652
  };
537
653
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|Uf|Af|Cf|Gf|u|a|c|g|s)/g, function (x: string) {return obj[x];});
538
654
  }
@@ -541,8 +657,8 @@ export function siRnaAxolabsToNucleotides(nucleotides: string) {
541
657
  //input: string nucleotides {semType: GCRS}
542
658
  //output: string result {semType: RNA nucleotides}
543
659
  export function siRnaGcrsToNucleotides(nucleotides: string) {
544
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)",
545
- "fU": "U", "fA": "A", "fC": "C", "fG": "G", "mU": "U", "mA": "A", "mC": "C", "mG": "G", "ps": ""
660
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)',
661
+ 'fU': 'U', 'fA': 'A', 'fC': 'C', 'fG': 'G', 'mU': 'U', 'mA': 'A', 'mC': 'C', 'mG': 'G', 'ps': ''
546
662
  };
547
663
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|fU|fA|fC|fG|mU|mA|mC|mG|ps)/g, function (x: string) {return obj[x];});
548
664
  }
@@ -551,8 +667,8 @@ export function siRnaGcrsToNucleotides(nucleotides: string) {
551
667
  //input: string nucleotides {semType: GCRS}
552
668
  //output: string result {semType: BioSpring / siRNA}
553
669
  export function siRnaGcrsToBioSpring(nucleotides: string) {
554
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)",
555
- "fU": "1", "fA": "2", "fC": "3", "fG": "4", "mU": "5", "mA": "6", "mC": "7", "mG": "8", "ps": "*"
670
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)',
671
+ 'fU': '1', 'fA': '2', 'fC': '3', 'fG': '4', 'mU': '5', 'mA': '6', 'mC': '7', 'mG': '8', 'ps': '*'
556
672
  };
557
673
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|fU|fA|fC|fG|mU|mA|mC|mG|ps)/g, function (x: string) {return obj[x];});
558
674
  }
@@ -561,8 +677,8 @@ export function siRnaGcrsToBioSpring(nucleotides: string) {
561
677
  //input: string nucleotides {semType: GCRS}
562
678
  //output: string result {semType: Axolabs / siRNA}
563
679
  export function siRnaGcrsToAxolabs(nucleotides: string) {
564
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)",
565
- "fU": "Uf", "fA": "Af", "fC": "Cf", "fG": "Gf", "mU": "u", "mA": "a", "mC": "c", "mG": "g", "ps": "s"
680
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)',
681
+ 'fU': 'Uf', 'fA': 'Af', 'fC': 'Cf', 'fG': 'Gf', 'mU': 'u', 'mA': 'a', 'mC': 'c', 'mG': 'g', 'ps': 's'
566
682
  };
567
683
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|fU|fA|fC|fG|mU|mA|mC|mG|ps)/g, function (x: string) {return obj[x];});
568
684
  }
@@ -572,10 +688,10 @@ export function siRnaGcrsToAxolabs(nucleotides: string) {
572
688
  //output: string result {semType: BioSpring / siRNA}
573
689
  export function siRnaNucleotideToBioSpringSenseStrand(nucleotides: string) {
574
690
  let count: number = -1;
575
- const objForLeftEdge: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "A": "6*", "U": "5*", "G": "8*", "C": "7*"};
576
- const objForRightEdge: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "A": "*6", "U": "*5", "G": "*8", "C": "*7"};
577
- const objForOddIndices: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "A": "6", "U": "5", "G": "8", "C": "7"};
578
- const objForEvenIndices: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "A": "2", "U": "1", "G": "4", "C": "3"};
691
+ const objForLeftEdge: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'A': '6*', 'U': '5*', 'G': '8*', 'C': '7*'};
692
+ const objForRightEdge: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'A': '*6', 'U': '*5', 'G': '*8', 'C': '*7'};
693
+ const objForOddIndices: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'A': '6', 'U': '5', 'G': '8', 'C': '7'};
694
+ const objForEvenIndices: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'A': '2', 'U': '1', 'G': '4', 'C': '3'};
579
695
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|A|U|G|C)/g, function (x: string) {
580
696
  count++;
581
697
  if (count < 2) return objForLeftEdge[x];
@@ -589,10 +705,10 @@ export function siRnaNucleotideToBioSpringSenseStrand(nucleotides: string) {
589
705
  //output: string result {semType: GCRS}
590
706
  export function siRnaNucleotidesToGcrs(nucleotides: string) {
591
707
  let count: number = -1;
592
- const objForLeftEdge: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "A": "mAps", "U": "mUps", "G": "mGps", "C": "mCps"};
593
- const objForRightEdge: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "A": "psmA", "U": "psmU", "G": "psmG", "C": "psmC"};
594
- const objForEvenIndices: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "A": "fA", "U": "fU", "G": "fG", "C": "fC"};
595
- const objForOddIndices: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "A": "mA", "U": "mU", "G": "mG", "C": "mC"};
708
+ const objForLeftEdge: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'A': 'mAps', 'U': 'mUps', 'G': 'mGps', 'C': 'mCps'};
709
+ const objForRightEdge: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'A': 'psmA', 'U': 'psmU', 'G': 'psmG', 'C': 'psmC'};
710
+ const objForEvenIndices: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'A': 'fA', 'U': 'fU', 'G': 'fG', 'C': 'fC'};
711
+ const objForOddIndices: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'A': 'mA', 'U': 'mU', 'G': 'mG', 'C': 'mC'};
596
712
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|A|U|G|C)/g, function (x: string) {
597
713
  count++;
598
714
  if (count < 2) return objForLeftEdge[x];
@@ -606,9 +722,9 @@ export function siRnaNucleotidesToGcrs(nucleotides: string) {
606
722
  //output: string result {semType: Axolabs}
607
723
  export function siRnaNucleotideToAxolabsSenseStrand(nucleotides: string) {
608
724
  let count: number = -1;
609
- const objForLeftEdge: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "A": "as", "U": "us", "G": "gs", "C": "cs"};
610
- const objForSomeIndices: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "A": "Af", "U": "Uf", "G": "Gf", "C": "Cf"};
611
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "A": "a", "U": "u", "G": "g", "C": "c"};
725
+ const objForLeftEdge: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'A': 'as', 'U': 'us', 'G': 'gs', 'C': 'cs'};
726
+ const objForSomeIndices: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'A': 'Af', 'U': 'Uf', 'G': 'Gf', 'C': 'Cf'};
727
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'A': 'a', 'U': 'u', 'G': 'g', 'C': 'c'};
612
728
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|A|U|G|C)/g, function (x: string) {
613
729
  count++;
614
730
  if (count < 2) return objForLeftEdge[x];
@@ -623,10 +739,10 @@ export function siRnaNucleotideToAxolabsSenseStrand(nucleotides: string) {
623
739
  //output: string result {semType: Axolabs}
624
740
  export function siRnaNucleotideToAxolabsAntisenseStrand(nucleotides: string) {
625
741
  let count: number = -1;
626
- const objForSmallLinkages: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "A": "as", "U": "us", "G": "gs", "C": "cs"};
627
- const objForBigLinkages: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "A": "Afs", "U": "Ufs", "G": "Gfs", "C": "Cfs"};
628
- const objForSomeIndices: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "A": "Af", "U": "Uf", "G": "Gf", "C": "Cf"};
629
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)", "A": "a", "U": "u", "G": "g", "C": "c"};
742
+ const objForSmallLinkages: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'A': 'as', 'U': 'us', 'G': 'gs', 'C': 'cs'};
743
+ const objForBigLinkages: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'A': 'Afs', 'U': 'Ufs', 'G': 'Gfs', 'C': 'Cfs'};
744
+ const objForSomeIndices: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'A': 'Af', 'U': 'Uf', 'G': 'Gf', 'C': 'Cf'};
745
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)', 'A': 'a', 'U': 'u', 'G': 'g', 'C': 'c'};
630
746
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|A|U|G|C)/g, function (x: string) {
631
747
  count++;
632
748
  if (count > 19 && count < 22) return objForSmallLinkages[x];
@@ -640,9 +756,9 @@ export function siRnaNucleotideToAxolabsAntisenseStrand(nucleotides: string) {
640
756
  //input: string nucleotides {semType: GCRS}
641
757
  //output: string result {semType: RNA nucleotides}
642
758
  export function gcrsToNucleotides(nucleotides: string) {
643
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)",
644
- "mAps": "A", "mUps": "U", "mGps": "G", "mCps": "C", "fAps": "A", "fUps": "U", "fGps": "G", "fCps": "C",
645
- "fU": "U", "fA": "A", "fC": "C", "fG": "G", "mU": "U", "mA": "A", "mC": "C", "mG": "G"
759
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)',
760
+ 'mAps': 'A', 'mUps': 'U', 'mGps': 'G', 'mCps': 'C', 'fAps': 'A', 'fUps': 'U', 'fGps': 'G', 'fCps': 'C',
761
+ 'fU': 'U', 'fA': 'A', 'fC': 'C', 'fG': 'G', 'mU': 'U', 'mA': 'A', 'mC': 'C', 'mG': 'G'
646
762
  };
647
763
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|mAps|mUps|mGps|mCps|fAps|fUps|fGps|fCps|fU|fA|fC|fG|mU|mA|mC|mG)/g, function (x: string) {return obj[x];});
648
764
  }
@@ -651,9 +767,130 @@ export function gcrsToNucleotides(nucleotides: string) {
651
767
  //input: string nucleotides {semType: GCRS}
652
768
  //output: string result {semType: Mermade 12 / siRNA}
653
769
  export function gcrsToMermade12(nucleotides: string) {
654
- const obj: {[index: string]: string} = {"(invabasic)": "(invabasic)", "(GalNAc-2-JNJ)": "(GalNAc-2-JNJ)",
655
- "mAps": "e", "mUps": "h", "mGps": "g", "mCps": "f", "fAps": "i", "fUps": "l", "fGps": "k", "fCps": "j", "fU": "L",
656
- "fA": "I", "fC": "J", "fG": "K", "mU": "H", "mA": "E", "mC": "F", "mG": "G"
770
+ const obj: {[index: string]: string} = {'(invabasic)': '(invabasic)', '(GalNAc-2-JNJ)': '(GalNAc-2-JNJ)',
771
+ 'mAps': 'e', 'mUps': 'h', 'mGps': 'g', 'mCps': 'f', 'fAps': 'i', 'fUps': 'l', 'fGps': 'k', 'fCps': 'j', 'fU': 'L',
772
+ 'fA': 'I', 'fC': 'J', 'fG': 'K', 'mU': 'H', 'mA': 'E', 'mC': 'F', 'mG': 'G'
657
773
  };
658
774
  return nucleotides.replace(/(\(invabasic\)|\(GalNAc-2-JNJ\)|mAps|mUps|mGps|mCps|fAps|fUps|fGps|fCps|fU|fA|fC|fG|mU|mA|mC|mG)/g, function (x: string) {return obj[x]});
659
775
  }
776
+
777
+ async function saveTableAsSdFile(table: DG.DataFrame) {
778
+ if (!table.columns.contains('Compound Name'))
779
+ grok.shell.warning("File was saved without columns 'Compound Name', 'Compound Components', 'Cpd MW', 'Salt mass', 'Batch MW'!");
780
+ let structureColumn = table.columns.byName('Sequence');
781
+ let result = '';
782
+ for (let i = 0; i < table.rowCount; i++) {
783
+ try {
784
+ let smiles = sequenceToSmiles(structureColumn.get(i));
785
+ //@ts-ignore
786
+ let mol = new OCL.Molecule.fromSmiles(smiles);
787
+ result += `\n${mol.toMolfile()}\n`;
788
+ for (let col of table.columns)
789
+ result += `> <${col.name}>\n${col.get(i)}\n\n`;
790
+ result += '$$$$';
791
+ }
792
+ catch (error) {
793
+ console.error(error);
794
+ }
795
+ }
796
+ let element = document.createElement('a');
797
+ element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result));
798
+ element.setAttribute('download', table.name + '.sdf');
799
+ element.click();
800
+ }
801
+
802
+ function parseNumber(saltName: string) {
803
+ let i = saltName.length;
804
+ while (saltName.length > -1 && saltName[i] != '(')
805
+ i--;
806
+ return parseInt(saltName.slice(i + 2));
807
+ }
808
+
809
+ //tags: autostart
810
+ export function autostartOligoSdFileSubscription() {
811
+ grok.events.onViewAdded.subscribe((v: any) => {
812
+ if (v.type == 'TableView' && v.dataFrame.columns.contains('Type'))
813
+ oligoSdFile(v.dataFrame);
814
+ });
815
+ }
816
+
817
+ let weightsObj: {[code: string]: number} = {};
818
+ for (let synthesizer of Object.keys(map))
819
+ for (let technology of Object.keys(map[synthesizer]))
820
+ for (let code of Object.keys(map[synthesizer][technology]))
821
+ weightsObj[code] = map[synthesizer][technology][code].weight;
822
+
823
+ function molecularWeight(sequence: string): number {
824
+ const codes = sortByStringLengthInDescendingOrder(Object.keys(weightsObj));
825
+ let weight = 0, i = 0;
826
+ while (i < sequence.length) {
827
+ let matchedCode = codes.find((s) => s == sequence.slice(i, i + s.length))!;
828
+ weight += weightsObj[sequence.slice(i, i + matchedCode.length)];
829
+ i += matchedCode!.length;
830
+ }
831
+ return weight - 61.97;
832
+ }
833
+
834
+ export function oligoSdFile(table: DG.DataFrame) {
835
+
836
+ function addColumns(t: DG.DataFrame) {
837
+ if (t.columns.contains('Compound Name'))
838
+ return grok.shell.error('Columns already exist!');
839
+
840
+ table.col('Source')?.init('Johnson and Johnson Pharma');
841
+ table.col('ICD')?.init('No Contract');
842
+
843
+ let sequence = t.col('Sequence')!;
844
+ let salt = t.col('Salt')!;
845
+ let equivalents = t.col('Equivalents')!;
846
+
847
+ t.columns.addNewString('Compound Name').init((i: number) => sequence.get(i));
848
+ t.columns.addNewString('Compound Comments').init((i: number) => (i > 0 && i % 2 == 0) ?
849
+ sequence.getString(i) + '; duplex of SS: ' + sequence.getString(i - 2) + ' and AS: ' + sequence.getString(i - 1) :
850
+ sequence.getString(i)
851
+ );
852
+ t.columns.addNewFloat('Cpd MW').init((i: number) => ((i + 1) % 3 == 0) ? DG.FLOAT_NULL : molecularWeight(sequence.get(i)));
853
+ t.columns.addNewFloat('Salt mass').init((i: number) => parseNumber(salt.get(i)) * equivalents.get(i));
854
+ t.columns.addNewCalculated('Batch MW', '${Cpd MW} + ${Salt mass}', DG.COLUMN_TYPE.FLOAT, false);
855
+
856
+ addColumnsPressed = true;
857
+ return newDf = t;
858
+ }
859
+
860
+ const columnsOrder = ["Chemistry", "Number", "Type", "Chemistry Name", "Internal compound ID", "IDP", "Sequence", "Compound Name",
861
+ "Compound Comments", "Salt", "Equivalents", "Purity", "Cpd MW", "Salt mass", "Batch MW", "Source", "ICD", "Owner"];
862
+ let newDf: DG.DataFrame;
863
+ let addColumnsPressed = false;
864
+
865
+ let d = ui.div([
866
+ ui.icons.edit(() => {
867
+ d.innerHTML = '';
868
+ d.append(
869
+ ui.link('Add Columns', async () => {
870
+ await addColumns(table);
871
+ grok.shell.tableView(table.name).grid.columns.setOrder(columnsOrder);
872
+ }, 'Add columns: Compound Name, Compound Components, Cpd MW, Salt mass, Batch MW', ''),
873
+ ui.button('Save SD file', () => saveTableAsSdFile(addColumnsPressed ? newDf : table))
874
+ );
875
+ let view = grok.shell.getTableView(table.name);
876
+ let typeCol = view.grid.col('Type')!;
877
+ let saltCol = view.grid.col('Salt')!;
878
+ saltCol.cellType = 'html';
879
+ typeCol.cellType = 'html';
880
+ view.grid.onCellPrepare(function (gc: DG.GridCell) {
881
+ if (gc.isTableCell) {
882
+ if (gc.gridColumn.name == 'Type')
883
+ gc.style.element = ui.choiceInput('', gc.cell.value, ['AS', 'SS', 'Duplex']).root;
884
+ else if (gc.gridColumn.name == 'Salt')
885
+ gc.style.element = ui.choiceInput('', gc.cell.value, ['Sodium (+1)', 'Sodium (+2)']).root;
886
+ }
887
+ });
888
+
889
+ table.onDataChanged.subscribe((_) => {
890
+ if (table.currentCol.name == 'IDP' && typeof table.currentCell.value != 'number')
891
+ grok.shell.error('Value should be numeric');
892
+ });
893
+ })
894
+ ]);
895
+ grok.shell.v.setRibbonPanels([[d]]);
896
+ }