@datagrok/sequence-translator 1.1.4 → 1.2.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/CHANGELOG.md +6 -0
- package/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/img/icons/calculator.png +0 -0
- package/img/icons/pattern.png +0 -0
- package/img/icons/structure.png +0 -0
- package/img/icons/toolkit.png +0 -0
- package/img/icons/translator.png +0 -0
- package/package.json +1 -1
- package/src/demo/demo-st-ui.ts +26 -36
- package/src/model/helpers.ts +17 -0
- package/src/model/sequence-to-structure-utils/monomer-code-parser.ts +2 -5
- package/src/model/sequence-to-structure-utils/sdf-tab.ts +1 -1
- package/src/model/sequence-to-structure-utils/sequence-to-molfile.ts +20 -3
- package/src/package.ts +57 -37
- package/src/plugins/const.ts +4 -0
- package/src/plugins/mermade.ts +48 -0
- package/src/view/app-ui.ts +193 -0
- package/src/view/{tabs/axolabs.ts → apps/oligo-pattern.ts} +106 -26
- package/src/view/{tabs/sdf.ts → apps/oligo-structure.ts} +13 -14
- package/src/view/{tabs/main.ts → apps/oligo-translator.ts} +15 -11
- package/src/view/const/ui.ts +14 -0
- package/src/view/style/pattern-app.css +1 -0
- package/src/view/style/structure-app.css +39 -0
- package/src/view/style/translator-app.css +46 -0
- package/src/view/utils/colored-input/colored-text-input.ts +7 -6
- package/src/view/utils/molecule-img.ts +21 -10
- package/src/view/utils/router.ts +71 -0
- package/src/demo/handle-error.ts +0 -12
- package/src/view/const/view.ts +0 -10
- package/src/view/css/axolabs-tab.css +0 -1
- package/src/view/css/main-tab.css +0 -46
- package/src/view/css/sdf-tab.css +0 -39
- package/src/view/view.ts +0 -127
- /package/src/view/const/{main-tab.ts → oligo-translator.ts} +0 -0
- /package/src/view/{css → style}/colored-text-input.css +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/src/demo/demo-st-ui.ts
CHANGED
|
@@ -2,24 +2,18 @@ import * as grok from 'datagrok-api/grok';
|
|
|
2
2
|
import * as ui from 'datagrok-api/ui';
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
|
-
import {handleError} from './handle-error';
|
|
6
|
-
|
|
7
5
|
import {delay} from '@datagrok-libraries/utils/src/test';
|
|
8
6
|
import {getJsonData} from '../model/data-loading-utils/json-loader';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
7
|
+
import {_package, oligoTranslatorApp, oligoPatternApp, oligoStructureApp} from '../package';
|
|
8
|
+
import {tryCatch} from '../model/helpers';
|
|
11
9
|
|
|
12
|
-
export async function
|
|
13
|
-
|
|
14
|
-
openSequenceTranslatorOnPane(0);
|
|
15
|
-
} catch (err: any) {
|
|
16
|
-
handleError(err);
|
|
17
|
-
}
|
|
10
|
+
export async function demoOligoTranslatorUI() {
|
|
11
|
+
await tryCatch(async () => oligoTranslatorApp());
|
|
18
12
|
}
|
|
19
13
|
|
|
20
|
-
export async function
|
|
21
|
-
|
|
22
|
-
async function emulateUserInput(value: string, idx: number, idxUpdate: (idx: number) => number) {
|
|
14
|
+
export async function demoOligoPatternUI() {
|
|
15
|
+
await tryCatch(async () => {
|
|
16
|
+
async function emulateUserInput(value: string, idx: number, idxUpdate: (idx: number) => number): Promise<void> {
|
|
23
17
|
await delay(3000);
|
|
24
18
|
|
|
25
19
|
// warning: this redefinition is necessary because
|
|
@@ -32,7 +26,7 @@ export async function demoDesignPatternUI() {
|
|
|
32
26
|
selectElement.dispatchEvent(event);
|
|
33
27
|
}
|
|
34
28
|
|
|
35
|
-
|
|
29
|
+
await oligoPatternApp();
|
|
36
30
|
|
|
37
31
|
let len: number;
|
|
38
32
|
|
|
@@ -44,28 +38,24 @@ export async function demoDesignPatternUI() {
|
|
|
44
38
|
const asNewValues = ['2\'-O-Methyl', '2\'-Fluoro', '2\'-O-MOE'];
|
|
45
39
|
asNewValues.forEach(async (value, idx) => {
|
|
46
40
|
emulateUserInput(value, idx, (i) => (len - 2 - 2 * i));
|
|
47
|
-
})
|
|
48
|
-
}
|
|
49
|
-
handleError(err);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export async function demoVisualizeDuplexUI() {
|
|
54
|
-
try {
|
|
55
|
-
await openSequenceTranslatorOnPane(2);
|
|
56
|
-
} catch (err: any) {
|
|
57
|
-
handleError(err);
|
|
58
|
-
}
|
|
41
|
+
});
|
|
42
|
+
})
|
|
59
43
|
}
|
|
60
44
|
|
|
61
|
-
async function
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
45
|
+
export async function demoOligoStructureUI() {
|
|
46
|
+
await tryCatch(async () => {
|
|
47
|
+
async function setInputValue(idx: number, sequence: string): Promise<void> {
|
|
48
|
+
await delay(500);
|
|
49
|
+
const textInputs: NodeListOf<HTMLTextAreaElement> = document.querySelectorAll('.colored-text-input > textarea');
|
|
50
|
+
const textarea = textInputs[idx];
|
|
51
|
+
textarea.value = sequence;
|
|
52
|
+
const event = new Event('input');
|
|
53
|
+
textarea.dispatchEvent(event);
|
|
54
|
+
}
|
|
55
|
+
await oligoStructureApp();
|
|
56
|
+
const inputSequences = ['Afcgacsu', 'Afcgacsu', 'Afcgacsu'];
|
|
57
|
+
inputSequences.forEach(async (sequence, idx) => {
|
|
58
|
+
await setInputValue(idx, sequence);
|
|
59
|
+
})
|
|
60
|
+
});
|
|
71
61
|
}
|
package/src/model/helpers.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
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';
|
|
1
4
|
import * as DG from 'datagrok-api/dg';
|
|
2
5
|
|
|
3
6
|
export function sortByReverseLength(array: string[]): string[] {
|
|
@@ -10,3 +13,17 @@ export function download(name: string, href: string): void {
|
|
|
10
13
|
element.setAttribute('download', name);
|
|
11
14
|
element.click();
|
|
12
15
|
}
|
|
16
|
+
|
|
17
|
+
export async function tryCatch<T>(func: () => Promise<T>, finallyFunc?: () => any, callbackName: string = 'Oligo app'): Promise<T> {
|
|
18
|
+
try {
|
|
19
|
+
return await func();
|
|
20
|
+
} catch (err: any) {
|
|
21
|
+
const errMsg: string = err.hasOwnProperty('message') ? err.message : err.toString();
|
|
22
|
+
grok.shell.error(`${callbackName} error: ` + errMsg);
|
|
23
|
+
throw err;
|
|
24
|
+
} finally {
|
|
25
|
+
if (finallyFunc)
|
|
26
|
+
finallyFunc();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
@@ -12,15 +12,12 @@ import {monomersWithPhosphateLinkers} from '../data-loading-utils/json-loader';
|
|
|
12
12
|
* omitted linkers, if needed) */
|
|
13
13
|
export class MonomerSequenceParser {
|
|
14
14
|
constructor(
|
|
15
|
-
private sequence: string,
|
|
15
|
+
private sequence: string,
|
|
16
16
|
// todo: remove from the list of parameters
|
|
17
17
|
private codeMap: Map<string, string>
|
|
18
18
|
) {
|
|
19
|
-
this.lib = MonomerLibWrapper.getInstance();
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
private lib: MonomerLibWrapper;
|
|
23
|
-
|
|
24
21
|
/** Get sequence of parsed monomer symbols, which are unique short names for
|
|
25
22
|
* the monomers within the Monomer Library */
|
|
26
23
|
parseSequence(): string[] {
|
|
@@ -65,7 +62,7 @@ export class MonomerSequenceParser {
|
|
|
65
62
|
const code = allCodesOfFormat.find(
|
|
66
63
|
(s: string) => s === this.sequence.substring(i, i + s.length)
|
|
67
64
|
)!;
|
|
68
|
-
|
|
65
|
+
parsedCodes.push(code);
|
|
69
66
|
i += code.length;
|
|
70
67
|
}
|
|
71
68
|
return parsedCodes;
|
|
@@ -62,7 +62,7 @@ export function saveSdf(
|
|
|
62
62
|
nonEmptyStrands.length === 0 ||
|
|
63
63
|
nonEmptyStrands.length === 1 && ss.strand === ''
|
|
64
64
|
) {
|
|
65
|
-
grok.shell.warning('Enter SS and AS/AS2 to save SDF');
|
|
65
|
+
grok.shell.warning('Enter SS and optionally AS/AS2 to save SDF');
|
|
66
66
|
} else {
|
|
67
67
|
let result: string;
|
|
68
68
|
if (oneEntity) {
|
|
@@ -8,11 +8,11 @@ import {MonomerLibWrapper} from '../monomer-lib/lib-wrapper';
|
|
|
8
8
|
|
|
9
9
|
export class SequenceToMolfileConverter {
|
|
10
10
|
constructor(
|
|
11
|
-
sequence: string, invert: boolean = false, format: string
|
|
11
|
+
sequence: string, private invert: boolean = false, format: string
|
|
12
12
|
) {
|
|
13
13
|
this.lib = MonomerLibWrapper.getInstance();
|
|
14
14
|
const codeToSymbolMap = this.lib.getCodeToSymbolMap(format);
|
|
15
|
-
this.parser = new MonomerSequenceParser(sequence,
|
|
15
|
+
this.parser = new MonomerSequenceParser(sequence, codeToSymbolMap);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
private parser: MonomerSequenceParser;
|
|
@@ -25,7 +25,24 @@ export class SequenceToMolfileConverter {
|
|
|
25
25
|
const monomerMolfile = this.getMonomerMolfile(monomerSymbol, idx);
|
|
26
26
|
monomerMolfiles.push(monomerMolfile);
|
|
27
27
|
})
|
|
28
|
-
|
|
28
|
+
let molfile = this.getPolymerMolfile(monomerMolfiles);
|
|
29
|
+
if (this.invert) {
|
|
30
|
+
molfile = this.reflect(molfile);
|
|
31
|
+
molfile = this.invertBondConfiguration(molfile);
|
|
32
|
+
}
|
|
33
|
+
return molfile;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private invertBondConfiguration(molfile: string): string {
|
|
37
|
+
const beginIdx = molfile.indexOf('M V30 BEGIN BOND');
|
|
38
|
+
const endIdx = molfile.indexOf('M V30 END BOND');
|
|
39
|
+
let bondBlock = molfile.substring(beginIdx, endIdx);
|
|
40
|
+
bondBlock = bondBlock.replace(
|
|
41
|
+
/(CFG=)([13])/g,
|
|
42
|
+
(match, group1, group2) => (group2 === '1') ? `${group1}3` : (group2 === '3') ? `${group1}1` : match
|
|
43
|
+
);
|
|
44
|
+
const result = molfile.substring(0, beginIdx) + bondBlock + molfile.substring(endIdx);
|
|
45
|
+
return result;
|
|
29
46
|
}
|
|
30
47
|
|
|
31
48
|
private getMonomerMolfile(monomerSymbol: string, idx: number): string {
|
package/src/package.ts
CHANGED
|
@@ -2,7 +2,8 @@ import * as grok from 'datagrok-api/grok';
|
|
|
2
2
|
import * as ui from 'datagrok-api/ui';
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {AppUIFactory, CombinedAppUI} from './view/app-ui';
|
|
6
|
+
import {tryCatch} from './model/helpers';
|
|
6
7
|
import {LIB_PATH, DEFAULT_LIB_FILENAME} from './model/data-loading-utils/const';
|
|
7
8
|
import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
|
|
8
9
|
import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
@@ -12,15 +13,17 @@ import {linkStrandsV3000} from './model/sequence-to-structure-utils/mol-transfor
|
|
|
12
13
|
import {MonomerLibWrapper} from './model/monomer-lib/lib-wrapper';
|
|
13
14
|
import {FormatDetector} from './model/parsing-validation/format-detector';
|
|
14
15
|
import {SequenceValidator} from './model/parsing-validation/sequence-validator';
|
|
15
|
-
import {
|
|
16
|
+
import {demoOligoTranslatorUI, demoOligoPatternUI, demoOligoStructureUI} from './demo/demo-st-ui';
|
|
16
17
|
import {FormatConverter} from './model/format-translation/format-converter';
|
|
18
|
+
import {APP} from './view/const/ui';
|
|
19
|
+
import {getExternalAppViewFactories} from './plugins/mermade';
|
|
17
20
|
|
|
18
21
|
class StPackage extends DG.Package {
|
|
19
22
|
private _monomerLib?: IMonomerLib;
|
|
20
23
|
|
|
21
24
|
get monomerLib(): IMonomerLib {
|
|
22
25
|
if (!this._monomerLib)
|
|
23
|
-
throw new Error ('
|
|
26
|
+
throw new Error ('Monomer lib not loaded')
|
|
24
27
|
return this._monomerLib!;
|
|
25
28
|
}
|
|
26
29
|
|
|
@@ -29,37 +32,54 @@ class StPackage extends DG.Package {
|
|
|
29
32
|
return;
|
|
30
33
|
|
|
31
34
|
const pi: DG.TaskBarProgressIndicator = DG.TaskBarProgressIndicator.create(
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
`Initializing ${APP.COMBINED} monomer library ...`);
|
|
36
|
+
await tryCatch(async () => {
|
|
34
37
|
const libHelper: IMonomerLibHelper = await getMonomerLibHelper();
|
|
35
38
|
this._monomerLib = await libHelper.readLibrary(LIB_PATH, DEFAULT_LIB_FILENAME);
|
|
36
|
-
}
|
|
37
|
-
const errMsg: string = err.hasOwnProperty('message') ? err.message : err.toString();
|
|
38
|
-
throw new Error('Loading monomer library: ' + errMsg);
|
|
39
|
-
} finally {
|
|
40
|
-
pi.close();
|
|
41
|
-
}
|
|
39
|
+
}, () => pi.close());
|
|
42
40
|
}
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
export const _package: StPackage = new StPackage();
|
|
46
44
|
|
|
47
|
-
|
|
45
|
+
async function buildLayout(appName: string): Promise<void> {
|
|
46
|
+
await initSequenceTranslatorLibData();
|
|
47
|
+
const appUI = AppUIFactory.getUI(appName);
|
|
48
|
+
await appUI.createAppLayout();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
//name: Oligo Toolkit
|
|
53
|
+
//meta.icon: img/icons/toolkit.png
|
|
48
54
|
//tags: app
|
|
49
|
-
export async function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
export async function oligoToolkitApp(): Promise<void> {
|
|
56
|
+
await initSequenceTranslatorLibData();
|
|
57
|
+
const externalViewFactories = await getExternalAppViewFactories();
|
|
58
|
+
if (!externalViewFactories)
|
|
59
|
+
throw new Error('External app view factories not loaded');
|
|
60
|
+
const appUI = new CombinedAppUI(externalViewFactories!);
|
|
61
|
+
await appUI.createAppLayout();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
//name: Oligo Translator
|
|
65
|
+
//meta.icon: img/icons/translator.png
|
|
66
|
+
//tags: app
|
|
67
|
+
export async function oligoTranslatorApp(): Promise<void> {
|
|
68
|
+
await buildLayout(APP.TRANSLATOR);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
//name: Oligo Pattern
|
|
72
|
+
//meta.icon: img/icons/pattern.png
|
|
73
|
+
//tags: app
|
|
74
|
+
export async function oligoPatternApp(): Promise<void> {
|
|
75
|
+
await buildLayout(APP.PATTERN);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
//name: Oligo Structure
|
|
79
|
+
//meta.icon: img/icons/structure.png
|
|
80
|
+
//tags: app
|
|
81
|
+
export async function oligoStructureApp(): Promise<void> {
|
|
82
|
+
await buildLayout(APP.STRUCTRE);
|
|
63
83
|
}
|
|
64
84
|
|
|
65
85
|
//name: initSequenceTranslatorLibData
|
|
@@ -99,28 +119,28 @@ export function linkStrands(strands: { senseStrands: string[], antiStrands: stri
|
|
|
99
119
|
return linkStrandsV3000(strands, true);
|
|
100
120
|
}
|
|
101
121
|
|
|
102
|
-
//name:
|
|
103
|
-
//meta.demoPath: Bioinformatics |
|
|
122
|
+
//name: demoOligoTranslator
|
|
123
|
+
//meta.demoPath: Bioinformatics | Oligo Toolkit | Translator
|
|
104
124
|
//description: Translate oligonucleotide sequences across various formats accepted by different synthesizers
|
|
105
125
|
//meta.path: /apps/Tutorials/Demo/Bioinformatics/Oligonucleotide%20Sequence:%20Translate
|
|
106
126
|
export async function demoTranslateSequence(): Promise<void> {
|
|
107
|
-
await
|
|
127
|
+
await demoOligoTranslatorUI();
|
|
108
128
|
}
|
|
109
129
|
|
|
110
|
-
//name:
|
|
111
|
-
//meta.demoPath: Bioinformatics |
|
|
130
|
+
//name: demoOligoPattern
|
|
131
|
+
//meta.demoPath: Bioinformatics | Oligo Toolkit | Pattern
|
|
112
132
|
//description: Design a modification pattern for an oligonucleotide sequence
|
|
113
133
|
//meta.path:%20/apps/Tutorials/Demo/Bioinformatics/Oligonucleotide%20Sequence:%20Visualize%20duplex
|
|
114
|
-
export async function
|
|
115
|
-
await
|
|
134
|
+
export async function demoOligoPattern(): Promise<void> {
|
|
135
|
+
await demoOligoPatternUI();
|
|
116
136
|
}
|
|
117
137
|
|
|
118
|
-
//name:
|
|
119
|
-
//meta.demoPath: Bioinformatics |
|
|
138
|
+
//name: demoOligoStructure
|
|
139
|
+
//meta.demoPath: Bioinformatics | Oligo Toolkit | Structure
|
|
120
140
|
//description: Visualize duplex and save SDF
|
|
121
141
|
//meta.path:%20/apps/Tutorials/Demo/Bioinformatics/Oligonucleotide%20Sequence:%20Visualize%20duplex
|
|
122
|
-
export async function
|
|
123
|
-
await
|
|
142
|
+
export async function demoOligoStructure(): Promise<void> {
|
|
143
|
+
await demoOligoStructureUI();
|
|
124
144
|
}
|
|
125
145
|
|
|
126
146
|
//name: translateOligonucleotideSequence
|
|
@@ -0,0 +1,48 @@
|
|
|
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 {ExternalPluginUI} from '../view/app-ui';
|
|
7
|
+
import {ColoredTextInput} from '../view/utils/colored-input/colored-text-input';
|
|
8
|
+
import {highlightInvalidSubsequence} from '../view/utils/colored-input/input-painters';
|
|
9
|
+
import {codesToHelmDictionary} from '../model/data-loading-utils/json-loader';
|
|
10
|
+
import {MERMADE} from './const';
|
|
11
|
+
|
|
12
|
+
export async function getExternalAppViewFactories(): Promise<{[name: string]: () => DG.View} | undefined> {
|
|
13
|
+
const externalPluginData = {
|
|
14
|
+
[MERMADE.FUNCTION_NAME]: {
|
|
15
|
+
tabName: MERMADE.TAB_NAME,
|
|
16
|
+
parameters: getMerMadeParameters()
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const result: {[tabName: string]: () => DG.View} = {};
|
|
21
|
+
|
|
22
|
+
for (const [pluginName, data] of Object.entries(externalPluginData)) {
|
|
23
|
+
let div: HTMLDivElement;
|
|
24
|
+
try {
|
|
25
|
+
div = await grok.functions.call(pluginName, data.parameters);
|
|
26
|
+
} catch (err) {
|
|
27
|
+
console.error(`Plugin ${pluginName} not loaded, error:`, err)
|
|
28
|
+
div = ui.divText('error loading');
|
|
29
|
+
}
|
|
30
|
+
const pluginUI = new ExternalPluginUI(data.tabName, div);
|
|
31
|
+
|
|
32
|
+
// intentonally don't await for the promise
|
|
33
|
+
pluginUI.initView();
|
|
34
|
+
|
|
35
|
+
result[data.tabName] = () => pluginUI.getView();
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getMerMadeParameters(): {[name: string]: any} {
|
|
41
|
+
const base = ui.textInput('', '');
|
|
42
|
+
const input = new ColoredTextInput(base, highlightInvalidSubsequence);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
coloredInput: input,
|
|
46
|
+
codes: codesToHelmDictionary
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
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 {TAB, APP} from './const/ui';
|
|
7
|
+
import {TranslatorLayoutHandler} from './apps/oligo-translator';
|
|
8
|
+
import {StructureLayoutHandler} from './apps/oligo-structure';
|
|
9
|
+
import {PatternLayoutHandler} from './apps/oligo-pattern';
|
|
10
|
+
import {MonomerLibViewer} from './monomer-lib-viewer/viewer';
|
|
11
|
+
import {_package} from '../package';
|
|
12
|
+
import {tryCatch} from '../model/helpers';
|
|
13
|
+
|
|
14
|
+
type ViewFactories = {[name: string]: () => DG.View};
|
|
15
|
+
|
|
16
|
+
export abstract class AppUIBase {
|
|
17
|
+
constructor(protected appName: string, protected parentAppName?: string) { }
|
|
18
|
+
abstract addView(): Promise<void>;
|
|
19
|
+
|
|
20
|
+
async createAppLayout(): Promise<void> {
|
|
21
|
+
const pi: DG.TaskBarProgressIndicator = DG.TaskBarProgressIndicator.create(`Loading ${this.appName}...`);
|
|
22
|
+
|
|
23
|
+
let currentView = grok.shell.v?.root;
|
|
24
|
+
if (currentView)
|
|
25
|
+
ui.setUpdateIndicator(currentView, true);
|
|
26
|
+
|
|
27
|
+
await tryCatch(async () => {
|
|
28
|
+
await this.addView();
|
|
29
|
+
}, () => pi.close());
|
|
30
|
+
|
|
31
|
+
if (currentView)
|
|
32
|
+
ui.setUpdateIndicator(currentView, false);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
abstract class SimpleAppUIBase extends AppUIBase {
|
|
37
|
+
constructor(appName: string) {
|
|
38
|
+
super(appName);
|
|
39
|
+
this.view = DG.View.create();
|
|
40
|
+
this.setupView();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
protected view: DG.View;
|
|
44
|
+
async addView(): Promise<void> {
|
|
45
|
+
await this.initView();
|
|
46
|
+
const name = this.parentAppName ? this.parentAppName + '/' + this.appName : this.appName;
|
|
47
|
+
this.view.path = `/apps/${_package.name}/${name.replace(/\s/g, '')}/`;
|
|
48
|
+
grok.shell.addView(this.view);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
protected abstract getHtml(): Promise<HTMLDivElement>;
|
|
52
|
+
async initView(): Promise<void> {
|
|
53
|
+
const html = await this.getHtml();
|
|
54
|
+
this.view.append(html);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
protected setupView(): void {
|
|
58
|
+
this.view.box = true;
|
|
59
|
+
this.view.name = this.appName;
|
|
60
|
+
|
|
61
|
+
const windows = grok.shell.windows;
|
|
62
|
+
windows.showProperties = false;
|
|
63
|
+
windows.showToolbox = false;
|
|
64
|
+
windows.showHelp = false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getView(): DG.View {
|
|
68
|
+
return this.view;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class CombinedAppUI extends AppUIBase {
|
|
73
|
+
constructor(externalViewFactories: ViewFactories) {
|
|
74
|
+
super(APP.COMBINED)
|
|
75
|
+
this.externalViewFactories = externalViewFactories;
|
|
76
|
+
const factories = this.getViewFactories();
|
|
77
|
+
this.multiView = new DG.MultiView({viewFactories: factories});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private multiView: DG.MultiView;
|
|
81
|
+
private externalViewFactories?: ViewFactories;
|
|
82
|
+
|
|
83
|
+
private getViewFactories(): ViewFactories {
|
|
84
|
+
function viewFactory(uiConstructor: new (view: DG.View) => SimpleAppUIBase): () => DG.View {
|
|
85
|
+
const view = DG.View.create();
|
|
86
|
+
const translateUI = new uiConstructor(view);
|
|
87
|
+
// intentonally don't await for the promise
|
|
88
|
+
translateUI.initView();
|
|
89
|
+
return () => translateUI.getView();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let result: {[key: string]: () => DG.View } = {
|
|
93
|
+
[TAB.TRANSLATOR]: viewFactory(OligoTranslatorUI),
|
|
94
|
+
[TAB.PATTERN]: viewFactory(OligoPatternUI),
|
|
95
|
+
[TAB.STRUCTRE]: viewFactory(OligoStructureUI),
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (this.externalViewFactories)
|
|
99
|
+
result = Object.assign({}, result, this.externalViewFactories);
|
|
100
|
+
|
|
101
|
+
return result
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private getPath(): string {
|
|
105
|
+
let name = this.multiView.tabs.currentPane.name;
|
|
106
|
+
name = name.charAt(0).toUpperCase() + name.substring(1).toLowerCase();
|
|
107
|
+
const path = `/apps/${_package.name}/OligoToolkit/${name}`;
|
|
108
|
+
return path;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private setUrl(): void {
|
|
112
|
+
this.multiView.path = this.getPath();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async addView(): Promise<void> {
|
|
116
|
+
this.multiView.tabs.onTabChanged.subscribe(() => this.setUrl());
|
|
117
|
+
this.setUrl();
|
|
118
|
+
grok.shell.addView(this.multiView);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** For plugins from external packages */
|
|
123
|
+
export class ExternalPluginUI extends SimpleAppUIBase {
|
|
124
|
+
constructor(viewName: string, layout: HTMLDivElement) {
|
|
125
|
+
super(viewName);
|
|
126
|
+
this.layout = layout;
|
|
127
|
+
}
|
|
128
|
+
private layout: HTMLDivElement;
|
|
129
|
+
|
|
130
|
+
protected getHtml(): Promise<HTMLDivElement> {
|
|
131
|
+
return Promise.resolve(this.layout);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export class AppUIFactory {
|
|
136
|
+
private constructor() {}
|
|
137
|
+
|
|
138
|
+
static getUI(appName: string): SimpleAppUIBase {
|
|
139
|
+
switch (appName) {
|
|
140
|
+
case APP.TRANSLATOR:
|
|
141
|
+
return new OligoTranslatorUI();
|
|
142
|
+
case APP.PATTERN:
|
|
143
|
+
return new OligoPatternUI();
|
|
144
|
+
case APP.STRUCTRE:
|
|
145
|
+
return new OligoStructureUI();
|
|
146
|
+
default:
|
|
147
|
+
throw new Error(`Unknown app name: ${appName}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
class OligoTranslatorUI extends SimpleAppUIBase {
|
|
153
|
+
constructor() {
|
|
154
|
+
super(APP.TRANSLATOR);
|
|
155
|
+
|
|
156
|
+
const viewMonomerLibIcon = ui.iconFA('book', MonomerLibViewer.view, 'View monomer library');
|
|
157
|
+
this.topPanel = [
|
|
158
|
+
viewMonomerLibIcon,
|
|
159
|
+
];
|
|
160
|
+
this.view.setRibbonPanels([this.topPanel]);
|
|
161
|
+
this.ui = new TranslatorLayoutHandler();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private readonly topPanel: HTMLElement[];
|
|
165
|
+
private readonly ui: TranslatorLayoutHandler;
|
|
166
|
+
|
|
167
|
+
protected getHtml(): Promise<HTMLDivElement> {
|
|
168
|
+
return this.ui.getHtmlElement();
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
class OligoPatternUI extends SimpleAppUIBase {
|
|
173
|
+
constructor() {
|
|
174
|
+
super(APP.PATTERN);
|
|
175
|
+
this.ui = new PatternLayoutHandler();
|
|
176
|
+
}
|
|
177
|
+
private readonly ui: PatternLayoutHandler;
|
|
178
|
+
protected getHtml(): Promise<HTMLDivElement> {
|
|
179
|
+
return Promise.resolve(this.ui.htmlDivElement);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
class OligoStructureUI extends SimpleAppUIBase {
|
|
184
|
+
constructor() {
|
|
185
|
+
super(APP.STRUCTRE)
|
|
186
|
+
this.ui = new StructureLayoutHandler();
|
|
187
|
+
}
|
|
188
|
+
private readonly ui: StructureLayoutHandler;
|
|
189
|
+
|
|
190
|
+
protected getHtml(): Promise<HTMLDivElement> {
|
|
191
|
+
return this.ui.getHtmlDivElement();
|
|
192
|
+
}
|
|
193
|
+
}
|