diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts index 5ad5d37985b1770a7be5689e5961c1c84e95dcee..57b40fc926ebf270218995b93e140b18564fc01b 100644 --- a/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts +++ b/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts @@ -28,10 +28,11 @@ export const InitVolumeStreaming = StateAction.build({ params(a) { return { method: PD.Select<VolumeServerInfo.Kind>(getStreamingMethod(a && a.data), [['em', 'EM'], ['x-ray', 'X-Ray']]), - id: PD.Text((a && a.data.models[0].label) || ''), + id: PD.Text((a && a.data.models.length > 0 && a.data.models[0].label) || ''), serverUrl: PD.Text('https://webchem.ncbr.muni.cz/DensityServer') }; - } + }, + isApplicable: (a) => a.data.models.length === 1 })(({ ref, state, params }, plugin: PluginContext) => Task.create('Volume Streaming', async taskCtx => { // TODO: custom react view for this and the VolumeStreamingBehavior transformer diff --git a/src/mol-plugin/state/actions/structure.ts b/src/mol-plugin/state/actions/structure.ts index c4737d976ea37b9401586ca0520a6a96ebf85e82..95a2d19f1be439f87df8d50fbd3710f8e8b59426 100644 --- a/src/mol-plugin/state/actions/structure.ts +++ b/src/mol-plugin/state/actions/structure.ts @@ -105,29 +105,41 @@ const DownloadStructure = StateAction.build({ })(({ params, state }, ctx: PluginContext) => { const b = state.build(); const src = params.source; - let downloadParams: StateTransformer.Params<Download>; + let downloadParams: StateTransformer.Params<Download>[]; switch (src.name) { case 'url': - downloadParams = { url: src.params.url, isBinary: src.params.isBinary }; + downloadParams = [{ url: src.params.url, isBinary: src.params.isBinary }]; break; case 'pdbe-updated': - downloadParams = { url: `https://www.ebi.ac.uk/pdbe/static/entry/${src.params.id.toLowerCase()}_updated.cif`, isBinary: false, label: `PDBe: ${src.params.id}` }; + downloadParams = getDownloadParams(src.params.id, id => `https://www.ebi.ac.uk/pdbe/static/entry/${id.toLowerCase()}_updated.cif`, id => `PDBe: ${id}`, false); break; case 'rcsb': - downloadParams = { url: `https://files.rcsb.org/download/${src.params.id.toUpperCase()}.cif`, isBinary: false, label: `RCSB: ${src.params.id}` }; + downloadParams = getDownloadParams(src.params.id, id => `https://files.rcsb.org/download/${id.toUpperCase()}.cif`, id => `RCSB: ${id}`, false); break; case 'bcif-static': - downloadParams = { url: `https://webchem.ncbr.muni.cz/ModelServer/static/bcif/${src.params.id.toLowerCase()}`, isBinary: true, label: `BinaryCIF: ${src.params.id}` }; + downloadParams = getDownloadParams(src.params.id, id => `https://webchem.ncbr.muni.cz/ModelServer/static/bcif/${id.toLowerCase()}`, id => `BinaryCIF: ${id}`, true); break; default: throw new Error(`${(src as any).name} not supported.`); } - const data = b.toRoot().apply(StateTransforms.Data.Download, downloadParams, { props: { isGhost: true }}); - const traj = createModelTree(data, src.name === 'url' ? src.params.format : 'cif'); - return state.updateTree(createStructureTree(ctx, traj, params.source.params.supportProps)); + for (const download of downloadParams) { + const data = b.toRoot().apply(StateTransforms.Data.Download, download, { props: { isGhost: true }}); + const traj = createModelTree(data, src.name === 'url' ? src.params.format : 'cif'); + createStructureTree(ctx, traj, params.source.params.supportProps) + } + return state.updateTree(b); }); +function getDownloadParams(src: string, url: (id: string) => string, label: (id: string) => string, isBinary: boolean): StateTransformer.Params<Download>[] { + const ids = src.split(',').map(id => id.trim()).filter(id => !!id && id.length >= 4); + const ret: StateTransformer.Params<Download>[] = []; + for (const id of ids) { + ret.push({ url: url(id), isBinary, label: label(id) }) + } + return ret; +} + function createModelTree(b: StateBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, format: 'pdb' | 'cif' | 'gro' = 'cif') { let parsed: StateBuilder.To<PluginStateObject.Molecule.Trajectory> switch (format) { diff --git a/src/mol-plugin/state/snapshots.ts b/src/mol-plugin/state/snapshots.ts index e30e142ef29bb4d054b770cd91a545167d68dcbb..0c121c87d858fd486ecd6c365916de9294c75990 100644 --- a/src/mol-plugin/state/snapshots.ts +++ b/src/mol-plugin/state/snapshots.ts @@ -178,7 +178,7 @@ class PluginStateSnapshotManager extends PluginComponent<{ const snapshot = this.setCurrent(next)!; await this.plugin.state.setSnapshot(snapshot); const delay = typeof snapshot.durationInMs !== 'undefined' ? snapshot.durationInMs : this.state.nextSnapshotDelayInMs; - this.timeoutHandle = setTimeout(this.next, delay); + if (this.state.isPlaying) this.timeoutHandle = setTimeout(this.next, delay); }; play() { diff --git a/src/mol-plugin/state/transforms/model.ts b/src/mol-plugin/state/transforms/model.ts index dd52f03e21207603c8123c540b2ef209939be7f2..82d4caeb2bd9651b95b169266c5b1ee5d41c3cee 100644 --- a/src/mol-plugin/state/transforms/model.ts +++ b/src/mol-plugin/state/transforms/model.ts @@ -266,7 +266,6 @@ const UserStructureSelection = PluginStateTransform.BuiltIn({ const compiled = compile<Sel>(query); const result = compiled(new QueryContext(a.data)); const s = Sel.unionStructure(result); - if (s.elementCount === 0) return StateObject.Null; const props = { label: `${params.label || 'Selection'}`, description: structureDesc(s) }; return new SO.Molecule.Structure(s, props); } diff --git a/src/mol-script/language/parser.ts b/src/mol-script/language/parser.ts index 906810e9d7bb4544f52863a5b70fd1a6382a8c1e..a1074abdf4f6eaa94c583537f65ebad965ad91de 100644 --- a/src/mol-script/language/parser.ts +++ b/src/mol-script/language/parser.ts @@ -163,7 +163,7 @@ namespace Language { } function isNumber(value: string) { - return /-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?/.test(value); + return /-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?/.test(value) && !isNaN(+value); } export function parse(input: string): Expression[] {