From 349880ce90e9354fa8e6a27606993af5beb0e28a Mon Sep 17 00:00:00 2001 From: Alexander Rose <alexander.rose@weirdbyte.de> Date: Fri, 28 Sep 2018 20:09:19 -0700 Subject: [PATCH] wip... --- package-lock.json | Bin 411616 -> 411635 bytes src/apps/canvas/app.ts | 7 +- src/apps/canvas/component/representation.tsx | 49 ++ .../component/structure-representation.tsx | 261 ------ src/apps/canvas/component/structure-view.tsx | 7 +- .../component/volume-representation.tsx | 245 ------ src/apps/canvas/index.ts | 2 +- src/apps/canvas/structure-view.ts | 4 +- src/apps/structure-info/volume.ts | 2 +- src/apps/viewer/index.html | 13 - src/apps/viewer/index.tsx | 107 --- src/mol-app/component/color-theme.tsx | 56 ++ src/mol-app/component/parameter/boolean.tsx | 38 + src/mol-app/component/parameter/number.tsx | 44 + src/mol-app/component/parameter/range.tsx | 44 + src/mol-app/component/parameter/select.tsx | 41 + src/mol-app/component/parameters.tsx | 52 ++ src/mol-app/component/sequence.tsx | 81 ++ src/mol-app/context/context.ts | 67 -- src/mol-app/controller/controller.ts | 49 -- src/mol-app/controller/entity/tree.ts | 27 - src/mol-app/controller/layout.ts | 216 ----- src/mol-app/controller/misc/jobs.ts | 81 -- src/mol-app/controller/misc/log.ts | 24 - src/mol-app/controller/transform/list.ts | 27 - .../controller/visualization/sequence-view.ts | 21 - .../controller/visualization/viewport.ts | 27 - src/mol-app/event/basic.ts | 40 - src/mol-app/event/event.ts | 43 - src/mol-app/service/dispatcher.ts | 59 -- src/mol-app/service/job.ts | 132 --- src/mol-app/service/logger.ts | 51 -- src/mol-app/skin/base.scss | 45 - src/mol-app/skin/bootstrap.scss | 25 - src/mol-app/skin/bootstrap/badges.scss | 68 -- src/mol-app/skin/bootstrap/button-groups.scss | 244 ------ src/mol-app/skin/bootstrap/buttons.scss | 168 ---- src/mol-app/skin/bootstrap/forms.scss | 617 ------------- src/mol-app/skin/bootstrap/input-groups.scss | 171 ---- src/mol-app/skin/bootstrap/labels.scss | 66 -- src/mol-app/skin/bootstrap/mixins.scss | 40 - .../bootstrap/mixins/background-variant.scss | 12 - .../skin/bootstrap/mixins/border-radius.scss | 18 - .../skin/bootstrap/mixins/buttons.scss | 65 -- .../skin/bootstrap/mixins/clearfix.scss | 22 - src/mol-app/skin/bootstrap/mixins/forms.scss | 88 -- src/mol-app/skin/bootstrap/mixins/grid.scss | 122 --- src/mol-app/skin/bootstrap/mixins/image.scss | 33 - src/mol-app/skin/bootstrap/mixins/labels.scss | 12 - .../skin/bootstrap/mixins/opacity.scss | 8 - .../skin/bootstrap/mixins/tab-focus.scss | 9 - .../skin/bootstrap/mixins/text-emphasis.scss | 12 - .../skin/bootstrap/mixins/text-overflow.scss | 8 - .../bootstrap/mixins/vendor-prefixes.scss | 222 ----- src/mol-app/skin/bootstrap/normalize.scss | 424 --------- src/mol-app/skin/bootstrap/scaffolding.scss | 161 ---- src/mol-app/skin/bootstrap/type.scss | 298 ------- src/mol-app/skin/bootstrap/variables.scss | 353 -------- src/mol-app/skin/colors/blue.scss | 24 - src/mol-app/skin/colors/dark.scss | 22 - src/mol-app/skin/colors/light.scss | 30 - .../skin/components/controls-base.scss | 144 --- src/mol-app/skin/components/controls.scss | 197 ----- src/mol-app/skin/components/entity.scss | 225 ----- src/mol-app/skin/components/help.scss | 28 - src/mol-app/skin/components/jobs.scss | 131 --- src/mol-app/skin/components/log.scss | 97 --- src/mol-app/skin/components/misc.scss | 69 -- src/mol-app/skin/components/panel.scss | 142 --- .../skin/components/sequence-view.scss | 9 - src/mol-app/skin/components/slider.scss | 164 ---- src/mol-app/skin/components/viewport.scss | 93 -- src/mol-app/skin/fonts/fontello.eot | Bin 49272 -> 0 bytes src/mol-app/skin/fonts/fontello.svg | 442 ---------- src/mol-app/skin/fonts/fontello.ttf | Bin 49104 -> 0 bytes src/mol-app/skin/fonts/fontello.woff | Bin 29392 -> 0 bytes src/mol-app/skin/fonts/fontello.woff2 | Bin 23892 -> 0 bytes src/mol-app/skin/icons.scss | 135 --- src/mol-app/skin/layout.scss | 29 - src/mol-app/skin/layout/common.scss | 60 -- src/mol-app/skin/layout/landscape.scss | 81 -- src/mol-app/skin/layout/outside.scss | 89 -- src/mol-app/skin/layout/portrait.scss | 99 --- src/mol-app/skin/logo.scss | 47 - src/mol-app/skin/molstar-blue.scss | 2 - src/mol-app/skin/molstar-dark.scss | 2 - src/mol-app/skin/molstar-light.scss | 2 - src/mol-app/skin/ui.scss | 39 - src/mol-app/skin/variables.scss | 78 -- src/mol-app/ui/controls/common.tsx | 167 ---- src/mol-app/ui/controls/slider.tsx | 824 ------------------ src/mol-app/ui/entity/tree.tsx | 113 --- src/mol-app/ui/layout.tsx | 89 -- src/mol-app/ui/misc/jobs.tsx | 64 -- src/mol-app/ui/misc/log.tsx | 69 -- src/mol-app/ui/transform/backbone.tsx | 244 ------ src/mol-app/ui/transform/ball-and-stick.tsx | 234 ----- src/mol-app/ui/transform/carbohydrate.tsx | 250 ------ src/mol-app/ui/transform/cartoon.tsx | 278 ------ .../ui/transform/distance-restraint.tsx | 219 ----- src/mol-app/ui/transform/file-loader.tsx | 71 -- src/mol-app/ui/transform/list.tsx | 95 -- src/mol-app/ui/transform/model.tsx | 67 -- src/mol-app/ui/transform/spacefill.tsx | 242 ----- src/mol-app/ui/transform/structure.tsx | 61 -- src/mol-app/ui/transform/url-loader.tsx | 124 --- src/mol-app/ui/view.tsx | 93 -- src/mol-app/ui/visualization/image-canvas.tsx | 89 -- .../ui/visualization/sequence-view.tsx | 85 -- src/mol-app/ui/visualization/viewport.tsx | 244 ------ src/mol-geo/geometry/color-data.ts | 26 +- src/mol-geo/geometry/geometry.ts | 10 +- src/mol-geo/geometry/lines/lines.ts | 9 +- src/mol-geo/geometry/mesh/mesh.ts | 10 +- src/mol-geo/geometry/points/points.ts | 17 +- src/mol-geo/geometry/size-data.ts | 23 +- src/mol-geo/representation/index.ts | 2 + src/mol-geo/representation/shape/index.ts | 12 +- .../structure/complex-representation.ts | 3 +- .../structure/complex-visual.ts | 16 +- src/mol-geo/representation/structure/index.ts | 18 +- .../structure/representation/backbone.ts | 9 +- .../representation/ball-and-stick.ts | 25 +- .../structure/representation/carbohydrate.ts | 20 +- .../structure/representation/cartoon.ts | 25 +- .../representation/distance-restraint.ts | 14 +- .../representation/molecular-surface.ts | 69 ++ .../structure/representation/point.ts | 9 +- .../structure/representation/spacefill.ts | 9 +- .../structure/representation/surface.ts | 64 -- .../structure/units-representation.ts | 3 +- .../representation/structure/units-visual.ts | 56 +- .../visual/carbohydrate-link-cylinder.ts | 24 +- .../visual/carbohydrate-symbol-mesh.ts | 20 +- .../visual/cross-link-restraint-cylinder.ts | 21 +- .../structure/visual/element-point.ts | 16 +- .../structure/visual/element-sphere.ts | 14 +- .../visual/gaussian-density-point.ts | 20 +- .../structure/visual/gaussian-surface-mesh.ts | 12 +- .../visual/gaussian-surface-wireframe.ts | 12 +- .../visual/inter-unit-link-cylinder.ts | 21 +- .../visual/intra-unit-link-cylinder.ts | 21 +- .../structure/visual/nucleotide-block-mesh.ts | 8 +- .../visual/polymer-backbone-cylinder.ts | 23 +- .../visual/polymer-direction-wedge.ts | 20 +- .../structure/visual/polymer-gap-cylinder.ts | 35 +- .../structure/visual/polymer-trace-mesh.ts | 35 +- .../structure/visual/util/element.ts | 7 +- .../structure/visual/util/link.ts | 20 +- src/mol-geo/representation/volume/index.ts | 7 +- .../volume/{surface.ts => isosurface.ts} | 20 +- .../structure/unit/gaussian-density.ts | 10 +- src/mol-view/parameter.ts | 22 +- src/mol-view/stage.ts | 188 ---- 154 files changed, 911 insertions(+), 11544 deletions(-) create mode 100644 src/apps/canvas/component/representation.tsx delete mode 100644 src/apps/canvas/component/structure-representation.tsx delete mode 100644 src/apps/canvas/component/volume-representation.tsx delete mode 100644 src/apps/viewer/index.html delete mode 100644 src/apps/viewer/index.tsx create mode 100644 src/mol-app/component/color-theme.tsx create mode 100644 src/mol-app/component/parameter/boolean.tsx create mode 100644 src/mol-app/component/parameter/number.tsx create mode 100644 src/mol-app/component/parameter/range.tsx create mode 100644 src/mol-app/component/parameter/select.tsx create mode 100644 src/mol-app/component/parameters.tsx create mode 100644 src/mol-app/component/sequence.tsx delete mode 100644 src/mol-app/context/context.ts delete mode 100644 src/mol-app/controller/controller.ts delete mode 100644 src/mol-app/controller/entity/tree.ts delete mode 100644 src/mol-app/controller/layout.ts delete mode 100644 src/mol-app/controller/misc/jobs.ts delete mode 100644 src/mol-app/controller/misc/log.ts delete mode 100644 src/mol-app/controller/transform/list.ts delete mode 100644 src/mol-app/controller/visualization/sequence-view.ts delete mode 100644 src/mol-app/controller/visualization/viewport.ts delete mode 100644 src/mol-app/event/basic.ts delete mode 100644 src/mol-app/event/event.ts delete mode 100644 src/mol-app/service/dispatcher.ts delete mode 100644 src/mol-app/service/job.ts delete mode 100644 src/mol-app/service/logger.ts delete mode 100644 src/mol-app/skin/base.scss delete mode 100644 src/mol-app/skin/bootstrap.scss delete mode 100644 src/mol-app/skin/bootstrap/badges.scss delete mode 100644 src/mol-app/skin/bootstrap/button-groups.scss delete mode 100644 src/mol-app/skin/bootstrap/buttons.scss delete mode 100644 src/mol-app/skin/bootstrap/forms.scss delete mode 100644 src/mol-app/skin/bootstrap/input-groups.scss delete mode 100644 src/mol-app/skin/bootstrap/labels.scss delete mode 100644 src/mol-app/skin/bootstrap/mixins.scss delete mode 100644 src/mol-app/skin/bootstrap/mixins/background-variant.scss delete mode 100644 src/mol-app/skin/bootstrap/mixins/border-radius.scss delete mode 100644 src/mol-app/skin/bootstrap/mixins/buttons.scss delete mode 100644 src/mol-app/skin/bootstrap/mixins/clearfix.scss delete mode 100644 src/mol-app/skin/bootstrap/mixins/forms.scss delete mode 100644 src/mol-app/skin/bootstrap/mixins/grid.scss delete mode 100644 src/mol-app/skin/bootstrap/mixins/image.scss delete mode 100644 src/mol-app/skin/bootstrap/mixins/labels.scss delete mode 100644 src/mol-app/skin/bootstrap/mixins/opacity.scss delete mode 100644 src/mol-app/skin/bootstrap/mixins/tab-focus.scss delete mode 100644 src/mol-app/skin/bootstrap/mixins/text-emphasis.scss delete mode 100644 src/mol-app/skin/bootstrap/mixins/text-overflow.scss delete mode 100644 src/mol-app/skin/bootstrap/mixins/vendor-prefixes.scss delete mode 100644 src/mol-app/skin/bootstrap/normalize.scss delete mode 100644 src/mol-app/skin/bootstrap/scaffolding.scss delete mode 100644 src/mol-app/skin/bootstrap/type.scss delete mode 100644 src/mol-app/skin/bootstrap/variables.scss delete mode 100644 src/mol-app/skin/colors/blue.scss delete mode 100644 src/mol-app/skin/colors/dark.scss delete mode 100644 src/mol-app/skin/colors/light.scss delete mode 100644 src/mol-app/skin/components/controls-base.scss delete mode 100644 src/mol-app/skin/components/controls.scss delete mode 100644 src/mol-app/skin/components/entity.scss delete mode 100644 src/mol-app/skin/components/help.scss delete mode 100644 src/mol-app/skin/components/jobs.scss delete mode 100644 src/mol-app/skin/components/log.scss delete mode 100644 src/mol-app/skin/components/misc.scss delete mode 100644 src/mol-app/skin/components/panel.scss delete mode 100644 src/mol-app/skin/components/sequence-view.scss delete mode 100644 src/mol-app/skin/components/slider.scss delete mode 100644 src/mol-app/skin/components/viewport.scss delete mode 100644 src/mol-app/skin/fonts/fontello.eot delete mode 100644 src/mol-app/skin/fonts/fontello.svg delete mode 100644 src/mol-app/skin/fonts/fontello.ttf delete mode 100644 src/mol-app/skin/fonts/fontello.woff delete mode 100644 src/mol-app/skin/fonts/fontello.woff2 delete mode 100644 src/mol-app/skin/icons.scss delete mode 100644 src/mol-app/skin/layout.scss delete mode 100644 src/mol-app/skin/layout/common.scss delete mode 100644 src/mol-app/skin/layout/landscape.scss delete mode 100644 src/mol-app/skin/layout/outside.scss delete mode 100644 src/mol-app/skin/layout/portrait.scss delete mode 100644 src/mol-app/skin/logo.scss delete mode 100644 src/mol-app/skin/molstar-blue.scss delete mode 100644 src/mol-app/skin/molstar-dark.scss delete mode 100644 src/mol-app/skin/molstar-light.scss delete mode 100644 src/mol-app/skin/ui.scss delete mode 100644 src/mol-app/skin/variables.scss delete mode 100644 src/mol-app/ui/controls/common.tsx delete mode 100644 src/mol-app/ui/controls/slider.tsx delete mode 100644 src/mol-app/ui/entity/tree.tsx delete mode 100644 src/mol-app/ui/layout.tsx delete mode 100644 src/mol-app/ui/misc/jobs.tsx delete mode 100644 src/mol-app/ui/misc/log.tsx delete mode 100644 src/mol-app/ui/transform/backbone.tsx delete mode 100644 src/mol-app/ui/transform/ball-and-stick.tsx delete mode 100644 src/mol-app/ui/transform/carbohydrate.tsx delete mode 100644 src/mol-app/ui/transform/cartoon.tsx delete mode 100644 src/mol-app/ui/transform/distance-restraint.tsx delete mode 100644 src/mol-app/ui/transform/file-loader.tsx delete mode 100644 src/mol-app/ui/transform/list.tsx delete mode 100644 src/mol-app/ui/transform/model.tsx delete mode 100644 src/mol-app/ui/transform/spacefill.tsx delete mode 100644 src/mol-app/ui/transform/structure.tsx delete mode 100644 src/mol-app/ui/transform/url-loader.tsx delete mode 100644 src/mol-app/ui/view.tsx delete mode 100644 src/mol-app/ui/visualization/image-canvas.tsx delete mode 100644 src/mol-app/ui/visualization/sequence-view.tsx delete mode 100644 src/mol-app/ui/visualization/viewport.tsx create mode 100644 src/mol-geo/representation/structure/representation/molecular-surface.ts delete mode 100644 src/mol-geo/representation/structure/representation/surface.ts rename src/mol-geo/representation/volume/{surface.ts => isosurface.ts} (78%) delete mode 100644 src/mol-view/stage.ts diff --git a/package-lock.json b/package-lock.json index 07548755dbc1dbc907eab692407d9650201d6438..a279c20acfa88ae56eeadb4587db80fba0f194f4 100644 GIT binary patch delta 290 zcmaFRFZsD&a>EthNuCm$U;9S5PF^rec5=cr_Q`dVIX34_zQzO+*qktJvN4b$F?rnu zuFWE+n%O{{&Fd~qg)pM7ABvpxT4KAgA>$fG5aXK><5rXD1|EzO?T6ACftYFgp>$@q z>gfrQOcLAWA2aVafiS;{vn&dnzJQxia{KyBmai7m1*R}dOn=bE%rf1egPDK(xkW5* zG^RTgGD>W}c$#H%z;uN)7Rl}V)Y;mYr}vAqvQNJ&!OAiH>3k-M=>hDFM%$|uSUn5T z4S6?_b;ne=&Mj<kW;^rr(5uWs)7LmK@=fPUVm8^HYQ$E~Kk<P0^!aO9__jH*Jzxa@ DW8HHQ delta 294 zcmey|FZrNfa>Eth>8}G=#Wuh8jdTUGCQn$-Ha$&%nSXP^<ZDcDq0PzDCL2SP@}BBr zgNXB<;@-UXLN|oBIsW?LNU-?yzebE4+Z_!VmokF++kyOTCSYd!sdPplX4-x#o!Pw_ zqB`ywGuw8($IQDRO1A$OXIUHw(YP~{<+}xlIdQtd4HmKO5sO*!H6T*=PP1$YfEe)4 znn`K<dv&%n=IH{GtnAw@6j)~$LUm~}iEgi&#A-7YRp}Ino2SFHrZaC>Hex%?4^g#d NEeqdvVJEf+tN`DOchUd= diff --git a/src/apps/canvas/app.ts b/src/apps/canvas/app.ts index ea0c7f2ff..4da00cdbf 100644 --- a/src/apps/canvas/app.ts +++ b/src/apps/canvas/app.ts @@ -11,8 +11,7 @@ import { BehaviorSubject } from 'rxjs'; import { CifBlock } from 'mol-io/reader/cif'; import { volumeFromCcp4 } from 'mol-model/volume/formats/ccp4'; import { VolumeRepresentation } from 'mol-geo/representation/volume'; -import SurfaceVisual from 'mol-geo/representation/volume/surface'; -import { VolumeIsoValue } from 'mol-model/volume'; +import IsosurfaceVisual from 'mol-geo/representation/volume/isosurface'; export class App { viewer: Viewer @@ -88,9 +87,9 @@ export class App { const ccp4 = await getCcp4FromUrl(url) console.log(ccp4) const volume = await volumeFromCcp4(ccp4).run() - const volRepr = VolumeRepresentation(SurfaceVisual) + const volRepr = VolumeRepresentation(IsosurfaceVisual) await volRepr.createOrUpdate({ - isoValue: VolumeIsoValue.relative(volume.dataStats, 1) + isoValue: 1 }, volume).run() this.viewer.add(volRepr) console.log('volRepr', volRepr) diff --git a/src/apps/canvas/component/representation.tsx b/src/apps/canvas/component/representation.tsx new file mode 100644 index 000000000..537003659 --- /dev/null +++ b/src/apps/canvas/component/representation.tsx @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import * as React from 'react' +import Viewer from 'mol-view/viewer'; +import { App } from '../app'; +import { Params } from 'mol-view/parameter'; +import { Representation } from 'mol-geo/representation'; +import { ParametersComponent } from 'mol-app/component/parameters'; + +export interface RepresentationComponentProps { + app: App + viewer: Viewer + repr: Representation<Params> +} + +export interface RepresentationComponentState { + +} + +export class RepresentationComponent extends React.Component<RepresentationComponentProps, RepresentationComponentState> { + + async onChange(k: string, v: any) { + await this.props.app.runTask(this.props.repr.createOrUpdate({ [k]: v }).run(), 'Representation Update') + this.props.viewer.add(this.props.repr) + this.props.viewer.requestDraw(true) + } + + render() { + const { repr } = this.props + // const ct = ColorTheme(colorTheme) + + return <div> + <div> + <h4>{repr.label}</h4> + </div> + <div> + <ParametersComponent + params={repr.params} + values={repr.props} + onChange={(k, v) => this.onChange(k as string, v)} + /> + </div> + </div>; + } +} \ No newline at end of file diff --git a/src/apps/canvas/component/structure-representation.tsx b/src/apps/canvas/component/structure-representation.tsx deleted file mode 100644 index 84ea120c1..000000000 --- a/src/apps/canvas/component/structure-representation.tsx +++ /dev/null @@ -1,261 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' -import { StructureRepresentation, StructureProps } from 'mol-geo/representation/structure'; -import Viewer from 'mol-view/viewer'; -import { ColorThemeProps, ColorThemeName, ColorThemeNames, ColorTheme } from 'mol-view/theme/color'; -import { Color } from 'mol-util/color'; -import { Progress } from 'mol-task'; -import { VisualQuality, VisualQualityNames } from 'mol-geo/geometry/geometry'; -import { SizeThemeProps } from 'mol-view/theme/size'; -import { App } from '../app'; - -export interface StructureRepresentationComponentProps { - app: App - viewer: Viewer - representation: StructureRepresentation<StructureProps> -} - -export interface StructureRepresentationComponentState { - label: string - visible: boolean - alpha: number - quality: VisualQuality - colorTheme: ColorThemeProps - sizeTheme: SizeThemeProps - depthMask: boolean - - flatShaded?: boolean - resolutionFactor?: number - radiusOffset?: number - smoothness?: number - pointSizeAttenuation?: boolean - pointFilledCircle?: boolean - pointEdgeBleach?: number - - visuals?: { [k: string]: boolean } -} - -export class StructureRepresentationComponent extends React.Component<StructureRepresentationComponentProps, StructureRepresentationComponentState> { - state = this.stateFromRepresentation(this.props.representation) - - private stateFromRepresentation(repr: StructureRepresentation<StructureProps>) { - return { - label: repr.label, - visible: repr.props.visible, - alpha: repr.props.alpha, - quality: repr.props.quality, - colorTheme: repr.props.colorTheme, - sizeTheme: repr.props.sizeTheme, - depthMask: repr.props.depthMask, - - flatShaded: (repr.props as any).flatShaded, - resolutionFactor: (repr.props as any).resolutionFactor, - radiusOffset: (repr.props as any).radiusOffset, - smoothness: (repr.props as any).smoothness, - pointSizeAttenuation: (repr.props as any).pointSizeAttenuation, - pointFilledCircle: (repr.props as any).pointFilledCircle, - pointEdgeBleach: (repr.props as any).pointEdgeBleach, - - visuals: (repr.props as any).visuals, - } - } - - componentWillMount() { - this.setState(this.stateFromRepresentation(this.props.representation)) - } - - async update(state: Partial<StructureRepresentationComponentState>) { - const repr = this.props.representation - const props: Partial<StructureProps> = {} - - if (state.visible !== undefined) props.visible = state.visible - if (state.quality !== undefined) props.quality = state.quality - if (state.alpha !== undefined) props.alpha = state.alpha - if (state.colorTheme !== undefined) props.colorTheme = state.colorTheme - if (state.sizeTheme !== undefined) props.sizeTheme = state.sizeTheme - if (state.depthMask !== undefined) props.depthMask = state.depthMask - - if (state.flatShaded !== undefined) (props as any).flatShaded = state.flatShaded - if (state.resolutionFactor !== undefined) (props as any).resolutionFactor = state.resolutionFactor - if (state.radiusOffset !== undefined) (props as any).radiusOffset = state.radiusOffset - if (state.smoothness !== undefined) (props as any).smoothness = state.smoothness - if (state.pointSizeAttenuation !== undefined) (props as any).pointSizeAttenuation = state.pointSizeAttenuation - if (state.pointFilledCircle !== undefined) (props as any).pointFilledCircle = state.pointFilledCircle - if (state.pointEdgeBleach !== undefined) (props as any).pointEdgeBleach = state.pointEdgeBleach - - if (state.visuals !== undefined) (props as any).visuals = state.visuals - - await this.props.app.runTask(repr.createOrUpdate(props).run( - progress => console.log(Progress.format(progress)) - ), 'Create/update representation') - this.props.viewer.add(repr) - this.props.viewer.draw(true) - - this.setState(this.stateFromRepresentation(repr)) - } - - render() { - const { label, visible, quality, alpha, colorTheme, depthMask } = this.state - const ct = ColorTheme(colorTheme) - - return <div> - <div> - <h4>{label}</h4> - </div> - <div> - <div> - <span>Visible </span> - <button onClick={(e) => this.update({ visible: !visible }) }> - {visible ? 'Hide' : 'Show'} - </button> - </div> - { this.state.visuals !== undefined ? <div> - <span>Visuals: </span> - { Object.keys(this.state.visuals).map(k => { - return <span key={k}>{k} <input - type='checkbox' - checked={this.state.visuals[k]} - onChange={e => { - this.update({ visuals: { ...this.state.visuals, [k]: !!e.target.checked } }) - }} - ></input> </span> - }) } - </div> : '' } - <div> - <span>Depth Mask </span> - <button onClick={(e) => this.update({ depthMask: !depthMask }) }> - {depthMask ? 'Deactivate' : 'Activate'} - </button> - </div> - { this.state.flatShaded !== undefined ? <div> - <span>Flat Shaded </span> - <button onClick={(e) => this.update({ flatShaded: !this.state.flatShaded }) }> - {this.state.flatShaded ? 'Deactivate' : 'Activate'} - </button> - </div> : '' } - <div> - <span>Quality </span> - <select value={quality} onChange={e => this.update({ quality: e.target.value as VisualQuality }) }> - {VisualQualityNames.map(name => <option key={name} value={name}>{name}</option>)} - </select> - </div> - <div> - <span>Opacity </span> - <input type='range' - defaultValue={alpha.toString()} - min='0' - max='1' - step='0.05' - onInput={e => this.update({ alpha: parseFloat(e.currentTarget.value) })} - > - </input> - </div> - { this.state.resolutionFactor !== undefined ? <div> - <span>Resolution Factor </span> - <input type='range' - defaultValue={this.state.resolutionFactor.toString()} - min='4' - max='9' - step='1' - onChange={(e) => this.update({ resolutionFactor: parseInt(e.currentTarget.value) })} - > - </input> - </div> : '' } - { this.state.smoothness !== undefined ? <div> - <span>Smoothness </span> - <input type='range' - defaultValue={this.state.smoothness.toString()} - min='1' - max='3' - step='0.1' - onChange={e => this.update({ smoothness: parseFloat(e.currentTarget.value) })} - > - </input> - </div> : '' } - { this.state.radiusOffset !== undefined ? <div> - <span>Radius Offset </span> - <input type='range' - defaultValue={this.state.radiusOffset.toString()} - min='0' - max='4' - step='0.1' - onChange={e => this.update({ radiusOffset: parseFloat(e.currentTarget.value) })} - > - </input> - </div> : '' } - { this.state.pointSizeAttenuation !== undefined ? <div> - <span>Size Attenuation </span> - <button onClick={e => this.update({ pointSizeAttenuation: !this.state.pointSizeAttenuation }) }> - {this.state.pointSizeAttenuation ? 'Deactivate' : 'Activate'} - </button> - </div> : '' } - { this.state.pointFilledCircle !== undefined ? <div> - <span>Filled Circle </span> - <button onClick={e => this.update({ pointFilledCircle: !this.state.pointFilledCircle }) }> - {this.state.pointFilledCircle ? 'Deactivate' : 'Activate'} - </button> - </div> : '' } - { this.state.pointEdgeBleach !== undefined ? <div> - <span>Edge Bleach </span> - <input type='range' - defaultValue={this.state.pointEdgeBleach.toString()} - min='0' - max='1' - step='0.05' - onInput={e => this.update({ pointEdgeBleach: parseFloat(e.currentTarget.value) })} - > - </input> - </div> : '' } - { this.state.sizeTheme !== undefined && this.state.sizeTheme.name === 'uniform' ? <div> - <span>Uniform Size </span> - <input type='range' - defaultValue={this.state.sizeTheme.value!.toString()} - min='0' - max='10' - step='0.1' - onInput={e => this.update({ - sizeTheme: { name: 'uniform', value: parseFloat(e.currentTarget.value) } - })} - > - </input> - </div> : '' } - <div> - <span>Color Theme </span> - <select value={colorTheme.name} onChange={e => this.update({ colorTheme: { name: e.target.value as ColorThemeName } }) }> - {ColorThemeNames.map(name => <option key={name} value={name}>{name}</option>)} - </select> - {ct.description ? <div><i>{ct.description}</i></div> : ''} - { - ct.legend && ct.legend.kind === 'scale-legend' - ? <div - style={{ - width: '100%', - height: '30px', - background: `linear-gradient(to right, ${ct.legend.colors.map(c => Color.toStyle(c)).join(', ')})` - }} - > - <span style={{float: 'left', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.minLabel}</span> - <span style={{float: 'right', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.maxLabel}</span> - </div> - : ct.legend && ct.legend.kind === 'table-legend' - ? <div> - {ct.legend.table.map((value, i) => { - const [name, color] = value - return <div key={i} style={{minWidth: '60px', marginRight: '5px', display: 'inline-block'}}> - <div style={{width: '30px', height: '20px', backgroundColor: Color.toStyle(color), display: 'inline-block'}}></div> - {name} - </div> - })} - </div> - : '' - } - </div> - </div> - </div>; - } -} \ No newline at end of file diff --git a/src/apps/canvas/component/structure-view.tsx b/src/apps/canvas/component/structure-view.tsx index 82ca7a29d..91bbe6cc9 100644 --- a/src/apps/canvas/component/structure-view.tsx +++ b/src/apps/canvas/component/structure-view.tsx @@ -7,7 +7,8 @@ import * as React from 'react' import { StructureView } from '../structure-view'; import { StructureRepresentation } from 'mol-geo/representation/structure'; -import { StructureRepresentationComponent } from './structure-representation'; +import { RepresentationComponent } from './representation'; +import { Representation } from 'mol-geo/representation'; // export function FileInput (props: { // accept: string @@ -182,8 +183,8 @@ export class StructureViewComponent extends React.Component<StructureViewCompone { Object.keys(structureRepresentations).map((k, i) => { if (active[k]) { return <div key={i}> - <StructureRepresentationComponent - representation={structureRepresentations[k]} + <RepresentationComponent + repr={structureRepresentations[k] as Representation<any>} viewer={structureView.viewer} app={structureView.app} /> diff --git a/src/apps/canvas/component/volume-representation.tsx b/src/apps/canvas/component/volume-representation.tsx deleted file mode 100644 index b22b95667..000000000 --- a/src/apps/canvas/component/volume-representation.tsx +++ /dev/null @@ -1,245 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' -import Viewer from 'mol-view/viewer'; -import { ColorThemeProps, ColorThemeName, ColorThemeNames, ColorTheme } from 'mol-view/theme/color'; -import { Color } from 'mol-util/color'; -import { Progress } from 'mol-task'; -import { VisualQuality, VisualQualityNames } from 'mol-geo/geometry/geometry'; -import { SizeThemeProps } from 'mol-view/theme/size'; -import { App } from '../app'; -import { VolumeRepresentation, VolumeProps } from 'mol-geo/representation/volume'; - -export interface VolumeRepresentationComponentProps { - app: App - viewer: Viewer - representation: VolumeRepresentation<VolumeProps> -} - -export interface VolumeRepresentationComponentState { - label: string - visible: boolean - alpha: number - quality: VisualQuality - colorTheme: ColorThemeProps - depthMask: boolean - - flatShaded?: boolean - resolutionFactor?: number - radiusOffset?: number - smoothness?: number - pointSizeAttenuation?: boolean - pointFilledCircle?: boolean - pointEdgeBleach?: number - - visuals?: { [k: string]: boolean } -} - -export class VolumeRepresentationComponent extends React.Component<VolumeRepresentationComponentProps, VolumeRepresentationComponentState> { - state = this.stateFromRepresentation(this.props.representation) - - private stateFromRepresentation(repr: VolumeRepresentation<VolumeProps>) { - return { - label: repr.label, - visible: repr.props.visible, - alpha: repr.props.alpha, - quality: repr.props.quality, - colorTheme: repr.props.colorTheme, - depthMask: repr.props.depthMask, - - flatShaded: (repr.props as any).flatShaded, - resolutionFactor: (repr.props as any).resolutionFactor, - radiusOffset: (repr.props as any).radiusOffset, - smoothness: (repr.props as any).smoothness, - pointSizeAttenuation: (repr.props as any).pointSizeAttenuation, - pointFilledCircle: (repr.props as any).pointFilledCircle, - pointEdgeBleach: (repr.props as any).pointEdgeBleach, - - visuals: (repr.props as any).visuals, - } - } - - componentWillMount() { - this.setState(this.stateFromRepresentation(this.props.representation)) - } - - async update(state: Partial<VolumeRepresentationComponentState>) { - const repr = this.props.representation - const props: Partial<VolumeProps> = {} - - if (state.visible !== undefined) props.visible = state.visible - if (state.quality !== undefined) props.quality = state.quality - if (state.alpha !== undefined) props.alpha = state.alpha - if (state.colorTheme !== undefined) props.colorTheme = state.colorTheme - if (state.depthMask !== undefined) props.depthMask = state.depthMask - - if (state.flatShaded !== undefined) (props as any).flatShaded = state.flatShaded - if (state.resolutionFactor !== undefined) (props as any).resolutionFactor = state.resolutionFactor - if (state.radiusOffset !== undefined) (props as any).radiusOffset = state.radiusOffset - if (state.smoothness !== undefined) (props as any).smoothness = state.smoothness - if (state.pointSizeAttenuation !== undefined) (props as any).pointSizeAttenuation = state.pointSizeAttenuation - if (state.pointFilledCircle !== undefined) (props as any).pointFilledCircle = state.pointFilledCircle - if (state.pointEdgeBleach !== undefined) (props as any).pointEdgeBleach = state.pointEdgeBleach - - if (state.visuals !== undefined) (props as any).visuals = state.visuals - - await this.props.app.runTask(repr.createOrUpdate(props).run( - progress => console.log(Progress.format(progress)) - ), 'Create/update representation') - this.props.viewer.add(repr) - this.props.viewer.draw(true) - - this.setState(this.stateFromRepresentation(repr)) - } - - render() { - const { label, visible, quality, alpha, colorTheme, depthMask } = this.state - const ct = ColorTheme(colorTheme) - - return <div> - <div> - <h4>{label}</h4> - </div> - <div> - <div> - <span>Visible </span> - <button onClick={(e) => this.update({ visible: !visible }) }> - {visible ? 'Hide' : 'Show'} - </button> - </div> - { this.state.visuals !== undefined ? <div> - <span>Visuals: </span> - { Object.keys(this.state.visuals).map(k => { - return <span key={k}>{k} <input - type='checkbox' - checked={this.state.visuals[k]} - onChange={e => { - this.update({ visuals: { ...this.state.visuals, [k]: !!e.target.checked } }) - }} - ></input> </span> - }) } - </div> : '' } - <div> - <span>Depth Mask </span> - <button onClick={(e) => this.update({ depthMask: !depthMask }) }> - {depthMask ? 'Deactivate' : 'Activate'} - </button> - </div> - { this.state.flatShaded !== undefined ? <div> - <span>Flat Shaded </span> - <button onClick={(e) => this.update({ flatShaded: !this.state.flatShaded }) }> - {this.state.flatShaded ? 'Deactivate' : 'Activate'} - </button> - </div> : '' } - <div> - <span>Quality </span> - <select value={quality} onChange={e => this.update({ quality: e.target.value as VisualQuality }) }> - {VisualQualityNames.map(name => <option key={name} value={name}>{name}</option>)} - </select> - </div> - <div> - <span>Opacity </span> - <input type='range' - defaultValue={alpha.toString()} - min='0' - max='1' - step='0.05' - onInput={e => this.update({ alpha: parseFloat(e.currentTarget.value) })} - > - </input> - </div> - { this.state.resolutionFactor !== undefined ? <div> - <span>Resolution Factor </span> - <input type='range' - defaultValue={this.state.resolutionFactor.toString()} - min='4' - max='9' - step='1' - onChange={(e) => this.update({ resolutionFactor: parseInt(e.currentTarget.value) })} - > - </input> - </div> : '' } - { this.state.smoothness !== undefined ? <div> - <span>Smoothness </span> - <input type='range' - defaultValue={this.state.smoothness.toString()} - min='1' - max='3' - step='0.1' - onChange={e => this.update({ smoothness: parseFloat(e.currentTarget.value) })} - > - </input> - </div> : '' } - { this.state.radiusOffset !== undefined ? <div> - <span>Radius Offset </span> - <input type='range' - defaultValue={this.state.radiusOffset.toString()} - min='0' - max='4' - step='0.1' - onChange={e => this.update({ radiusOffset: parseFloat(e.currentTarget.value) })} - > - </input> - </div> : '' } - { this.state.pointSizeAttenuation !== undefined ? <div> - <span>Size Attenuation </span> - <button onClick={e => this.update({ pointSizeAttenuation: !this.state.pointSizeAttenuation }) }> - {this.state.pointSizeAttenuation ? 'Deactivate' : 'Activate'} - </button> - </div> : '' } - { this.state.pointFilledCircle !== undefined ? <div> - <span>Filled Circle </span> - <button onClick={e => this.update({ pointFilledCircle: !this.state.pointFilledCircle }) }> - {this.state.pointFilledCircle ? 'Deactivate' : 'Activate'} - </button> - </div> : '' } - { this.state.pointEdgeBleach !== undefined ? <div> - <span>Edge Bleach </span> - <input type='range' - defaultValue={this.state.pointEdgeBleach.toString()} - min='0' - max='1' - step='0.05' - onInput={e => this.update({ pointEdgeBleach: parseFloat(e.currentTarget.value) })} - > - </input> - </div> : '' } - <div> - <span>Color Theme </span> - <select value={colorTheme.name} onChange={e => this.update({ colorTheme: { name: e.target.value as ColorThemeName } }) }> - {ColorThemeNames.map(name => <option key={name} value={name}>{name}</option>)} - </select> - {ct.description ? <div><i>{ct.description}</i></div> : ''} - { - ct.legend && ct.legend.kind === 'scale-legend' - ? <div - style={{ - width: '100%', - height: '30px', - background: `linear-gradient(to right, ${ct.legend.colors.map(c => Color.toStyle(c)).join(', ')})` - }} - > - <span style={{float: 'left', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.minLabel}</span> - <span style={{float: 'right', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.maxLabel}</span> - </div> - : ct.legend && ct.legend.kind === 'table-legend' - ? <div> - {ct.legend.table.map((value, i) => { - const [name, color] = value - return <div key={i} style={{minWidth: '60px', marginRight: '5px', display: 'inline-block'}}> - <div style={{width: '30px', height: '20px', backgroundColor: Color.toStyle(color), display: 'inline-block'}}></div> - {name} - </div> - })} - </div> - : '' - } - </div> - </div> - </div>; - } -} \ No newline at end of file diff --git a/src/apps/canvas/index.ts b/src/apps/canvas/index.ts index 76e32dee2..b02f1c33f 100644 --- a/src/apps/canvas/index.ts +++ b/src/apps/canvas/index.ts @@ -23,4 +23,4 @@ const assemblyId = urlQueryParameter('assembly') const pdbId = urlQueryParameter('pdb') if (pdbId) app.loadPdbIdOrMmcifUrl(pdbId, { assemblyId }) -app.loadCcp4File() \ No newline at end of file +// app.loadCcp4File() \ No newline at end of file diff --git a/src/apps/canvas/structure-view.ts b/src/apps/canvas/structure-view.ts index 1f0d7149f..422487447 100644 --- a/src/apps/canvas/structure-view.ts +++ b/src/apps/canvas/structure-view.ts @@ -24,7 +24,7 @@ import { StructureRepresentation } from 'mol-geo/representation/structure'; import { BehaviorSubject } from 'rxjs'; import { SpacefillRepresentation } from 'mol-geo/representation/structure/representation/spacefill'; import { DistanceRestraintRepresentation } from 'mol-geo/representation/structure/representation/distance-restraint'; -import { SurfaceRepresentation } from 'mol-geo/representation/structure/representation/surface'; +import { MolecularSurfaceRepresentation } from 'mol-geo/representation/structure/representation/molecular-surface'; import { App } from './app'; import { Progress } from 'mol-task'; @@ -81,7 +81,7 @@ export async function StructureView(app: App, viewer: Viewer, models: ReadonlyAr const structureRepresentations: { [k: string]: StructureRepresentation<any> } = { cartoon: CartoonRepresentation(), - surface: SurfaceRepresentation(), + surface: MolecularSurfaceRepresentation(), point: PointRepresentation(), ballAndStick: BallAndStickRepresentation(), carbohydrate: CarbohydrateRepresentation(), diff --git a/src/apps/structure-info/volume.ts b/src/apps/structure-info/volume.ts index 00dd39171..2f5e6441f 100644 --- a/src/apps/structure-info/volume.ts +++ b/src/apps/structure-info/volume.ts @@ -13,7 +13,7 @@ import { downloadCif } from './helpers' import CIF from 'mol-io/reader/cif' import { DensityServer_Data_Database } from 'mol-io/reader/cif/schema/density-server'; import { Table } from 'mol-data/db'; -import { computeVolumeSurface } from 'mol-geo/representation/volume/surface'; +import { computeVolumeSurface } from 'mol-geo/representation/volume/isosurface'; import { StringBuilder } from 'mol-util'; require('util.promisify').shim(); diff --git a/src/apps/viewer/index.html b/src/apps/viewer/index.html deleted file mode 100644 index b2c505caf..000000000 --- a/src/apps/viewer/index.html +++ /dev/null @@ -1,13 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="utf-8" /> - <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> - <title>Mol* Viewer</title> - <link href='./app.css', rel="stylesheet"> - </head> - <body> - <div id="app"></div> - <script type="text/javascript" src="./index.js"></script> - </body> -</html> \ No newline at end of file diff --git a/src/apps/viewer/index.tsx b/src/apps/viewer/index.tsx deleted file mode 100644 index 26be8a64a..000000000 --- a/src/apps/viewer/index.tsx +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' -import * as ReactDOM from 'react-dom' - -import './index.html' -import 'mol-app/skin/molstar-light.scss' - -import { Context } from 'mol-app/context/context'; -import { Viewport } from 'mol-app/ui/visualization/viewport' -import { makeEmptyTargets, LayoutRegion } from 'mol-app/controller/layout'; -import { Layout } from 'mol-app/ui/layout'; -import { LogController } from 'mol-app/controller/misc/log'; -import { Log } from 'mol-app/ui/misc/log'; -import { JobsController } from 'mol-app/controller/misc/jobs'; -import { BackgroundJobs, Overlay } from 'mol-app/ui/misc/jobs'; -import { EntityTree } from 'mol-app/ui/entity/tree'; -import { EntityTreeController } from 'mol-app/controller/entity/tree'; -import { TransformListController } from 'mol-app/controller/transform/list'; -import { TransformList } from 'mol-app/ui/transform/list'; -import { SequenceView } from 'mol-app/ui/visualization/sequence-view'; -import { InteractivityEvents } from 'mol-app/event/basic'; -import { MarkerAction } from 'mol-geo/geometry/marker-data'; -import { EveryLoci } from 'mol-model/loci'; - -const elm = document.getElementById('app') -if (!elm) throw new Error('Can not find element with id "app".') - -const ctx = new Context() -const targets = makeEmptyTargets(); - -targets[LayoutRegion.Main].components.push({ - key: 'molstar-internal-viewport', - controller: ctx.viewport, - region: LayoutRegion.Main, - view: Viewport, - isStatic: true -}); - -targets[LayoutRegion.Bottom].components.push({ - key: 'molstar-log', - controller: new LogController(ctx), - region: LayoutRegion.Bottom, - view: Log, - isStatic: true -}); - -targets[LayoutRegion.Top].components.push({ - key: 'molstar-sequence-view', - controller: ctx.components.sequenceView, - region: LayoutRegion.Top, - view: SequenceView, - isStatic: true -}); - -targets[LayoutRegion.Main].components.push({ - key: 'molstar-background-jobs', - controller: new JobsController(ctx, 'Background'), - region: LayoutRegion.Main, - view: BackgroundJobs, - isStatic: true -}); - -targets[LayoutRegion.Root].components.push({ - key: 'molstar-overlay', - controller: new JobsController(ctx, 'Normal'), - region: LayoutRegion.Root, - view: Overlay, - isStatic: true -}); - -targets[LayoutRegion.Right].components.push({ - key: 'molstar-transform-list', - controller: new TransformListController(ctx), - region: LayoutRegion.Right, - view: TransformList, - isStatic: false -}); - -targets[LayoutRegion.Left].components.push({ - key: 'molstar-entity-tree', - controller: new EntityTreeController(ctx), - region: LayoutRegion.Left, - view: EntityTree, - isStatic: true -}); - -ctx.createLayout(targets, elm) -ctx.layout.setState({ - isExpanded: true, - hideControls: false, - collapsedControlsLayout: 0 -}) -// ctx.viewport.setState() - -ctx.dispatcher.getStream(InteractivityEvents.HighlightLoci).subscribe(event => { - ctx.stage.viewer.mark(EveryLoci, MarkerAction.RemoveHighlight) - if (event && event.data) { - ctx.stage.viewer.mark(event.data, MarkerAction.Highlight) - } -}) - -ReactDOM.render(React.createElement(Layout, { controller: ctx.layout }), elm); diff --git a/src/mol-app/component/color-theme.tsx b/src/mol-app/component/color-theme.tsx new file mode 100644 index 000000000..1df6a6b38 --- /dev/null +++ b/src/mol-app/component/color-theme.tsx @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import * as React from 'react' +import { ColorTheme } from 'mol-view/theme/color'; +import { Color } from 'mol-util/color'; + +export interface ColorThemeComponentProps { + colorTheme: ColorTheme +} + +export interface ColorThemeComponentState { + +} + +export class ColorThemeComponent extends React.Component<ColorThemeComponentProps, ColorThemeComponentState> { + state = { + + } + + render() { + const ct = this.props.colorTheme + return <div> + <span>Color Theme </span> + + {ct.description ? <div><i>{ct.description}</i></div> : ''} + { + ct.legend && ct.legend.kind === 'scale-legend' + ? <div + style={{ + width: '100%', + height: '30px', + background: `linear-gradient(to right, ${ct.legend.colors.map(c => Color.toStyle(c)).join(', ')})` + }} + > + <span style={{float: 'left', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.minLabel}</span> + <span style={{float: 'right', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.maxLabel}</span> + </div> + : ct.legend && ct.legend.kind === 'table-legend' + ? <div> + {ct.legend.table.map((value, i) => { + const [name, color] = value + return <div key={i} style={{minWidth: '60px', marginRight: '5px', display: 'inline-block'}}> + <div style={{width: '30px', height: '20px', backgroundColor: Color.toStyle(color), display: 'inline-block'}}></div> + {name} + </div> + })} + </div> + : '' + } + </div>; + } +} \ No newline at end of file diff --git a/src/mol-app/component/parameter/boolean.tsx b/src/mol-app/component/parameter/boolean.tsx new file mode 100644 index 000000000..997b2ca5c --- /dev/null +++ b/src/mol-app/component/parameter/boolean.tsx @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import * as React from 'react' +import { BooleanParam } from 'mol-view/parameter'; + +export interface BooleanParamComponentProps { + param: BooleanParam + value: boolean + onChange(v: boolean): void +} + +export interface BooleanParamComponentState { + value: boolean +} + +export class BooleanParamComponent extends React.Component<BooleanParamComponentProps, BooleanParamComponentState> { + state = { + value: this.props.value + } + + onChange(value: boolean) { + this.setState({ value }) + this.props.onChange(value) + } + + render() { + return <div> + <span>{this.props.param.label} </span> + <button onClick={e => this.onChange(!this.state.value) }> + {this.state.value ? 'Off' : 'On'} + </button> + </div>; + } +} \ No newline at end of file diff --git a/src/mol-app/component/parameter/number.tsx b/src/mol-app/component/parameter/number.tsx new file mode 100644 index 000000000..be97aee26 --- /dev/null +++ b/src/mol-app/component/parameter/number.tsx @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import * as React from 'react' +import { NumberParam } from 'mol-view/parameter'; + +export interface NumberParamComponentProps { + param: NumberParam + value: number + onChange(v: number): void +} + +export interface NumberParamComponentState { + value: number +} + +export class NumberParamComponent extends React.Component<NumberParamComponentProps, NumberParamComponentState> { + state = { + value: this.props.value + } + + onChange(valueStr: string) { + const value = Number.isInteger(this.props.param.step) ? parseInt(valueStr) : parseFloat(valueStr) + this.setState({ value }) + this.props.onChange(value) + } + + render() { + return <div> + <span>{this.props.param.label} </span> + <input type='range' + value={this.state.value} + min={this.props.param.min} + max={this.props.param.max} + step={this.props.param.step} + onChange={e => this.onChange(e.currentTarget.value)} + > + </input> + </div>; + } +} \ No newline at end of file diff --git a/src/mol-app/component/parameter/range.tsx b/src/mol-app/component/parameter/range.tsx new file mode 100644 index 000000000..dd9c82fa2 --- /dev/null +++ b/src/mol-app/component/parameter/range.tsx @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import * as React from 'react' +import { RangeParam } from 'mol-view/parameter'; + +export interface RangeParamComponentProps { + param: RangeParam + value: number + onChange(v: number): void +} + +export interface RangeParamComponentState { + value: number +} + +export class RangeParamComponent extends React.Component<RangeParamComponentProps, RangeParamComponentState> { + state = { + value: this.props.value + } + + onChange(valueStr: string) { + const value = Number.isInteger(this.props.param.step) ? parseInt(valueStr) : parseFloat(valueStr) + this.setState({ value }) + this.props.onChange(value) + } + + render() { + return <div> + <span>{this.props.param.label} </span> + <input type='range' + value={this.state.value} + min={this.props.param.min} + max={this.props.param.max} + step={this.props.param.step} + onChange={e => this.onChange(e.currentTarget.value)} + > + </input> + </div>; + } +} \ No newline at end of file diff --git a/src/mol-app/component/parameter/select.tsx b/src/mol-app/component/parameter/select.tsx new file mode 100644 index 000000000..e403bbb79 --- /dev/null +++ b/src/mol-app/component/parameter/select.tsx @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import * as React from 'react' +import { SelectParam } from 'mol-view/parameter'; + +export interface SelectParamComponentProps<T extends string> { + param: SelectParam<T> + value: T + onChange(v: T): void +} + +export interface SelectParamComponentState<T extends string> { + value: T +} + +export class SelectParamComponent<T extends string> extends React.Component<SelectParamComponentProps<T>, SelectParamComponentState<T>> { + state = { + value: this.props.value + } + + onChange(value: T) { + this.setState({ value }) + this.props.onChange(value) + } + + render() { + return <div> + <span>{this.props.param.label} </span> + <select value={this.state.value} onChange={e => this.onChange(e.target.value as T) }> + {this.props.param.options.map(v => { + const [value, label] = v + return <option key={label} value={value}>{label}</option> + })} + </select> + </div>; + } +} \ No newline at end of file diff --git a/src/mol-app/component/parameters.tsx b/src/mol-app/component/parameters.tsx new file mode 100644 index 000000000..d5446e33c --- /dev/null +++ b/src/mol-app/component/parameters.tsx @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import * as React from 'react' +import { Param, Params } from 'mol-view/parameter'; +import { BooleanParamComponent } from './parameter/boolean'; +import { NumberParamComponent } from './parameter/number'; +import { RangeParamComponent } from './parameter/range'; +import { SelectParamComponent } from './parameter/select'; + +interface ParametersProps<P extends Params> { + params: P + values: { [k in keyof P]: P[k]['defaultValue'] } + onChange<K extends keyof P>(k: K, v: P[K]['defaultValue']): void +} + +type ParametersState = {} + +function getParamComponent<P extends Param>(p: Param, value: P['defaultValue'], onChange: (v: P['defaultValue']) => void) { + switch (p.type) { + case 'boolean': + return <BooleanParamComponent param={p} value={value} onChange={onChange} /> + case 'number': + return <NumberParamComponent param={p} value={value} onChange={onChange} /> + case 'range': + return <RangeParamComponent param={p} value={value} onChange={onChange} /> + case 'select': + return <SelectParamComponent param={p} value={value} onChange={onChange} /> + } + return '' +} + +export class ParametersComponent<P extends Params> extends React.Component<ParametersProps<P>, ParametersState> { + onChange(k: string, value: any) { + this.props.onChange(k, value) + } + + render() { + return <div> + { Object.keys(this.props.params).map(k => { + const param = this.props.params[k] + const value = this.props.values[k] + return <div key={k}> + {getParamComponent(param, value, v => this.onChange(k, v))} + </div> + })} + </div>; + } +} \ No newline at end of file diff --git a/src/mol-app/component/sequence.tsx b/src/mol-app/component/sequence.tsx new file mode 100644 index 000000000..f90039eb3 --- /dev/null +++ b/src/mol-app/component/sequence.tsx @@ -0,0 +1,81 @@ +// /** +// * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. +// * +// * @author David Sehnal <david.sehnal@gmail.com> +// */ + +// import * as React from 'react' +// import { Structure, StructureSequence, Queries, StructureSelection, StructureProperties, StructureQuery } from 'mol-model/structure'; +// import { EmptyLoci } from 'mol-model/loci'; + +// export class SequenceView extends View<SequenceViewController, {}, {}> { +// render() { +// const s = this.controller.latestState.structure; +// if (!s) return <div className='molstar-sequence-view-wrap'>No structure available.</div>; + +// const seqs = s.models[0].sequence.sequences; +// return <div className='molstar-sequence-view-wrap'> +// {seqs.map((seq, i) => <EntitySequence key={i} ctx={this.controller.context} seq={seq} structure={s} /> )} +// </div>; +// } +// } + +// function createQuery(entityId: string, label_seq_id: number) { +// return Queries.generators.atoms({ +// entityTest: ctx => StructureProperties.entity.id(ctx.element) === entityId, +// residueTest: ctx => StructureProperties.residue.label_seq_id(ctx.element) === label_seq_id +// }); +// } + +// // TODO: this is really ineffective and should be done using a canvas. +// class EntitySequence extends React.Component<{ ctx: Context, seq: StructureSequence.Entity, structure: Structure }> { + +// raiseInteractityEvent(seqId?: number) { +// if (typeof seqId === 'undefined') { +// InteractivityEvents.HighlightLoci.dispatch(this.props.ctx, EmptyLoci); +// return; +// } + +// const query = createQuery(this.props.seq.entityId, seqId); +// const loci = StructureSelection.toLoci(StructureQuery.run(query, this.props.structure)); +// if (loci.elements.length === 0) InteractivityEvents.HighlightLoci.dispatch(this.props.ctx, EmptyLoci); +// else InteractivityEvents.HighlightLoci.dispatch(this.props.ctx, loci); +// } + + +// render() { +// const { ctx, seq } = this.props; +// const { offset, sequence } = seq.sequence; + +// const elems: JSX.Element[] = []; +// for (let i = 0, _i = sequence.length; i < _i; i++) { +// elems[elems.length] = <ResidueView ctx={ctx} seqId={offset + i} letter={sequence[i]} parent={this} key={i} />; +// } + +// return <div style={{ wordWrap: 'break-word' }}> +// <span style={{ fontWeight: 'bold' }}>{this.props.seq.entityId}:{offset} </span> +// {elems} +// </div>; +// } +// } + +// class ResidueView extends React.Component<{ ctx: Context, seqId: number, letter: string, parent: EntitySequence }, { isHighlighted: boolean }> { +// state = { isHighlighted: false } + +// mouseEnter = () => { +// this.setState({ isHighlighted: true }); +// this.props.parent.raiseInteractityEvent(this.props.seqId); +// } + +// mouseLeave = () => { +// this.setState({ isHighlighted: false }); +// this.props.parent.raiseInteractityEvent(); +// } + +// render() { +// return <span onMouseEnter={this.mouseEnter} onMouseLeave={this.mouseLeave} +// style={{ cursor: 'pointer', backgroundColor: this.state.isHighlighted ? 'yellow' : void 0 }}> +// {this.props.letter} +// </span>; +// } +// } \ No newline at end of file diff --git a/src/mol-app/context/context.ts b/src/mol-app/context/context.ts deleted file mode 100644 index 5c8fee555..000000000 --- a/src/mol-app/context/context.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import { UUID } from 'mol-util' -import { PerformanceMonitor } from 'mol-util/performance-monitor'; -import { Dispatcher } from '../service/dispatcher' -import { Logger } from '../service/logger' -import { LayoutTarget, LayoutController } from '../controller/layout'; -import { ViewportController } from '../controller/visualization/viewport'; -import { Stage } from 'mol-view/stage'; -import { AnyTransform } from 'mol-view/state/transform'; -import { BehaviorSubject } from 'rxjs'; -import { AnyEntity } from 'mol-view/state/entity'; -import { SequenceViewController } from '../controller/visualization/sequence-view'; - -export class Settings { - private settings = new Map<string, any>(); - - set(key: string, value: any) { - this.settings.set(key, value); - } - - get(key: string) { - return this.settings.get(key); - } -} - -export class Context { - id = UUID.create() - - dispatcher = new Dispatcher(); - logger = new Logger(this); - performance = new PerformanceMonitor(); - - stage = new Stage(this); - viewport = new ViewportController(this); - layout: LayoutController; - settings = new Settings(); - - // TODO: this is a temporary solution - components = { - sequenceView: new SequenceViewController(this) - }; - - currentEntity = new BehaviorSubject(undefined) as BehaviorSubject<AnyEntity | undefined> - currentTransforms = new BehaviorSubject([] as AnyTransform[]) - - createLayout(targets: LayoutTarget[], target: HTMLElement) { - this.layout = new LayoutController(this, targets, target); - } - - initStage(canvas: HTMLCanvasElement, container: HTMLDivElement) { - this.stage.initRenderer(canvas, container) - return true - } - - destroy() { - if (this.stage) { - this.stage.dispose() - this.stage = null as any - } - } -} \ No newline at end of file diff --git a/src/mol-app/controller/controller.ts b/src/mol-app/controller/controller.ts deleted file mode 100644 index bfd6127f6..000000000 --- a/src/mol-app/controller/controller.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import { BehaviorSubject } from 'rxjs'; -import { merge } from 'mol-util'; -import { Context } from '../context/context' -import { LayoutRegion } from './layout'; - -export class Controller<State> { - - private _state = new BehaviorSubject<State>(<any>void 0); - private _latestState: State = <any>void 0; - - get dispatcher() { - return this.context.dispatcher; - } - - setState(...states: Partial<State>[]) { - let s = merge(this._latestState, ...states); - if (s !== this._latestState) { - this._latestState = s; - this._state.next(s); - } - } - - get state() { - return this._state; - } - - get latestState() { - return this._latestState; - } - - constructor(public context: Context, initialState: State) { - this._latestState = initialState; - } -} - -export interface ControllerInfo { - key: string; - controller: Controller<any>; - view: any; - region: LayoutRegion; - isStatic?: boolean; -} \ No newline at end of file diff --git a/src/mol-app/controller/entity/tree.ts b/src/mol-app/controller/entity/tree.ts deleted file mode 100644 index fdcb620e5..000000000 --- a/src/mol-app/controller/entity/tree.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import { Context } from '../../context/context' -import { Controller } from '../controller'; -import { AnyEntity } from 'mol-view/state/entity'; - -export interface EntityTreeState { - entities: Set<AnyEntity> -} - -export class EntityTreeController extends Controller<EntityTreeState> { - constructor(context: Context) { - super(context, { entities: new Set() }); - - context.stage.ctx.change.subscribe(() => { - if (context.stage.ctx) { - this.state.next({ entities: context.stage.ctx.entities }) // TODO - this.setState({ entities: context.stage.ctx.entities }) - } - }) - } -} \ No newline at end of file diff --git a/src/mol-app/controller/layout.ts b/src/mol-app/controller/layout.ts deleted file mode 100644 index 388f57788..000000000 --- a/src/mol-app/controller/layout.ts +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import { Context } from '../context/context' -import { Controller, ControllerInfo } from './controller' -import { CommonEvents, LayoutEvents } from '../event/basic'; - -export enum LayoutRegion { - Main = 0, - Top = 1, - Right = 2, - Bottom = 3, - Left = 4, - Root = 5 -} - -export enum CollapsedControlsLayout { - Outside = 0, - Landscape = 1, - Portrait = 2 -} - -export class LayoutTarget { - components: ControllerInfo[] = []; - constructor(public cssClass: string) { - } -} - -export function makeEmptyTargets() { - let ret: LayoutTarget[] = []; - for (let i = 0; i <= LayoutRegion.Root; i++) { - ret.push(new LayoutTarget(LayoutRegion[i].toLowerCase())); - } - return ret; -} - -export type RegionState = 'Hidden' | 'Sticky' | 'Default' - -export interface LayoutState { - isExpanded: boolean, - hideControls: boolean, - collapsedControlsLayout: CollapsedControlsLayout, - regionStates?: { [region: number]: RegionState } -} - -interface RootState { - top: string | null, - bottom: string | null, - left: string | null, - right: string | null, - - width: string | null; - height: string | null; - maxWidth: string | null; - maxHeight: string | null; - margin: string | null; - marginLeft: string | null; - marginRight: string | null; - marginTop: string | null; - marginBottom: string | null; - - scrollTop: number, - scrollLeft: number, - position: string | null, - overflow: string | null, - viewports: HTMLElement[], - zindex: string | null -} - -export class LayoutController extends Controller<LayoutState> { - - update(state: Partial<LayoutState>) { - let prevExpanded = !!this.latestState.isExpanded; - this.setState(state); - if (typeof state.isExpanded === 'boolean' && state.isExpanded !== prevExpanded) this.handleExpand(); - - this.dispatcher.schedule(() => CommonEvents.LayoutChanged.dispatch(this.context, {})); - } - - private rootState: RootState | undefined = void 0; - private expandedViewport: HTMLMetaElement; - - private getScrollElement() { - if ((document as any).scrollingElement) return (document as any).scrollingElement; - if (document.documentElement) return document.documentElement; - return document.body; - } - - private handleExpand() { - try { - let body = document.getElementsByTagName('body')[0]; - let head = document.getElementsByTagName('head')[0]; - - if (!body || !head) return; - - if (this.latestState.isExpanded) { - - let children = head.children; - let hasExp = false; - let viewports: HTMLElement[] = []; - for (let i = 0; i < children.length; i++) { - if (children[i] === this.expandedViewport) { - hasExp = true; - } else if (((children[i] as any).name || '').toLowerCase() === 'viewport') { - viewports.push(children[i] as any); - } - } - - for (let v of viewports) { - head.removeChild(v); - } - - if (!hasExp) head.appendChild(this.expandedViewport); - - - let s = body.style; - - let doc = this.getScrollElement(); - let scrollLeft = doc.scrollLeft; - let scrollTop = doc.scrollTop; - - this.rootState = { - top: s.top, bottom: s.bottom, right: s.right, left: s.left, scrollTop, scrollLeft, position: s.position, overflow: s.overflow, viewports, zindex: this.root.style.zIndex, - width: s.width, height: s.height, - maxWidth: s.maxWidth, maxHeight: s.maxHeight, - margin: s.margin, marginLeft: s.marginLeft, marginRight: s.marginRight, marginTop: s.marginTop, marginBottom: s.marginBottom - }; - - s.overflow = 'hidden'; - s.position = 'fixed'; - s.top = '0'; - s.bottom = '0'; - s.right = '0'; - s.left = '0'; - - s.width = '100%'; - s.height = '100%'; - s.maxWidth = '100%'; - s.maxHeight = '100%'; - s.margin = '0'; - s.marginLeft = '0'; - s.marginRight = '0'; - s.marginTop = '0'; - s.marginBottom = '0'; - - this.root.style.zIndex = '100000'; - } else { - // root.style.overflow = rootOverflow; - let children = head.children; - for (let i = 0; i < children.length; i++) { - if (children[i] === this.expandedViewport) { - head.removeChild(this.expandedViewport); - break; - } - } - - if (this.rootState) { - let s = body.style, t = this.rootState; - for (let v of t.viewports) { - head.appendChild(v); - } - s.top = t.top; - s.bottom = t.bottom; - s.left = t.left; - s.right = t.right; - - s.width = t.width; - s.height = t.height; - s.maxWidth = t.maxWidth; - s.maxHeight = t.maxHeight; - s.margin = t.margin; - s.marginLeft = t.marginLeft; - s.marginRight = t.marginRight; - s.marginTop = t.marginTop; - s.marginBottom = t.marginBottom; - - s.position = t.position; - s.overflow = t.overflow; - let doc = this.getScrollElement(); - doc.scrollTop = t.scrollTop; - doc.scrollLeft = t.scrollLeft; - this.rootState = void 0; - this.root.style.zIndex = t.zindex; - } - } - } catch (e) { - this.context.logger.error('Layout change error, you might have to reload the page.'); - console.log('Layout change error, you might have to reload the page.', e); - } - } - - updateTargets(targets: LayoutTarget[]) { - this.targets = targets; - this.dispatcher.schedule(() => CommonEvents.ComponentsChanged.dispatch(this.context, {})); - } - - constructor(context: Context, public targets: LayoutTarget[], private root: HTMLElement) { - super(context, { - isExpanded: false, - hideControls: false, - collapsedControlsLayout: CollapsedControlsLayout.Outside, - regionStates: { } - }); - - LayoutEvents.SetState.getStream(this.context).subscribe(e => this.update(e.data)); - - // <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" /> - this.expandedViewport = document.createElement('meta') as any; - this.expandedViewport.name = 'viewport'; - this.expandedViewport.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0'; - } -} \ No newline at end of file diff --git a/src/mol-app/controller/misc/jobs.ts b/src/mol-app/controller/misc/jobs.ts deleted file mode 100644 index 754edef44..000000000 --- a/src/mol-app/controller/misc/jobs.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import produce from 'immer' -import { filter } from 'rxjs/operators'; - -import { Controller } from '../controller' -import { JobEvents } from '../../event/basic'; -import { Context } from '../../context/context'; -import { Job } from '../../service/job'; - - -export interface JobInfo { - name: string; - message: string; - abort?: () => void -} - -export interface JobsState { - jobs: { [k: number]: JobInfo } -} - -export class JobsController extends Controller<JobsState> { - private updated(state: Job.State) { - let isWatched = state.type === this.type; - let jobs = this.latestState.jobs!; - - if (!isWatched) { - if (jobs[state.jobId] !== undefined) { - jobs = produce(jobs, _jobs => { delete _jobs[state.jobId] }); - this.setState({ jobs }); - } - return; - } - - jobs = produce(jobs, _jobs => { - _jobs[state.jobId] = { - name: state.name, - message: state.message, - abort: state.abort - }; - }) - this.setState({ jobs }); - } - - private started(job: Job.Info) { - this.setState({ - jobs: produce(this.latestState.jobs!, _jobs => { - _jobs[job.id] = { name: job.name, message: 'Running...' } - }) - }); - } - - private completed(taskId: number) { - if (!this.latestState.jobs![taskId]) return; - - this.setState({ - jobs: produce(this.latestState.jobs!, _jobs => { delete _jobs[taskId] }) - }); - } - - constructor(context: Context, private type: Job.Type) { - super(context, { - jobs: {} - }); - - JobEvents.StateUpdated.getStream(this.context) - .subscribe(e => this.updated(e.data)); - - JobEvents.Started.getStream(this.context).pipe( - filter(e => e.data.type === type)) - .subscribe(e => this.started(e.data)); - - JobEvents.Completed.getStream(this.context) - .subscribe(e => this.completed(e.data)); - } -} \ No newline at end of file diff --git a/src/mol-app/controller/misc/log.ts b/src/mol-app/controller/misc/log.ts deleted file mode 100644 index d703375a8..000000000 --- a/src/mol-app/controller/misc/log.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import produce from 'immer' - -import { Controller } from '../controller' -import { Context } from '../../context/context'; -import { LogEvent } from '../../event/basic'; -import { Logger } from '../../service/logger'; - -export class LogController extends Controller<{ entries: Logger.Entry[] }> { - constructor(context: Context) { - super(context, { entries: [] }); - - LogEvent.getStream(this.context) - .subscribe(e => this.setState({ - entries: produce(this.latestState.entries, _entries => { _entries.push(e.data) }) - })) - } -} \ No newline at end of file diff --git a/src/mol-app/controller/transform/list.ts b/src/mol-app/controller/transform/list.ts deleted file mode 100644 index 75ee2b5c2..000000000 --- a/src/mol-app/controller/transform/list.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import { Context } from '../../context/context' -import { Controller } from '../controller'; -import { AnyTransform } from 'mol-view/state/transform'; -import { AnyEntity } from 'mol-view/state/entity'; - -export interface TransformListState { - entity?: AnyEntity - transforms: AnyTransform[] -} - -export class TransformListController extends Controller<TransformListState> { - constructor(context: Context) { - super(context, { transforms: [], entity: undefined }); - - context.currentTransforms.subscribe((transforms) => { - this.state.next({ transforms, entity: context.currentEntity.getValue() }) // TODO - this.setState({ transforms, entity: context.currentEntity.getValue() }) - }) - } -} \ No newline at end of file diff --git a/src/mol-app/controller/visualization/sequence-view.ts b/src/mol-app/controller/visualization/sequence-view.ts deleted file mode 100644 index 17423f980..000000000 --- a/src/mol-app/controller/visualization/sequence-view.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import { shallowClone } from 'mol-util'; -import { Context } from '../../context/context' -import { Controller } from '../controller'; -import { Structure } from 'mol-model/structure'; - -export const DefaultSequenceViewState = { - structure: void 0 as (Structure | undefined) -} -export type SequenceViewState = typeof DefaultSequenceViewState - -export class SequenceViewController extends Controller<SequenceViewState> { - constructor(context: Context) { - super(context, shallowClone(DefaultSequenceViewState)); - } -} \ No newline at end of file diff --git a/src/mol-app/controller/visualization/viewport.ts b/src/mol-app/controller/visualization/viewport.ts deleted file mode 100644 index 40a4ac53a..000000000 --- a/src/mol-app/controller/visualization/viewport.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -// import { throttle } from 'rxjs/operators'; -// import { interval } from 'rxjs'; - -import { shallowClone } from 'mol-util'; -import { Context } from '../../context/context' -import { Controller } from '../controller'; - -export const DefaultViewportOptions = { - clearColor: { r: 1, g: 1, b: 1 }, - enableFog: true, - cameraFOV: 30, - cameraSpeed: 4 -} -export type ViewportOptions = typeof DefaultViewportOptions - -export class ViewportController extends Controller<ViewportOptions> { - constructor(context: Context) { - super(context, shallowClone(DefaultViewportOptions)); - } -} \ No newline at end of file diff --git a/src/mol-app/event/basic.ts b/src/mol-app/event/basic.ts deleted file mode 100644 index f52d6e651..000000000 --- a/src/mol-app/event/basic.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import { Event } from './event' -import { Logger } from '../service/logger'; -import { Dispatcher } from '../service/dispatcher' -import { LayoutState } from '../controller/layout'; -import { ViewportOptions } from '../controller/visualization/viewport'; -import { Job } from '../service/job'; -import { Loci } from 'mol-model/loci'; - -const Lane = Dispatcher.Lane; - -export const LogEvent = Event.create<Logger.Entry>('bs.Log', Lane.Log); - -export namespace CommonEvents { - export const LayoutChanged = Event.create('bs.Common.LayoutChanged', Lane.Slow); - export const ComponentsChanged = Event.create('bs.Common.ComponentsChanged', Lane.Slow); -} - -export namespace JobEvents { - export const Started = Event.create<Job.Info>('bs.Jobs.Started', Lane.Job); - export const Completed = Event.create<number>('bs.Jobs.Completed', Lane.Job); - export const StateUpdated = Event.create<Job.State>('bs.Jobs.StateUpdated', Lane.Busy); -} - -export namespace LayoutEvents { - export const SetState = Event.create<Partial<LayoutState>>('lm.cmd.Layout.SetState', Lane.Slow); - export const SetViewportOptions = Event.create<ViewportOptions>('bs.cmd.Layout.SetViewportOptions', Lane.Slow); -} - -export namespace InteractivityEvents { - export const HighlightLoci = Event.create<Loci>('bs.Interactivity.HighlightLoci', Lane.Slow); - export const SelectLoci = Event.create<Loci>('bs.Interactivity.SelectLoci', Lane.Slow); - export const LabelLoci = Event.create<Loci>('bs.Interactivity.LabelLoci', Lane.Slow); -} diff --git a/src/mol-app/event/event.ts b/src/mol-app/event/event.ts deleted file mode 100644 index 4e6affecc..000000000 --- a/src/mol-app/event/event.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import { Observable } from 'rxjs'; -import { Context } from '../context/context' -import { Dispatcher } from '../service/dispatcher' - -export interface Event<T> { - type: Event.Type<T>; - data: T; -} - -export namespace Event { - export type Stream<T> = Observable<Event<T>>; - - import Lane = Dispatcher.Lane - - export type Any = Event<any> - export type AnyType = Type<any> - - export interface Type<T> { - name: string, - lane: Lane, - dispatch(context: Context, data: T): void; - getStream(context: Context): Stream<T>; - } - - const EventPrototype = { - dispatch<T>(this: any, context: Context, data: T) { context.dispatcher.dispatch({ type: this, data }) }, - getStream(this: any, context: Context) { return context.dispatcher.getStream(this); } - } - - export function create<T>(name: string, lane: Dispatcher.Lane): Type<T> { - return Object.create(EventPrototype, { - name: { writable: false, configurable: false, value: name }, - lane: { writable: false, configurable: false, value: lane } - }); - } -} \ No newline at end of file diff --git a/src/mol-app/service/dispatcher.ts b/src/mol-app/service/dispatcher.ts deleted file mode 100644 index 58a6345c6..000000000 --- a/src/mol-app/service/dispatcher.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import { Subject } from 'rxjs'; -import { filter } from 'rxjs/operators'; -import { Event } from '../event/event' - -export class Dispatcher { - LOG_DISPATCH_STREAM = false; - - private lanes: Subject<Event<any>>[] = []; - constructor() { - for (let i = 0; i <= Dispatcher.Lane.Job; i++) { - this.lanes.push(new Subject<Event<any>>()); - } - } - - dispatch<T>(event: Event<T>) { - if (this.LOG_DISPATCH_STREAM) console.log(event.type.name, Dispatcher.Lane[event.type.lane], event.data); - this.lanes[event.type.lane].next(event); - } - - schedule(action: () => void, onError?: (e: string) => void, timeout = 1000 / 31) { - return setTimeout(() => { - if (onError) { - try { - action.call(null) - } catch (e) { - onError.call(null, '' + e); - } - } else { - action.call(null); - } - }, timeout); - } - - getStream<T>(type: Event.Type<T>): Event.Stream<T> { - return this.lanes[type.lane].pipe(filter(e => e.type === type)); - } - - finished() { - this.lanes.forEach(l => l.complete()); - } -} - -export namespace Dispatcher { - export enum Lane { - Slow = 0, - Fast = 1, - Log = 2, - Busy = 3, - Transformer = 4, - Job = 5 - } -} \ No newline at end of file diff --git a/src/mol-app/service/job.ts b/src/mol-app/service/job.ts deleted file mode 100644 index 3bfd0263e..000000000 --- a/src/mol-app/service/job.ts +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import { Context } from '../context/context' -import { JobEvents } from '../event/basic'; -import { PerformanceMonitor } from 'mol-util/performance-monitor'; -import { formatProgress } from 'mol-util'; -import { Progress, Task } from 'mol-task'; - -export class Job<T> { - private info: Job.Info; - get id() { return this.info.id; } - get reportTime() { return this.info.reportTime; } - - run(context: Context) { - return this.runWithContext(context).result; - } - - runWithContext(context: Context): Job.Running<T> { - return new Job.Running(context, this.task, this.info); - } - - setReportTime(report: boolean) { - this.info.reportTime = report; - return this; - } - - constructor(public name: string, public type: Job.Type, private task: Task<T>) { - this.info = { - id: serialJobId++, - name, - type, - reportTime: false - }; - } -} - -let serialJobId = 0; -export namespace Job { - export let __DEBUG_MODE__ = false; - - export type Type = 'Normal' | 'Background' | 'Silent'; - - export interface Info { - id: number, - type: Type, - name: string, - reportTime: boolean - } - - export class Running<T> { - result: Promise<T>; - - private progressUpdated(progress: Progress) { - JobEvents.StateUpdated.dispatch(this.context, { - jobId: this.info.id, - type: this.info.type, - name: this.info.name, - message: formatProgress(progress), - abort: progress.requestAbort - }); - } - - private resolved() { - try { - this.context.performance.end('job' + this.info.id); - if (this.info.reportTime) { - let time = this.context.performance.time('job' + this.info.id); - if (this.info.type !== 'Silent') this.context.logger.info(`${this.info.name} finished in ${PerformanceMonitor.format(time)}.`) - } - } finally { - JobEvents.Completed.dispatch(this.context, this.info.id); - } - } - - private rejected(err: any) { - this.context.performance.end('job' + this.info.id); - this.context.performance.formatTime('job' + this.info.id); - - if (__DEBUG_MODE__) { - console.error(err); - } - - try { - if (this.info.type === 'Silent') { - if (err.warn) this.context.logger.warning(`Warning (${this.info.name}): ${err.message}`); - else console.error(`Error (${this.info.name})`, err); - } else { - if (err.warn) { - this.context.logger.warning(`Warning (${this.info.name}): ${err.message}`); - } else { - let e = '' + err; - if (e.indexOf('Aborted') >= 0) this.context.logger.info(`${this.info.name}: Aborted.`); - else this.context.logger.error(`Error (${this.info.name}): ${err}`); - } - } - } catch (e) { - console.error(e); - } finally { - JobEvents.Completed.dispatch(this.context, this.info.id); - } - } - - private run() { - JobEvents.Started.dispatch(this.context, this.info); - this.context.performance.start('job' + this.info.id); - - this.result = this.task.run((p: Progress) => this.progressUpdated(p), 250) - this.result.then(() => this.resolved()).catch(e => this.rejected(e)); - } - - constructor(private context: Context, private task: Task<T>, private info: Info) { - this.run(); - } - } - - export interface State { - jobId: number, - type: Type, - name: string, - message: string, - abort?: () => void - } - - export function create<T>(name: string, type: Type, task: Task<T>) { - return new Job<T>(name, type, task); - } -} \ No newline at end of file diff --git a/src/mol-app/service/logger.ts b/src/mol-app/service/logger.ts deleted file mode 100644 index 901596719..000000000 --- a/src/mol-app/service/logger.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import { LogEvent } from '../event/basic' -import { Context } from '../context/context' - -export class Logger { - - private log(e: Logger.Entry) { - LogEvent.dispatch(this.context, e); - } - - message(m: string) { - this.log({ type: Logger.EntryType.Message, timestamp: new Date(), message: m }); - } - - error(m: string) { - this.log({ type: Logger.EntryType.Error, timestamp: new Date(), message: m }); - } - - warning(m: string) { - this.log({ type: Logger.EntryType.Warning, timestamp: new Date(), message: m }); - } - - info(m: string) { - this.log({ type: Logger.EntryType.Info, timestamp: new Date(), message: m }); - } - - constructor(private context: Context) { - - } -} - -export namespace Logger { - export enum EntryType { - Message, - Error, - Warning, - Info - } - - export interface Entry { - type: EntryType; - timestamp: Date; - message: any - } -} \ No newline at end of file diff --git a/src/mol-app/skin/base.scss b/src/mol-app/skin/base.scss deleted file mode 100644 index 355f0ecca..000000000 --- a/src/mol-app/skin/base.scss +++ /dev/null @@ -1,45 +0,0 @@ - -@font-face { - font-family: 'fontello'; - src: url('./fonts/fontello.eot'); - src: url('./fonts/fontello.eot#iefix') format('embedded-opentype'), - url('./fonts/fontello.woff2') format('woff2'), - url('./fonts/fontello.woff') format('woff'), - url('./fonts/fontello.ttf') format('truetype'), - url('./fonts/fontello.svg#fontello') format('svg'); - font-weight: normal; - font-style: normal; -} - -@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,700); - -.molstar-plugin { - font-family: "Helvetica Neue", "Source Sans Pro", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.42857143; - - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - @import 'variables'; - - // for bootstrap - $border-radius-base: 0; - $border-radius-large: 0; - $border-radius-small: 0; - - @import 'bootstrap'; - - @import 'icons'; - @import 'layout'; - @import 'ui'; - @import 'logo'; - - .molstar-plugin-content { - color: $font-color; - } - - background: $default-background; -} \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap.scss b/src/mol-app/skin/bootstrap.scss deleted file mode 100644 index f26fdba91..000000000 --- a/src/mol-app/skin/bootstrap.scss +++ /dev/null @@ -1,25 +0,0 @@ -/*! - * Bootstrap v3.3.6 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ - -// Core variables and mixins -@import "bootstrap/variables"; -@import "bootstrap/mixins"; - -// Reset and dependencies -@import "bootstrap/normalize"; - -// Core CSS -@import "bootstrap/scaffolding"; -@import "bootstrap/type"; -@import "bootstrap/forms"; -@import "bootstrap/buttons"; - -// Components -@import "bootstrap/button-groups"; -@import "bootstrap/input-groups"; - -@import "bootstrap/labels"; -@import "bootstrap/badges"; diff --git a/src/mol-app/skin/bootstrap/badges.scss b/src/mol-app/skin/bootstrap/badges.scss deleted file mode 100644 index b03cc5562..000000000 --- a/src/mol-app/skin/bootstrap/badges.scss +++ /dev/null @@ -1,68 +0,0 @@ -// -// Badges -// -------------------------------------------------- - - -// Base class -.badge { - display: inline-block; - min-width: 10px; - padding: 3px 7px; - font-size: $font-size-small; - font-weight: $badge-font-weight; - color: $badge-color; - line-height: $badge-line-height; - vertical-align: middle; - white-space: nowrap; - text-align: center; - background-color: $badge-bg; - border-radius: $badge-border-radius; - - // Empty badges collapse automatically (not available in IE8) - &:empty { - display: none; - } - - // Quick fix for badges in buttons - .molstar-btn & { - position: relative; - top: -1px; - } - - .molstar-btn-xs &, - .molstar-btn-group-xs > .molstar-btn & { - top: 0; - padding: 1px 5px; - } - - // [converter] extracted a& to a.badge - - // Account for badges in navs - .list-group-item.active > &, - .nav-pills > .active > a > & { - color: $badge-active-color; - background-color: $badge-active-bg; - } - - .list-group-item > & { - float: right; - } - - .list-group-item > & + & { - margin-right: 5px; - } - - .nav-pills > li > a > & { - margin-left: 3px; - } - } - - // Hover state, but only for links - a.badge { - &:hover, - &:focus { - color: $badge-link-hover-color; - text-decoration: none; - cursor: pointer; - } - } \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/button-groups.scss b/src/mol-app/skin/bootstrap/button-groups.scss deleted file mode 100644 index 3fd6d085e..000000000 --- a/src/mol-app/skin/bootstrap/button-groups.scss +++ /dev/null @@ -1,244 +0,0 @@ -// -// Button groups -// -------------------------------------------------- - -// Make the div behave like a button -.molstar-btn-group, -.molstar-btn-group-vertical { - position: relative; - display: inline-block; - vertical-align: middle; // match .molstar-btn alignment given font-size hack above - > .molstar-btn { - position: relative; - float: left; - // Bring the "active" button to the front - &:hover, - &:focus, - &:active, - &.active { - z-index: 2; - } - } -} - -// Prevent double borders when buttons are next to each other -.molstar-btn-group { - .molstar-btn + .molstar-btn, - .molstar-btn + .molstar-btn-group, - .molstar-btn-group + .molstar-btn, - .molstar-btn-group + .molstar-btn-group { - margin-left: -1px; - } -} - -// Optional: Group multiple button groups together for a toolbar -.molstar-btn-toolbar { - margin-left: -5px; // Offset the first child's margin - @include clearfix; - - .molstar-btn, - .molstar-btn-group, - .input-group { - float: left; - } - > .molstar-btn, - > .molstar-btn-group, - > .input-group { - margin-left: 5px; - } -} - -.molstar-btn-group > .molstar-btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { - border-radius: 0; -} - -// Set corners individual because sometimes a single button can be in a .molstar-btn-group and we need :first-child and :last-child to both match -.molstar-btn-group > .molstar-btn:first-child { - margin-left: 0; - &:not(:last-child):not(.dropdown-toggle) { - @include border-right-radius(0); - } -} -// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it -.molstar-btn-group > .molstar-btn:last-child:not(:first-child), -.molstar-btn-group > .dropdown-toggle:not(:first-child) { - @include border-left-radius(0); -} - -// Custom edits for including molstar-btn-groups within molstar-btn-groups (useful for including dropdown buttons within a molstar-btn-group) -.molstar-btn-group > .molstar-btn-group { - float: left; -} -.molstar-btn-group > .molstar-btn-group:not(:first-child):not(:last-child) > .molstar-btn { - border-radius: 0; -} -.molstar-btn-group > .molstar-btn-group:first-child:not(:last-child) { - > .molstar-btn:last-child, - > .dropdown-toggle { - @include border-right-radius(0); - } -} -.molstar-btn-group > .molstar-btn-group:last-child:not(:first-child) > .molstar-btn:first-child { - @include border-left-radius(0); -} - -// On active and open, don't show outline -.molstar-btn-group .dropdown-toggle:active, -.molstar-btn-group.open .dropdown-toggle { - outline: 0; -} - - -// Sizing -// -// Remix the default button sizing classes into new ones for easier manipulation. - -.molstar-btn-group-xs > .molstar-btn { @extend .molstar-btn-xs; } -.molstar-btn-group-sm > .molstar-btn { @extend .molstar-btn-sm; } -.molstar-btn-group-lg > .molstar-btn { @extend .molstar-btn-lg; } - - -// Split button dropdowns -// ---------------------- - -// Give the line between buttons some depth -.molstar-btn-group > .molstar-btn + .dropdown-toggle { - padding-left: 8px; - padding-right: 8px; -} -.molstar-btn-group > .molstar-btn-lg + .dropdown-toggle { - padding-left: 12px; - padding-right: 12px; -} - -// The clickable button for toggling the menu -// Remove the gradient and set the same inset shadow as the :active state -.molstar-btn-group.open .dropdown-toggle { - @include box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); - - // Show no shadow for `.molstar-btn-link` since it has no other button styles. - &.molstar-btn-link { - @include box-shadow(none); - } -} - - -// Reposition the caret -.molstar-btn .caret { - margin-left: 0; -} -// Carets in other button sizes -.molstar-btn-lg .caret { - border-width: $caret-width-large $caret-width-large 0; - border-bottom-width: 0; -} -// Upside down carets for .dropup -.dropup .molstar-btn-lg .caret { - border-width: 0 $caret-width-large $caret-width-large; -} - - -// Vertical button groups -// ---------------------- - -.molstar-btn-group-vertical { - > .molstar-btn, - > .molstar-btn-group, - > .molstar-btn-group > .molstar-btn { - display: block; - float: none; - width: 100%; - max-width: 100%; - } - - // Clear floats so dropdown menus can be properly placed - > .molstar-btn-group { - @include clearfix; - > .molstar-btn { - float: none; - } - } - - > .molstar-btn + .molstar-btn, - > .molstar-btn + .molstar-btn-group, - > .molstar-btn-group + .molstar-btn, - > .molstar-btn-group + .molstar-btn-group { - margin-top: -1px; - margin-left: 0; - } -} - -.molstar-btn-group-vertical > .molstar-btn { - &:not(:first-child):not(:last-child) { - border-radius: 0; - } - &:first-child:not(:last-child) { - @include border-top-radius($molstar-btn-border-radius-base); - @include border-bottom-radius(0); - } - &:last-child:not(:first-child) { - @include border-top-radius(0); - @include border-bottom-radius($molstar-btn-border-radius-base); - } -} -.molstar-btn-group-vertical > .molstar-btn-group:not(:first-child):not(:last-child) > .molstar-btn { - border-radius: 0; -} -.molstar-btn-group-vertical > .molstar-btn-group:first-child:not(:last-child) { - > .molstar-btn:last-child, - > .dropdown-toggle { - @include border-bottom-radius(0); - } -} -.molstar-btn-group-vertical > .molstar-btn-group:last-child:not(:first-child) > .molstar-btn:first-child { - @include border-top-radius(0); -} - - -// Justified button groups -// ---------------------- - -.molstar-btn-group-justified { - display: table; - width: 100%; - table-layout: fixed; - border-collapse: separate; - > .molstar-btn, - > .molstar-btn-group { - float: none; - display: table-cell; - width: 1%; - } - > .molstar-btn-group .molstar-btn { - width: 100%; - } - - > .molstar-btn-group .dropdown-menu { - left: auto; - } -} - - -// Checkbox and radio options -// -// In order to support the browser's form validation feedback, powered by the -// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use -// `display: none;` or `visibility: hidden;` as that also hides the popover. -// Simply visually hiding the inputs via `opacity` would leave them clickable in -// certain cases which is prevented by using `clip` and `pointer-events`. -// This way, we ensure a DOM element is visible to position the popover from. -// -// See https://github.com/twbs/bootstrap/pull/12794 and -// https://github.com/twbs/bootstrap/pull/14559 for more information. - -[data-toggle="buttons"] { - > .molstar-btn, - > .molstar-btn-group > .molstar-btn { - input[type="radio"], - input[type="checkbox"] { - position: absolute; - clip: rect(0,0,0,0); - pointer-events: none; - } - } -} \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/buttons.scss b/src/mol-app/skin/bootstrap/buttons.scss deleted file mode 100644 index f775ee279..000000000 --- a/src/mol-app/skin/bootstrap/buttons.scss +++ /dev/null @@ -1,168 +0,0 @@ -// -// Buttons -// -------------------------------------------------- - - -// Base styles -// -------------------------------------------------- - -.molstar-btn { - display: inline-block; - margin-bottom: 0; // For input.molstar-btn - font-weight: $molstar-btn-font-weight; - text-align: center; - vertical-align: middle; - touch-action: manipulation; - cursor: pointer; - background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 - border: 1px solid transparent; - white-space: nowrap; - @include button-size($padding-base-vertical, $padding-base-horizontal, $font-size-base, $line-height-base, $molstar-btn-border-radius-base); - @include user-select(none); - - &, - &:active, - &.active { - &:focus, - &.focus { - @include tab-focus; - } - } - - &:hover, - &:focus, - &.focus { - color: $molstar-btn-default-color; - text-decoration: none; - } - - &:active, - &.active { - outline: 0; - background-image: none; - @include box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); - } - - &.disabled, - &[disabled], - fieldset[disabled] & { - cursor: $cursor-disabled; - @include opacity(.65); - @include box-shadow(none); - } - - // [converter] extracted a& to a.molstar-btn - } - - a.molstar-btn { - &.disabled, - fieldset[disabled] & { - pointer-events: none; // Future-proof disabling of clicks on `<a>` elements - } - } - - - // Alternate buttons - // -------------------------------------------------- - - .molstar-btn-default { - @include button-variant($molstar-btn-default-color, $molstar-btn-default-bg, $molstar-btn-default-border); - } - .molstar-btn-primary { - @include button-variant($molstar-btn-primary-color, $molstar-btn-primary-bg, $molstar-btn-primary-border); - } - // Success appears as green - .molstar-btn-success { - @include button-variant($molstar-btn-success-color, $molstar-btn-success-bg, $molstar-btn-success-border); - } - // Info appears as blue-green - .molstar-btn-info { - @include button-variant($molstar-btn-info-color, $molstar-btn-info-bg, $molstar-btn-info-border); - } - // Warning appears as orange - .molstar-btn-warning { - @include button-variant($molstar-btn-warning-color, $molstar-btn-warning-bg, $molstar-btn-warning-border); - } - // Danger and error appear as red - .molstar-btn-danger { - @include button-variant($molstar-btn-danger-color, $molstar-btn-danger-bg, $molstar-btn-danger-border); - } - - - // Link buttons - // ------------------------- - - // Make a button look and behave like a link - .molstar-btn-link { - color: $link-color; - font-weight: normal; - border-radius: 0; - - &, - &:active, - &.active, - &[disabled], - fieldset[disabled] & { - background-color: transparent; - @include box-shadow(none); - } - &, - &:hover, - &:focus, - &:active { - border-color: transparent; - } - &:hover, - &:focus { - color: $link-hover-color; - text-decoration: $link-hover-decoration; - background-color: transparent; - } - &[disabled], - fieldset[disabled] & { - &:hover, - &:focus { - color: $molstar-btn-link-disabled-color; - text-decoration: none; - } - } - } - - - // Button Sizes - // -------------------------------------------------- - - .molstar-btn-lg { - // line-height: ensure even-numbered height of button next to large input - @include button-size($padding-large-vertical, $padding-large-horizontal, $font-size-large, $line-height-large, $molstar-btn-border-radius-large); - } - .molstar-btn-sm { - // line-height: ensure proper height of button next to small input - @include button-size($padding-small-vertical, $padding-small-horizontal, $font-size-small, $line-height-small, $molstar-btn-border-radius-small); - } - .molstar-btn-xs { - @include button-size($padding-xs-vertical, $padding-xs-horizontal, $font-size-small, $line-height-small, $molstar-btn-border-radius-small); - } - - - // Block button - // -------------------------------------------------- - - .molstar-btn-block { - display: block; - width: 100%; - } - - // Vertically space out multiple block buttons - .molstar-btn-block + .molstar-btn-block { - margin-top: 5px; - } - - // Specificity overrides - input[type="submit"], - input[type="reset"], - input[type="button"] { - &.molstar-btn-block { - width: 100%; - } - } \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/forms.scss b/src/mol-app/skin/bootstrap/forms.scss deleted file mode 100644 index b51eb372b..000000000 --- a/src/mol-app/skin/bootstrap/forms.scss +++ /dev/null @@ -1,617 +0,0 @@ -// -// Forms -// -------------------------------------------------- - - -// Normalize non-controls -// -// Restyle and baseline non-control form elements. - -fieldset { - padding: 0; - margin: 0; - border: 0; - // Chrome and Firefox set a `min-width: min-content;` on fieldsets, - // so we reset that to ensure it behaves more like a standard block element. - // See https://github.com/twbs/bootstrap/issues/12359. - min-width: 0; - } - - legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: $line-height-computed; - font-size: ($font-size-base * 1.5); - line-height: inherit; - color: $legend-color; - border: 0; - border-bottom: 1px solid $legend-border-color; - } - - label { - display: inline-block; - max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141) - margin-bottom: 5px; - font-weight: bold; - } - - - // Normalize form controls - // - // While most of our form styles require extra classes, some basic normalization - // is required to ensure optimum display with or without those classes to better - // address browser inconsistencies. - - // Override content-box in Normalize (* isn't specific enough) - input[type="search"] { - @include box-sizing(border-box); - } - - // Position radios and checkboxes better - input[type="radio"], - input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; // IE8-9 - line-height: normal; - } - - input[type="file"] { - display: block; - } - - // Make range inputs behave like textual form controls - input[type="range"] { - display: block; - width: 100%; - } - - // Make multiple select elements height not fixed - select[multiple], - select[size] { - height: auto; - } - - // Focus for file, radio, and checkbox - input[type="file"]:focus, - input[type="radio"]:focus, - input[type="checkbox"]:focus { - @include tab-focus; - } - - // Adjust output element - output { - display: block; - padding-top: ($padding-base-vertical + 1); - font-size: $font-size-base; - line-height: $line-height-base; - color: $input-color; - } - - - // Common form controls - // - // Shared size and type resets for form controls. Apply `.molstar-form-control` to any - // of the following form controls: - // - // select - // textarea - // input[type="text"] - // input[type="password"] - // input[type="datetime"] - // input[type="datetime-local"] - // input[type="date"] - // input[type="month"] - // input[type="time"] - // input[type="week"] - // input[type="number"] - // input[type="email"] - // input[type="url"] - // input[type="search"] - // input[type="tel"] - // input[type="color"] - - .molstar-form-control { - display: block; - width: 100%; - height: $input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border) - padding: $padding-base-vertical $padding-base-horizontal; - font-size: $font-size-base; - line-height: $line-height-base; - color: $input-color; - background-color: $input-bg; - background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 - border: 1px solid $input-border; - border-radius: $input-border-radius; // Note: This has no effect on <select>s in some browsers, due to the limited stylability of <select>s in CSS. - //@include box-shadow(none);//inset 0 1px 1px rgba(0,0,0,.075)); - //@include transition(border-color ease-in-out .15s, box-shadow ease-in-out .15s); - - // Customize the `:focus` state to imitate native WebKit styles. - @include molstar-form-control-focus; - - // Placeholder - @include placeholder; - - // Unstyle the caret on `<select>`s in IE10+. - &::-ms-expand { - border: 0; - background-color: transparent; - } - - // Disabled and read-only inputs - // - // HTML5 says that controls under a fieldset > legend:first-child won't be - // disabled if the fieldset is disabled. Due to implementation difficulty, we - // don't honor that edge case; we style them as disabled anyway. - &[disabled], - &[readonly], - fieldset[disabled] & { - background-color: $input-bg-disabled; - opacity: 1; // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655 - } - - &[disabled], - fieldset[disabled] & { - cursor: $cursor-disabled; - } - - // [converter] extracted textarea& to textarea.molstar-form-control - } - - // Reset height for `textarea`s - textarea.molstar-form-control { - height: auto; - } - - - // Search inputs in iOS - // - // This overrides the extra rounded corners on search inputs in iOS so that our - // `.molstar-form-control` class can properly style them. Note that this cannot simply - // be added to `.molstar-form-control` as it's not specific enough. For details, see - // https://github.com/twbs/bootstrap/issues/11586. - - input[type="search"] { - -webkit-appearance: none; - } - - - // Special styles for iOS temporal inputs - // - // In Mobile Safari, setting `display: block` on temporal inputs causes the - // text within the input to become vertically misaligned. As a workaround, we - // set a pixel line-height that matches the given height of the input, but only - // for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848 - // - // Note that as of 8.3, iOS doesn't support `datetime` or `week`. - - @media screen and (-webkit-min-device-pixel-ratio: 0) { - input[type="date"], - input[type="time"], - input[type="datetime-local"], - input[type="month"] { - &.molstar-form-control { - line-height: $input-height-base; - } - - &.input-sm, - .input-group-sm & { - line-height: $input-height-small; - } - - &.input-lg, - .input-group-lg & { - line-height: $input-height-large; - } - } - } - - - // Form groups - // - // Designed to help with the organization and spacing of vertical forms. For - // horizontal forms, use the predefined grid classes. - - .form-group { - margin-bottom: $form-group-margin-bottom; - } - - - // Checkboxes and radios - // - // Indent the labels to position radios/checkboxes as hanging controls. - - .radio, - .checkbox { - position: relative; - display: block; - margin-top: 10px; - margin-bottom: 10px; - - label { - min-height: $line-height-computed; // Ensure the input doesn't jump when there is no text - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - cursor: pointer; - } - } - .radio input[type="radio"], - .radio-inline input[type="radio"], - .checkbox input[type="checkbox"], - .checkbox-inline input[type="checkbox"] { - position: absolute; - margin-left: -20px; - margin-top: 4px \9; - } - - .radio + .radio, - .checkbox + .checkbox { - margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing - } - - // Radios and checkboxes on same line - .radio-inline, - .checkbox-inline { - position: relative; - display: inline-block; - padding-left: 20px; - margin-bottom: 0; - vertical-align: middle; - font-weight: normal; - cursor: pointer; - } - .radio-inline + .radio-inline, - .checkbox-inline + .checkbox-inline { - margin-top: 0; - margin-left: 10px; // space out consecutive inline controls - } - - // Apply same disabled cursor tweak as for inputs - // Some special care is needed because <label>s don't inherit their parent's `cursor`. - // - // Note: Neither radios nor checkboxes can be readonly. - input[type="radio"], - input[type="checkbox"] { - &[disabled], - &.disabled, - fieldset[disabled] & { - cursor: $cursor-disabled; - } - } - // These classes are used directly on <label>s - .radio-inline, - .checkbox-inline { - &.disabled, - fieldset[disabled] & { - cursor: $cursor-disabled; - } - } - // These classes are used on elements with <label> descendants - .radio, - .checkbox { - &.disabled, - fieldset[disabled] & { - label { - cursor: $cursor-disabled; - } - } - } - - - // Static form control text - // - // Apply class to a `p` element to make any string of text align with labels in - // a horizontal form layout. - - .molstar-form-control-static { - // Size it appropriately next to real form controls - padding-top: ($padding-base-vertical + 1); - padding-bottom: ($padding-base-vertical + 1); - // Remove default margin from `p` - margin-bottom: 0; - min-height: ($line-height-computed + $font-size-base); - - &.input-lg, - &.input-sm { - padding-left: 0; - padding-right: 0; - } - } - - - // Form control sizing - // - // Build on `.molstar-form-control` with modifier classes to decrease or increase the - // height and font-size of form controls. - // - // The `.form-group-* molstar-form-control` variations are sadly duplicated to avoid the - // issue documented in https://github.com/twbs/bootstrap/issues/15074. - - @include input-size('.input-sm', $input-height-small, $padding-small-vertical, $padding-small-horizontal, $font-size-small, $line-height-small, $input-border-radius-small); - .form-group-sm { - .molstar-form-control { - height: $input-height-small; - padding: $padding-small-vertical $padding-small-horizontal; - font-size: $font-size-small; - line-height: $line-height-small; - border-radius: $input-border-radius-small; - } - select.molstar-form-control { - height: $input-height-small; - line-height: $input-height-small; - } - textarea.molstar-form-control, - select[multiple].molstar-form-control { - height: auto; - } - .molstar-form-control-static { - height: $input-height-small; - min-height: ($line-height-computed + $font-size-small); - padding: ($padding-small-vertical + 1) $padding-small-horizontal; - font-size: $font-size-small; - line-height: $line-height-small; - } - } - - @include input-size('.input-lg', $input-height-large, $padding-large-vertical, $padding-large-horizontal, $font-size-large, $line-height-large, $input-border-radius-large); - .form-group-lg { - .molstar-form-control { - height: $input-height-large; - padding: $padding-large-vertical $padding-large-horizontal; - font-size: $font-size-large; - line-height: $line-height-large; - border-radius: $input-border-radius-large; - } - select.molstar-form-control { - height: $input-height-large; - line-height: $input-height-large; - } - textarea.molstar-form-control, - select[multiple].molstar-form-control { - height: auto; - } - .molstar-form-control-static { - height: $input-height-large; - min-height: ($line-height-computed + $font-size-large); - padding: ($padding-large-vertical + 1) $padding-large-horizontal; - font-size: $font-size-large; - line-height: $line-height-large; - } - } - - - // Form control feedback states - // - // Apply contextual and semantic states to individual form controls. - - .has-feedback { - // Enable absolute positioning - position: relative; - - // Ensure icons don't overlap text - .molstar-form-control { - padding-right: ($input-height-base * 1.25); - } - } - // Feedback icon (requires .glyphicon classes) - .molstar-form-control-feedback { - position: absolute; - top: 0; - right: 0; - z-index: 2; // Ensure icon is above input groups - display: block; - width: $input-height-base; - height: $input-height-base; - line-height: $input-height-base; - text-align: center; - pointer-events: none; - } - .input-lg + .molstar-form-control-feedback, - .input-group-lg + .molstar-form-control-feedback, - .form-group-lg .molstar-form-control + .molstar-form-control-feedback { - width: $input-height-large; - height: $input-height-large; - line-height: $input-height-large; - } - .input-sm + .molstar-form-control-feedback, - .input-group-sm + .molstar-form-control-feedback, - .form-group-sm .molstar-form-control + .molstar-form-control-feedback { - width: $input-height-small; - height: $input-height-small; - line-height: $input-height-small; - } - - // Feedback states - .has-success { - @include molstar-form-control-validation($state-success-text, $state-success-text, $state-success-bg); - } - .has-warning { - @include molstar-form-control-validation($state-warning-text, $state-warning-text, $state-warning-bg); - } - .has-error { - @include molstar-form-control-validation($state-danger-text, $state-danger-text, $state-danger-bg); - } - - // Reposition feedback icon if input has visible label above - .has-feedback label { - - & ~ .molstar-form-control-feedback { - top: ($line-height-computed + 5); // Height of the `label` and its margin - } - &.sr-only ~ .molstar-form-control-feedback { - top: 0; - } - } - - - // Help text - // - // Apply to any element you wish to create light text for placement immediately - // below a form control. Use for general help, formatting, or instructional text. - - .help-block { - display: block; // account for any element using help-block - margin-top: 5px; - margin-bottom: 10px; - color: lighten($text-color, 25%); // lighten the text some for contrast - } - - - // Inline forms - // - // Make forms appear inline(-block) by adding the `.form-inline` class. Inline - // forms begin stacked on extra small (mobile) devices and then go inline when - // viewports reach <768px. - // - // Requires wrapping inputs and labels with `.form-group` for proper display of - // default HTML form controls and our custom form controls (e.g., input groups). - // - // Heads up! This is mixin-ed into `.navbar-form` in navbars.less. - - // [converter] extracted from `.form-inline` for libsass compatibility - @mixin form-inline { - - // Kick in the inline - @media (min-width: $screen-sm-min) { - // Inline-block all the things for "inline" - .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - - // In navbar-form, allow folks to *not* use `.form-group` - .molstar-form-control { - display: inline-block; - width: auto; // Prevent labels from stacking above inputs in `.form-group` - vertical-align: middle; - } - - // Make static controls behave like regular ones - .molstar-form-control-static { - display: inline-block; - } - - .input-group { - display: inline-table; - vertical-align: middle; - - .input-group-addon, - .input-group-molstar-btn, - .molstar-form-control { - width: auto; - } - } - - // Input groups need that 100% width though - .input-group > .molstar-form-control { - width: 100%; - } - - .control-label { - margin-bottom: 0; - vertical-align: middle; - } - - // Remove default margin on radios/checkboxes that were used for stacking, and - // then undo the floating of radios and checkboxes to match. - .radio, - .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - - label { - padding-left: 0; - } - } - .radio input[type="radio"], - .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - - // Re-override the feedback icon. - .has-feedback .molstar-form-control-feedback { - top: 0; - } - } - } - // [converter] extracted as `@mixin form-inline` for libsass compatibility - .form-inline { - @include form-inline; - } - - - - // Horizontal forms - // - // Horizontal forms are built on grid classes and allow you to create forms with - // labels on the left and inputs on the right. - - .form-horizontal { - - // Consistent vertical alignment of radios and checkboxes - // - // Labels also get some reset styles, but that is scoped to a media query below. - .radio, - .checkbox, - .radio-inline, - .checkbox-inline { - margin-top: 0; - margin-bottom: 0; - padding-top: ($padding-base-vertical + 1); // Default padding plus a border - } - // Account for padding we're adding to ensure the alignment and of help text - // and other content below items - .radio, - .checkbox { - min-height: ($line-height-computed + ($padding-base-vertical + 1)); - } - - // Make form groups behave like rows - .form-group { - @include make-row; - } - - // Reset spacing and right align labels, but scope to media queries so that - // labels on narrow viewports stack the same as a default form example. - @media (min-width: $screen-sm-min) { - .control-label { - text-align: right; - margin-bottom: 0; - padding-top: ($padding-base-vertical + 1); // Default padding plus a border - } - } - - // Validation states - // - // Reposition the icon because it's now within a grid column and columns have - // `position: relative;` on them. Also accounts for the grid gutter padding. - .has-feedback .molstar-form-control-feedback { - right: floor(($grid-gutter-width / 2)); - } - - // Form group sizes - // - // Quick utility class for applying `.input-lg` and `.input-sm` styles to the - // inputs and labels within a `.form-group`. - .form-group-lg { - @media (min-width: $screen-sm-min) { - .control-label { - padding-top: ($padding-large-vertical + 1); - font-size: $font-size-large; - } - } - } - .form-group-sm { - @media (min-width: $screen-sm-min) { - .control-label { - padding-top: ($padding-small-vertical + 1); - font-size: $font-size-small; - } - } - } - } \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/input-groups.scss b/src/mol-app/skin/bootstrap/input-groups.scss deleted file mode 100644 index 5f57fc3df..000000000 --- a/src/mol-app/skin/bootstrap/input-groups.scss +++ /dev/null @@ -1,171 +0,0 @@ -// -// Input groups -// -------------------------------------------------- - -// Base styles -// ------------------------- -.input-group { - position: relative; // For dropdowns - display: table; - border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table - - // Undo padding and float of grid classes - &[class*="col-"] { - float: none; - padding-left: 0; - padding-right: 0; - } - - .molstar-form-control { - // Ensure that the input is always above the *appended* addon button for - // proper border colors. - position: relative; - z-index: 2; - - // IE9 fubars the placeholder attribute in text inputs and the arrows on - // select elements in input groups. To fix it, we float the input. Details: - // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855 - float: left; - - width: 100%; - margin-bottom: 0; - - &:focus { - z-index: 3; - } - } - } - - // Sizing options - // - // Remix the default form control sizing classes into new ones for easier - // manipulation. - - .input-group-lg > .molstar-form-control, - .input-group-lg > .input-group-addon, - .input-group-lg > .input-group-molstar-btn > .molstar-btn { - @extend .input-lg; - } - .input-group-sm > .molstar-form-control, - .input-group-sm > .input-group-addon, - .input-group-sm > .input-group-molstar-btn > .molstar-btn { - @extend .input-sm; - } - - - // Display as table-cell - // ------------------------- - .input-group-addon, - .input-group-molstar-btn, - .input-group .molstar-form-control { - display: table-cell; - - &:not(:first-child):not(:last-child) { - border-radius: 0; - } - } - // Addon and addon wrapper for buttons - .input-group-addon, - .input-group-molstar-btn { - width: 1%; - white-space: nowrap; - vertical-align: middle; // Match the inputs - } - - // Text input groups - // ------------------------- - .input-group-addon { - padding: $padding-base-vertical $padding-base-horizontal; - font-size: $font-size-base; - font-weight: normal; - line-height: 1; - color: $input-color; - text-align: center; - background-color: $input-group-addon-bg; - border: 1px solid $input-group-addon-border-color; - border-radius: $input-border-radius; - - // Sizing - &.input-sm { - padding: $padding-small-vertical $padding-small-horizontal; - font-size: $font-size-small; - border-radius: $input-border-radius-small; - } - &.input-lg { - padding: $padding-large-vertical $padding-large-horizontal; - font-size: $font-size-large; - border-radius: $input-border-radius-large; - } - - // Nuke default margins from checkboxes and radios to vertically center within. - input[type="radio"], - input[type="checkbox"] { - margin-top: 0; - } - } - - // Reset rounded corners - .input-group .molstar-form-control:first-child, - .input-group-addon:first-child, - .input-group-molstar-btn:first-child > .molstar-btn, - .input-group-molstar-btn:first-child > .molstar-btn-group > .molstar-btn, - .input-group-molstar-btn:first-child > .dropdown-toggle, - .input-group-molstar-btn:last-child > .molstar-btn:not(:last-child):not(.dropdown-toggle), - .input-group-molstar-btn:last-child > .molstar-btn-group:not(:last-child) > .molstar-btn { - @include border-right-radius(0); - } - .input-group-addon:first-child { - border-right: 0; - } - .input-group .molstar-form-control:last-child, - .input-group-addon:last-child, - .input-group-molstar-btn:last-child > .molstar-btn, - .input-group-molstar-btn:last-child > .molstar-btn-group > .molstar-btn, - .input-group-molstar-btn:last-child > .dropdown-toggle, - .input-group-molstar-btn:first-child > .molstar-btn:not(:first-child), - .input-group-molstar-btn:first-child > .molstar-btn-group:not(:first-child) > .molstar-btn { - @include border-left-radius(0); - } - .input-group-addon:last-child { - border-left: 0; - } - - // Button input groups - // ------------------------- - .input-group-molstar-btn { - position: relative; - // Jankily prevent input button groups from wrapping with `white-space` and - // `font-size` in combination with `inline-block` on buttons. - font-size: 0; - white-space: nowrap; - - // Negative margin for spacing, position for bringing hovered/focused/actived - // element above the siblings. - > .molstar-btn { - position: relative; - + .molstar-btn { - margin-left: -1px; - } - // Bring the "active" button to the front - &:hover, - &:focus, - &:active { - z-index: 2; - } - } - - // Negative margin to only have a 1px border between the two - &:first-child { - > .molstar-btn, - > .molstar-btn-group { - margin-right: -1px; - } - } - &:last-child { - > .molstar-btn, - > .molstar-btn-group { - z-index: 2; - margin-left: -1px; - } - } - } \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/labels.scss b/src/mol-app/skin/bootstrap/labels.scss deleted file mode 100644 index c453cd417..000000000 --- a/src/mol-app/skin/bootstrap/labels.scss +++ /dev/null @@ -1,66 +0,0 @@ -// -// Labels -// -------------------------------------------------- - -.label { - display: inline; - padding: .2em .6em .3em; - font-size: 75%; - font-weight: bold; - line-height: 1; - color: $label-color; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em; - - // [converter] extracted a& to a.label - - // Empty labels collapse automatically (not available in IE8) - &:empty { - display: none; - } - - // Quick fix for labels in buttons - .molstar-btn & { - position: relative; - top: -1px; - } - } - - // Add hover effects, but only for links - a.label { - &:hover, - &:focus { - color: $label-link-hover-color; - text-decoration: none; - cursor: pointer; - } - } - - // Colors - // Contextual variations (linked labels get darker on :hover) - - .label-default { - @include label-variant($label-default-bg); - } - - .label-primary { - @include label-variant($label-primary-bg); - } - - .label-success { - @include label-variant($label-success-bg); - } - - .label-info { - @include label-variant($label-info-bg); - } - - .label-warning { - @include label-variant($label-warning-bg); - } - - .label-danger { - @include label-variant($label-danger-bg); - } \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/mixins.scss b/src/mol-app/skin/bootstrap/mixins.scss deleted file mode 100644 index 94b12fed2..000000000 --- a/src/mol-app/skin/bootstrap/mixins.scss +++ /dev/null @@ -1,40 +0,0 @@ -// Mixins -// -------------------------------------------------- - -// Utilities -// @import "mixins/hide-text"; -@import "mixins/opacity"; -@import "mixins/image"; -@import "mixins/labels"; -// @import "mixins/reset-filter"; -// @import "mixins/resize"; -// @import "mixins/responsive-visibility"; -// @import "mixins/size"; -@import "mixins/tab-focus"; -// @import "mixins/reset-text"; -@import "mixins/text-emphasis"; -@import "mixins/text-overflow"; -@import "mixins/vendor-prefixes"; - -// Components -// @import "mixins/alerts"; -@import "mixins/buttons"; -// @import "mixins/panels"; -// @import "mixins/pagination"; -// @import "mixins/list-group"; -// @import "mixins/nav-divider"; -@import "mixins/forms"; -// @import "mixins/progress-bar"; -// @import "mixins/table-row"; - -// Skins -@import "mixins/background-variant"; -@import "mixins/border-radius"; -// @import "mixins/gradients"; - -// Layout -@import "mixins/clearfix"; -// @import "mixins/center-block"; -// @import "mixins/nav-vertical-align"; -// @import "mixins/grid-framework"; -@import "mixins/grid"; \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/mixins/background-variant.scss b/src/mol-app/skin/bootstrap/mixins/background-variant.scss deleted file mode 100644 index a3044fff2..000000000 --- a/src/mol-app/skin/bootstrap/mixins/background-variant.scss +++ /dev/null @@ -1,12 +0,0 @@ -// Contextual backgrounds - -// [converter] $parent hack -@mixin bg-variant($parent, $color) { - #{$parent} { - background-color: $color; - } - a#{$parent}:hover, - a#{$parent}:focus { - background-color: darken($color, 10%); - } -} \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/mixins/border-radius.scss b/src/mol-app/skin/bootstrap/mixins/border-radius.scss deleted file mode 100644 index 2ba5d06e8..000000000 --- a/src/mol-app/skin/bootstrap/mixins/border-radius.scss +++ /dev/null @@ -1,18 +0,0 @@ -// Single side border-radius - -@mixin border-top-radius($radius) { - border-top-right-radius: $radius; - border-top-left-radius: $radius; -} -@mixin border-right-radius($radius) { - border-bottom-right-radius: $radius; - border-top-right-radius: $radius; -} -@mixin border-bottom-radius($radius) { - border-bottom-right-radius: $radius; - border-bottom-left-radius: $radius; -} -@mixin border-left-radius($radius) { - border-bottom-left-radius: $radius; - border-top-left-radius: $radius; -} \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/mixins/buttons.scss b/src/mol-app/skin/bootstrap/mixins/buttons.scss deleted file mode 100644 index c7be1237a..000000000 --- a/src/mol-app/skin/bootstrap/mixins/buttons.scss +++ /dev/null @@ -1,65 +0,0 @@ -// Button variants -// -// Easily pump out default styles, as well as :hover, :focus, :active, -// and disabled options for all buttons - -@mixin button-variant($color, $background, $border) { - color: $color; - background-color: $background; - border-color: $border; - - &:focus, - &.focus { - color: $color; - background-color: darken($background, 10%); - border-color: darken($border, 25%); - } - &:hover { - color: $color; - background-color: darken($background, 10%); - border-color: darken($border, 12%); - } - &:active, - &.active, - .open > &.dropdown-toggle { - color: $color; - background-color: darken($background, 10%); - border-color: darken($border, 12%); - - &:hover, - &:focus, - &.focus { - color: $color; - background-color: darken($background, 17%); - border-color: darken($border, 25%); - } - } - &:active, - &.active, - .open > &.dropdown-toggle { - background-image: none; - } - &.disabled, - &[disabled], - fieldset[disabled] & { - &:hover, - &:focus, - &.focus { - background-color: $background; - border-color: $border; - } - } - - .badge { - color: $background; - background-color: $color; - } - } - - // Button sizes - @mixin button-size($padding-vertical, $padding-horizontal, $font-size, $line-height, $border-radius) { - padding: $padding-vertical $padding-horizontal; - font-size: $font-size; - line-height: $line-height; - border-radius: $border-radius; - } \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/mixins/clearfix.scss b/src/mol-app/skin/bootstrap/mixins/clearfix.scss deleted file mode 100644 index 12d42afa7..000000000 --- a/src/mol-app/skin/bootstrap/mixins/clearfix.scss +++ /dev/null @@ -1,22 +0,0 @@ -// Clearfix -// -// For modern browsers -// 1. The space content is one way to avoid an Opera bug when the -// contenteditable attribute is included anywhere else in the document. -// Otherwise it causes space to appear at the top and bottom of elements -// that are clearfixed. -// 2. The use of `table` rather than `block` is only necessary if using -// `:before` to contain the top-margins of child elements. -// -// Source: http://nicolasgallagher.com/micro-clearfix-hack/ - -@mixin clearfix() { - &:before, - &:after { - content: " "; // 1 - display: table; // 2 - } - &:after { - clear: both; - } -} \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/mixins/forms.scss b/src/mol-app/skin/bootstrap/mixins/forms.scss deleted file mode 100644 index 2bfe4f00a..000000000 --- a/src/mol-app/skin/bootstrap/mixins/forms.scss +++ /dev/null @@ -1,88 +0,0 @@ -// Form validation states -// -// Used in forms.less to generate the form validation CSS for warnings, errors, -// and successes. - -@mixin molstar-form-control-validation($text-color: #555, $border-color: #ccc, $background-color: #f5f5f5) { - // Color the label and help text - .help-block, - .control-label, - .radio, - .checkbox, - .radio-inline, - .checkbox-inline, - &.radio label, - &.checkbox label, - &.radio-inline label, - &.checkbox-inline label { - color: $text-color; - } - // Set the border and box shadow on specific inputs to match - .molstar-form-control { - border-color: $border-color; - @include box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work - &:focus { - border-color: darken($border-color, 10%); - $shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten($border-color, 20%); - @include box-shadow($shadow); - } - } - // Set validation states also for addons - .input-group-addon { - color: $text-color; - border-color: $border-color; - background-color: $background-color; - } - // Optional feedback icon - .molstar-form-control-feedback { - color: $text-color; - } - } - - - // Form control focus state - // - // Generate a customized focus state and for any input with the specified color, - // which defaults to the `$input-border-focus` variable. - // - // We highly encourage you to not customize the default value, but instead use - // this to tweak colors on an as-needed basis. This aesthetic change is based on - // WebKit's default styles, but applicable to a wider range of browsers. Its - // usability and accessibility should be taken into account with any change. - // - // Example usage: change the default blue border and shadow to white for better - // contrast against a dark gray background. - @mixin molstar-form-control-focus($color: $input-border-focus) { - $color-rgba: rgba(red($color), green($color), blue($color), .6); - &:focus { - border-color: $color; - outline: 0; - @include box-shadow(inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px $color-rgba); - } - } - - // Form control sizing - // - // Relative text size, padding, and border-radii changes for form controls. For - // horizontal sizing, wrap controls in the predefined grid classes. `<select>` - // element gets special love because it's special, and that's a fact! - // [converter] $parent hack - @mixin input-size($parent, $input-height, $padding-vertical, $padding-horizontal, $font-size, $line-height, $border-radius) { - #{$parent} { - height: $input-height; - padding: $padding-vertical $padding-horizontal; - font-size: $font-size; - line-height: $line-height; - border-radius: $border-radius; - } - - select#{$parent} { - height: $input-height; - line-height: $input-height; - } - - textarea#{$parent}, - select[multiple]#{$parent} { - height: auto; - } - } \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/mixins/grid.scss b/src/mol-app/skin/bootstrap/mixins/grid.scss deleted file mode 100644 index 7457a0aba..000000000 --- a/src/mol-app/skin/bootstrap/mixins/grid.scss +++ /dev/null @@ -1,122 +0,0 @@ -// Grid system -// -// Generate semantic grid columns with these mixins. - -// Centered container element -@mixin container-fixed($gutter: $grid-gutter-width) { - margin-right: auto; - margin-left: auto; - padding-left: floor(($gutter / 2)); - padding-right: ceil(($gutter / 2)); - @include clearfix; - } - - // Creates a wrapper for a series of columns - @mixin make-row($gutter: $grid-gutter-width) { - margin-left: ceil(($gutter / -2)); - margin-right: floor(($gutter / -2)); - @include clearfix; - } - - // Generate the extra small columns - @mixin make-xs-column($columns, $gutter: $grid-gutter-width) { - position: relative; - float: left; - width: percentage(($columns / $grid-columns)); - min-height: 1px; - padding-left: ($gutter / 2); - padding-right: ($gutter / 2); - } - @mixin make-xs-column-offset($columns) { - margin-left: percentage(($columns / $grid-columns)); - } - @mixin make-xs-column-push($columns) { - left: percentage(($columns / $grid-columns)); - } - @mixin make-xs-column-pull($columns) { - right: percentage(($columns / $grid-columns)); - } - - // Generate the small columns - @mixin make-sm-column($columns, $gutter: $grid-gutter-width) { - position: relative; - min-height: 1px; - padding-left: ($gutter / 2); - padding-right: ($gutter / 2); - - @media (min-width: $screen-sm-min) { - float: left; - width: percentage(($columns / $grid-columns)); - } - } - @mixin make-sm-column-offset($columns) { - @media (min-width: $screen-sm-min) { - margin-left: percentage(($columns / $grid-columns)); - } - } - @mixin make-sm-column-push($columns) { - @media (min-width: $screen-sm-min) { - left: percentage(($columns / $grid-columns)); - } - } - @mixin make-sm-column-pull($columns) { - @media (min-width: $screen-sm-min) { - right: percentage(($columns / $grid-columns)); - } - } - - // Generate the medium columns - @mixin make-md-column($columns, $gutter: $grid-gutter-width) { - position: relative; - min-height: 1px; - padding-left: ($gutter / 2); - padding-right: ($gutter / 2); - - @media (min-width: $screen-md-min) { - float: left; - width: percentage(($columns / $grid-columns)); - } - } - @mixin make-md-column-offset($columns) { - @media (min-width: $screen-md-min) { - margin-left: percentage(($columns / $grid-columns)); - } - } - @mixin make-md-column-push($columns) { - @media (min-width: $screen-md-min) { - left: percentage(($columns / $grid-columns)); - } - } - @mixin make-md-column-pull($columns) { - @media (min-width: $screen-md-min) { - right: percentage(($columns / $grid-columns)); - } - } - - // Generate the large columns - @mixin make-lg-column($columns, $gutter: $grid-gutter-width) { - position: relative; - min-height: 1px; - padding-left: ($gutter / 2); - padding-right: ($gutter / 2); - - @media (min-width: $screen-lg-min) { - float: left; - width: percentage(($columns / $grid-columns)); - } - } - @mixin make-lg-column-offset($columns) { - @media (min-width: $screen-lg-min) { - margin-left: percentage(($columns / $grid-columns)); - } - } - @mixin make-lg-column-push($columns) { - @media (min-width: $screen-lg-min) { - left: percentage(($columns / $grid-columns)); - } - } - @mixin make-lg-column-pull($columns) { - @media (min-width: $screen-lg-min) { - right: percentage(($columns / $grid-columns)); - } - } \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/mixins/image.scss b/src/mol-app/skin/bootstrap/mixins/image.scss deleted file mode 100644 index 608cad5a5..000000000 --- a/src/mol-app/skin/bootstrap/mixins/image.scss +++ /dev/null @@ -1,33 +0,0 @@ -// Image Mixins -// - Responsive image -// - Retina image - - -// Responsive image -// -// Keep images from scaling beyond the width of their parents. -@mixin img-responsive($display: block) { - display: $display; - max-width: 100%; // Part 1: Set a maximum relative to the parent - height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching -} - - -// Retina image -// -// Short retina mixin for setting background-image and -size. Note that the -// spelling of `min--moz-device-pixel-ratio` is intentional. -@mixin img-retina($file-1x, $file-2x, $width-1x, $height-1x) { - background-image: url(if($bootstrap-sass-asset-helper, twbs-image-path("#{$file-1x}"), "#{$file-1x}")); - - @media - only screen and (-webkit-min-device-pixel-ratio: 2), - only screen and ( min--moz-device-pixel-ratio: 2), - only screen and ( -o-min-device-pixel-ratio: 2/1), - only screen and ( min-device-pixel-ratio: 2), - only screen and ( min-resolution: 192dpi), - only screen and ( min-resolution: 2dppx) { - background-image: url(if($bootstrap-sass-asset-helper, twbs-image-path("#{$file-2x}"), "#{$file-2x}")); - background-size: $width-1x $height-1x; - } -} \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/mixins/labels.scss b/src/mol-app/skin/bootstrap/mixins/labels.scss deleted file mode 100644 index 1353b098d..000000000 --- a/src/mol-app/skin/bootstrap/mixins/labels.scss +++ /dev/null @@ -1,12 +0,0 @@ -// Labels - -@mixin label-variant($color) { - background-color: $color; - - &[href] { - &:hover, - &:focus { - background-color: darken($color, 10%); - } - } -} \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/mixins/opacity.scss b/src/mol-app/skin/bootstrap/mixins/opacity.scss deleted file mode 100644 index f04356c6b..000000000 --- a/src/mol-app/skin/bootstrap/mixins/opacity.scss +++ /dev/null @@ -1,8 +0,0 @@ -// Opacity - -@mixin opacity($opacity) { - opacity: $opacity; - // IE8 filter - $opacity-ie: ($opacity * 100); - filter: alpha(opacity=$opacity-ie); -} \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/mixins/tab-focus.scss b/src/mol-app/skin/bootstrap/mixins/tab-focus.scss deleted file mode 100644 index 414babf68..000000000 --- a/src/mol-app/skin/bootstrap/mixins/tab-focus.scss +++ /dev/null @@ -1,9 +0,0 @@ -// WebKit-style focus - -@mixin tab-focus() { - // Default - outline: thin dotted; - // WebKit - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/mixins/text-emphasis.scss b/src/mol-app/skin/bootstrap/mixins/text-emphasis.scss deleted file mode 100644 index dddb9ae25..000000000 --- a/src/mol-app/skin/bootstrap/mixins/text-emphasis.scss +++ /dev/null @@ -1,12 +0,0 @@ -// Typography - -// [converter] $parent hack -@mixin text-emphasis-variant($parent, $color) { - #{$parent} { - color: $color; - } - a#{$parent}:hover, - a#{$parent}:focus { - color: darken($color, 10%); - } -} \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/mixins/text-overflow.scss b/src/mol-app/skin/bootstrap/mixins/text-overflow.scss deleted file mode 100644 index b6ac5f707..000000000 --- a/src/mol-app/skin/bootstrap/mixins/text-overflow.scss +++ /dev/null @@ -1,8 +0,0 @@ -// Text overflow -// Requires inline-block or block for proper styling - -@mixin text-overflow() { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/mixins/vendor-prefixes.scss b/src/mol-app/skin/bootstrap/mixins/vendor-prefixes.scss deleted file mode 100644 index b657e7a26..000000000 --- a/src/mol-app/skin/bootstrap/mixins/vendor-prefixes.scss +++ /dev/null @@ -1,222 +0,0 @@ -// Vendor Prefixes -// -// All vendor mixins are deprecated as of v3.2.0 due to the introduction of -// Autoprefixer in our Gruntfile. They have been removed in v4. - -// - Animations -// - Backface visibility -// - Box shadow -// - Box sizing -// - Content columns -// - Hyphens -// - Placeholder text -// - Transformations -// - Transitions -// - User Select - - -// Animations -@mixin animation($animation) { - -webkit-animation: $animation; - -o-animation: $animation; - animation: $animation; - } - @mixin animation-name($name) { - -webkit-animation-name: $name; - animation-name: $name; - } - @mixin animation-duration($duration) { - -webkit-animation-duration: $duration; - animation-duration: $duration; - } - @mixin animation-timing-function($timing-function) { - -webkit-animation-timing-function: $timing-function; - animation-timing-function: $timing-function; - } - @mixin animation-delay($delay) { - -webkit-animation-delay: $delay; - animation-delay: $delay; - } - @mixin animation-iteration-count($iteration-count) { - -webkit-animation-iteration-count: $iteration-count; - animation-iteration-count: $iteration-count; - } - @mixin animation-direction($direction) { - -webkit-animation-direction: $direction; - animation-direction: $direction; - } - @mixin animation-fill-mode($fill-mode) { - -webkit-animation-fill-mode: $fill-mode; - animation-fill-mode: $fill-mode; - } - - // Backface visibility - // Prevent browsers from flickering when using CSS 3D transforms. - // Default value is `visible`, but can be changed to `hidden` - - @mixin backface-visibility($visibility) { - -webkit-backface-visibility: $visibility; - -moz-backface-visibility: $visibility; - backface-visibility: $visibility; - } - - // Drop shadows - // - // Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's - // supported browsers that have box shadow capabilities now support it. - - @mixin box-shadow($shadow...) { - -webkit-box-shadow: $shadow; // iOS <4.3 & Android <4.1 - box-shadow: $shadow; - } - - // Box sizing - @mixin box-sizing($boxmodel) { - -webkit-box-sizing: $boxmodel; - -moz-box-sizing: $boxmodel; - box-sizing: $boxmodel; - } - - // CSS3 Content Columns - @mixin content-columns($column-count, $column-gap: $grid-gutter-width) { - -webkit-column-count: $column-count; - -moz-column-count: $column-count; - column-count: $column-count; - -webkit-column-gap: $column-gap; - -moz-column-gap: $column-gap; - column-gap: $column-gap; - } - - // Optional hyphenation - @mixin hyphens($mode: auto) { - word-wrap: break-word; - -webkit-hyphens: $mode; - -moz-hyphens: $mode; - -ms-hyphens: $mode; // IE10+ - -o-hyphens: $mode; - hyphens: $mode; - } - - // Placeholder text - @mixin placeholder($color: $input-color-placeholder) { - // Firefox - &::-moz-placeholder { - color: $color; - opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526 - } - &:-ms-input-placeholder { color: $color; } // Internet Explorer 10+ - &::-webkit-input-placeholder { color: $color; } // Safari and Chrome - } - - // Transformations - @mixin scale($ratio...) { - -webkit-transform: scale($ratio); - -ms-transform: scale($ratio); // IE9 only - -o-transform: scale($ratio); - transform: scale($ratio); - } - - @mixin scaleX($ratio) { - -webkit-transform: scaleX($ratio); - -ms-transform: scaleX($ratio); // IE9 only - -o-transform: scaleX($ratio); - transform: scaleX($ratio); - } - @mixin scaleY($ratio) { - -webkit-transform: scaleY($ratio); - -ms-transform: scaleY($ratio); // IE9 only - -o-transform: scaleY($ratio); - transform: scaleY($ratio); - } - @mixin skew($x, $y) { - -webkit-transform: skewX($x) skewY($y); - -ms-transform: skewX($x) skewY($y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+ - -o-transform: skewX($x) skewY($y); - transform: skewX($x) skewY($y); - } - @mixin translate($x, $y) { - -webkit-transform: translate($x, $y); - -ms-transform: translate($x, $y); // IE9 only - -o-transform: translate($x, $y); - transform: translate($x, $y); - } - @mixin translate3d($x, $y, $z) { - -webkit-transform: translate3d($x, $y, $z); - transform: translate3d($x, $y, $z); - } - @mixin rotate($degrees) { - -webkit-transform: rotate($degrees); - -ms-transform: rotate($degrees); // IE9 only - -o-transform: rotate($degrees); - transform: rotate($degrees); - } - @mixin rotateX($degrees) { - -webkit-transform: rotateX($degrees); - -ms-transform: rotateX($degrees); // IE9 only - -o-transform: rotateX($degrees); - transform: rotateX($degrees); - } - @mixin rotateY($degrees) { - -webkit-transform: rotateY($degrees); - -ms-transform: rotateY($degrees); // IE9 only - -o-transform: rotateY($degrees); - transform: rotateY($degrees); - } - @mixin perspective($perspective) { - -webkit-perspective: $perspective; - -moz-perspective: $perspective; - perspective: $perspective; - } - @mixin perspective-origin($perspective) { - -webkit-perspective-origin: $perspective; - -moz-perspective-origin: $perspective; - perspective-origin: $perspective; - } - @mixin transform-origin($origin) { - -webkit-transform-origin: $origin; - -moz-transform-origin: $origin; - -ms-transform-origin: $origin; // IE9 only - transform-origin: $origin; - } - - - // Transitions - - @mixin transition($transition...) { - -webkit-transition: $transition; - -o-transition: $transition; - transition: $transition; - } - @mixin transition-property($transition-property...) { - -webkit-transition-property: $transition-property; - transition-property: $transition-property; - } - @mixin transition-delay($transition-delay) { - -webkit-transition-delay: $transition-delay; - transition-delay: $transition-delay; - } - @mixin transition-duration($transition-duration...) { - -webkit-transition-duration: $transition-duration; - transition-duration: $transition-duration; - } - @mixin transition-timing-function($timing-function) { - -webkit-transition-timing-function: $timing-function; - transition-timing-function: $timing-function; - } - @mixin transition-transform($transition...) { - -webkit-transition: -webkit-transform $transition; - -moz-transition: -moz-transform $transition; - -o-transition: -o-transform $transition; - transition: transform $transition; - } - - - // User select - // For selecting text on the page - - @mixin user-select($select) { - -webkit-user-select: $select; - -moz-user-select: $select; - -ms-user-select: $select; // IE10+ - user-select: $select; - } \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/normalize.scss b/src/mol-app/skin/bootstrap/normalize.scss deleted file mode 100644 index 7caf32b8c..000000000 --- a/src/mol-app/skin/bootstrap/normalize.scss +++ /dev/null @@ -1,424 +0,0 @@ -/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ - -// -// 1. Set default font family to sans-serif. -// 2. Prevent iOS and IE text size adjust after device orientation change, -// without disabling user zoom. -// - -html { - font-family: sans-serif; // 1 - -ms-text-size-adjust: 100%; // 2 - -webkit-text-size-adjust: 100%; // 2 - } - - // - // Remove default margin. - // - - body { - margin: 0; - } - - // HTML5 display definitions - // ========================================================================== - - // - // Correct `block` display not defined for any HTML5 element in IE 8/9. - // Correct `block` display not defined for `details` or `summary` in IE 10/11 - // and Firefox. - // Correct `block` display not defined for `main` in IE 11. - // - - article, - aside, - details, - figcaption, - figure, - footer, - header, - hgroup, - main, - menu, - nav, - section, - summary { - display: block; - } - - // - // 1. Correct `inline-block` display not defined in IE 8/9. - // 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. - // - - audio, - canvas, - progress, - video { - display: inline-block; // 1 - vertical-align: baseline; // 2 - } - - // - // Prevent modern browsers from displaying `audio` without controls. - // Remove excess height in iOS 5 devices. - // - - audio:not([controls]) { - display: none; - height: 0; - } - - // - // Address `[hidden]` styling not present in IE 8/9/10. - // Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22. - // - - [hidden], - template { - display: none; - } - - // Links - // ========================================================================== - - // - // Remove the gray background color from active links in IE 10. - // - - a { - background-color: transparent; - } - - // - // Improve readability of focused elements when they are also in an - // active/hover state. - // - - a:active, - a:hover { - outline: 0; - } - - // Text-level semantics - // ========================================================================== - - // - // Address styling not present in IE 8/9/10/11, Safari, and Chrome. - // - - abbr[title] { - border-bottom: 1px dotted; - } - - // - // Address style set to `bolder` in Firefox 4+, Safari, and Chrome. - // - - b, - strong { - font-weight: bold; - } - - // - // Address styling not present in Safari and Chrome. - // - - dfn { - font-style: italic; - } - - // - // Address variable `h1` font-size and margin within `section` and `article` - // contexts in Firefox 4+, Safari, and Chrome. - // - - h1 { - font-size: 2em; - margin: 0.67em 0; - } - - // - // Address styling not present in IE 8/9. - // - - mark { - background: #ff0; - color: #000; - } - - // - // Address inconsistent and variable font size in all browsers. - // - - small { - font-size: 80%; - } - - // - // Prevent `sub` and `sup` affecting `line-height` in all browsers. - // - - sub, - sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; - } - - sup { - top: -0.5em; - } - - sub { - bottom: -0.25em; - } - - // Embedded content - // ========================================================================== - - // - // Remove border when inside `a` element in IE 8/9/10. - // - - img { - border: 0; - } - - // - // Correct overflow not hidden in IE 9/10/11. - // - - svg:not(:root) { - overflow: hidden; - } - - // Grouping content - // ========================================================================== - - // - // Address margin not present in IE 8/9 and Safari. - // - - figure { - margin: 1em 40px; - } - - // - // Address differences between Firefox and other browsers. - // - - hr { - box-sizing: content-box; - height: 0; - } - - // - // Contain overflow in all browsers. - // - - pre { - overflow: auto; - } - - // - // Address odd `em`-unit font size rendering in all browsers. - // - - code, - kbd, - pre, - samp { - font-family: monospace, monospace; - font-size: 1em; - } - - // Forms - // ========================================================================== - - // - // Known limitation: by default, Chrome and Safari on OS X allow very limited - // styling of `select`, unless a `border` property is set. - // - - // - // 1. Correct color not being inherited. - // Known issue: affects color of disabled elements. - // 2. Correct font properties not being inherited. - // 3. Address margins set differently in Firefox 4+, Safari, and Chrome. - // - - button, - input, - optgroup, - select, - textarea { - color: inherit; // 1 - font: inherit; // 2 - margin: 0; // 3 - } - - // - // Address `overflow` set to `hidden` in IE 8/9/10/11. - // - - button { - overflow: visible; - } - - // - // Address inconsistent `text-transform` inheritance for `button` and `select`. - // All other form control elements do not inherit `text-transform` values. - // Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. - // Correct `select` style inheritance in Firefox. - // - - button, - select { - text-transform: none; - } - - // - // 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` - // and `video` controls. - // 2. Correct inability to style clickable `input` types in iOS. - // 3. Improve usability and consistency of cursor style between image-type - // `input` and others. - // - - button, - html input[type="button"], // 1 - input[type="reset"], - input[type="submit"] { - -webkit-appearance: button; // 2 - cursor: pointer; // 3 - } - - // - // Re-set default cursor for disabled elements. - // - - button[disabled], - html input[disabled] { - cursor: default; - } - - // - // Remove inner padding and border in Firefox 4+. - // - - button::-moz-focus-inner, - input::-moz-focus-inner { - border: 0; - padding: 0; - } - - // - // Address Firefox 4+ setting `line-height` on `input` using `!important` in - // the UA stylesheet. - // - - input { - line-height: normal; - } - - // - // It's recommended that you don't attempt to style these elements. - // Firefox's implementation doesn't respect box-sizing, padding, or width. - // - // 1. Address box sizing set to `content-box` in IE 8/9/10. - // 2. Remove excess padding in IE 8/9/10. - // - - input[type="checkbox"], - input[type="radio"] { - box-sizing: border-box; // 1 - padding: 0; // 2 - } - - // - // Fix the cursor style for Chrome's increment/decrement buttons. For certain - // `font-size` values of the `input`, it causes the cursor style of the - // decrement button to change from `default` to `text`. - // - - input[type="number"]::-webkit-inner-spin-button, - input[type="number"]::-webkit-outer-spin-button { - height: auto; - } - - // - // 1. Address `appearance` set to `searchfield` in Safari and Chrome. - // 2. Address `box-sizing` set to `border-box` in Safari and Chrome. - // - - input[type="search"] { - -webkit-appearance: textfield; // 1 - box-sizing: content-box; //2 - } - - // - // Remove inner padding and search cancel button in Safari and Chrome on OS X. - // Safari (but not Chrome) clips the cancel button when the search input has - // padding (and `textfield` appearance). - // - - input[type="search"]::-webkit-search-cancel-button, - input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; - } - - // - // Define consistent border, margin, and padding. - // - - fieldset { - border: 1px solid #c0c0c0; - margin: 0 2px; - padding: 0.35em 0.625em 0.75em; - } - - // - // 1. Correct `color` not being inherited in IE 8/9/10/11. - // 2. Remove padding so people aren't caught out if they zero out fieldsets. - // - - legend { - border: 0; // 1 - padding: 0; // 2 - } - - // - // Remove default vertical scrollbar in IE 8/9/10/11. - // - - textarea { - overflow: auto; - } - - // - // Don't inherit the `font-weight` (applied by a rule above). - // NOTE: the default cannot safely be changed in Chrome and Safari on OS X. - // - - optgroup { - font-weight: bold; - } - - // Tables - // ========================================================================== - - // - // Remove most spacing between table cells. - // - - table { - border-collapse: collapse; - border-spacing: 0; - } - - td, - th { - padding: 0; - } \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/scaffolding.scss b/src/mol-app/skin/bootstrap/scaffolding.scss deleted file mode 100644 index 556b6acdf..000000000 --- a/src/mol-app/skin/bootstrap/scaffolding.scss +++ /dev/null @@ -1,161 +0,0 @@ -// -// Scaffolding -// -------------------------------------------------- - - -// Reset the box-sizing -// -// Heads up! This reset may cause conflicts with some third-party widgets. -// For recommendations on resolving such conflicts, see -// http://getbootstrap.com/getting-started/#third-box-sizing -* { - @include box-sizing(border-box); - } - *:before, - *:after { - @include box-sizing(border-box); - } - - - // Body reset - - html { - font-size: 10px; - -webkit-tap-highlight-color: rgba(0,0,0,0); - } - - body { - font-family: $font-family-base; - font-size: $font-size-base; - line-height: $line-height-base; - color: $text-color; - background-color: $body-bg; - } - - // Reset fonts for relevant elements - input, - button, - select, - textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; - } - - - // Links - - a { - color: $link-color; - text-decoration: none; - - &:hover, - &:focus { - color: $link-hover-color; - text-decoration: $link-hover-decoration; - } - - &:focus { - @include tab-focus; - } - } - - - // Figures - // - // We reset this here because previously Normalize had no `figure` margins. This - // ensures we don't break anyone's use of the element. - - figure { - margin: 0; - } - - - // Images - - img { - vertical-align: middle; - } - - // Responsive images (ensure images don't scale beyond their parents) - .img-responsive { - @include img-responsive; - } - - // Rounded corners - .img-rounded { - border-radius: $border-radius-large; - } - - // Image thumbnails - // - // Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`. - .img-thumbnail { - padding: $thumbnail-padding; - line-height: $line-height-base; - background-color: $thumbnail-bg; - border: 1px solid $thumbnail-border; - border-radius: $thumbnail-border-radius; - @include transition(all .2s ease-in-out); - - // Keep them at most 100% wide - @include img-responsive(inline-block); - } - - // Perfect circle - .img-circle { - border-radius: 50%; // set radius in percents - } - - - // Horizontal rules - - hr { - margin-top: $line-height-computed; - margin-bottom: $line-height-computed; - border: 0; - border-top: 1px solid $hr-border; - } - - - // Only display content to screen readers - // - // See: http://a11yproject.com/posts/how-to-hide-content/ - - .sr-only { - position: absolute; - width: 1px; - height: 1px; - margin: -1px; - padding: 0; - overflow: hidden; - clip: rect(0,0,0,0); - border: 0; - } - - // Use in conjunction with .sr-only to only display content when it's focused. - // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 - // Credit: HTML5 Boilerplate - - .sr-only-focusable { - &:active, - &:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; - } - } - - - // iOS "clickable elements" fix for role="button" - // - // Fixes "clickability" issue (and more generally, the firing of events such as focus as well) - // for traditionally non-focusable elements with role="button" - // see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile - - [role="button"] { - cursor: pointer; - } \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/type.scss b/src/mol-app/skin/bootstrap/type.scss deleted file mode 100644 index 92c0b63ca..000000000 --- a/src/mol-app/skin/bootstrap/type.scss +++ /dev/null @@ -1,298 +0,0 @@ -// -// Typography -// -------------------------------------------------- - - -// Headings -// ------------------------- - -h1, h2, h3, h4, h5, h6, -.h1, .h2, .h3, .h4, .h5, .h6 { - font-family: $headings-font-family; - font-weight: $headings-font-weight; - line-height: $headings-line-height; - color: $headings-color; - - small, - .small { - font-weight: normal; - line-height: 1; - color: $headings-small-color; - } -} - -h1, .h1, -h2, .h2, -h3, .h3 { - margin-top: $line-height-computed; - margin-bottom: ($line-height-computed / 2); - - small, - .small { - font-size: 65%; - } -} -h4, .h4, -h5, .h5, -h6, .h6 { - margin-top: ($line-height-computed / 2); - margin-bottom: ($line-height-computed / 2); - - small, - .small { - font-size: 75%; - } -} - -h1, .h1 { font-size: $font-size-h1; } -h2, .h2 { font-size: $font-size-h2; } -h3, .h3 { font-size: $font-size-h3; } -h4, .h4 { font-size: $font-size-h4; } -h5, .h5 { font-size: $font-size-h5; } -h6, .h6 { font-size: $font-size-h6; } - - -// Body text -// ------------------------- - -p { - margin: 0 0 ($line-height-computed / 2); -} - -.lead { - margin-bottom: $line-height-computed; - font-size: floor(($font-size-base * 1.15)); - font-weight: 300; - line-height: 1.4; - - @media (min-width: $screen-sm-min) { - font-size: ($font-size-base * 1.5); - } -} - - -// Emphasis & misc -// ------------------------- - -// Ex: (12px small font / 14px base font) * 100% = about 85% -small, -.small { - font-size: floor((100% * $font-size-small / $font-size-base)); -} - -mark, -.mark { - background-color: $state-warning-bg; - padding: .2em; -} - -// Alignment -.text-left { text-align: left; } -.text-right { text-align: right; } -.text-center { text-align: center; } -.text-justify { text-align: justify; } -.text-nowrap { white-space: nowrap; } - -// Transformation -.text-lowercase { text-transform: lowercase; } -.text-uppercase { text-transform: uppercase; } -.text-capitalize { text-transform: capitalize; } - -// Contextual colors -.text-muted { - color: $text-muted; -} - -@include text-emphasis-variant('.text-primary', $brand-primary); - -@include text-emphasis-variant('.text-success', $state-success-text); - -@include text-emphasis-variant('.text-info', $state-info-text); - -@include text-emphasis-variant('.text-warning', $state-warning-text); - -@include text-emphasis-variant('.text-danger', $state-danger-text); - -// Contextual backgrounds -// For now we'll leave these alongside the text classes until v4 when we can -// safely shift things around (per SemVer rules). -.bg-primary { - // Given the contrast here, this is the only class to have its color inverted - // automatically. - color: #fff; -} -@include bg-variant('.bg-primary', $brand-primary); - -@include bg-variant('.bg-success', $state-success-bg); - -@include bg-variant('.bg-info', $state-info-bg); - -@include bg-variant('.bg-warning', $state-warning-bg); - -@include bg-variant('.bg-danger', $state-danger-bg); - - -// Page header -// ------------------------- - -.page-header { - padding-bottom: (($line-height-computed / 2) - 1); - margin: ($line-height-computed * 2) 0 $line-height-computed; - border-bottom: 1px solid $page-header-border-color; -} - - -// Lists -// ------------------------- - -// Unordered and Ordered lists -ul, -ol { - margin-top: 0; - margin-bottom: ($line-height-computed / 2); - ul, - ol { - margin-bottom: 0; - } -} - -// List options - -// [converter] extracted from `.lm-list-unstyled` for libsass compatibility -@mixin lm-list-unstyled { - padding-left: 0; - list-style: none; -} -// [converter] extracted as `@mixin lm-list-unstyled` for libsass compatibility -.lm-list-unstyled { - @include lm-list-unstyled; -} - - -// Inline turns list items into inline-block -.list-inline { - @include lm-list-unstyled; - margin-left: -5px; - - > li { - display: inline-block; - padding-left: 5px; - padding-right: 5px; - } -} - -// Description Lists -dl { - margin-top: 0; // Remove browser default - margin-bottom: $line-height-computed; -} -dt, -dd { - line-height: $line-height-base; -} -dt { - font-weight: bold; -} -dd { - margin-left: 0; // Undo browser default -} - -// Horizontal description lists -// -// Defaults to being stacked without any of the below styles applied, until the -// grid breakpoint is reached (default of ~768px). - -.dl-horizontal { - dd { - @include clearfix; // Clear the floated `dt` if an empty `dd` is present - } - - @media (min-width: $dl-horizontal-breakpoint) { - dt { - float: left; - width: ($dl-horizontal-offset - 20); - clear: left; - text-align: right; - @include text-overflow; - } - dd { - margin-left: $dl-horizontal-offset; - } - } -} - - -// Misc -// ------------------------- - -// Abbreviations and acronyms -abbr[title], -// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257 -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted $abbr-border-color; -} -.initialism { - font-size: 90%; - @extend .text-uppercase; -} - -// Blockquotes -blockquote { - padding: ($line-height-computed / 2) $line-height-computed; - margin: 0 0 $line-height-computed; - font-size: $blockquote-font-size; - border-left: 5px solid $blockquote-border-color; - - p, - ul, - ol { - &:last-child { - margin-bottom: 0; - } - } - - // Note: Deprecated small and .small as of v3.1.0 - // Context: https://github.com/twbs/bootstrap/issues/11660 - footer, - small, - .small { - display: block; - font-size: 80%; // back to default font-size - line-height: $line-height-base; - color: $blockquote-small-color; - - &:before { - content: '\2014 \00A0'; // em dash, nbsp - } - } -} - -// Opposite alignment of blockquote -// -// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0. -.blockquote-reverse, -blockquote.pull-right { - padding-right: 15px; - padding-left: 0; - border-right: 5px solid $blockquote-border-color; - border-left: 0; - text-align: right; - - // Account for citation - footer, - small, - .small { - &:before { content: ''; } - &:after { - content: '\00A0 \2014'; // nbsp, em dash - } - } -} - -// Addresses -address { - margin-bottom: $line-height-computed; - font-style: normal; - line-height: $line-height-base; -} \ No newline at end of file diff --git a/src/mol-app/skin/bootstrap/variables.scss b/src/mol-app/skin/bootstrap/variables.scss deleted file mode 100644 index ead0db3c4..000000000 --- a/src/mol-app/skin/bootstrap/variables.scss +++ /dev/null @@ -1,353 +0,0 @@ -//== Colors -// -//## Gray and brand colors for use across Bootstrap. - -$gray-base: #000 !default; -$gray-darker: lighten($gray-base, 13.5%) !default; // #222 -$gray-dark: lighten($gray-base, 20%) !default; // #333 -$gray: lighten($gray-base, 33.5%) !default; // #555 -$gray-light: lighten($gray-base, 46.7%) !default; // #777 -$gray-lighter: lighten($gray-base, 93.5%) !default; // #eee - -$brand-primary: darken(#428bca, 6.5%) !default; // #337ab7 -$brand-success: #5cb85c !default; -$brand-info: #5bc0de !default; -$brand-warning: #f0ad4e !default; -$brand-danger: #d9534f !default; - - -//== Scaffolding -// -//## Settings for some of the most global styles. - -//** Background color for `<body>`. -$body-bg: #fff !default; -//** Global text color on `<body>`. -$text-color: $gray-dark !default; - -//** Global textual link color. -$link-color: $brand-primary !default; -//** Link hover color set via `darken()` function. -$link-hover-color: darken($link-color, 15%) !default; -//** Link hover decoration. -$link-hover-decoration: underline !default; - - -//== Typography -// -//## Font, line-height, and color for body text, headings, and more. - -$font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif !default; -$font-family-serif: Georgia, "Times New Roman", Times, serif !default; -//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`. -$font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace !default; -$font-family-base: $font-family-sans-serif !default; - -$font-size-base: 14px !default; -$font-size-large: ceil(($font-size-base * 1.25)) !default; // ~18px -$font-size-small: ceil(($font-size-base * 0.85)) !default; // ~12px - -$font-size-h1: floor(($font-size-base * 2.6)) !default; // ~36px -$font-size-h2: floor(($font-size-base * 2.15)) !default; // ~30px -$font-size-h3: ceil(($font-size-base * 1.7)) !default; // ~24px -$font-size-h4: ceil(($font-size-base * 1.25)) !default; // ~18px -$font-size-h5: $font-size-base !default; -$font-size-h6: ceil(($font-size-base * 0.85)) !default; // ~12px - -//** Unit-less `line-height` for use in components like buttons. -$line-height-base: 1.428571429 !default; // 20/14 -//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc. -$line-height-computed: floor(($font-size-base * $line-height-base)) !default; // ~20px - -//** By default, this inherits from the `<body>`. -$headings-font-family: inherit !default; -$headings-font-weight: 500 !default; -$headings-line-height: 1.1 !default; -$headings-color: inherit !default; - - -//== Components -// -//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start). - -$padding-base-vertical: 6px !default; -$padding-base-horizontal: 12px !default; - -$padding-large-vertical: 10px !default; -$padding-large-horizontal: 16px !default; - -$padding-small-vertical: 5px !default; -$padding-small-horizontal: 10px !default; - -$padding-xs-vertical: 1px !default; -$padding-xs-horizontal: 5px !default; - -$line-height-large: 1.3333333 !default; // extra decimals for Win 8.1 Chrome -$line-height-small: 1.5 !default; - -$border-radius-base: 4px !default; -$border-radius-large: 6px !default; -$border-radius-small: 3px !default; - -//** Global color for active items (e.g., navs or dropdowns). -$component-active-color: #fff !default; -//** Global background color for active items (e.g., navs or dropdowns). -$component-active-bg: $brand-primary !default; - -//** Width of the `border` for generating carets that indicator dropdowns. -$caret-width-base: 4px !default; -//** Carets increase slightly in size for larger components. -$caret-width-large: 5px !default; - - -//== Buttons -// -//## For each of Bootstrap's buttons, define text, background and border color. - -$molstar-btn-font-weight: normal !default; - -$molstar-btn-default-color: #333 !default; -$molstar-btn-default-bg: #fff !default; -$molstar-btn-default-border: #ccc !default; - -$molstar-btn-primary-color: #fff !default; -$molstar-btn-primary-bg: $brand-primary !default; -$molstar-btn-primary-border: darken($molstar-btn-primary-bg, 5%) !default; - -$molstar-btn-success-color: #fff !default; -$molstar-btn-success-bg: $brand-success !default; -$molstar-btn-success-border: darken($molstar-btn-success-bg, 5%) !default; - -$molstar-btn-info-color: #fff !default; -$molstar-btn-info-bg: $brand-info !default; -$molstar-btn-info-border: darken($molstar-btn-info-bg, 5%) !default; - -$molstar-btn-warning-color: #fff !default; -$molstar-btn-warning-bg: $brand-warning !default; -$molstar-btn-warning-border: darken($molstar-btn-warning-bg, 5%) !default; - -$molstar-btn-danger-color: #fff !default; -$molstar-btn-danger-bg: $brand-danger !default; -$molstar-btn-danger-border: darken($molstar-btn-danger-bg, 5%) !default; - -$molstar-btn-link-disabled-color: $gray-light !default; - -// Allows for customizing button radius independently from global border radius -$molstar-btn-border-radius-base: $border-radius-base !default; -$molstar-btn-border-radius-large: $border-radius-large !default; -$molstar-btn-border-radius-small: $border-radius-small !default; - - -//== Media queries breakpoints -// -//## Define the breakpoints at which your layout will change, adapting to different screen sizes. - -// Extra small screen / phone -//** Deprecated `$screen-xs` as of v3.0.1 -$screen-xs: 480px !default; -//** Deprecated `$screen-xs-min` as of v3.2.0 -$screen-xs-min: $screen-xs !default; -//** Deprecated `$screen-phone` as of v3.0.1 -$screen-phone: $screen-xs-min !default; - -// Small screen / tablet -//** Deprecated `$screen-sm` as of v3.0.1 -$screen-sm: 768px !default; -$screen-sm-min: $screen-sm !default; -//** Deprecated `$screen-tablet` as of v3.0.1 -$screen-tablet: $screen-sm-min !default; - -// Medium screen / desktop -//** Deprecated `$screen-md` as of v3.0.1 -$screen-md: 992px !default; -$screen-md-min: $screen-md !default; -//** Deprecated `$screen-desktop` as of v3.0.1 -$screen-desktop: $screen-md-min !default; - -// Large screen / wide desktop -//** Deprecated `$screen-lg` as of v3.0.1 -$screen-lg: 1200px !default; -$screen-lg-min: $screen-lg !default; -//** Deprecated `$screen-lg-desktop` as of v3.0.1 -$screen-lg-desktop: $screen-lg-min !default; - -// So media queries don't overlap when required, provide a maximum -$screen-xs-max: ($screen-sm-min - 1) !default; -$screen-sm-max: ($screen-md-min - 1) !default; -$screen-md-max: ($screen-lg-min - 1) !default; - - -//== Grid system -// -//## Define your custom responsive grid. - -//** Number of columns in the grid. -$grid-columns: 12 !default; -//** Padding between columns. Gets divided in half for the left and right. -$grid-gutter-width: 30px !default; -// Navbar collapse -//** Point at which the navbar becomes uncollapsed. -$grid-float-breakpoint: $screen-sm-min !default; -//** Point at which the navbar begins collapsing. -$grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default; - - -//== Forms -// -//## - -//** `<input>` background color -$input-bg: #fff !default; -//** `<input disabled>` background color -$input-bg-disabled: $gray-lighter !default; - -//** Text color for `<input>`s -$input-color: $gray !default; -//** `<input>` border color -$input-border: #ccc !default; - -// TODO: Rename `$input-border-radius` to `$input-border-radius-base` in v4 -//** Default `.lm-form-control` border radius -// This has no effect on `<select>`s in some browsers, due to the limited stylability of `<select>`s in CSS. -$input-border-radius: $border-radius-base !default; -//** Large `.lm-form-control` border radius -$input-border-radius-large: $border-radius-large !default; -//** Small `.lm-form-control` border radius -$input-border-radius-small: $border-radius-small !default; - -//** Border color for inputs on focus -$input-border-focus: #66afe9 !default; - -//** Placeholder text color -$input-color-placeholder: #999 !default; - -//** Default `.lm-form-control` height -$input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2) !default; -//** Large `.lm-form-control` height -$input-height-large: (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2) !default; -//** Small `.lm-form-control` height -$input-height-small: (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2) !default; - -//** `.form-group` margin -$form-group-margin-bottom: 15px !default; - -$legend-color: $gray-dark !default; -$legend-border-color: #e5e5e5 !default; - -//** Background color for textual input addons -$input-group-addon-bg: $gray-lighter !default; -//** Border color for textual input addons -$input-group-addon-border-color: $input-border !default; - -//** Disabled cursor for form controls and buttons. -$cursor-disabled: not-allowed !default; - - -//== Thumbnails -// -//## - -//** Padding around the thumbnail image -$thumbnail-padding: 4px !default; -//** Thumbnail background color -$thumbnail-bg: $body-bg !default; -//** Thumbnail border color -$thumbnail-border: #ddd !default; -//** Thumbnail border radius -$thumbnail-border-radius: $border-radius-base !default; - -//** Custom text color for thumbnail captions -$thumbnail-caption-color: $text-color !default; -//** Padding around the thumbnail caption -$thumbnail-caption-padding: 9px !default; - - -//== Type -// -//## - -//** Horizontal offset for forms and lists. -$component-offset-horizontal: 180px !default; -//** Text muted color -$text-muted: $gray-light !default; -//** Abbreviations and acronyms border color -$abbr-border-color: $gray-light !default; -//** Headings small color -$headings-small-color: $gray-light !default; -//** Blockquote small color -$blockquote-small-color: $gray-light !default; -//** Blockquote font size -$blockquote-font-size: ($font-size-base * 1.25) !default; -//** Blockquote border color -$blockquote-border-color: $gray-lighter !default; -//** Page header border color -$page-header-border-color: $gray-lighter !default; -//** Width of horizontal description list titles -$dl-horizontal-offset: $component-offset-horizontal !default; -//** Point at which .dl-horizontal becomes horizontal -$dl-horizontal-breakpoint: $grid-float-breakpoint !default; -//** Horizontal line color. -$hr-border: $gray-lighter !default; - - -//== Form states and alerts -// -//## Define colors for form feedback states and, by default, alerts. - -$state-success-text: #3c763d !default; -$state-success-bg: #dff0d8 !default; -$state-success-border: darken(adjust-hue($state-success-bg, -10), 5%) !default; - -$state-info-text: #31708f !default; -$state-info-bg: #d9edf7 !default; -$state-info-border: darken(adjust-hue($state-info-bg, -10), 7%) !default; - -$state-warning-text: #8a6d3b !default; -$state-warning-bg: #fcf8e3 !default; -$state-warning-border: darken(adjust-hue($state-warning-bg, -10), 5%) !default; - -$state-danger-text: #a94442 !default; -$state-danger-bg: #f2dede !default; -$state-danger-border: darken(adjust-hue($state-danger-bg, -10), 5%) !default; - - -//== Labels -// -//## - -//** Default label background color -$label-default-bg: $gray-light !default; -//** Primary label background color -$label-primary-bg: $brand-primary !default; -//** Success label background color -$label-success-bg: $brand-success !default; -//** Info label background color -$label-info-bg: $brand-info !default; -//** Warning label background color -$label-warning-bg: $brand-warning !default; -//** Danger label background color -$label-danger-bg: $brand-danger !default; - -//** Default label text color -$label-color: #fff !default; -//** Default text color of a linked label -$label-link-hover-color: #fff !default; - - -//== Badges -// -//## - -$badge-color: #fff !default; -//** Linked badge text color on hover -$badge-link-hover-color: #fff !default; -$badge-bg: $gray-light !default; - -//** Badge text color in active nav link -$badge-active-color: $link-color !default; -//** Badge background color in active nav link -$badge-active-bg: #fff !default; - -$badge-font-weight: bold !default; -$badge-line-height: 1 !default; -$badge-border-radius: 10px !default; \ No newline at end of file diff --git a/src/mol-app/skin/colors/blue.scss b/src/mol-app/skin/colors/blue.scss deleted file mode 100644 index c31a02bd7..000000000 --- a/src/mol-app/skin/colors/blue.scss +++ /dev/null @@ -1,24 +0,0 @@ -$default-background: #2D3E50; -$font-color: #EDF1F2; -$hover-font-color: #3B9AD9; -$entity-current-font-color: #FFFFFF; -$lm-btn-remove-background: #BF3A31; -$lm-btn-remove-hover-font-color:#ffffff; -$lm-btn-commit-on-font-color: #ffffff; -$entity-badge-font-color: #ccd4e0; - -// used in LOG -$log-message: #0CCA5D; -$log-info: #5E3673; -$log-warning: #FCC937; -$log-error: #FD354B; - -$logo-background: rgba(0,0,0,0.75); - -@function color-lower-contrast($color, $amount) { - @return darken($color, $amount); -} - -@function color-increase-contrast($color, $amount) { - @return lighten($color, $amount); -} \ No newline at end of file diff --git a/src/mol-app/skin/colors/dark.scss b/src/mol-app/skin/colors/dark.scss deleted file mode 100644 index 6df758473..000000000 --- a/src/mol-app/skin/colors/dark.scss +++ /dev/null @@ -1,22 +0,0 @@ -$default-background: #111318; -$font-color: #ccd4e0; -$hover-font-color: #51A2FB; -$entity-current-font-color: #68BEFD; -$molstar-btn-remove-background: #DE0A28; -$molstar-btn-remove-hover-font-color:#F2F4F7; -$molstar-btn-commit-on-font-color: #68BEFD; -$entity-badge-font-color: #ccd4e0; - -// used in LOG -$log-message: #0CCA5D; -$log-info: #5E3673; -$log-warning: #FCC937; -$log-error: #FD354B; - -@function color-lower-contrast($color, $amount) { - @return darken($color, $amount); -} - -@function color-increase-contrast($color, $amount) { - @return lighten($color, $amount); -} \ No newline at end of file diff --git a/src/mol-app/skin/colors/light.scss b/src/mol-app/skin/colors/light.scss deleted file mode 100644 index 678b744d5..000000000 --- a/src/mol-app/skin/colors/light.scss +++ /dev/null @@ -1,30 +0,0 @@ -// this is complement of the dark theme - -@function compl($color) { - @return rgb(255 - red($color), 255 - green($color), 255 - blue($color)); -} - -$default-background: compl(#111318); -$font-color: compl(#ccd4e0); -$hover-font-color: compl(#51A2FB); -$entity-current-font-color: compl(#68BEFD); -$molstar-btn-commit-on-font-color: compl(#68BEFD); -$entity-badge-font-color: lighten(#ccd4e0,10%); -$molstar-btn-remove-background: #DE0A28; -$molstar-btn-remove-hover-font-color:#F2F4F7; - -// used in LOG -$log-message: #0CCA5D; -$log-info: #5E3673; -$log-warning: #FCC937; -$log-error: #FD354B; - -$logo-background: rgba(204,201,193,0.85); - -@function color-lower-contrast($color, $amount) { - @return lighten($color, $amount); -} - -@function color-increase-contrast($color, $amount) { - @return darken($color, $amount); -} \ No newline at end of file diff --git a/src/mol-app/skin/components/controls-base.scss b/src/mol-app/skin/components/controls-base.scss deleted file mode 100644 index e1f17a683..000000000 --- a/src/mol-app/skin/components/controls-base.scss +++ /dev/null @@ -1,144 +0,0 @@ -.molstar-btn { - padding: 0 $control-spacing; - line-height: $row-height; - border: none; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -.molstar-btn, .molstar-btn:active, .molstar-btn-link:focus, .molstar-btn:hover { - outline: none !important; -} - -.molstar-btn-icon { - height: $row-height; - width: $row-height; - line-height: $row-height; - padding: 0; - text-align: center; -} - -.molstar-btn-link { - .molstar-icon { - font-size: 100%; - } -} - -.molstar-btn-link, .molstar-btn-link:active, .molstar-btn-link:focus { - color: $molstar-btn-link-font-color; - text-decoration: none; -} - -.molstar-btn-link:hover { - color: $hover-font-color; - text-decoration: none; -} - -.molstar-btn-link-toggle-on { - color: $molstar-btn-link-toggle-on-font-color; -} - -.molstar-btn-link-toggle-off, .molstar-btn-link-toggle-off:active, .molstar-btn-link-toggle-off:focus { - color: $molstar-btn-link-toggle-off-font-color; -} - -.molstar-btn-link-toggle-off:hover, .molstar-btn-link-toggle-on:hover { - color: $hover-font-color; -} - -@mixin molstar-btn($name, $font, $bg) { - .molstar-btn-#{$name}, .molstar-btn-#{$name}:active, .molstar-btn-#{$name}:focus { - color: $font; - background: $bg; - } - .molstar-btn-#{$name}:hover { - color: $hover-font-color; - background: color-lower-contrast($bg, 2.5%); - } - - .molstar-btn-#{$name}[disabled], .molstar-btn-#{$name}[disabled]:hover, - .molstar-btn-#{$name}[disabled]:active, .molstar-btn-#{$name}[disabled]:focus { - color: color-lower-contrast($font, 1%); - } -} - -@include molstar-btn('remove', $molstar-btn-remove-font-color, $molstar-btn-remove-background); -@include molstar-btn('action', $font-color, $molstar-btn-action-background); -@include molstar-btn('commit-on', $molstar-btn-commit-on-font-color, $molstar-btn-commit-on-background); -@include molstar-btn('commit-off', $molstar-btn-commit-off-font-color, $molstar-btn-commit-off-background); - -.molstar-btn-remove:hover { - color: $molstar-btn-remove-hover-font-color; -} -.molstar-btn-commit-on:hover { - color: $molstar-btn-commit-on-hover-font-color; -} - -.molstar-btn-action { - height: $row-height; - line-height: $row-height; -} - -.molstar-form-control { - width: 100%; - background: $molstar-form-control-background; - color: $font-color; - border: none !important; - padding: 0 $control-spacing; - line-height: $row-height - 2px; - height: $row-height; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - box-shadow: none !important; - - &:hover { - color: $hover-font-color; - background-color: color-increase-contrast($molstar-form-control-background, 5%); - border: none; - outline-offset: -1px; - outline: 1px solid color-increase-contrast($molstar-form-control-background, 20%); - } - - &:active, &:focus { - color: $font-color; - background-color: $molstar-form-control-background; - border: none; - outline-offset: 0; - outline: none; - } -} - -.molstar-btn-commit { - text-align: right; - padding-top: 0; - padding-bottom: 0; - padding-right: $control-spacing; - padding-left: 0; - line-height: $row-height; - border: none; - overflow: hidden; - - .molstar-icon { - display: block-inline; - line-height: $row-height; - margin-right: $control-spacing; - width: $row-height; - text-align: center; - float: left; - } -} - -select.molstar-form-control { - background: none; - background-color: $molstar-form-control-background; - background-size: 8px 12px; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAUCAMAAACzvE1FAAAADFBMVEUzMzMzMzMzMzMzMzMKAG/3AAAAA3RSTlMAf4C/aSLHAAAAPElEQVR42q3NMQ4AIAgEQTn//2cLdRKppSGzBYwzVXvznNWs8C58CiussPJj8h6NwgorrKRdTvuV9v16Afn0AYFOB7aYAAAAAElFTkSuQmCC); - background-repeat: no-repeat; - background-position: right $control-spacing top (($row-height - 12px) / 2); -} - -select.molstar-form-control:-moz-focusring { - color: transparent; - text-shadow: 0 0 0 $font-color; -} \ No newline at end of file diff --git a/src/mol-app/skin/components/controls.scss b/src/mol-app/skin/components/controls.scss deleted file mode 100644 index 2c8aeda0a..000000000 --- a/src/mol-app/skin/components/controls.scss +++ /dev/null @@ -1,197 +0,0 @@ - -.molstar-control-row { - position: relative; - height: $row-height; - background: $default-background; - margin-top: 1px; - - > span { - line-height: $row-height; - display: block; - width: $control-label-width + $control-spacing; - text-align: right; - padding: 0 $control-spacing; - color: color-lower-contrast($font-color, 15%); - - @include non-selectable; - } - - select, button, input[type=text] { - @extend .molstar-form-control; - } - - button { - @extend .molstar-btn; - @extend .molstar-btn-block; - } - - > div:nth-child(2) { - background: $molstar-form-control-background; - position: absolute; - left: $control-label-width + $control-spacing; - top: 0; - right: 0; - bottom: 0; - } -} - -.molstar-control-group { - position: relative; -} - -.molstar-toggle-button { - .molstar-icon { - display: inline-block; - margin-right: 6px; - } - - > div > button:hover { - border-color: color-increase-contrast($molstar-form-control-background, 5%) !important; - border: none; - outline-offset: -1px !important; - outline: 1px solid color-increase-contrast($molstar-form-control-background, 20%) !important; - } -} - -.molstar-slider { - > div { - > div:first-child { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - width: 100%; - padding-right: 50px; - display: table; - - > div { - height: $row-height; - display: table-cell; - vertical-align: middle; - padding: 0 ($control-spacing + 4px); - } - } - > div:last-child { - position: absolute; - height: $row-height; - right: 0; - width: 50px; - top: 0; - bottom: 0; - } - } - - input[type=text] { - text-align: right; - } - - input[type=range] { - width: 100%; - } -} - -.molstar-toggle-color-picker { - button { - border: $control-spacing solid $molstar-form-control-background !important; - margin: 0; - text-align: center; - padding-right: $control-spacing; - padding-left: $control-spacing; - - &:hover { - border-color: color-increase-contrast($molstar-form-control-background, 5%) !important; - border: none; - outline-offset: -1px !important; - outline: 1px solid color-increase-contrast($molstar-form-control-background, 20%) !important; - } - } - - .molstar-color-picker { - position: absolute; - z-index: 100000; - background: $default-background; - border-top: 1px solid $default-background; - padding-bottom: $control-spacing / 2; - width: 100%; - - // input[type=text] { - // background: $molstar-form-control-background !important; - // } - } -} - -.molstar-toggle-color-picker-above { - .molstar-color-picker { - top: -2 * 32px - 16px - $control-spacing / 2; - height: 2 * 32px + 16px + $control-spacing / 2; - } -} - -.molstar-toggle-color-picker-below { - .molstar-color-picker { - top: $row-height; - height: 2 * 32px + 16px; - } -} - - -.molstar-control-subgroup { - margin-top: 1px; - - .molstar-control-row { - margin-left: $control-spacing !important; - > span { - width: $control-label-width !important; - } - - > div:nth-child(2) { - left: $control-label-width !important; - } - } -} - -.molstar-conrol-group-expander { - display: block; - position: absolute; - line-height: $row-height; - padding: 0; - left: 0; - top: 0; - width: $control-label-width + $control-spacing; - text-align: left; - - .molstar-icon { - line-height: $row-height - 3; - width: $row-height - 1; - text-align: center; - display: inline-block; - font-size: 100%; - } -} - -.molstar-plugin-layout_controls { - position: absolute; - left: $control-spacing; - top: $control-spacing; -} - -.molstar-plugin-layout_controls > button:first-child { - margin-right: 6px; -} - -.molstar-empty-control { - display: none; -} - -.molstar-control .molstar-btn-block { - margin-bottom: 0px; - margin-top: 0px; -} - -.molstar-row-text { - > div { - line-height: $row-height; - text-align: center; - } -} \ No newline at end of file diff --git a/src/mol-app/skin/components/entity.scss b/src/mol-app/skin/components/entity.scss deleted file mode 100644 index 3ce2e2305..000000000 --- a/src/mol-app/skin/components/entity.scss +++ /dev/null @@ -1,225 +0,0 @@ - - -.molstar-entity-tree { - overflow: hidden; - position: absolute; - bottom: 0; - left: 0; - right: 0; - top: 0; - padding-top: $control-spacing; - background: $control-background; - - .molstar-entity-tree-children { - overflow-x: hidden; - overflow-y: auto; - position: absolute; - bottom: 0; - left: 0; - right: 0; - top: $row-height + $control-spacing + 1; - padding: $control-spacing 0; - } -} - -.molstar-entity-store-header { - height: $row-height + 1; - position: relative; - - > span { - margin-left: 6px; - display: inline-block; - line-height: $row-height; - font-weight: bold; - - @include non-selectable - } - - button { - display: block !important; - height: $row-height !important; - margin: 0 !important; - line-height: $row-height !important; - border: none !important; - position: absolute; - top: 0; - } - - border-bottom: 1px solid $border-color; -} - -.molstar-entity-store-root { - overflow-x: hidden; - overflow-y: auto; - position: absolute; - bottom: 0; - left: 0; - top: $row-height + 1; - right: 0; -} - -.molstar-entity-tree-entry { - height: $row-height + 1; - position: relative; - border-bottom: 1px solid $control-background; -} - -.molstar-entity-tree-entry-current { - background: color-lower-contrast($default-background, 4%) !important; - - .molstar-entity-tree-entry-label { - color: $entity-current-font-color; - font-weight: bold; - .molstar-entity-tree-entry-label-tag { - font-weight: normal; - } - &:hover { - color: $hover-font-color; - } - } -} - -.molstar-entity-tree-entry-current-path { - background: color-lower-contrast($default-background, 2%) !important; - .molstar-entity-tree-entry-label { - color: color-lower-contrast($entity-current-font-color, 5%); - &:hover { - color: $hover-font-color; - } - } -} - -.molstar-entity-tree-entry button, .molstar-entity-tree-entry > div { - display: block !important; - height: $row-height !important; - margin: 0 !important; - line-height: $row-height !important; - border: none !important; - position: absolute; - top: 0; -} - - -.molstar-entity-tree-entry-toggle-group { - width: $row-height; - height: $row-height; - padding: 0; - left: 0; -} - -.molstar-entity-tree-entry-toggle-visible { - width: $row-height; - right: 0; //$row-height + 6; - padding: 0 !important; - font-size: 80%; -} - -.molstar-entity-tree-entry-toggle-visible-full, .molstar-entity-tree-entry-toggle-visible-full:focus, .molstar-entity-tree-entry-toggle-visible-full:active { - color: $entity-color-fully-visible; -} - -.molstar-entity-tree-entry-toggle-visible-partial, .molstar-entity-tree-entry-toggle-visible-partial:focus, .molstar-entity-tree-entry-toggle-visible-partial:active { - color: $entity-color-partialy-visible; -} - -.molstar-entity-tree-entry-toggle-visible-none, .molstar-entity-tree-entry-toggle-visible-none:focus, .molstar-entity-tree-entry-toggle-visible-none:active { - //background: transparent !important; - color: $entity-color-not-visible; -} - -.molstar-entity-tree-entry-remove { - width: $row-height; - height: $row-height; - right: $row-height; - padding: 0 !important; - text-align: center; - font-size: 80%; - color: color-lower-contrast($font-color, 66%) -} - -.molstar-entity-tree-entry-body { - position: absolute; - left: $row-height; - border-radius: 0 0 0 $entity-subtree-offset; - right: 0; - background: $default-background; -} - -.molstar-entity-tree-entry .molstar-entity-badge { - width: $row-height; - position: absolute; - height: $row-height; - left: 0; - top: 0; - border-radius: 0 $entity-subtree-offset 0 $entity-subtree-offset; -} - -.molstar-entity-tree-entry-label-wrap { - right: 2 * $row-height; - overflow: hidden; - left: $row-height; - height: $row-height; - position: absolute; -} - -.molstar-entity-tree-entry-label { - position: absolute; - right: 0; - top: 0; - left: 0; - text-align: left !important; - width: 100%; - padding: 0 $control-spacing !important; -} - -.molstar-entity-tree-entry-label-tag { - color: $entity-tag-color; - font-size: 70%; - display: inline-block; - margin-left: 6px; -} - - -.molstar-entity-tree-children-wrap { - padding-left: $entity-subtree-offset; -} - -.molstar-entity-tree-root { - > .molstar-entity-tree-entry { - .molstar-entity-badge { - border-top-right-radius: 0; - } - .molstar-entity-tree-entry-label { - font-weight: bold; - } - .molstar-entity-tree-entry-toggle-group { - display: none !important; - } - .molstar-entity-tree-entry-body { - left: $row-height - $entity-subtree-offset !important; - } - background: $default-background; - border-bottom: 1px solid $border-color; - } - - > .molstar-entity-tree-children-wrap { - margin-top: $control-spacing; - padding-left: 0 !important; - } -} - -.molstar-panel { - .molstar-entity-tree-entry-toggle-visible { - position: absolute; - top: 0; - right: 0; - height: $row-height; - font-size: 100%; - - background: $default-background; //color-increase-contrast($default-background, 4%); - } - - // .molstar-entity-tree-entry-toggle-visible-full { - // background: color-increase-contrast($default-background, 8%); - // } -} \ No newline at end of file diff --git a/src/mol-app/skin/components/help.scss b/src/mol-app/skin/components/help.scss deleted file mode 100644 index 31b400458..000000000 --- a/src/mol-app/skin/components/help.scss +++ /dev/null @@ -1,28 +0,0 @@ - -.molstar-help-row { - position: relative; - height: $row-height; - background: $default-background; - margin-top: 1px; - display: table; - width: 100%; - - > span { - width: $control-label-width + $control-spacing; - text-align: right; - padding: $info-vertical-padding $control-spacing; - color: color-lower-contrast($font-color, 15%); - display: table-cell; - font-weight: bold; - - @include non-selectable; - } - - > div { - background: $molstar-form-control-background; - position: relative; - padding: $info-vertical-padding $control-spacing; - display: table-cell; - color: color-lower-contrast($font-color, 15%); - } -} \ No newline at end of file diff --git a/src/mol-app/skin/components/jobs.scss b/src/mol-app/skin/components/jobs.scss deleted file mode 100644 index 13bae9ef1..000000000 --- a/src/mol-app/skin/components/jobs.scss +++ /dev/null @@ -1,131 +0,0 @@ -.molstar-job-state { - - line-height: $row-height; - //height: $row-height; - //position: relative; - //margin-top: 1px; - - > span { - @include non-selectable; - //display: inline-block; - //padding: 0 $control-spacing; - } - - // > button { - // margin-top: -2px; - // float: left; - // display: block; - // line-height: $row-height; - // height: $row-height; - // } -} - -/* overlay */ - -.molstar-overlay { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - z-index: 1000; - - .molstar-overlay-background { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - background: transparent; - //background: black; - //opacity: 0.5; - } - - .molstar-overlay-content-wrap { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - display: block; - width: 100%; - height: 100%; - } - - .molstar-overlay-content { - text-align: center; - - > div { - - padding-top: 2 * $row-height; - - .molstar-job-state { - $size: $row-height; - text-align: center; - - > div { - height: $size; - margin-top: $control-spacing; - position: relative; - text-align: center; - width: 100%; - - > div { - height: $size; - line-height: $size; - display: inline-block; - background: $default-background; - padding: 0 ($control-spacing); - font-weight: bold; - @include non-selectable; - } - - > button { - display: inline-block; - margin-top: -3px; - font-size: 140%; - } - } - } - } - } -} - -/* background */ - -.molstar-background-jobs { - position: absolute; - left: 0; - bottom: 0; - z-index: 1000; - - .molstar-job-state { - $size: $row-height; - - > div { - height: $size; - margin-top: 1px; - position: relative; - width: 100%; - background: $default-background; - - > div { - height: $size; - line-height: $size; - display: inline-block; - padding: 0 ($control-spacing); - @include non-selectable; - } - - > button { - display: inline-block; - margin-top: -3px; - font-size: 140%; - } - } - } -} - -// .molstar-background-jobs .molstar-job-state { -// color: -// } diff --git a/src/mol-app/skin/components/log.scss b/src/mol-app/skin/components/log.scss deleted file mode 100644 index 9bc74918c..000000000 --- a/src/mol-app/skin/components/log.scss +++ /dev/null @@ -1,97 +0,0 @@ - -.molstar-log-wrap { - position: absolute; - right: 0; - top: 0; - left: 0; - bottom: 0; - overflow: hidden; -} - -.molstar-log { - position: absolute; - right: -20px; - top: 0; - left: 0; - bottom: 0; - overflow-y: scroll; - overflow-x: hidden; - font-size: 90%; - background: $control-background; -} - -.molstar-log { - ul { - padding: 0; - margin: 0; - } - - color: $log-font-color; - - li { - clear: both; - margin: 0; - background: $default-background; - position: relative; - - &:not(:last-child) { - border-bottom: 1px solid $border-color; - } - } - - - .molstar-log-entry { - margin-left: $control-label-width; - background: color-lower-contrast($control-background, 5%); - padding: $info-vertical-padding ($control-spacing + 15px) $info-vertical-padding $control-spacing ; - } - - .molstar-log-timestamp { - padding: ($info-vertical-padding + 1) $control-spacing ($info-vertical-padding - 1) $control-spacing; - float: left; - text-align: right; - width: $control-label-width; - color: $log-timestamp-font-color; - //vertical-align: baseline; - //line-height: $row-height; - font-size: 90%; - } - - .molstar-log-timestamp small { - font-size: 90%; - } -} - -// .molstar-log hr { -// border-color: $separator-color; -// margin: 3px 3px 0 5px; -// } - -.molstar-log .label { - margin-top: -3px; - font-size: 7pt; -} - -.molstar-log-entry-badge { - position: absolute; - left: 0; - top: 0; - bottom: 0; - width: 6px; -} - -.molstar-log-entry-message { - background: $log-message; -} - -.molstar-log-entry-info { - background: $log-info; -} - -.molstar-log-entry-error { - background: $log-error; -} - -.molstar-log-entry-warning { - background: $log-warning; -} diff --git a/src/mol-app/skin/components/misc.scss b/src/mol-app/skin/components/misc.scss deleted file mode 100644 index a19eefd6a..000000000 --- a/src/mol-app/skin/components/misc.scss +++ /dev/null @@ -1,69 +0,0 @@ -.molstar-description { - padding: $control-spacing; - font-size: 85%; - background: $default-background; - text-align: center; - //font-style: italic; - - -webkit-user-select: none; /* Chrome/Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+ */ - - /* Rules below not implemented in browsers yet */ - -o-user-select: none; - user-select: none; - - font-weight: light; - - cursor: default; -} - -.molstar-description:not(:first-child) { - border-top: 1px solid $control-background; -} - -.molstar-color-picker input { - color: black !important; -} - -.molstar-no-webgl { - position: absolute; - width: 100%; - height: 100%; - left: 0; - top: 0; - display: table; - text-align: center; - - > div { - b { - font-size: 120%; - } - display: table-cell; - vertical-align: middle; - text-align: center; - width: 100%; - height: 100%; - } -} - -.molstar-loader-molstar-btn-file { - position: relative; - overflow: hidden; -} - -.molstar-loader-molstar-btn-file input[type=file] { - position: absolute; - top: 0; - right: 0; - min-width: 100%; - min-height: 100%; - font-size: 100px; - text-align: right; - filter: alpha(opacity=0); - opacity: 0; - outline: none; - background: white; - cursor: inherit; - display: block; -} \ No newline at end of file diff --git a/src/mol-app/skin/components/panel.scss b/src/mol-app/skin/components/panel.scss deleted file mode 100644 index affbcf9f2..000000000 --- a/src/mol-app/skin/components/panel.scss +++ /dev/null @@ -1,142 +0,0 @@ -.molstar-panel-header .molstar-panel-expander { - display: block; - width: 100%; - text-align: left; -} - -.molstar-panel-header { - - //border-bottom-width: 1px; - //border-bottom-style: solid; - height: $row-height; - border-color: $border-color; - position: relative; - - //border-radius: $control-spacing 0 0 0; - - .molstar-panel-expander-wrapper { - - position: absolute; - top: 0; - left: 0; - right: 2 * $row-height; - - button { - // width: 100%; - - display: block; - width: 100%; - text-align: left; - - height: $row-height; - line-height: $row-height; - border: none; - font-weight: bold; - //color: $panel-header-font-color; - padding-left: 0; - background: color-lower-contrast($default-background, 4%); - //text-align: right!important; - - .molstar-icon { - display: inline-block; - margin-right: $control-spacing; - width: $row-height; - text-align: center; - } - - &:hover { - background: color-lower-contrast($default-background, 4%); - } - } - } - - .molstar-panel-description-standalone { - > .molstar-icon { - margin-left: $row-height; - } - - width: 2 * $row-height; - } - - .molstar-panel-description-with-action { - width: $row-height; - margin-right: $row-height; - } - - .molstar-panel-description { - color: $font-color; - float: right; - background: color-lower-contrast($default-background, 4%); - //margin-right: $row-height; - - > .molstar-icon { - display: block; - width: $row-height; - height: $row-height; - line-height: $row-height; - text-align: center; - font-size: 70%; - cursor: default; - background: color-lower-contrast($default-background, 4%); - color: color-lower-contrast($font-color, 66%); - } - - .molstar-panel-description-content { - @include non-selectable; - - color: $font-color; - display: none; - position: absolute; - left: 0; - width: 100%; - background: color-increase-contrast($molstar-form-control-background, 20%); - min-height: $row-height; - z-index: 1000000; - padding: $info-vertical-padding $control-spacing $info-vertical-padding ($row-height + $control-spacing); - text-align: left; - //border-bottom: 1px solid color-lower-contrast($default-background, 4%); - - > .molstar-icon { - position: absolute; - width: $row-height; - height: $row-height; - line-height: $row-height; - text-align: center; - font-size: 80%; - cursor: default; - top: 0; - left: 0; - } - } - - &:hover { - color: $hover-font-color; - > .molstar-icon { - color: $hover-font-color; - } - .molstar-panel-description-content { - display: block; - } - } - } -} - -.molstar-panel-body { - background: $control-background; -} - -.molstar-panel { - margin-bottom: $control-spacing; -} - -.molstar-transform-view { - padding-top: $control-spacing; -} - -.molstar-expandable-group-color-stripe { - position: absolute; - left: 0; - top: $row-height - 2px; - width: $control-label-width + $control-spacing; - height: 2px; -} \ No newline at end of file diff --git a/src/mol-app/skin/components/sequence-view.scss b/src/mol-app/skin/components/sequence-view.scss deleted file mode 100644 index b8bf89167..000000000 --- a/src/mol-app/skin/components/sequence-view.scss +++ /dev/null @@ -1,9 +0,0 @@ -.molstar-sequence-view-wrap { - position: absolute; - right: 0; - top: 0; - left: 0; - bottom: 0; - overflow: hidden; - overflow-x: scroll; -} \ No newline at end of file diff --git a/src/mol-app/skin/components/slider.scss b/src/mol-app/skin/components/slider.scss deleted file mode 100644 index 2785a3de7..000000000 --- a/src/mol-app/skin/components/slider.scss +++ /dev/null @@ -1,164 +0,0 @@ -@mixin borderBox { - box-sizing: border-box; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); // remove tap highlight color for mobile safari - - * { - box-sizing: border-box; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); // remove tap highlight color for mobile safari - } - } - - .molstar-slider-base { - position: relative; - height: 14px; - padding: 5px 0; - width: 100%; - border-radius: $slider-border-radius-base; - @include borderBox; - - &-rail { - position: absolute; - width: 100%; - background-color: $border-color; - height: 4px; - border-radius: 2px; - } - - &-track { - position: absolute; - left: 0; - height: 4px; - border-radius: $slider-border-radius-base; - background-color: tint($font-color, 60%); - } - - &-handle { - position: absolute; - margin-left: -11px; - margin-top: -9px; - width: 22px; - height: 22px; - cursor: pointer; - border-radius: 50%; - background-color: $font-color; - border: 4px solid $border-color; - - &:hover { - background-color: $hover-font-color; - } - } - - &-mark { - position: absolute; - top: 18px; - left: 0; - width: 100%; - font-size: 12px; - } - - &-mark-text { - position: absolute; - display: inline-block; - vertical-align: middle; - text-align: center; - cursor: pointer; - color: #999; - - &-active { - color: #666; - } - } - - &-step { - position: absolute; - width: 100%; - height: 4px; - background: transparent; - } - - &-dot { - position: absolute; - bottom: -2px; - margin-left: -4px; - width: 8px; - height: 8px; - border: 2px solid #e9e9e9; - background-color: #fff; - cursor: pointer; - border-radius: 50%; - vertical-align: middle; - &:first-child { - margin-left: -4px; - } - &:last-child { - margin-left: -4px; - } - &-active { - border-color: tint($font-color, 50%); - } - } - - &-disabled { - background-color: #e9e9e9; - - .molstar-slider-base-track { - background-color: $slider-disabledColor; - } - - .molstar-slider-base-handle, .molstar-slider-base-dot { - border-color: $slider-disabledColor; - background-color: #fff; - cursor: not-allowed; - } - - .molstar-slider-base-mark-text, .molstar-slider-base-dot { - cursor: not-allowed!important; - } - } - } - - .molstar-slider-base-vertical { - width: 14px; - height: 100%; - padding: 0 5px; - - .molstar-slider-base { - &-rail { - height: 100%; - width: 4px; - } - - &-track { - left: 5px; - bottom: 0; - width: 4px; - } - - &-handle { - margin-left: -5px; - margin-bottom: -7px; - } - - &-mark { - top: 0; - left: 18px; - height: 100%; - } - - &-step { - height: 100%; - width: 4px; - } - - &-dot { - left: 2px; - margin-bottom: -4px; - &:first-child { - margin-bottom: -4px; - } - &:last-child { - margin-bottom: -4px; - } - } - } - } \ No newline at end of file diff --git a/src/mol-app/skin/components/viewport.scss b/src/mol-app/skin/components/viewport.scss deleted file mode 100644 index 3ef5c8a6a..000000000 --- a/src/mol-app/skin/components/viewport.scss +++ /dev/null @@ -1,93 +0,0 @@ - -.molstar-viewport { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - background: black; - - .molstar-btn-link { - background: rgba(0,0,0,0.2); - } - -} - -.molstar-viewport-expanded { - position: fixed; - z-index: 1000; -} - -.molstar-viewport-container { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - -webkit-user-select: none; - -webkit-tap-highlight-color: rgba(0,0,0,0); - -webkit-touch-callout: none; -} - -.molstar-viewport-controls { - position: absolute; - right: $control-spacing; - top: $control-spacing; -} - -.molstar-viewport-controls-buttons { - text-align: right; - - > button { - padding: 0; - text-align: center; - width: $row-height; - } - - > button:last-child { - margin-left: $control-spacing; - } - - .molstar-btn-link, .molstar-btn-link-toggle-on { - color: #eee; - } - - .molstar-btn-link-toggle-off { - color: $molstar-btn-link-toggle-off-font-color; - } - - .molstar-btn-link:hover { - color: $hover-font-color; - } -} - -.molstar-viewport-controls-scene-options { - width: 290px; - background: $control-background; -} - -/* highlight */ - -.molstar-highlight-info { - - color: $highlight-info-font-color; - padding: $info-vertical-padding $control-spacing; - background: $default-background; //$highlight-info-background; - - position: absolute; - top: $control-spacing; - left: $control-spacing; - text-align: left; - min-height: $row-height; - max-width: 95%; - - //border-bottom-right-radius: 6px; - z-index: 10000; - @include non-selectable; -} - -.molstar-highlight-info-additional { - font-size: 85%; - display: inline-block; - color: $highlight-info-additional-font-color; -} \ No newline at end of file diff --git a/src/mol-app/skin/fonts/fontello.eot b/src/mol-app/skin/fonts/fontello.eot deleted file mode 100644 index b522411b265bb3854a7f23579001bfe51e4229cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49272 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�SD_F<qRgQ-ta>EJd6_5ZG^Dr^rNx(sd#Ku5BTU=#p92(W55AQo4)c;8lG6r zbj1!HjCEy}Ctwt#>}%a0JKCozMK?n592ta84s?%uAWQ5PJkiE-_vWWmD4HTSO=TSR zS!}S+X9G^FRLL`C5~iEH0M0a`AgE~&H8zU}4h&xY+2z3l2hh9y5Xv^Ge6s9u*}D8v z<mitJ&y3|SUCNI=Q~2>w9rnh+kL?Pz;w;Y9ckn#b=h|PceJp)nd>`EF2+r3qH(>Kl zIAs8Cz_}n<O;lms!XeBs_uau6*}iLgpxI8c@wFNSgbGTR5J$z{n$LPrKjWs3${yN5 zd4V;H^D@kXhz3a|@ynh)Wb!#wrq-Uq+2ZL~r&pG}kK|I1^v#Ti#FxBLuP=Ns9Dj_w z0i)k24+jp8o$TjcuWVGA)*rX}?|h6iI+ZhE^a0pDrSY~pJwAD%-|KKVzbB8#F0Yrn zp^azq#pHk}`{>d8GSRH^&@SKg5cOBZiL!#RN&E{1n}3h+0Au@#er({z>VdXs&{d^( z*v*nbr!^#j;HB^ewqrww^|E--R;!mZ+nknal@_pZ2d!JTNV+<cQL2%2H4i&*Mhm=7 z+$u9yLv)-cs;x52S5nm-`Fkfj75#c7(%PzMBt;&GJP;+!e*pe>cC_wk1;Voe_kTC= zF=zUI0s305EE$w}lrsp36~my;QVZ5^Jvi3jS(BY!cN^HkTKotSyRA-3*Yd;7iZ7gD z1Ex*N>3qrQ?8bkf;+*Ry$2qz<#aAG`0JZ`o8Ngxy7$oEv*lS^ccfl~q`X8byf4u3? zCl%eT8qR%!g%qsDv0x`LL`dTR`a}Q<9flJ+B&QX;N&#P2O`r)?Lu4#OVMJMLO<S<( zFtqDv0;=C67zL~FjnrYW3MJ0B_`S0g{2L(3=D^$yKv5Ouf1g?9*3t<Sr}Xyc#P@xl z+yA9;<1jEC$1rYe6HX~39-yaSok;>pi5kLElvUNkIK79K`(_3A?Uh&vUAQZz1{%Q4 zSmwf9WI3tzNZ@CUmt%hmTWr-nl2yg|93(sTaKjdSEx4&+>w#(13_RcZHt{IgWLSNZ zuHEdKuXH|;@m%yQh@T)-NA?%Q&m8`9guO}i58lh#Q&I|ZQUI$2-4E_*YpJ92Ia2fn zt_B^dW4NFZg>Trf;(-Ih2;>^ozzV^FIwau?Mv`n@=2k~6dV0*^&{=INAJo3X?M|C) z*L0elI@hRit8K?)SB3%_o2~T{tl8N+M>?T?+d10FK3)EW#o!Dh&}r>#*V}YBfHmve zhdT|BSW+2UJ@P?gd+y-=Si9cxQfWxlck=OkL^pi;)Zq}Hk1nHatehbbq;+R4h=k71 z!~t5stV5S!J%`mIZS*&rb#gWOnj3?zV12!<j+o0$5z=;6@RCvRaHKX;j~Rdma6f8( zBZhdz_-5(Dw{#l$`p4H@Iu!|S8J(|u{7UJKC*OFZbnA^b-n3X#LytT%l+vm6?R|YL zY%&!9?$uWSHaq06j*dj41f(2|U@a<wSM;HD7H6fkziNKM)U$`rPj|6~K;vJ+6S4!i zlz2iGPnjpg&fm!lvWg?b3~CLuE6kww0GL5{FoKkQ*?GvQc=$RQx_LWg@ZOprw<-Cc zkAxL~N<~W-EN1{c26-rzVu_M8Fgb(|7_nrNl3<P`#VBQ<g$d!^EggZ52H2N3ZNZ_M zhk43-oia@KF6>9jXOlST#|jc8=!KafZv-@2->hkZOy@d+x2V5zOLqXKdM};r2d4J3 zT@K=V>RV4oXRu_u1VoKnrH@pxE;+?sA3HZZ{M6;AaFGntovSaIVR8i+jGD1wVwE~9 zrmh~9{$S4|JqH7w(<MKwDBTR4^Y|Tp55G{~;p2h><APae6!Ot3$F^!9Uf}Fq86vD@ zEiqt~kd7uX_Ao$I00U|fMrj;ak45k#b`U7%b}COd!!%+HsIk-nYp>DL*a1Eb_?Ucy z&4Knhwh}lS#LGx4*xQWlA+boT?8)fxg<BVfqrT@Y&d2*x(e_jXhk7Z`qr<ri7jnao zde|$_#lL+e)!#X3V)@dOrv07$7#WBbbCO+}z=#(Vz@>l%rat6<rp3>VKGAt>Gw@8n zfZ<G3#>WJ#Hoy@f0che#68{aYBWW!0nOO8_|18!D28(FYLG#aMG@s2MzM65G$5T$7 z4n{w$+Fp~<W`rn4s_-MTLI0ZJvO|T%fN|PsXOD;b4?hc-q*nrmdn^HiwaTQmsCOG+ z#uI1!Dzw8H;7YZFGTUH<cP|i0PSqJib#)U18p?oBZBPTE6o96XSCtG9CpIxy;;D+` zO|4#~jYGp<Bj`0icn}1mPB3DVBZxIRF}@X8C9Gx~w;GbetzpAG_Yu*?9=LCB&)$2x zIwGyRTG|3_JDWYQx`VUJdH~^)y*fYhlZ28YRRhpD61=7zEZ@u4S#V;D-430N;88R8 z*V6uxwzaIX$;IHKY;r|ixW7L<>bx{LG1#B#9~`;l94&|VJ><by-|6?4jCAevvv(B# zal{?UMA@6JVTwKM%8$_NL#Ee81|ezKg|6XaHtHch-|Y_+Kd;N{s7sOKl=MKB3zpBl zGEk%pusX;F^jh?A?B)rVhd2^PaCn9T49Mw292*->^*C;7B6YPe9KF9I(${<cKy=_< z$WV9mw6~$U`~=I%57aD>%BKI=-}7xj$EGSGMAZ(Z#@2WJ>*5U?HFI)E6By|Nv}n6c z)lYT9E66w9)gI>l)t7u!C%!(a65z|epxnjfQD@ggyUl2XrBshnn<FmvNTsH3I-O=~ zcSz<Hr_|7DzfwccV&*l6zCioyGuR8Y14(2UYxje~A&dgq?lw`aw{HQmS&M6%3F6U^ zgB$?*q`F$Ko(I=Q2dRA>Aci$S1kqGOhy`fYOs8gXld(p^dbVnuJF0|9Kr&;2I~;#F zm3;V6{Lrr*n3^2_=IA%}4b?@g9WVzGv1|owW7B&5?P<q}e{<c2pQPiAz$@Aaq4V?b zeK1PAi55YWYK77;-BqZswi2>&){^3tJDdu*xWgGrxx5<goR~}vbH_+(Vq$eaRduKT zZKdj$6(1;#H$l!dLew|{YBb!exNjP~4nw};%SSWqPgI)XiS|tN0}@~H`NVh2P2pAi zJg|$9!f`Gs`~oyfJfkM!f!0DS9dJ=tMpooQ2uTRLC}niz!e)YM918?U4$%lQ?A#7$ zw<c#(ZT&WgQrn;*`9-R1Uay==*L;qbnqLZcP};MV!fX)L!kvUfQXa@R?`ulF?-61+ zr2%&2J*~UE#1;k@nhoe6YyrS!*zf?LKgLN38s4BF+7-rY^tASr#_7J!&h6`UZXX=z z-PgD8f#`kRpb20C@RC`!)f-W=T8nn;-G!4Xt&RCZac<DeMz8#0`G28wII4iK($4+m zp7#D%)r`12>?)nUt9n+CmYcX$>0PA*cSXSP^@aMQwd)lrDTRQc5GD;qLp5=C6+xx4 z7H{jWK+CpzIvR&PS8mUi6nU|P1@E4KpzdY$J*s*DgaRu{)Yg{I7cJrrjmn~l)_h;K z>wS+Ob~p_?YIRn?t=G2?=)HyG&%;&=*jk<Ol*ed|1_IH*$Kjq%n@`hckR1!Bd<KX7 zjoqf;b|cW*!^bCk3}8=>y?}(6!5WH&LQxDKoNhnE+2d7AH``%Z8WuCKg=B$RbbHKN zxWKyM`f`=dz=dIbmB($jRc+UACxxCSGSULZiWjp7$wA<tkxtm!1uy}%Uf{}e6Zbg& zCh-XX$(?htS)<`yXZVG)&wV6{4xbDKFV|axJ^f$>EUO$A6`_ZZYK&Jq+3PyZ>aEB` zioFF3(<ZAEPjw#s2dvXPq3U`1r(UleknKkK+)-l_%7T2-Z~ob}%MWkq{y##s^br;D zPaoR%zbbcFbli`V{W$!XUMUoRct;vr!_kuT7kK`mbepH7JX5Yep_HVz*Z!|mhx_TZ zzmZO@m81i3(+?sB{0BW{9*@T*_Zn&z%NTTS@K6Nt`)jN09fS|!C&Q1m%vZY4uiXYu z4V<;L+gRJdiYo4SNBX_B5>E^1@o(K&E3sLm<)t|IQ5^d5Z!6#D;glyl7oonlo<{@x z*Vk@IZ*L0Y@4_$R-PhJWm%h8UT8Yn~LCxJ%<{N9b#oxv6Go(Rz`SWsFshs4ebm)7^ zvy${@$UpfJA8D`ga({&ME8nBMe_M&e^Gk70A`D7r(pjW^63@;ftd-JS`+q!LDii%~ z;Ag{+(x{||aHakbhW9Ezs&n=be&cJOaQ9nlf5ocSKE>~^*qdvgO4GRi3H*Pu_E%DT z?Njk7eDA&T4{k~mn!CymZeIS%bCiwO(cAbQe%$>6-1KWlyHQ@LE$L2qr96I$G$!#g z<41KzZv571isEkm9Z=$}hf&&i&S~j9-2WT2`CZg2)k*n1_9W`Az3l&O++RR_g095f zpyzTN;eDjhg=dt;Ylu(bKVSPLZ>PUNe5%*CLG$JK(x1a$E*rIp^c!n`$)6z}Z<_;a zf5Y#mk(b|E`%H8oj$!R{YE#~>cvtvE*^RjLF8>=Ao)m6Drecy^hGsKNg^3?aap_0c z%NNyV^<nkX>i0A@%>m6*n)kGR?XdPy?aMlk?&Ye%s@JM+>pS%?SIgD;>K_{Xh9?YH z45gY#&2r5L#!2JrTdZ69x18E?)ntN{+-1|xwvKJRW%ikW24s&+Eq2J3AJq-lJzw{8 z>!9@!>nHWy^{>|7w)NW{vHfgY_qJR1sQrxn7Y*kdK5+OQ1;;P9KfV1k=TYZ7JGyo} zD|g9nG(Oh&u4}*R5!ajUtDeCogV*2O*ZfTL&F0U2W4`xyj_%CteBE#J&-icnKWeeI z#99uwEVn%0^5d2d1NuNHFcv5T&IPUqej2zPv;;lD_TX@EA^2qQrQlm3UuZa#3!M$U zyUVp}Vb{f7Z?=xMo^O4-^_RP?yZd*~?tXgr>$^V-2f{PqD{VsC{<drFqwP2MME5+s z=flW_$a@`w9nW>1?!0kN;GVI29=YfBE?rlD*R6Z)_g?FEbVs|7cHihO-Iu!W%6+$b zhI*dtdF6iF{imZN(c%Nf2PPl*EcR}%zxRCaXMG3zUf3Jidui|I{TBx!1D_904F2`k zCcgFue5Cw|@EvHW(w_)Kvi-s$Tz^=d;~@g}r9WLCPsxS?jnZ`00fSI+gDVa+Ld6X$ zW0*b_H)I{Krd8aq2_}q}-4Nsu9xl7}kmNs9c2{HV^*zjJOhqTMhlSrMyBTq&%WhG? zzOd|;5FRYMRdC-|cB=&|`+C_;T<v7ptrsTPV%c3Sc-bHKW^*U<bBAUNvaffi9B$nm zk*80{Sp>~3Br<YhsW6+(FUnDQCc98bXENDPGCN;!kEIVSWfJ*{w{o9I=NIR)3-WH9 z(3jsDNH3)Gi9$L>sV^Qq6fP8I<e7YSUhXf)kaPL$;dHVPnk^J^UAuNw-lXqgV~`bc z!U-WS%%Q4gg@PapK7{VXJq-5AZXEi{!Zc#Yh^fSy19E9X$RH#kEa8n={$vsUD1I~W zEg&?F7#X-jVC!XZOnP_t7-Ag4`x&H?|H9L|V^1J<9>g=pQ<ssi5OQ~SEGYKi+Y~dw zbC5!Ql)NtrNAa~VLJN3{a+&A3l7)Vr;zoXD)CWB~jC7KS9pbf8Kxmh+3;*u^W~F?f zDv^j6e|qKih3ij<Zvs&cHLQVXu`8{DDUWJchpvH?*cLFlwlXv9>snYHK-20We7g;J zX${PQ{Slp2$gGjMn45VRaFAIu^Rb=G&stc31u-V>Vy$d93$r%X&i1ef>tLPi9+(li zmvyuIz)rcJMcD%^#(G&F+spdd02^dq13tw*sP29pyx(uIQT8AkW8Y-s>>)P6zJ*bC zitT6LW(U}>0m0&77H0`I&5|s|(vZkK#AewXJIs!-44Y>QEX#82I{*_|WCgawj<RDw zhByIqhkpQEg?|WaqkqIsvVY7TXTQ!)vG20e?4PhR>^Iml`={&)_L~qh{%7n-_FL?G z?4Prz*uMZf?_aX>?6=v|?ECB)_OI9l_OIDR_HUq<{%_eO_B$B={~ddd{Vsc+{T_RP z{d-7){RdWLE9@$Jk-fxzz+PtmkzHfI&#tpSV6U)0WUsRS#BQ+v%wA*vh5eBISN0?J zN9=X>$Lz=Kzp<O_zq2>k|6p&jKVfgNKV@&TKV$E(|H<BEKVh)&l1pULg+f}hm`>!A zv-+7#VsSP@{aQ7jn9HbV5nQO|*I;@+S2&?sEF|)Jx|ID(i|M?YE{i(41{0~2`siFL zoz>>%l7*#wT9Zu7qYrB`i4)l+B%5AXQYUBA$s>5MkW6Qc$}O0j%O^AGRK>gaoux!R zol@m8ON$1&R3a#z@?-V<+``gg4Zl_*lzo*1X49FRfi9Kjs=0-ktbs1;A=8QE5!GyV zKCQ~kEgaD#3WY>+R+Y&nk5thmn9ZdZr1XijR7f1sO=q)5<`elNRTamg3e|R~sxZ4W zKfM@S%2k(LsqC=@9bGcnL`t)iqnkmw1(TWVQc9gq=Q1a%_%)cwWVAEc{INtnrT)%R zwvbjOv#GQueLR=V7c|H~at;yFiNY)@JeQwaC}{KP!m)I^pgWR2F`dPGT8w$9--0SR zn<%KJG3-{MC<wr)szyWS3+kguiibwZrxzD>C|rUbO7n?aRcbDeaTGpECYM-Tl#)w1 zbs-OmkW$&C3ctk~T!NXzLTWLY$fZ%ZWU!DvUNBS~!MTN_RSW53i#a^b^BUrnr=q&j z%w#jEbY4r8Hg_~FO=piw`Nc(bZZ^A+mXg^+>OwY~SyV61qUEdjH8Gt@Yo-&6N74mp z8g-vUDHc+RygHM`M5DnKG$qZ?B~|lFg|vDxyR?wf9L;8y=F@6aG1_l7yOci!s$NuO z67xAdr|lqb8O6Dj(<D-dmlg}^X^LNsdYOA99h_OpWDI5Z;yl_$oy+Eya@yR|bY^aG zR_0f|&6^7-tsAnI#6i#j;AP@yP8COuQAcxw1WS@G$Y)+w=yze2=S#p9DpuS6!N z7BT5=T#pt6q0brD<8tqojTg8d6vp4;9<&!SMTrEb=ML3uya}Ii(~Iy{SKfmgZGCJ( zMX}X9lDL%Pgd7Bo=y)P<R4MPn1=^-3N^z#q$Vzd@RW1%WsW|*0&r@M`u3Tzzm7|f9 zqS0MSCXb29V|rf2=>2eJ(nr&oa^rC)8f0;HX=Wyaj+@QqG>gaP3h1~wj5QdjvN?4w zfk8<Fy3XcPnnE@R;?dzo<5$5jlUOVS%U!O9duSY>4yebX;t=(aVC#l+_l7gP;cU|& ztwZSoMvldTjxIC=YNx5=<xdzWMRbZ_VQxOHozE`J6|#BFe0F*+lNOUH_1pqFfp%^I z83KtN%4DZG-7ckS_yB>5070X_^Kn@<pUp04W;oqe^9~k7Nf%J|bYcddo=HzHWlyN* zvnX@bbbc;9lT0k8b&E@=6gbd{3u^p=VSa5>Pv@5wXLT3|K~#kkY7EkN4?{=35X{Y` z^@<AvVkK}IBfk=e{+Un}(8Z01@`=<EA47r|5p>ATp{2y3w5BkZM4we3!`dycP35yW zOfynqE?=F*pgxDLfm%jUvbo%es$^y^$H%X#`ShU#9}RS=L?MBGpH`>x7%DW=OOzZ2 z)^vUz!!#0=Fz6YO0D4~ngHbA7%{|k3Oe(Wg++E10(~64<QNzP>i9BjLozdjc*^$9R ziKRnnWhsLLp0zc|)!h9Dfp7YUJ@J0}6@`FY6dsz~J0;8et_j8m_Z2nY+&@`tZz%ev z;xqE{Lz6|ZDe?O-b0H*??&*fc#-cD)6nfo#SFu{}jmLsT7AVT`nP5>2$SJvaW3;I9 z?!W3|`rf_Cy<^{=Y;-p^EKkbC(b37qVsxrOE_RZub81RnQ6eQ$MIVC7KDpRR&s!<x zjnPRNnOIK9q=*?uh)mD)<cg3h5^sn@xv`<hTBfGlMPYO@Jv9|9N&$JVTvRnBkRf&N z=wwmtjukcT7_vWAWbt586>uY8a%x3A9h2#a@^QN2`QBtv@;4&9S6-Hvk@iZfx(UU4 za56sHkQkeqbWh=F^qZ4-)IfzQe>qrG2a4L>maAA^@~Y9m>yEim#qL<5C{E85SrX|N z)&5{n8;~iJ)xF7UDq$K?kj-d(ilW5(cqVm$tJ-RzcW=z!xL(;+fjcW-ucX3SkcD29 zI4<v9b|<JIcuffnRGUS)0hz4ivM4pV6Mf1js=wm(q6cr`<@KU%e#t;I<Bnae)=PUQ z8ynn>Q~t(au_mx0ihGNxL|?FI44?qATr~6!QMur7$EJ!k<Qs#p2EJf%3z9MMDv?nG zNo2cd?2XIIak*$jr3H(oz`pUx6;-Nl%2TXKyN?HpTLb$ZoZR<KC9t6p;k7*699R*y z^gcAXvSka}FA*zlX(1v7>Bd$Jbg99m$gF4yscCd_g(w%56<bF0(r5jRZoF1;Hz?2P z70^CCokB?lkpF==8u5;%`icfz5rkSds<^i(^jrnd7pI2WfUqKnd&eh>Tih{uZ?PIg zsmF)oF*%OU|A6cm8--YGIlf}nv=s9#4K7rZ1!b#k2^Q-DD~#?|)HB`d11l2UZGjaP z-M0l+)O5E8Ry1^P2&`!7?g*^t=)OI$Qbl)I{43V9{2el~0~v850T~&gdm}PJcNa23 zcQ-OZcMmc`_a<b7?p|bs?#;*u-F?Ui-FF7$ZcgX^0LpKQ%e_<=)MyFZi#v&^S^`CX zOVN++5&)$RfTX^(zqk{fZW^op))=6dU@^Gf2Fwbh;6+x~%4<2axmNCcv@0O*;rVPu z9Jcq1-4&z2mu8E?1?x+E2=41~cdqPWbtuVhlp7iS(pTc3+C*ot7!HK$yMx8HUlkP; zmPGV+G=*Sol0)(U^-NUZ*Or$D+yfX1CNXAX%tLo=W8mWB`+G2iSV8^x&!ZHpdRx-V zA-61dFC(qU#$!38q)}8MO~jImaq7&`2Pa<?Wl3&$QS?gosTd7#It-Qg9=_=AN7q6t z-q}yksacCMl8e3Zl)EVPCQ=w<#NI>$-0>-N%)8%7AWIl^-TjHq1~<OYk8<J0zk>9> z<Ri!kjf5)nZq&URgoJVXuACsn1YU09nZOm(2n}Hy-vX6%P#wtly4qW=19vy7rjrL3 zb?Cja-0vQsk5Oyg!>f!6P_9v7d@>|=W8$M6mjjX5MwM%t;QJaLtxv5=Lw%9B;_h<G zAdkUfS2+j0m8OZ)+<SKsD@}QCz%7TU)_@}>cTa^@V08u^uX{Zht+^3=-+C~QU3vA6 zI6Z;lo|ej!I}*J=P>i%JgNBG$meC!*@L@DdsMvz<M|pJ-0aj|4h|7&$5ke<X^7sG- z3k=Zflv*k8uO`KTFC&b<3-wXG(YVs>?rdn>)ZUF#<vhlyGAfnYOXY8*nx`z5%duYM zJ`}l5>71C#(GP1w#og$Od%q&QA7d@E)E2{tGY}}Y<2FdOwiop(_hZhg)YI1jM2^Kl zRO?XSsvtZ7$38e1IfesQ84vk796V%%;>7Ui8x)5eqZEf64^kX*j8U8(IKD}7$T3cF z$ng-xA;$#8iNf(MibIY`ibIYmibIb56sH%CZ&Ms{9H2Pl_%(_{j)N5EemEYcIOK>^ z9C9Qm4mqX+#g6rAPLij1FM?Ct)eTpg(-VU4gJ&jC>|Bq1h&(*@EO$}tIqssUhXciX z)}tOF509GRE{ZzOT@-a8Q0!Wdnk5g9n&U2t`W^0~sQJK)fDNmxjAAWCUAicFMvv1} z6BMw>etirJ5Ox-Wva+nstqAHEN-y}y@uFU2I>luK6gOO&*mdy+9#_Gwja?U_idz!; zRy^#?gIG15nOu=leJft_UDut&!Flvd5=$pUA>@x24I(r6ud%i77FEBAi2}T{{|~i; BVu}C& diff --git a/src/mol-app/skin/fonts/fontello.svg b/src/mol-app/skin/fonts/fontello.svg deleted file mode 100644 index 753bf788b..000000000 --- a/src/mol-app/skin/fonts/fontello.svg +++ /dev/null @@ -1,442 +0,0 @@ -<?xml version="1.0" standalone="no"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg xmlns="http://www.w3.org/2000/svg"> -<metadata>Copyright (C) 2016 by original authors @ fontello.com</metadata> -<defs> -<font id="fontello" horiz-adv-x="1000" > -<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" /> -<missing-glyph horiz-adv-x="1000" /> -<glyph glyph-name="palette" unicode="" d="M857 622q72-48 101-110t20-104-35-48q-16-4-54 10t-80 10-80-46q-30-46-21-75t34-65 23-50q-2-26-36-63t-126-74-216-37q-186 0-291 101t-95 245q8 118 104 235t216 151q290 84 536-80z m-318-466q30 0 52 22t22 54-22 53-52 21q-32 0-54-21t-22-53 22-54 54-22z" horiz-adv-x="980" /> - -<glyph glyph-name="search" unicode="" d="M772 78q30-34 6-62l-46-46q-36-32-68 0l-190 190q-74-42-156-42-128 0-223 95t-95 223 90 219 218 91 224-95 96-223q0-88-46-162z m-678 358q0-88 68-156t156-68 151 63 63 153q0 88-68 155t-156 67-151-63-63-151z" horiz-adv-x="789" /> - -<glyph glyph-name="flashlight" unicode="" d="M807 706q62-62 85-130t-5-92l-134-134q-16-16-62-26t-96-4l-408-408q-18-18-57-6t-75 50q-36 36-49 74t5 56l408 408q-6 50 4 96t26 62l136 136q24 28 92 4t130-86z m-448-408q32-32 80 14 46 46 14 82-14 14-38 10t-44-24-23-43 11-39z m336 298q30-30 68-50t62-25 28-1q2 4-4 27t-26 60-50 67-66 50-59 26-27 2 1-28 25-62 48-66z" horiz-adv-x="902" /> - -<glyph glyph-name="mail" unicode="" d="M30 586q-32 18-28 40 2 14 26 14l846 0q38 0 20-32-8-14-24-22-14-6-192-102t-182-98q-16-10-46-10-28 0-46 10-4 2-182 98t-192 102z m850-100q20 10 20-10l0-368q0-16-17-32t-33-16l-800 0q-16 0-33 16t-17 32l0 368q0 20 20 10l384-200q18-10 46-10t46 10z" horiz-adv-x="900" /> - -<glyph glyph-name="heart" unicode="" d="M790 644q70-64 70-156t-70-158l-360-330-360 330q-70 66-70 158t70 156q62 58 151 58t153-58l56-52 58 52q62 58 150 58t152-58z" horiz-adv-x="860" /> - -<glyph glyph-name="heart-empty" unicode="" d="M790 642q70-64 70-156t-70-156l-360-330-360 330q-70 64-70 156t70 156q64 58 152 58t150-58l58-52 56 52q64 58 152 58t152-58z m-54-260q42 40 42 104 0 66-38 100-38 38-102 38-52 0-104-48l-104-92-106 92q-48 48-102 48-64 0-104-38-38-36-38-100 0-66 44-104l306-286z" horiz-adv-x="860" /> - -<glyph glyph-name="star" unicode="" d="M440 790l120-336 320 0-262-196 94-348-272 208-272-208 94 348-262 196 320 0z" horiz-adv-x="880" /> - -<glyph glyph-name="star-empty" unicode="" d="M880 454l-262-196 94-348-272 208-272-208 94 348-262 196 320 0 120 336 120-336 320 0z m-440-238l150-124-62 178 144 114-176-4-56 202-54-202-176 4 142-114-62-178z" horiz-adv-x="880" /> - -<glyph glyph-name="user" unicode="" d="M736 128q204-72 204-122l0-106-940 0 0 106q0 50 204 122 94 34 128 69t34 95q0 22-22 49t-32 73q-2 12-9 18t-14 8-14 17-9 43q0 16 5 26t9 12l4 4q-8 50-12 88-4 54 41 112t157 58 158-58 40-112l-12-88q18-8 18-42-2-28-9-43t-14-17-14-8-9-18q-8-48-31-74t-23-48q0-60 35-95t127-69z" horiz-adv-x="940" /> - -<glyph glyph-name="users" unicode="" d="M1000-90l-224 0 0 150q0 54-30 81t-154 89q40 30 40 84 0 16-13 33t-19 51q-2 8-14 16t-14 42q0 24 12 30-6 34-8 60-4 38 23 78t95 40 96-40 24-78l-8-60q12-6 12-30-2-34-14-42t-14-16q-6-34-19-51t-13-33q0-42 21-66t77-48q112-46 130-80 6-8 9-61t5-101l0-48z m-488 262q182-78 182-124l0-138-694 0 0 184q0 44 84 78 76 32 104 64t28 88q0 20-19 44t-25 68q-2 10-18 22t-20 56q0 14 3 23t7 13l4 2q-6 46-10 82-4 50 33 103t127 53 127-53 33-103l-10-82q14-8 14-38-4-44-20-56t-18-22q-6-44-25-68t-19-44q0-56 28-88t104-64z" horiz-adv-x="1000" /> - -<glyph glyph-name="user-add" unicode="" d="M620 128q180-64 180-122l0-106-800 0 0 202q36 14 82 26 94 34 129 69t35 95q0 22-23 48t-31 74q-2 12-23 25t-25 61q0 16 5 26t9 12l4 4q-8 50-12 88-6 54 40 112t160 58 160-58 42-112l-14-88q18-8 18-42-2-28-9-43t-14-17-14-8-9-18q-10-46-33-73t-23-49q0-60 36-95t130-69z m230 272l150 0 0-100-150 0 0-150-100 0 0 150-150 0 0 100 150 0 0 150 100 0 0-150z" horiz-adv-x="1000" /> - -<glyph glyph-name="video" unicode="" d="M980 600l-100 0 0-100 100 0 0-100-100 0 0-100 100 0 0-100-100 0 0-100 100 0 0-60q0-16-12-28t-28-12l-900 0q-16 0-28 12t-12 28l0 60 100 0 0 100-100 0 0 100 100 0 0 100-100 0 0 100 100 0 0 100-100 0 0 60q0 18 12 29t28 11l900 0q16 0 28-11t12-29l0-60z m-600-400l250 150-250 150 0-300z" horiz-adv-x="980" /> - -<glyph glyph-name="picture" unicode="" d="M856 518l-100 0-124 150-214-150-180 0q-52 0-90-39t-38-91l0-160-108 296q-10 38 22 52l680 248q36 10 50-24z m106-90q16 0 27-12t11-28l0-472q0-16-11-28t-27-12l-724 0q-16 0-27 12t-11 28l0 472q0 16 11 28t27 12l724 0z m-56-452l0 162-72 160-166-60-130-132-138 170-92-214 0-86 598 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="camera" unicode="" d="M500 450q64 0 107-44t43-106-44-106-106-44-106 44-44 106 44 106 106 44z m400 150q42 0 71-29t29-71l0-450q0-40-29-70t-71-30l-800 0q-40 0-70 30t-30 70l0 450q0 42 30 71t70 29l120 0q28 0 40 30l30 92q10 28 40 28l340 0q30 0 40-28l30-92q12-30 40-30l120 0z m-400-550q104 0 177 73t73 177-73 177-177 73-177-73-73-177 73-177 177-73z m366 380q14 0 24 11t10 25-10 24-24 10q-36 0-36-34 0-16 11-26t25-10z" horiz-adv-x="1000" /> - -<glyph glyph-name="layout" unicode="" d="M170 650q80 0 80-80l0-90q0-80-80-80l-90 0q-80 0-80 80l0 90q0 80 80 80l90 0z m350 0q80 0 80-80l0-90q0-80-80-80l-90 0q-80 0-80 80l0 90q0 80 80 80l90 0z m-350-350q80 0 80-80l0-90q0-80-80-80l-90 0q-80 0-80 80l0 90q0 80 80 80l90 0z m350 0q80 0 80-80l0-90q0-80-80-80l-90 0q-80 0-80 80l0 90q0 80 80 80l90 0z" horiz-adv-x="600" /> - -<glyph glyph-name="menu" unicode="" d="M650 400q22 0 36-15t14-35-15-35-35-15l-600 0q-20 0-35 15t-15 35 14 35 36 15l600 0z m-600 100q-20 0-35 15t-15 35 14 35 36 15l600 0q22 0 36-15t14-35-15-35-35-15l-600 0z m600-300q22 0 36-15t14-35-15-35-35-15l-600 0q-20 0-35 15t-15 35 14 35 36 15l600 0z" horiz-adv-x="700" /> - -<glyph glyph-name="check" unicode="" d="M249 0q-34 0-56 28l-180 236q-16 24-12 52t26 46 51 14 47-28l118-154 296 474q16 24 43 30t53-8q24-16 30-43t-8-53l-350-560q-20-32-56-32z" horiz-adv-x="667" /> - -<glyph glyph-name="cancel" unicode="" d="M452 194q18-18 18-43t-18-43q-18-16-43-16t-43 16l-132 152-132-152q-18-16-43-16t-43 16q-16 18-16 43t16 43l138 156-138 158q-16 18-16 43t16 43q18 16 43 16t43-16l132-152 132 152q18 16 43 16t43-16q18-18 18-43t-18-43l-138-158z" horiz-adv-x="470" /> - -<glyph glyph-name="cancel-circled" unicode="" d="M420 770q174 0 297-123t123-297-123-297-297-123-297 123-123 297 123 297 297 123z m86-420l154 154-86 86-154-152-152 152-88-86 154-154-154-152 88-86 152 152 154-152 86 86z" horiz-adv-x="840" /> - -<glyph glyph-name="cancel-squared" unicode="" d="M700 750q42 0 71-29t29-71l0-600q0-40-29-70t-71-30l-600 0q-40 0-70 30t-30 70l0 600q0 42 30 71t70 29l600 0z m-146-638l86 86-154 152 154 154-86 86-154-152-152 152-88-86 154-154-154-152 88-86 152 152z" horiz-adv-x="800" /> - -<glyph glyph-name="plus" unicode="" d="M550 400q30 0 30-50t-30-50l-210 0 0-210q0-30-50-30t-50 30l0 210-210 0q-30 0-30 50t30 50l210 0 0 210q0 30 50 30t50-30l0-210 210 0z" horiz-adv-x="580" /> - -<glyph glyph-name="plus-circled" unicode="" d="M420 770q174 0 297-123t123-297-123-297-297-123-297 123-123 297 123 297 297 123z m52-470l200 0 0 102-200 0 0 202-102 0 0-202-202 0 0-102 202 0 0-202 102 0 0 202z" horiz-adv-x="840" /> - -<glyph glyph-name="plus-squared" unicode="" d="M700 750q42 0 71-29t29-71l0-600q0-40-29-70t-71-30l-600 0q-40 0-70 30t-30 70l0 600q0 42 30 71t70 29l600 0z m-50-450l0 100-200 0 0 200-100 0 0-200-200 0 0-100 200 0 0-200 100 0 0 200 200 0z" horiz-adv-x="800" /> - -<glyph glyph-name="minus" unicode="" d="M550 400q30 0 30-50t-30-50l-520 0q-30 0-30 50t30 50l520 0z" horiz-adv-x="580" /> - -<glyph glyph-name="minus-circled" unicode="" d="M420 770q174 0 297-123t123-297-123-297-297-123-297 123-123 297 123 297 297 123z m252-368l-504 0 0-102 504 0 0 102z" horiz-adv-x="840" /> - -<glyph glyph-name="minus-squared" unicode="" d="M700 750q42 0 71-29t29-71l0-600q0-40-29-70t-71-30l-600 0q-40 0-70 30t-30 70l0 600q0 42 30 71t70 29l600 0z m-50-450l0 100-500 0 0-100 500 0z" horiz-adv-x="800" /> - -<glyph glyph-name="help" unicode="" d="M494 740q86-62 86-184 0-64-42-124-12-20-88-80l-46-30q-40-34-48-60-6-16-8-44 0-14-16-14l-128 0q-16 0-16 12 4 98 28 124 16 22 48 48t56 42l24 14q22 16 34 34 28 44 28 70 0 40-26 78-28 36-92 36-68 0-94-44-28-42-28-92l-166 0q6 162 114 232 70 42 166 42 130 0 214-60z m-216-636q44 0 73-30t27-74q-2-46-32-73t-74-25q-44 0-73 29t-27 75 32 73 74 25z" horiz-adv-x="580" /> - -<glyph glyph-name="help-circled" unicode="" d="M454 810q190 2 326-130t140-322q2-190-131-327t-323-141q-190-2-327 131t-139 323q-4 190 130 327t324 139z m-2-740q30 0 49 19t19 47q2 30-17 49t-49 19l-2 0q-28 0-47-18t-21-46q0-30 19-49t47-21l2 0z m166 328q26 34 26 78 0 78-54 116-52 38-134 38-64 0-104-26-68-42-72-146l0-4 110 0 0 4q0 26 16 54 16 24 54 24 40 0 52-20 16-20 16-44 0-18-16-40-8-12-20-20l-6-4q-6-4-16-11t-20-15-21-17-17-17q-14-20-18-78l0-8 108 0 0 4q0 12 4 28 6 20 28 36l28 18q46 34 56 50z" horiz-adv-x="920" /> - -<glyph glyph-name="info" unicode="" d="M352 850q48 0 74-27t26-69q0-50-39-88t-95-38q-48 0-74 26t-24 72q0 46 35 85t97 39z m-206-1000q-100 0-54 178l60 254q14 56 0 56-12 0-54-18t-72-38l-26 44q90 78 189 126t151 48q78 0 36-162l-70-266q-16-64 6-64 44 0 118 60l30-40q-84-86-175-132t-139-46z" horiz-adv-x="460" /> - -<glyph glyph-name="info-circled" unicode="" d="M454 810q190 2 326-130t140-322q2-190-131-327t-323-141q-190-2-327 131t-139 323q-4 190 130 327t324 139z m52-152q-42 0-65-24t-23-50q-2-28 15-44t49-16q38 0 61 22t23 54q0 58-60 58z m-120-594q30 0 84 26t106 78l-18 24q-48-36-72-36-14 0-4 38l42 160q26 96-22 96-30 0-89-29t-115-75l16-26q52 34 74 34 12 0 0-34l-36-152q-26-104 34-104z" horiz-adv-x="920" /> - -<glyph glyph-name="back" unicode="" d="M750 540q40 0 70-29t30-71l0-290q0-40-30-70t-70-30l-690 0 0 140 650 0 0 210-500 0 0-110-210 180 210 180 0-110 540 0z" horiz-adv-x="850" /> - -<glyph glyph-name="home" unicode="" d="M888 336q16-16 11-27t-27-11l-84 0 0-310q0-14-1-21t-8-13-23-6l-204 0 0 310-204 0 0-310-194 0q-28 0-35 10t-7 30l0 310-84 0q-22 0-27 11t11 27l400 402q16 16 38 16t38-16z" horiz-adv-x="900" /> - -<glyph glyph-name="link" unicode="" d="M294 116q14 14 34 14t36-14q32-34 0-70l-42-40q-56-56-132-56-78 0-134 56t-56 132q0 78 56 134l148 148q70 68 144 77t128-43q16-16 16-36t-16-36q-36-32-70 0-50 48-132-34l-148-146q-26-26-26-64t26-62q26-26 63-26t63 26z m450 574q56-56 56-132 0-78-56-134l-158-158q-74-72-150-72-62 0-112 50-14 14-14 34t14 36q14 14 35 14t35-14q50-48 122 24l158 156q28 28 28 64 0 38-28 62-24 26-56 31t-60-21l-50-50q-16-14-36-14t-34 14q-34 34 0 70l50 50q54 54 127 51t129-61z" horiz-adv-x="800" /> - -<glyph glyph-name="attach" unicode="" d="M244-140q-102 0-170 72-72 70-74 166t84 190l496 496q80 80 174 54 44-12 79-47t47-79q26-96-54-176l-474-474q-40-40-88-46-48-4-80 28-30 24-27 74t47 92l332 334q24 26 50 0t0-50l-332-332q-44-44-20-70 12-8 24-6 24 4 46 26l474 474q50 50 34 108-16 60-76 76-54 14-108-36l-494-494q-66-76-64-143t52-117q50-48 117-50t141 62l496 494q24 24 50 0 26-22 0-48l-496-496q-82-82-186-82z" horiz-adv-x="939" /> - -<glyph glyph-name="lock" unicode="" d="M640 476q20 0 40-19t20-41l0-390q0-48-48-66l-60-18q-42-16-96-16l-290 0q-56 0-98 16l-60 18q-48 18-48 66l0 390q0 22 15 41t35 19l100 0 0 70q0 110 51 170t149 60 149-60 51-170l0-70 90 0z m-390 90l0-90 200 0 0 90q0 52-27 81t-73 29-73-29-27-81z" horiz-adv-x="700" /> - -<glyph glyph-name="lock-open" unicode="" d="M640 450q20 0 40-20t20-40l0-390q0-20-14-39t-34-25l-60-20q-52-16-96-16l-290 0q-46 0-98 16l-60 20q-20 6-34 25t-14 39l0 390q0 22 15 41t35 19l400 0 0 140q0 110-100 110t-100-110l0-40-100 0 0 20q0 110 51 170t149 60q200 0 200-230l0-120 90 0z" horiz-adv-x="700" /> - -<glyph glyph-name="eye" unicode="" d="M500 630q92 0 177-25t141-62 99-77 63-71 20-45-20-44-63-71-99-78-141-62-177-25-177 25-141 62-99 78-63 71-20 44 20 45 63 71 99 77 141 62 177 25z m0-494q92 0 157 63t65 151q0 90-65 153t-157 63-157-63-65-153q0-88 65-151t157-63z m0 214q8-8 37-2t50 11 25-9q0-44-33-75t-79-31-78 31-32 75q0 46 32 77t78 31q14 0 10-23t-12-47 2-38z" horiz-adv-x="1000" /> - -<glyph glyph-name="tag" unicode="" d="M944 830q36-106-8-199t-128-157l18-24q16-28 6-54l-48-158q-12-30-36-46l-464-328q-42-30-64 4l-210 304q-12 18-9 39t21 33l464 328q26 18 54 18l158 0q30 0 48-26l28-40q168 130 114 286-10 28 18 40 32 8 38-20z m-216-468q40 32 34 80l-32-16q-8-4-12-4-18 0-28 18-12 30 16 40l24 14q-48 34-92 0-28-18-34-51t14-61q18-26 51-32t59 12z" horiz-adv-x="960" /> - -<glyph glyph-name="bookmark" unicode="" d="M310 800q22 0 36-15t14-35l0-850-180 180-180-180 0 850q0 50 40 50l270 0z" horiz-adv-x="360" /> - -<glyph glyph-name="bookmarks" unicode="" d="M500 850q20 0 35-15t15-35l0-850-150 180 0 620q0 20-15 35t-35 15l-100 0q0 50 40 50l210 0z m-250-150q20 0 35-15t15-35l0-800-150 180-150-180 0 800q0 50 40 50l210 0z" horiz-adv-x="550" /> - -<glyph glyph-name="flag" unicode="" d="M874 616q14 6 22-1t0-19q-96-138-164-213t-110-90-73-2-60 37-63 40-93-4-139-86l90-352-100 0-184 720 92 34q90 66 152 86t98 3 64-51 62-71 79-62 129-20 198 51z" horiz-adv-x="900" /> - -<glyph glyph-name="thumbs-up" unicode="" d="M582 480q2-6 58-13t108-24 52-47q0-72-61-284t-107-212q-144 0-288 42t-144 88l0 342q0 14 15 34t46 45 53 41 62 43 46 31q50 34 104 100t85 104 41 26q48-76 29-137t-59-119-40-60z m-432-4q14 0 0-14-50-50-50-104l0-318q0-50 52-104 10-10-2-10-26 0-55 8t-62 45-33 99l0 242q0 62 33 100t63 47 54 9z" horiz-adv-x="800" /> - -<glyph glyph-name="thumbs-down" unicode="" d="M218 218q-2 6-57 13t-108 24-53 47q0 72 62 285t106 213q144 0 288-43t144-89l0-342q0-10-8-24t-25-30-32-29-42-32-41-29-41-28l-33-22q-50-34-104-100t-85-104-41-26q-48 76-29 137t59 119 40 60z m432 4q-12 0 2 14 48 50 48 104l0 318q0 50-52 104-10 10 2 10 26 0 55-8t62-45 33-99l0-242q0-48-18-81t-45-48-48-21-39-6z" horiz-adv-x="800" /> - -<glyph glyph-name="download" unicode="" d="M968 198q18-10 27-32t3-40l-28-154q-4-20-22-33t-40-13l-816 0q-22 0-40 13t-22 33l-28 154q-10 48 32 72l158 108 98 0-170-130 178 0q8 0 12-8l40-110 300 0 40 110q8 8 12 8l178 0-170 130 98 0z m-208 322l-260-244-260 244 166 0 0 256 190 0 0-256 164 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="upload" unicode="" d="M500 776l260-244-164 0 0-256-190 0 0 256-166 0z m468-578q18-10 27-32t3-40l-28-154q-4-20-22-33t-40-13l-816 0q-22 0-40 13t-22 33l-28 154q-10 48 32 72l158 108 98 0-170-130 178 0q8 0 12-8l40-110 300 0 40 110q8 8 12 8l178 0-170 130 98 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="upload-cloud" unicode="" d="M760 494q100 0 170-68t70-166-70-166-170-68l-190 0 0 190 106 0-176 230-174-230 104 0 0-190-248 0q-74 0-128 52t-54 124q0 74 53 126t129 52q14 0 20-2-2 12-2 38 0 108 78 184t188 76q90 0 160-52t94-134q28 4 40 4z" horiz-adv-x="1000" /> - -<glyph glyph-name="reply" unicode="" d="M900 10q-86 152-208 197t-330 45l0-218-362 334 362 322 0-192q90 0 168-27t131-70 96-95 69-104 44-95 24-69z" horiz-adv-x="900" /> - -<glyph glyph-name="reply-all" unicode="" d="M362 556l-212-188 212-196 0-138-362 334 362 322 0-134z m250-58q104 0 182-50t115-122 60-144 27-122l4-50q-86 154-168 198t-220 44l0-218-362 334 362 322 0-192z" horiz-adv-x="1000" /> - -<glyph glyph-name="forward" unicode="" d="M540 252q-210 0-332-45t-208-197q4 20 13 53t50 117 96 148 156 117 225 53l0 192 360-322-360-334 0 218z" horiz-adv-x="900" /> - -<glyph glyph-name="quote" unicode="" d="M146 680q146 0 184-146 38-140-40-302-80-168-224-204-32-8-66-8l0 70q112 0 182 108 54 86 26 146-16 36-62 36-60 0-103 44t-43 106 43 106 103 44z m420 0q146 0 184-146 38-140-40-302-80-168-224-204-32-8-66-8l0 70q112 0 182 108 54 86 26 146-16 36-62 36-60 0-103 44t-43 106 43 106 103 44z" horiz-adv-x="762" /> - -<glyph glyph-name="code" unicode="" d="M380 636q16-14 16-32t-16-30l-246-224 246-226q16-12 16-30t-16-32q-30-30-60 0l-320 288 320 286q30 30 60 0z m302 0l318-286-318-288q-32-30-62 0-32 32 0 62l248 226-248 224q-32 30 0 62 30 30 62 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="export" unicode="" d="M750 60l0 56 100 82 0-188q0-20-15-35t-35-15l-750 0q-20 0-35 15t-15 35l0 550q0 22 14 36t36 14l288 0q-32-24-59-49t-39-39l-10-12-130 0 0-450 650 0z m-82 348q-166 0-242-41t-160-181q0 8 1 22t9 56 22 79 44 83 70 79 107 56 149 23l0 156 332-250-332-260 0 178z" horiz-adv-x="1000" /> - -<glyph glyph-name="pencil" unicode="" d="M718 680q32-32 47-64t15-48l0-16-252-252-290-288-238-52 50 240 290 288 252 252q54 12 126-60z m-494-640l24 24q-2 44-52 94-22 22-45 35t-35 13l-14 2-22-24-18-80q28-16 46-34 24-24 36-48z" horiz-adv-x="780" /> - -<glyph glyph-name="feather" unicode="" d="M60-138q-6-20-26-8-18 8-16 34 4 100 50 226-100 154-52 316 10-32 32-78t44-80 32-30q8 4 0 83t-11 166 25 157q22 44 80 94t104 70q-24-46-33-94t-4-78 21-32q12 0 84 120t106 122q46 4 114-29t82-65q12-24 0-79t-40-83q-44-44-146-62t-114-24q-16-10 12-34 54-48 176-20-56-80-136-114t-132-38-54-10q-4-24 49-54t101-14q-30-56-63-84t-54-35-76-11-85-8z" horiz-adv-x="698" /> - -<glyph glyph-name="print" unicode="" d="M66 526q-26 0-22 22 4 10 12 14 2 0 49 17t93 32 58 15l44 0 0 150 380 0 0-150 46 0q12 0 57-15t92-32 49-17q18-8 12-26-4-10-20-10l-850 0z m860-56q20 0 37-19t17-41l0-174q0-22-17-41t-37-19l-100 0 44-250-760 0 44 250-98 0q-20 0-38 19t-18 41l0 174q0 22 18 41t38 19l870 0z m-716-444l560 0-70 324-420 0z" horiz-adv-x="980" /> - -<glyph glyph-name="retweet" unicode="" d="M250 190l272 0 128-140-448 0q-42 0-71 30t-29 70l0 302-102 0 176 198 174-198-100 0 0-262z m650 60l100 0-174-200-176 200 102 0 0 260-274 0-128 140 450 0q40 0 70-29t30-71l0-300z" horiz-adv-x="1000" /> - -<glyph glyph-name="keyboard" unicode="" d="M930 650q28 0 49-21t21-49l0-460q0-30-21-50t-49-20l-860 0q-28 0-49 20t-21 50l0 460q0 28 21 49t49 21l860 0z m-380-100l0-100 100 0 0 100-100 0z m150-150l-100 0 0-100 100 0 0 100z m-300 150l0-100 100 0 0 100-100 0z m150-150l-100 0 0-100 100 0 0 100z m-300 150l0-100 100 0 0 100-100 0z m150-150l-100 0 0-100 100 0 0 100z m-300 150l0-100 100 0 0 100-100 0z m150-150l-100 0 0-100 100 0 0 100z m-50-250l0 100-100 0 0-100 100 0z m550 0l0 100-500 0 0-100 500 0z m150 0l0 100-100 0 0-100 100 0z m-150 150l100 0 0 100-100 0 0-100z m150 150l0 100-200 0 0-100 200 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="comment" unicode="" d="M700 700q42 0 71-29t29-71l0-350q0-40-29-70t-71-30l-200 0 0-150-200 150-200 0q-40 0-70 30t-30 70l0 350q0 42 30 71t70 29l600 0z" horiz-adv-x="800" /> - -<glyph glyph-name="chat" unicode="" d="M290 240l350 0q2 0 6 2l4 0 0-92q0-40-29-70t-71-30l-250 0-150-150 0 150-50 0q-40 0-70 30t-30 70l0 300q0 42 30 71t70 29l190 0 0-310z m610 560q42 0 71-29t29-71l0-300q0-40-29-70t-71-30l-50 0 0-150-150 150-350 0 0 400q0 42 30 71t70 29l450 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="bell" unicode="" d="M632 426q16-34 40-52t45-22 44-23 35-55q22-62-74-161t-252-157q-164-58-297-45t-155 75q-20 54 12 111t18 111q-56 192-47 300t113 192q26 22 29 51t29 39q24 8 46-12t56-18q132 2 198-66t160-268z m-186-404q88 32 159 85t100 91 25 50q-8 22-49 33t-124 1-187-48q-102-38-173-87t-94-84-17-53q4-12 50-22t134-4 176 38z m-62 174q8 2 21 7t17 7l2-2q14-40-17-83t-89-63q-96-36-152 14 78 68 218 120z" horiz-adv-x="800" /> - -<glyph glyph-name="attention" unicode="" d="M957-24q10-16 0-34-10-16-30-16l-892 0q-18 0-28 16-13 18-2 34l446 782q8 18 30 18t30-18z m-420 50l0 100-110 0 0-100 110 0z m0 174l0 300-110 0 0-300 110 0z" horiz-adv-x="962" /> - -<glyph glyph-name="alert" unicode="" d="M885 234q20-16 16-33t-28-23l-78-22q-24-6-40-28t-14-48l4-82q2-24-14-34t-38 0l-86 44q-22 12-47 4t-35-30l-46-88q-12-22-29-23t-33 19l-50 78q-34 48-88 20l-122-70q-22-14-32-6t-2 32l54 164q8 24-4 44t-36 22l-106 12q-24 4-29 18t15 30l86 76q20 16 20 41t-20 41l-86 76q-20 16-16 33t28 23l78 22q24 6 41 28t15 48l-6 82q0 26 15 36t37 0l80-38q24-10 49-2t37 30l46 80q12 22 30 21t30-23l50-86q12-22 35-29t45 7l136 84q22 14 30 6t0-32l-60-170q-10-22 2-41t38-21l114-12q26-2 30-16t-16-30l-86-76q-18-16-18-41t18-41z m-384-92l0 104-100 0 0-104 100 0z m0 160l0 260-100 0 0-260 100 0z" horiz-adv-x="901" /> - -<glyph glyph-name="vcard" unicode="" d="M900 750q42 0 71-29t29-71l0-600q0-40-29-70t-71-30l-800 0q-40 0-70 30t-30 70l0 600q0 42 30 71t70 29l800 0z m0-700l0 600-800 0 0-600 800 0z m-450 196l0-90-250 0 0 90 250 0z m0 150l0-90-250 0 0 90 250 0z m0 150l0-90-250 0 0 90 250 0z m346-320l4-70-250 0q0 70 6 70 84 22 84 66 0 16-27 56t-27 88q0 110 90 110t90-110q0-48-28-88t-28-56q0-20 21-36t43-22z" horiz-adv-x="1000" /> - -<glyph glyph-name="address" unicode="" d="M426 800q20 0 20-20l0-860q0-20-20-20l-46 0q-20 0-20 20l0 440-176 0q-16 0-28 6-12 2-26 12l-120 82q-10 6-10 16t10 16l120 82q14 10 26 12 8 4 28 4l176 0 0 190q0 20 20 20l46 0z m564-208q10-6 10-16t-10-16l-118-82q-22-12-26-12-14-6-28-6l-302 0-40 230 342 0q18 0 28-4t26-12z" horiz-adv-x="1000" /> - -<glyph glyph-name="location" unicode="" d="M250 750q104 0 177-73t73-177q0-106-62-243t-126-223l-62-84q-10 12-27 35t-60 89-76 130-60 147-27 149q0 104 73 177t177 73z m0-388q56 0 96 40t40 96-40 95-96 39-95-39-39-95 39-96 95-40z" horiz-adv-x="500" /> - -<glyph glyph-name="map" unicode="" d="M984 600q16-10 16-30l0-584q0-20-16-30-8-6-16-6t-18 6l-216 136-216-136q-18-10-34 0l-218 136-216-136q-16-10-34 0-16 10-16 30l0 584q0 20 16 30l234 146q18 10 34 0l216-136 218 136q16 10 32 0z m-750-450l0 506-168-104 0-506z m234-104l0 506-168 104 0-506z m234 104l0 506-170-104 0-506z m232-104l0 506-168 104 0-506z" horiz-adv-x="1000" /> - -<glyph glyph-name="direction" unicode="" d="M848 768q8-8 11-16t-2-22-10-26-19-39-24-49q-54-112-147-286t-157-292l-66-118-54 380-380 56q442 246 696 368 20 10 48 25t39 20 25 9 23 1 17-11z m-92-96l-304-280 28-234z" horiz-adv-x="860" /> - -<glyph glyph-name="compass" unicode="" d="M474 830q198 2 340-136t146-336q2-200-136-342t-338-146q-198-2-341 137t-145 337q-4 200 135 342t339 144z m12-858q156 2 266 114t108 270-115 267-269 107q-158-2-267-114t-107-270 114-267 270-107z m-234 154q4 26 12 66t41 128 77 132 125 76 141 42l60 10q-4-26-12-66t-41-128-77-132q-42-42-124-74t-142-42z m180 276q-22-20-22-48t22-50q20-22 49-22t49 22q52 52 88 186-136-36-186-88z" horiz-adv-x="960" /> - -<glyph glyph-name="cup" unicode="" d="M340 760q152 0 249-41t91-87l-72-594q-2-14-34-36t-97-42-137-20-136 20-97 42-35 36l-72 594q-4 28 36 57t121 50 183 21z m0-216q72 0 137 15t98 33 33 30-33 29-98 32-137 15-137-15-98-32-33-29 33-30 98-33 137-15z" horiz-adv-x="681" /> - -<glyph glyph-name="trash" unicode="" d="M50 458q122-70 330-70t330 70l-54-486q-2-14-35-36t-100-43-141-21-140 21-100 43-36 36z m488 300q94-18 158-55t64-71l0-10q0-58-112-99t-268-41-268 41-112 99l0 10q0 34 64 71t158 55l42 48q22 26 70 26l92 0q52 0 70-26z m-54-112l84 0q-92 110-104 126-14 16-32 16l-102 0q-22 0-32-16l-106-126 84 0 64 66 82 0z" horiz-adv-x="760" /> - -<glyph glyph-name="doc" unicode="" d="M600 800q42 0 71-29t29-71l0-700q0-40-29-70t-71-30l-500 0q-40 0-70 30t-30 70l0 700q0 42 30 71t70 29l500 0z m0-800l0 700-500 0 0-700 500 0z" horiz-adv-x="700" /> - -<glyph glyph-name="docs" unicode="" d="M970 480q38-10 30-46l-150-556q-4-16-18-23t-30-3l-406 110q-16 4-24 18t-4 28l24 92-180-48q-40-10-50 26l-160 602q-10 36 28 48l454 122q16 4 30-3t18-23l66-244z m-888 190l144-542 392 106-144 540z m702-742l132 492-298 82 76-282q10-34-28-46l-196-52-26-102z" horiz-adv-x="1001" /> - -<glyph glyph-name="doc-landscape" unicode="" d="M0 600q0 42 30 71t70 29l800 0q42 0 71-29t29-71l0-500q0-40-29-70t-71-30l-800 0q-40 0-70 30t-30 70l0 500z m900 0l-800 0 0-500 800 0 0 500z" horiz-adv-x="1000" /> - -<glyph glyph-name="doc-text" unicode="" d="M212 308l0 90 280 0 0-90-280 0z m388 492q42 0 71-29t29-71l0-700q0-40-29-70t-71-30l-500 0q-40 0-70 30t-30 70l0 700q0 42 30 71t70 29l500 0z m0-800l0 700-500 0 0-700 500 0z m-110 592l0-88-280 0 0 88 280 0z m0-392l0-88-280 0 0 88 280 0z" horiz-adv-x="700" /> - -<glyph glyph-name="doc-text-inv" unicode="" d="M600 800q42 0 71-29t29-71l0-700q0-40-29-70t-71-30l-500 0q-40 0-70 30t-30 70l0 700q0 42 30 71t70 29l500 0z m-460-208l0-88 420 0 0 88-420 0z m420-480l0 88-420 0 0-88 420 0z m0 196l0 90-418 0 0-90 418 0z" horiz-adv-x="700" /> - -<glyph glyph-name="newspaper" unicode="" d="M700 800q42 0 71-29t29-71l0-700q0-40-29-70t-71-30l-600 0q-40 0-70 30t-30 70l0 700q0 42 30 71t70 29l600 0z m0-800l0 700-600 0 0-700 600 0z m-250 250l0-50-250 0 0 50 250 0z m150 200l0-50-200 0 0 50 200 0z m-200 50l0 100 200 0 0-100-200 0z m-50 100l0-200-150 0 0 200 150 0z m-50-250l0-50-100 0 0 50 100 0z m50-50l0 50 250 0 0-50-250 0z m250-150l0-50-400 0 0 50 400 0z m-100 50l0 50 100 0 0-50-100 0z" horiz-adv-x="800" /> - -<glyph glyph-name="book-open" unicode="" d="M340 238l0-68-200 80 0 68z m0 208l0-68-200 80 0 68z m538 346q22-12 22-42l0-640q0-34-32-46l-398-160q-8-2-10-2t-5-1-5-1-5 1-5 1l-10 2-398 160q-32 12-32 46l0 640q0 30 22 42 22 16 46 6l382-154 382 154q24 10 46-6z m-478-788l0 560-320 128 0-560z m420 128l0 560-320-128 0-560z m-60 186l0-68-200-80 0 68z m0 208l0-68-200-80 0 68z" horiz-adv-x="900" /> - -<glyph glyph-name="book" unicode="" d="M682 594q18-8 18-28l0-562q0-14-12-25t-28-11q-46 0-46 36l0 522q0 12-12 18l-404 216q-32 10-68-10-44-20-56-44l408-228q18-8 18-28l0-550q0-22-18-28-6-4-16-4-14 0-20 4-8 6-202 127t-212 131q-26 18-26 34l-6 524q0 28 14 52 28 46 102 77t116 9z" horiz-adv-x="700" /> - -<glyph glyph-name="folder" unicode="" d="M954 500q32 0 40-12t6-36l-42-452q-2-24-12-37t-42-13l-806 0q-52 0-56 50l-42 452q-2 24 6 36t40 12l908 0z m-34 110l10-40-846 0 14 132q4 20 20 34t36 14l164 0q52 0 86-34l30-30q32-36 86-36l340 0q20 0 38-12t22-28z" horiz-adv-x="1001" /> - -<glyph glyph-name="archive" unicode="" d="M840 600l0-50-696 0 0 50q0 22 13 35t25 15l608 0q6 0 14-1t22-14 14-35z m-148 150q6 0 14-1t22-14 14-35l-498 0q0 22 13 35t25 15l410 0z m248-200q34-32 38-46 6-18 0-54l-76-450q-4-22-20-35t-28-15l-710 0q-52 0-60 50-6 26-39 223t-39 227q-10 22-3 44t10 26 21 20l10 10 30 30 0-80 836 0 0 80z m-248-270l0 100-70 0 0-80-260 0 0 80-68 0 0-100q0-50 48-50l300 0q22 0 35 12t13 24z" horiz-adv-x="981" /> - -<glyph glyph-name="box" unicode="" d="M870 750q12 0 21-9t9-21l0-120-900 0 0 120q0 12 9 21t21 9l840 0z m-820-730l0 530 800 0 0-530q0-30-21-50t-49-20l-660 0q-28 0-49 20t-21 50z m250 430l0-100 300 0 0 100-300 0z" horiz-adv-x="900" /> - -<glyph glyph-name="rss" unicode="" d="M0 730q314 0 537-223t223-537l-118 0q0 266-188 453t-454 187l0 120z m0-238q218 0 371-153t153-369l-118 0q0 166-119 285t-287 119l0 118z m114-296q46 0 80-33t34-81q0-46-34-79t-80-33-80 33-34 79q0 48 34 81t80 33z" horiz-adv-x="760" /> - -<glyph glyph-name="phone" unicode="" d="M461 290q162 162 118 206l-8 8q-30 30-41 48t-4 54 49 88q20 24 37 39t35 16 30 1 29-13 24-18 26-25 21-22q48-48-6-194t-204-294q-150-150-295-205t-193-7q-2 2-23 22t-25 25-18 24-13 31 2 30 15 35 38 37q42 34 70 47t54 2 35-18 39-37q44-44 208 120z" horiz-adv-x="800" /> - -<glyph glyph-name="cog" unicode="" d="M760 350q0-72 80-122-12-40-34-82-70 18-136-44-54-58-34-136-40-20-84-36-46 82-132 82t-132-82q-44 16-84 36 20 80-34 136-54 54-136 34-14 26-34 82 82 52 82 132 0 72-82 124 20 56 34 82 74-18 136 44 54 56 34 136 42 22 84 34 46-80 132-80t132 80q42-12 84-34-20-78 34-136 66-62 136-44 22-42 34-82-80-50-80-124z m-340-182q76 0 129 53t53 129-53 130-129 54-129-54-53-130 53-129 129-53z" horiz-adv-x="840" /> - -<glyph glyph-name="tools" unicode="" d="M155 506q-8-8-11-22t-3-25-2-11q-2-2-17-15t-19-17q-16-14-28 4l-70 76q-11 12 2 24 2 2 18 14t20 16q6 6 27 6t37 14q14 14 18 38t10 30q2 0 9 7t26 22 41 31q134 90 186 96 122 0 148-2 12 0-8-8-120-52-152-76-80-56-36-114 34-46 38-48 8-8-2-14-2-2-38-35t-38-35q-14-8-18-4-42 48-71 60t-67-12z m286-26l410-476q18-22-2-38l-48-42q-22-14-38 4l-414 472q-8 8 0 20l72 62q12 8 20-2z m554 202q16-104-16-166-50-88-154-62-56 12-100-32l-82-78-68 78 68 70q24 24 31 53t6 65 5 58q12 56 140 112 12 6 18-3t2-15q-12-12-46-80-14-10-12-35t40-53q58-40 96 22 6 12 26 41t22 33q4 10 13 9t11-17z m-858-684l254 248 76-86-246-242q-20-20-38-4l-46 46q-22 18 0 38z" horiz-adv-x="1000" /> - -<glyph glyph-name="share" unicode="" d="M650 200q62 0 106-43t44-107q0-62-44-106t-106-44-106 44-44 106q0 6 1 14t1 12l-260 156q-42-32-92-32-62 0-106 44t-44 106 44 106 106 44q54 0 92-30l260 156q0 4-1 12t-1 12q0 62 44 106t106 44 106-43 44-107q0-62-44-106t-106-44q-52 0-90 32l-262-156q2-8 2-26 0-16-2-24l262-156q36 30 90 30z" horiz-adv-x="800" /> - -<glyph glyph-name="shareable" unicode="" d="M340 350q0 68 47 114t113 46 113-46 47-114q0-66-47-113t-113-47-113 47-47 113z m-114 60q-14-60-66-60l-160 0 0 120 118 0q40 124 145 202t237 78q164 0 284-116 16-18 16-43t-16-43q-18-16-43-16t-43 16q-78 82-198 82-100 0-176-62t-98-158z m614-60l160 0 0-120-118 0q-40-124-144-202t-238-78q-164 0-282 118-18 18-18 43t18 41q16 18 41 18t43-18q82-82 198-82 100 0 176 63t98 157q12 60 66 60z" horiz-adv-x="1000" /> - -<glyph glyph-name="basket" unicode="" d="M150 0q0 40 30 70t70 30q42 0 71-30t29-70q0-42-29-71t-71-29q-40 0-70 29t-30 71z m500 0q0 40 30 70t70 30q42 0 71-30t29-70q0-42-29-71t-71-29q-40 0-70 29t-30 71z m-322 236q-36-10-34-23t44-13l562 0 0-76q0-20-20-20l-654 0q-20 0-20 20l0 76-10 46-98 454-98 0 0 80q0 20 20 20l156 0q20 0 20-20l0-86 704 0 0-274q0-22-18-26z" horiz-adv-x="900" /> - -<glyph glyph-name="bag" unicode="" d="M835 668q28-26 24-60l-98-648q-8-30-38-30l-586 0q-28 0-40 30-94 620-96 648-5 34 22 60 6 6 54 43t56 43q18 16 56 16l480 0q38 0 56-16 78-58 110-86z m-406-436q56 0 98 34t63 89 30 89 13 66l-92 0q-38-188-112-188t-112 188l-92 0q46-278 204-278z m-352 368l704 0-110 116-484 0z" horiz-adv-x="859" /> - -<glyph glyph-name="calendar" unicode="" d="M800 700q42 0 71-29t29-71l0-600q0-40-29-70t-71-30l-700 0q-40 0-70 30t-30 70l0 600q0 42 30 71t70 29l46 0 0-100 160 0 0 100 290 0 0-100 160 0 0 100 44 0z m0-700l0 400-700 0 0-400 700 0z m-540 800l0-170-70 0 0 170 70 0z m450 0l0-170-70 0 0 170 70 0z" horiz-adv-x="900" /> - -<glyph glyph-name="login" unicode="" d="M800 800q42 0 71-29t29-71l0-700q0-40-29-70t-71-30l-450 0q-40 0-69 30t-29 70l0 100 98 0 0-100 450 0 0 700-450 0 0-150-98 0 0 150q0 42 29 71t69 29l450 0z m-350-670l0 120-450 0 0 150 450 0 0 120 200-194z" horiz-adv-x="900" /> - -<glyph glyph-name="logout" unicode="" d="M502 0l0 100 98 0 0-100q0-40-29-70t-71-30l-400 0q-40 0-70 30t-30 70l0 700q0 42 30 71t70 29l400 0q42 0 71-29t29-71l0-150-98 0 0 150-402 0 0-700 402 0z m398 326l-198-196 0 120-450 0 0 150 450 0 0 120z" horiz-adv-x="900" /> - -<glyph glyph-name="mic" unicode="" d="M620 488q20 0 20-20l0-138q0-92-69-164t-201-84l0-132 130 0q20 0 20-20l0-60q0-20-20-20l-360 0q-20 0-20 20l0 60q0 20 20 20l130 0 0 132q-132 12-201 84t-69 164l0 138q0 20 20 20l30 0q20 0 20-20l0-138q0-66 59-123t191-57 191 57 59 123l0 138q0 20 20 20l30 0z m-300-238q-80 0-115 25t-35 55l0 158 300 0 0-158q0-30-35-55t-115-25z m150 520l0-212-300 0 0 212q0 30 35 55t115 25 115-25 35-55z" horiz-adv-x="640" /> - -<glyph glyph-name="mute" unicode="" d="M868 778q16-16 16-36t-16-36l-782-782q-18-14-34-14-18 0-36 14-16 14-16 36t16 36l782 782q34 32 70 0z m-216-386l50 50q74-92 101-172t-7-116q-24-24-75-57t-131-71-161-45-165 23l278 276q44-32 88-54t67-25 33 1q6 10 2 34t-26 68-54 88z m-276 62l-270-270q-40 132 28 283t132 215q34 32 105 11t159-85l-52-50q-58 38-105 53t-57 5q-4-8-2-28t19-58 43-76z" horiz-adv-x="884" /> - -<glyph glyph-name="sound" unicode="" d="M176 588q42 42 149-5t217-157 157-217 5-149q-28-28-92-67t-156-78-194-29-176 84-84 176 29 194 78 156 67 92z m464-480q8 10-3 49t-49 101-96 118q-56 58-118 96t-101 49-49 3q-8-10 3-49t49-101 94-120q58-56 120-94t101-49 49-3z m6 394q-18 0-34 16-16 14-16 35t16 35l94 96q36 32 72 0 32-36 0-72l-96-94q-16-16-36-16z m-180 124q-18 10-23 30t5 38l54 96q26 44 68 20 18-10 23-30t-5-38l-54-96q-14-26-42-26-14 0-26 6z m438-150q10-18 4-38t-24-30l-96-54q-16-8-24-8-28 0-44 26-10 18-4 38t24 30l96 54q18 10 38 5t30-23z" horiz-adv-x="910" /> - -<glyph glyph-name="volume" unicode="" d="M896 180q0-34-24-57t-56-23l-780 0q-22 0-31 5t-3 15 24 20l802 452q28 18 48 7t20-45l0-374z" horiz-adv-x="896" /> - -<glyph glyph-name="clock" unicode="" d="M460 810q190 0 325-135t135-325-135-325-325-135-325 135-135 325 135 325 325 135z m0-820q150 0 255 106t105 254q0 150-105 255t-255 105q-148 0-254-105t-106-255q0-148 106-254t254-106z m36 620l0-244 150-150-50-50-170 170 0 274 70 0z" horiz-adv-x="920" /> - -<glyph glyph-name="hourglass" unicode="" d="M560 622q0-44-48-96t-97-99-49-77 49-76 97-97 48-97l0-118q0-34-86-73t-194-39-194 39-86 73l0 118q0 46 48 97t97 97 49 76-49 77-97 99-48 96l0 118q0 32 87 71t193 39 193-39 87-71l0-118z m-482 112l-18-14q-4-8 4-14 92-52 216-52 132 0 220 50 14 10-16 30-96 54-202 54-120 0-204-54z m228-384q0 18 4 33t18 33 20 25 31 31 29 28q92 92 92 122l2 50q-100-54-222-54t-222 54l4-50q0-32 90-122 6-6 22-21t23-22l19-19t17-21 11-20 9-23 3-24q0-10-1-19t-6-18-8-16-11-17l-12-15t-15-16-16-15-18-16-17-16q-90-90-90-122l0-66q8 4 66 23t92 43 34 58q0 30 26 30t26-30q0-34 33-58t94-43 67-23l0 66q0 30-92 122-4 4-21 20t-22 21-18 19-18 22-12 20-9 23-2 23z" horiz-adv-x="560" /> - -<glyph glyph-name="lamp" unicode="" d="M209-110l0 104 282 0 0-104q-70-42-142-40-70-2-140 40z m276 164l-270 0q0 72-36 140t-78 113-74 112-26 139q8 120 94 206t254 86q170 0 255-86t95-206q4-60-16-113t-52-96-65-85-57-96-24-114z m-378 496q-4-4 0-20t2-20 5-19 6-18 8-18 11-19 13-19 14-19 15-21 16-23q88-122 112-212l82 0q24 94 112 212 4 6 25 35t25 36 17 29 16 33 6 28 1 35q-16 196-244 196-226 0-242-196z" horiz-adv-x="700" /> - -<glyph glyph-name="light-down" unicode="" d="M350 510q68 0 114-47t46-113q0-68-46-114t-114-46q-66 0-113 46t-47 114q0 66 47 113t113 47z m0-264q44 0 73 30t29 74q0 42-29 72t-73 30q-42 0-72-30t-30-72q0-44 30-74t72-30z m-300 144q20 0 35-12t15-28q0-40-50-40t-50 40q0 16 15 28t35 12z m546 204q28-28-8-64-14-14-33-16t-29 10q-12 12-10 31t16 33q36 34 64 6z m54-204q20 0 35-12t15-28q0-40-50-40-48 0-48 40 0 16 14 28t34 12z m-300-290q16 0 28-15t12-35-12-35-28-15-28 15-12 35 12 35 28 15z m-238 62q36 36 64 8t-8-64q-14-14-33-16t-29 8q-30 28 6 64z m-10 430q28 28 64-8 14-14 16-33t-8-29q-30-28-64 6-36 36-8 64z m432-484q-34 36-6 64t64-8q14-14 16-33t-10-29q-30-28-64 6z m-184 492q-16 0-28 15t-12 35 12 35 28 15 28-15 12-35-12-35-28-15z" horiz-adv-x="700" /> - -<glyph glyph-name="light-up" unicode="" d="M950 390q20 0 35-12t15-28q0-40-50-40l-48 0q-50 0-50 40 0 16 15 28t35 12l48 0z m-450 234q114 0 195-80t81-194q0-116-81-196t-195-80-194 80-80 196q0 114 80 194t194 80z m0-474q82 0 141 58t59 142q0 82-59 141t-141 59-141-59-59-141q0-84 59-142t141-58z m-350 200q0-40-50-40l-50 0q-50 0-50 40 0 16 15 28t35 12l50 0q20 0 35-12t15-28z m350 350q-16 0-28 15t-12 35l0 50q0 20 12 35t28 15 28-15 12-35l0-50q0-20-12-35t-28-15z m0-700q16 0 28-15t12-35l0-50q0-20-12-35t-28-15-28 15-12 35l0 50q0 20 12 35t28 15z m368 660l-34-34q-34-34-64-8-28 28 8 64 4 6 34 36 36 34 64 6t-8-64z m-700-588q14 16 33 18t29-10q12-12 10-31t-16-33l-36-36q-14-14-33-16t-29 10q-30 28 6 64 6 4 36 34z m20 646l36-36q36-36 6-64-10-10-29-8t-33 16q-30 30-36 34-14 14-16 33t10 31q10 12 29 10t33-16z m590-702q-36 36-8 64t64-8l34-34q36-36 8-64t-64 6q-30 30-34 36z" horiz-adv-x="1000" /> - -<glyph glyph-name="adjust" unicode="" d="M950 390q20 0 35-12t15-28q0-40-50-40l-48 0q-50 0-50 40 0 16 15 28t35 12l48 0z m-450 234q114 0 195-80t81-194q0-116-81-196t-195-80-194 80-80 196q0 114 80 194t194 80z m6-474l0 400q-86 0-146-59t-60-141q0-84 60-142t146-58z m-356 200q0-40-50-40l-50 0q-50 0-50 40 0 16 15 28t35 12l50 0q20 0 35-12t15-28z m350 350q-16 0-28 15t-12 35l0 50q0 20 12 35t28 15 28-15 12-35l0-50q0-20-12-35t-28-15z m0-700q16 0 28-15t12-35l0-50q0-20-12-35t-28-15-28 15-12 35l0 50q0 20 12 35t28 15z m368 660l-34-34q-34-34-64-8-28 28 8 64 4 6 34 36 36 34 64 6t-8-64z m-700-588q14 16 33 18t29-10q12-12 10-31t-16-33l-36-36q-14-14-33-16t-29 10q-30 28 6 64 6 4 36 34z m20 646l36-36q36-36 6-64-10-10-29-8t-33 16q-30 30-36 34-14 14-16 33t10 31q10 12 29 10t33-16z m590-702q-36 36-8 64t64-8l34-34q36-36 8-64t-64 6q-30 30-34 36z" horiz-adv-x="1000" /> - -<glyph glyph-name="block" unicode="" d="M480 830q200 0 340-140t140-340q0-198-140-339t-340-141q-198 0-339 141t-141 339q0 200 141 340t339 140z m258-220z m-622-260q0-132 82-230l514 514q-100 82-232 82-152 0-258-107t-106-259z m106-258z m258-106q152 0 259 107t107 257q0 130-82 232l-514-514q98-82 230-82z" horiz-adv-x="960" /> - -<glyph glyph-name="resize-full" unicode="" d="M476 746l316 0 0-316-100 124-146-152-100 100 152 146z m-230-444l100-100-152-146 122-100-316 0 0 316 100-122z" horiz-adv-x="792" /> - -<glyph glyph-name="resize-small" unicode="" d="M156 146l-106 100 296 0 0-296-100 106-146-156-100 100z m744 554l-154-144 104-100-294 0 0 294 100-104 144 154z" horiz-adv-x="900" /> - -<glyph glyph-name="popup" unicode="" d="M700 750q42 0 71-29t29-71l0-400q0-40-29-70t-71-30l-400 0q-40 0-70 30t-30 70l0 402q0 40 29 69t71 29l400 0z m0-500l0 400-400 0 0-400 400 0z m-600 100l0-300 300 0 0-100-300 0q-40 0-70 30t-30 70l0 300 100 0z" horiz-adv-x="800" /> - -<glyph glyph-name="publish" unicode="" d="M900 800q42 0 71-30t29-70l0-600q0-42-29-71t-71-29l-198 0 0 98 200 0 0 462-802 0 0-462 200 0 0-98-200 0q-40 0-70 29t-30 71l0 600q0 40 30 70t70 30l800 0z m-770-168q38 0 38 38 0 16-11 26t-27 10-27-11-11-25q0-16 11-27t27-11z m100 0q38 0 38 38 0 16-11 26t-27 10-27-11-11-25q0-16 11-27t27-11z m672 6l0 62-602 0 0-62 602 0z m-404-198l242-240-150 0 0-300-184 0 0 300-150 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="window" unicode="" d="M900 750q42 0 71-30t29-70l0-600q0-42-29-71t-71-29l-800 0q-40 0-70 29t-30 71l0 600q0 40 30 70t70 30l800 0z m-670-94q-16 0-27-11t-11-25q0-16 11-27t27-11q38 0 38 38 0 16-11 26t-27 10z m-138-36q0-16 11-27t27-11q38 0 38 38 0 16-11 26t-27 10-27-11-11-25z m810-570l0 460-802 0 0-460 802 0z m0 540l0 60-602 0 0-60 602 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="arrow-combo" unicode="" d="M230 850l230-364-460 0z m0-1000l-230 366 460 0z" horiz-adv-x="460" /> - -<glyph glyph-name="down-circled" unicode="" d="M460 810q190 0 325-135t135-325-135-325-325-135-325 135-135 325 135 325 325 135z m0-820q148 0 254 106t106 254q0 150-106 255t-254 105-254-105-106-255q0-148 106-254t254-106z m90 554l0-206 112 0-202-190-202 190 112 0 0 206 180 0z" horiz-adv-x="920" /> - -<glyph glyph-name="left-circled" unicode="" d="M920 350q0-190-135-325t-325-135-325 135-135 325q0 192 135 326t325 134 325-134 135-326z m-820 0q0-148 106-254t254-106 254 106 106 254q0 150-106 255t-254 105-254-105-106-255z m552-90l-204 0 0-112-190 202 190 204 0-114 204 0 0-180z" horiz-adv-x="920" /> - -<glyph glyph-name="right-circled" unicode="" d="M0 350q0 190 135 325t325 135 325-135 135-325-135-325-325-135-325 135-135 325z m820 0q0 150-105 255t-255 105q-148 0-254-105t-106-255q0-148 106-254t254-106q150 0 255 106t105 254z m-552 90l204 0 0 114 190-204-190-202 0 112-204 0 0 180z" horiz-adv-x="920" /> - -<glyph glyph-name="up-circled" unicode="" d="M460-110q-190 0-325 135t-135 325q0 192 135 326t325 134 325-134 135-326q0-190-135-325t-325-135z m0 820q-148 0-254-105t-106-255q0-148 106-254t254-106q150 0 255 106t105 254q0 150-105 255t-255 105z m-90-552l0 204-112 0 202 192 202-192-112 0 0-204-180 0z" horiz-adv-x="920" /> - -<glyph glyph-name="down-open" unicode="" d="M564 422l-234-224q-18-18-40-18t-40 18l-234 224q-16 16-16 41t16 41q38 38 78 0l196-188 196 188q40 38 78 0 16-16 16-41t-16-41z" horiz-adv-x="580" /> - -<glyph glyph-name="left-open" unicode="" d="M242 626q14 16 39 16t41-16q38-36 0-80l-186-196 186-194q38-44 0-80-16-16-40-16t-40 16l-226 236q-16 16-16 38 0 24 16 40 206 214 226 236z" horiz-adv-x="341" /> - -<glyph glyph-name="right-open" unicode="" d="M98 626l226-236q16-16 16-40 0-22-16-38l-226-236q-16-16-40-16t-40 16q-36 36 0 80l186 194-186 196q-36 44 0 80 16 16 41 16t39-16z" horiz-adv-x="340" /> - -<glyph glyph-name="up-open" unicode="" d="M564 280q16-16 16-41t-16-41q-38-38-78 0l-196 188-196-188q-40-38-78 0-16 16-16 41t16 41l234 224q16 16 40 16t40-16z" horiz-adv-x="580" /> - -<glyph glyph-name="down-open-mini" unicode="" d="M405 470q22 26 48 0 26-22 0-48l-196-192q-22-22-48 0l-196 192q-26 26 0 48 24 24 50 0l170-156z" horiz-adv-x="466" /> - -<glyph glyph-name="left-open-mini" unicode="" d="M252 180q26-26 0-48-26-26-48 0l-192 194q-24 24 0 50l192 194q22 26 48 0 26-22 0-48l-156-172z" horiz-adv-x="265" /> - -<glyph glyph-name="right-open-mini" unicode="" d="M13 180l158 170-158 172q-26 26 0 48 26 26 48 0l192-194q24-26 0-50l-192-194q-22-26-48 0-26 22 0 48z" horiz-adv-x="265" /> - -<glyph glyph-name="up-open-mini" unicode="" d="M62 230q-26-22-50 0-24 24 0 50l196 190q26 26 48 0l196-190q24-26 0-50-24-22-50 0l-170 158z" horiz-adv-x="464" /> - -<glyph glyph-name="down-open-big" unicode="" d="M63 570l370-356 372 356q22 26 48 0 26-22 0-48l-396-392q-22-22-48 0l-396 392q-26 26 0 48 24 24 50 0z" horiz-adv-x="866" /> - -<glyph glyph-name="left-open-big" unicode="" d="M452-20q26-26 0-48-26-26-48 0l-392 394q-24 24 0 50l392 394q22 26 48 0 26-22 0-48l-358-372z" horiz-adv-x="465" /> - -<glyph glyph-name="right-open-big" unicode="" d="M13-20l358 370-358 372q-26 26 0 48 26 26 48 0l392-394q24-26 0-50l-392-394q-22-26-48 0-26 22 0 48z" horiz-adv-x="465" /> - -<glyph glyph-name="up-open-big" unicode="" d="M804 130l-372 358-370-358q-26-22-50 0-24 24 0 50l396 390q26 26 48 0l396-390q24-26 0-50-26-22-48 0z" horiz-adv-x="864" /> - -<glyph glyph-name="down" unicode="" d="M660 366l-330-380-330 380 192 0 0 350 276 0 0-350 192 0z" horiz-adv-x="660" /> - -<glyph glyph-name="left" unicode="" d="M378 20l-378 330 378 330 0-190 352 0 0-278-352 0 0-192z" horiz-adv-x="730" /> - -<glyph glyph-name="right" unicode="" d="M350 680l380-330-380-330 0 192-350 0 0 278 350 0 0 190z" horiz-adv-x="730" /> - -<glyph glyph-name="up" unicode="" d="M660 336l-192 0 0-350-276 0 0 350-192 0 330 380z" horiz-adv-x="660" /> - -<glyph glyph-name="down-dir" unicode="" d="M460 550l-230-400-230 400 460 0z" horiz-adv-x="460" /> - -<glyph glyph-name="left-dir" unicode="" d="M400 580l0-460-400 230z" horiz-adv-x="400" /> - -<glyph glyph-name="right-dir" unicode="" d="M0 580l400-230-400-230 0 460z" horiz-adv-x="400" /> - -<glyph glyph-name="up-dir" unicode="" d="M0 150l230 400 230-400-460 0z" horiz-adv-x="460" /> - -<glyph glyph-name="down-bold" unicode="" d="M760 366l-380-380-380 380 192 0 0 350 376 0 0-350 192 0z" horiz-adv-x="760" /> - -<glyph glyph-name="left-bold" unicode="" d="M378 730l0-190 352 0 0-378-352 0 0-192-378 380z" horiz-adv-x="730" /> - -<glyph glyph-name="right-bold" unicode="" d="M350 730l380-380-380-380 0 192-350 0 0 378 350 0 0 190z" horiz-adv-x="730" /> - -<glyph glyph-name="up-bold" unicode="" d="M760 336l-192 0 0-350-376 0 0 350-192 0 380 380z" horiz-adv-x="760" /> - -<glyph glyph-name="down-thin" unicode="" d="M500 100l-250-240-250 240 162 0 0 740 176 0 0-740 162 0z" horiz-adv-x="500" /> - -<glyph glyph-name="left-thin" unicode="" d="M240 100l-240 250 240 250 0-160 740 0 0-178-740 0 0-162z" horiz-adv-x="980" /> - -<glyph glyph-name="right-thin" unicode="" d="M742 100l0 162-742 0 0 178 742 0 0 160 238-250z" horiz-adv-x="980" /> - -<glyph glyph-name="up-thin" unicode="" d="M500 602l-162 0 0-742-176 0 0 742-162 0 250 238z" horiz-adv-x="500" /> - -<glyph glyph-name="ccw" unicode="" d="M532 736q170 0 289-120t119-290-119-290-289-120q-142 0-252 88l70 74q84-60 182-60 126 0 216 90t90 218-90 218-216 90q-124 0-214-87t-92-211l142 0-184-204-184 204 124 0q2 166 122 283t286 117z" horiz-adv-x="940" /> - -<glyph glyph-name="cw" unicode="" d="M408 760q168 0 287-116t123-282l122 0-184-206-184 206 144 0q-4 124-94 210t-214 86q-126 0-216-90t-90-218q0-126 90-216t216-90q104 0 182 60l70-76q-110-88-252-88-168 0-288 120t-120 290 120 290 288 120z" horiz-adv-x="940" /> - -<glyph glyph-name="arrows-ccw" unicode="" d="M186 140l116 116 0-292-276 16 88 86q-116 122-114 290t120 288q100 100 240 116l4-102q-100-16-172-88-88-88-90-213t84-217z m332 598l276-16-88-86q116-122 114-290t-120-288q-96-98-240-118l-2 104q98 16 170 88 88 88 90 213t-84 217l-114-116z" horiz-adv-x="820" /> - -<glyph glyph-name="level-down" unicode="" d="M100 200q-42 0-71 30t-29 70l0 350 140 0 0-310 364 0 0 150 240-220-240-220 0 150-404 0z" horiz-adv-x="744" /> - -<glyph glyph-name="level-up" unicode="" d="M200 350l0-90-200 160 200 170 0-100 550 0q40 0 70-29t30-71l0-280-140 0 0 240-510 0z" horiz-adv-x="850" /> - -<glyph glyph-name="shuffle" unicode="" d="M754 516q-54 0-105-32t-80-66-83-104q-48-62-75-94t-78-77-107-66-122-21l-104 0 0 140 104 0q54 0 106 32t81 66 83 104q62 82 101 126t116 88 163 44l36 0 0 120 210-180-210-180 0 100-36 0z m-484-88q-74 78-166 78l-104 0 0 140 104 0q140 0 254-108-14-16-37-45t-27-33q-8-12-24-32z m520-242l0 100 210-180-210-180 0 120-36 0q-140 0-260 116 46 58 72 92 0 2 6 9t8 11q84-88 174-88l36 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="loop" unicode="" d="M800 540q42 0 71-29t29-71l0-290q0-40-29-70t-71-30l-700 0q-40 0-70 30t-30 70l0 290q0 42 30 71t70 29l250 0 0 110 200-180-200-180 0 110-210 0 0-210 620 0 0 210-150 0 0 140 190 0z" horiz-adv-x="900" /> - -<glyph glyph-name="switch" unicode="" d="M700 592l0-140-500 0 0-90-200 160 200 170 0-100 500 0z m300-420l-200-160 0 90-500 0 0 140 500 0 0 100z" horiz-adv-x="1000" /> - -<glyph glyph-name="play" unicode="" d="M486 376q14-10 14-26 0-14-14-24l-428-266q-24-16-41-6t-17 40l0 514q0 30 17 40t41-6z" horiz-adv-x="500" /> - -<glyph glyph-name="stop" unicode="" d="M526 650q74 0 74-64l0-470q0-66-74-66l-450 0q-76 0-76 66l0 470q0 36 18 50t58 14l450 0z" horiz-adv-x="600" /> - -<glyph glyph-name="pause" unicode="" d="M440 700q90 0 90-64l0-570q0-66-90-66t-90 66l0 570q0 64 90 64z m-350 0q90 0 90-64l0-570q0-66-90-66t-90 66l0 570q0 64 90 64z" horiz-adv-x="530" /> - -<glyph glyph-name="record" unicode="" d="M350 700q146 0 248-102t102-248q0-144-102-247t-248-103-248 103-102 247q0 146 102 248t248 102z" horiz-adv-x="700" /> - -<glyph glyph-name="to-end" unicode="" d="M412 374q14-10 14-24 0-12-14-22l-362-228q-22-14-36-5t-14 35l0 442q0 26 14 35t36-5z m114 268q74 0 74-58l0-466q0-58-74-58-76 0-76 58l0 466q0 58 76 58z" horiz-adv-x="600" /> - -<glyph glyph-name="to-start" unicode="" d="M174 350q0 14 14 24l364 228q20 14 34 5t14-35l0-442q0-26-14-35t-34 5l-364 228q-14 10-14 22z m-174 234q0 58 76 58 74 0 74-58l0-466q0-58-74-58-76 0-76 58l0 466z" horiz-adv-x="600" /> - -<glyph glyph-name="fast-forward" unicode="" d="M866 374q14-10 14-24t-14-22l-372-248q-22-14-37-6t-15 36l0 482q0 28 15 36t37-6z m-454 0q14-10 14-24t-14-22l-360-248q-20-14-36-6t-16 36l0 482q0 28 16 36t36-6z" horiz-adv-x="880" /> - -<glyph glyph-name="fast-backward" unicode="" d="M0 350q0 14 14 24l374 248q20 14 36 6t16-36l0-482q0-28-16-36t-36 6l-374 248q-14 8-14 22z m454 0q0 14 14 24l360 248q20 14 36 6t16-36l0-482q0-28-16-36t-36 6l-360 248q-14 8-14 22z" horiz-adv-x="880" /> - -<glyph glyph-name="progress-0" unicode="" d="M1000 450l0-250q0-42-29-71t-71-29l-800 0q-40 0-70 29t-30 71l0 300q0 40 30 70t70 30l800 0q42 0 71-30t29-70l0-50z m-100-250l0 300-800 0 0-300 800 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="progress-1" unicode="" d="M1000 450l0-250q0-42-29-71t-71-29l-800 0q-40 0-70 29t-30 71l0 300q0 40 30 70t70 30l800 0q42 0 71-30t29-70l0-50z m-100-250l0 300-800 0 0-300 800 0z m-750 50l0 198 200 0 0-198-200 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="progress-2" unicode="" d="M1000 450l0-250q0-42-29-71t-71-29l-800 0q-40 0-70 29t-30 71l0 300q0 40 30 70t70 30l800 0q42 0 71-30t29-70l0-50z m-100-250l0 300-800 0 0-300 800 0z m-750 50l0 198 200 0 0-198-200 0z m250 0l0 198 200 0 0-198-200 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="progress-3" unicode="" d="M1000 450l0-250q0-42-29-71t-71-29l-800 0q-40 0-70 29t-30 71l0 300q0 40 30 70t70 30l800 0q42 0 71-30t29-70l0-50z m-100-250l0 300-800 0 0-300 800 0z m-750 50l0 198 200 0 0-198-200 0z m250 0l0 198 200 0 0-198-200 0z m250 198l200 0 0-198-200 0 0 198z" horiz-adv-x="1000" /> - -<glyph glyph-name="target" unicode="" d="M430 780q178 0 304-126t126-304-126-304-304-126-304 126-126 304 126 304 304 126z m36-778q124 14 212 102t100 212l-192 0 0 70 192 0q-12 124-100 212t-212 102l0-194-70 0 0 194q-124-14-213-102t-101-212l194 0 0-70-194 0q12-124 101-212t213-102l0 194 70 0 0-194z" horiz-adv-x="860" /> - -<glyph glyph-name="list" unicode="" d="M100 200q20 0 35-15t15-35-15-35-35-15l-50 0q-20 0-35 15t-15 35 14 35 36 15l50 0z m0 200q20 0 35-15t15-35-15-35-35-15l-50 0q-20 0-35 15t-15 35 14 35 36 15l50 0z m0 200q20 0 35-15t15-35-15-35-35-15l-50 0q-20 0-35 15t-15 35 14 35 36 15l50 0z m200-100q-20 0-35 15t-15 35 15 35 35 15l350 0q22 0 36-15t14-35-15-35-35-15l-350 0z m350-100q22 0 36-15t14-35-15-35-35-15l-350 0q-20 0-35 15t-15 35 15 35 35 15l350 0z m0-200q22 0 36-15t14-35-15-35-35-15l-350 0q-20 0-35 15t-15 35 15 35 35 15l350 0z" horiz-adv-x="700" /> - -<glyph glyph-name="list-add" unicode="" d="M350 400q22 0 36-15t14-35-15-35-35-15l-300 0q-20 0-35 15t-15 35 14 35 36 15l300 0z m0-200q22 0 36-15t14-35-15-35-35-15l-300 0q-20 0-35 15t-15 35 14 35 36 15l300 0z m620 200q30 0 30-50t-30-50l-170 0 0-170q0-30-50-30t-50 30l0 170-164 0q-30 0-30 50t30 50l164 0 0 170q0 30 50 30t50-30l0-170 170 0z m-620 200q22 0 36-15t14-35-15-35-35-15l-300 0q-20 0-35 15t-15 35 14 35 36 15l300 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="battery" unicode="" d="M770 350q0-98 36-157t78-59l66 0q-30-46-64-65t-118-19l-500 0q-130 0-199 94t-69 206q0 110 69 205t199 95l500 0q84 0 118-19t64-65l-66 0q-42 0-78-60t-36-156z m-136-90q10 12-8 26-136 134-178 164-16 10-26 13t-18-5-10-12-8-18l-22-56-148 66q-26 12-34 0-8-14 8-28 136-132 180-162 34-16 42-11t18 31l24 58 146-68q26-12 34 2z m310 192q22 0 39-27t17-71-17-72-39-28l-38 0q-22 0-38 28t-16 72 16 71 38 27l38 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="back-in-time" unicode="" d="M532 760q170 0 289-120t119-290-119-290-289-120q-138 0-252 88l70 76q82-60 182-60 126 0 216 90t90 216q0 128-90 218t-216 90q-124 0-213-86t-93-210l142 0-184-206-184 206 124 0q4 166 123 282t285 116z m-36-190l70 0 0-204 130-130-50-50-150 150 0 234z" horiz-adv-x="940" /> - -<glyph glyph-name="monitor" unicode="" d="M900 790q42 0 71-30t29-70l0-550q0-42-29-77t-69-43l-218-44 86-38q50-28-20-28l-500 0q-98 0 32 52l36 14-220 44q-40 8-69 43t-29 77l0 550q0 40 30 70t70 30l800 0z m0-646l0 556-800 0 0-556 800 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="mobile" unicode="" d="M480 840q42 0 71-29t29-71l0-780q0-40-29-70t-71-30l-380 0q-40 0-70 30t-30 70l0 780q0 42 30 71t70 29l380 0z m-190-940q30 0 50 15t20 35q0 22-20 36t-50 14q-28 0-49-15t-21-35 21-35 49-15z m210 150l0 660-420 0 0-660 420 0z" horiz-adv-x="580" /> - -<glyph glyph-name="cd" unicode="" d="M460 810q190 0 325-135t135-325-135-325-325-135-325 135-135 325 135 325 325 135z m0-610q62 0 106 44t44 106q0 64-43 107t-107 43q-62 0-106-44t-44-106 44-106 106-44z" horiz-adv-x="920" /> - -<glyph glyph-name="inbox" unicode="" d="M967 398q40-42 30-72l-28-154q-4-20-22-33t-40-13l-816 0q-22 0-40 13t-22 33l-28 154q-8 32 32 72 8 10 36 38t68 67 52 51q22 22 52 22l516 0q30 0 52-22 16-16 53-52t67-65 38-39z m-266-32l178 0-102 114-556 0-102-114 178 0q8 0 12-8l40-100 300 0 40 100q4 8 12 8z" horiz-adv-x="999" /> - -<glyph glyph-name="install" unicode="" d="M884 306q24-52 14-96l-34-184q-2-20-19-35t-39-15l-712 0q-22 0-39 15t-19 35l-34 184q-8 50 14 96l158 374q22 46 72 46l104 0-20-204-134 0 254-210 256 210-136 0-18 204 102 0q50 0 74-46z m-68-132q2 22-10 38t-34 16l-644 0q-22 0-34-16t-10-38l14-74q2-22 19-37t37-15l592 0q22 0 39 15t19 37z" horiz-adv-x="901" /> - -<glyph glyph-name="globe" unicode="" d="M480 830q200 0 340-141t140-339q0-200-140-340t-340-140q-198 0-339 140t-141 340q0 198 141 339t339 141z m410-480q0 132-78 239t-202 149q-18-24-16-32 4-38 18-51t30-7l32 12t20 2q22-24 0-47t-45-56-1-77q34-64 96-64 28-2 43-36t17-66q10-80-14-140-22-44 14-76 86 112 86 250z m-466 404q-112-14-199-84t-127-174q6 0 22-2t28-3 26-4 24-8 12-13q4-12-14-45t-18-61q0-30 38-56t38-46q0-28 8-68t8-44q0-12 36-54t52-42q10 0 11 22t-2 54-3 40q0 32 14 74 12 42 59 70t55 46q16 34 9 61t-17 43-34 28-41 17-37 9-22 4q-16 6-42 7t-36-3-27 11-17 29q0 10 15 27t35 37 28 30q8 14 17 21t22 16 27 21q4 4 25 17t27 23z m-72-794q66-20 128-20 128 0 226 68-26 44-118 34-24-2-65-17t-47-17q-74-16-76-16-12-2-26-14t-22-18z" horiz-adv-x="960" /> - -<glyph glyph-name="cloud" unicode="" d="M760 494q100 0 170-68t70-166-70-166-170-68l-578 0q-74 0-128 52t-54 124q0 74 53 126t129 52q2 0 10-1t10-1q-2 12-2 38 0 108 78 184t188 76q90 0 160-52t94-134q28 4 40 4z" horiz-adv-x="1000" /> - -<glyph glyph-name="cloud-thunder" unicode="" d="M760 494q100 0 170-68t70-166-70-166-170-68l-578 0q-74 0-128 52t-54 124q0 74 53 126t129 52q2 0 10-1t10-1q-2 12-2 38 0 108 78 184t188 76q90 0 160-52t94-134q28 4 40 4z m-192-216q14 16 14 30 0 20-30 32l-4 0q-26 14-38 16l50 116q6 0 6 20 0 14-8 18-16 10-34-8-2-2-30-32t-61-66-45-52q-12-18-12-30 0-22 30-30l4-2q8-4 38-16l-52-114-2-8q-2-8-2-14 0-10 8-18 18-10 34 10 100 100 134 148z" horiz-adv-x="1000" /> - -<glyph glyph-name="flash" unicode="" d="M40-100q-4 4 35 94t79 182 38 98-94 45-98 55q-4 12 84 120t180 209 96 97q6-4-74-186t-78-186 95-43 97-57q4-20-174-227t-186-201z" horiz-adv-x="400" /> - -<glyph glyph-name="moon" unicode="" d="M524 238q106 106 125 252t-53 270q52-26 96-72 128-128 128-309t-128-309-310-128-310 128q-40 40-72 94 124-70 271-51t253 125z" horiz-adv-x="820" /> - -<glyph glyph-name="flight" unicode="" d="M268-120l124 400-180 0-112-100-100 0 80 170-80 170 100 0 112-100 180 0-124 400 100 0 224-400 274 0t36-4 46-11 36-21 16-34q0-32-38-49t-74-19l-38-2-258 0-224-400-100 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="paper-plane" unicode="" d="M894 720q14 4 22-3t4-19q-2-6-72-310t-74-316q-2-14-14-19t-24 1l-248 134-30 16 22 26q388 420 394 426 4 4-1 9t-9 1l-550-402-112 44-190 76q-12 4-12 12t12 12q8 4 441 157t441 155z m-582-728l0 204 160-82q-130-116-142-128-18-14-18 6z" horiz-adv-x="921" /> - -<glyph glyph-name="leaf" unicode="" d="M236 646q182 106 506 66 168-22 196-50 4-6-2-10-76-40-130-109t-78-132-65-132-93-105q-138-96-382-4-66-76-114-176-12-24-47-7t-25 39q44 100 129 193t176 153 176 106 141 68l54 20q-14 0-41-1t-104-14-148-38-162-84-161-141q-22 242 174 358z" horiz-adv-x="940" /> - -<glyph glyph-name="lifebuoy" unicode="" d="M454 810q190 2 326-130t140-322q2-190-131-327t-323-141q-190-2-327 131t-139 323q-4 190 130 327t324 139z m0-60q-94 0-178-44l62-104q56 28 122 28t122-28l62 104q-88 46-190 44z m-246-522q-28 60-28 122 0 64 28 124l-102 62q-46-88-46-190 2-96 46-180z m258-278q98 4 178 46l-62 104q-60-30-122-30t-122 30l-62-104q86-46 190-46z m-6 180q92 0 156 65t64 155q0 92-64 156t-156 64-156-64-64-156q0-90 64-155t156-65z m252 98l104-62q46 96 44 190 0 96-44 180l-104-62q28-60 28-124 0-62-28-122z" horiz-adv-x="920" /> - -<glyph glyph-name="mouse" unicode="" d="M551 130q28-80-17-157t-139-111q-94-28-175 9t-103 117l-106 384q-20 68 6 134t84 106l-96 186q-14 34 14 48 30 18 48-14l98-192q80 22 154-16t102-116z m-324 274q28 10 40 36t4 54q-10 28-35 41t-53 5q-28-10-40-36t-4-54q10-28 35-41t53-5z" horiz-adv-x="561" /> - -<glyph glyph-name="briefcase" unicode="" d="M456 326l0-100-456 0q8 226 10 292 4 108 100 108l160 0q16 26 37 67t23 45q14 26 23 32t37 6l222 0q26 0 36-7t22-31q18-32 60-112l160 0q96 0 100-108l10-292-454 0 0 100-90 0z m-74 354l-28-54 292 0-28 54q-14 26-42 26l-152 0q-28 0-42-26z m164-604l0 100 430 0q-6-88-10-166-6-84-90-84l-750 0q-90 0-90 84l-10 166 430 0 0-100 90 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="suitcase" unicode="" d="M900 650q42 0 71-30t29-70l0-550q0-42-29-71t-71-29l-50 0 0 750 50 0z m-900-100q0 40 30 70t70 30l50 0 0-750-50 0q-40 0-70 29t-30 71l0 550z m670 204l0-104 110 0 0-750-560 0 0 750 110 0 0 104q98 46 170 46t170-46z m-60-104l0 66q-52 24-110 24-54 0-110-24l0-66 220 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="dot" unicode="" d="M110 460q46 0 78-32t32-78q0-44-32-77t-78-33-78 33-32 77q0 46 32 78t78 32z" horiz-adv-x="220" /> - -<glyph glyph-name="dot-2" unicode="" d="M110 460q46 0 78-32t32-78q0-44-32-77t-78-33-78 32-32 78 32 78 78 32z m350 0q46 0 78-32t32-78q0-44-33-77t-77-33q-46 0-78 32t-32 78 32 78 78 32z" horiz-adv-x="570" /> - -<glyph glyph-name="dot-3" unicode="" d="M110 460q46 0 78-32t32-78q0-44-32-77t-78-33-78 33-32 77q0 46 32 78t78 32z m350 0q46 0 78-32t32-78q0-44-33-77t-77-33-77 33-33 77q0 46 32 78t78 32z m350 0q46 0 78-32t32-78q0-44-32-77t-78-33-78 33-32 77q0 46 32 78t78 32z" horiz-adv-x="920" /> - -<glyph glyph-name="brush" unicode="" d="M118 170q38 34 85 29t87-45q42-40 48-87t-30-83q-86-84-228-102-84-12-80 14 0 4 6 10 52 60 64 145t48 119z m840 646q26-26-148-248t-292-338q-38-38-124-104-8-6-16 8-18 34-48 64-32 32-66 48-16 6-8 16 64 84 104 122 118 116 344 287t254 145z" horiz-adv-x="962" /> - -<glyph glyph-name="infinity" unicode="" d="M796 570q84 0 144-53t60-167q0-112-60-166t-144-54q-78 0-157 40t-139 106q-58-66-137-106t-157-40q-86 0-146 54t-60 166q0 114 60 167t146 53q78 0 157-39t137-105q58 66 138 105t158 39z m-590-352q60 0 127 37t113 95q-46 58-112 95t-128 37q-114 0-114-132t114-132z m590 0q114 0 114 132t-114 132q-62 0-129-37t-111-95q44-58 111-95t129-37z" horiz-adv-x="1000" /> - -<glyph glyph-name="erase" unicode="" d="M902 700q42 0 71-29t29-71l0-500q0-40-29-70t-71-30l-478 0q-38 0-70 28l-340 296q-28 26 0 54l340 296q30 26 70 26l478 0z m-140-550l72 74-128 126 128 128-72 72-128-126-128 126-72-72 128-128-128-126 72-74 128 128z" horiz-adv-x="1002" /> - -<glyph glyph-name="chart-pie" unicode="" d="M368 770l0-368-368 0q18 146 121 249t247 119z m106 0q156-20 261-139t105-279q0-174-123-298t-299-124q-160 0-278 105t-140 263l424 0q20 0 35 14t15 36l0 422z" horiz-adv-x="840" /> - -<glyph glyph-name="chart-line" unicode="" d="M34 284q-42 10-32 56 10 42 54 32l98-24-52-80z m890-12q14 12 33 11t31-15q32-32-2-64l-252-226q-12-12-30-12-14 0-28 10l-286 220-54 14 50 80 36-8q12-4 16-8l264-204z m-490 220l-350-550q-12-22-38-22-12 0-24 8-16 10-20 29t6 33l374 588q8 16 28 20 18 6 36-6l246-156 226 326q10 16 28 19t34-9q38-24 12-62l-252-362q-24-36-62-12z" horiz-adv-x="1003" /> - -<glyph glyph-name="chart-bar" unicode="" d="M750 800q22 0 36-15t14-35l0-850-200 0 0 850q0 50 40 50l110 0z m-300-300q22 0 36-15t14-35l0-550-200 0 0 550q0 50 40 50l110 0z m-300-300q22 0 36-15t14-35l0-250-200 0 0 250q0 50 40 50l110 0z" horiz-adv-x="800" /> - -<glyph glyph-name="chart-area" unicode="" d="M964 732q16 22 16-4l0-768-964 0q-12 0-15 7t5 17l230 288q20 22 40 2l74-66q10-8 21-7t17 11l158 238q16 26 38 4l112-104q20-20 38 4z" horiz-adv-x="980" /> - -<glyph glyph-name="tape" unicode="" d="M770 580q96 0 163-67t67-163q0-94-67-162t-163-68l-540 0q-94 0-162 68t-68 162q0 96 68 163t162 67q96 0 163-67t67-163q0-72-40-130l160 0q-40 64-40 130 0 96 68 163t162 67z m-670-230q0-52 38-91t92-39 92 39 38 91q0 54-38 92t-92 38-92-38-38-92z m670-130q54 0 92 39t38 91q0 54-38 92t-92 38-92-38-38-92q0-52 38-91t92-39z" horiz-adv-x="1000" /> - -<glyph glyph-name="graduation-cap" unicode="" d="M166 238l334-168 276 136q-4-22-8-47t-6-35-11-23-24-23-45-22q-40-18-80-41t-63-34-39-11-40 13-64 37-80 40q-72 32-103 69t-47 109z m810 246q24-14 24-33t-24-33l-78-44-308 102q-22 36-90 36-40 0-67-16t-27-40 27-40 67-16q26 0 36 4l292-68-268-152q-60-32-120 0l-416 234q-24 14-24 33t24 33l416 234q60 32 120 0z m-128-442q18 116 13 182t-19 90l-14 22 70 38q6-8 12-28t17-101-7-197q-4-26-22-30t-35 5-15 19z" horiz-adv-x="1000" /> - -<glyph glyph-name="language" unicode="" d="M988 306q30-82-10-176t-134-160q-10 0-12 2t-16 19-16 19q-2 6 2 10 86 60 117 152t-11 148q-16-38-39-76t-59-80-86-65-106-15q-52 6-84 41t-32 93q0 84 60 148 50 50 114 66l-2 100q-140-24-146-24-6-2-10 4 0 2-5 29t-5 31q-2 2 1 4t7 2l156 28q0 110-2 114 0 8 8 8 46 0 52 2 10 0 10-8l0-104q158 22 164 22 8 4 10-6 0-2 4-23t4-25q4-10-4-12l-176-30 0-102 12 0q86 0 148-36t86-100z m-370-160q28-6 62 6l-4 214q-34-12-60-40-44-44-44-108 0-66 46-72z m122 28q28 24 58 68t45 79 7 41q-36 18-96 18-2 0-6-1t-6-1z m-448 382q10-28 53-165t83-261 40-126q0-4-4-4l-86 0q-6 0-6 4l-50 166-176 0q-48-164-50-166 0-4-6-4l-86 0q-4 0-4 4 10 18 176 552 2 8 10 8l96 0q10 0 10-8z m-130-316l144 0-72 264z" horiz-adv-x="1001" /> - -<glyph glyph-name="ticket" unicode="" d="M216 272l326 326 178-178-326-326z m710 244q14-14 14-36t-14-36l-550-550q-16-16-36-16t-36 16l-76 76q12 20 12 48 0 42-29 72t-71 30q-22 0-50-14l-74 76q-16 16-16 36t16 36l550 550q14 14 36 14t36-14l74-76q-12-22-12-48 0-42 30-71t72-29q26 0 48 12z m-532-502l406 406-258 258-408-406z" horiz-adv-x="940" /> - -<glyph glyph-name="water" unicode="" d="M168 844q10-86 50-155t73-123 33-112q0-66-48-113t-114-47-114 47-48 113q0 58 33 112t73 123 50 155q2 4 7 4t5-4z m616 0q10-86 50-155t73-123 33-112q0-66-48-113t-114-47-114 47-48 113q0 48 21 93t48 78 53 92 34 127q2 4 7 4t5-4z m-320-444q2 4 7 4t5-4q10-86 50-155t73-123 33-112q0-66-48-113t-114-47-114 47-48 113q0 58 33 112t73 123 50 155z" horiz-adv-x="940" /> - -<glyph glyph-name="droplet" unicode="" d="M290 822q14-118 60-219t92-159 82-136 36-160q0-114-83-196t-197-82-197 82-83 196q0 82 36 160t82 136 92 159 60 219q2 8 11 8t9-8z m-42-392q2 4-2 14-6 6-14 6t-12-6l-40-58q-32-46-48-70t-34-75-18-101q0-24 17-41t41-17q58 0 58 68 0 94 42 246 2 6 5 17t5 17z" horiz-adv-x="560" /> - -<glyph glyph-name="air" unicode="" d="M85 534q-16-14-36-12t-34 18q-14 14-12 36t18 36q48 40 79 60t89 40 129 4 159-66 155-53 100 16 89 67q38 30 70-6 32-40-6-72-122-110-234-110-100 0-222 70-68 38-119 52t-93 0-65-29-67-51z m736-110q38 32 70-6 32-40-6-72-40-34-65-53t-72-38-97-19q-96 0-222 70-68 38-119 52t-93 0-65-29-67-51q-14-14-35-12t-35 18q-32 40 6 72 38 34 60 50t69 38 88 23 105-15 134-56q68-38 119-52t93 0 65 29 67 51z m0-256q38 32 70-6 14-14 12-36t-18-36q-40-34-65-53t-72-38-97-19q-96 0-222 70-68 38-119 52t-93 1-66-29-66-52q-14-14-35-12t-35 18q-32 40 6 72 38 34 60 50t69 38 88 23 105-15 134-56q68-38 119-52t93 0 65 29 67 51z" horiz-adv-x="905" /> - -<glyph glyph-name="credit-card" unicode="" d="M900 700q42 0 71-30t29-70l0-500q0-42-29-71t-71-29l-800 0q-40 0-70 29t-30 71l0 500q0 40 30 70t70 30l800 0z m0-600l0 300-800 0 0-300 800 0z m0 450l0 50-800 0 0-50 800 0z m-700-256l30 0 0-30-30 0 0 30z m180-60l30 0 0 30 30 0 0 30 60 0 0-30-30 0 0-30-30 0 0-30-60 0 0 30z m120-30l-30 0 0 30 30 0 0-30z m-150 0l-60 0 0 30 60 0 0-30z m30 60l0-30-30 0 0 60 60 0 0-30-30 0z m-120-30l0-30-60 0 0 30 30 0 0 30 30 0 0 30 60 0 0-30-30 0 0-30-30 0z" horiz-adv-x="1000" /> - -<glyph glyph-name="floppy" unicode="" d="M658 750l142-156 0-544q0-40-29-70t-71-30l-600 0q-40 0-70 30t-30 70l0 600q0 42 30 71t70 29l558 0z m-58-300l0 250-400 0 0-250q0-20 15-35t35-15l300 0q20 0 35 15t15 35z m-50 200l0-200-100 0 0 200 100 0z" horiz-adv-x="800" /> - -<glyph glyph-name="clipboard" unicode="" d="M630 750q28 0 49-21t21-49l0-760q0-30-21-50t-49-20l-560 0q-28 0-49 20t-21 50l0 760q0 28 21 49t49 21l60-150 440 0z m-100-100l-360 0-44 100 108 0 36 100 160 0 36-100 110 0z" horiz-adv-x="700" /> - -<glyph glyph-name="megaphone" unicode="" d="M792 500q58-138 67-258t-39-140q-28-12-61 3t-65 40-99 41-149 8q-28-4-42-19t-6-37q22-56 46-108 4-10 24-22t24-20q14-34-22-46-50-22-102-40-30-10-54 42-32 76-58 132-6 12-34 17t-46 31q-30-10-38-14-34-12-74 12t-54 60q-17 32-5 79t43 61q126 52 213 108t124 103 59 92 25 78 15 59 36 36q48 20 130-70t142-228z m-28-300q8 4 10 38t-11 98-41 128q-28 66-67 123t-67 84-36 23-10-42 10-105 40-133 68-119 68-76 36-19z" horiz-adv-x="860" /> - -<glyph glyph-name="database" unicode="" d="M686 208q14 20 14-2l0-100q0-74-104-135t-246-61q-140 0-245 61t-105 135l0 100q0 8 4 10t10-8q32-52 125-86t211-34 211 34 125 86z m2 254q8 16 12 0l0-116q0-68-102-114t-248-46q-144 0-247 46t-103 114l0 116q0 20 14 0 30-46 124-75t212-29 212 29 126 75z m-338 328q144 0 247-39t103-93l0-64q0-58-103-99t-247-41-247 41-103 99l0 64q0 54 103 93t247 39z" horiz-adv-x="700" /> - -<glyph glyph-name="drive" unicode="" d="M884 304q26-44 14-96l-34-184q-2-20-19-35t-39-15l-712 0q-20 0-38 15t-20 35l-34 184q-8 52 14 96l158 374q22 46 72 46l408 0q50 0 74-46z m-68-132q2 22-10 38t-34 16l-644 0q-22 0-34-16t-10-38l14-74q2-22 19-37t39-15l590 0q22 0 39 15t19 37z" horiz-adv-x="902" /> - -<glyph glyph-name="bucket" unicode="" d="M522 780q174 0 286-49t104-105q-6-38-48-307t-44-281q-2-18-37-44t-107-50-154-24-153 24-106 50-37 44q0 2-4 30 82-6 163 35t139 117q28 0 48 20t20 50q0 28-20 49t-50 21q-28 0-49-21t-21-49q0-20 10-36-48-58-115-89t-131-27q-102 10-157 57t-59 109q-8 122 156 184-18 94-22 138-8 56 104 105t284 49z m-452-470q4-32 37-59t91-39l-32 204q-100-44-96-106z m452 212q82 0 157 18t113 39 38 35-38 35-112 39-158 18q-82 0-156-18t-112-39-38-35 38-35 112-39 156-18z" horiz-adv-x="913" /> - -<glyph glyph-name="thermometer" unicode="" d="M400 356q64-36 102-98t38-138q0-112-79-191t-191-79-191 79-79 191q0 76 38 138t102 98l0 444q0 50 40 50l170 0q20 0 35-15t15-35l0-444z m-130-406q70 0 120 50t50 120q0 56-32 100t-84 60l0 370-100 0 0-368q-54-16-89-61t-35-101q0-70 50-120t120-50z" horiz-adv-x="540" /> - -<glyph glyph-name="key" unicode="" d="M774 612q20-116-28-215t-150-117q-66-12-130-2l-118-194-70-12-104-166q-14-28-46-32l-76-14q-12-4-22 4t-12 22l-16 98q-8 30 12 56l258 386q-24 50-38 120-18 106 53 187t185 101q106 20 195-45t107-177z m-126-76q30 44 21 97t-51 83q-42 32-92 22t-80-54q-8-12-12-23t-1-20 5-16 13-17 18-15 22-16 23-17q6-4 22-16t23-16 19-12 19-8 17 1 18 8 16 19z" horiz-adv-x="780" /> - -<glyph glyph-name="flow-cascade" unicode="" d="M520 120q50 0 85-35t35-85-35-85-85-35q-80 0-110 74l-164 0q-88 0-131 54t-43 118l0 464q-72 34-72 110 0 50 35 85t85 35 85-35 35-85q0-76-72-110l0-114q0-78 78-78l164 0q30 72 110 72 50 0 85-35t35-85-35-85-85-35q-80 0-110 74l-164 0q-42 0-78 16l0-194q0-78 78-78l164 0q30 72 110 72z m0 300q-28 0-49-20t-21-50q0-28 21-48t49-20 49 20 21 48q0 30-21 50t-49 20z m-470 280q0-28 21-48t49-20 49 20 21 48q0 30-21 50t-49 20-49-20-21-50z m470-768q28 0 49 20t21 48q0 30-21 50t-49 20-49-20-21-50q0-28 21-48t49-20z" horiz-adv-x="640" /> - -<glyph glyph-name="flow-branch" unicode="" d="M640 650q0-80-74-110-6-58-28-101t-61-69-68-38-75-26q-42-14-63-22t-47-24-38-40-16-60q70-30 70-110 0-50-35-85t-85-35-85 35-35 85q0 78 72 112l0 378q-72 34-72 110 0 50 35 85t85 35 85-35 35-85q0-76-72-110l0-204q40 30 138 60 58 18 84 29t51 41 29 76q-70 32-70 108 0 50 35 85t85 35 85-35 35-85z m-588 0q0-28 20-48t48-20 49 20 21 48q0 30-21 50t-49 20-48-20-20-50z m68-668q28 0 49 20t21 48q0 30-21 50t-49 20-48-20-20-50q0-28 20-48t48-20z m400 600q28 0 49 20t21 48q0 30-21 50t-49 20-48-20-20-50q0-28 20-48t48-20z" horiz-adv-x="640" /> - -<glyph glyph-name="flow-tree" unicode="" d="M868 112q72-34 72-112 0-50-35-85t-85-35-85 35-35 85q0 78 72 112l0 114q0 78-76 78l-100 0q-44 0-78 12l0-204q72-34 72-112 0-50-35-85t-85-35-85 35-35 85q0 78 72 112l0 204q-30-12-76-12l-100 0q-34 0-53-19t-22-33-3-26l0-114q72-34 72-112 0-50-35-85t-85-35-85 35-35 85q0 78 72 112l0 114q0 64 43 118t131 54l100 0q76 0 76 52l0 140q-72 34-72 110 0 50 35 85t85 35 85-35 35-85q0-76-72-110l0-140q0-52 78-52l100 0q86 0 129-54t43-118l0-114z m-678-112q0 30-21 50t-49 20-48-20-20-50q0-28 20-48t48-20 49 20 21 48z m212 700q0-28 20-48t48-20 49 20 21 48q0 30-21 50t-49 20-48-20-20-50z m138-700q0 30-21 50t-49 20-48-20-20-50q0-28 20-48t48-20 49 20 21 48z m280-68q28 0 49 20t21 48q0 30-21 50t-49 20-48-20-20-50q0-28 20-48t48-20z" horiz-adv-x="940" /> - -<glyph glyph-name="flow-line" unicode="" d="M168 162q72-34 72-112 0-50-35-85t-85-35-85 35-35 85q0 78 72 112l0 378q-72 34-72 110 0 50 35 85t85 35 85-35 35-85q0-76-72-110l0-378z m-116 488q0-28 20-48t48-20 49 20 21 48q0 30-21 50t-49 20-48-20-20-50z m68-668q28 0 49 20t21 48q0 30-21 50t-49 20-48-20-20-50q0-28 20-48t48-20z" horiz-adv-x="240" /> - -<glyph glyph-name="flow-parallel" unicode="" d="M240 650q0-76-72-110l0-378q72-34 72-112 0-50-35-85t-85-35-85 35-35 85q0 78 72 112l0 378q-72 34-72 110 0 50 35 85t85 35 85-35 35-85z m-50-600q0 30-21 50t-49 20-48-20-20-50q0-28 20-48t48-20 49 20 21 48z m-70 532q28 0 49 20t21 48q0 30-21 50t-49 20-48-20-20-50q0-28 20-48t48-20z m448-420q72-34 72-112 0-50-35-85t-85-35-85 35-35 85q0 78 72 112l0 378q-72 34-72 110 0 50 35 85t85 35 85-35 35-85q0-76-72-110l0-378z m-116 488q0-28 20-48t48-20 49 20 21 48q0 30-21 50t-49 20-48-20-20-50z m68-668q28 0 49 20t21 48q0 30-21 50t-49 20-48-20-20-50q0-28 20-48t48-20z" horiz-adv-x="640" /> - -<glyph glyph-name="rocket" unicode="" d="M543 236q6-50 8-81t-8-59-13-40-35-32-45-26-70-31-85-37q-32-12-45 4t-3 44l40 110-130 132-106-40q-28-12-43 2t-3 46q12 30 31 79t27 65 22 45 25 36 29 20 41 13l52 0t71-6q10 14 29 39t77 85 118 104 145 75 165 19q8 0 14-6 4-4 6-14 10-82-18-168t-76-151-98-118-86-81z m50 296q22-22 54-22t54 22q22 24 22 56t-22 56q-22 22-54 22t-54-22q-22-24-22-56t22-56z" horiz-adv-x="860" /> - -<glyph glyph-name="gauge" unicode="" d="M406 178q34 56 214 284t194 220q12-6-96-278t-138-326q-50-86-136-36t-38 136z m94 380q-168 0-284-127t-116-311q0-30 2-46 2-22-12-37t-34-17-36 12-18 34q0 8-1 26t-1 28q0 226 145 382t355 156q72 0 134-18l-70-86q-40 4-64 4z m362-62q138-154 138-376 0-38-2-56-2-20-16-33t-34-13l-4 0q-22 4-35 20t-11 36q2 14 2 46 0 150-80 268 6 14 20 51t22 57z" horiz-adv-x="1000" /> -</font> -</defs> -</svg> \ No newline at end of file diff --git a/src/mol-app/skin/fonts/fontello.ttf b/src/mol-app/skin/fonts/fontello.ttf deleted file mode 100644 index 39a234370e14bbf53e699758840b2ea5de8ccede..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49104 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 X@uERw2LDyI_MM{Yw=q$GclQ4Qo9#|w diff --git a/src/mol-app/skin/fonts/fontello.woff b/src/mol-app/skin/fonts/fontello.woff deleted file mode 100644 index 674807bae0bb1793ab6274a50737b707d9356fd2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29392 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	Xlb?M=rJv=ekMXZ)^zi@8gVJB41Z6%{7jl07 zW7oqzyM|=^hxAR#{KcLyh5Q@sodye)tZU78d>!$Gv_2%z4gdjwe0>3s+_Ss7k*v{{ zJ2>2@Gydkvkni8R^KeWL;&8Vx4!isqnRZcLW|tM#v$eJLZTIj9&}XC1%9=2v`U-Zp z9u$NA6K;x9i98_|ZrTZ5Ko?aBd)wwye%1i}=Uri_kWNT-$hEWFOI1~MsE`jx7$+P0 zF&&)~%zxP}U$rfCffF~A{W8tD{qpIThvM;go#!*~ejObuL>Y;?h)tY+VZa~;9oG+1 z<iH^uWHp%@j-!j_6LT1NULWESAm2ODA1GwxTBy=j!jVLeiON=oH#z*-fT;#0j+UBA zQT#OiPT@v&yN2KLRwGlII2M1|{-T&06Mkylt`CC*@9k<m^YnN!pCH4UQ)`AS1YTgi zjFaUZ_f8qjwAkz`gH1pp#0KDi@0&!o5f<Bpg-FQn6QGPY;dIjNoC|(gW<WBi7%E2r zhPZCt2pDTjOb!>RLy0}=cSp}PR~r-bv>|<lUP!Rvc4ziP&W)%)#Ubfn^LJg(7x+Lk z&U_xOg5K*ha~2mtYeg0NNr^0DgAg|6P79%_*`wZTD)mR~X}4Jz6s5~hf@>G2n;N>N zn~>pg7Rw#vXxfzXSMMfu)e^8FalaKNDUzk_-4?T^?;onT`Kcu<9e5trdq^yLSlA-M z3*Em5dvYW?&Cf-&5tlY_SeKA8a?j=@4s8(Sl$j#}JfbhcJR%F+n`3I15QA~Lz|0~T zioy*?If&tL;6Hu=1&=A>DKWU$?m%e>Ws*N|<?M&@^^c8hTniWUp&i6}Ohh$}n1o?u zRZ^r0t4uHVrDf`DtD=xpv`5~W>0;n5LQz#kd@Z#SOUuZ~HaSbhihgZXNVk&02!ILf z5Hbta7LpgMW|^es>qTm0{bLPgUNRKM^5#&a#JI1)-oh@bo8dgozBsSasdP=D#v1E8 zOCdNm=9zUW(W1kGHASM|;NuVBTR(AMm&q`Nxx_*?Om>wY#*snBiEcf&I&s(cmdn?W zAUASFE4>D-3%eOls=}f^tUVK@%8^ik3t6RYH)2g0-QnR>=vFGpSFy_HSXPF*5V18P zP75cL;MfKZzg>WtQ5X{u!m6<b9BYJDA*|kX3L`c-1h_t14X7X4FM4pD@;DO6a>+ZS zFlI-wXFKKj#ZuS;Z~ktn15zqO$P@D2Kr=T0Ij9#T-84T)1xX&lN7K2FK!V>Z=b@jT z;bU!qYYUlwkfl<Ci{s&8CZ`wUa<a0t%keu`RJ)$_AhxT){KImhkdEmujriZ9H(e_D zS{5e5WY9|#off0DLWhZ?PtCw(f<V~9z`%1?Y@2pkra@<eriF+zOcNniup&$f)5COs zgJXy(*ekG)2CYXGwNiL7qbj0@NYGF>T$Dngbyc@x4O(x1e!i#DzW8?5ty7wrW;7F5 z%B^@8wt2ZZ=roaFQctu8&TXn^Rb3m@CR#w3R9^aYZ|WA1%h`jNFf<N?B+j}#yRbIO zA(babIxxZ-&4r**z?G;f7$J?vqcqr`g#6sa3RAI~^nS|s;=?$KkTl7y9~#iDrW}Cc z%f4ExxaDz}j7x(QgX%=(iD=-F{(_`Ks!!|}gULHtYf_+5t1h%$FSN|+`XL98MA6}~ zpeDMmw!q136{YQ{4{sW$Gqy5knU*^tVJ0@Gkr=a(fs0ZTO=arnsoosQUuGy*NErfq z!oKKBU06kf!V#?@B?O_t8UW3WJV6vHy1tWo$rG;;8r>VfV2p1D&8%=T`Zw2$wv(@s zuG=*R2RTi>?YVJ5QG53>)_?~4Jle(8*)_f<4U2HFG08_Wp;*)H&r1Zqe0^l=ic$;V z6CKTD@Ajvq$Ao#L3|4o1(m;D!8jp4`nV-ZuUB0QOJ<^FFDuYw?>svE`eJg&LBSdpP z4A)fSRg7iT{}1@UfXRFq4oV~bfE;Z4zK1P1B0hhtdn}=Kt9DJQgp+&BDU$Q^DZlEj z*3zy<eC)87NAR8O#%}DC7Y3yuYhYWl$<<WbmyC;p9_#t>q~M|s*Vh9TjvKvmRcVE* zS6QZ^g`8mkVyMUC!K$D}BSA3{9NFypqNNwl5+;TRW+?c{8)Av01ymS}wChc}DK@5B zlxc>8hOk+Ev#O1yY!msUK~hP}N~Hjm91Yc=bq@q{gpwB{I)GAL!sic|8+iaCte}~< z*=N*5NMDUoa&{bbUR7uTo#_cT=jK;m00~;Kz6SQM?7oWNJCTN*qSCYC6Uv*k%2Ve& z-UXkVek@}KNX(ln#QPr};Uehb=ZY{kwV#6hqZF_4Z8a!SD-?TfC`Uf&epn6?HxB*i z>ww_Nuy8x6VV;4TBKw`we0Q^(X6r)TJ42OGbLqO2d8_qhiY0OF{+~5tSZPN1Q6e<I zvU?DZ5pr75LX6(-g}uR!0~iPHnv9CTmdVhcNX@`<_eK`~+`im8cvI-om0=MUcXTQ{ zp{yp-@W9$R4!+nvKHtv75sv6ncGM0cc;~R?5$aQx<-pp+)Rj#viI6fG(r%U^@bkwW z$3QbkZ)PElCyb&10jO&5-JzeQYsVzS7hKT+fpV-jr~{7p+-JG<8yxv069_FM%jxKD z%O!ht&F0!9X4Wx36b==Ne%6<RNHf86#@qEV*uTl@_}|uK@8`Aq8oIkL+%WX)5o8Z7 z=8dAf>U}v)*M0_LyTRuLV(-Qjl)Yjaf$y-xXY5tvON<CUp+|!vGC+!i?I7%!&iedY zER+W4SUK%!ZC8J<s!bvh8=Oy@K35yer)QGmuhSVAsZNIQW8Ug(KmGxdYc37A_LHv# zIR@vuxMxFLgRcWDLG$zkN%3M>HyP>l3G73}V}1>>(N!6hR$ul}HDj}x4slkEknpGL zI?VBeTn|9Bbv<|hldNvAOmLfts@67)D0$$F?jh44u5U=Q>j`d56mz6BK<y~dLRtm( zG*X4&yJ7b6w0P=%@RT32s`5IDsIp!w>u@{jPOP7eC0&&)Tz4YuB;_n`Jz2a_$nXmr z#GNK@pMp%)u~Ef11_1#Fv5Bzi(1Q#nc3})#*;U@?CPqZcM6kiZ3REin$9%7+6vrF# z{DzOfm5QYOGP#&M9@b&x>$O4o>A{LWNff(yuX_8`Zt0X4*W^H%JlP0S=wNHWZi)Gu zJ2<N2<?75rj*=q|^c~fvhxGAgx3kj)kO}|5@Pi3QJ}nI~I!7mTNE>V(&e0CwqINGJ z+4e)lWp(ThoFG>06lK?;a%@LXW{9}Gj{-mjfe#H$CvgLNR?lqYx{F|7R<A74-*7d( zQDXO;?pADfGQ@dZ^NIURA}xTv4tb#j7=EpVCIVqLrAYAq!w?7#5~s`uhos-k$7jdu zUA0UipeZqFO7~;p{C28FJB$C#`o1znVqqq)y9rW2UI5&LBh-G~8+6w_bYIs1pk{`8 zi+xWLeoV!{L!hD>4K#CEtn`Q??Y2x%>pj!ymLPSoI$Em9I8Ga>n6+xPT|D$f9*g@4 zB3ea@PGTTf{wx|`pB6;IAl(&)!^KlSD_G`x8e!VkHPgkg&9o@3nf6an)J$BqxCpz2 zsc6FqdkfQAyYB)nHaxF7bJRF1FK+;#r7l}@Mwr_22yklwJ-eg1t;;TH*|~4rAElR_ zg0Gw~DPB$)(-Z$&t7b3&SQT&%gvRItKtAdcwW=t9jGv*Z_=e<{E}Jda`S5;t(!^h4 z>I^|TYgY~g{1X4&MV$=NNF5+^e#+K|?R_%J>>=EzN#ydptD!|OSt^oS{-(O5QdRnU zG{L@j4KLQa*X}Zl%br2mtaD^n^iyY5`*Nw1mb+RE-Gj>-T}5<}X;+0fSMqq~u_W!a zlO03Sqwr*$E@~#0u`C_FIxS#JXn*nV<u6b*NCO%oyE~eZyA3vQBdn2L<kM*n^?dG7 zahe;%Kt_-)LFggmKq#sadGZSj=|=rqoWwtt%jgM^Iv6buJu8|rz4eASVw3`h?p!7y z@hPaQ3O<fqFSoV*2Rm8nf5y4_IiYgGcvAVBQT;tgL9Y|;cWFr$Rp>i)Rl`hFF-(OM z0};CVZB0E$5>4j^J@z)qcRi|UH6y}P!Uv*ue7Tufu;peNtT~L%Ix27)R!GBr^UBji z)zn;68#n)L2ih@{NC+uDlvU1H!dNg6x)0dVwIIf+EBgcBC0y2#!6-)q+(j6~w@NzA zv+p$93DfC*llm>ZXGJ+!<axVzty#Q{%Rbv>R-OA5kX_rz`<i;$ZfGEk`DZ2AQ<k%_ ziX4{r{)CY>LU?nr)T%HT4m}NCl~=PD*E4vI^`<H|;7Or|cR7SDzFv`O(T0iB+LdL8 z6F%iz9EH~?B4j2Ra`vrfU~IC`bL`mjG_yFXzE^JMW@QV|>n#FkMU__h(FchTHclZ= z!1zn%tKNeSk@ixg&T|qPU=u6X+qeUar21)~)r#y5PVF^#uz^%;WP6HL9x**^ZEC9C zAXGbsDg&0$u&dtj>|B64SeYITseT5-U?N_)pDQB%;`BSyai=tYiXtvdt*itmG9r@^ zkE?lgE&JjR-JeCBHAh28=46_>9ux}<hSAJR$5-oZ^0I5zCB+29ROad`MIo5W=Qi17 ziQ4NYlC!T8P@1|#dG;voB!+&qX41Lze;G8OlOj=pXyFoz<G)<#qJnt*J6Cy{>s+VT zhkWq&=$XPN=PzWitkR<Us!kG^0fe)I89t8K<idfK>@FMrtu=6udofPFQ!FA3wHcnQ zNMw{k|Mz%#j$#UoSMipaDtFgl&7MIQ9Tq)i)*TTQ9vV?~$+-FqCoacxQTG%5SNkKm z>oR{oIAsIU3dA~j?TJ!R1IPXeZ)m!?qHPl&@wLKoL5`iX*+6j&c8~+?(TfQL%b7&K zU!^Ay0z4v$Ey#t&zg9|{0<E@yuAmrb!LsRMX}%oMgC?pPv;LBRw%ad&{WZJz@3Yr{ ztWIQJs^{xNVz655&Qj{nrT6W#t>~ZqirSpA4!H+Qh!odEJWc|F$*{4bT4mkqr{k35 zl7m-yamdMf^y!787vk&4=$UNu&J1dXhGj)7)<7M+s!m1S0vI)xZp`jj{W5Dt6R<Xh zJ|Msl8aSCw*)Ps<C@dC{8#s<YNTbyG>h5I6&mBsLAaC}1a8sWoAuCGD7X&pWw9=l< zXu8UB^Wojpso~s2;CeI*@D=&1Bp&<tq?;9tH{hsm7P&<!I+ejf0E;El#z=#jy45DG zdKb&)Zjo8D7iN~arE&b5Fi9Q<g){*>Wl*D^IeY-Wf8D@hJz&Sa7A5hHAvB0_%|<N> zXTBM-4T2qRB3(MIDrWpJcL|cqWy_-PGTA<8T<r0@LdUJ+#CtR4YFhJk@(3?0$E!6G zP4r#0-~RE4B|e^&HVg?TqUX}dY5k4OW=(}MEzL$9V2n=WU9_gk?t%P^3bfH@IV1@T zSsVobXGt*v$hN6TIASd=nBYSn_=Cam!j-Riarf0S9k>~&iw8&{0+|lq(jDc{H;3Eg zqNILasmYjHXubKDe08yi)>T`{l{ZV3FWzb=f4Y9$lOvoP3*mWB5KpJA5PJ5>1`Tir zE17@~1N`O~M2*cs<uRkGW{@U<PEWFo3tI-)j!uPyIn{WwXdXI&H8#%>3V{KtZYP3x z4JoH^-wD(a%ImCfrb@+JcG;KsGBG;r^rnD8kq<g%US^c5HAa>v^1cM@N)>yV5akhK zpT??$LIDULb=RQ$^urs9O@5`wrdUrmO7gD{ZWy!m5zG{7ux45#Fw&?Z#!pzzGL=O% zL+JXzpG56knF<_`?i+EPT-LOu%Opq2FjBQ~ztCnQqgU24V(sr2W&S!eSr{vc>13+g zof$}YqNR20p%g82Q}$LdVU>(cZ<i5SY9=UcI83U<f%$7!F^w@X%t=eT?@OD_WzR(% z6^lDnQEet!Kh&Hnp;5aFp9_(FTcDs?R=SZ%hNZ_xlgPzK3c+u&Axh%-;usmB^|~jX zM$b(0^18JmgxY&;8XqC{2{R~>?^L|JzlAGtnP1oTmRG@SxzIW4&Xb`zlvL$gf>I<V z3?oH^vL9-inD>As0Q<`sntGOJsRm+&xB?v?OKCD34r8@a+oP@P832ervU=MyK4K^W zalWyr?Ab-|N4ciKbD0XaYK8<Yil^BP5r_ssSul#6#1zFSLyS`!@Ae91pVG-#VcFhZ z>^$YkIK+4v4aArhRpy<>xoh7{YVd-(n{RlZ5XnH-3R+T}!fAsKfKsekW&SyGdDC&^ z0y8rr63BJ)!C~D13zCT7_Y>K;ohQ86m)B(Qg2opO>sR_iLQbPs<487?8d=i#QIy~M zuS3*<>H5M0xI}!qrw8YKs4gSzkLhO%%s8qF@22*}s|?xEATPIjsh$&^87wlF{5xY) zh5H>IM0+t1Km8*6!sD01#YAg6k15Zokj7-P9Eci{8Bhh-+XLBW=|F#=Z4l`dlwuc& z-7Y4){x-#W>!f+?Zn>rQlvT=63s?PfVf6uz!T!*L50-|vA<elCz!?!ri*!l@fxZ^F zGxbk{>7tdQK9(PV5DN%-oB1?Rgau(T1v6OHLQW6<!IyB84mA`y&3>Ng@g*RMm`^^C zACvt4-hy*5qux>iyL>MTf}^daWkk#9*J}s$E~dCge7?r(hP@c&AO1IlI~Z}L&xUMe zY%G>^d29FjhPjm`Ca&JW_|}PJTCDitZ2^le!W!0GiP-D3-+5Zo8I!-wQ+eE??7?1x zKZUuK0J+r;3+X$7GK$U2R{ptTSo#9unZQ0%M7f3&UH-{$b#M<eU;6+nToS4LV8W|l zL>W%^HdRLE)3a#*^Q%*s`sKrykQKy<@z=|aJ}#+m^s19OKau|D9TCp$E%S=ubV-`H zW*Wp_l4E~3|Im}7XmlXOAXs)Uh=39?zCi&s91|U!!5<2Q&?$d?kOT#Ecg*G;%s;kf z+F{(>i9+HGM+xr={idp7<r2A*_`F<BE)zp>W_j?|n@r_52M>?3;#1JR?$5)!RolK^ zE|1SN2?N^l)3NmvpIt!*s7mEx)9nv|P@W)7-EJu?`&`j3F^RE{G_epq9O<nq$WHbb zx5v|<TMefVsegpB)8uSB;==4_n)g;NE;QO~C%zD0j|GVJ^=02GmO%HH{l5y_RB(V| z8*ju&FgSEEs6sc{3R}cAD_-PVj@JY}YYVFjV4A5vxZQ$%i8JKw@E!8FoGvu@{46&h z4F9c|Bv%jfYEkyB>=e)=(G_<DflcN2yE+F#qPD-eM=y%C__4*A=OjgC$(`{YKiRb( z>A27>j+7+b=g*POX)oFmN?-nkPDLl(`q4Eq>*2<_X#@2OPRt>zgZROOq2d?OF&xmA ztX)kX8CtL;#^p#_wDM@6vZZ@(?RGz6aQ5a;d3R$SwqoO^vGH@CblT|4zW7XTT$GRY zNXP#E{r-%f5Ve&fEGy_D=2z43_6f=Pa{FF0g~OYD!o6ak_*7O2ajyDep0ASXgk$Y) zB}BTWL#=p?#lu*V1(5h&O2_vm249^9PW%r<O%F(ijcAP>L2s70*Wcjd4;%<Z{C}$3 zv4*LG%g73PnhkxA>1g>N8O3+VV0XxIx@X`}p|yxXkSLZ>q3|a}<*jb8Iu^QK=2UIt zMhm6A<gqUmhxrP-TwI(o1k)O?iAP!~MRSETKZtY*v+>C+i@ly_p$VMn+}vWkE45SQ zP{jr(sT-90&`89GMo)T0<R2fD%~2K&)5b`E04E3~u!|_SC2%|lOa|!ZtfLWUc_lp} zgMwhg1}R_=rZk)3nH8y9;ZouKzX6^Wtf*=7q=T61YHTeLW>ZH#E!9QQW5HdNSh{g( zT{CjK;0VrY%hJ;`Or%`dB&$j6@uJ$w!uID*zMpqAVFEiAV82!c6w}7>YKwQZ&F=8{ zOIrkf%?7)71eS<Igwh+}E))O^@?o(+cnD1B1AY<0qPV-82ip>`ac1R=NoGWs-k}(~ zEdfbY`OM6bs2gy`Hc}TQ9PE}eH2ZDGG<i=imr!&u2bkaFa$@8jBCq{`X&G~k%9{r$ zhF*n7j}nmOff-ToPmx`#HLfmH#3kI()MXoBnS>%%bDqu@>{vbwxm{I6pCBi#@(&Js zY@r>`<p`Jjw!JiW4%K$35j^y<M2ZI^$n<w$Q;W;GY@c=Q=ElveCI%w9`F~n0sPjkH z6e3Zz^85oe+<q|jo=CsI`Ub6mQZ-)tZ(_qKeuN}`_rj^fctia?1K3JyC?*?-GqU5E z-TA2PGD&e~DidwjUuDcBCuPoW+k7JrUbA5eu~Dsp;LTSH$z8~z<Dv%_%C7oOOOowP z{X9R(q_pdA5J&C1d-2^<DK6L$AtCX1bxDZ`)NJQV@<^y38mH=Bngz0cfg;g2hiu5A zQimAngvDRnw31aA-&3hQaW6W1+<#|}(S(dRI$KwQE2x;#x3jq`^y#gxm(?2WbY3kv zF!S;O;mgXOv#{C$@)UwwUI;GvsGeZmAzNaEYF{JJ_SF{3RzYM<;k7;_Th$Ug;!EWX zqk+kja+v~_8q>28m^)2l8l#9U4j|EBB+Uze9|8a*wJKfR5FxrWDbb=*N!gag)+)QF z_t%>7tW8}z=WfMM)dCfOB+YWd`RHG{oYe}K3YXL7F%d5w+^FFTwzCJ;tf`|LTuW9R zn;RRykiImq6c%(i*QT#Y#gk}=3Yb!a*s<7Y4Vc);RSB*inmpp$w?P^2lhIDsLr?GU z&A{G={+E7QN75e-uxh_dch?gh;B_)RUf<UPk9GC(FRk9GrT8GGSG8iEb>Q=Ju>*O9 z+ez;v27Fyj1<vgmaH?X)m<_f&Tqc?|{j}t`&6PLd;Og6wPVXWw<o02l^C!9&^#^iy zY0lzGbsE(3A3UHIvjcM4d(tszNNVARp~bYdGlLvRmTV~itCKF5HZQ0yWnkA=(Th3F zm(=ym?Mp6r@~Ek_WqEW5RMzTaEFmm^cd{?#c!|+iDVb02lmV{<AK`Hzqrf!Z$T4P2 zg*|kGt<1`V5mpSKlYB{Hsq}+DPQhSI90i)Fr3px-I?GO61#<orEZA$Q*PKl<O(G|2 z+VuEv^TKg<9(;GD^Y2DOK5Mg!ASjCAZR*CoQ7E^qiMxxzp?Yky2qh|c7ld6nR~?x_ zYW9gk`;xXQ_sa>zBAN&)Aw>F|C|jIl1YyGLs|6F?9$``;C1X#X?ejI^JkZNGH~4>n z<qS_iBglqx#6d?IGUIRt{0#35sMn8}7~jo0s3x&yMFjAB;v2!~e2we}csCG%3DnW1 z$uH8=*F8a5OY2_sp=j)_9-q#NwaQYa$Wq!Nb2Gkvoa0=$iTOsKNxvbLIb@nkY~0Gv zBUXP=^l}RK0ay(HWVv%RmI^2X>K=q<JfjE))6LuV*Ol8}!nco=?buJ-12>W{vuAbP z%skktOGtUsb2aN5BJIv&dvM)I+zV9TmQam*U3-En#&e}YaH%a&m-<G<Gq~l7vl%3` z?e+On5Nqluvy=uZ>u_{ea*7SB9o7?WhX%T=Qxw&yG1vQ?8rv?d@yg_cCMVrMr^AZE zknI@!HEh&tdMT%VZ|s|1vq#WOu(*uw_-G}gEwT?K)~T|}eRc*h6P_Z}B2Sm)RDVZu zyo&J((T%q#PKYa3G<ZV9^40sr1<qt3cpv?UT=ncU&zj~YrVFhgQ;g01Po~xUenE8a zJrgv@!f6misBVpZ75q$V@B|!Y5_<zmACnscQlrccxzoLlrcKP>F@W~_@*DzCUaq#x ztrz{nox9eT^&rOhf-Wq?A=n?l&3VnyfyWXDrOCdO9T@VCXMj9k$=4|5TQVW7-jx&{ z{j;Oj*z^1gUKSf|o8NSE4<AVp;&F@J3__J>vc9Zcredbnkca#S$f-L8sA4=$&H8{E zC_bYU*_v_b+zwbo6;2p$){p3r*Wq_yAo!@V{SOmh&q|*Mn-TVRf8nRM;Bw=usA_5o zr(~wGiQ?b#q~DTj#`TeV8?w8Mvg#S-J??VLw-~t+Bwf>S62aY-esVJ=pW2GmZ|1kg z8sZRYjOw~bTuOCSrHTWH2+AfK45F6L*eY4&er;66q4D4MZj76NW7b_7IM20)#jDec z+ZRjGh7r%m5%bQ_FgHYmgs_ooE~F8$!;8KT!iPx$x1-a=oB;;LlT~wn@i=vd?UqUi z8hTL=0*0>giCbJBkO#yR0D=qxo%13Dy;eZa$=-m@oIwckBb-0Dq`Qask+dp?>gfbM z>%Y%50qUM*ApLdK+5Db|H~uGGFE>uwLkV@q;C&d9G-{V~&cpe<!;_BID>8qM%QM!N zrITAih6(x#2;>P~XszU6Omm45g$&#R0lg}=aPLnujRP_&VhsL@NA#SaXEgC^CgQd7 zdyinjLR`kEVY%atTweE{^Rn(l2zdLYmn$#aq<ibzRUW4wCmLW1K<x3^@KhI+ex_am z4TUicP$jfNF(b1x&dW*G*7@>OmHE1H&mubeau9atTr3!8!~pRza99-v$@(rcQtAtB zbo=;huv+axjlJ8CERKHE<1lUZ96Ny|#;csq$dBQ6k31iS(MsNZ^UluQc*(r;T;_Sx zZe)J=?9rFqBiiE`n!e#FZH@-_T%O)d`yi+N&(oWmq1#H*zMo08QX0TcCb3<GjphM5 zzD_|tVtM~uk#d{dqFB>V_f!vqonBfo-LH9$dU&&0BvN9Nauh%CrYZ6bm3vpXXrT)l zIi?6$$`uTqQ6ud>E{3nUq~#Z1C0=`LKcrLhuo>Gc{@Dem9c&*Gd9em*LaGdvUq$f= z5}Y3;80<C^4O49)mP{YMcZ0e?YP~aG@AA>-@Ly9yfbhrjgx){ZH{v2C|4ijU_rODo zd>w?k+SnUVR0QwVk1SJLj=^2m_1y-BErawYtaNwt^Q5{#NVF<r=2WMh`Oc)y?b_8& z;%5_v1Ozkf#VbhuV&$!Blk$ei;a`wF&mVk3P1iuZQ7nkr7Uqu?p&8mV{<OiViQSob zea)^<lq_S0E&2`CqC$W}CH?513KuqG9as70X?Y6((hHt`oG;So!kvL;9q<rO*C5~i z{e=qhNM2G)1U+WUFl8)mRw&@+#altAtLH7ir+X&atTCW@gmMxE2}^<}`30QEX9L5J zu_~jEO}b3ph6Up5Tb9wZ@Xk)}2zd2c5Ko%JIDoWeH;Sri-aAThk&xZUvi3jH&=L%U z#qI719!CjMIgmsXO5B!sRrcGOlZ=bVMJCBQc+gjs$FbA;G=_*^((e+AW!u@b*L#Sj zy#RBNI@cmC7Xmg`1tyYqk(`fyJE#1`bkocO-#D@>f-j$rP7jJg5+L_Al+t-Unc{Ao zH2+e_u>?#SLI`P6+N&N^N)fFG7Q$a(VAGXHCoz@!7NT$fe~1#e9SX?%WHho#(Uj9U z+r4D@_d^O$xl;kqmf(Xb1@xcl+y8ke-2Ob~w(hU0|Bd#!dzeBxu)jaO@!W>x)*dMB zEc3Sxv2*J-^B(&abe96pz{0smMk;+E9WMY(oFZi)$mQqmu?W};1~wLG`3FO#D=t{0 zWv23THZw@w*zw%8lRq05AEGy*B&)u_LyG6#mjZ9uLN`(WUfpk^!U&(hlPq{c!G3R& z6u183KkX5tIz>Dc0odE9MAd}FdC-p}DYsQgf_6|4>aGfOX&{#9apFbPpb2HDW_lC; zY9~}7x1=W^m$_Qc6NQQY;0jT@wn%^Uk!FXUR5ccbG`Y06x$+QFfiodXNPX9vwB-de zwB64x1~W3$FIUQ5-fO%G9=dQWGRwX&H{8IbcymUAWMJ!M1QchdlzL|zDHT-pwg1}M z{6evcdWMMY>wk5*>?4J?ACrq)aO#>`kI-ny2}I<{wW2rj<A(PK3r6PxyXh~=M@h7c zE^m}JsbCd!bQMJy7<{|oTR3&BP2lHVW`K(!ctm`<3d2e@vHJa&Ht*-nb+mQTM{2cH znVe*le!|9Q$;>9ZPAP5C&CyH#5r|!wA~#bdh(?lIDr*yKfvXah%yYV|F4ZMW02YV5 zQUT}>8Z`=G;d|kOK(k8C)m7XWa}#f+1$W6^QFA@Gd#MAF%;@mf`5!~!Xp%>N!3_uB znRVLiW;xyo$z;hK2Pf6wm)$(VUba#;X;|*FqGEbujfi0DCkZ*aGCLK%uNnSj?>`Qy z{{F6AV}bS>p=Vpu_-716MLhMzh?nS#p{7ZFMK3>ZEaC;K6_%2+8{m}V-Ak&F{Vo*8 zy2e^zQ7oFo{C)e~XaDB}u^x$eTTU<%C%bUUHsIksz|vZVc!q+vLR&e;<(5uw$b)*( z$Z{t1rK>7O0n%IRGDv}J(Dp039&j){0ygmPPgk4|7SR@6fSjbir_4NSprR}>yB2{1 zK0mopZqZVc+%?;Fi{GLTUX_`!@Sg!`8jY+5VuXND|B5#}0nrlYdIg&1L)z?M^?C$8 zd$G}+p8;pJ!JbQ7g;Ute)2MJ~4AMh#rG6FsG)Yu?G1>lUMZIDBY!mlE)+&0xuta*o zjDk~*Jg=WPqq;e>8T(Cm<ZH@FyYhk&INC8(o7$8E8ojXz|3pWZmxhMrcrgzQFncK1 zj?ucM2QlRFrP-=h*?|f|3`GsAF|Kb?{z}&&YRkW#xA<WjZP~j4D2E><A=W~Z-7Ang z$c!46rKfTQH@Nm_A{7vC)m*|nX4Qmp#nfEGmE<47xl>3wJ~FYP5^O37!;D{DXK9G< ze?agMx&ni+5F$Sdbh+iAh>0u&DNttk?!J9Dt#8u-j-+u=@0JScvVSXn`^qZ&=(y+b zN4_&+G(+4|VpEfrLoC3d_nLaU#5t|JobpXyd${|mx3szywx(^kd1hy6^m{#Wxb?j6 z?3Uz?5j+b&uQr;eW$5?eHy4#wZbb9mKWV8?P%4S`XjCFcZr?!hU^Ou<XQ&mBo8w*w zel0iR%rPZ5*^Yn+{6wC#ak=8uKNb>HQx<@XOFyj*G&8N2#T$NuD;33Mm1B3DR-eXN zcM6@(09l>4-bBQUOws%ypDCDQQUA(rH9!Pc$Lz9_8a=G!H5B@&0W`YhmW@dq?cgJ1 zpPHSh1LgGKoMY(Z`+9wywl3RTx**p){iz4&5ZSi*nLtAh&R#>QNrO{D>@$-Vjoh3) z@a6~!>cK>V=ff+?n=<B`K2`isLlViyR7ee$QkW|@(Cn;5|M-A&SAV~|lS)g`YTcLg z@uH$tl^o{mVj{8_)_T5M+i&kzKwYic5n4dz%&AgqajNo(v*ze{zLccRgM0I^N_CW% z!C=3PiW~G}RSCfkzpe+O$T`NT7SI2v`&kv&3mtIEC$=eB|Erc0KG@fCt5obNK8qcj zX~QDM1T(DOODL^w#nv4}L$WR66(zD(9yZgHja3J>UH7w<SpHqM`qvXH%e+i^69iUr zutRx0JI%S%+-~$#<a~aA>4f|ZmFx^oB{Oa02h&*9+`F~9S#j&K3N-z7nyFd~2AiVX z4OPEo)NxK<o-?i0PMOi5g{29GjY4X~*j)mCoyHk?Za73^RW=#%Hbny`^YtJCYmLRT zv|?@evKjThAty5n&^+}mrj|}`|4@&LP*U}Pi8`TVi^7=y%u=oDuwQYNOp><1Cf)cS zq)>XW0KlBBjg0~KLWG$ChrTO79|ME1IdcH03dceek)+~g{q%@~95C(Lb5&|n2ANuz zg=9#<35M;OfL}2?iMB^mdT*~rMJ~`>uu7RC)b9Zc`-|`|YYP(J-7haLSbdE$&6Jwx zf##FnbC~kltgnZM$4&V>F?Hau0q(I{=hy^{*TghZ#HlSmD+H5q{fh_EXymD2P_UWE zg21Va1hYjD`1A)TEiG<hNc4m3>K8!+NHz(qqO@L?x_&f${PABJWvv_7qP!59cM)B% zP+WGhP!=bfaRE2(eYd7(B^DW=m+oC4N0RH`rYo^GlRvxab8@mJ9-Q5nlw^We=i_&_ zMVhVdVr%d^D*m}YwiOW}oRsv|aeO3y?V3a;b|gRWLO@$*xi;D}cGWR0X<9-Vy@}Ly zQ)B4{XXZeND%gS|=mGvmzu1Uh+D3W<=oE0SM;wrfbg&P?n?INCAN$(7c21EFmHML? z<kHTFd`Z(isB{mYiiO!BK7N>B`@X`>rM}-ge*Zd8=X2iVt3|QQg8aNw)#TP2icSFh zC;0BJu{@F;2B{qv^AY^wm2W5rglzhkV=2F>^Rr`><3!ie0YV1NokpfMm}S<4FBb<I z2l%|d=9?MQqOe{E%g2tOZlKf1P%zC;uMR6=-y<Z&s8N(Y+(fpX-b!-OWI^kLpFjb# zJbB#F1nwI@8=6g`Tl707M<QE7#-uLEYCQ*<^G8eC5lxhJ%!*-;PflfOj?VSt)=60& z*Y<u+C$H|1Zdk_#zv@Sv1~GFp&7V@pkXuO&6M<dO(ako!%H+8wSPtO*FCIaH#|7#B zj<l8hUqr?s_7Xmt3VvWv+~wbMD0dqGi4&Wfy9Y7(hw0qD$Bap(>kK~PM@7HU$jii6 zom9_TMso_ZZot=Bi<BJ?W{0w>R(<arx(6Cs?&^*QwZ1Sub^Qh*ijL7J;ifgp^b}@A z`(difMvRK?i%lbi=TX;su#Mk8pT%YM?S3?~b^Wd|NN;12dxG<bUssbjJ;&x(vKuJi z2;3*bT#y#&4(6j-EM<wm<oD0zOgpkO9ezE()?m<@nT2|H)W_4&%g)eF>gwN;Egqk% zsW#O2R`&t4ZE78fB;jc8#lefigc(fHr+9BFwCFrLsw7;NV^rK~<d@F3d>HQjz7AXZ z{^RA@Yxy|LGU#9lru-ZHJrWo&jrccCyRo*Walf4s?AHp2*~_0DIgHQWxa5{)RcnOO zX=OQ=G7scART&u_4eE0AGyuzgoBkP<b5*n^{M9O`EFE+Yf4%V!U;qlzSQ>E#t<NGv z8~8|$tXt&us6n_p=pgNnF9*K3HDXhfRgRGJf1S}v9P+zlX0v?!7U0hTc#cpC(hegt zEKs@3T7IzPEKd0@Hn1d_&JBD?<26&OHnfI6NcHjnlXSbQ$mJoj(DN3WaZ2+S)a<D< zWN95JwDZ=YvBdbu%6vPO;2uHNi*fio`RCJ;y8QMx;kKt<^=;yAzW7%I89jW*dfOR% z?>W9Ed+FW^*Gs$C`QFxh;kAUPtIbu%;hzk4-CsQ^6nouXn5sFOFCYG^Q^krwcurO7 z9}$mgBrBs{X1Dp~n9C6C!(G8qG7(Fi28k=gB}SO7`@v$uB*8SLDGcd2s=DurjW#KY z7ZWc5g*LW)POj*98MoDgQ8>pf_r_=Ti@tVU3c1M|OJ=035N?zS(fyQ)=1lw!<hGCS zzc81IaB@=|_qDSLk@l=vGi#5~Qb|i8QI3zG3C%qfNP4?4588XQXgIg}fO}74M{4Ia z?9w?)YfH_*Z9Gb?L4=)Z19$_9#^6A_28mvT9Qq~G_SOx1CwA*x=4Y+vu5*LV^v}Jj zF=vynly!=Sf}ig}7VxQ^M`5HKt|`Z1DzueyN)zMBp3+IhMjW!+DdB<i5)Eu6ktity z&FDBX^1lB>xtLgr^V)n&|1QuXOu3~%A)v$_)(V0Vw$6{Mu;9kYSKcd0WsRpl@OsNg zONuheY?DPC(e>_KQY=V_&GXNtj4z~EKC$KDkrSFc9v4Q%A;>Oab0>CKo0IAGv$G7; z*1S0!kydl3fC;e##X7>L*1Lnbn0%2z`IpWGqtX_KB#Y(g-+5J#<}!t;pogdv>8qeA z>al|M#m7JFudDyrdq2T<_8lBw4C!>sp}*SfY>5BcTYRp5%cF*Aw>|LtNxI=5qjyv@ zF2X1Xf`u3rOAKFiWsXNYYm~~}8?7Ln-*1<s00AjuPG39)+Ege>z$mu3&J#8S-oyvP zotR1)SOiv<a^LQdHg-P9x!$6EJ1E>l2M+E9N8oDF`rq9rlHDWs1~Qu2CX~&8XtN(z z!++a)g4lIE7)BLo3yUyoF>i#txuuEhgCEp}C9oNBWu>3Nx5rv{U>$s7{F`z@mc!5C z$lEJUPb-lAzQ+^rbvz{)|E}g>^t0FbQ`$251C8V^GZlvE-aT})iErm=B2~r!ZCqA% zaEtWniuU2+kYXvo`&STK7cVpSD(@f8&JhCKh9?ttd1v`)vqzDz(84a_yO6G8tQ?T3 zNNO{a{&B3Rmy=SL0E74gQd#9<a#b?6E%Zx6G|sc#QL+^RD<c~L6qnVTgO;!3RhpMP zLxg~;z{wANHPf1edi>@jqVanRXSJxTz`^-8w&kYDHn^i&VUpT9E%$a5*2LY2!xsE% zbXN0)ND^i0nB2IBf{7$Q-Z_H1b>Cd(<q(03-6Zddns~DOLQz3wWo%@_UH-+X2$VC* zj(}whWQ3wGJjADePn!<D7lbu;dtvSefrF)$yqw3#)D5t=tnrW~E*toaBg^2Lek3z! zi&S=bIkISuWnVX?!XHkAz8uTlrq<5XJ(FUhQFgHPvH_D@vPoV>v_esaD{BbB#agv5 zWmjA9om=g459A8%KOfvI#9gQ@Z;WABC6pa~g4k|h>+RvwCuCWb_M;?jk|Ra)<ncp- zB>thzv2(n4Uc5fK_ilC;{r%e9M)=Ap8>*8XV!q6XF9-qKU3}Bj)>ScBMUviEcE)=~ zq3nc(G*X6NtfOI<n|JrfmI0^aEcwOQ<TQi*N^7I4uHngac&Fjkx_`11EgG4?QQlXL zN;p?@8h)Q+nW?z&&b0-RMn(<4-H>QHa%+QETi@WaZRMXcCfvk+EXn@By5uvdqD-~e zCA*#lGxRzAcHw<_IHGRhSB>81cDi32{===KOI##eNxs1C@?Jar8eopM5uMyz9=;{^ zS2SzZFVMB{Yodq~f@>3>cq5qzE%Ubw90=vjv;<AHw9S<uvf>|7*65(xl@$f?!)3o) zy+z&q(1;X1k=`4+gCMv;3b~JxEIi!exmV#5$jFyEmz1TFfL5?Il`Wd?&3}*Q?amIm zjElCdo*vAsVc#XOd;-W_F+_iw<xeI8U=L`*P_LeS9@f=}JJfYz=W8t)xOzR-mJmOb zMUySDq%r?*a@-i>0N2Tpj+B{0+RfFT^NUsfJkCZdlbKwn?}td-y+>On)E)b2O`DeX z^GAz~=w3C(wN|8Ox-bga^zpufv=k*~UA<VKOz+v+O~cJkB(xs>`%9c>C)7^<mB!<s z&DIlEG^F-8`|VUu_31H~bx7boZKu^~x)e&}9sy5EcGL(7YL!NHBExD);1l|~CyHA? zWdN4~214&=4oJHb>lL8D=v;cVU3#<+)zJ^*yBgi?hj8^iKULS8GcEKB$JyaH#<5^c zbE;O}wU`UwLfmK1fY0>FwQ>9P6~f_U?Bp~Z_f=32N%)53&jO_AjB6iO@xOZWzq9gZ zKeUWkLN54M%LdflSI)Kac22R<K{7yxJ~vdog=YV&g}h%+&<~tO-?7lML&baUqdik{ zapm0JW9Vsi_70BE2%@;Q={q>UB9WeEQP%u?<hAxaq#?*AH#vYazfYydM6}j(OuZ+# zW+Z<haQ7rh=@m4`{GEz@%Zp{&HavyiYx7zBJ+u*zY<VAcqu%Cq^gJnfJUKbmJspE{ z*X?Bdc`)g44AvIe`NUU8LjSZ))bRY{6?%r_QqxA*R`-w6ybr*&lRjrFP<a;3=>}Ns z<ae}ki*r0dTP6s{&lZH%Sa!x2?RgmlU4wWyz+(tKV_n4g5-bi5)PoG5i5AS<;cx|~ zRsVUzQ|2iWSMAY9@;Z)-s<)vq`*CsFrUFG*RsnK|i9jRcvanL0Y2?kL>T2?e5ff43 zkmF3(cBsLiRTMAgk2S0Fk1(E;1$v&4b))wl!B0Uzhb&sAr9f<>tuey02Lc90*;lc| zOg6*(Gxy)T*@|ie=4sPr%kbc6l~amdo?f12nuVRVRo%bwAFo!b+#<{W6EQN*%^nh1 z9XtzKf1BUUs_|Um`A&~Bbkt9%Q@j<sX+vIo+|65yUwaX@R>FuH#%}kWTwMC#=8p?k z7MN&vYUzBsJdN)-`RQ_+jHkNKCsf_A4DX$<RBk?U?zxk~t%?;~Q}dipR{t#m3P;30 zHew&c_-NfFu7OZ5`V?EUqy4??+0o<`I~sM0)6k#Q4sg4T7Tr<T_}6$#tGvqjzdTxD zdDLz=1B`ohK;QvdMk-;PqHj@yNiTlQ*h?Rfhu&ztaZY-JjH~WYm#*O3PJu+ayI0jr zpbXz%RhD;_=3;&`ZVNDN-+t>9zWtUqb51NB#M@ug@k}&Q{ap}K1rIQtVHw_yi>Hd7 zTZMvR$SbN))D%5aGed?RSS=h%Kcno?nEB4<wY?Mf?m1uHyZ3PCXP@o-S#>Xc=jzov z=q26Mq%_dL923N#$OHV0Tm6#5%jv+AFIK_SHEN!AllFBNx+vnPNrghl#b{lC4(8;G zP^4TKN$U31tGDswRrjlXSau#?1L!G?k9K}`d)Lpvr^7@BF~=I1;Q&5>f~B5E)-Cqo z*&0VzVs}>H^A~FHwHMskix%5EIzvKd%!A8=ktw*kGQhC(WevKQHR*@DZhyA(W!U~r zbqMASm<L^Ip0NBjy7)llsE>mpgX<qfE_7%d@PI*oVaYkWw&aZM0<0#OPMRwbge9-f zr}N|0l}Y<-=l1$kItN}D&9!T{muuw^3p#D8(yAM-=}B`<Pi{0O^^3Scn1)UY-)kA| zdsR)OZrn&ERQ$mMO?#l`)kNacPZJ3>U+WLgk^|JTQ3XxoIc{XSdnVu&PN1?xt^Xet z+Q%oKKp#2v8&$nScByJSbK`m@uBtz%S@jFAe!)YU1_ynsv~Mp*ZY5XP&BFr;MXy&` zpb|@d8oK)idX2|F`6M1!^JEw8?+4ob^&8MVTA2gUn*+Y)FShFDH#Z#Bt)B&elfv@w z)$gHSY_1x=`ao4Foc|1O|0jL~oRj}Yn1c1|YDO;V(1gjv|BVq2hi@3+8{x1SzJR|7 zhmG(x!?+eUO!G9{{}}E!U{}%meGJ@RGF;bX%~-YwcN^DC^BV2u^kuj?eBBIRr@efL z>dz0>?r-4Wes3~bJ!fg6@%{H#^>F#Zhi(sHllI`=PoMu4rg>!iKXMfQk{bl?g~nIH zIrB!?xMAkP7rr&J;j3Zz%Np-{&t-Pho-0zg13n6|;Cg7Ob7vRcO<{K6-PsL;CuQd} zo}qv{bB2Kr!vwg`e|8ps%|uql&r#PdrilZzH++MF@63S!c-Zakluk#0kC^asJ3H#( z%RfUu|3|GKcOF7M3J0)K!JP-Z**QhK!iP;000@V53L)*w*%LP5wK5;fRv4EI{RkAE zcG$@-G|&^z#GRwgdoU~bD0FqyG{XmI$1pYc5cIS+Y#Of5_D6Dp+y;57r5eV#CPOhm zIMHT!2#<Kb#sUWW2xaW#g!8fU@inaD?ao2i)|1ZJ8lK)K=b<r3WEj`#oxKD5pm%wX z`2%ils1H+L3Et}*!h7+Qb4JGlc;Kq@{W89<>c`th_#J{*Np@a8KGI3MnZ)xz^3LK( za^8c>sO9h1$K>vH^v+84H9z&`ht`4o(dLg~a^jlv!!-b5n0A8aYjol^NdV6XRy8^w zZgm4!^dxRxOasQZzx|dZoSpbst^G05OCt&Y5uO5j%HK{UHPQ>b_l55X{odb*z3;#M z7QNzm@4WE5&Sw1Ww+r+Rv^=&afBkyVQacB);koD6>)AIj&-cA~uF*nxGWXnid6jux z&ldL9$~i>tUv&;$^X6%FAOE_wy6?fX_l5ENrL}r;%{dIuUcFY&cH_rPKiqrdAL&$h zZn8!?87nEBzqu&6S)MD?1ecUowQG90UGU|0&o$Xd|IrlEm5Fbs`GFmN{P^k9vlnL` zJtBWziq!JZ?vZh5m#D^G+9NfC<QVOGz?-%b$Rwa3Jo0*&dHVFsCG=uHZjmbB<8Jz| zmySI`I~$`d2fQV0_dJs1+odr1_TkLgOJ|;<=jQGg<{kc*@Z+-=&psua+}rCo_#yKl zc>sFH+tq47B!m^H&x;Bao|k~H8zEmTR)kSd*Fbw{@bN5lLJRfsbTK<wqCpNSNXr5h z7b}?P7al&m@ZR>m(dm7cFF(CZ-n(*To;)HC9yuRA8XxGsjM3$f=5OEr;Cm0I8vxT2 z4<?lZF~$$V7lBE}AlPEog9%fZ+yZhdnj2_sT2rG$s~|F~U<y$vve=X6OChgrIzD;+ zA-ng=m18Ubba~(GP~Z0V&cg~l`R;=|cjiC5>|E|19DjsIA0e;a%MGRoFe#`7wO8$y zNXRd-*sZ_pgEcc&@0EyK7~n0S9wq8Ng9i!<#++affftfA)h;P~vfKG#d^fvi<|sSU zKhEN_Sf?MSj*)5jIJWEPZs#j?Y%d<3n^8Bb{o_yZQ~2myoSW8X>BouPXqnD>xv_=n z>r}q?xfSFk7ap(l2y&zJ+!8E=mSCb)yJ=1bb)1!`rUTT4N~JUD@=?6?(W6^5C(`+B z;j6C}mY>0lkRf<RzWSOQ`stzw<KjHcR&q0b(CQl9(6%8Er+X+s1#&lpJ=RHMZ4;^P zqj+-DnF}+Z5`Xuop8NbABZ8LK4tno&_3zt4pb8~TK*secf(2#+2;T3k#e6SfL`;ht zEtd-N(ZZ$zU?l?e9@CEkEp#mKgNX!2i9(_yla5*~&A~upg9J~&Yh1m?rX{Tzs^QY0 zuL^ks(+S`b?v)VD0(UO}CKpSEB49K5vADTh%oR89?3tY{&X$Xnp;Ke=yLXqK3{1?< z9^Iv6SrI?EdpCY`E*qA~=qAEed;2QYP3fa>92p;<yL@?OeCX&_wam)S)0xYc=XRHE zqOjx3bs3JJFpbyIK#dL1&9}i%cQOw%f$9F6M6~J~wNliIzG37QxO9U+!u4FR7lJ_C zQP3FD=VCve-nw;{Y+a}qOoR2d(`8yT?e5*9%Q*^{UAxSi)iSMX)aVv2bIB7R8BO(a zci^O3fuF`%(9B1Mt~|9RxD?b491Oa3+G_Cu<Won!b&HAKa%B9N3?HRP;7pv^baZAJ z&*1uTS0<FGEaiEo-Jyi=w~FG7nzn7?ImwcopH$r<#M0H3eL!mv%gC$-S665iSXxVn zsVB<8)OhK@%D{kA8H}gMk*bsK>A_#bBenbtqSbsev1$~E4@3KMtKv|-X|=w@*E^+M zw$)GYl}(qO#Z8yVr`4sKJw2pctsm=fdcL0*;CxjT+sWg%Z#!SzzD+*8?R<Ub4z_Qj zI*j9>qfp!PbG<amo|)F|g~rp>=B|Aiw$m_)3q~Yj;I9T2@E$2F{aMk%_>-`vq{>dV zT*i0H&pktiXRNkK1T0qvD$tA>ifYOBM6%#@|B-^X>XC@<oKQYmSiqBt+qcsPeg9ed zsqA)N@0;04=G}aL@lS?d;=Fnr%kT=T&Y5Wd`KV!8#t598)T2=y;Esq-6g7gsiKxnx z)gPYgUv}$x7=9lM{rgvg+`jsQg7?r~og)gqso+`n{G>wRr`<XheTJw!_X#aO=R4rv zG;4WnH9^aE(DLi7m#WOJPF1Ff%6ex9=f;0OA3_skKqg|oTS+cWmuD6q;OkH6e#IGj zjatOK{N(4J>Dnfii-k}JAvH(5oGBd)`u#yO2-KnmhWK+}lsY?tFM*Q3^$K9}H*ca^ z?eA6aMBIqI8bmy1CBetIK#3ku;tEh})=;q4MAk%_!_NWN@JqmTyc)ot1CavBy#!;F z+Joy*iacR`ve3Y~a7rGW(u;7?kiu8M+W41XEp-HAuY@;$>t{g*puHT?2}TAw5k<q* z-v%|pBFhKNkZcg$`cB9#M<4(@7Q9Rh`f~=v%cvt`^oI_}07lVPS_fGVP$TcF>M{fO zi*<r%MDuun?E$nj0-*zcbZK$XIlZ{}#r#G2+BNy&eBb;f`TBMF(tIByyWilM#l@c# z`o%@%`t|yWD?Ov<YN@}5-as4BAFI=XLf9srxL#~kY@~h!;3>?MUy>O&4AnhBc)Y<4 zM18aS^<2G1{M*&;4R7|93b{94FaKP<c<RkAdDWoY_~M^C53MT39$Zz7om+K2Q}gmf zjQlIkp3*ZsS+CXZoL%|so!|1D%+DWYo;%!H_Cv4iAv#|)etlzgtVtq1`0Q(#5*1cv zk-)if7xnDH@{TsrAFvpwRS1~D^%-xDI&zuP`r_JDA|7jNwIX42-I{f)=SO{^DE;d3 zw!flMc>AjFYlFMzmJiCToHHja>%z=A%Q`pXR8}=P_WT6QT%gV9+sd$)Y1t9jm2}iV z%htAnOh?N_yZP@lt<?)tez73d`iwVNyCZMAWukTCjatnzZ)rocq;1jlBJxcnnQ+7T zi*MnO(Y^{37}B5av3f@x`M{AQ2k_(3UaKeFe^}BEr&V1gqH!Xus)sYSDw)T_xMJA0 zkuaiB15Zxx$tFJi`f4IO_lc|=vLs$*wXPmHtQ@pdDO~QA?-a+oy2gt&KC!INw9uTz zFlPCdNOM5sJY|d7fPT(k|FX`t#(ZAu5?|E13M*PysDp-PdAiq;U40`nRQI~XQr(Mv zc%bh<et|r36|li>&~`qYzjNn<@2=j4%d38GhQ=><RXdwRf|(T`<tx&6X`Hebo7^Z- zPtvONU22GhwRsCCYXwEA55@JLr0$R9`y6@tK>Vvz*(<ET7~QF*`{_P0lZX_QjM$f^ zD#OxVl3BXv>I%C_FX*~73n+qB&rHxFQs*||7+N}%PSga5$+hCpY~5@S=j%bjt6RTN zXtZWe_=_Dj{VO^Q{^bt;y1qOMMe~|9)R$G`Ms9wS=fU#5!h^L^6P3EH9=OiQgKm`> zE0rpz(+`~qrXSeTW0yV2>GI;Jm5#s;T_7ixw$|!I*8L4$4S5E6Ssxs&<LSzTua=8f zTt~GZBzewf<fd$lKkqGCdVhT1m|K*MJo^{e0sl9tx*TNsHy34?!#0)&s>gBUkG1GL zS|AO%d3UsGBS#B7+Fl#_O94OEXk*pC#%zny;AUVUv0rt%KO$qSDtcJY5opB**O6?i z7eHkGT}oOhdMKvE$edvj)-vWS$GNn5>E7mA4MY~dm$tmRhf%m?k74{nOM2HXac0J? zo$>7Hv+tOr<O|UE(zIrQjyYN*p95Ww<F|oo1wtf8B|6}w{3zmiDv9uDu(Qyn`I7){ z^9$exK+zLoq4ro3l-EEp0oQq2B!*@UmYCh9WE0z1>N4OU8t)tVS^bcM(P3?K-W(^U zu{3X(L?O0LhM@`07cy5U6T6bn;yu~hckkZLIy@zN15^n2^04c13~lId(I3!%Q4I!U zkWbn%VWSZcJT{F10<#;uWlQiid4;g`A2;AQ<`0O*oYQi|%W>-MuV~2Zc(L8aY5=2+ zf7idEtGLGW@J(2aZ@7?1@*xYqMGOV#PKR4vnhzCWz64lDi-Zh7#-RHL<TBA&ps8C~ zHkPHDX2CTFlq#5^mZF4PUO%Sb=OQY|)aiK;$0BM}H!@knu0$*%nMQof$?p6p#j6oH zq7ybgsP?N8;lq=ns^~d8Mbv_BTPac0!U_u-XJ!9%c*0!!+5nLwOvU*YI{yxv_&pqU zq6fc7n_?o&%f!s~WU8b$I^3O!?j)9SbQW|BUJ~V=azT#_v+`)BDn>`UG`kozB@kvs zOOfo9q$rxqh8G{{^aseA{3<b5Y&o>{W|02kpiCnKHqh+-)s@jbeN<B$BoG{eILN&q zIJ{V1bLlEAt*NiBthu_XCRz2&BDEkzfdZ~?k!fwu{}0a=Xsk;tku2ruPeIV_5Y%DA zhG4;hIxQgnD&Q_F78{oHyr^pbw#~Fy(JBxkYU<33P=5()CM#sOuCS6e9k*peB&@tk z6k+Nm-LqFbTYR>FPcU&nI{qJ3iTZx13k!s4`olZKA}GZsf0tsf@a)Aa(i!#>(K$WC zZu)pa9#Z8mKNFo3;!whwuG?X6G6mPZs8$<kMNZdAYPwP1MJ);93!pa$G)A1rTN3?+ z%>HM{Ik7(#oyUoXTX1|noZas%JVX0a#lD2w-(luQrT|>BxB6?11Br@Oep%jG7zqdq z+e9n~RB@m>59jJUJof5K!i^@a46D)Nu+8hZKun00k{zKArX$c6vWP(0dK-h~`O7<L z?(ojb^D_^y7LV%%X)vB}oCkCG*vy3sGt}L|vPwjc+JsQg2jwY$W=@lP@CXa^b1N)j zs{hkmN?J!I*<M1WVnV>4;}+r05HIk|K1F58QI?pp^Syx&@~p%z-QRgtmPKZI>H3)i zvPM2<6`5Z;BP$ZW5XR?d6K8v7kX2b0=a<sAM|R89kn!xvvu~J>$RpHBEgM!N@tm<? z6T6uNZZ5XM2z-+M0%)niyoGJ}nE2si+<QL4@`GD*BZVW6dvbE0l)3Qng|4DfJoVnk zeFizSC1pf-Hg#m*k@(ca;EZV-GoQ>&7&-~(_l*2Vb)!|k{jd`IyQ{iGZGdHIl8L}{ z<g;NT$n!#*1?<3X0d0xf@Z66;z+?zh4VZU-AmSCj2GJgmG*g!Bh5dpC7>mSKvuFAF zUxW}s#eArgpp%{CgGPv>zYAd}1*5ba2^uOU3vfYy;nyLqPL5W(4t7<>opX`M<}AtJ z+3Dd*_k4F{Y<gn<{ww?U<MCOzJR5oOmm<n=Ei#*3dfdIQyE49cYV=DsJ!@IB*~<q$ z-oO9j1JjkM$*Ivw_db_j{`+T}$lD+c(?~AyPZ3k0_MCQFtS;e}QuiFsG)<*~vp@(e zOp=jJlaptSEqb9mr9>=kdiX@-dc@T7UGO?Ee9Ddt4CDR12UDNeiWa?Q3=YjoG|3oV zHQl6UlQw~=tVb9A)Q)`(G%blHVZFhFU?Fe<=C;WotqB}@7f6^WAn{!ZiwzCp9}xZV zA0mLdMpH;f;LzaPNOMSm&2|6s-GbQg?$6)8v1+xoA=I|MZGC3l+C+^0eoUC|)&IA& zD*$cdO4IY+j7FpJcrunoBUu@b$Fe+{%8H`OvZSi2R#9BVI9jdG=jz<7&lRaQ4XG1? zF(!l(LI}PPN(iNxa2rerA%s#wD7~YUP)b>jvV>4dSxN~@2uoQ?$(5xnxe}}Q{x_0r zCn>jZjy0O6nT$OD{O`U0fB*k~pEoid?vq{pu;dE%07a043{2wrfK@Ow&bo)9KqliG z9|9cRq40Qg$d5j6<wUM=Mezq@um0zdDtTj98l2e5U(-=Um-S2f8oJ4I{OpGJfQW9Q zN)tsEL(lCBNpjGfUTocKqJx=CXybfyt~NV+z1hZTp}A~;A27f~ATw#FiUs%ZAZ7%- z4_p^4@}P<gI!bIcfRS(uf_s*6yM;L-3ZZHkh%q8YgxsI*5IGT_M-q6_4Ls7;9JM|d zEOS%B4&<l#YU4zm$AKuPouZX`e6wJuQg#8aAKUoi*ujfdI-kkm^VH4A<A+V+U3l{` z{I|m!Ui|QIVyzqumOFDLa}V8tHJD%Ec<Qd%`Z_7F#}cK|e5r))ln#{32TI+w)I7aT zA7DN^Ys%zWbf;T*`z$NEd)c8Uum5(UL@w`q4}3tkZJ+v5`@3j<sld50@1oz=-X-4% zLMLD8lnOju7~py4S3mP_m@Cuwt@pfj%QzTar`WIADai69{dZ8M<9!xNwumBT0ow!t zt{7y%z&}U|W>&0)87We~Fw61jAiEf9U0bp0#C9^wup*S~74(Pq!s7HoWY#N%IKJ>6 zz7c9oEL&y3l{Xb$oVAuwy}QPcC$@*dM44ZgrbhaGfWC_Mu>$lma>HZ{<vpx3ioy;@ z0X@k;YEoF-;i18(86DWE83Chjha~dUFzM+UCSfZI)!mk3mymlQMt6dIpbVuHScWhV ztIwV|F<V<N<kxHU6DR7m^?d$Hiz%!o5@(A`_|v7O;%Yp;it|SvEKZlp(~A#|)XL>r z>&jF<Kh=7K1b8QMBr^#69llNM4IOyt^i7JhXYtZ0XvcP5Iw!kj8!;WbN<G4`Wwzee zbu??(XlC;C>CP{ONtNt^j_eS;HoloUb$asDsmar)rcQU(HXO_ej?5=|so%v=%Vn!M z7q<Io2)b{G*wzMexRs&+Xo8NnE_(0&botA_-@5X>hHj_^i(41pxvQuTuiSj}<+|Rk zEqe6xE%q@>tSmCnB_aA6^zjrmJ*v9cE}9MvcH%>$BSH8&ADt82BN+$BVK^KeiDt=I z2teM$ZRQy`!w0lFZkCURW1<*8m}WDwC7I#Jl2?{n4>GBU#37;eW9zCEk|aeC(TbpY zy?=ioMieXbIA|eVOvJ<>F~qTKnhzxLo;*$T>khiU@r@>kmW-<k$4i${Md78O)mo#V zHEb7O!Re=&(il&TU^gW`b+G&C$arW=AMK}oRH1#;-Szf|`_g0VmL1B}GKL>&`sem1 z-Hk)s!Oo;wUm5DgH`t;(KmC-VoIPK&U*W$oYP;5O*nu(e_Ys?$IG1e<Qu3B%|DZfB zNWAc^s!oX-zpiM<RWH7$g*F&jM5d;e1uQ-cX@}Kdx9?5R>KY4To7m}cEa`G&4q$2C zB;DyOA`OyZe>iMdgO<KC7*KXlH$X+IxZMqa+5`6(xB{*Y@42hX%#ItYtBsAnESx*{ z$*O(DKDY5{b+Jl*@p=Z{s(51c6F9con9P^Tjn!|{wc3$t^&{9EskO0YXx_0x{PQfJ zv?X_&eoPE5aF(|5c?`!xpln&!h#N2vDZpL=QRw&)NXe)92E7QPOb~GjP?Xm!)T$!u zI;sI-hSxCc>4ce_wkL~hC>X4@?r0NsCS!+06lwifD6|^EAi7d0Ak#~{x(Y98UM>=T zc;noR2-9^!3ndkE3KeBbjvtDcNt2tnWSZZR=69yKF~58^s9A!2bvYz>X-@S6_9y%e z^8&Mn*-w1;L$ESG8pdvPXgtVbcL1^Yr5-G>W#keHJh49cEnLdL*ax#7#l3}PS^*Qg zOmMj<oVcsbxCGbK@0zbSetV$)%FI83lO7%Md1#g+?+!a?D^Zz+g+OB4XUWJJO<Utu zW-ObIrirXl17w8C1c-M|daa)fs4guFcDR~_3Oypx;StTN(E^j`aEB8R?41-l#<KT} z=>-v+n5!3KN@B8^OP30%5`J6ah$GHj#zKM@*LeX8oN!C3zty^~hq-VlenT%9LIkw4 zRTS&fT%AwdN@BjwiPy2v5YJqkM5&qEWo+?W;iH^YolOXQB|lqADa|0}afY~6`Z11N z_OM32C`Mk!p~?Ha^e0J=pV5MI72b<K&|425@!a)mnB%-y6I!>4^+R=Sm3^O0kv+(8 zC*H9&tV8raIs!nJK8g5bh~2l96tbOUVg)S9{q92nPrHJ9H)>M^Mrkc~Y7ksu&_ECS zHL85j`WUOSyzz}<D^)2U)AT7t!9;1~DX9<(X{{VuYqeU3kwVW!*U1rbvQky`SV69m z|3yxgt6C_Qmo`2@YY4ZFbZq4#<Q}G(9mG~nGBKz6NkW}kZ79tziQVR*SO0S~HmI=> zMg|5P3JXIqcwC!JOq%2l{98`AAx@VYXB*{d5sUb+(}-o#WWZ^Cip9=Bwpu^Gm@h9Z zl=F+{>m1i<B@dKWCb(8<^AmEM&R?f+k}X2CpNW))+TFXu2;<~U(>S250zY36z_hpi zk!&Vvg?Ats`<)7bEO8d+=O?fK<@)6OJn7vr%<&3mn`R^&%*WWw${&|6&n#WLwls5j z`Hw3C@r^~E&nJ?HQo-=V!X-N2{rAt`v%kmpn0_Y1yaICr19T`XKFYwlAaEOU#BQNb zWKTv$26hj`qanyW_wf!0m4TsxQ*%4Bo{U_S>ZskIrB+22NZ`pln4uI1&ph^uV(Xle zEG}QTwOY<urfHo#-aI*RXf}aAv`UtpT1YicK{GDLWV0Gym^odcP0N%yrF%FT+kO~p zGH1kP+qTUnZzYY0Z62#wx^BE<*37VF(ftTqp=`#nX+!g#2|DW>44F?HB$Dv7=bhVK z+(id-ah%zM(EeL7qMI`T<Z(-a=XZl4FDnUd>GhqR<@W&q9OqqlA?OoXzTZ%LIJbv) zFKB%`Si!HzY%eSHzNS0j4}CNIv9mNwFY5M4M>|&)-u!=G_SMp_Gnr)4sYuG~$?Pcs z=KqWN7joI;cyc`5hWmdK_?QNLpTcuAf1!E_%2|A4{^7tFP}u{iy?=OYczn!FUI2$Z z=6Cxk%RN2#Boa2fnuhVAVHC*k3By<`z|I<M41Bv_w<H?o{*m}uHz3H3K1-#3W{fRA zh0acAJaKBbdg;@vl=dQu4i0IETpM#8GF|B+Iau(V1HR7H0hekD$U@p6IX-0vxrme& zU;+v}NolJB_+SPi-}s#iU3%spTgMF*#Z@EK#1AiT-0xD{e)SW?eeY5JUx)YXHakPc zjeh2=GvXQK4}^NMqiz>w9j4^h7@n6Vw-vY#bk2g9{S#;MWT1>vDx{}Q;y7luefGZ) zW1HI#xvG8e;~%HQ7EjeK4Mm5dW7)C57+wKRhK(;^TR<P#txNI6$d$-3{E&Pq0RIAh zq{r3(dz0GqKmX6q@3KuYC&h`CApFM*W21ftpW4kQz;X#_4rQa`un~)gF2n$@fGeBP zh(VoTWim>#!Mr!AmrJWNx-Muz&gSGuDs2SK5TElJLU>y43l5&TITh!F!Q?el;6k}t zPT_*N=^Xm;{12*Rq{xScmP?C4Axx|uF<s3`Vo2nqWW-#M)5jMMl+&X6VQY$;IDKX* zBUD$<9!WTuC*;{^G$61G>`ot15)b7Y1p}rFT}DA18&5JHg@^3u(5~>VKp^Oc;POaj z6EB(Ec#4=C0e|}g+D1-Ut8)gvDSy%W_Dg{`KmPvXu)sm$mTcVq{&wr`#k+S~_wU~Q zLe-j6Cr?f_1x`#)OrVsaEKkhOPb{k{<X^4SG9>^~E@f&?tms?z0{)7OWdFzfM5dl` zIzK%T8Un1Vr(;4P$%zT6k#<Z7UD_TD(r^ueLCJX92?nL(XeF^N5ajq}msp93P&;{{ z^xNl5%(_N`7_&CwFtGqQ4onvS8#)U7yqv>C3E^!DuOm>b;aOo5YdAiNnAG#5*?4wm zNG07@a5E7U@oa<X-c9_G_S&Rh4ai_|N3ASqAV4cd`H`&%-dMbOUzo?0i`OnxFgJ5G zuS48ZOs<v=5tHqjmlN){{>qi}W)porvsSIXd;MLqVWoq)jSs!)WHRkF_?aq%N_ADq z7ddgEc(S+<&mC;V%#3s=z7~n;u?XFC#|52?3tnc3St@z-{vL+IUH)Tj6RV_ywzvhc z#ODApGV|NuNkW}K=F>2ZWDMw4eLF+GA$2J0cR?JJI@lCwFWU!bLz<t3s$eTh+(QVF zXy{_8x_*DXTC(3$jWd;IDc#Ieny49ht9pEW{do1Q2)dmDU$I%q%_(T9bx}Ext27;5 zJkEyLIvMeHL6pMVu3{REp*F!KAKif=Vn$UD(J_Ho8<_-x1ZdDch`*7i!}4n5P`R{H zIZTWJNyUnwA{>;x7nY8EuxNN^n}#5;F08rtDsoU(72I6@FRvtiCSBJ#GT+MwjSxDM zsvNnhs%LJ;j}+Coq<Iv#>Uv%h6f8SykmvAeHcd2hj43cLGyhgP>zA;rX9T%9ypQRT zTvCtgDCsy-4=Xhomx4T;%L6riZjp007?BLaBH!3SR8<xjnK(HN?iFyOj|}Cgcb2^M z!XxTM)#6K_wB<c7y<B|x#e6oC+&wxLAA2qu2@ehGq5eRh0_o^X48^=1AVgwe(V|)h z{*d^xe)UBJ%SY`)QQG!_{|I`HI!^8;w5E`%R8rH%wYmCarCFJ*T{EWJpTr{2AY*+l z7Hi3{&Bf54oc0NF?$lxleG#t0+12n;4gUL-@>y*X#qB)l8a4_-53%jX51clY<;O&p z4z6gC*$2$$mN}^TC7vgmExJh$Cr&)z=Ric_O^lELlNV%XL+pK)ytzPQT}-v~t88Xs z@2~ci_Pv<T6tl(j7!g+tU^(f6nhJpz@~gF#IJ-qeV9aKnX4mWAOfKk3=JXUFujLc5 zSUSj6KsHoB_Q8BQMcW%6+8`5q0z_il=zZrMpPx4J^>k2{6*C!eq*)82lO0LjF$}L( zNSL(gNNA(tNC?lhCFRsCpCi6dn%T=#iS53?yaCaj!Wedmp&b~xM#KGzhv5bA>BJsa zkLWtOL&5^`Ai)FpLp>tj(|{!sB10EyC~_}L>{-t&-SIGU9O?TWrrLO|`TA>bG~W2v z`B&#=|9Sd1`=|UFEgQ^e8MV(fFgnte-*}pM$Wi}ho2SX|18B<`;61;Dw&^0E7di4I z(_L5ColiE`TAEb2)n+_QcZ@_cZ1HrrKG&=|s@bg9Hx7V^bFr@+(Vur(MAN9~kB4iH zw3lkqozDIp$<n2z?vc`B`t8o6csspVdSK8;fWK}(3d?ER?I!^?z{kuFL}wvpGy*YD zpXdSXC&(^q=Z6525b>hgNKcS##&EO1p}<hI`KmDx9vT=iMtp&O6%2vwGqf`LlqTMo z>?+;zHch+QxtUF+I<{SrXzpaYuQ`AB3rv~W57?0}BzId778anuTaYlh6Bmb%_8=ff zV&8NlqCwylHhM)&4Fh=ZhI1$O8UvI2_U@n9zo+z4f#^i6eP?QaK!!C0)!p~x&9R-Q z&360PKXm^;Om+p3a-MUg-P7*<l4S7tYPhxfgm^Yq+K)Kt2&Xmw56BN}*PHC?Y=U`? zNkKK6)C197XDPb9aFW~|AMFpo()b8at~l&VQ8z6=1t6#wQJ}~LQjjqyW4Et;e<~j2 zIh8N<{@x6U_L(EPAr1NjP1YiT^ghvAKJnhP0Aa0MqZyI4QaoOYf1fJmf;K-PnfkG_ zwxpZCd0yEyAcuH4bz-h4@tipGKDiK*v_vV9D0O_FN0c8Fuxk^*m_b}f6!(#Ck0CD- z*p$2GDy2WGik`@DDCijw2cV#51V~yixf>s~jx?D}!vM3(jHLo@%SE0UP~0a_)`4$E zYhiR)mcBMp?_ap_SB!Pr#2AIYoGeysG;^NQRSwX@r+N8<9Qs_~H|}TZP4s2!BSX;) za#V<j4^Ylq=ZgRSXUht4rYf6jEAq(y0y{Qyx&U~bV_;-pU;yIWC9SjK`E9;3a5KLE ziZEOb^}Y|I|3CO2#r%gk0m$WGU;>E(0DV#p5CC|bV_;-pU=I491teepfAIeq^B)GF z2nx6f0JLNXO8|JBeUiOO13?hSZ+9!fN)Rk8uCi80Wo=;{6aro$R+bh%K#C+_BN0+W zd;&2~ASpuX6h@+jSlD?{P%#HyVshhuXXh@bg~gBA-I<+l6MCK*(3e?;tkKw1C@}%f zmLs7BLSbMf#rCXApbai)rta~+4Sd|U{y8^ks(MbEB4h;;I&(z{`)W&^4khXk_cd$d z{xclg#pidCdXmd=5%+6)a=8kAD)F~V6H}#Gs#B5Z0**V?mmrp0hEi*qn=kR4E-`n= z-`->PO<ZdT|NcBTv*&D5bQ$s(oWge*x+dP%txx*S>gOJSm0bO4zTCgx*xssLutRTr zzNL{`pa_iA4MpYvvX6V8MrNNP-N2s*q2YY0y3lj=WcpZoG;r{_B@{3>n^!Scuk@7n zV@Y~fa%4Ld{vpWCKAi8I+ZphT-K?Uoom?O4=v_hX=k^c!iM`Sr*k`fv9^qqCd~{)N z-(TpH=kyADpZ7n>;tuDv*_oIVdQbO<^9yLqvW8sFDAucX-)LfI%5%j(??cY8wSRvA zFpTdt000000000?0FVIS0WJYp0j2@W0u%yb0_+1|1I7d>1h54X1!@JF1=<D}23!V| z2HFQI2TTWi2et?#2(}4G3Bn2d3OEY33m^+}3&ad642%q}4E_x^4T=ro4p<Jz4;Bwb z4}=f05D*YZ5T+315mFK05-1Yn6BH9{6VMbE6vh=y6{;2L7DyI)7VZ~37rqz#7)BU+ z7~~l|8Q>Z~8i*S38<-p794Z`i9P%BS9rzx09@ZZ^AEF>SAkHCvA=n~XB6=dwBeEn& zB@8AiCqyTlC(I}MC{ifgDNreFDaa}qDrPFcD(EW~D?%%3D~c<sE6OY8ED9_xEK)3V zES4;~EZ!{qEfg&zEi^4mEp9E4EwnAtE+{TcE^sc6F4!+3FK#cOFVHYhFqkmZF!C`L zF-9?HF`6;RG59hpGG;QaGXOJMGq^NRG|V+XHJUZ%Hl8-xH%K?AIEFaDIV?F+IeIzB zItV&OI^a7RJGeX?JU~2lJit8uJ!CzuJ>Wh{K9D}VKNvrTKu$ogK@LG!LHI)0Ln=d< zL-<5qM65+NMWjXgMxaMFNBT%sNcKrqNxJ|60002l0C4~g0000000IC?09pWZ0002J z3vd7c004NLb&$(W!ax*;|B;Idi5gAZIx7P)mP;ZF5)u*&Y;;2mTT@DDOG<}K2NGVx zckp3+37^HEb}$BFo9TS#n=@xl2f#JHA&@^$961Y|G0OP_r|4NO&_7!)l3y&BP{o_& zGX1;d3LX$xuHptCtRRZ#j28H`EN~?*EuY{*+*vNrKUyx5dzMSM6|a`d^q%DkCL*<5 z#l83%sMS_SL1<)cP?zmyt0Sjdsi>mFjitLbq0*`B%ehL7AIHk^)N;>bKUl}E-iP~n z;_Eb0iEKH|y&Cz6uU+HMcGc5O&^Bf+=UOf0&}PV$Rtw)VPH4=k+i3hf4+AKyu!Tm% zs|sNtQKPMMZlj47I;1HxBs24@2nk%oG%nWM5oRkX<37inQ9|o8BPKiW_+1|PF)85w zm{s)A>R~(~?aqiYFWHyF?hdm?q$JC9Gduh3J;jEnwP_7^?Yh)iR}#a68j|<1TU*e0 z^iFmwhPI0a-+%t^2jgjkPXKtFZB_TXEJYE$=kCmGyccs;L=;6Gh4;)k3yLV_tUW!w zGtKr)_v>)?EoKo62u1`k3z!hhIV+e0nDMW%x_9?pe{jEVt50=T)pk`^o!!~pS$`%w z|NnD4b}>MP90i6LVT=i;m|+k5I0bjW9dRez8F#^5aW~u@_rN`IFWejV!F_Q*oQnJ7 zG&}&O<AHb(&cK855S)pJ;$e6=N<0FO#925S$8a2v!lUsRJQk0`<M9MM5l_OC@f182 zPs7vk3_KIh!n5%lJQwHSTs#lY$9Z@G&c_S!B3ytM<0W`0UWS+B6?i3Hg;(P>cr9Ls z*W(R%BN!@h)DW1XK?{isv4BE{9vTKGVBz2qaDXLFVug$FCcGJM!CUb*ydCerJ8?1I zg-dWL-i`O*y?7rk!~1bLuD}QIL3{`w#z*i`T#1k2DqM|^;~IPdpTwu|X?zBs#pm#O zT#M^)J-&c1;!F54zJeR@RosZ1@HKoL-@rHVEqoi_!FTaJd>=o+5Ah@X7(c;J@iY7! zzrZi?E8L7*@N4`Azs2wHd;9@^#GmkI`~|n-HvAQT!{6}_{1gAezi~VMvokbI3GYSW z1hc%I%oTI3l1=Ngj!BibwC$%!mZCF$l{wGsB#Mn5oUnNmZWLw7>N-D=wa`N&xerzp zoOQyoLa~(&6jt;h=dIuiI_SAj`|G9T(sCv0t?Et$W<{MD72GU}t-)H^9Os?vgWF4T zYp~I_0a~HVEQ;-UCi}UbMRB-OF}}!J-HA-eeo-**8E-SC`C=SJsg39dVkHKiHKR)F zMaS%7yag_!vNdDhhOTmDFw+gH^|BvDp){+DVB#`cFC|wx)VUQ#t;R`~OsU~q+a<Gg zej;cuGOlY;h?7QJU(f}(Bm=>GO9eMp_I_xEUkc$z3$dy+`3<@5sJ=eqE%R9=lp0ei zv>|n$(?eUIAMj{Lk7R{&BTA0N!$HT)xRzG%sKqdbIXB?J<la(83~J3Y`no+5rDA>U zI5T1tH7`#3*%p+tKN$C7=?op)RH0P7ETUqrRW0l=cA7j8gG!$qSm$!n>Rt@EZgQ`+ za=B~i;l@d_N{OOkZXx`jqS|xHqOY0Fm2PBTkffd(bdqOX@FI6Q^mTEdRp>;{)BCkL z*oL~=Wr}rXl6rgnGHV!2!RiZx^LZ8Rr&JetktpXuso4g*j@~9W+5|H+p;FSd#ZvYZ zWXi17%aZC<=~)bV*rZC#{b57e^h+CU5)M0YoLpjk#>qaS$=W!=Az8+C|In<Y)|UH+ zwn;tZ$RX*ojaO3kj$A9*>>YWMw*Nnlq)xYfgtO?DMe95n?FN$?xuhN$r62^xYkxx2 z+i4@2RP@N}bRycM6Jbmz9VRp1%1zc0ZOkH!W^o;GzTD-@Nh&c7H$f#1h#A(8mmoce zYr}l5XmGVQg<DEbgKLOuh^Vy54I@%2sIRrH3$IIRo)ImPzx-^@oG&*cx0jU215uk8 ztD86><ylAY>?3&W2p%seRwFzyhx4N-A_WesICyq7ixFukN-sMx>~t@^wne8asl+a? zbJ^1n3}sIjq84kEu2SEIdM`;T1T{3xd*bD+)4DI_sc)xgz)H%rr`!db(@)Pu74$0a zH03<5tQ2$3oEW)4MIk^#>~s2-$C5mrS2nnIL_|o9;#WD5H2D#A5Z4<iCTm25xZSLX z`PV2;X6ESz`;BFFNE}iUBaG-eO<;{EyyP^q`I1o;hPBm({$>VD+9@YamNX1hWlBXG zvl??HP2#U{CmNR6U{o{DXzoQ`TcV1h3NhR?a$iR@O+g1ldNT?@)5nO6YB5bpW$Evu z9VhIq5Ni~Z*h|PTOO+O?usU6^VWAN_JO2gBt>wG`004NLWANU=a3UyTBO_y9<Oar_ z91I)`$psr3Se-U8FfwR?7@N767&#y;J_az0&1o0Y0iZM|kj?J2i@^cHW@d2NsKVG0 z7_q^jBVr?Sipxed5N{Vp0|O(ALq~E*1V~9F5NzgQV&u`<!T7&*1558lCI%M(40a>* F001y~g2(^> diff --git a/src/mol-app/skin/fonts/fontello.woff2 b/src/mol-app/skin/fonts/fontello.woff2 deleted file mode 100644 index 49eb9640642432843920fcacac3346c95157ba26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23892 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∈=2{R& z;CQo<9eI^2ofJ03J}i@D%L+(^GJqhb&J~c2i(yUb&RFJw3H|7H%|+dNNIaerCdko~ zapitS&Fwi#fd=Eik#6^b3eA7ERsk0@-SaQV_uu=E=R|CjrPpEMs<K6jr%Gp!XVVxG za`Y|2^5eLJ1I>*pw=;}qxkexESeRk*6Ip`;xMpTDy=mz-^c>;pm-SYve~eco7$#*M z*PpaerK;N6n>Q9Jb!NUX?SQSVCA(4{n<Plh4@+%Q^3FdWAoMEQaN+>wD|9F7JV{`p zHKekjL)JIT;pBN-BH!vktfNL#lgg18r2f73+?lqixZdz!xJ{L4N_I+tNs*X9rY!nu zM*uou5-HFAlq~EwH!?<Zbf*QYxfRaZL&w7hx!rWr%DP3Gg<aEJTuC>ppkNWXq}KRX z<6)x0BTrAYk=wG%b9W~8&zy(X@ivo{{ZaJ{kZ#B5<jhv9yVB%oVpH_`sdn7uk(;&& z;LF+rAZRjUwQE}*b10>DgvjM-%VOQUKa0cXF*kdn?PU<9p3B573`H-6*4M3-LMf!E z!%4FuY2SG4b5Wp2!U*m8q*q1u>I$%C3qk%eivCY+IsJ1AnoU?(a8_>OB$?)-^!q&* zGIjCB#}Qz}*tv+vGoq`MeKQtwF~dKHFvPX~tfU&`?P2>yrd)^P8gana<d8}9mQ(q} z=CD2*Rz#x-Rii}9=T@p=1f7Br?*)?W_WS~gCM?~=d?DOa;3-{)<710n^H7XZdBL-Y zR!zrh8INm=rp5MV&)-F85tb?5y7_Ai3e0g?M7mcezM2X_8b`}r9CJx_rRh6I<mu5O z9i^q|CsQh%bVDNBtY*7LFwKYm=Q@lN_a=)TqEA7xM@eBVVWH_po37l=&QWb8T2TMz z{;85<OXO}Kp3`s2ueAGZF*c8(8lq`cZQtXoEV>^9-S&Mjb@>bUedm*{Oq|q|z2n;1 zCcYx`N@bMxn9Qp6=&_c%Z15fzi=^%srt4|);ADy<M0XRf-?9BeG~+;+Ru6f=ar};? zuJJl5h`sAFVz~t>qfQfJ9<c;r-C~hMGQDA1pjZ(C0SI2QSYdk^9MHzKp<RM9Ndrb* zkuK-ad%9V=QP-^~-lHpWva_(4PEvY~ZO(qDw?a`{oix57^rw|-xskVB+^r8%&_lQ5 zXNsMx60SL2VPuPA)u2luvhdB|YNvm>PD&KVxf+cEb0A~=bp9eyYOJu&4>XukaUW*Q zn#y+D<~iGJzmzE$`0}`>U8er={qw*@fAxS;<L&pOe8Dv<B)IdVRE-gaG&)$V6zS_t z^P|*q`n^->e`T25s^E1=Gg&E6MN<_6gU<qbyUEIm!y50-l>(Z#sYmIbW$**e^yj6m z*~yUFLScQek%Y?OlS(M5_Zb?Ien@KjXB-{|k!UXIY4vMO{1B=>=8LcsVT_|byw%oG zWHg+M`B};@*|X5f<le|QDGy}}9@B)T<EHmY92@Q;Q9yQs4=yoD#;2_yPr+Y*q>t@X z9yUYiLNtemUjCD0f8?ap{xG9FwuxN76Vsu+^YRAa21n-LOYb#slnfaX1-tEbQNAC% z7$oPU+e?7lt&X$2<oR~L0&)Ih%-V2bE<4aKT6sw0RE{Lp&G^Hv^<zJr!6j24F@4*_ zK}{`1f0vvsu?p;8_1CPzi)ZioW3Ern&(|=LYGSBi-Dv3LH4Y++2ve|g!YQ2nGw}02 zi^}?lyAvT9O3QCk4(Fm5$VP<L`JN7WJo90e)|+X2lP>%p7_fTwcP_;C#s`;lhgDk* ztR?&O^oPWf3bSOb!^aQLD7vZIQ#n1?8D+vT)P$x<?V`|Lc2?>8gl(+723T^e_cwzE zh{kaApTW^L`Nk6447M&~|I-Ivk~KS_d-k2G_@0Wt+5Wwr$6=2r`<(tdjX~y&mdbl% zcW`$t{^7?vmK=&P`Ve4tcJxh=<3%`n=gCa?&bX1m*Hva9VR9hXOtvRAHSKd-dHJm2 zmbnwe15tdT&7`Q}F!(-1sNsfNQ5a9}o}9SPmr6}O6D@}0;sw}$u(DN481u>~Rbvj{ zLNqJHr`7N?2MMJ99m&eeEN#SIqS2xACTmcZ4mON0L_ieqQ6LsYk`x|;EY`R7Z3_kO zGnUKhDsSOi-VCj$oy|l{p%Ydgh>`7<#Ifi6sHfly2&{iSDA#&fw8vQQz`|X6xiT_P z^{11DjaH2O_DEuesj&&cMooxTJw)9O5D^~^Jael_ODig|sH&uLUb>Qs4D+>fdPp=W zPCZY|XdYC$r}gqxFP$5B(5k5m5m{*I$HEj=SC{6qJpZ|hP^ThLk#x7+XO-!-Zx>9J zKdpz3JaDa~`*&|plsk3Xo3(39He<QQ^$0Ogd(l&z&wH-bO6Dohz&mV5CspQzX$~;e zlVD^xY5N1qFL+8bFqshTqWuZSYv%hTFx`<w#jqp~w{Y#b!8{AWRmw4*Y%2f6eGwu> zELtt>f9iPGaB=LJgE>h(xfRP|Kyd53xH&bhK?t{aG_He*N3x(RAAS3mF$euU_9l3x zwpY7`lfX8e!OR4n$^Z?DEuuN|(D%_2h$T_!40aA;t{ReXQNtwAy;*QS^?m{q>s}lo z%q-ztH*6wS@N34GfR$oSZq!^v8;=3Pz4v0&uvh9(7S*`~3zxX2Av5oifnEM8<IZGv zctfs3fh-kSs@Uh}*6q`z&ZqLTzWKs6UCKSls+TpQL%tEIcTtk?;cjF9WjET-L2xTW zFu|<OI4w0mjopqFvVe!-HXms@G{U<e;cgxIu~3+9(&DjrUVEpl{x9K#tO*OTI%O>M z8QeQ}Io}_Q0>h#a=^Ot&Rc|?o&>)M85GaulEc4neQx*ZmA;sJDYv||Q;F`~>@Df|X z=+)X~=)RR(;jh38BiPIQl?d`;>RXieukFI9B4&32e(igrXOD~W;^y-eyrLzCM$Thy zLPp&$a;-E))9YsKP?7j^(`uoo$$*fc5O-H0yLck#WWD<KRQSA}wZDG*ZQMh5(03H% z9RI0gKrdV+SL$vOW2qB(6pr4<Qp$HH84$Gz*To`>gE|l`G%MI-;l+9>+Y&M+M%`Ul z3G8g?&(fXpDVn$aS2R9;6(7(2Hd6(Q*X|s@^_`@LO8;KTO`to-i3J3d-jK_f>L9J~ za3UrTP3><J3sHsH>pweCw!RLx*ZQ<oWkzl9K`oX0C60MtT=-tg;B^thb%eNo=$M0i zW68H@X_Knzcf@dT`Fl+hP8gB@%a~Oq^r0vLXnC4=DLkLd-{q4^B}Jw2rjAB=Z?<sW zQ!#V;zu^Bk!dk~%>+2$$Qd6N6DUwCQPVa3M?VBny_C*Kx2+d*G&J-Vt_h|lJJqbpr z&A0s_u>39S4aGhK(*_VaMq!ZUrK2nr!o+g%z(z3>3(qFg472$w3JXmqvitx6)fnL9 zsh}?|+nCA~7^zKY57WzeqbyW{r-TCSV^^+Gi=`hVAx01pQLiVQZ;X4;@diF{b~0=l zXy~I$9H0lQ2&Sb)qAW=VG#O`LS)f8SkygjFTP3|0!(A7ZS5dgXdZ}j$&&6Er=2R!S zK5R)-vkFk*R#O|X>Gnr%fYa244>{9KsWQGhE437$i}Nm1kNS^r-0V%{^TmF}9B_K< zfI8ied1`=72t&*L=c!sDY--jfk_ojk_DPMRIjbG7tM~fMhwf<HR|I`ge0NKO<rFvI zZ|Qa9k(pND^RL+U8c_?(yQVB=XW2fxMMnx;aImpLc31gXENtB!<i{Hj&qgL%;RM#N zK2Mn}EYKIsQ+IFz+#w1i)B38(Spdlq_wW6dDqHO}U);pfU;FE~Bl{-D9`W4f4Tlr= z$ASF1_R(UNT|bH;cE>r<Z}7koubs)j^gE{j5xo!Ct{sL&xRuB+@Y_C2BZ4<UpuFv; z+~7XnbyemD{7%`6%2MrVwZPd$M|!x<CCOOfwm|a27+9u@EyS_|c?5wq>&IV2riFob zQ}p@;sh^_=m@yUchnSOR8T)|{lmUQFrXFgr6*#HHGgD{D$4rL8pD8jo?>`xfCsW?8 z%o58T;Xg#S6uH*NJ*_Shx%ZmgkL+(geUl#wTuO4_@e~xPX7Of2*o;+l?bC-Wp5hFz zH63^}=ggI~-QJ-$66y{{(D%v%Ws|Y_+6WkPpYV)%w7Lc)zX6!sG$xS^hMDbCk)iPr z-dvZ{4R#<@8$OZXkZXI+WY&R`CVydAH@c|3WBFBbqMsZ3r(U5&%Rc<BJJuT8Og!Fn z#CTu6vo6~#i;<$20J7%O3^hOvYK?LJ%F8(z)soLozvgl&XjW@N4pGm0;YEI7pUKn` zH>bkvXc_w}(@-}YR7l0Ib|r9GB)nVeXNIpUiJAF<G&;&8bn4ar(vu9@$>loK^Tcw* zbMIHpzFhOi+Tu3H8Us(=;l2Ic15ZWE%jr%7zV|$t-u_mpCq@KjamoM#QKK(n#QkQE z0r6+;&zDHYO1{vL8<_uWWHqj!@*{Za@Qb^W`*cmYv0QlIgDA*z%^6XU$LGYmCOcqh zfkf1UuNpea>m;&u>Q903>bJ{EOil9&=D-S}^a4dHe2kBf)TkBh?VW=rt89ztDmUnd z8iRL4#xKnZ6TkZ-6W{MMY#M0&`?F=GCP{hykOun0MP7~1m#7qZCW&j69}wYv8JF`U zPXo?9|753(iP-Sldrp8Zx1HYh2mb8>fe-t<*WE4%yjmBnv!*$O@G&+3m#8Se$i!?a zo=s1ipO;x*>HPBpmt<{JiC!fdEjk3U@__8Ry&cJKrz4Vz*k?pe6M^pL)AeL=+XXCC zY2@6+FM0Z|kL-wy3ExT58Vqi%oc+rA9Q6Hm`<ds_%Fl0L;w$~R4yEnu$LR7pggY8> zb@4k`Uh}$nOH=Oe7RqZrA`W(gUt-b8%B95K!;x{P(bn6DX^3yY(d%5`;&xZ!yH&kM zwCqwc1V}KXw!f^U=hfhKZ-H8T;}=()7|b0%_`sK~#8PRH4C%I%$`cVoy2gvUt^+Tm z$x!XFmYC79Q=>uEBPs3^>r<4$z%B!;=il0(A8@-;!t~sWk^s$<P_}C}EU)3$s>T>d z5A0Xd7e8%x|4l+MfL3>Iw0k#(aN$Y-$Y_lO{s3*%mpoKYOzzv?r$;k+B}Ew#Tw>W~ z98lcEzk#g+u<G<*^A9C41lbk)$!3To$TpTEZabPB#*3htFWCSm{pGyS{ee&rdvoZ| z;Mr!en<)%AN$hboHyDWPI=Gws&<Dyeohk3tr4nmXBhJjus6Uh(de~9$8a;@5GIu=? zuMs=>1^jnD$2^B~9O((A&>8{RqUlD?epjCjH^EF@Q=hV;wuMwvK6p(87R8o)W&Z@I zWax$8d>BJ%s~S;*xiJGaCCxiFS{XWll$}1F0&<7<O#d0~xss@zYlp4jg1GoVJPl<1 zPuocT4yzggaM&@b7Y2Q#Nm*<2hsVIt+I!`TnC|#j6H{`}JAB1n|4TD9@W~icba4H? zf(Th2Z;`M+(NG+`4*2gkL1C76NTH=aEJjjpq+Yjiv<_6qyI3f<O$B7cdKUktpRmmi zfof?_Gv_8vnh?7A9xtG4g}i@U-1qBhlVeU1#s53N7S%Q>!#QNOoXn+j0_#!%MMjWQ zbu~rigV4o~DCKcg=K{c#x+GeDZ{QN^naW8?+%DCk5}matD;WpTDlp6<6T+lnegkd0 z3pRWJMh@BHB`%9h497yq8>xz5C>UPOlOVE7Y1`D?IIB}_o8)AsU|$0l-TAYyRSyLR z@FMXWC5%(iuv&+8bdS!p4-gf!sf&cFBfQM06R>@e?vRnU{wL@h7voDk_N@C>(uGr) z(h*lqW0=cLOQhl|#|&_TPb^uk*un!WNeeS8wajt(-o^w?7N%H=S&<egVZa0%7&3b% zLB9W<0CzodKD_p9r~9yESyiTlnGr%^=FSpX3WTo;n>4Mn>-Bge(G)hylN@7?ag`oq zq%t;h`kFu-c(fB?0Z#Z4gEG!>{D(yE2hCw<rgqBM;bk*@*I>PQ!y!&ZO#|cM84EQJ zJ7T%%m;j#eITNWd`3n~hP~4X*@m`t6fk*Y?LAmbFt(&i7W-i@0d^(!{xrIsOG`cO} z6tp-}F@#n~93o)zSt4!qkHcU7&hd+Z30{<<>^8%@%YPisIWBm5I}&3Yi`Mts=-5@n zGgnx|s9vwnlA~i+8{E<%%k<WT6iY7}*o-WFk?;(c2A_>GXvWB5IhUODG4XI7pmV?C z{jur0c^rBUZ>)ys#c4V93EOAXbunxQ`qmD1CSe02H9V-iJUm@Z+=ZU{mxU-4y#Bg+ zyqU9v=IPp>EOQHG<VrnOU@~YSFu2W3Sp-`3wddQlsq@rxExOX(4KE_V5fTRlVlVMw zgMI?fmgIJpnucJ23*;@1&}<;IT977hCSf22H?@qXaQGnH)CBpO0u4N78>m_IHLTxZ zQ*T7Rhdi6_3cLlEWak(L3l0%r0h=jY;d2tcA*r>s1$<Z+x$9Vg9cU%aV9w`3!7369 zBTfV)Mn-?)GO@O`o7J_usnawGG}iCoFH37~WuXfZjLS<|i_mrK5Ts3Ven$#|H8eCu zNpFblgLYMBY}k+6xtyAS99rC|+t}9E-)HkIgyM=IEu5LBZs&ESj$OlpCOYpVsEXhi z9)FSi6oPVLF})@!RI;qaqGYaK9yR6rH^O%quq$l%92ov}I4Akm)Jox<gaU>#O;JWu z&$Dk^f40=MgCrI>*39WE46cfK_B+OGqqNxPIIXIc6Y18BmFjzBYlz+{iK@We`l<vt zFv#?M2xL^T2J-|a@bQRY#uLe?R#3Xil6VfsnSjV|7~~LfXK*3k1R9&o{{JhQrb{t` z!??n*>vuuU3yFzs?37Y;9GMdGlqj!vLpJTIRt<(VG1(erHV{?E(pDUJI|VKlURf2< zmwB?_6ebO_<4+FV7y4wG^$3{W-eGZb4HvT0OioP{ZsBt@T`-9o4v(+U9=s6?8=a$b zUR*c+aGBMmV7IgO$#DU2Gg26sUjDQ`DwXH~QD>G(Yk;7fj7Q9(s_y@5I<z#jUq;}$ zkIG1-zDuIj7Ig{O5l(zlakTI@aWd-_r{*<N^YU;mN%0-VpT5U{umNKcnsVt;=-Z8` zr#QV8oWoq7|HE`uDZ3Ug0;Q*XagKBxS)@u%fW*}_j-OLg+hYk){Mj=9n*dK@<Fq<S z$9Wz0apmb{h9393>>l!II~Et_@nFW|v>Jbph}23}lgQ~eA9ppuPw-SedkdQnm&-WT zbsS4c2=M23$DpA$czb<$cpi9qyxa$F*YT{LCJ?OEU42Wb$|8@mRMW(e)ubnH2v#go z^z1t^uiC|VAXWt$a=Mzh4}+Z<O^X-u90_NWi^R=W9JWTFs$d9I#Tu=xI0`%~x3eNG zi2?;C?I&0<dDBe4tK^uw&MideO2SwAAwU+IBrj#4`{v%P0cP&|#AKZm>H7`M7Qk4G zU>b@rA`|2H3Uf(FW_=x9qtNBItBZ@P8>@fKGP<~;jOC@)gGuLf>^`U&nA)laP1TX? zwU$<=PC-V=sTCdF!l#CjcJ-iv@c@ncBVS6+C&v0wn<jqb_c%{AF8FN`W$%sD*DKEk z6klPNjk>77wreW!epzJF){5eI3I{4|Hn$dDY)&Ju!p<%b2y~O)5S%e%(+hOM6ZH<| zfYc0LY0SL##VI?0n>B3q37ebSEn)DW^RXERur9yJ#R2B9d6gm1Z8U>;zoKL}H*<3I z?3;B`c_lKMbh*{Q3}fOScEGUxK!Arf@u{yQ+~J*kn*6j7NZBwM<jxW9<p&@N60C<P z7IWm1b!E}#$fW^Dp7*T~#TQm>kJkHmG9O%vF3|PzO95oi<fA#nOPa*mm%vj8WMz?$ z22d^vjX<Xdc0KK3a*nJ7FOrn$g2Z}Z>&OO9?OWFxtxmNtiz_C=7G8+n#L55x^4ynx z>LUib*J*LO1RMn;50Z4{R4Et?+f)iNu98!N2N>K7=aNK{-CWYo0zeg-cAC~lQk2@J z4Wy2_75}^c)aGfYo7irq(Z>FK!gkMXI)Z__i=qGD){8?{zk#P?-`91X$MN#uw<)bZ z_BWntd6nOIdOH>l`=YLR9J;11v&5$xH<rn`RUyD`bFu71f&E;kF!G5a$lUEaCm~Le zd?c>_6Tidt&%-&4%xz6!?ok4)Z4XX~8gasHUR~MmEr!2da8V@0jDl)^wsibFn;(ha zmZFJhnjjFw1m=LD+I2>t9+PTWUV3d|^Y`e~s$af9KvJ4I^tDNeEvMQBnbAWY7B);X zyNv(}oKVEBVBNS*LUB2BV3@s3qwd4eSnFA?2|1YE5os}JN#>Y5W%4>rqRZt#EKVeq zruvJUMBOz^urC1x>OF|*WlyJE9Qe_eW0T!Cg>1Oa;&3Uu?$%WYJaZ=6>|(-WB09u8 z9}lN#xE`));;qUt-G6Q2fO|}V2!zz=lq^(L(-63yRVsToDPe;~2!VOAVi_PVk0=4$ zQ4ts7WrX8BxZT2bbqGylU;&n}BdlFzw{ZLFwjK#GJZB6Q(_Aa4EOp|w){nft9r*gT zKMh4xX$yd14^Po4wNjd?FU6lii!BCV{5vN-t1m3FmYfJ2PnXYoecnNlKc3hrXPlV; zHay&(`!YERM}S+1606Dtf3<7J;+GqH<>zRQXD1?gyr@v}=6$+s42jyL+fJ-?tk&w} zcA;p?GRSh3h;9yk`MwBXiSl@bLiEeS9>S*IjQ>cA7ZS@O(X;{H*$XJVc>KWE`KBK- zz{8;;7*xY+Df{`zzYZ6#RLt_yDGNJeE~S6VQnoO(gNJ8$CnA=^5xRaBwOm+WkA>7- z^cRr<Gm#N^-Ua6$F_7}s3lkDWN1_{2DGwxf%;PiWGXGnch=sw97&5|hW}t`Yp+?&9 zOjUope74rgaGeawq1w<Y+?&kclBUkke@sEnd<`EcAawvgIqh@?93Ce`eU%Znr=%!c zAC{Gax~2sRk2R<iiee8mHSnw3`2=HaV>6~n;PMSAy%oF%H#Ie+vWQfn6d??|;*f?( zuECAruA9^F{q=qgo2Xe5@+DQ^Ihl~tkK|IFLjrTMMsr&b_l4%^O}kePhznbrh}RQV z`=yu;pOYcps7G;vfD9#U15X?gp)%<JPbD{>0c_Ei72|1NIdGdETR*OtNi`fo=yeAk zk9+{DSpVO+y}SzC&06enHtJyzA-v2o#fKaM<OwsO)+ny7u`OE`ai=%|Aovv@j@D>F zWS5t-m>(9ghfou8f6vN7(3GwL(MEUh?n#Nho9q7GQ<o>^g&~i*A+U;b54p8ECh^)1 z;*+=kYB4_)Ih%-i<F|{OOPdM-qD`NuFM45{E^{Y`xzA4psg}4-KryEwBqEO6x0-cc zwNN~i^T+g1cKxGXzwJ-Iln<tUJM>-uReoFtL~Ek<)mx-i=Zv5aj}@w2KTu}j;oh}U zUAHFw_<H}Q`AunTZ{4l-NAQj)!Q5sH-sR%%0YBh@VB-0YrRBTbWXX?9k;h*zb!EB! z?&&hu=KN5d>oXnqEmvy_`=ri|;KD6#;$W)ZFb^HqnHg~Ea+l~+kBB*Y_jUX5hHh%M zr?hjXz@H`i>fGIU<AaA1u?>Cr<(M){nbMX7P(o8?pc`ir;a8yFyh$59^Hs?)RoU*x z;~j44OZUSFRc8NPYh$vF7Jl5m>Y%Z7DD$B7A;LCkFU=WMZpaRPU{xM*BoOS<+M&J7 zD~qH_n*gIs{SUNiEOu(J3r9`$hek7Nfv4L>FI8wZ4S&Cv^^h7F>Tf^^V3b2Q?0nzm z#G%d|u_qC}H+x6gWYGrd913Y}iI+^s+|dv0xtXxFhNRv=*qkk1uGhY>h7E4#VsR$w zHb7Vqx2-sZPSx2ok2uu&DH|M2=VCmN5V@WLkVEr=Fb~DQYD9JlPTdPOjf1~UraYIf z#FP+lnsUNmihLg_91<8hFu`urQh{*|b&Z5b+n$qD1hBx78?Z;&1kvot%!jq`lkQPN zkLEoqSyKr=BAZV~XE%scYoX|p50J=v<5Ku3wBko7>>w{%GdVE)GoHLYcWP+}r?*qr zf}O{W48}KjNaWf^L_HpY6lco^pgBDl^wa0GyFG0b1`CoK`wNMZ50r;-ug_Hpog}~f z2h!DOZ3z5&MbGnG!gHyMe@^x<;5Uo9Ryi^Tf(B);<;yNGkt5{b&RX6<%wDnA^5rlr zWkfbcMNxSnrNJT3fmqa!V@jNJb1gCYmVWKoq#dESQs+H!*Pv)_^z1CTa9{*_PKh(q zXu4L??ev!r4tznk#sYFEaJL?--XpVWNfEIT(op`4_hV4ls;%G<K-f!^3`fu23(Y8C z5qM`b4B&4<d<7(o#4BW=M}K-X++T*nkif)yTde-c9hPFl8p7@#;svgR8@xje3WZrl z1g7};WtTiKCHFTa@b0BN_iY>op2hZ6SMeBj3;r^@CIp;<nT7$_X$nDe*hEOhA%ylu zBm%zN-bsFf@l|Ut1>Bkpn(ld-E=*s~PFUKvBz29fTWHni#EJL@(Y^IEW?L})>55~+ zJ{er8+Ygn1y+OGW>lK@)WAnHd{%E8bTDn3|13goi{>i3W*C+3~Ra<Y4BSJ<^=GwiH zp>u*?&nqd7Gy)NN3fJipkF6_<QcGGrCgft31H;V7yVC4-LmraVopaF~-?57p7r<%< zrt)*j+otq>xuJZHC9<eX6~pegHE%Pq;f1O!8ZtOm9OrW@s-R0TH;FKTkJX4JBwmkV z094L&d7<Y!@5H#v@1$Vz_Ijl;H5z2|u&oUw#`hb8hYG>{>o3!I#N&6=-`?u}(EGk| z8K0-8;~wn&S{p)FLyy+adG}4kN-Qhe+>n{ZWIfp-GTHu7U$Et8V}BoRSR9OvfxeV? zaAoIog|c_+H<}2$b3ibc&GGR&4S38Rv5`l8S<~tHlpZ87m)u}!#ZEBA*RNO02Eav$ zO>B+F^1|l#Q!}z?V6v3+cWf)&q$lo6JcM=hQC*l%k$@>|P6Ucy5kEE%cGWQ!UAr0< zaTfD33p(ZY750J(&u6^@%Hg%tLXYE60?0Rq@bSi9xW$t&)^FK^MU~u4v|mQtsI!EY zQL+gAV71Op6hJbQ_#8wKSqlI9%`(Np()TDiGKrB5D8PF5c#Sn^cbS8W163+=F*GWu zi2-tC5(kjjzq%wQtulb8JG8N#I^F^G*N<?}7eA2Ad}!%td_7|s@nP(|2>Xtbkp{!q z%u2CTYuZZJ-=hM2{^QhS^XAAw;Wqh1YA0;-D;RbRgWq<&?h~ynj&oQ!-|zZD42n;J zU7*Ar|3J2CAjCnE`5D++G<l^kR?u8WqJU+|O1X^*1LguJGMxW6fk)_32%VU>^ry?G z&{bqzKf+&)!qfPHga-bjKZ=@~?rc=-XU`9=ZLMQ@;{<!Yd5kS$h<EwzD`{n}B*HTe zz9g=5-@EX;e7G*YLgr_L*(5*tQa4SLD4^&aTZ~-M&N_%C3S+4SO5kWv3+k~{`@VjM zP^dCDoVNHXiTCaE$8bFFL^o%s_%x4?UH0ft5`;H~WPcWK$}EnFxP50c^J_|Vf4oof z^l+TZTc?<+VOxr;%dYGeT)c|BWos=XTk3&R>i#4IUgU!1NPdJET+yPNiXUcU+Z3p6 zDXsB!VthaBRxYjkGaHH(Wuz58!l!YsVmty<0&ij3#HWZ$#Lj184pYp7Ls*2uaKv0@ zv(ZKG;Zs#HdghIXila#tSAl1TBBVy$r2vzPtcv+jaV6vY9()%%-L*o*7cB<;qNeaS z8J27v^4)aHSHVxj0n1t4EQL&=R@P4!tchHaCEQq)-`sD&3G`#7ITiz#BVF84#vW6o zo5gH=FX|NQX$ZWPhEbszz9$7SW~zP$v$^=24~)my!jcdN;&=4R-fi4J^hqYgi8rt0 zLd!!5`a`AF{wl5hKB`B?%r1yL5UeVKRoTpmh=TyppfxW4J3Bqv-Pu@QSzeP8Rl(Fd z_)993nGa@q?-~c%X&nr-A+}I}>-vo-<A`||)EEnSMM&|%+44Dz@|Zohk-dmY7Vb$r z7R1}o1e&<xQk1mAxq^(MX+c57kvhW{R@6{PJ3a<|<=X7pFQZfa7qjAkqHJpW;nX!3 zbuF9k4)q*!d~Ya2)0S;RK0e`^m6l~%QzO*#UJqNV+m}a1z>p*Ta9%6$SZSMuy=?1| z&EMmE{WgxMQS_HziQsV#c%6E<kJpqhPm22PIapXcAj-ZT*9lAQIP7pcEFB~C-&_*z z#~^^;CgGP`iQu`(XT)HO&Cj#H6pbVHlauMjB{kWh*AMuT$<1fTaM;K3#vpIGa8CXQ zoj{3PQyg{2$O--*hyT+WCifRH>l!<g{PcM4tQEwo%fP)+ra_d4y?7raMAIB2RrMmN zt7J$AA*5wAE>+GR{_1kS-V3{_7>9Ugi`Cv2(}r{(kh#ehLi6~-=>!N@e4H%IV$9)s z_lF(u)2X}nchBX+I4>8x4OfR%pY6TVaFy8u0Ag^nFbp6mp3gJ!mD4}$hCn6!qOjLA z?lE~U(-NNCt<};R<xZ?9wzP_PtDovlqkl(|Yv65S)5n6b_TM%$y(@)Bn+ZziW#%H$ z<=g>IDo$n&w5^&4XlOhVW6J}d#rJQ#b{YTmUr^QSf7f}s-^OEi?$1q?A2x!@DK03n z`;+((9*k65kH`L+rn{KNp`2x&Zn=0-J>sZX=|(%-3?Rlt3miW9!v3wn{r0Us_yH$7 zPq(-G+vQvfX+yoN)~?6y8GHAnQ^rRZ4pGTU8@PS+T`b*DWn*n*`{=vu=Om1*!MWEP zYd{;{@4n$+MQGUnd3bhuczJZWzqh@$wz{-9J7c@qcUkcpr=HggP1zQ3BU6N^@w%-| zf+uD!av(MuzyinqB=BHm7Gc4J!%1?#H6BZI%x9Hz$U`0q*HV#%>`6m5hBM}-%K#hS z4Vz&N-_!lUkCXcB*sO-Vm+K|oh%5VH+!6W$@2(Oq)Ss|TXFNUjog-A_pu7k0;|XgT zkkKuF)rr)=ax2%jFI}k~m&e8}{7+(CTzF&5p3hsy1($C*9`Xy|>JaK{zfs8J?EgU; zfDdA=3-}+v(%9=5Fbsc+#`bU9FR*KK7$IyamrwEgAe@ew%)SmsuKDVmJBtp7o`65O zyZHDISPv{kXa~$~=jAMPyM|fJ5cR$MYUC4Xm}lnaVIW!YJa7(|E}u3>6a3ZsOTYO6 z+n~qrdJ0B3#em6<zdyHo_~`eSFLZR8+N&`~{zDIWAfKXMa!Oh}UHiSDJHp8i6oY(X zr@D-&*NBaZ7oDfl`NHPnwQBJRI(`>OiMoS4c}EU&raT5jUAeL=Vmy_0K!)gp|A(FR z$}EfxtuVgutE1aZU=G6g;sz|xJ3{Y(v{~J#tOx0GUWVi3ALS!mGq|0+4{$}5>A#>F z3#$Xgb4^1ID>rGlGu)giIVc}p?PadH@y*2$_Oe;xo-!@JTLySN$i)WPAr8wDysL=X zl!f>O+ykvkj?alAn*M6$D#EW30(`9s1+>Zr@`bA7n5`f-9ZolW^#9ApOZ9lHjZ@fA zz=f(4IVi~}9}FnQrSBnt+sxu%y@_(k#RHtII+z>|<3YESD}_xED#qJSR0@BXFzUN! z1Mq;3{S!Uu8K3Ay<>eFo_=pMT_ve(281#tU1}1Y)2*>XNJW-qo0UNC&A3Jn#o)|n3 z2~OzHKC$CkjC<;NS04RNUG3<+wMeJm&eF}*^+U?Xq=)x}2S|byO?{@%sp<$4(F@d$ zj{i;D67ad`Z}gvhvW|3pab@KGqCpNpMe%v&Zo7%7sk-*`GxiUa%=MKpE?R`WTwbQ- z^W>Y_(Rpi;PARiz>E{11&3s7Nm`o~Q<pBbju%c1%1pL{+b!v_vWEH(YyK<-D-$>im z^p%6h6vg%bq@FVEgHP8NR|3p0I)o@tB1La)?reO6^r#wA*XWg{pHn><J^z(3E=t52 z_Hy~Pq`dLhoi_u@bpw+5K{aX8Y-FH!+kYMn{pa4ksT|Z~+_HsqT+a`}C{EHWFUqQJ z+O8iC$8kEHFW1}s;qmGD<@N1-20#c#Pz)zXie^}j7eq-`{!6d_v(*jLvK`m+gD{Gd zG|P*!s++d!hjE&hb=!~gx}W#^0T6-_6vGLUq8XOs1yPa}RnraAvK`m+gD{GdG|P*! zs++d!hjE&hb=!~gx}W#^kyt8itn(qJTx#oMu6^$NuHPCY6hS))$lr4n;5)TwtL_k! zX{4Aeo~NVU<f~@@&8~na*F-X)@@VYUK?!EZ0qbDwr4w3&j09HTTf0m<m2R-1e~5F% zW7@Z;IArn(!~SFDdk0F>CY@JTWb%8$N2cjqI&Yq62$--;(~VMFUGXDL^Em4Y!;wqL z;2+&IqZTfYg%I$!7WK~r7wgopC_17FxW=sY$w1E(oS$_SxTa`5AMBPbR<h{3j3_-- z2zJ@_hPApwsXZ8{mcEnO76+1ss`Lg5=6zK`jT+F>p;Z!VRD5Lafk18~V;+kPPVz>S z@r}MmP2zJv%^8bmY#=&QN)v<}q{Lbb2ohp;DB=kv5HgRV9vB{~Lz@Z{`aej9p9ahd zS2N+jw<_wjqwwZ>Ie`+`L&FifCFmn-;Np0^ra^CREJ~%r6&%rFj;)#k($L7RLVTgh z`cXdFSgQpU>SaP}Vlx{MPM$j)32g>`P*g7WdI$+i05YOiEEX=Oc2S+Z?n}^2?IqZq zrk>GY)irC{=}N$?1#+94HD4+Gx7RWY<^RrNvgp|tCyqaKb)~c}{mu?48PnF=VCzVc zZ@cFr!FvIx`gW-KsH?=`8hT9Vl)c#IsR+wM^RgM8_)*!=X@x>*UZEu(R>+U+s}o%3 z5_d6fzwAG5ZYi$`xK-J9-0Nk&HWOmy`vNY*%L1;5UJxxv4Tt`>tQOC@!Zb>NfixwB zA-2b%1CD(pYYOLrLIKbc0mtBAGFr?r3I#w*1jwIJi0?nsxp*~p_<gg`3)6+;Ac(=k zZ2deFYVX?Y+(+@ZHi2HsZ*9h)Gg>EKDcDd)BaGX#%>a_M0*h;JblXEi09qzz^LllT zRqpD0$n}t$2DcMH7m5~U8C`D&B@7uvkiM(3y*VB(IOEW@M5$!E9x>q`7Q>?jBnKWW z^=o>QdNf(Rht11((Rv9;W=PJ|U2Cxw(WP~{ITI+Th2T8xrJQ*5`ZZlU<CHz(>49;a zD@tIhqGFtwyYYP8HhbrL<Z;Fl<!i_x$vW78#2Mop9uh8uZ^=N-z!7vn!+moIefa2d z*uGJqTpcd7!Tv!pdl00dcx?$q0n>txAquBL;XLwdYu^bawxMfw>U6<C(85?i-~dKb zJigH8C99Ps8d9zq1xdZpcdgR#GJ!n=T=p`vL-F@y9tlSb(HYPUsu>WZgvq*QE@gv$ z;XlIvZ?ARQb-rJ}NQ9lw4-x-6L5Z0c!SjD8N9Ds{?x-a^EXL~Px0zmT$H==Z+^mrI z1;>RP*r;t6hok&c_W!^$vwZ6?rTb&!DK|_fBp%=K)4?5V^z&ci%WQa90=dUfi^sW6 Jw_N}L004YQQtbc$ diff --git a/src/mol-app/skin/icons.scss b/src/mol-app/skin/icons.scss deleted file mode 100644 index 3d1d6cfcb..000000000 --- a/src/mol-app/skin/icons.scss +++ /dev/null @@ -1,135 +0,0 @@ - -[class^="molstar-icon-"]:before, [class*=" molstar-icon-"]:before { - font-family: "fontello"; - font-style: normal; - font-weight: normal; - speak: none; - - display: inline-block; - text-decoration: inherit; - width: 1em; - margin-right: .2em; - text-align: center; - /* opacity: .8; */ - - /* For safety - reset parent styles, that can break glyph codes*/ - font-variant: normal; - text-transform: none; - - /* fix buttons height, for twitter bootstrap */ - line-height: 1em; - - /* Animation center compensation - margins should be symmetric */ - /* remove if not needed */ - margin-left: .2em; - - /* you can be more comfortable with increased icons size */ - /* font-size: 120%; */ - - /* Font smoothing. That was taken from TWBS */ - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - - /* Uncomment for 3D effect */ - /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ - } - - .molstar-icon-expand-layout:before { - content: "\e84a"; - } - - .molstar-icon-plus:before { - content: "\e816"; - } - - .molstar-icon-minus:before { - content: "\e819"; - } - - .molstar-icon-reset-scene:before { - content: "\e891"; - } - - .molstar-icon-ok:before { - content: "\e812"; - } - - .molstar-icon-cross:before { - content: "\e868"; - } - - .molstar-icon-off:before { - content: "\e813"; - } - - .molstar-icon-expand:before { - content: "\e885"; - } - - .molstar-icon-collapse:before { - content: "\e883"; - } - - .molstar-icon-visual-visibility:before { - content: "\e826"; - } - - .molstar-icon-abort:before { - content: "\e814"; - } - - .molstar-icon-focus-on-visual:before { - content: "\e8a3"; - } - - .molstar-icon-settings:before { - content: "\e855"; - } - - .molstar-icon-tools:before { - content: "\e856"; - } - - .molstar-icon-log:before { - content: "\e8a5"; - } - - .molstar-icon-remove:before { - content: "\e847"; - } - - .molstar-icon-help:before { - content: '\e81c' - } - - .molstar-icon-info:before { - content: '\e81e' - } - - .molstar-icon-left-open-big:before { - content: '\e87c' - } - - .molstar-icon-right-open-big:before { - content: '\e87d' - } - - .molstar-icon-left-open:before { - content: '\e874' - } - - .molstar-icon-right-open:before { - content: '\e875' - } - - .molstar-icon-screenshot:before { - content: "\e80f"; - } - - .molstar-icon-help:before { - content: "\e81c"; - } - - .molstar-icon-help-circle:before { - content: "\e81d"; - } \ No newline at end of file diff --git a/src/mol-app/skin/layout.scss b/src/mol-app/skin/layout.scss deleted file mode 100644 index ded62b221..000000000 --- a/src/mol-app/skin/layout.scss +++ /dev/null @@ -1,29 +0,0 @@ - -@import 'layout/common'; - -.molstar-layout-standard-outside { - position: absolute; - @import 'layout/outside'; -} - -.molstar-layout-standard-landscape { - position: absolute; - @import 'layout/landscape'; -} - -.molstar-layout-standard-portrait { - position: absolute; - @import 'layout/portrait'; -} - -.molstar-layout-expanded { - position: fixed; - - @media (orientation:landscape) { - @import 'layout/landscape'; - }; - - @media (orientation:portrait) { - @import 'layout/portrait'; - }; -} \ No newline at end of file diff --git a/src/mol-app/skin/layout/common.scss b/src/mol-app/skin/layout/common.scss deleted file mode 100644 index 219de13b1..000000000 --- a/src/mol-app/skin/layout/common.scss +++ /dev/null @@ -1,60 +0,0 @@ - -.molstar-layout-expanded, .molstar-layout-standard { - left: 0; - right: 0; - top: 0; - bottom: 0; -} - -.molstar-layout-region { - overflow: hidden; - background: $default-background; -} - -.molstar-layout-static, .molstar-layout-scrollable { - position: absolute; -} - -.molstar-layout-scrollable { - overflow-y: auto; -} - -.molstar-layout-static { - overflow: hidden; -} - -.molstar-layout-main, .molstar-layout-bottom, .molstar-layout-top { - .molstar-layout-static { - left: 0; - right: 0; - top: 0; - bottom: 0; - } -} - -.molstar-layout-right { - - .molstar-layout-static { - left: 0; - right: 0; - top: 0; - height: $row-height + $control-spacing; - } - - .molstar-layout-scrollable { - left: 0; - right: 0; - top: $row-height + $control-spacing + 1; - bottom: 0; - } - -} - -.molstar-layout-left { - .molstar-layout-static { - left: 0; - right: 0; - bottom: 0; - top: 0; - } -} \ No newline at end of file diff --git a/src/mol-app/skin/layout/landscape.scss b/src/mol-app/skin/layout/landscape.scss deleted file mode 100644 index 418a7cd4b..000000000 --- a/src/mol-app/skin/layout/landscape.scss +++ /dev/null @@ -1,81 +0,0 @@ - -.molstar-layout-main { - position: absolute; - left: $expanded-left-width; - right: $expanded-right-width; - bottom: $expanded-bottom-height; - top: $expanded-top-height; -} - -.molstar-layout-top { - position: absolute; - left: $expanded-left-width; - right: $expanded-right-width; - height: $expanded-top-height; - top: 0; - border-bottom: 1px solid $border-color; -} - -.molstar-layout-bottom { - position: absolute; - left: $expanded-left-width; - right: $expanded-right-width; - height: $expanded-bottom-height; - bottom: 0; - border-top: 1px solid $border-color; -} - -.molstar-layout-right { - position: absolute; - width: $expanded-right-width; - right: 0; - bottom: 0; - top: 0; - border-left: 1px solid $border-color; -} - -.molstar-layout-left { - position: absolute; - width: $expanded-left-width; - left: 0; - bottom: 0; - top: 0; - border-right: 1px solid $border-color; -} - -.molstar-layout-hide-right { - .molstar-layout-right { - display: none; - } - .molstar-layout-main, .molstar-layout-top, .molstar-layout-bottom { - right: 0; - } -} - - -.molstar-layout-hide-left { - .molstar-layout-left { - display: none; - } - .molstar-layout-main, .molstar-layout-top, .molstar-layout-bottom { - left: 0; - } -} - -.molstar-layout-hide-bottom { - .molstar-layout-bottom { - display: none; - } - .molstar-layout-main { - bottom: 0; - } -} - -.molstar-layout-hide-top { - .molstar-layout-top { - display: none; - } - .molstar-layout-main { - top: 0; - } -} \ No newline at end of file diff --git a/src/mol-app/skin/layout/outside.scss b/src/mol-app/skin/layout/outside.scss deleted file mode 100644 index 74cac3e42..000000000 --- a/src/mol-app/skin/layout/outside.scss +++ /dev/null @@ -1,89 +0,0 @@ - -.molstar-layout-main { - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; -} - -.molstar-layout-top { - position: absolute; - right: 0; - height: $standard-top-height; - top: -$standard-top-height; - width: 50%; - border-left: 1px solid $border-color; - border-bottom: 1px solid $border-color; -} - -.molstar-layout-bottom { - position: absolute; - left: 0; - right: 0; - height: $standard-top-height; - top: -$standard-top-height; - width: 50%; - border-bottom: 1px solid $border-color; -} - -.molstar-layout-right { - position: absolute; - width: 50%; - right: 0; - bottom: -$standard-bottom-height; - height: $standard-bottom-height; - border-left: 1px solid $border-color; - border-top: 1px solid $border-color; -} - -.molstar-layout-left { - position: absolute; - width: 50%; - left: 0; - bottom: 0; - bottom: -$standard-bottom-height; - height: $standard-bottom-height; - border-top: 1px solid $border-color; -} - -///////////////////////////////////////// -.molstar-layout-hide-right { - .molstar-layout-right { - display: none; - } - .molstar-layout-left { - width: 100%; - } -} - -.molstar-layout-hide-left { - .molstar-layout-left { - display: none; - } - .molstar-layout-right { - width: 100%; - border-left: none; - } -} - -/////////////////////////////////// -.molstar-layout-hide-top { - .molstar-layout-top { - display: none; - } - .molstar-layout-bottom { - width: 100%; - border-left: none; - } -} - -.molstar-layout-hide-bottom { - .molstar-layout-bottom { - display: none; - } - .molstar-layout-top { - width: 100%; - border-left: none; - } -} \ No newline at end of file diff --git a/src/mol-app/skin/layout/portrait.scss b/src/mol-app/skin/layout/portrait.scss deleted file mode 100644 index efaf39113..000000000 --- a/src/mol-app/skin/layout/portrait.scss +++ /dev/null @@ -1,99 +0,0 @@ - -.molstar-layout-main { - position: absolute; - left: 0; - right: 0; - bottom: $expanded-portrait-bottom-height; - top: $expanded-portrait-top-height; -} - -.molstar-layout-top { - position: absolute; - right: 0; - height: $expanded-portrait-top-height; - top: 0; - width: 50%; - border-left: 1px solid $border-color; - border-bottom: 1px solid $border-color; -} - -.molstar-layout-bottom { - position: absolute; - left: 0; - right: 0; - height: $expanded-portrait-top-height; - width: 50%; - border-bottom: 1px solid $border-color; -} - -.molstar-layout-right { - position: absolute; - width: 50%; - right: 0; - bottom: 0; - height: $expanded-portrait-bottom-height; - border-left: 1px solid $border-color; - border-top: 1px solid $border-color; -} - -.molstar-layout-left { - position: absolute; - width: 50%; - left: 0; - bottom: 0; - height: $expanded-portrait-bottom-height; - border-top: 1px solid $border-color; -} - -///////////////////////////////////////// -.molstar-layout-hide-right { - .molstar-layout-right { - display: none; - } - .molstar-layout-left { - width: 100%; - } -} - -.molstar-layout-hide-left { - .molstar-layout-left { - display: none; - } - .molstar-layout-right { - width: 100%; - border-left: none; - } -} - -.molstar-layout-hide-right.molstar-layout-hide-left { - .molstar-layout-main { - bottom: 0; - } -} - -/////////////////////////////////// -.molstar-layout-hide-top { - .molstar-layout-top { - display: none; - } - .molstar-layout-bottom { - width: 100%; - border-left: none; - } -} - -.molstar-layout-hide-bottom { - .molstar-layout-bottom { - display: none; - } - .molstar-layout-top { - width: 100%; - border-left: none; - } -} - -.molstar-layout-hide-top.molstar-layout-hide-bottom { - .molstar-layout-main { - top: 0; - } -} \ No newline at end of file diff --git a/src/mol-app/skin/logo.scss b/src/mol-app/skin/logo.scss deleted file mode 100644 index 7bd02b7d3..000000000 --- a/src/mol-app/skin/logo.scss +++ /dev/null @@ -1,47 +0,0 @@ -.molstar-logo { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - - display: table; - width: 100%; - height: 100%; - - > div { - display: table-cell; - vertical-align: middle; - text-align: center; - - > div { - display: inline-block; - position: relative; - width: 50%; - max-width: 390px; - height: 130px; - - > div { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - } - - > div:first-child { - //border-radius: 8px; - background: rgba(0,0,0,0.75) - } - } - } -} - -.molstar-logo-image { -@include non-selectable; - -background-repeat: no-repeat; -background-position: center; -background-size: 90%; -background-image: url('data:image/png;base64,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'); -} \ No newline at end of file diff --git a/src/mol-app/skin/molstar-blue.scss b/src/mol-app/skin/molstar-blue.scss deleted file mode 100644 index 0d870a8c6..000000000 --- a/src/mol-app/skin/molstar-blue.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'colors/blue'; -@import 'base'; \ No newline at end of file diff --git a/src/mol-app/skin/molstar-dark.scss b/src/mol-app/skin/molstar-dark.scss deleted file mode 100644 index cdf80ffd9..000000000 --- a/src/mol-app/skin/molstar-dark.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'colors/dark'; -@import 'base'; \ No newline at end of file diff --git a/src/mol-app/skin/molstar-light.scss b/src/mol-app/skin/molstar-light.scss deleted file mode 100644 index d45b6bc81..000000000 --- a/src/mol-app/skin/molstar-light.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'colors/light'; -@import 'base'; \ No newline at end of file diff --git a/src/mol-app/skin/ui.scss b/src/mol-app/skin/ui.scss deleted file mode 100644 index cd572232a..000000000 --- a/src/mol-app/skin/ui.scss +++ /dev/null @@ -1,39 +0,0 @@ -@mixin non-selectable { - -webkit-user-select: none; /* Chrome/Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+ */ - /* Rules below not implemented in browsers yet */ - -o-user-select: none; - user-select: none; - - cursor: default; -} - -::-webkit-scrollbar { - width: 10px; - height:10px; -} - -::-webkit-scrollbar-track { - //-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.8); - border-radius: 0; - background-color: color-lower-contrast($control-background, 4%); -} - -::-webkit-scrollbar-thumb { - border-radius: 0; - //-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.9); - background-color: color-lower-contrast($control-background, 8%); -} - -@import 'components/controls-base'; -@import 'components/controls'; -@import 'components/entity'; -@import 'components/help'; -@import 'components/jobs'; -@import 'components/log'; -@import 'components/misc'; -@import 'components/panel'; -@import 'components/slider'; -@import 'components/viewport'; -@import 'components/sequence-view'; \ No newline at end of file diff --git a/src/mol-app/skin/variables.scss b/src/mol-app/skin/variables.scss deleted file mode 100644 index ddbf57aab..000000000 --- a/src/mol-app/skin/variables.scss +++ /dev/null @@ -1,78 +0,0 @@ - -// measures - -$control-label-width: 110px; -$row-height: 32px; -$control-spacing: 10px; -$entity-subtree-offset: 8px; -$info-vertical-padding: 6px; -$slider-border-radius-base: 6px; - -// layout -$expanded-top-height: 100px; -$expanded-bottom-height: 3 * $row-height + 2; -$expanded-right-width: 290px; -$expanded-left-width: 290px; - -$expanded-portrait-bottom-height: 10 * ($row-height + 1) + 3 * $control-spacing + 1; -$expanded-portrait-top-height: 2 * $row-height + 1; - -$standard-bottom-height: 8 * ($row-height + 1) + 3 * $control-spacing + 1; -$standard-top-height: 2 * $row-height + 1; - -////////////////////////////////////////////////// -// ENTITY COLORS - - -// entity colors are "somewhat orthogonal" on the RGB cube -// TypeClass = 'Root' | 'Group' | 'Data' | 'Object' | 'Visual' | 'Selection' | 'Action' | 'Behaviour' - -// DO NOT CHANGE THESE!! -$entity-color-Root: $default-background; -$entity-color-Data: color-lower-contrast(#95a5a6, 15%); -$entity-color-Selection: color-lower-contrast(#e74c3c, 15%); -$entity-color-Action: color-lower-contrast(#34495e, 10%); -$entity-color-Object: color-lower-contrast(#2ecc71, 10%); -$entity-color-Behaviour: color-lower-contrast(#9b59b6, 10%); -$entity-color-Visual: color-lower-contrast(#3498db, 5%); -$entity-color-Group: color-lower-contrast(#e67e22, 5%); - -////////////////////////////////////////////////// -// COLORS and COMPUTED COLORS - -$slider-disabledColor: #ccc; - -$control-background: color-increase-contrast($default-background, 6.5%); -$border-color: color-increase-contrast($default-background, 15%); -$molstar-form-control-background: color-lower-contrast($default-background, 2.5%); - -// buttons -$molstar-btn-link-font-color: $font-color; -$molstar-btn-link-toggle-on-font-color: $font-color; -$molstar-btn-link-toggle-off-font-color: color-lower-contrast($font-color, 33%); - -// used for "actions" -- i.e. + in selection -$molstar-btn-remove-font-color: $font-color; - -$molstar-btn-action-background: $molstar-form-control-background; - -// update selection etc -//!! $molstar-btn-commit-on-font-color: $entity-current-font-color; -$molstar-btn-commit-on-hover-font-color: color-lower-contrast($molstar-btn-commit-on-font-color, 20%); //!!Change -$molstar-btn-commit-on-background: color-lower-contrast($default-background, 2%); -$molstar-btn-commit-off-background: color-lower-contrast($default-background, 4%); //$control-background; -$molstar-btn-commit-off-font-color: $font-color; - -// log -$log-font-color: color-lower-contrast($font-color, 5%); -$log-timestamp-font-color: color-lower-contrast($font-color, 20%); - -// highlight -$highlight-info-font-color: $hover-font-color; -$highlight-info-additional-font-color: color-lower-contrast($hover-font-color, 20%); - -// entity state -$entity-color-fully-visible: $font-color; -$entity-color-not-visible: color-lower-contrast($font-color, 66%); -$entity-color-partialy-visible: color-lower-contrast($font-color, 33%); -$entity-tag-color: color-lower-contrast($font-color, 20%); \ No newline at end of file diff --git a/src/mol-app/ui/controls/common.tsx b/src/mol-app/ui/controls/common.tsx deleted file mode 100644 index 901250441..000000000 --- a/src/mol-app/ui/controls/common.tsx +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import * as React from 'react' -import { shallowEqual } from 'mol-util' - -export type ButtonSize = 'xs' | 'sm' | 'normal' | 'lg' - -export type ButtonStyle = 'link' | 'remove' | 'default' - -export abstract class Pure<Props> extends React.Component<Props, {}> { - shouldComponentUpdate(nextProps: any, nextState: any) { - return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState); - } -} - -export class Button extends Pure<{ - onClick: (e: React.MouseEvent<HTMLButtonElement> | React.TouchEvent<HTMLButtonElement>) => void, - size?: ButtonSize, - style?: ButtonStyle, - active?: boolean, - activeStyle?: ButtonStyle, - icon?: string, - activeIcon?: string, - disabled?: boolean, - disabledStyle?: ButtonStyle, - asBlock?: boolean, - title?: string, - customClass?: string, - customStyle?: any -}> { - render() { - - let props = this.props; - - let className = 'molstar-btn'; - if (props.size && props.size !== 'normal') className += ' molstar-btn-' + props.size; - if (props.asBlock) className += ' molstar-btn-block'; - - if (props.disabled) className += ' molstar-btn-' + (props.disabledStyle || props.style || 'default'); - else if (props.active) className += ' molstar-btn-' + (props.activeStyle || props.style || 'default'); - else className += ' molstar-btn-' + (props.style || 'default'); - - if (props.customClass) className += ' ' + props.customClass; - - let icon: any = void 0; - - if (props.icon) { - if (props.active && props.activeIcon) icon = <span className={ `molstar-icon molstar-icon-${props.activeIcon}` }></span> - else icon = <span className={ `molstar-icon molstar-icon-${props.icon}` }></span> - } - //onTouchEnd={(e) => { (e.target as HTMLElement).blur() } } - - return <button - title={props.title} - className={className} - style={props.customStyle} - disabled={props.disabled} - onClick={(e) => { props.onClick.call(null, e); (e.target as HTMLElement).blur() } } - > - {icon}{props.children} - </button> - } -} - -export const TextBox = (props: { - onChange: (v: string) => void, - value?: string, - defaultValue?: string, - onKeyPress?: (e: React.KeyboardEvent<HTMLInputElement>) => void, - onBlur?: (e: React.FormEvent<HTMLInputElement>) => void, - placeholder?: string -}) => <input type='text' className='molstar-form-control' placeholder={props.placeholder} value={props.value} defaultValue={props.defaultValue} - onBlur={e => { if (props.onBlur) props.onBlur.call(null, e) } } - onChange={e => props.onChange.call(null, (e.target as HTMLInputElement).value)} onKeyPress={props.onKeyPress} />; - -export function isEnter(e: React.KeyboardEvent<HTMLInputElement>) { - if ((e.keyCode === 13 || e.charCode === 13)) { - return true; - } - return false; -} - -export function TextBoxGroup(props: { - value: string, - onChange: (v: string) => void, - placeholder?: string, - label: string, - onEnter?: (e: React.KeyboardEvent<HTMLInputElement>) => void - title?: string -}) { - return <div className='molstar-control-row molstar-options-group' title={props.title}> - <span>{props.label}</span> - <div> - <TextBox placeholder={props.placeholder} onChange={props.onChange} value={props.value} onKeyPress={(e) => { - if (isEnter(e) && props.onEnter) props.onEnter.call(null, e) - } } /> - </div> - </div>; -} - -export const CommitButton = (props: { - action: () => void, - isOn: boolean, - on: string, - off?: string, - title?: string -}) => <div style={{ marginTop: '1px' }}><button onClick={e => { props.action(); (e.target as HTMLElement).blur(); }} - className={'molstar-btn molstar-btn-block molstar-btn-commit molstar-btn-commit-' + (props.isOn ? 'on' : 'off')} - disabled={!props.isOn} title={props.title}> - <span className={ `molstar-icon molstar-icon-${props.isOn ? 'ok' : 'cross'}` }></span> - {props.isOn ? <b>{props.on}</b> : (props.off ? props.off : props.on) } - </button></div> ; - -export const Toggle = (props: { - onChange: (v: boolean) => void, - value: boolean, - label: string, - title?: string -}) => <div className='molstar-control-row molstar-toggle-button' title={props.title}> - <span>{props.label}</span> - <div> - <button onClick={e => { props.onChange.call(null, !props.value); (e.target as HTMLElement).blur(); }}> - <span className={ `molstar-icon molstar-icon-${props.value ? 'ok' : 'off'}` }></span> {props.value ? 'On' : 'Off'} - </button> - </div> - </div> - -export const ControlGroupExpander = (props: { onChange: (e: boolean) => void, isExpanded: boolean }) => - <Button style='link' title={`${props.isExpanded ? 'Less' : 'More'} options`} onClick={() => props.onChange.call(null, !props.isExpanded) } - icon={props.isExpanded ? 'minus' : 'plus'} customClass='molstar-conrol-group-expander' /> - - -export const RowText = (props: { - value: any, - label: string, - title?: string -}) => <div className='molstar-control-row molstar-row-text' title={props.title}> - <span>{props.label}</span> - <div> - {props.value} - </div> - </div> - -export const HelpBox = (props: { - title: string, - content: JSX.Element | string -}) => <div className='molstar-help-row'> - <span>{props.title}</span> - <div>{props.content}</div> - </div> - -export function FileInput (props: { - accept: string - onChange: (v: FileList | null) => void, -}) { - return <input - accept={props.accept || '*.*'} - type='file' - className='molstar-form-control' - onChange={e => props.onChange.call(null, e.target.files)} - /> -} \ No newline at end of file diff --git a/src/mol-app/ui/controls/slider.tsx b/src/mol-app/ui/controls/slider.tsx deleted file mode 100644 index 23a2387bb..000000000 --- a/src/mol-app/ui/controls/slider.tsx +++ /dev/null @@ -1,824 +0,0 @@ -/* - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import * as React from 'react' -import { TextBox, isEnter } from './common' - -export class Slider extends React.Component<{ - label: any, - min: number, - max: number, - value: number, - step?: number, - title?: string, - callOnChangeWhileSliding?: boolean, - onChange: (v: number) => void -}, { value: string }> { - - state = { value: '0' } - - private firedValue = NaN; - - componentWillMount() { - this.setState({ value: '' + this.props.value }); - } - - componentWillReceiveProps(nextProps: any) { - this.setState({ value: '' + nextProps.value }); - } - - private updateValue(s: string) { - let v = +s; - if (v < this.props.min) { v = this.props.min; s = '' + v; } - else if (v > this.props.max) { v = this.props.max; s = '' + v; } - this.setState({ value: s }) - } - - private fire() { - let v = +this.state.value; - if (isNaN(v)) { v = this.props.value; } - if (v !== this.props.value) { - if (this.firedValue !== v) { - this.firedValue = v; - this.props.onChange.call(null, v); - } - } - } - - render() { - let step = this.props.step; - if (step === void 0) step = 1; - return <div className='molstar-control-row molstar-slider' title={this.props.title}> - <span>{this.props.label}</span> - <div> - <div> - <div> - <SliderBase min={this.props.min} max={this.props.max} step={step} value={+this.state.value} - onChange={v => { - this.setState({ value: '' + v }); - if (this.props.callOnChangeWhileSliding) this.fire(); - }} - onAfterChange={v => this.fire()} /> - </div> - </div> - <div> - <TextBox value={this.state.value} onChange={v => this.updateValue(v)} onBlur={() => this.fire()} onKeyPress={e => { - if (isEnter(e)) this.fire(); - } } /> - </div> - </div> - </div>; - } -} - -/** - * The following code was adapted from react-components/slider library. - * - * The MIT License (MIT) - * Copyright (c) 2015-present Alipay.com, https://www.alipay.com/ - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -function classNames(_classes: { [name: string]: boolean | number }) { - let classes = []; - let hasOwn = {}.hasOwnProperty; - - for (let i = 0; i < arguments.length; i++) { - let arg = arguments[i]; - if (!arg) continue; - - let argType = typeof arg; - - if (argType === 'string' || argType === 'number') { - classes.push(arg); - } else if (Array.isArray(arg)) { - classes.push(classNames.apply(null, arg)); - } else if (argType === 'object') { - for (let key in arg) { - if (hasOwn.call(arg, key) && arg[key]) { - classes.push(key); - } - } - } - } - - return classes.join(' '); -} - -function noop() { -} - -function isNotTouchEvent(e: TouchEvent) { - return e.touches.length > 1 || (e.type.toLowerCase() === 'touchend' && e.touches.length > 0); -} - -function getTouchPosition(vertical: boolean, e: TouchEvent) { - return vertical ? e.touches[0].clientY : e.touches[0].pageX; -} - -function getMousePosition(vertical: boolean, e: MouseEvent) { - return vertical ? e.clientY : e.pageX; -} - -function getHandleCenterPosition(vertical: boolean, handle: HTMLElement) { - const coords = handle.getBoundingClientRect(); - return vertical ? - coords.top + (coords.height * 0.5) : - coords.left + (coords.width * 0.5); -} - -function pauseEvent(e: Event) { - e.stopPropagation(); - e.preventDefault(); -} - -export class Handle extends React.Component<Partial<HandleProps>, {}> { - render() { - const { - className, - tipFormatter, - vertical, - offset, - value, - index, - } = this.props as HandleProps; - - const style = vertical ? { bottom: `${offset}%` } : { left: `${offset}%` }; - return ( - <div className={className} style={style} title={tipFormatter(value, index)} - /> - ); - } -} - -export interface SliderBaseProps { - min: number, - max: number, - step?: number, - defaultValue?: number | number[], - value?: number | number[], - marks?: any, - included?: boolean, - className?: string, - prefixCls?: string, - disabled?: boolean, - children?: any, - onBeforeChange?: (value: number | number[]) => void, - onChange?: (value: number | number[]) => void, - onAfterChange?: (value: number | number[]) => void, - handle?: JSX.Element, - tipFormatter?: (value: number, index: number) => any, - dots?: boolean, - range?: boolean | number, - vertical?: boolean, - allowCross?: boolean, - pushable?: boolean | number, -} - -export interface SliderBaseState { - handle: number | null, - recent: number, - bounds: number[] -} - -export class SliderBase extends React.Component<SliderBaseProps, SliderBaseState> { - private sliderElement: HTMLElement | undefined = void 0; - private handleElements: (HTMLElement | undefined)[] = []; - - state: SliderBaseState = { - handle: null, - recent: 0, - bounds: [0, 0], - }; - - constructor(props: SliderBaseProps) { - super(props); - - const { range, min, max } = props; - const initialValue = range ? Array.apply(null, Array(+range + 1)).map(() => min) : min; - const defaultValue = ('defaultValue' in props ? props.defaultValue : initialValue); - const value = (props.value !== undefined ? props.value : defaultValue); - - const bounds = (range ? value : [min, value]).map((v: number) => this.trimAlignValue(v)); - - let recent; - if (range && bounds[0] === bounds[bounds.length - 1] && bounds[0] === max) { - recent = 0; - } else { - recent = bounds.length - 1; - } - - this.state = { - handle: null, - recent, - bounds, - }; - } - - public static defaultProps: SliderBaseProps = { - prefixCls: 'molstar-slider-base', - className: '', - min: 0, - max: 100, - step: 1, - marks: {}, - handle: <Handle className='' vertical={false} offset={0} tipFormatter={v => v} value={0} index={0} />, - onBeforeChange: noop, - onChange: noop, - onAfterChange: noop, - tipFormatter: (value, index) => value, - included: true, - disabled: false, - dots: false, - range: false, - vertical: false, - allowCross: true, - pushable: false, - }; - - private dragOffset = 0; - private startPosition = 0; - private startValue = 0; - private _getPointsCache: any = void 0; - - componentWillReceiveProps(nextProps: SliderBaseProps) { - if (!('value' in nextProps || 'min' in nextProps || 'max' in nextProps)) return; - - const { bounds } = this.state; - if (nextProps.range) { - const value = nextProps.value || bounds; - const nextBounds = (value as number[]).map((v: number) => this.trimAlignValue(v, nextProps)); - if (nextBounds.every((v: number, i: number) => v === bounds[i])) return; - - this.setState({ bounds: nextBounds } as SliderBaseState); - if (bounds.some(v => this.isValueOutOfBounds(v, nextProps))) { - this.props.onChange!(nextBounds); - } - } else { - const value = nextProps.value !== undefined ? nextProps.value : bounds[1]; - const nextValue = this.trimAlignValue(value as number, nextProps); - if (nextValue === bounds[1] && bounds[0] === nextProps.min) return; - - this.setState({ bounds: [nextProps.min, nextValue] } as SliderBaseState); - if (this.isValueOutOfBounds(bounds[1], nextProps)) { - this.props.onChange!(nextValue); - } - } - } - - onChange(state: this['state']) { - const props = this.props; - const isNotControlled = !('value' in props); - if (isNotControlled) { - this.setState(state); - } else if (state.handle !== undefined) { - this.setState({ handle: state.handle } as SliderBaseState); - } - - const data = { ...this.state, ...(state as any) }; - const changedValue = props.range ? data.bounds : data.bounds[1]; - props.onChange!(changedValue); - } - - onMouseDown(e: MouseEvent) { - if (e.button !== 0) { return; } - - let position = getMousePosition(this.props.vertical!, e); - if (!this.isEventFromHandle(e)) { - this.dragOffset = 0; - } else { - const handlePosition = getHandleCenterPosition(this.props.vertical!, e.target as HTMLElement); - this.dragOffset = position - handlePosition; - position = handlePosition; - } - this.onStart(position); - this.addDocumentEvents('mouse'); - pauseEvent(e); - } - - onMouseMove(e: MouseEvent) { - const position = getMousePosition(this.props.vertical!, e); - this.onMove(e, position - this.dragOffset); - } - - onMove(e: MouseEvent | TouchEvent, position: number) { - pauseEvent(e); - const props = this.props; - const state = this.state; - - let diffPosition = position - this.startPosition; - diffPosition = this.props.vertical ? -diffPosition : diffPosition; - const diffValue = diffPosition / this.getSliderLength() * (props.max - props.min); - - const value = this.trimAlignValue(this.startValue + diffValue); - const oldValue = state.bounds[state.handle!]; - if (value === oldValue) return; - - const nextBounds = [...state.bounds]; - nextBounds[state.handle!] = value; - let nextHandle = state.handle!; - if (props.pushable !== false) { - const originalValue = state.bounds[nextHandle]; - this.pushSurroundingHandles(nextBounds, nextHandle, originalValue); - } else if (props.allowCross) { - nextBounds.sort((a, b) => a - b); - nextHandle = nextBounds.indexOf(value); - } - this.onChange({ - handle: nextHandle, - bounds: nextBounds, - } as SliderBaseState); - } - - onStart(position: number) { - const props = this.props; - props.onBeforeChange!(this.getValue()); - - const value = this.calcValueByPos(position); - this.startValue = value; - this.startPosition = position; - - const state = this.state; - const { bounds } = state; - - let valueNeedChanging = 1; - if (this.props.range) { - let closestBound = 0; - for (let i = 1; i < bounds.length - 1; ++i) { - if (value > bounds[i]) { closestBound = i; } - } - if (Math.abs(bounds[closestBound + 1] - value) < Math.abs(bounds[closestBound] - value)) { - closestBound = closestBound + 1; - } - valueNeedChanging = closestBound; - - const isAtTheSamePoint = (bounds[closestBound + 1] === bounds[closestBound]); - if (isAtTheSamePoint) { - valueNeedChanging = state.recent; - } - - if (isAtTheSamePoint && (value !== bounds[closestBound + 1])) { - valueNeedChanging = value < bounds[closestBound + 1] ? closestBound : closestBound + 1; - } - } - - this.setState({ - handle: valueNeedChanging, - recent: valueNeedChanging, - } as SliderBaseState); - - const oldValue = state.bounds[valueNeedChanging]; - if (value === oldValue) return; - - const nextBounds = [...state.bounds]; - nextBounds[valueNeedChanging] = value; - this.onChange({ bounds: nextBounds } as SliderBaseState); - } - - onTouchMove(e: TouchEvent) { - if (isNotTouchEvent(e)) { - this.end('touch'); - return; - } - - const position = getTouchPosition(this.props.vertical!, e); - this.onMove(e, position - this.dragOffset); - } - - onTouchStart(e: TouchEvent) { - if (isNotTouchEvent(e)) return; - - let position = getTouchPosition(this.props.vertical!, e); - if (!this.isEventFromHandle(e)) { - this.dragOffset = 0; - } else { - const handlePosition = getHandleCenterPosition(this.props.vertical!, e.target as HTMLElement); - this.dragOffset = position - handlePosition; - position = handlePosition; - } - this.onStart(position); - this.addDocumentEvents('touch'); - pauseEvent(e); - } - - /** - * Returns an array of possible slider points, taking into account both - * `marks` and `step`. The result is cached. - */ - getPoints() { - const { marks, step, min, max } = this.props; - const cache = this._getPointsCache; - if (!cache || cache.marks !== marks || cache.step !== step) { - const pointsObject = { ...marks }; - if (step !== null) { - for (let point = min; point <= max; point += step!) { - pointsObject[point] = point; - } - } - const points = Object.keys(pointsObject).map(parseFloat); - points.sort((a, b) => a - b); - this._getPointsCache = { marks, step, points }; - } - return this._getPointsCache.points; - } - - getPrecision(step: number) { - const stepString = step.toString(); - let precision = 0; - if (stepString.indexOf('.') >= 0) { - precision = stepString.length - stepString.indexOf('.') - 1; - } - return precision; - } - - getSliderLength() { - const slider = this.sliderElement; - if (!slider) { - return 0; - } - - return this.props.vertical ? slider.clientHeight : slider.clientWidth; - } - - getSliderStart() { - const slider = this.sliderElement as HTMLElement; - const rect = slider.getBoundingClientRect(); - - return this.props.vertical ? rect.top : rect.left; - } - - getValue(): number { - const { bounds } = this.state; - return (this.props.range ? bounds : bounds[1]) as number; - } - - private eventHandlers = { - 'touchmove': (e: TouchEvent) => this.onTouchMove(e), - 'touchend': (e: TouchEvent) => this.end('touch'), - 'mousemove': (e: MouseEvent) => this.onMouseMove(e), - 'mouseup': (e: MouseEvent) => this.end('mouse'), - } - - addDocumentEvents(type: 'touch' | 'mouse') { - if (type === 'touch') { - document.addEventListener('touchmove', this.eventHandlers.touchmove); - document.addEventListener('touchend', this.eventHandlers.touchend); - } else if (type === 'mouse') { - document.addEventListener('mousemove', this.eventHandlers.mousemove); - document.addEventListener('mouseup', this.eventHandlers.mouseup); - } - } - - calcOffset(value: number) { - const { min, max } = this.props; - const ratio = (value - min) / (max - min); - return ratio * 100; - } - - calcValue(offset: number) { - const { vertical, min, max } = this.props; - const ratio = Math.abs(offset / this.getSliderLength()); - const value = vertical ? (1 - ratio) * (max - min) + min : ratio * (max - min) + min; - return value; - } - - calcValueByPos(position: number) { - const pixelOffset = position - this.getSliderStart(); - const nextValue = this.trimAlignValue(this.calcValue(pixelOffset)); - return nextValue; - } - - end(type: 'mouse' | 'touch') { - this.removeEvents(type); - this.props.onAfterChange!(this.getValue()); - this.setState({ handle: null } as SliderBaseState); - } - - isEventFromHandle(e: Event) { - for (const h of this.handleElements) { - if (h === e.target) return true; - } - return false; - - // return this.state.bounds.some((x, i) => e.target - - // ( - // //this.handleElements[i] && e.target === ReactDOM.findDOMNode(this.handleElements[i]) - // )); - } - - isValueOutOfBounds(value: number, props: SliderBaseProps) { - return value < props.min || value > props.max; - } - - pushHandle(bounds: number[], handle: number, direction: number, amount: number) { - const originalValue = bounds[handle]; - let currentValue = bounds[handle]; - while (direction * (currentValue - originalValue) < amount) { - if (!this.pushHandleOnePoint(bounds, handle, direction)) { - // can't push handle enough to create the needed `amount` gap, so we - // revert its position to the original value - bounds[handle] = originalValue; - return false; - } - currentValue = bounds[handle]; - } - // the handle was pushed enough to create the needed `amount` gap - return true; - } - - pushHandleOnePoint(bounds: number[], handle: number, direction: number) { - const points = this.getPoints(); - const pointIndex = points.indexOf(bounds[handle]); - const nextPointIndex = pointIndex + direction; - if (nextPointIndex >= points.length || nextPointIndex < 0) { - // reached the minimum or maximum available point, can't push anymore - return false; - } - const nextHandle = handle + direction; - const nextValue = points[nextPointIndex]; - const { pushable: threshold } = this.props; - const diffToNext = direction * (bounds[nextHandle] - nextValue); - if (!this.pushHandle(bounds, nextHandle, direction, +threshold! - diffToNext)) { - // couldn't push next handle, so we won't push this one either - return false; - } - // push the handle - bounds[handle] = nextValue; - return true; - } - - pushSurroundingHandles(bounds: number[], handle: number, originalValue: number) { - const { pushable: threshold } = this.props; - const value = bounds[handle]; - - let direction = 0; - if (bounds[handle + 1] - value < threshold!) { - direction = +1; - } else if (value - bounds[handle - 1] < threshold!) { - direction = -1; - } - - if (direction === 0) { return; } - - const nextHandle = handle + direction; - const diffToNext = direction * (bounds[nextHandle] - value); - if (!this.pushHandle(bounds, nextHandle, direction, +threshold! - diffToNext)) { - // revert to original value if pushing is impossible - bounds[handle] = originalValue; - } - } - - removeEvents(type: 'touch' | 'mouse') { - if (type === 'touch') { - document.removeEventListener('touchmove', this.eventHandlers.touchmove); - document.removeEventListener('touchend', this.eventHandlers.touchend); - } else if (type === 'mouse') { - document.removeEventListener('mousemove', this.eventHandlers.mousemove); - document.removeEventListener('mouseup', this.eventHandlers.mouseup); - } - } - - trimAlignValue(v: number, nextProps?: SliderBaseProps) { - const { handle, bounds } = (this.state || {}) as this['state']; - const { marks, step, min, max, allowCross } = { ...this.props, ...(nextProps || {}) } as SliderBaseProps; - - let val = v; - if (val <= min) { - val = min; - } - if (val >= max) { - val = max; - } - /* eslint-disable eqeqeq */ - if (!allowCross && handle != null && handle > 0 && val <= bounds[handle - 1]) { - val = bounds[handle - 1]; - } - if (!allowCross && handle != null && handle < bounds.length - 1 && val >= bounds[handle + 1]) { - val = bounds[handle + 1]; - } - /* eslint-enable eqeqeq */ - - const points = Object.keys(marks).map(parseFloat); - if (step !== null) { - const closestStep = (Math.round((val - min) / step!) * step!) + min; - points.push(closestStep); - } - - const diffs = points.map((point) => Math.abs(val - point)); - const closestPoint = points[diffs.indexOf(Math.min.apply(Math, diffs))]; - - return step !== null ? parseFloat(closestPoint.toFixed(this.getPrecision(step!))) : closestPoint; - } - - render() { - const { - handle, - bounds, - } = this.state; - const { - className, - prefixCls, - disabled, - vertical, - dots, - included, - range, - step, - marks, - max, min, - tipFormatter, - children, - } = this.props; - - const customHandle = this.props.handle; - - const offsets = bounds.map(v => this.calcOffset(v)); - - const handleClassName = `${prefixCls}-handle`; - - const handlesClassNames = bounds.map((v, i) => classNames({ - [handleClassName]: true, - [`${handleClassName}-${i + 1}`]: true, - [`${handleClassName}-lower`]: i === 0, - [`${handleClassName}-upper`]: i === bounds.length - 1, - })); - - const isNoTip = (step === null) || (tipFormatter === null); - - const commonHandleProps = { - prefixCls, - noTip: isNoTip, - tipFormatter, - vertical, - }; - - this.handleElements = []; - const handles = bounds.map((v, i) => React.cloneElement(customHandle!, { - ...commonHandleProps, - className: handlesClassNames[i], - value: v, - offset: offsets[i], - dragging: handle === i, - index: i, - key: i, - ref: (h: any) => this.handleElements.push(h) //`handle-${i}`, - })); - if (!range) { handles.shift(); } - - const isIncluded = included || range; - - const tracks: JSX.Element[] = []; - // for (let i = 1; i < bounds.length; ++i) { - // const trackClassName = classNames({ - // [`${prefixCls}-track`]: true, - // [`${prefixCls}-track-${i}`]: true, - // }); - // tracks.push( - // <Track className={trackClassName} vertical={vertical} included={isIncluded} - // offset={offsets[i - 1]} length={offsets[i] - offsets[i - 1]} key={i} - // /> - // ); - // } - - const sliderClassName = classNames({ - [prefixCls!]: true, - [`${prefixCls}-with-marks`]: Object.keys(marks).length, - [`${prefixCls}-disabled`]: disabled!, - [`${prefixCls}-vertical`]: this.props.vertical!, - [className!]: !!className, - }); - - return ( - <div ref={e => this.sliderElement = e!} className={sliderClassName} - onTouchStart={disabled ? noop : this.onTouchStart.bind(this)} - onMouseDown={disabled ? noop : this.onMouseDown.bind(this)} - > - <div className={`${prefixCls}-rail`} /> - {tracks} - <Steps prefixCls={prefixCls} vertical={vertical} marks={marks} dots={dots} step={step} - included={isIncluded} lowerBound={bounds[0]} - upperBound={bounds[bounds.length - 1]} max={max} min={min} - /> - {handles} - <Marks className={`${prefixCls}-mark`} vertical={vertical!} marks={marks} - included={isIncluded!} lowerBound={bounds[0]} - upperBound={bounds[bounds.length - 1]} max={max} min={min} - /> - {children} - </div> - ); - } -} - -export interface HandleProps { - className: string, - vertical: boolean, - offset: number, - tipFormatter: (v: number, index: number) => any, - value: number, - index: number, -} - -interface MarksProps { - className: string, - vertical: boolean, - marks: any, - included: boolean | number, - upperBound: number, - lowerBound: number, - max: number, - min: number -} -const Marks = ({ className, vertical, marks, included, upperBound, lowerBound, max, min }: MarksProps) => { - const marksKeys = Object.keys(marks); - const marksCount = marksKeys.length; - const unit = 100 / (marksCount - 1); - const markWidth = unit * 0.9; - - const range = max - min; - const elements = marksKeys.map(parseFloat).sort((a, b) => a - b).map((point) => { - const isActived = (!included && point === upperBound) || - (included && point <= upperBound && point >= lowerBound); - const markClassName = classNames({ - [`${className}-text`]: true, - [`${className}-text-active`]: isActived, - }); - - const bottomStyle = { - // height: markWidth + '%', - marginBottom: '-50%', - bottom: `${(point - min) / range * 100}%`, - }; - - const leftStyle = { - width: `${markWidth}%`, - marginLeft: `${-markWidth / 2}%`, - left: `${(point - min) / range * 100}%`, - }; - - const style = vertical ? bottomStyle : leftStyle; - - const markPoint = marks[point]; - const markPointIsObject = typeof markPoint === 'object' && !React.isValidElement(markPoint); - const markLabel = markPointIsObject ? markPoint.label : markPoint; - const markStyle = markPointIsObject ? { ...style, ...markPoint.style } : style; - return (<span className={markClassName} style={markStyle} key={point}> - {markLabel} - </span>); - }); - - return <div className={className}>{elements}</div>; -}; - -function calcPoints(vertical: boolean, marks: any, dots: boolean, step: number, min: number, max: number) { - const points = Object.keys(marks).map(parseFloat); - if (dots) { - for (let i = min; i <= max; i = i + step) { - if (points.indexOf(i) >= 0) continue; - points.push(i); - } - } - return points; -} - -const Steps = ({ prefixCls, vertical, marks, dots, step, included, - lowerBound, upperBound, max, min }: any) => { - const range = max - min; - const elements = calcPoints(vertical, marks, dots, step, min, max).map((point) => { - const offset = `${Math.abs(point - min) / range * 100}%`; - const style = vertical ? { bottom: offset } : { left: offset }; - - const isActived = (!included && point === upperBound) || - (included && point <= upperBound && point >= lowerBound); - const pointClassName = classNames({ - [`${prefixCls}-dot`]: true, - [`${prefixCls}-dot-active`]: isActived, - }); - - return <span className={pointClassName} style={style} key={point} />; - }); - - return <div className={`${prefixCls}-step`}>{elements}</div>; -}; \ No newline at end of file diff --git a/src/mol-app/ui/entity/tree.tsx b/src/mol-app/ui/entity/tree.tsx deleted file mode 100644 index d83e0a9da..000000000 --- a/src/mol-app/ui/entity/tree.tsx +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' - -import { View } from '../view'; -import { EntityTreeController } from '../../controller/entity/tree'; -import { Controller } from '../../controller/controller'; -import { AnyEntity, RootEntity } from 'mol-view/state/entity'; -import { AnyTransform, SpacefillUpdate, UrlToData, DataToCif, FileToData, CifToMmcif, MmcifToModel, ModelToStructure, StructureToSpacefill, MmcifFileToSpacefill, StructureCenter, StructureToBallAndStick, DistanceRestraintUpdate, CartoonUpdate, BallAndStickUpdate, BackboneUpdate, MmcifUrlToSpacefill, CarbohydrateUpdate } from 'mol-view/state/transform'; - -function getTransforms(entity: AnyEntity): AnyTransform[] { - const transforms: AnyTransform[] = [] - switch (entity.kind) { - case 'root': - transforms.push(MmcifFileToSpacefill, MmcifUrlToSpacefill) - break; - case 'url': - transforms.push(UrlToData) - break; - case 'file': - transforms.push(FileToData) - break; - case 'data': - transforms.push(DataToCif) - break; - case 'cif': - transforms.push(CifToMmcif) - break; - case 'mmcif': - transforms.push(MmcifToModel) - break; - case 'model': - transforms.push(ModelToStructure) - break; - case 'structure': - transforms.push(StructureToSpacefill, StructureToBallAndStick, StructureCenter) - break; - case 'spacefill': - transforms.push(SpacefillUpdate) - break; - case 'ballandstick': - transforms.push(BallAndStickUpdate) - break; - case 'distancerestraint': - transforms.push(DistanceRestraintUpdate) - break; - case 'backbone': - transforms.push(BackboneUpdate) - break; - case 'cartoon': - transforms.push(CartoonUpdate) - break; - case 'carbohydrate': - transforms.push(CarbohydrateUpdate) - break; - } - return transforms -} - -export class Entity extends View<Controller<any>, {}, { entity: AnyEntity}> { - render() { - const entity = this.props.entity - - return <div className='molstar-entity-tree-entry'> - <div className='molstar-entity-tree-entry-body'> - <div className='molstar-entity-tree-entry-label-wrap'> - <button - className='molstar-entity-tree-entry-label' - onClick={() => { - console.log(entity) - this.controller.context.currentEntity.next(entity) - this.controller.context.currentTransforms.next(getTransforms(entity)) - }} - > - <span>{entity.id} - {entity.kind}</span> - </button> - </div> - </div> - </div>; - } -} - -export class EntityTree extends View<EntityTreeController, {}, {}> { - render() { - const entities: JSX.Element[] = [] - const state = this.controller.state.getValue() - if (state) { - state.entities.forEach(e => { - entities.push( - <div key={e.id}> - <Entity controller={this.controller} entity={e}></Entity> - </div> - ) - }) - } - - return <div className='molstar-entity-tree'> - <div className='molstar-entity-tree-root'> - <Entity controller={this.controller} entity={RootEntity}></Entity> - </div> - <div className='molstar-entity-tree-children'> - <div>{entities}</div> - </div> - </div>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/layout.tsx b/src/mol-app/ui/layout.tsx deleted file mode 100644 index df5184a84..000000000 --- a/src/mol-app/ui/layout.tsx +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import * as React from 'react' -import { LayoutController, LayoutTarget, LayoutRegion, CollapsedControlsLayout } from '../controller/layout'; -import { View } from './view'; - -export class Layout extends View<LayoutController, { }, { }> { - - private renderTarget(target: LayoutTarget) { - const statics: any[] = []; - const scrollable: any[] = []; - - for (let c of target.components) { - if (c.isStatic) statics.push(<c.view key={c.key} controller={c.controller} />); - else scrollable.push(<c.view key={c.key} controller={c.controller} />); - } - - return <div key={target.cssClass} className={'molstar-layout-region molstar-layout-' + target.cssClass}> - { statics.length ? <div className='molstar-layout-static'>{statics}</div> : void 0 } - { scrollable.length ? <div className='molstar-layout-scrollable'>{scrollable}</div> : void 0 } - </div>; - } - - private updateTarget(name: string, regionType: LayoutRegion, layout: { regions: any[], layoutClass: string }) { - const state = this.controller.latestState; - const regionStates = state.regionStates; - const region = this.controller.targets[regionType]; - let show: boolean; - - if (state.hideControls) { - show = regionStates !== void 0 && regionStates[regionType] === 'Sticky' && region.components.length > 0; - } else if (regionStates && regionStates[regionType] === 'Hidden') { - show = false; - } else { - show = region.components.length > 0; - } - - if (show) { - layout.regions.push(this.renderTarget(region)); - } else { - layout.layoutClass += ' molstar-layout-hide-' + name; - } - } - - render() { - let layoutClass = ''; - - const state = this.controller.latestState; - let layoutType: string; - - if (state.isExpanded) { - layoutType = 'molstar-layout-expanded'; - } else { - layoutType = 'molstar-layout-standard '; - switch (state.collapsedControlsLayout) { - case CollapsedControlsLayout.Outside: layoutType += 'molstar-layout-standard-outside'; break; - case CollapsedControlsLayout.Landscape: layoutType += 'molstar-layout-standard-landscape'; break; - case CollapsedControlsLayout.Portrait: layoutType += 'molstar-layout-standard-portrait'; break; - default: layoutType += 'molstar-layout-standard-outside'; break; - } - } - - const targets = this.controller.targets; - const regions = [this.renderTarget(targets[LayoutRegion.Main])]; - - const layout = { regions, layoutClass }; - this.updateTarget('top', LayoutRegion.Top, layout); - this.updateTarget('right', LayoutRegion.Right, layout); - this.updateTarget('bottom', LayoutRegion.Bottom, layout); - this.updateTarget('left', LayoutRegion.Left, layout); - layoutClass = layout.layoutClass; - - let root = targets[LayoutRegion.Root].components.map(c => <c.view key={c.key} controller={c.controller} />); - - return <div className='molstar-plugin'> - <div className={'molstar-plugin-content ' + layoutType}> - <div className={layoutClass}> - {regions} - {root} - </div> - </div> - </div>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/misc/jobs.tsx b/src/mol-app/ui/misc/jobs.tsx deleted file mode 100644 index 6090da945..000000000 --- a/src/mol-app/ui/misc/jobs.tsx +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import * as React from 'react' -import { JobInfo, JobsController } from '../../controller/misc/jobs'; -import { Button } from '../controls/common'; -import { View } from '../view'; - -class JobState extends React.Component<{ info: JobInfo, isSmall?: boolean }, {}> { - render() { - const info = this.props.info; - return <div className='molstar-task-state'> - <div> - { info.abort ? <Button onClick={() => info.abort!.call(null) } style='remove' - icon='abort' title='Abort' customClass='molstar-btn-icon' - /> : void 0 } - <div> - {info.name}: {info.message} - </div> - </div> - </div>; - } -} - -export class Overlay extends View<JobsController, {}, {}> { - render() { - const state = this.controller.latestState; - - if (!Object.keys(state.jobs).length) return <div className='molstar-empty-control' /> - - const jobs: any[] = []; - Object.keys(state.jobs).forEach(k => jobs.push(<JobState key={k} info={state.jobs[parseInt(k)]} />)); - - return <div className='molstar-overlay'> - <div className='molstar-overlay-background' /> - <div className='molstar-overlay-content-wrap'> - <div className='molstar-overlay-content'> - <div> - {jobs} - </div> - </div> - </div> - </div>; - } -} - -export class BackgroundJobs extends View<JobsController, {}, {}> { - render() { - const state = this.controller.latestState; - - if (!Object.keys(state.jobs).length) return <div className='molstar-empty-control' /> - - const jobs: any[] = []; - Object.keys(state.jobs).forEach(k => jobs.push(<JobState key={k} info={state.jobs[parseInt(k)]} isSmall={true} />)); - - return <div className='molstar-background-jobs'> - {jobs} - </div>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/misc/log.tsx b/src/mol-app/ui/misc/log.tsx deleted file mode 100644 index 803d86f70..000000000 --- a/src/mol-app/ui/misc/log.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import * as React from 'react' -import { View } from '../view'; -import { LogController } from '../../controller/misc/log'; -import { CommonEvents } from '../../event/basic'; -import { formatTime } from 'mol-util'; -import { Logger } from '../../service/logger'; - -export class Log extends View<LogController, {}, {}> { - - private wrapper: HTMLDivElement | undefined = void 0; - - componentWillMount() { - super.componentWillMount(); - this.subscribe(CommonEvents.LayoutChanged.getStream(this.controller.context), () => this.scrollToBottom()); - } - - componentDidUpdate() { - this.scrollToBottom(); - } - - private scrollToBottom() { - const log = this.wrapper; - if (log) log.scrollTop = log.scrollHeight - log.clientHeight - 1; - } - - render() { - const entries = this.controller.latestState.entries; - - return <div className='molstar-log-wrap'> - <div className='molstar-log' ref={log => this.wrapper = log!}> - <ul className='molstar-list-unstyled'> - {entries.map((entry, i, arr) => { - - let label: JSX.Element; - let e = entry!; - switch (e.type) { - case Logger.EntryType.Error: - label = <span className='label label-danger'>Error</span>; - break; - case Logger.EntryType.Warning: - label = <span className='label label-warning'>Warning</span>; - break; - case Logger.EntryType.Info: - label = <span className='label label-info'>Info</span>; - break; - default: - label = <span></span> - } - - let t = formatTime(e.timestamp); - return <li key={i}> - <div className={'molstar-log-entry-badge molstar-log-entry-' + Logger.EntryType[e.type].toLowerCase()} /> - {label} - <div className='molstar-log-timestamp'>{t}</div> - <div className='molstar-log-entry'>{e.message}</div> - </li>; - }) } - </ul> - </div> - </div>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/transform/backbone.tsx b/src/mol-app/ui/transform/backbone.tsx deleted file mode 100644 index 93a4a3392..000000000 --- a/src/mol-app/ui/transform/backbone.tsx +++ /dev/null @@ -1,244 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' - -import { View } from '../view'; -import { Controller } from '../../controller/controller'; -import { Toggle } from '../controls/common'; -import { BackboneEntity } from 'mol-view/state/entity'; -import { BackboneUpdate } from 'mol-view/state/transform' -import { StateContext } from 'mol-view/state/context'; -import { ColorThemeProps, ColorThemeNames, ColorThemeName } from 'mol-view/theme/color'; -import { SizeThemeProps } from 'mol-view/theme/size'; -import { Color, ColorNames } from 'mol-util/color'; -import { Slider } from '../controls/slider'; -import { Unit } from 'mol-model/structure'; -import { VisualQuality } from 'mol-geo/geometry/geometry'; - -interface BackboneState { - doubleSided: boolean - flipSided: boolean - flatShaded: boolean - detail: number - colorTheme: ColorThemeProps - colorValue: Color - sizeTheme: SizeThemeProps - visible: boolean - alpha: number - depthMask: boolean - useFog: boolean - quality: VisualQuality - unitKinds: Unit.Kind[] - radialSegments: number -} - -export class Backbone extends View<Controller<any>, BackboneState, { transform: BackboneUpdate, entity: BackboneEntity, ctx: StateContext }> { - state = { - doubleSided: true, - flipSided: false, - flatShaded: false, - detail: 2, - colorTheme: { name: 'element-symbol' } as ColorThemeProps, - colorValue: Color(0x000000), - sizeTheme: { name: 'uniform', factor: 1 } as SizeThemeProps, - visible: true, - alpha: 1, - depthMask: true, - useFog: true, - quality: 'auto' as VisualQuality, - unitKinds: [] as Unit.Kind[], - radialSegments: 16 - } - - componentWillMount() { - this.setState({ ...this.state, ...this.props.entity.value.props }) - } - - update(state?: Partial<BackboneState>) { - const { transform, entity, ctx } = this.props - const newState = { ...this.state, ...state } - this.setState(newState) - transform.apply(ctx, entity, newState) - } - - render() { - const { transform } = this.props - - const qualityOptions = ['auto', 'custom', 'highest', 'high', 'medium', 'low', 'lowest'].map((name, idx) => { - return <option key={name} value={name}>{name}</option> - }) - - const sphereDetailOptions = [0, 1, 2, 3].map((value, idx) => { - return <option key={value} value={value}>{value.toString()}</option> - }) - - const colorThemeOptions = ColorThemeNames.map((name, idx) => { - return <option key={name} value={name}>{name}</option> - }) - - const colorValueOptions = Object.keys(ColorNames).map((name, idx) => { - return <option key={name} value={(ColorNames as any)[name]}>{name}</option> - }) - - return <div className='molstar-transformer-wrapper'> - <div className='molstar-panel molstar-control molstar-transformer molstar-panel-expanded'> - <div className='molstar-panel-header'> - <button - className='molstar-btn molstar-btn-link molstar-panel-expander' - onClick={() => this.update()} - > - <span>[{transform.kind}] {transform.inputKind} -> {transform.outputKind}</span> - </button> - </div> - <div className='molstar-panel-body'> - <div> - <div className='molstar-control-row molstar-options-group'> - <span>Quality</span> - <div> - <select - className='molstar-form-control' - value={this.state.quality} - onChange={(e) => this.update({ quality: e.target.value as VisualQuality })} - > - {qualityOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Sphere detail</span> - <div> - <select - className='molstar-form-control' - value={this.state.detail} - onChange={(e) => this.update({ detail: parseInt(e.target.value) })} - > - {sphereDetailOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Color theme</span> - <div> - <select - className='molstar-form-control' - value={this.state.colorTheme.name} - onChange={(e) => { - this.update({ - colorTheme: { - name: e.target.value as ColorThemeName, - value: this.state.colorValue - } - }) - }} - > - {colorThemeOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Color value</span> - <div> - <select - className='molstar-form-control' - value={this.state.colorValue} - onChange={(e) => { - const colorValue = Color(parseInt(e.target.value)) - this.update({ - colorTheme: { - name: 'uniform', - value: colorValue - }, - colorValue - }) - }} - > - {colorValueOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.visible} - label='Visibility' - onChange={value => this.update({ visible: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.depthMask} - label='Depth write' - onChange={value => this.update({ depthMask: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.doubleSided} - label='Double sided' - onChange={value => this.update({ doubleSided: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.flipSided} - label='Flip sided' - onChange={value => this.update({ flipSided: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.flatShaded} - label='Flat shaded' - onChange={value => this.update({ flatShaded: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Slider - value={this.state.alpha} - label='Opacity' - min={0} - max={1} - step={0.01} - callOnChangeWhileSliding={true} - onChange={value => this.update({ alpha: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Slider - value={this.state.sizeTheme.factor || 1} - label='Size factor' - min={0.1} - max={3} - step={0.01} - callOnChangeWhileSliding={true} - onChange={value => this.update({ - sizeTheme: { ...this.state.sizeTheme, factor: value } - })} - /> - </div> - </div> - </div> - </div> - </div> - </div>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/transform/ball-and-stick.tsx b/src/mol-app/ui/transform/ball-and-stick.tsx deleted file mode 100644 index 38320c2b0..000000000 --- a/src/mol-app/ui/transform/ball-and-stick.tsx +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' - -import { View } from '../view'; -import { Controller } from '../../controller/controller'; -import { Toggle } from '../controls/common'; -import { DistanceRestraintEntity } from 'mol-view/state/entity'; -import { DistanceRestraintUpdate } from 'mol-view/state/transform' -import { StateContext } from 'mol-view/state/context'; -import { ColorThemeProps, ColorThemeNames, ColorThemeName } from 'mol-view/theme/color'; -import { SizeThemeProps } from 'mol-view/theme/size'; -import { Color, ColorNames } from 'mol-util/color'; -import { Slider } from '../controls/slider'; -import { Unit } from 'mol-model/structure'; -import { VisualQuality } from 'mol-geo/geometry/geometry'; - -interface BallAndStickState { - doubleSided: boolean - flipSided: boolean - flatShaded: boolean - colorTheme: ColorThemeProps - colorValue: Color - sizeTheme: SizeThemeProps - visible: boolean - alpha: number - depthMask: boolean - useFog: boolean - quality: VisualQuality - linkScale: number - linkSpacing: number - linkRadius: number - radialSegments: number - detail: number - unitKinds: Unit.Kind[] -} - -export class BallAndStick extends View<Controller<any>, BallAndStickState, { transform: DistanceRestraintUpdate, entity: DistanceRestraintEntity, ctx: StateContext }> { - state = { - doubleSided: true, - flipSided: false, - flatShaded: false, - colorTheme: { name: 'element-symbol' } as ColorThemeProps, - colorValue: Color(0x000000), - sizeTheme: { name: 'uniform', value: 0.15 } as SizeThemeProps, - visible: true, - alpha: 1, - depthMask: true, - useFog: true, - quality: 'auto' as VisualQuality, - linkScale: 0.4, - linkSpacing: 1, - linkRadius: 0.25, - radialSegments: 16, - detail: 1, - unitKinds: [] as Unit.Kind[] - } - - componentWillMount() { - this.setState({ ...this.state, ...this.props.entity.value.props }) - } - - update(state?: Partial<BallAndStickState>) { - const { transform, entity, ctx } = this.props - const newState = { ...this.state, ...state } - this.setState(newState) - transform.apply(ctx, entity, newState) - } - - render() { - const { transform } = this.props - - const qualityOptions = ['auto', 'custom', 'highest', 'high', 'medium', 'low', 'lowest'].map((name, idx) => { - return <option key={name} value={name}>{name}</option> - }) - - const colorThemeOptions = ColorThemeNames.map((name, idx) => { - return <option key={name} value={name}>{name}</option> - }) - - const colorValueOptions = Object.keys(ColorNames).map((name, idx) => { - return <option key={name} value={(ColorNames as any)[name]}>{name}</option> - }) - - return <div className='molstar-transformer-wrapper'> - <div className='molstar-panel molstar-control molstar-transformer molstar-panel-expanded'> - <div className='molstar-panel-header'> - <button - className='molstar-btn molstar-btn-link molstar-panel-expander' - onClick={() => this.update()} - > - <span>[{transform.kind}] {transform.inputKind} -> {transform.outputKind}</span> - </button> - </div> - <div className='molstar-panel-body'> - <div> - <div className='molstar-control-row molstar-options-group'> - <span>Quality</span> - <div> - <select - className='molstar-form-control' - value={this.state.quality} - onChange={(e) => this.update({ quality: e.target.value as VisualQuality })} - > - {qualityOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Color theme</span> - <div> - <select - className='molstar-form-control' - value={this.state.colorTheme.name} - onChange={(e) => { - this.update({ - colorTheme: { - name: e.target.value as ColorThemeName, - value: this.state.colorValue - } - }) - }} - > - {colorThemeOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Color value</span> - <div> - <select - className='molstar-form-control' - value={this.state.colorValue} - onChange={(e) => { - const colorValue = Color(parseInt(e.target.value)) - this.update({ - colorTheme: { - name: 'uniform', - value: colorValue - }, - colorValue - }) - }} - > - {colorValueOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.visible} - label='Visibility' - onChange={value => this.update({ visible: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.depthMask} - label='Depth write' - onChange={value => this.update({ depthMask: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.doubleSided} - label='Double sided' - onChange={value => this.update({ doubleSided: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.flipSided} - label='Flip sided' - onChange={value => this.update({ flipSided: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.flatShaded} - label='Flat shaded' - onChange={value => this.update({ flatShaded: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Slider - value={this.state.alpha} - label='Opacity' - min={0} - max={1} - step={0.01} - callOnChangeWhileSliding={true} - onChange={value => this.update({ alpha: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Slider - value={this.state.sizeTheme.factor || 1} - label='Size factor' - min={0.1} - max={3} - step={0.01} - callOnChangeWhileSliding={true} - onChange={value => this.update({ - sizeTheme: { ...this.state.sizeTheme, factor: value } - })} - /> - </div> - </div> - </div> - </div> - </div> - </div>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/transform/carbohydrate.tsx b/src/mol-app/ui/transform/carbohydrate.tsx deleted file mode 100644 index e72513147..000000000 --- a/src/mol-app/ui/transform/carbohydrate.tsx +++ /dev/null @@ -1,250 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' - -import { View } from '../view'; -import { Controller } from '../../controller/controller'; -import { Toggle } from '../controls/common'; -import { CarbohydrateEntity } from 'mol-view/state/entity'; -import { CarbohydrateUpdate } from 'mol-view/state/transform' -import { StateContext } from 'mol-view/state/context'; -import { ColorThemeProps, ColorThemeNames, ColorThemeName } from 'mol-view/theme/color'; -import { SizeThemeProps } from 'mol-view/theme/size'; -import { Color, ColorNames } from 'mol-util/color'; -import { Slider } from '../controls/slider'; -import { Unit } from 'mol-model/structure'; -import { VisualQuality } from 'mol-geo/geometry/geometry'; - -interface CarbohydrateState { - doubleSided: boolean - flipSided: boolean - flatShaded: boolean - detail: number - colorTheme: ColorThemeProps - colorValue: Color - sizeTheme: SizeThemeProps - visible: boolean - alpha: number - depthMask: boolean - useFog: boolean - quality: VisualQuality - unitKinds: Unit.Kind[] - linkScale: number - linkSpacing: number - linkRadius: number - radialSegments: number -} - -export class Carbohydrate extends View<Controller<any>, CarbohydrateState, { transform: CarbohydrateUpdate, entity: CarbohydrateEntity, ctx: StateContext }> { - state = { - doubleSided: true, - flipSided: false, - flatShaded: false, - detail: 2, - colorTheme: { name: 'element-symbol' } as ColorThemeProps, - colorValue: Color(0x000000), - sizeTheme: { name: 'uniform', factor: 1 } as SizeThemeProps, - visible: true, - alpha: 1, - depthMask: true, - useFog: true, - quality: 'auto' as VisualQuality, - unitKinds: [] as Unit.Kind[], - linkScale: 0.4, - linkSpacing: 1, - linkRadius: 0.25, - radialSegments: 16 - } - - componentWillMount() { - this.setState({ ...this.state, ...this.props.entity.value.props }) - } - - update(state?: Partial<CarbohydrateState>) { - const { transform, entity, ctx } = this.props - const newState = { ...this.state, ...state } - this.setState(newState) - transform.apply(ctx, entity, newState) - } - - render() { - const { transform } = this.props - - const qualityOptions = ['auto', 'custom', 'highest', 'high', 'medium', 'low', 'lowest'].map((name, idx) => { - return <option key={name} value={name}>{name}</option> - }) - - const sphereDetailOptions = [0, 1, 2, 3].map((value, idx) => { - return <option key={value} value={value}>{value.toString()}</option> - }) - - const colorThemeOptions = ColorThemeNames.map((name, idx) => { - return <option key={name} value={name}>{name}</option> - }) - - const colorValueOptions = Object.keys(ColorNames).map((name, idx) => { - return <option key={name} value={(ColorNames as any)[name]}>{name}</option> - }) - - return <div className='molstar-transformer-wrapper'> - <div className='molstar-panel molstar-control molstar-transformer molstar-panel-expanded'> - <div className='molstar-panel-header'> - <button - className='molstar-btn molstar-btn-link molstar-panel-expander' - onClick={() => this.update()} - > - <span>[{transform.kind}] {transform.inputKind} -> {transform.outputKind}</span> - </button> - </div> - <div className='molstar-panel-body'> - <div> - <div className='molstar-control-row molstar-options-group'> - <span>Quality</span> - <div> - <select - className='molstar-form-control' - value={this.state.quality} - onChange={(e) => this.update({ quality: e.target.value as VisualQuality })} - > - {qualityOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Sphere detail</span> - <div> - <select - className='molstar-form-control' - value={this.state.detail} - onChange={(e) => this.update({ detail: parseInt(e.target.value) })} - > - {sphereDetailOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Color theme</span> - <div> - <select - className='molstar-form-control' - value={this.state.colorTheme.name} - onChange={(e) => { - this.update({ - colorTheme: { - name: e.target.value as ColorThemeName, - value: this.state.colorValue - } - }) - }} - > - {colorThemeOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Color value</span> - <div> - <select - className='molstar-form-control' - value={this.state.colorValue} - onChange={(e) => { - const colorValue = Color(parseInt(e.target.value)) - this.update({ - colorTheme: { - name: 'uniform', - value: colorValue - }, - colorValue - }) - }} - > - {colorValueOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.visible} - label='Visibility' - onChange={value => this.update({ visible: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.depthMask} - label='Depth write' - onChange={value => this.update({ depthMask: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.doubleSided} - label='Double sided' - onChange={value => this.update({ doubleSided: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.flipSided} - label='Flip sided' - onChange={value => this.update({ flipSided: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.flatShaded} - label='Flat shaded' - onChange={value => this.update({ flatShaded: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Slider - value={this.state.alpha} - label='Opacity' - min={0} - max={1} - step={0.01} - callOnChangeWhileSliding={true} - onChange={value => this.update({ alpha: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Slider - value={this.state.sizeTheme.factor || 1} - label='Size factor' - min={0.1} - max={3} - step={0.01} - callOnChangeWhileSliding={true} - onChange={value => this.update({ - sizeTheme: { ...this.state.sizeTheme, factor: value } - })} - /> - </div> - </div> - </div> - </div> - </div> - </div>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/transform/cartoon.tsx b/src/mol-app/ui/transform/cartoon.tsx deleted file mode 100644 index 994934214..000000000 --- a/src/mol-app/ui/transform/cartoon.tsx +++ /dev/null @@ -1,278 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' - -import { View } from '../view'; -import { Controller } from '../../controller/controller'; -import { Toggle } from '../controls/common'; -import { CartoonEntity } from 'mol-view/state/entity'; -import { CartoonUpdate } from 'mol-view/state/transform' -import { StateContext } from 'mol-view/state/context'; -import { ColorThemeProps, ColorThemeNames, ColorThemeName } from 'mol-view/theme/color'; -import { SizeThemeProps } from 'mol-view/theme/size'; -import { Color, ColorNames } from 'mol-util/color'; -import { Slider } from '../controls/slider'; -import { Unit } from 'mol-model/structure'; -import { VisualQuality } from 'mol-geo/geometry/geometry'; - -interface CartoonState { - doubleSided: boolean - flipSided: boolean - flatShaded: boolean - detail: number - colorTheme: ColorThemeProps - colorValue: Color - sizeTheme: SizeThemeProps - visible: boolean - alpha: number - depthMask: boolean - useFog: boolean - quality: VisualQuality - unitKinds: Unit.Kind[] - linearSegments: number - radialSegments: number - aspectRatio: number - arrowFactor: number -} - -export class Cartoon extends View<Controller<any>, CartoonState, { transform: CartoonUpdate, entity: CartoonEntity, ctx: StateContext }> { - state = { - doubleSided: true, - flipSided: false, - flatShaded: false, - detail: 2, - colorTheme: { name: 'element-symbol' } as ColorThemeProps, - colorValue: Color(0x000000), - sizeTheme: { name: 'uniform', value: 0.13, factor: 1 } as SizeThemeProps, - visible: true, - alpha: 1, - depthMask: true, - useFog: true, - quality: 'auto' as VisualQuality, - unitKinds: [] as Unit.Kind[], - linearSegments: 8, - radialSegments: 12, - aspectRatio: 8, - arrowFactor: 1.5 - } - - componentWillMount() { - this.setState({ ...this.state, ...this.props.entity.value.props }) - } - - update(state?: Partial<CartoonState>) { - const { transform, entity, ctx } = this.props - const newState = { ...this.state, ...state } - this.setState(newState) - transform.apply(ctx, entity, newState) - } - - render() { - const { transform } = this.props - - const qualityOptions = ['auto', 'custom', 'highest', 'high', 'medium', 'low', 'lowest'].map((name, idx) => { - return <option key={name} value={name}>{name}</option> - }) - - const sphereDetailOptions = [0, 1, 2, 3].map((value, idx) => { - return <option key={value} value={value}>{value.toString()}</option> - }) - - const colorThemeOptions = ColorThemeNames.map((name, idx) => { - return <option key={name} value={name}>{name}</option> - }) - - const colorValueOptions = Object.keys(ColorNames).map((name, idx) => { - return <option key={name} value={(ColorNames as any)[name]}>{name}</option> - }) - - return <div className='molstar-transformer-wrapper'> - <div className='molstar-panel molstar-control molstar-transformer molstar-panel-expanded'> - <div className='molstar-panel-header'> - <button - className='molstar-btn molstar-btn-link molstar-panel-expander' - onClick={() => this.update()} - > - <span>[{transform.kind}] {transform.inputKind} -> {transform.outputKind}</span> - </button> - </div> - <div className='molstar-panel-body'> - <div> - <div className='molstar-control-row molstar-options-group'> - <span>Quality</span> - <div> - <select - className='molstar-form-control' - value={this.state.quality} - onChange={(e) => this.update({ quality: e.target.value as VisualQuality })} - > - {qualityOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Sphere detail</span> - <div> - <select - className='molstar-form-control' - value={this.state.detail} - onChange={(e) => this.update({ detail: parseInt(e.target.value) })} - > - {sphereDetailOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Color theme</span> - <div> - <select - className='molstar-form-control' - value={this.state.colorTheme.name} - onChange={(e) => { - this.update({ - colorTheme: { - name: e.target.value as ColorThemeName, - value: this.state.colorValue - } - }) - }} - > - {colorThemeOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Color value</span> - <div> - <select - className='molstar-form-control' - value={this.state.colorValue} - onChange={(e) => { - const colorValue = Color(parseInt(e.target.value)) - this.update({ - colorTheme: { - name: 'uniform', - value: colorValue - }, - colorValue - }) - }} - > - {colorValueOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.visible} - label='Visibility' - onChange={value => this.update({ visible: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.depthMask} - label='Depth write' - onChange={value => this.update({ depthMask: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.doubleSided} - label='Double sided' - onChange={value => this.update({ doubleSided: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.flipSided} - label='Flip sided' - onChange={value => this.update({ flipSided: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.flatShaded} - label='Flat shaded' - onChange={value => this.update({ flatShaded: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Slider - value={this.state.alpha} - label='Opacity' - min={0} - max={1} - step={0.01} - callOnChangeWhileSliding={true} - onChange={value => this.update({ alpha: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Slider - value={this.state.aspectRatio || 1} - label='Aspect ratio' - min={0.1} - max={10} - step={0.1} - callOnChangeWhileSliding={true} - onChange={value => this.update({ aspectRatio: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Slider - value={this.state.sizeTheme.value || 0.1} - label='Size value' - min={0.01} - max={0.3} - step={0.01} - callOnChangeWhileSliding={true} - onChange={value => this.update({ - sizeTheme: { ...this.state.sizeTheme, value: value } - })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Slider - value={this.state.sizeTheme.factor || 1} - label='Size factor' - min={0.1} - max={3} - step={0.01} - callOnChangeWhileSliding={true} - onChange={value => this.update({ - sizeTheme: { ...this.state.sizeTheme, factor: value } - })} - /> - </div> - </div> - </div> - </div> - </div> - </div>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/transform/distance-restraint.tsx b/src/mol-app/ui/transform/distance-restraint.tsx deleted file mode 100644 index 037532629..000000000 --- a/src/mol-app/ui/transform/distance-restraint.tsx +++ /dev/null @@ -1,219 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' - -import { View } from '../view'; -import { Controller } from '../../controller/controller'; -import { Toggle } from '../controls/common'; -import { DistanceRestraintEntity } from 'mol-view/state/entity'; -import { DistanceRestraintUpdate } from 'mol-view/state/transform' -import { StateContext } from 'mol-view/state/context'; -import { ColorThemeProps, ColorThemeNames, ColorThemeName } from 'mol-view/theme/color'; -import { SizeThemeProps } from 'mol-view/theme/size'; -import { Color, ColorNames } from 'mol-util/color'; -import { Slider } from '../controls/slider'; -import { Unit } from 'mol-model/structure'; -import { VisualQuality } from 'mol-geo/geometry/geometry'; - -interface DistanceRestraintState { - doubleSided: boolean - flipSided: boolean - flatShaded: boolean - colorTheme: ColorThemeProps - colorValue: Color - sizeTheme: SizeThemeProps - visible: boolean - alpha: number - depthMask: boolean - useFog: boolean - quality: VisualQuality - linkScale: number - linkSpacing: number - linkRadius: number - radialSegments: number - detail: number - unitKinds: Unit.Kind[] -} - -export class DistanceRestraint extends View<Controller<any>, DistanceRestraintState, { transform: DistanceRestraintUpdate, entity: DistanceRestraintEntity, ctx: StateContext }> { - state = { - doubleSided: true, - flipSided: false, - flatShaded: false, - colorTheme: { name: 'element-symbol' } as ColorThemeProps, - colorValue: Color(0x000000), - sizeTheme: { name: 'uniform' } as SizeThemeProps, - visible: true, - alpha: 1, - depthMask: true, - useFog: true, - quality: 'auto' as VisualQuality, - linkScale: 0.4, - linkSpacing: 1, - linkRadius: 0.25, - radialSegments: 16, - detail: 1, - unitKinds: [] as Unit.Kind[] - } - - componentWillMount() { - this.setState({ ...this.state, ...this.props.entity.value.props }) - } - - update(state?: Partial<DistanceRestraintState>) { - const { transform, entity, ctx } = this.props - const newState = { ...this.state, ...state } - this.setState(newState) - transform.apply(ctx, entity, newState) - } - - render() { - const { transform } = this.props - - const qualityOptions = ['auto', 'custom', 'highest', 'high', 'medium', 'low', 'lowest'].map((name, idx) => { - return <option key={name} value={name}>{name}</option> - }) - - const colorThemeOptions = ColorThemeNames.map((name, idx) => { - return <option key={name} value={name}>{name}</option> - }) - - const colorValueOptions = Object.keys(ColorNames).map((name, idx) => { - return <option key={name} value={(ColorNames as any)[name]}>{name}</option> - }) - - return <div className='molstar-transformer-wrapper'> - <div className='molstar-panel molstar-control molstar-transformer molstar-panel-expanded'> - <div className='molstar-panel-header'> - <button - className='molstar-btn molstar-btn-link molstar-panel-expander' - onClick={() => this.update()} - > - <span>[{transform.kind}] {transform.inputKind} -> {transform.outputKind}</span> - </button> - </div> - <div className='molstar-panel-body'> - <div> - <div className='molstar-control-row molstar-options-group'> - <span>Quality</span> - <div> - <select - className='molstar-form-control' - value={this.state.quality} - onChange={(e) => this.update({ quality: e.target.value as VisualQuality })} - > - {qualityOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Color theme</span> - <div> - <select - className='molstar-form-control' - value={this.state.colorTheme.name} - onChange={(e) => { - this.update({ - colorTheme: { - name: e.target.value as ColorThemeName, - value: this.state.colorValue - } - }) - }} - > - {colorThemeOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Color value</span> - <div> - <select - className='molstar-form-control' - value={this.state.colorValue} - onChange={(e) => { - const colorValue = Color(parseInt(e.target.value)) - this.update({ - colorTheme: { - name: 'uniform', - value: colorValue - }, - colorValue - }) - }} - > - {colorValueOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.visible} - label='Visibility' - onChange={value => this.update({ visible: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.depthMask} - label='Depth write' - onChange={value => this.update({ depthMask: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.doubleSided} - label='Double sided' - onChange={value => this.update({ doubleSided: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.flipSided} - label='Flip sided' - onChange={value => this.update({ flipSided: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.flatShaded} - label='Flat shaded' - onChange={value => this.update({ flatShaded: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Slider - value={this.state.alpha} - label='Opacity' - min={0} - max={1} - step={0.01} - callOnChangeWhileSliding={true} - onChange={value => this.update({ alpha: value })} - /> - </div> - </div> - </div> - </div> - </div> - </div>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/transform/file-loader.tsx b/src/mol-app/ui/transform/file-loader.tsx deleted file mode 100644 index 32712dfa0..000000000 --- a/src/mol-app/ui/transform/file-loader.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' -import { View } from '../view'; -import { FileInput } from '../controls/common'; -import { TransformListController } from '../../controller/transform/list'; -import { FileEntity } from 'mol-view/state/entity'; -import { MmcifFileToModel, ModelToStructure, StructureToBallAndStick, StructureToSpacefill, StructureToDistanceRestraint, StructureToBackbone } from 'mol-view/state/transform'; -import { StateContext } from 'mol-view/state/context'; -import { SpacefillProps } from 'mol-geo/representation/structure/representation/spacefill'; -import { BallAndStickProps } from 'mol-geo/representation/structure/representation/ball-and-stick'; -import { DistanceRestraintProps } from 'mol-geo/representation/structure/representation/distance-restraint'; -import { BackboneProps } from 'mol-geo/representation/structure/representation/backbone'; - -const spacefillProps: Partial<SpacefillProps> = { - doubleSided: true, - colorTheme: { name: 'chain-id' }, - quality: 'auto', - useFog: false -} - -const ballAndStickProps: Partial<BallAndStickProps> = { - doubleSided: true, - colorTheme: { name: 'chain-id' }, - sizeTheme: { name: 'uniform', value: 0.05 }, - linkRadius: 0.05, - quality: 'auto', - useFog: false -} - -const distanceRestraintProps: Partial<DistanceRestraintProps> = { - doubleSided: true, - colorTheme: { name: 'chain-id' }, - linkRadius: 0.5, - quality: 'auto', - useFog: false -} - -const backboneProps: Partial<BackboneProps> = { - doubleSided: true, - colorTheme: { name: 'chain-id' }, - quality: 'auto', - useFog: false -} - -export class FileLoader extends View<TransformListController, {}, { ctx: StateContext }> { - render() { - return <div className='molstar-file-loader'> - <FileInput - accept='*.cif' - onChange={async files => { - if (files) { - const ctx = this.props.ctx - const fileEntity = FileEntity.ofFile(ctx, files[0]) - const modelEntity = await MmcifFileToModel.apply(ctx, fileEntity) - const structureEntity = await ModelToStructure.apply(ctx, modelEntity) - - StructureToBallAndStick.apply(ctx, structureEntity, { ...ballAndStickProps, visible: true }) - StructureToSpacefill.apply(ctx, structureEntity, { ...spacefillProps, visible: false }) - StructureToDistanceRestraint.apply(ctx, structureEntity, { ...distanceRestraintProps, visible: false }) - StructureToBackbone.apply(ctx, structureEntity, { ...backboneProps, visible: true }) - } - }} - /> - </div>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/transform/list.tsx b/src/mol-app/ui/transform/list.tsx deleted file mode 100644 index f6f72b804..000000000 --- a/src/mol-app/ui/transform/list.tsx +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' - -import { View } from '../view'; -import { Controller } from '../../controller/controller'; -import { TransformListController } from '../../controller/transform/list'; -import { AnyTransform } from 'mol-view/state/transform'; -import { Spacefill } from './spacefill'; -import { BallAndStick } from './ball-and-stick'; -import { AnyEntity } from 'mol-view/state/entity'; -import { FileLoader } from './file-loader'; -import { ModelToStructure } from './model'; -import { StructureCenter } from './structure'; -import { Cartoon } from './cartoon'; -import { DistanceRestraint } from './distance-restraint'; -import { Backbone } from './backbone'; -import { UrlLoader } from './url-loader'; -import { Carbohydrate } from './carbohydrate'; - -function getTransformComponent(controller: TransformListController, entity: AnyEntity, transform: AnyTransform) { - switch (transform.kind) { - case 'url-to-spacefill': - return <UrlLoader controller={controller} ctx={controller.context.stage.ctx}></UrlLoader> - case 'file-to-spacefill': - return <FileLoader controller={controller} ctx={controller.context.stage.ctx}></FileLoader> - case 'model-to-structure': - return <ModelToStructure controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></ModelToStructure> - case 'structure-center': - return <StructureCenter controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></StructureCenter> - case 'spacefill-update': - return <Spacefill controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></Spacefill> - case 'ballandstick-update': - return <BallAndStick controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></BallAndStick> - case 'distancerestraint-update': - return <DistanceRestraint controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></DistanceRestraint> - case 'backbone-update': - return <Backbone controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></Backbone> - case 'cartoon-update': - return <Cartoon controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></Cartoon> - case 'carbohydrate-update': - return <Carbohydrate controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></Carbohydrate> - } - return <Transform controller={controller} entity={entity} transform={transform}></Transform> -} - -export class Transform extends View<Controller<any>, {}, { transform: AnyTransform, entity: AnyEntity }> { - render() { - const { transform, entity } = this.props - - return <div className='molstar-transformer-wrapper'> - <div className='molstar-panel molstar-control molstar-transformer'> - <div className='molstar-panel-header'> - <button - className='molstar-btn molstar-btn-link molstar-panel-expander' - onClick={(e)=> { - console.log(transform, entity) - }} - > - <span>[{transform.kind}] {transform.inputKind} -> {transform.outputKind}</span> - </button> - </div> - </div> - </div>; - } -} - -export class TransformList extends View<TransformListController, {}, {}> { - render() { - const transforms: JSX.Element[] = [] - const state = this.controller.state.getValue() - if (state && state.entity) { - const entity = state.entity - if (entity) { - state.transforms.forEach(t => { - transforms.push( - <div - key={`${t.inputKind}|${t.outputKind}`} - children={getTransformComponent(this.controller, entity, t)} - /> - ) - }) - } - } - - return <div className='molstar-transform-view' children={transforms} />; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/transform/model.tsx b/src/mol-app/ui/transform/model.tsx deleted file mode 100644 index ebd4d2edb..000000000 --- a/src/mol-app/ui/transform/model.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' - -import { View } from '../view'; -import { Controller } from '../../controller/controller'; -import { ModelEntity } from 'mol-view/state/entity'; -import { StructureProps, ModelToStructure as ModelToStructureTransform } from 'mol-view/state/transform' -import { StateContext } from 'mol-view/state/context'; - -export class ModelToStructure extends View<Controller<any>, StructureProps, { transform: ModelToStructureTransform, entity: ModelEntity, ctx: StateContext }> { - state = { - assembly: '' - } - - create(state?: Partial<StructureProps>) { - const { transform, entity, ctx } = this.props - console.log('create structure', transform, entity) - const newState = { ...this.state, ...state } - this.setState(newState) - transform.apply(ctx, entity, newState) - } - - render() { - const { transform, entity } = this.props - - const assemblyOptions = entity.value[0].symmetry.assemblies.map((value, idx) => { - return <option key={value.id} value={value.id}>{value.details}</option> - }) - - return <div className='molstar-transformer-wrapper'> - <div className='molstar-panel molstar-control molstar-transformer molstar-panel-expanded'> - <div className='molstar-panel-header'> - <button - className='molstar-btn molstar-btn-link molstar-panel-expander' - onClick={() => this.create()} - > - <span>[{transform.kind}] {transform.inputKind} -> {transform.outputKind}</span> - </button> - </div> - <div className='molstar-panel-body'> - <div> - <div className='molstar-control-row molstar-options-group'> - <span>Details</span> - <div> - <select - className='molstar-form-control' - value={this.state.assembly} - onChange={(e) => this.create({ assembly: e.target.value })} - > - {assemblyOptions} - </select> - </div> - </div> - </div> - </div> - </div> - </div>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/transform/spacefill.tsx b/src/mol-app/ui/transform/spacefill.tsx deleted file mode 100644 index d9694e10e..000000000 --- a/src/mol-app/ui/transform/spacefill.tsx +++ /dev/null @@ -1,242 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' - -import { View } from '../view'; -import { Controller } from '../../controller/controller'; -import { Toggle } from '../controls/common'; -import { SpacefillEntity } from 'mol-view/state/entity'; -import { SpacefillUpdate } from 'mol-view/state/transform' -import { StateContext } from 'mol-view/state/context'; -import { ColorThemeProps, ColorThemeNames, ColorThemeName } from 'mol-view/theme/color'; -import { SizeThemeProps } from 'mol-view/theme/size'; -import { Color, ColorNames } from 'mol-util/color'; -import { Slider } from '../controls/slider'; -import { Unit } from 'mol-model/structure'; -import { VisualQuality } from 'mol-geo/geometry/geometry'; - -interface SpacefillState { - doubleSided: boolean - flipSided: boolean - flatShaded: boolean - detail: number - colorTheme: ColorThemeProps - colorValue: Color - sizeTheme: SizeThemeProps - visible: boolean - alpha: number - depthMask: boolean - useFog: boolean - quality: VisualQuality - unitKinds: Unit.Kind[] -} - -export class Spacefill extends View<Controller<any>, SpacefillState, { transform: SpacefillUpdate, entity: SpacefillEntity, ctx: StateContext }> { - state = { - doubleSided: true, - flipSided: false, - flatShaded: false, - detail: 2, - colorTheme: { name: 'element-symbol' } as ColorThemeProps, - colorValue: Color(0x000000), - sizeTheme: { name: 'uniform', factor: 1 } as SizeThemeProps, - visible: true, - alpha: 1, - depthMask: true, - useFog: true, - quality: 'auto' as VisualQuality, - unitKinds: [] as Unit.Kind[] - } - - componentWillMount() { - this.setState({ ...this.state, ...this.props.entity.value.props }) - } - - update(state?: Partial<SpacefillState>) { - const { transform, entity, ctx } = this.props - const newState = { ...this.state, ...state } - this.setState(newState) - transform.apply(ctx, entity, newState) - } - - render() { - const { transform } = this.props - - const qualityOptions = ['auto', 'custom', 'highest', 'high', 'medium', 'low', 'lowest'].map((name, idx) => { - return <option key={name} value={name}>{name}</option> - }) - - const sphereDetailOptions = [0, 1, 2, 3].map((value, idx) => { - return <option key={value} value={value}>{value.toString()}</option> - }) - - const colorThemeOptions = ColorThemeNames.map((name, idx) => { - return <option key={name} value={name}>{name}</option> - }) - - const colorValueOptions = Object.keys(ColorNames).map((name, idx) => { - return <option key={name} value={(ColorNames as any)[name]}>{name}</option> - }) - - return <div className='molstar-transformer-wrapper'> - <div className='molstar-panel molstar-control molstar-transformer molstar-panel-expanded'> - <div className='molstar-panel-header'> - <button - className='molstar-btn molstar-btn-link molstar-panel-expander' - onClick={() => this.update()} - > - <span>[{transform.kind}] {transform.inputKind} -> {transform.outputKind}</span> - </button> - </div> - <div className='molstar-panel-body'> - <div> - <div className='molstar-control-row molstar-options-group'> - <span>Quality</span> - <div> - <select - className='molstar-form-control' - value={this.state.quality} - onChange={(e) => this.update({ quality: e.target.value as VisualQuality })} - > - {qualityOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Sphere detail</span> - <div> - <select - className='molstar-form-control' - value={this.state.detail} - onChange={(e) => this.update({ detail: parseInt(e.target.value) })} - > - {sphereDetailOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Color theme</span> - <div> - <select - className='molstar-form-control' - value={this.state.colorTheme.name} - onChange={(e) => { - this.update({ - colorTheme: { - name: e.target.value as ColorThemeName, - value: this.state.colorValue - } - }) - }} - > - {colorThemeOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <span>Color value</span> - <div> - <select - className='molstar-form-control' - value={this.state.colorValue} - onChange={(e) => { - const colorValue = Color(parseInt(e.target.value)) - this.update({ - colorTheme: { - name: 'uniform', - value: colorValue - }, - colorValue - }) - }} - > - {colorValueOptions} - </select> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.visible} - label='Visibility' - onChange={value => this.update({ visible: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.depthMask} - label='Depth write' - onChange={value => this.update({ depthMask: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.doubleSided} - label='Double sided' - onChange={value => this.update({ doubleSided: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.flipSided} - label='Flip sided' - onChange={value => this.update({ flipSided: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Toggle - value={this.state.flatShaded} - label='Flat shaded' - onChange={value => this.update({ flatShaded: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Slider - value={this.state.alpha} - label='Opacity' - min={0} - max={1} - step={0.01} - callOnChangeWhileSliding={true} - onChange={value => this.update({ alpha: value })} - /> - </div> - </div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Slider - value={this.state.sizeTheme.factor || 1} - label='Size factor' - min={0.1} - max={3} - step={0.01} - callOnChangeWhileSliding={true} - onChange={value => this.update({ - sizeTheme: { ...this.state.sizeTheme, factor: value } - })} - /> - </div> - </div> - </div> - </div> - </div> - </div>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/transform/structure.tsx b/src/mol-app/ui/transform/structure.tsx deleted file mode 100644 index 59871cd21..000000000 --- a/src/mol-app/ui/transform/structure.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' - -import { View } from '../view'; -import { Controller } from '../../controller/controller'; -import { Button } from '../controls/common'; -import { StructureEntity } from 'mol-view/state/entity'; -import { StructureCenter as StructureCenterTransform } from 'mol-view/state/transform' -import { StateContext } from 'mol-view/state/context'; - -export const ColorThemeInfo = { - 'atom-index': {}, - 'chain-id': {}, - 'element-symbol': {}, - 'instance-index': {}, - 'uniform': {} -} -export type ColorThemeInfo = keyof typeof ColorThemeInfo - -export class StructureCenter extends View<Controller<any>, {}, { transform: StructureCenterTransform, entity: StructureEntity, ctx: StateContext }> { - center() { - const { ctx, entity, transform } = this.props - transform.apply(ctx, entity) - } - - render() { - const { transform } = this.props - - return <div className='molstar-transformer-wrapper'> - <div className='molstar-panel molstar-control molstar-transformer molstar-panel-expanded'> - <div className='molstar-panel-header'> - <button - className='molstar-btn molstar-btn-link molstar-panel-expander' - onClick={() => {}} - > - <span>[{transform.kind}] {transform.inputKind} -> {transform.outputKind}</span> - </button> - </div> - <div className='molstar-panel-body'> - <div> - <div className='molstar-control-row molstar-options-group'> - <div> - <Button onClick={value => this.center()}> - Center - </Button> - </div> - </div> - </div> - </div> - </div> - </div>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/transform/url-loader.tsx b/src/mol-app/ui/transform/url-loader.tsx deleted file mode 100644 index 58667e93d..000000000 --- a/src/mol-app/ui/transform/url-loader.tsx +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' -import { View } from '../view'; -import { TransformListController } from '../../controller/transform/list'; -import { UrlEntity } from 'mol-view/state/entity'; -import { ModelToStructure, StructureToBallAndStick, StructureToSpacefill, StructureToDistanceRestraint, StructureToBackbone, MmcifUrlToModel, StructureToCartoon } from 'mol-view/state/transform'; -import { StateContext } from 'mol-view/state/context'; -import { SpacefillProps } from 'mol-geo/representation/structure/representation/spacefill'; -import { BallAndStickProps } from 'mol-geo/representation/structure/representation/ball-and-stick'; -import { DistanceRestraintProps } from 'mol-geo/representation/structure/representation/distance-restraint'; -import { BackboneProps } from 'mol-geo/representation/structure/representation/backbone'; -import { CartoonProps } from 'mol-geo/representation/structure/representation/cartoon'; - -const spacefillProps: Partial<SpacefillProps> = { - doubleSided: true, - colorTheme: { name: 'chain-id' }, - quality: 'auto', - useFog: false -} - -const ballAndStickProps: Partial<BallAndStickProps> = { - doubleSided: true, - colorTheme: { name: 'chain-id' }, - sizeTheme: { name: 'uniform', value: 0.05 }, - linkRadius: 0.05, - quality: 'auto', - useFog: false -} - -const distanceRestraintProps: Partial<DistanceRestraintProps> = { - doubleSided: true, - colorTheme: { name: 'chain-id' }, - linkRadius: 0.5, - quality: 'auto', - useFog: false -} - -const backboneProps: Partial<BackboneProps> = { - doubleSided: true, - colorTheme: { name: 'chain-id' }, - quality: 'auto', - useFog: false -} - -const cartoonProps: Partial<CartoonProps> = { - doubleSided: true, - colorTheme: { name: 'chain-id' }, - quality: 'auto', - useFog: false -} - -function getPdbdevUrl(pdbdevId: string) { - return `https://pdb-dev.wwpdb.org/static/cif/${pdbdevId}.cif` -} - -const exampleUrls = { - PDBDEV_00000001: getPdbdevUrl('PDBDEV_00000001'), // ok - PDBDEV_00000002: getPdbdevUrl('PDBDEV_00000002'), // ok - PDBDEV_00000003: getPdbdevUrl('PDBDEV_00000003'), // ok - PDBDEV_00000004: getPdbdevUrl('PDBDEV_00000004'), // TODO issue with cross-link extraction - PDBDEV_00000005: getPdbdevUrl('PDBDEV_00000005'), // ok - PDBDEV_00000006: getPdbdevUrl('PDBDEV_00000006'), // TODO only three spacefill atoms rendered - PDBDEV_00000007: getPdbdevUrl('PDBDEV_00000007'), // TODO only three spacefill atoms rendered - PDBDEV_00000008: getPdbdevUrl('PDBDEV_00000008'), // ok - PDBDEV_00000010: getPdbdevUrl('PDBDEV_00000010'), // ok - PDBDEV_00000011: getPdbdevUrl('PDBDEV_00000011'), // ok - PDBDEV_00000012: getPdbdevUrl('PDBDEV_00000012'), // ok - PDBDEV_00000014: getPdbdevUrl('PDBDEV_00000014'), // ok - PDBDEV_00000016: getPdbdevUrl('PDBDEV_00000016'), -} - -export class UrlLoader extends View<TransformListController, { }, { ctx: StateContext }> { - async load(name: keyof typeof exampleUrls) { - console.log(exampleUrls[name]) - const ctx = this.props.ctx - const urlEntity = UrlEntity.ofUrl(ctx, exampleUrls[name]) - console.log(await urlEntity.value.getData()) - const modelEntity = await MmcifUrlToModel.apply(ctx, urlEntity) - const structureEntity = await ModelToStructure.apply(ctx, modelEntity) - - StructureToBallAndStick.apply(ctx, structureEntity, { ...ballAndStickProps, visible: true }) - StructureToSpacefill.apply(ctx, structureEntity, { ...spacefillProps, visible: false }) - StructureToDistanceRestraint.apply(ctx, structureEntity, { ...distanceRestraintProps, visible: false }) - StructureToBackbone.apply(ctx, structureEntity, { ...backboneProps, visible: true }) - StructureToCartoon.apply(ctx, structureEntity, { ...cartoonProps, visible: false }) - } - - render() { - const exampleOptions = Object.keys(exampleUrls).map(name => { - return <option key={name} value={name}>{name}</option> - }) - - return <div className='molstar-transformer-wrapper'> - <div className='molstar-panel molstar-control molstar-transformer molstar-panel-expanded'> - <div className='molstar-panel-body'> - <div> - <div className='molstar-control-row molstar-options-group'> - <span>Examples</span> - <div> - <select - className='molstar-form-control' - value='' - onChange={(e) => { - if (e.target.value) { - this.load(e.target.value as keyof typeof exampleUrls)} - } - } - > - <option key='' value=''></option> - {exampleOptions} - </select> - </div> - </div> - </div> - </div> - </div> - </div>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/view.tsx b/src/mol-app/ui/view.tsx deleted file mode 100644 index d7374759f..000000000 --- a/src/mol-app/ui/view.tsx +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - */ - -import * as React from 'react' -import { Observable, Subscription } from 'rxjs'; -import { merge, shallowEqual } from 'mol-util' -import { Context } from '../context/context'; -import { Controller } from '../controller/controller'; - -export abstract class PureView<State, Props, ViewState> extends React.Component<{ - state: State - onChange: (s: State) => void -} & Props, ViewState> { - - protected update(s: State) { - let ns = merge<State>(this.props.state, s); - if (ns !== this.props.state as any) this.props.onChange(ns); - } - - shouldComponentUpdate(nextProps: any, nextState: any) { - return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState); - } -} - -export abstract class ComponentView<Props> extends React.Component<{ context: Context } & Props, {}> { - - // shouldComponentUpdate(nextProps: any, nextState: any) { - // return !shallowEqual(this.props, nextProps); - // } - - private subs: Subscription[] = []; - protected subscribe<T>(stream: Observable<T>, obs: (n: T) => void) { - let sub = stream.subscribe(obs); - this.subs.push(sub); - return sub; - } - - protected unsubscribe(sub: Subscription) { - let idx = this.subs.indexOf(sub); - for (let i = idx; i < this.subs.length - 1; i++) { - this.subs[i] = this.subs[i + 1]; - } - sub.unsubscribe(); - this.subs.pop(); - } - - componentWillUnmount() { - for (let s of this.subs) s.unsubscribe(); - this.subs = []; - } -} - -export abstract class ObserverView<P, S> extends React.Component<P, S> { - private subs: Subscription[] = []; - - protected subscribe<T>(stream: Observable<T>, obs: (n: T) => void) { - let sub = stream.subscribe(obs); - this.subs.push(sub); - return sub; - } - - protected unsubscribe(sub: Subscription) { - let idx = this.subs.indexOf(sub); - for (let i = idx; i < this.subs.length - 1; i++) { - this.subs[i] = this.subs[i + 1]; - } - sub.unsubscribe(); - this.subs.pop(); - } - - componentWillUnmount() { - for (let s of this.subs) s.unsubscribe(); - this.subs = []; - } -} - -export abstract class View<T extends Controller<any>, State, CustomProps> - extends ObserverView<{ controller: T } & CustomProps, State> { - - public get controller(): T { - return this.props.controller as any; - } - - componentWillMount() { - this.subscribe(this.controller.state as any, (s) => { - this.forceUpdate() - }); - } -} \ No newline at end of file diff --git a/src/mol-app/ui/visualization/image-canvas.tsx b/src/mol-app/ui/visualization/image-canvas.tsx deleted file mode 100644 index f70699b2e..000000000 --- a/src/mol-app/ui/visualization/image-canvas.tsx +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' - -type State = { imageData: ImageData, width: number, height: number } - -function getExtend(aspectRatio: number, maxWidth: number, maxHeight: number) { - let width = maxWidth - let height = width / aspectRatio - if (height > maxHeight) { - height = maxHeight - width = height * aspectRatio - } - return { width, height } -} - -export class ImageCanvas extends React.Component<{ imageData: ImageData, aspectRatio: number, maxWidth: number, maxHeight: number }, State> { - private canvas: HTMLCanvasElement | null = null; - private ctx: CanvasRenderingContext2D | null = null; - - state = { - imageData: new ImageData(1, 1), - width: 1, - height: 1 - } - - updateStateFromProps() { - this.setState({ - imageData: this.props.imageData, - ...getExtend(this.props.aspectRatio, this.props.maxWidth, this.props.maxHeight) - }) - } - - updateImage() { - if (this.canvas) { - this.canvas.width = this.state.imageData.width - this.canvas.height = this.state.imageData.height - } - if (this.ctx) { - this.ctx.putImageData(this.state.imageData, 0, 0) - } - } - - componentWillMount() { - this.updateStateFromProps() - } - - componentDidMount() { - if (this.canvas && !this.ctx) { - this.ctx = this.canvas.getContext('2d') - if (this.ctx) this.ctx.imageSmoothingEnabled = false - } - this.updateImage() - } - - componentWillReceiveProps() { - this.updateStateFromProps() - } - - componentDidUpdate() { - this.updateImage() - } - - render() { - return <div - className='molstar-image-canvas' - style={{ - width: this.state.width + 6, - height: this.state.height + 6, - margin: 10, - display: 'inline-block', - border: '3px white solid', - }} - > - <canvas - ref={elm => this.canvas = elm} - style={{ - width: this.state.width, - height: this.state.height, - imageRendering: 'pixelated' - }} - /> - </div>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/visualization/sequence-view.tsx b/src/mol-app/ui/visualization/sequence-view.tsx deleted file mode 100644 index 2084dcd6c..000000000 --- a/src/mol-app/ui/visualization/sequence-view.tsx +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import * as React from 'react' -import { View } from '../view'; -import { SequenceViewController } from '../../controller/visualization/sequence-view'; -import { Structure, StructureSequence, Queries, StructureSelection, StructureProperties, StructureQuery } from 'mol-model/structure'; -import { Context } from '../../context/context'; -import { InteractivityEvents } from '../../event/basic'; -import { EmptyLoci } from 'mol-model/loci'; - -export class SequenceView extends View<SequenceViewController, {}, {}> { - render() { - const s = this.controller.latestState.structure; - if (!s) return <div className='molstar-sequence-view-wrap'>No structure available.</div>; - - const seqs = s.models[0].sequence.sequences; - return <div className='molstar-sequence-view-wrap'> - {seqs.map((seq, i) => <EntitySequence key={i} ctx={this.controller.context} seq={seq} structure={s} /> )} - </div>; - } -} - -function createQuery(entityId: string, label_seq_id: number) { - return Queries.generators.atoms({ - entityTest: ctx => StructureProperties.entity.id(ctx.element) === entityId, - residueTest: ctx => StructureProperties.residue.label_seq_id(ctx.element) === label_seq_id - }); -} - -// TODO: this is really ineffective and should be done using a canvas. -class EntitySequence extends React.Component<{ ctx: Context, seq: StructureSequence.Entity, structure: Structure }> { - - raiseInteractityEvent(seqId?: number) { - if (typeof seqId === 'undefined') { - InteractivityEvents.HighlightLoci.dispatch(this.props.ctx, EmptyLoci); - return; - } - - const query = createQuery(this.props.seq.entityId, seqId); - const loci = StructureSelection.toLoci(StructureQuery.run(query, this.props.structure)); - if (loci.elements.length === 0) InteractivityEvents.HighlightLoci.dispatch(this.props.ctx, EmptyLoci); - else InteractivityEvents.HighlightLoci.dispatch(this.props.ctx, loci); - } - - - render() { - const { ctx, seq } = this.props; - const { offset, sequence } = seq.sequence; - - const elems: JSX.Element[] = []; - for (let i = 0, _i = sequence.length; i < _i; i++) { - elems[elems.length] = <ResidueView ctx={ctx} seqId={offset + i} letter={sequence[i]} parent={this} key={i} />; - } - - return <div style={{ wordWrap: 'break-word' }}> - <span style={{ fontWeight: 'bold' }}>{this.props.seq.entityId}:{offset} </span> - {elems} - </div>; - } -} - -class ResidueView extends React.Component<{ ctx: Context, seqId: number, letter: string, parent: EntitySequence }, { isHighlighted: boolean }> { - state = { isHighlighted: false } - - mouseEnter = () => { - this.setState({ isHighlighted: true }); - this.props.parent.raiseInteractityEvent(this.props.seqId); - } - - mouseLeave = () => { - this.setState({ isHighlighted: false }); - this.props.parent.raiseInteractityEvent(); - } - - render() { - return <span onMouseEnter={this.mouseEnter} onMouseLeave={this.mouseLeave} - style={{ cursor: 'pointer', backgroundColor: this.state.isHighlighted ? 'yellow' : void 0 }}> - {this.props.letter} - </span>; - } -} \ No newline at end of file diff --git a/src/mol-app/ui/visualization/viewport.tsx b/src/mol-app/ui/visualization/viewport.tsx deleted file mode 100644 index ed6c563a4..000000000 --- a/src/mol-app/ui/visualization/viewport.tsx +++ /dev/null @@ -1,244 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * Adapted from LiteMol - * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import * as React from 'react' - -import { ViewportController } from '../../controller/visualization/viewport' -import { View } from '../view'; -import { HelpBox, Toggle, Button } from '../controls/common' -import { Slider } from '../controls/slider' -import { ImageCanvas } from './image-canvas'; -import { InteractivityEvents } from '../../event/basic'; -import { labelFirst } from 'mol-view/label'; - -export class ViewportControls extends View<ViewportController, { showSceneOptions?: boolean, showHelp?: boolean }, {}> { - state = { showSceneOptions: false, showHelp: false }; - - private help() { - return <div className='molstar-viewport-controls-scene-options molstar-control'> - <HelpBox title='Rotate' content={<div><div>Left button</div><div>One finger touch</div></div>} /> - <HelpBox title='Zoom' content={<div><div>Right button</div><div>Pinch</div></div>} /> - <HelpBox title='Move' content={<div><div>Middle button</div><div>Two finger touch</div></div>} /> - <HelpBox title='Slab' content={<div><div>Mouse wheel</div><div>Three finger touch</div></div>} /> - </div> - } - - render() { - let state = this.controller.latestState; - - let options: any; - - let layoutController = this.controller.context.layout; - let layoutState = layoutController.latestState; - if (this.state.showSceneOptions) { - options = <div className='molstar-viewport-controls-scene-options molstar-control'> - <Toggle onChange={v => this.controller.setState({ enableFog: v })} value={state.enableFog!} label='Fog' /> - <Slider label='FOV' min={30} max={90} onChange={v => this.controller.setState({ cameraFOV: v }) } value={state.cameraFOV!} /> - <Slider label='Camera Speed' min={1} max={10} step={0.01} onChange={v => this.controller.setState({ cameraSpeed: v }) } value={state.cameraSpeed!} /> - </div>; - } else if (this.state.showHelp) { - options = this.help(); - } - - let controlsShown = !layoutState.hideControls; - return <div className='molstar-viewport-controls' onMouseLeave={() => this.setState({ showSceneOptions: false, showHelp: false })}> - <div className='molstar-viewport-controls-buttons'> - <Button - style='link' - active={this.state.showHelp} - customClass={'molstar-btn-link-toggle-' + (this.state.showHelp ? 'on' : 'off')} - icon='help-circle' - onClick={(e) => this.setState({ showHelp: !this.state.showHelp, showSceneOptions: false }) } title='Controls Help' /> - <Button - style='link' - active={this.state.showSceneOptions} - customClass={'molstar-btn-link-toggle-' + (this.state.showSceneOptions ? 'on' : 'off')} - icon='settings' - onClick={(e) => this.setState({ showSceneOptions: !this.state.showSceneOptions, showHelp: false }) } title='Scene Options' /> - <Button - style='link' - icon='screenshot' - onClick={(e) => this.controller.context.stage.viewer.downloadScreenshot()} - title='Screenshot' /> - <Button onClick={() => { layoutController.update({ hideControls: controlsShown }); this.forceUpdate(); } } - icon='tools' title={controlsShown ? 'Hide Controls' : 'Show Controls'} active={controlsShown } - customClass={'molstar-btn-link-toggle-' + (controlsShown ? 'on' : 'off')} - style='link' /> - <Button onClick={() => layoutController.update({ isExpanded: !layoutState.isExpanded }) } - icon='expand-layout' title={layoutState.isExpanded ? 'Collapse' : 'Expand'} active={layoutState.isExpanded } - customClass={'molstar-btn-link-toggle-' + (layoutState.isExpanded ? 'on' : 'off')} - style='link' /> - <Button - style='link' - icon='reset-scene' - onClick={(e) => this.controller.context.stage.viewer.resetCamera()} - title='Reset camera' /> - </div> - {options} - </div>; - } -} - -export const Logo = () => - <div className='molstar-logo'> - <div> - <div> - <div /> - <div className='molstar-logo-image' /> - </div> - </div> - </div> - - -type ViewportState = { - noWebGl: boolean, - showLogo: boolean, - aspectRatio: number, - width: number - height: number - images: { [k: string]: ImageData } - info: string -} - -export class Viewport extends View<ViewportController, ViewportState, { noWebGl?: boolean, showLogo?: boolean, aspectRatio: number, info: string }> { - private container: HTMLDivElement | null = null; - private canvas: HTMLCanvasElement | null = null; - private defaultBg = { r: 1, g: 1, b: 1 } - state: ViewportState = { - noWebGl: false, - showLogo: true, - images: {}, - aspectRatio: 1, - width: 0, - height: 0, - info: '' - }; - - handleResize() { - if (this.container) { - this.setState({ - aspectRatio: this.container.clientWidth / this.container.clientHeight, - width: this.container.clientWidth, - height: this.container.clientHeight - }) - } - } - - componentDidMount() { - if (!this.canvas || !this.container || !this.controller.context.initStage(this.canvas, this.container)) { - this.setState({ noWebGl: true }); - } - this.handleResize() - - const viewer = this.controller.context.stage.viewer - - viewer.reprCount.subscribe(count => { - this.setState({ - showLogo: false - // showLogo: count === 0 - }) - }) - - viewer.didDraw.subscribe(() => { - // this.setState({ imageData: viewer.getImageData() }) - this.setState({ - images: { - 'object': viewer.getImageData('pickObject'), - 'instance': viewer.getImageData('pickInstance'), - 'group': viewer.getImageData('pickGroup') - } - }) - }) - - viewer.input.resize.subscribe(() => this.handleResize()) - - // TODO: clear highlight on mouse/touch down? - - viewer.input.move.subscribe(({x, y, inside, buttons}) => { - if (!inside || buttons) return - const p = viewer.identify(x, y) - if (p) { - const loci = viewer.getLoci(p) - InteractivityEvents.HighlightLoci.dispatch(this.controller.context, loci); - - // TODO use LabelLoci event and make configurable - const label = labelFirst(loci) - const info = `Object: ${p.objectId}, Instance: ${p.instanceId}, Group: ${p.groupId}, Label: ${label}` - this.setState({ info }) - } - }) - - // TODO filter only for left button/single finger touch? - viewer.input.click.subscribe(({x, y}) => { - const p = viewer.identify(x, y) - if (p) { - const loci = viewer.getLoci(p) - InteractivityEvents.SelectLoci.dispatch(this.controller.context, loci); - } - }) - } - - componentWillUnmount() { - super.componentWillUnmount(); - this.controller.context.destroy(); - } - - renderMissing() { - return <div className='molstar-no-webgl'> - <div> - <p><b>WebGL does not seem to be available.</b></p> - <p>This can be caused by an outdated browser, graphics card driver issue, or bad weather. Sometimes, just restarting the browser helps.</p> - <p>For a list of supported browsers, refer to <a href='http://caniuse.com/#feat=webgl' target='_blank'>http://caniuse.com/#feat=webgl</a>.</p> - </div> - </div> - } - - render() { - if (this.state.noWebGl) return this.renderMissing(); - - const color = this.controller.latestState.clearColor! || this.defaultBg; - return <div className='molstar-viewport' style={{ backgroundColor: `rgb(${255 * color.r}, ${255 * color.g}, ${255 * color.b})` }}> - <div ref={elm => this.container = elm} className='molstar-viewport-container'> - <canvas ref={elm => this.canvas = elm} className='molstar-viewport-canvas'></canvas> - </div> - {this.state.showLogo ? <Logo /> : void 0} - <ViewportControls controller={this.controller} /> - <div - style={{ - position: 'absolute', - top: 10, - left: 10, - padding: 10, - color: 'lightgrey', - background: 'rgba(0, 0, 0, 0.2)' - }} - > - {this.state.info} - </div> - <div - style={{ - position: 'absolute', - bottom: 10, - left: 10, - }} - > - {Object.keys(this.state.images).map(k => { - const imageData = this.state.images[k] - return <ImageCanvas - key={k} - imageData={imageData} - aspectRatio={this.state.aspectRatio} - maxWidth={this.state.width / 4} - maxHeight={this.state.height / 4} - /> - })} - </div> - </div>; - } -} \ No newline at end of file diff --git a/src/mol-geo/geometry/color-data.ts b/src/mol-geo/geometry/color-data.ts index ee1a32c79..7ac3f24cd 100644 --- a/src/mol-geo/geometry/color-data.ts +++ b/src/mol-geo/geometry/color-data.ts @@ -10,9 +10,10 @@ import { Color } from 'mol-util/color'; import { Vec2, Vec3 } from 'mol-math/linear-algebra'; import { LocationIterator } from '../util/location-iterator'; import { NullLocation } from 'mol-model/location'; -import { LocationColor, ColorThemeProps, ColorTheme } from 'mol-view/theme/color'; +import { LocationColor, ColorThemeProps, ColorTheme, ColorThemeName } from 'mol-view/theme/color'; import { RuntimeContext } from 'mol-task'; import { getGranularity } from './geometry'; +import { Structure } from 'mol-model/structure'; export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance' @@ -24,8 +25,27 @@ export type ColorData = { dColorType: ValueCell<string>, } -export function createColors(ctx: RuntimeContext, locationIt: LocationIterator, props: ColorThemeProps, colorData?: ColorData): Promise<ColorData> { - const colorTheme = ColorTheme(props) +export interface ColorProps { + colorTheme: ColorThemeName + colorValue?: Color + structure?: Structure +} + +export function getColorThemeProps(props: ColorProps): ColorThemeProps { + return { + name: props.colorTheme, + // domain: [number, number], + value: props.colorValue, + structure: props.structure, + // color?: LocationColor, + // granularity?: ColorType, + // description?: string, + // legend?: ScaleLegend | TableLegend + } +} + +export function createColors(ctx: RuntimeContext, locationIt: LocationIterator, props: ColorProps, colorData?: ColorData): Promise<ColorData> { + const colorTheme = ColorTheme(getColorThemeProps(props)) switch (getGranularity(locationIt, colorTheme.granularity)) { case 'uniform': return createUniformColor(ctx, locationIt, colorTheme.color, colorData) case 'group': return createGroupColor(ctx, locationIt, colorTheme.color, colorData) diff --git a/src/mol-geo/geometry/geometry.ts b/src/mol-geo/geometry/geometry.ts index e8b1bfcf6..6b5c7253c 100644 --- a/src/mol-geo/geometry/geometry.ts +++ b/src/mol-geo/geometry/geometry.ts @@ -15,7 +15,8 @@ import { LocationIterator } from '../util/location-iterator'; import { ColorType } from './color-data'; import { SizeType } from './size-data'; import { Lines } from './lines/lines'; -import { paramDefaultValues, RangeParam, CheckboxParam, SelectParam, ColorParam } from 'mol-view/parameter' +import { paramDefaultValues, RangeParam, BooleanParam, SelectParam, ColorParam, StructureParam } from 'mol-view/parameter' +import { Structure } from 'mol-model/structure'; // @@ -53,12 +54,13 @@ export namespace Geometry { export const Params = { alpha: RangeParam('Opacity', '', 1, 0, 1, 0.01), - visible: CheckboxParam('Visible', '', true), - depthMask: CheckboxParam('Depth Mask', '', true), - useFog: CheckboxParam('Use Fog', '', false), + visible: BooleanParam('Visible', '', true), + depthMask: BooleanParam('Depth Mask', '', true), + useFog: BooleanParam('Use Fog', '', false), quality: SelectParam<VisualQuality>('Quality', '', 'auto', VisualQualityOptions), colorTheme: SelectParam<ColorThemeName>('Color Theme', '', 'uniform', ColorThemeOptions), colorValue: ColorParam('Color Value', '', Color(0xCCCCCC)), + structure: StructureParam('Structure', '', Structure.Empty), } export const DefaultProps = paramDefaultValues(Params) export type Props = typeof DefaultProps diff --git a/src/mol-geo/geometry/lines/lines.ts b/src/mol-geo/geometry/lines/lines.ts index b3b630de9..9e0cba304 100644 --- a/src/mol-geo/geometry/lines/lines.ts +++ b/src/mol-geo/geometry/lines/lines.ts @@ -18,7 +18,7 @@ import { SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; import { LinesValues } from 'mol-gl/renderable/lines'; import { Mesh } from '../mesh/mesh'; import { LinesBuilder } from './lines-builder'; -import { CheckboxParam, SelectParam, NumberParam, paramDefaultValues } from 'mol-view/parameter'; +import { BooleanParam, SelectParam, NumberParam, paramDefaultValues } from 'mol-view/parameter'; /** Wide line */ export interface Lines { @@ -93,17 +93,18 @@ export namespace Lines { export const Params = { ...Geometry.Params, - lineSizeAttenuation: CheckboxParam('Line Size Attenuation', '', false), + lineSizeAttenuation: BooleanParam('Line Size Attenuation', '', false), sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), sizeValue: NumberParam('Size Value', '', 1, 0, 0.1, 20), + sizeFactor: NumberParam('Size Factor', '', 1, 0, 10, 0.1), } export const DefaultProps = paramDefaultValues(Params) export type Props = typeof DefaultProps export async function createValues(ctx: RuntimeContext, lines: Lines, transform: TransformData, locationIt: LocationIterator, props: Props): Promise<LinesValues> { const { instanceCount, groupCount } = locationIt - const color = await createColors(ctx, locationIt, { name: props.colorTheme, value: props.colorValue }) - const size = await createSizes(ctx, locationIt, { name: props.sizeTheme, value: props.sizeValue }) + const color = await createColors(ctx, locationIt, props) + const size = await createSizes(ctx, locationIt, props) const marker = createMarkers(instanceCount * groupCount) const counts = { drawCount: lines.lineCount * 2 * 3, groupCount, instanceCount } diff --git a/src/mol-geo/geometry/mesh/mesh.ts b/src/mol-geo/geometry/mesh/mesh.ts index b7a8c7c19..e87de9bf2 100644 --- a/src/mol-geo/geometry/mesh/mesh.ts +++ b/src/mol-geo/geometry/mesh/mesh.ts @@ -16,7 +16,7 @@ import { TransformData } from '../transform-data'; import { LocationIterator } from '../../util/location-iterator'; import { createColors } from '../color-data'; import { ChunkedArray } from 'mol-data/util'; -import { CheckboxParam, paramDefaultValues } from 'mol-view/parameter'; +import { BooleanParam, paramDefaultValues } from 'mol-view/parameter'; export interface Mesh { readonly kind: 'mesh', @@ -317,16 +317,16 @@ export namespace Mesh { export const Params = { ...Geometry.Params, - doubleSided: CheckboxParam('Double Sided', '', false), - flipSided: CheckboxParam('Flip Sided', '', false), - flatShaded: CheckboxParam('Flat Shaded', '', false), + doubleSided: BooleanParam('Double Sided', '', false), + flipSided: BooleanParam('Flip Sided', '', false), + flatShaded: BooleanParam('Flat Shaded', '', false), } export const DefaultProps = paramDefaultValues(Params) export type Props = typeof DefaultProps export async function createValues(ctx: RuntimeContext, mesh: Mesh, transform: TransformData, locationIt: LocationIterator, props: Props): Promise<MeshValues> { const { instanceCount, groupCount } = locationIt - const color = await createColors(ctx, locationIt, { name: props.colorTheme, value: props.colorValue }) + const color = await createColors(ctx, locationIt, props) const marker = createMarkers(instanceCount * groupCount) const counts = { drawCount: mesh.triangleCount * 3, groupCount, instanceCount } diff --git a/src/mol-geo/geometry/points/points.ts b/src/mol-geo/geometry/points/points.ts index cd805c2eb..3181353cf 100644 --- a/src/mol-geo/geometry/points/points.ts +++ b/src/mol-geo/geometry/points/points.ts @@ -15,8 +15,8 @@ import { createMarkers } from '../marker-data'; import { createSizes } from '../size-data'; import { TransformData } from '../transform-data'; import { LocationIterator } from '../../util/location-iterator'; -import { SizeThemeProps, SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; -import { CheckboxParam, NumberParam, SelectParam, paramDefaultValues } from 'mol-view/parameter'; +import { SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; +import { BooleanParam, NumberParam, SelectParam, paramDefaultValues } from 'mol-view/parameter'; /** Point cloud */ export interface Points { @@ -55,19 +55,20 @@ export namespace Points { export const Params = { ...Geometry.Params, - pointSizeAttenuation: CheckboxParam('Point Size Attenuation', '', false), - pointFilledCircle: CheckboxParam('Point Filled Circle', '', false), - pointEdgeBleach: NumberParam('Point Edge Bleach', '', 0.2, 0, 0.05, 1), + pointSizeAttenuation: BooleanParam('Point Size Attenuation', '', false), + pointFilledCircle: BooleanParam('Point Filled Circle', '', false), + pointEdgeBleach: NumberParam('Point Edge Bleach', '', 0.2, 0, 1, 0.05), sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), - sizeValue: NumberParam('Size Value', '', 1, 0, 0.1, 20), + sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1), + sizeFactor: NumberParam('Size Factor', '', 1, 0, 10, 0.1), } export const DefaultProps = paramDefaultValues(Params) export type Props = typeof DefaultProps export async function createValues(ctx: RuntimeContext, points: Points, transform: TransformData, locationIt: LocationIterator, props: Props): Promise<PointsValues> { const { instanceCount, groupCount } = locationIt - const color = await createColors(ctx, locationIt, { name: props.colorTheme, value: props.colorValue }) - const size = await createSizes(ctx, locationIt, { name: props.sizeTheme, value: props.sizeValue }) + const color = await createColors(ctx, locationIt, props) + const size = await createSizes(ctx, locationIt, props) const marker = createMarkers(instanceCount * groupCount) const counts = { drawCount: points.pointCount, groupCount, instanceCount } diff --git a/src/mol-geo/geometry/size-data.ts b/src/mol-geo/geometry/size-data.ts index 32510ac20..c9520dc3d 100644 --- a/src/mol-geo/geometry/size-data.ts +++ b/src/mol-geo/geometry/size-data.ts @@ -10,8 +10,9 @@ import { TextureImage, createTextureImage } from 'mol-gl/renderable/util'; import { LocationIterator } from '../util/location-iterator'; import { Location, NullLocation } from 'mol-model/location'; import { RuntimeContext } from 'mol-task'; -import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; +import { SizeThemeProps, SizeTheme, SizeThemeName } from 'mol-view/theme/size'; import { getGranularity } from './geometry'; +import { Structure } from 'mol-model/structure'; export type SizeType = 'uniform' | 'instance' | 'group' | 'groupInstance' @@ -23,8 +24,24 @@ export type SizeData = { dSizeType: ValueCell<string>, } -export async function createSizes(ctx: RuntimeContext, locationIt: LocationIterator, props: SizeThemeProps, sizeData?: SizeData): Promise<SizeData> { - const sizeTheme = SizeTheme(props) +export interface SizeProps { + sizeTheme: SizeThemeName + sizeValue?: number + sizeFactor?: number + structure?: Structure +} + +export function getSizeThemeProps(props: SizeProps): SizeThemeProps { + return { + name: props.sizeTheme, + value: props.sizeValue, + factor: props.sizeFactor, + structure: props.structure, + } +} + +export async function createSizes(ctx: RuntimeContext, locationIt: LocationIterator, props: SizeProps, sizeData?: SizeData): Promise<SizeData> { + const sizeTheme = SizeTheme(getSizeThemeProps(props)) switch (getGranularity(locationIt, sizeTheme.granularity)) { case 'uniform': return createUniformSize(ctx, locationIt, sizeTheme.size, sizeData) case 'group': return createGroupSize(ctx, locationIt, sizeTheme.size, sizeData) diff --git a/src/mol-geo/representation/index.ts b/src/mol-geo/representation/index.ts index 885add00c..0a35e6e89 100644 --- a/src/mol-geo/representation/index.ts +++ b/src/mol-geo/representation/index.ts @@ -9,11 +9,13 @@ import { RenderObject } from 'mol-gl/render-object' import { PickingId } from '../geometry/picking'; import { Loci } from 'mol-model/loci'; import { MarkerAction } from '../geometry/marker-data'; +import { Params } from 'mol-view/parameter'; export interface RepresentationProps {} export interface Representation<D, P extends RepresentationProps = {}> { readonly label: string + readonly params: Params readonly renderObjects: ReadonlyArray<RenderObject> readonly props: Readonly<P> createOrUpdate: (props?: Partial<P>, data?: D) => Task<void> diff --git a/src/mol-geo/representation/shape/index.ts b/src/mol-geo/representation/shape/index.ts index 6a98661db..6c1f7b1ff 100644 --- a/src/mol-geo/representation/shape/index.ts +++ b/src/mol-geo/representation/shape/index.ts @@ -11,21 +11,22 @@ import { PickingId } from '../../geometry/picking'; import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci'; import { MarkerAction, applyMarkerAction } from '../../geometry/marker-data'; import { ValueCell } from 'mol-util'; -import { ColorThemeProps } from 'mol-view/theme/color'; +import { ColorThemeName, ColorThemeOptions } from 'mol-view/theme/color'; import { Shape } from 'mol-model/shape'; import { LocationIterator } from '../../util/location-iterator'; import { OrderedSet, Interval } from 'mol-data/int'; import { createIdentityTransform } from '../../geometry/transform-data'; import { createRenderableState } from '../../geometry/geometry'; import { Mesh } from '../../geometry/mesh/mesh'; +import { paramDefaultValues, SelectParam } from 'mol-view/parameter'; export interface ShapeRepresentation<P extends RepresentationProps = {}> extends Representation<Shape, P> { } -export const DefaultShapeProps = { - ...Mesh.DefaultProps, - - colorTheme: { name: 'shape-group' } as ColorThemeProps +export const ShapeParams = { + ...Mesh.Params, + colorTheme: SelectParam<ColorThemeName>('Color Theme', '', 'shape-group', ColorThemeOptions) } +export const DefaultShapeProps = paramDefaultValues(ShapeParams) export type ShapeProps = typeof DefaultShapeProps // TODO @@ -60,6 +61,7 @@ export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation return { label: 'Shape mesh', + params: ShapeParams, get renderObjects () { return renderObjects }, get props () { return currentProps }, createOrUpdate, diff --git a/src/mol-geo/representation/structure/complex-representation.ts b/src/mol-geo/representation/structure/complex-representation.ts index c2fd4ba63..cf59bb872 100644 --- a/src/mol-geo/representation/structure/complex-representation.ts +++ b/src/mol-geo/representation/structure/complex-representation.ts @@ -10,7 +10,7 @@ import { Task } from 'mol-task' import { PickingId } from '../../geometry/picking'; import { Loci, EmptyLoci } from 'mol-model/loci'; import { MarkerAction } from '../../geometry/marker-data'; -import { StructureProps, StructureRepresentation } from '.'; +import { StructureProps, StructureRepresentation, StructureParams } from '.'; import { ComplexVisual } from './complex-visual'; export function ComplexRepresentation<P extends StructureProps>(label: string, visualCtor: () => ComplexVisual<P>): StructureRepresentation<P> { @@ -40,6 +40,7 @@ export function ComplexRepresentation<P extends StructureProps>(label: string, v return { label, + params: StructureParams, // TODO get renderObjects() { return visual && visual.renderObject ? [ visual.renderObject ] : [] }, diff --git a/src/mol-geo/representation/structure/complex-visual.ts b/src/mol-geo/representation/structure/complex-visual.ts index ff65e6897..a20063eda 100644 --- a/src/mol-geo/representation/structure/complex-visual.ts +++ b/src/mol-geo/representation/structure/complex-visual.ts @@ -11,7 +11,7 @@ import { Mesh } from '../../geometry/mesh/mesh'; import { RuntimeContext } from 'mol-task'; import { LocationIterator } from '../../util/location-iterator'; import { createComplexMeshRenderObject } from './visual/util/common'; -import { StructureProps, DefaultStructureMeshProps, VisualUpdateState } from '.'; +import { StructureProps, VisualUpdateState, StructureMeshParams } from '.'; import { deepEqual, ValueCell } from 'mol-util'; import { PickingId } from '../../geometry/picking'; import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci'; @@ -19,12 +19,16 @@ import { MarkerAction, applyMarkerAction } from '../../geometry/marker-data'; import { Interval } from 'mol-data/int'; import { updateRenderableState } from '../../geometry/geometry'; import { createColors } from '../../geometry/color-data'; +import { UnitKindOptions, UnitKind } from './units-visual'; +import { MultiSelectParam, paramDefaultValues } from 'mol-view/parameter'; export interface ComplexVisual<P extends StructureProps> extends Visual<Structure, P> { } -export const DefaultComplexMeshProps = { - ...DefaultStructureMeshProps +export const ComplexMeshParams = { + ...StructureMeshParams, + unitKinds: MultiSelectParam<UnitKind>('Unit Kind', '', [ 'atomic', 'spheres' ], UnitKindOptions), } +export const DefaultComplexMeshProps = paramDefaultValues(ComplexMeshParams) export type ComplexMeshProps = typeof DefaultComplexMeshProps export interface ComplexMeshVisualBuilder<P extends ComplexMeshProps> { @@ -48,8 +52,7 @@ export function ComplexMeshVisual<P extends ComplexMeshProps>(builder: ComplexMe let conformationHash: number async function create(ctx: RuntimeContext, structure: Structure, props: Partial<P> = {}) { - currentProps = Object.assign({}, defaultProps, props) - currentProps.colorTheme.structure = structure + currentProps = Object.assign({}, defaultProps, props, { structure }) currentStructure = structure conformationHash = Structure.conformationHash(currentStructure) @@ -61,7 +64,6 @@ export function ComplexMeshVisual<P extends ComplexMeshProps>(builder: ComplexMe async function update(ctx: RuntimeContext, props: Partial<P>) { const newProps = Object.assign({}, currentProps, props) - newProps.colorTheme.structure = currentStructure if (!renderObject) return false @@ -88,7 +90,7 @@ export function ComplexMeshVisual<P extends ComplexMeshProps>(builder: ComplexMe } if (updateState.updateColor) { - await createColors(ctx, locationIt, newProps.colorTheme, renderObject.values) + await createColors(ctx, locationIt, newProps, renderObject.values) } // TODO why do I need to cast here? diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts index b177db9fd..471ca0a03 100644 --- a/src/mol-geo/representation/structure/index.ts +++ b/src/mol-geo/representation/structure/index.ts @@ -6,8 +6,8 @@ */ import { Structure } from 'mol-model/structure'; -import { ColorThemeProps, ColorThemeName, ColorThemeOptions } from 'mol-view/theme/color'; -import { SizeThemeProps, SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; +import { ColorThemeName, ColorThemeOptions } from 'mol-view/theme/color'; +import { SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; import { Representation, RepresentationProps } from '..'; import { Geometry } from '../../geometry/geometry'; import { Mesh } from '../../geometry/mesh/mesh'; @@ -32,16 +32,18 @@ export const StructureMeshParams = { export const DefaultStructureMeshProps = paramDefaultValues(StructureMeshParams) export type StructureMeshProps = typeof DefaultStructureMeshProps -export const DefaultStructurePointsProps = { - ...Points.DefaultProps, - ...DefaultStructureProps, +export const StructurePointsParams = { + ...Points.Params, + ...StructureParams, } +export const DefaultStructurePointsProps = paramDefaultValues(StructurePointsParams) export type StructurePointsProps = typeof DefaultStructurePointsProps -export const DefaultStructureLinesProps = { - ...Lines.DefaultProps, - ...DefaultStructureProps, +export const StructureLinesParams = { + ...Lines.Params, + ...StructureParams, } +export const DefaultStructureLinesProps = paramDefaultValues(StructureLinesParams) export type StructureLinesProps = typeof DefaultStructureLinesProps export interface VisualUpdateState { diff --git a/src/mol-geo/representation/structure/representation/backbone.ts b/src/mol-geo/representation/structure/representation/backbone.ts index 821876a7e..ad6e7e641 100644 --- a/src/mol-geo/representation/structure/representation/backbone.ts +++ b/src/mol-geo/representation/structure/representation/backbone.ts @@ -10,12 +10,14 @@ import { Structure } from 'mol-model/structure'; import { Task } from 'mol-task'; import { Loci } from 'mol-model/loci'; import { MarkerAction } from '../../../geometry/marker-data'; -import { PolymerBackboneVisual, DefaultPolymerBackboneProps } from '../visual/polymer-backbone-cylinder'; +import { PolymerBackboneVisual, PolymerBackboneParams } from '../visual/polymer-backbone-cylinder'; import { getQualityProps } from '../../util'; +import { paramDefaultValues } from 'mol-view/parameter'; -export const DefaultBackboneProps = { - ...DefaultPolymerBackboneProps +export const BackboneParams = { + ...PolymerBackboneParams } +export const DefaultBackboneProps = paramDefaultValues(BackboneParams) export type BackboneProps = typeof DefaultBackboneProps export type BackboneRepresentation = StructureRepresentation<BackboneProps> @@ -26,6 +28,7 @@ export function BackboneRepresentation(): BackboneRepresentation { let currentProps: BackboneProps return { label: 'Backbone', + params: BackboneParams, get renderObjects() { return [ ...traceRepr.renderObjects ] }, diff --git a/src/mol-geo/representation/structure/representation/ball-and-stick.ts b/src/mol-geo/representation/structure/representation/ball-and-stick.ts index 93ce4295f..e10c4a0ef 100644 --- a/src/mol-geo/representation/structure/representation/ball-and-stick.ts +++ b/src/mol-geo/representation/structure/representation/ball-and-stick.ts @@ -5,24 +5,28 @@ */ import { ComplexRepresentation, StructureRepresentation, UnitsRepresentation } from '..'; -import { ElementSphereVisual, DefaultElementSphereProps } from '../visual/element-sphere'; -import { IntraUnitLinkVisual, DefaultIntraUnitLinkProps } from '../visual/intra-unit-link-cylinder'; +import { ElementSphereVisual, ElementSphereParams } from '../visual/element-sphere'; +import { IntraUnitLinkVisual, IntraUnitLinkParams } from '../visual/intra-unit-link-cylinder'; import { PickingId } from '../../../geometry/picking'; -import { Structure, Unit } from 'mol-model/structure'; +import { Structure } from 'mol-model/structure'; import { Task } from 'mol-task'; import { Loci, isEmptyLoci } from 'mol-model/loci'; import { MarkerAction } from '../../../geometry/marker-data'; import { InterUnitLinkVisual } from '../visual/inter-unit-link-cylinder'; -import { SizeThemeProps } from 'mol-view/theme/size'; +import { SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; import { getQualityProps } from '../../util'; +import { paramDefaultValues, SelectParam, NumberParam, MultiSelectParam } from 'mol-view/parameter'; +import { UnitKind, UnitKindOptions } from '../units-visual'; -export const DefaultBallAndStickProps = { - ...DefaultElementSphereProps, - ...DefaultIntraUnitLinkProps, - - sizeTheme: { name: 'uniform', value: 0.2 } as SizeThemeProps, - unitKinds: [ Unit.Kind.Atomic ] as Unit.Kind[] +export const BallAndStickParams = { + ...ElementSphereParams, + ...IntraUnitLinkParams, + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 0.1, 20), + sizeFactor: NumberParam('Size Factor', '', 1, 0, 10, 0.1), + unitKinds: MultiSelectParam<UnitKind>('Unit Kind', '', ['atomic'], UnitKindOptions), } +export const DefaultBallAndStickProps = paramDefaultValues(BallAndStickParams) export type BallAndStickProps = typeof DefaultBallAndStickProps export type BallAndStickRepresentation = StructureRepresentation<BallAndStickProps> @@ -35,6 +39,7 @@ export function BallAndStickRepresentation(): BallAndStickRepresentation { let currentProps: BallAndStickProps return { label: 'Ball & Stick', + params: BallAndStickParams, get renderObjects() { return [ ...elmementRepr.renderObjects, ...intraLinkRepr.renderObjects, ...interLinkRepr.renderObjects ] }, diff --git a/src/mol-geo/representation/structure/representation/carbohydrate.ts b/src/mol-geo/representation/structure/representation/carbohydrate.ts index af3961d3d..89b348ba7 100644 --- a/src/mol-geo/representation/structure/representation/carbohydrate.ts +++ b/src/mol-geo/representation/structure/representation/carbohydrate.ts @@ -10,17 +10,20 @@ import { Structure } from 'mol-model/structure'; import { Task } from 'mol-task'; import { Loci, isEmptyLoci } from 'mol-model/loci'; import { MarkerAction } from '../../../geometry/marker-data'; -import { CarbohydrateSymbolVisual, DefaultCarbohydrateSymbolProps } from '../visual/carbohydrate-symbol-mesh'; -import { CarbohydrateLinkVisual, DefaultCarbohydrateLinkProps } from '../visual/carbohydrate-link-cylinder'; -import { SizeThemeProps } from 'mol-view/theme/size'; +import { CarbohydrateSymbolVisual, CarbohydrateSymbolParams } from '../visual/carbohydrate-symbol-mesh'; +import { CarbohydrateLinkVisual, CarbohydrateLinkParams } from '../visual/carbohydrate-link-cylinder'; +import { SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; import { getQualityProps } from '../../util'; +import { paramDefaultValues, SelectParam, NumberParam } from 'mol-view/parameter'; -export const DefaultCarbohydrateProps = { - ...DefaultCarbohydrateSymbolProps, - ...DefaultCarbohydrateLinkProps, - - sizeTheme: { name: 'uniform', value: 1, factor: 1 } as SizeThemeProps, +export const CarbohydrateParams = { + ...CarbohydrateSymbolParams, + ...CarbohydrateLinkParams, + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 0.1, 20), + sizeFactor: NumberParam('Size Factor', '', 1, 0, 10, 0.1), } +export const DefaultCarbohydrateProps = paramDefaultValues(CarbohydrateParams) export type CarbohydrateProps = typeof DefaultCarbohydrateProps export type CarbohydrateRepresentation = StructureRepresentation<CarbohydrateProps> @@ -32,6 +35,7 @@ export function CarbohydrateRepresentation(): CarbohydrateRepresentation { let currentProps: CarbohydrateProps return { label: 'Carbohydrate', + params: CarbohydrateParams, get renderObjects() { return [ ...carbohydrateSymbolRepr.renderObjects, ...carbohydrateLinkRepr.renderObjects ] }, diff --git a/src/mol-geo/representation/structure/representation/cartoon.ts b/src/mol-geo/representation/structure/representation/cartoon.ts index bb52e94ec..0691430a6 100644 --- a/src/mol-geo/representation/structure/representation/cartoon.ts +++ b/src/mol-geo/representation/structure/representation/cartoon.ts @@ -10,21 +10,23 @@ import { Structure } from 'mol-model/structure'; import { Task } from 'mol-task'; import { Loci, isEmptyLoci } from 'mol-model/loci'; import { MarkerAction } from '../../../geometry/marker-data'; -import { PolymerTraceVisual, DefaultPolymerTraceProps } from '../visual/polymer-trace-mesh'; -import { PolymerGapVisual, DefaultPolymerGapProps } from '../visual/polymer-gap-cylinder'; -import { NucleotideBlockVisual, DefaultNucleotideBlockProps } from '../visual/nucleotide-block-mesh'; -import { SizeThemeProps } from 'mol-view/theme/size'; +import { PolymerTraceVisual, PolymerTraceParams } from '../visual/polymer-trace-mesh'; +import { PolymerGapVisual, PolymerGapParams } from '../visual/polymer-gap-cylinder'; +import { NucleotideBlockVisual, NucleotideBlockParams } from '../visual/nucleotide-block-mesh'; +import { SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; import { getQualityProps } from '../../util'; +import { paramDefaultValues, SelectParam, NumberParam } from 'mol-view/parameter'; // import { PolymerDirectionVisual, DefaultPolymerDirectionProps } from '../visual/polymer-direction-wedge'; -export const DefaultCartoonProps = { - ...DefaultPolymerTraceProps, - ...DefaultPolymerGapProps, - ...DefaultNucleotideBlockProps, - // ...DefaultPolymerDirectionProps, - - sizeTheme: { name: 'uniform', value: 0.2 } as SizeThemeProps, +export const CartoonParams = { + ...PolymerTraceParams, + ...PolymerGapParams, + ...NucleotideBlockParams, + // ...PolymerDirectionParams, + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 0.6, 0, 0.1, 20), } +export const DefaultCartoonProps = paramDefaultValues(CartoonParams) export type CartoonProps = typeof DefaultCartoonProps export type CartoonRepresentation = StructureRepresentation<CartoonProps> @@ -38,6 +40,7 @@ export function CartoonRepresentation(): CartoonRepresentation { let currentProps: CartoonProps return { label: 'Cartoon', + params: CartoonParams, get renderObjects() { return [ ...traceRepr.renderObjects, ...gapRepr.renderObjects, ...blockRepr.renderObjects // , ...directionRepr.renderObjects diff --git a/src/mol-geo/representation/structure/representation/distance-restraint.ts b/src/mol-geo/representation/structure/representation/distance-restraint.ts index 2d9a411bf..5d460bb59 100644 --- a/src/mol-geo/representation/structure/representation/distance-restraint.ts +++ b/src/mol-geo/representation/structure/representation/distance-restraint.ts @@ -10,14 +10,17 @@ import { Structure } from 'mol-model/structure'; import { Task } from 'mol-task'; import { Loci } from 'mol-model/loci'; import { MarkerAction } from '../../../geometry/marker-data'; -import { CrossLinkRestraintVisual, DefaultCrossLinkRestraintProps } from '../visual/cross-link-restraint-cylinder'; -import { SizeThemeProps } from 'mol-view/theme/size'; +import { CrossLinkRestraintVisual, CrossLinkRestraintParams } from '../visual/cross-link-restraint-cylinder'; +import { SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; import { getQualityProps } from '../../util'; +import { paramDefaultValues, SelectParam, NumberParam } from 'mol-view/parameter'; -export const DefaultDistanceRestraintProps = { - ...DefaultCrossLinkRestraintProps, - sizeTheme: { name: 'uniform', value: 0.25 } as SizeThemeProps, +export const DistanceRestraintParams = { + ...CrossLinkRestraintParams, + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 0.25, 0, 0.05, 20), } +export const DefaultDistanceRestraintProps = paramDefaultValues(DistanceRestraintParams) export type DistanceRestraintProps = typeof DefaultDistanceRestraintProps export type DistanceRestraintRepresentation = StructureRepresentation<DistanceRestraintProps> @@ -28,6 +31,7 @@ export function DistanceRestraintRepresentation(): DistanceRestraintRepresentati let currentProps: DistanceRestraintProps return { label: 'Distance restraint', + params: DistanceRestraintParams, get renderObjects() { return [ ...crossLinkRepr.renderObjects ] }, diff --git a/src/mol-geo/representation/structure/representation/molecular-surface.ts b/src/mol-geo/representation/structure/representation/molecular-surface.ts new file mode 100644 index 000000000..9d3d8361a --- /dev/null +++ b/src/mol-geo/representation/structure/representation/molecular-surface.ts @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { UnitsRepresentation } from '..'; +import { GaussianSurfaceVisual, GaussianSurfaceParams } from '../visual/gaussian-surface-mesh'; +import { StructureRepresentation } from '../units-representation'; +import { Structure } from 'mol-model/structure'; +import { MarkerAction } from '../../../geometry/marker-data'; +import { Loci } from 'mol-model/loci'; +import { PickingId } from '../../../geometry/picking'; +import { Task } from 'mol-task'; +import { GaussianWireframeVisual, GaussianWireframeParams } from '../visual/gaussian-surface-wireframe'; +import { getQualityProps } from '../../util'; +import { paramDefaultValues, MultiSelectParam } from 'mol-view/parameter'; + +const VisualOptions = [['surface', 'Surface'], ['wireframe', 'Wireframe']] as [string, string][] + +export const MolecularSurfaceParams = { + ...GaussianSurfaceParams, + ...GaussianWireframeParams, + + visuals: MultiSelectParam<string>('Visuals', '', ['surface'], VisualOptions) +} +export const DefaultMolecularSurfaceProps = paramDefaultValues(MolecularSurfaceParams) +export type MolecularSurfaceProps = typeof DefaultMolecularSurfaceProps + +export type MolecularSurfaceRepresentation = StructureRepresentation<MolecularSurfaceProps> + +export function MolecularSurfaceRepresentation(): MolecularSurfaceRepresentation { + let currentProps: MolecularSurfaceProps + let currentStructure: Structure + const gaussianSurfaceRepr = UnitsRepresentation('Gaussian surface', GaussianSurfaceVisual) + const gaussianWireframeRepr = UnitsRepresentation('Gaussian wireframe', GaussianWireframeVisual) + return { + label: 'Molecular Surface', + params: MolecularSurfaceParams, + get renderObjects() { + const renderObjects = [] + if (currentProps.visuals.includes('surface')) renderObjects.push(...gaussianSurfaceRepr.renderObjects) + if (currentProps.visuals.includes('wireframe')) renderObjects.push(...gaussianWireframeRepr.renderObjects) + return renderObjects + }, + get props() { + return { ...gaussianSurfaceRepr.props, ...gaussianWireframeRepr.props, visuals: currentProps.visuals } + }, + createOrUpdate: (props: Partial<MolecularSurfaceProps> = {}, structure?: Structure) => { + if (structure) currentStructure = structure + const qualityProps = getQualityProps(Object.assign({}, currentProps, props), currentStructure) + currentProps = Object.assign({}, DefaultMolecularSurfaceProps, currentProps, props, qualityProps) + return Task.create('Creating MolecularSurfaceRepresentation', async ctx => { + if (currentProps.visuals.includes('surface')) await gaussianSurfaceRepr.createOrUpdate(currentProps, currentStructure).runInContext(ctx) + if (currentProps.visuals.includes('wireframe')) await gaussianWireframeRepr.createOrUpdate(currentProps, currentStructure).runInContext(ctx) + }) + }, + getLoci: (pickingId: PickingId) => { + return gaussianSurfaceRepr.getLoci(pickingId) + }, + mark: (loci: Loci, action: MarkerAction) => { + return gaussianSurfaceRepr.mark(loci, action) + }, + destroy() { + gaussianSurfaceRepr.destroy() + gaussianWireframeRepr.destroy() + } + } +} \ No newline at end of file diff --git a/src/mol-geo/representation/structure/representation/point.ts b/src/mol-geo/representation/structure/representation/point.ts index 51153cc35..09049bf3b 100644 --- a/src/mol-geo/representation/structure/representation/point.ts +++ b/src/mol-geo/representation/structure/representation/point.ts @@ -5,16 +5,18 @@ */ import { UnitsRepresentation } from '..'; -import { ElementPointVisual, DefaultElementPointProps } from '../visual/element-point'; +import { ElementPointVisual, ElementPointParams } from '../visual/element-point'; import { StructureRepresentation } from '../units-representation'; import { Structure } from 'mol-model/structure'; import { MarkerAction } from '../../../geometry/marker-data'; import { Loci } from 'mol-model/loci'; import { PickingId } from '../../../geometry/picking'; +import { paramDefaultValues } from 'mol-view/parameter'; -export const DefaultPointProps = { - ...DefaultElementPointProps, +export const PointParams = { + ...ElementPointParams, } +export const DefaultPointProps = paramDefaultValues(PointParams) export type PointProps = typeof DefaultPointProps export type PointRepresentation = StructureRepresentation<PointProps> @@ -24,6 +26,7 @@ export function PointRepresentation(): PointRepresentation { const pointRepr = UnitsRepresentation('Point', ElementPointVisual) return { label: 'Point', + params: PointParams, get renderObjects() { return [ ...pointRepr.renderObjects ] }, diff --git a/src/mol-geo/representation/structure/representation/spacefill.ts b/src/mol-geo/representation/structure/representation/spacefill.ts index 2582bb107..740c240c7 100644 --- a/src/mol-geo/representation/structure/representation/spacefill.ts +++ b/src/mol-geo/representation/structure/representation/spacefill.ts @@ -5,17 +5,19 @@ */ import { UnitsRepresentation } from '..'; -import { ElementSphereVisual, DefaultElementSphereProps } from '../visual/element-sphere'; +import { ElementSphereVisual, ElementSphereParams } from '../visual/element-sphere'; import { StructureRepresentation } from '../units-representation'; import { Structure } from 'mol-model/structure'; import { PickingId } from '../../../geometry/picking'; import { MarkerAction } from '../../../geometry/marker-data'; import { Loci } from 'mol-model/loci'; import { getQualityProps } from '../../util'; +import { paramDefaultValues } from 'mol-view/parameter'; -export const DefaultSpacefillProps = { - ...DefaultElementSphereProps +export const SpacefillParams = { + ...ElementSphereParams } +export const DefaultSpacefillProps = paramDefaultValues(SpacefillParams) export type SpacefillProps = typeof DefaultSpacefillProps export type SpacefillRepresentation = StructureRepresentation<SpacefillProps> @@ -25,6 +27,7 @@ export function SpacefillRepresentation(): SpacefillRepresentation { const sphereRepr = UnitsRepresentation('Sphere mesh', ElementSphereVisual) return { label: 'Spacefill', + params: SpacefillParams, get renderObjects() { return [ ...sphereRepr.renderObjects ] }, diff --git a/src/mol-geo/representation/structure/representation/surface.ts b/src/mol-geo/representation/structure/representation/surface.ts deleted file mode 100644 index d397bd3ea..000000000 --- a/src/mol-geo/representation/structure/representation/surface.ts +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import { UnitsRepresentation } from '..'; -import { GaussianSurfaceVisual, DefaultGaussianSurfaceProps } from '../visual/gaussian-surface-mesh'; -import { StructureRepresentation } from '../units-representation'; -import { Structure } from 'mol-model/structure'; -import { MarkerAction } from '../../../geometry/marker-data'; -import { Loci } from 'mol-model/loci'; -import { PickingId } from '../../../geometry/picking'; -import { Task } from 'mol-task'; -import { DefaultGaussianWireframeProps, GaussianWireframeVisual } from '../visual/gaussian-surface-wireframe'; -import { getQualityProps } from '../../util'; - -export const DefaultSurfaceProps = { - ...DefaultGaussianSurfaceProps, - ...DefaultGaussianWireframeProps, - - visuals: { surface: true, wireframe: false }, -} -export type SurfaceProps = typeof DefaultSurfaceProps - -export type SurfaceRepresentation = StructureRepresentation<SurfaceProps> - -export function SurfaceRepresentation(): SurfaceRepresentation { - let currentProps: SurfaceProps - let currentStructure: Structure - const gaussianSurfaceRepr = UnitsRepresentation('Gaussian surface', GaussianSurfaceVisual) - const gaussianWireframeRepr = UnitsRepresentation('Gaussian wireframe', GaussianWireframeVisual) - return { - label: 'Surface', - get renderObjects() { - const renderObjects = [] - if (currentProps.visuals.surface) renderObjects.push(...gaussianSurfaceRepr.renderObjects) - if (currentProps.visuals.wireframe) renderObjects.push(...gaussianWireframeRepr.renderObjects) - return renderObjects - }, - get props() { - return { ...gaussianSurfaceRepr.props, ...gaussianWireframeRepr.props, visuals: currentProps.visuals } - }, - createOrUpdate: (props: Partial<SurfaceProps> = {}, structure?: Structure) => { - if (structure) currentStructure = structure - const qualityProps = getQualityProps(Object.assign({}, currentProps, props), currentStructure) - currentProps = Object.assign({}, DefaultSurfaceProps, currentProps, props, qualityProps) - return Task.create('Creating SurfaceRepresentation', async ctx => { - if (currentProps.visuals.surface) await gaussianSurfaceRepr.createOrUpdate(currentProps, currentStructure).runInContext(ctx) - if (currentProps.visuals.wireframe) await gaussianWireframeRepr.createOrUpdate(currentProps, currentStructure).runInContext(ctx) - }) - }, - getLoci: (pickingId: PickingId) => { - return gaussianSurfaceRepr.getLoci(pickingId) - }, - mark: (loci: Loci, action: MarkerAction) => { - return gaussianSurfaceRepr.mark(loci, action) - }, - destroy() { - gaussianSurfaceRepr.destroy() - gaussianWireframeRepr.destroy() - } - } -} \ No newline at end of file diff --git a/src/mol-geo/representation/structure/units-representation.ts b/src/mol-geo/representation/structure/units-representation.ts index f3f7e7199..c2be322d7 100644 --- a/src/mol-geo/representation/structure/units-representation.ts +++ b/src/mol-geo/representation/structure/units-representation.ts @@ -12,7 +12,7 @@ import { Representation, RepresentationProps, Visual } from '..'; import { PickingId } from '../../geometry/picking'; import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci'; import { MarkerAction } from '../../geometry/marker-data'; -import { StructureProps } from '.'; +import { StructureProps, StructureParams } from '.'; import { StructureGroup } from './units-visual'; export interface UnitsVisual<P extends RepresentationProps = {}> extends Visual<StructureGroup, P> { } @@ -132,6 +132,7 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis return { label, + params: StructureParams, // TODO get renderObjects() { const renderObjects: RenderObject[] = [] visuals.forEach(({ visual }) => { diff --git a/src/mol-geo/representation/structure/units-visual.ts b/src/mol-geo/representation/structure/units-visual.ts index df24f1ae5..fe2abda3d 100644 --- a/src/mol-geo/representation/structure/units-visual.ts +++ b/src/mol-geo/representation/structure/units-visual.ts @@ -8,7 +8,7 @@ import { Unit, Structure } from 'mol-model/structure'; import { RepresentationProps, Visual } from '../'; -import { DefaultStructureMeshProps, VisualUpdateState, DefaultStructurePointsProps, DefaultStructureLinesProps, StructureMeshParams } from '.'; +import { VisualUpdateState, StructureMeshParams, StructurePointsParams, StructureLinesParams } from '.'; import { RuntimeContext } from 'mol-task'; import { PickingId } from '../../geometry/picking'; import { LocationIterator } from '../../util/location-iterator'; @@ -86,8 +86,7 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu let currentConformationId: UUID async function create(ctx: RuntimeContext, group: Unit.SymmetryGroup, props: Partial<P> = {}) { - currentProps = Object.assign({}, defaultProps, props) - currentProps.colorTheme.structure = currentStructure + currentProps = Object.assign({}, defaultProps, props, { structure: currentStructure }) currentGroup = group const unit = group.units[0] @@ -105,7 +104,6 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu if (!renderObject) return const newProps = Object.assign({}, currentProps, props) - newProps.colorTheme.structure = currentStructure const unit = currentGroup.units[0] locationIt.reset() @@ -120,8 +118,8 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu if (currentGroup.units.length !== locationIt.instanceCount) updateState.updateTransform = true - if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) updateState.createGeometry = true - if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) updateState.updateColor = true + if (newProps.sizeTheme !== currentProps.sizeTheme) updateState.createGeometry = true + if (newProps.colorTheme !== currentProps.colorTheme) updateState.updateColor = true if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true // @@ -135,7 +133,7 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu } if (updateState.createGeometry) { - mesh = newProps.unitKinds.includes(unit.kind) + mesh = includesUnitKind(newProps.unitKinds, unit) ? await createMesh(ctx, unit, currentStructure, newProps, mesh) : Mesh.createEmpty(mesh) ValueCell.update(renderObject.values.drawCount, mesh.triangleCount * 3) @@ -143,7 +141,7 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu } if (updateState.updateColor) { - await createColors(ctx, locationIt, newProps.colorTheme, renderObject.values) + await createColors(ctx, locationIt, newProps, renderObject.values) } // TODO why do I need to cast here? @@ -209,10 +207,11 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu // points -export const DefaultUnitsPointsProps = { - ...DefaultStructurePointsProps, - unitKinds: [ Unit.Kind.Atomic, Unit.Kind.Spheres ] as Unit.Kind[] +export const UnitsPointsParams = { + ...StructurePointsParams, + unitKinds: MultiSelectParam<UnitKind>('Unit Kind', '', [ 'atomic', 'spheres' ], UnitKindOptions), } +export const DefaultUnitsPointsProps = paramDefaultValues(UnitsPointsParams) export type UnitsPointsProps = typeof DefaultUnitsPointsProps export interface UnitsPointVisualBuilder<P extends UnitsPointsProps> { @@ -237,8 +236,7 @@ export function UnitsPointsVisual<P extends UnitsPointsProps>(builder: UnitsPoin let currentConformationId: UUID async function create(ctx: RuntimeContext, group: Unit.SymmetryGroup, props: Partial<P> = {}) { - currentProps = Object.assign({}, defaultProps, props) - currentProps.colorTheme.structure = currentStructure + currentProps = Object.assign({}, defaultProps, props, { structure: currentStructure }) currentGroup = group const unit = group.units[0] @@ -256,7 +254,6 @@ export function UnitsPointsVisual<P extends UnitsPointsProps>(builder: UnitsPoin if (!renderObject) return const newProps = Object.assign({}, currentProps, props) - newProps.colorTheme.structure = currentStructure const unit = currentGroup.units[0] locationIt.reset() @@ -271,8 +268,8 @@ export function UnitsPointsVisual<P extends UnitsPointsProps>(builder: UnitsPoin if (currentGroup.units.length !== locationIt.instanceCount) updateState.updateTransform = true - if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) updateState.updateSize = true - if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) updateState.updateColor = true + if (newProps.sizeTheme !== currentProps.sizeTheme) updateState.updateSize = true + if (newProps.colorTheme !== currentProps.colorTheme) updateState.updateColor = true if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true // @@ -286,7 +283,7 @@ export function UnitsPointsVisual<P extends UnitsPointsProps>(builder: UnitsPoin } if (updateState.createGeometry) { - points = newProps.unitKinds.includes(unit.kind) + points = includesUnitKind(newProps.unitKinds, unit) ? await createPoints(ctx, unit, currentStructure, newProps, points) : Points.createEmpty(points) ValueCell.update(renderObject.values.drawCount, points.pointCount) @@ -294,11 +291,11 @@ export function UnitsPointsVisual<P extends UnitsPointsProps>(builder: UnitsPoin } if (updateState.updateSize) { - await createSizes(ctx, locationIt, newProps.sizeTheme, renderObject.values) + await createSizes(ctx, locationIt, newProps, renderObject.values) } if (updateState.updateColor) { - await createColors(ctx, locationIt, newProps.colorTheme, renderObject.values) + await createColors(ctx, locationIt, newProps, renderObject.values) } // TODO why do I need to cast here? @@ -364,10 +361,11 @@ export function UnitsPointsVisual<P extends UnitsPointsProps>(builder: UnitsPoin // lines -export const DefaultUnitsLinesProps = { - ...DefaultStructureLinesProps, - unitKinds: [ Unit.Kind.Atomic, Unit.Kind.Spheres ] as Unit.Kind[] +export const UnitsLinesParams = { + ...StructureLinesParams, + unitKinds: MultiSelectParam<UnitKind>('Unit Kind', '', [ 'atomic', 'spheres' ], UnitKindOptions), } +export const DefaultUnitsLinesProps = paramDefaultValues(UnitsLinesParams) export type UnitsLinesProps = typeof DefaultUnitsLinesProps export interface UnitsLinesVisualBuilder<P extends UnitsLinesProps> { @@ -392,8 +390,7 @@ export function UnitsLinesVisual<P extends UnitsLinesProps>(builder: UnitsLinesV let currentConformationId: UUID async function create(ctx: RuntimeContext, group: Unit.SymmetryGroup, props: Partial<P> = {}) { - currentProps = Object.assign({}, defaultProps, props) - currentProps.colorTheme.structure = currentStructure + currentProps = Object.assign({}, defaultProps, props, { structure: currentStructure }) currentGroup = group const unit = group.units[0] @@ -411,7 +408,6 @@ export function UnitsLinesVisual<P extends UnitsLinesProps>(builder: UnitsLinesV if (!renderObject) return const newProps = Object.assign({}, currentProps, props) - newProps.colorTheme.structure = currentStructure const unit = currentGroup.units[0] locationIt.reset() @@ -426,8 +422,8 @@ export function UnitsLinesVisual<P extends UnitsLinesProps>(builder: UnitsLinesV if (currentGroup.units.length !== locationIt.instanceCount) updateState.updateTransform = true - if (!deepEqual(newProps.sizeTheme, currentProps.sizeTheme)) updateState.updateSize = true - if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) updateState.updateColor = true + if (newProps.sizeTheme !== currentProps.sizeTheme) updateState.updateSize = true + if (newProps.colorTheme !== currentProps.colorTheme) updateState.updateColor = true if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true // @@ -441,7 +437,7 @@ export function UnitsLinesVisual<P extends UnitsLinesProps>(builder: UnitsLinesV } if (updateState.createGeometry) { - lines = newProps.unitKinds.includes(unit.kind) + lines = includesUnitKind(newProps.unitKinds, unit) ? await createLines(ctx, unit, currentStructure, newProps, lines) : Lines.createEmpty(lines) ValueCell.update(renderObject.values.drawCount, lines.lineCount * 2 * 3) @@ -449,11 +445,11 @@ export function UnitsLinesVisual<P extends UnitsLinesProps>(builder: UnitsLinesV } if (updateState.updateSize) { - await createSizes(ctx, locationIt, newProps.sizeTheme, renderObject.values) + await createSizes(ctx, locationIt, newProps, renderObject.values) } if (updateState.updateColor) { - await createColors(ctx, locationIt, newProps.colorTheme, renderObject.values) + await createColors(ctx, locationIt, newProps, renderObject.values) } // TODO why do I need to cast here? diff --git a/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts b/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts index 243610f4e..926573e83 100644 --- a/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts +++ b/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts @@ -4,7 +4,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Unit, Structure, Link, StructureElement } from 'mol-model/structure'; +import { Structure, Link, StructureElement } from 'mol-model/structure'; import { ComplexVisual, VisualUpdateState } from '..'; import { RuntimeContext } from 'mol-task' import { Mesh } from '../../../geometry/mesh/mesh'; @@ -12,13 +12,14 @@ import { PickingId } from '../../../geometry/picking'; import { Loci, EmptyLoci } from 'mol-model/loci'; import { Vec3 } from 'mol-math/linear-algebra'; import { LocationIterator } from '../../../util/location-iterator'; -import { createLinkCylinderMesh, DefaultLinkCylinderProps, LinkCylinderProps } from './util/link'; +import { createLinkCylinderMesh, LinkCylinderProps, LinkCylinderParams } from './util/link'; import { OrderedSet, Interval } from 'mol-data/int'; import { ComplexMeshVisual } from '../complex-visual'; -import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; +import { SizeTheme, SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; import { LinkType } from 'mol-model/structure/model/types'; import { BitFlags } from 'mol-util'; -import { DefaultUnitsMeshProps } from '../units-visual'; +import { UnitsMeshParams } from '../units-visual'; +import { SelectParam, NumberParam, paramDefaultValues } from 'mol-view/parameter'; // TODO create seperate visual // for (let i = 0, il = carbohydrates.terminalLinks.length; i < il; ++i) { @@ -36,7 +37,7 @@ const radiusFactor = 0.3 async function createCarbohydrateLinkCylinderMesh(ctx: RuntimeContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) { const { links, elements } = structure.carbohydrates - const sizeTheme = SizeTheme(props.sizeTheme) + const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue }) const location = StructureElement.create() const builderProps = { @@ -60,13 +61,14 @@ async function createCarbohydrateLinkCylinderMesh(ctx: RuntimeContext, structure return createLinkCylinderMesh(ctx, builderProps, props, mesh) } -export const DefaultCarbohydrateLinkProps = { - ...DefaultUnitsMeshProps, - ...DefaultLinkCylinderProps, - sizeTheme: { name: 'physical', factor: 1 } as SizeThemeProps, - detail: 0, - unitKinds: [ Unit.Kind.Atomic, Unit.Kind.Spheres ] as Unit.Kind[] +export const CarbohydrateLinkParams = { + ...UnitsMeshParams, + ...LinkCylinderParams, + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1), + detail: NumberParam('Sphere Detail', '', 0, 0, 3, 1), } +export const DefaultCarbohydrateLinkProps = paramDefaultValues(CarbohydrateLinkParams) export type CarbohydrateLinkProps = typeof DefaultCarbohydrateLinkProps export function CarbohydrateLinkVisual(): ComplexVisual<CarbohydrateLinkProps> { diff --git a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts index b184d219b..70c535d04 100644 --- a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts +++ b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts @@ -4,7 +4,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Unit, Structure, StructureElement } from 'mol-model/structure'; +import { Structure, StructureElement } from 'mol-model/structure'; import { ComplexVisual, VisualUpdateState } from '..'; import { RuntimeContext } from 'mol-task' import { Mesh } from '../../../geometry/mesh/mesh'; @@ -15,14 +15,15 @@ import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { getSaccharideShape, SaccharideShapes } from 'mol-model/structure/structure/carbohydrates/constants'; import { LocationIterator } from '../../../util/location-iterator'; import { OrderedSet, Interval } from 'mol-data/int'; -import { ComplexMeshVisual, DefaultComplexMeshProps } from '../complex-visual'; -import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; +import { ComplexMeshVisual, ComplexMeshParams } from '../complex-visual'; +import { SizeTheme, SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; import { addSphere } from '../../../geometry/mesh/builder/sphere'; import { Box, PerforatedBox } from '../../../primitive/box'; import { OctagonalPyramid, PerforatedOctagonalPyramid } from '../../../primitive/pyramid'; import { Star } from '../../../primitive/star'; import { Octahedron, PerforatedOctahedron } from '../../../primitive/octahedron'; import { DiamondPrism, PentagonalPrism, HexagonalPrism } from '../../../primitive/prism'; +import { SelectParam, NumberParam, paramDefaultValues } from 'mol-view/parameter'; const t = Mat4.identity() const sVec = Vec3.zero() @@ -45,7 +46,7 @@ const hexagonalPrism = HexagonalPrism() async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Structure, props: CarbohydrateSymbolProps, mesh?: Mesh) { const builder = MeshBuilder.create(256, 128, mesh) - const sizeTheme = SizeTheme(props.sizeTheme) + const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue }) const { detail } = props const carbohydrates = structure.carbohydrates @@ -144,12 +145,13 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru return builder.getMesh() } -export const DefaultCarbohydrateSymbolProps = { - ...DefaultComplexMeshProps, - sizeTheme: { name: 'uniform', value: 1, factor: 1 } as SizeThemeProps, - detail: 0, - unitKinds: [ Unit.Kind.Atomic, Unit.Kind.Spheres ] as Unit.Kind[] +export const CarbohydrateSymbolParams = { + ...ComplexMeshParams, + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1), + detail: NumberParam('Sphere Detail', '', 0, 0, 3, 1), } +export const DefaultCarbohydrateSymbolProps = paramDefaultValues(CarbohydrateSymbolParams) export type CarbohydrateSymbolProps = typeof DefaultCarbohydrateSymbolProps export function CarbohydrateSymbolVisual(): ComplexVisual<CarbohydrateSymbolProps> { diff --git a/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts b/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts index 1de9ac740..9b3070e05 100644 --- a/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts +++ b/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts @@ -7,24 +7,25 @@ import { Link, Structure, StructureElement } from 'mol-model/structure'; import { ComplexVisual, VisualUpdateState } from '..'; import { RuntimeContext } from 'mol-task' -import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh } from './util/link'; +import { LinkCylinderProps, createLinkCylinderMesh, LinkCylinderParams } from './util/link'; import { Mesh } from '../../../geometry/mesh/mesh'; import { PickingId } from '../../../geometry/picking'; import { Vec3 } from 'mol-math/linear-algebra'; import { Loci, EmptyLoci } from 'mol-model/loci'; -import { ComplexMeshVisual, DefaultComplexMeshProps } from '../complex-visual'; +import { ComplexMeshVisual, ComplexMeshParams } from '../complex-visual'; import { LocationIterator } from '../../../util/location-iterator'; import { Interval } from 'mol-data/int'; -import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; +import { SizeTheme, SizeThemeOptions, SizeThemeName } from 'mol-view/theme/size'; import { BitFlags } from 'mol-util'; import { LinkType } from 'mol-model/structure/model/types'; +import { SelectParam, NumberParam, paramDefaultValues } from 'mol-view/parameter'; async function createCrossLinkRestraintCylinderMesh(ctx: RuntimeContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) { const crossLinks = structure.crossLinkRestraints if (!crossLinks.count) return Mesh.createEmpty(mesh) - const sizeTheme = SizeTheme(props.sizeTheme) + const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue }) const location = StructureElement.create() const builderProps = { @@ -49,13 +50,13 @@ async function createCrossLinkRestraintCylinderMesh(ctx: RuntimeContext, structu return createLinkCylinderMesh(ctx, builderProps, props, mesh) } -export const DefaultCrossLinkRestraintProps = { - ...DefaultComplexMeshProps, - ...DefaultLinkCylinderProps, - sizeTheme: { name: 'physical', factor: 0.3 } as SizeThemeProps, - flipSided: false, - flatShaded: false, +export const CrossLinkRestraintParams = { + ...ComplexMeshParams, + ...LinkCylinderParams, + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1), } +export const DefaultCrossLinkRestraintProps = paramDefaultValues(CrossLinkRestraintParams) export type CrossLinkRestraintProps = typeof DefaultCrossLinkRestraintProps export function CrossLinkRestraintVisual(): ComplexVisual<CrossLinkRestraintProps> { diff --git a/src/mol-geo/representation/structure/visual/element-point.ts b/src/mol-geo/representation/structure/visual/element-point.ts index ab6f51d8a..4ca4230e0 100644 --- a/src/mol-geo/representation/structure/visual/element-point.ts +++ b/src/mol-geo/representation/structure/visual/element-point.ts @@ -9,17 +9,19 @@ import { RuntimeContext } from 'mol-task' import { UnitsVisual, VisualUpdateState } from '..'; import { getElementLoci, StructureElementIterator, markElement } from './util/element'; import { Vec3 } from 'mol-math/linear-algebra'; -import { SizeThemeProps } from 'mol-view/theme/size'; -import { UnitsPointsVisual, DefaultUnitsPointsProps } from '../units-visual'; +import { SizeThemeOptions, SizeThemeName } from 'mol-view/theme/size'; +import { UnitsPointsVisual, UnitsPointsParams } from '../units-visual'; import { Points } from '../../../geometry/points/points'; import { PointsBuilder } from '../../../geometry/points/points-builder'; +import { SelectParam, NumberParam, BooleanParam, paramDefaultValues } from 'mol-view/parameter'; -export const DefaultElementPointProps = { - ...DefaultUnitsPointsProps, - - sizeTheme: { name: 'uniform', value: 0.2 } as SizeThemeProps, - pointSizeAttenuation: true, +export const ElementPointParams = { + ...UnitsPointsParams, + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1), + pointSizeAttenuation: BooleanParam('Point Size Attenuation', '', true), } +export const DefaultElementPointProps = paramDefaultValues(ElementPointParams) export type ElementPointProps = typeof DefaultElementPointProps // TODO size diff --git a/src/mol-geo/representation/structure/visual/element-sphere.ts b/src/mol-geo/representation/structure/visual/element-sphere.ts index 80176be24..979d073da 100644 --- a/src/mol-geo/representation/structure/visual/element-sphere.ts +++ b/src/mol-geo/representation/structure/visual/element-sphere.ts @@ -7,15 +7,17 @@ import { UnitsVisual, VisualUpdateState } from '..'; import { createElementSphereMesh, markElement, getElementLoci, StructureElementIterator } from './util/element'; -import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual'; +import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual'; +import { NumberParam, paramDefaultValues, SelectParam } from 'mol-view/parameter'; +import { SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; export const ElementSphereParams = { - UnitsMe -} -export const DefaultElementSphereProps = { - ...DefaultUnitsMeshProps, - detail: 0 + ...UnitsMeshParams, + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1), + detail: NumberParam('Sphere Detail', '', 0, 0, 3, 1), } +export const DefaultElementSphereProps = paramDefaultValues(ElementSphereParams) export type ElementSphereProps = typeof DefaultElementSphereProps export function ElementSphereVisual(): UnitsVisual<ElementSphereProps> { diff --git a/src/mol-geo/representation/structure/visual/gaussian-density-point.ts b/src/mol-geo/representation/structure/visual/gaussian-density-point.ts index 27689484e..a445ac86f 100644 --- a/src/mol-geo/representation/structure/visual/gaussian-density-point.ts +++ b/src/mol-geo/representation/structure/visual/gaussian-density-point.ts @@ -10,19 +10,21 @@ import { UnitsVisual, VisualUpdateState } from '..'; import { StructureElementIterator } from './util/element'; import { EmptyLoci } from 'mol-model/loci'; import { Vec3 } from 'mol-math/linear-algebra'; -import { UnitsPointsVisual, DefaultUnitsPointsProps } from '../units-visual'; +import { UnitsPointsVisual, UnitsPointsParams } from '../units-visual'; import { Points } from '../../../geometry/points/points'; import { PointsBuilder } from '../../../geometry/points/points-builder'; -import { SizeThemeProps } from 'mol-view/theme/size'; -import { DefaultGaussianDensityProps, GaussianDensityProps } from 'mol-model/structure/structure/unit/gaussian-density'; +import { SizeThemeOptions, SizeThemeName } from 'mol-view/theme/size'; +import { GaussianDensityProps, GaussianDensityParams } from 'mol-model/structure/structure/unit/gaussian-density'; +import { paramDefaultValues, SelectParam, NumberParam, BooleanParam } from 'mol-view/parameter'; -export const DefaultGaussianDensityPointProps = { - ...DefaultUnitsPointsProps, - ...DefaultGaussianDensityProps, - - sizeTheme: { name: 'uniform', value: 1 } as SizeThemeProps, - pointSizeAttenuation: false, +export const GaussianDensityPointParams = { + ...UnitsPointsParams, + ...GaussianDensityParams, + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1), + pointSizeAttenuation: BooleanParam('Point Size Attenuation', '', false), } +export const DefaultGaussianDensityPointProps = paramDefaultValues(GaussianDensityPointParams) export type GaussianDensityPointProps = typeof DefaultGaussianDensityPointProps export async function createGaussianDensityPoint(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityProps, points?: Points) { diff --git a/src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts b/src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts index 331ed499a..510d1b50a 100644 --- a/src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts +++ b/src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts @@ -8,10 +8,11 @@ import { Unit, Structure } from 'mol-model/structure'; import { UnitsVisual, VisualUpdateState } from '..'; import { RuntimeContext } from 'mol-task' import { Mesh } from '../../../geometry/mesh/mesh'; -import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual'; +import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual'; import { StructureElementIterator, getElementLoci, markElement } from './util/element'; import { computeMarchingCubesMesh } from '../../../util/marching-cubes/algorithm'; -import { DefaultGaussianDensityProps, GaussianDensityProps } from 'mol-model/structure/structure/unit/gaussian-density'; +import { GaussianDensityProps, GaussianDensityParams } from 'mol-model/structure/structure/unit/gaussian-density'; +import { paramDefaultValues } from 'mol-view/parameter'; async function createGaussianSurfaceMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityProps, mesh?: Mesh): Promise<Mesh> { const { smoothness } = props @@ -31,10 +32,11 @@ async function createGaussianSurfaceMesh(ctx: RuntimeContext, unit: Unit, struct return surface; } -export const DefaultGaussianSurfaceProps = { - ...DefaultUnitsMeshProps, - ...DefaultGaussianDensityProps, +export const GaussianSurfaceParams = { + ...UnitsMeshParams, + ...GaussianDensityParams, } +export const DefaultGaussianSurfaceProps = paramDefaultValues(GaussianSurfaceParams) export type GaussianSurfaceProps = typeof DefaultGaussianSurfaceProps export function GaussianSurfaceVisual(): UnitsVisual<GaussianSurfaceProps> { diff --git a/src/mol-geo/representation/structure/visual/gaussian-surface-wireframe.ts b/src/mol-geo/representation/structure/visual/gaussian-surface-wireframe.ts index 3d2635a3a..1c5d4e091 100644 --- a/src/mol-geo/representation/structure/visual/gaussian-surface-wireframe.ts +++ b/src/mol-geo/representation/structure/visual/gaussian-surface-wireframe.ts @@ -7,11 +7,12 @@ import { Unit, Structure } from 'mol-model/structure'; import { UnitsVisual, VisualUpdateState } from '..'; import { RuntimeContext } from 'mol-task' -import { UnitsLinesVisual, DefaultUnitsLinesProps } from '../units-visual'; +import { UnitsLinesVisual, UnitsLinesParams } from '../units-visual'; import { StructureElementIterator, getElementLoci, markElement } from './util/element'; import { computeMarchingCubesLines } from '../../../util/marching-cubes/algorithm'; import { Lines } from '../../../geometry/lines/lines'; -import { GaussianDensityProps, DefaultGaussianDensityProps } from 'mol-model/structure/structure/unit/gaussian-density'; +import { GaussianDensityProps, GaussianDensityParams } from 'mol-model/structure/structure/unit/gaussian-density'; +import { paramDefaultValues } from 'mol-view/parameter'; async function createGaussianWireframe(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityProps, lines?: Lines): Promise<Lines> { const { smoothness } = props @@ -29,10 +30,11 @@ async function createGaussianWireframe(ctx: RuntimeContext, unit: Unit, structur return wireframe } -export const DefaultGaussianWireframeProps = { - ...DefaultUnitsLinesProps, - ...DefaultGaussianDensityProps, +export const GaussianWireframeParams = { + ...UnitsLinesParams, + ...GaussianDensityParams, } +export const DefaultGaussianWireframeProps = paramDefaultValues(GaussianWireframeParams) export type GaussianWireframeProps = typeof DefaultGaussianWireframeProps export function GaussianWireframeVisual(): UnitsVisual<GaussianWireframeProps> { diff --git a/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts b/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts index 0a5ba226e..c6b9af62d 100644 --- a/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts +++ b/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts @@ -7,15 +7,16 @@ import { Link, Structure, StructureElement } from 'mol-model/structure'; import { ComplexVisual, VisualUpdateState } from '..'; import { RuntimeContext } from 'mol-task' -import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh, LinkIterator } from './util/link'; +import { LinkCylinderProps, createLinkCylinderMesh, LinkIterator, LinkCylinderParams } from './util/link'; import { Mesh } from '../../../geometry/mesh/mesh'; import { PickingId } from '../../../geometry/picking'; import { Vec3 } from 'mol-math/linear-algebra'; import { Loci, EmptyLoci } from 'mol-model/loci'; -import { ComplexMeshVisual, DefaultComplexMeshProps } from '../complex-visual'; +import { ComplexMeshVisual, ComplexMeshParams } from '../complex-visual'; import { Interval } from 'mol-data/int'; -import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; +import { SizeTheme, SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; import { BitFlags } from 'mol-util'; +import { SelectParam, NumberParam, paramDefaultValues } from 'mol-view/parameter'; async function createInterUnitLinkCylinderMesh(ctx: RuntimeContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) { const links = structure.links @@ -23,7 +24,7 @@ async function createInterUnitLinkCylinderMesh(ctx: RuntimeContext, structure: S if (!bondCount) return Mesh.createEmpty(mesh) - const sizeTheme = SizeTheme(props.sizeTheme) + const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue, factor: props.sizeFactor }) const location = StructureElement.create() const builderProps = { @@ -48,12 +49,14 @@ async function createInterUnitLinkCylinderMesh(ctx: RuntimeContext, structure: S return createLinkCylinderMesh(ctx, builderProps, props, mesh) } -export const DefaultInterUnitLinkProps = { - ...DefaultComplexMeshProps, - ...DefaultLinkCylinderProps, - - sizeTheme: { name: 'physical', factor: 0.3 } as SizeThemeProps, +export const InterUnitLinkParams = { + ...ComplexMeshParams, + ...LinkCylinderParams, + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1), + sizeFactor: NumberParam('Size Factor', '', 0.3, 0, 10, 0.1), } +export const DefaultInterUnitLinkProps = paramDefaultValues(InterUnitLinkParams) export type InterUnitLinkProps = typeof DefaultInterUnitLinkProps export function InterUnitLinkVisual(): ComplexVisual<InterUnitLinkProps> { diff --git a/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts b/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts index df2185a28..26f4f849d 100644 --- a/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts +++ b/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts @@ -8,20 +8,21 @@ import { Unit, Link, StructureElement, Structure } from 'mol-model/structure'; import { UnitsVisual, VisualUpdateState } from '..'; import { RuntimeContext } from 'mol-task' -import { DefaultLinkCylinderProps, LinkCylinderProps, createLinkCylinderMesh, LinkIterator } from './util/link'; +import { LinkCylinderProps, createLinkCylinderMesh, LinkIterator, LinkCylinderParams } from './util/link'; import { Mesh } from '../../../geometry/mesh/mesh'; import { PickingId } from '../../../geometry/picking'; import { Vec3 } from 'mol-math/linear-algebra'; import { Loci, EmptyLoci } from 'mol-model/loci'; -import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual'; +import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual'; import { Interval } from 'mol-data/int'; -import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; +import { SizeTheme, SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; import { BitFlags } from 'mol-util'; +import { SelectParam, NumberParam, paramDefaultValues } from 'mol-view/parameter'; async function createIntraUnitLinkCylinderMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) { if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh) - const sizeTheme = SizeTheme(props.sizeTheme) + const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue, factor: props.sizeFactor }) const location = StructureElement.create(unit) const elements = unit.elements; @@ -62,12 +63,14 @@ async function createIntraUnitLinkCylinderMesh(ctx: RuntimeContext, unit: Unit, return createLinkCylinderMesh(ctx, builderProps, props, mesh) } -export const DefaultIntraUnitLinkProps = { - ...DefaultUnitsMeshProps, - ...DefaultLinkCylinderProps, - - sizeTheme: { name: 'physical', factor: 0.3 } as SizeThemeProps, +export const IntraUnitLinkParams = { + ...UnitsMeshParams, + ...LinkCylinderParams, + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1), + sizeFactor: NumberParam('Size Factor', '', 0.3, 0, 10, 0.1), } +export const DefaultIntraUnitLinkProps = paramDefaultValues(IntraUnitLinkParams) export type IntraUnitLinkProps = typeof DefaultIntraUnitLinkProps export function IntraUnitLinkVisual(): UnitsVisual<IntraUnitLinkProps> { diff --git a/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts b/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts index 48733329c..f29a9a3e7 100644 --- a/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts +++ b/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts @@ -13,10 +13,11 @@ import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { Segmentation } from 'mol-data/int'; import { MoleculeType, isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types'; import { getElementIndexForAtomId, getElementIndexForAtomRole } from 'mol-model/structure/util'; -import { DefaultUnitsMeshProps, UnitsMeshVisual } from '../units-visual'; +import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual'; import { addCylinder } from '../../../geometry/mesh/builder/cylinder'; import { Box } from '../../../primitive/box'; import { NucleotideLocationIterator, markNucleotideElement, getNucleotideElementLoci } from './util/nucleotide'; +import { paramDefaultValues } from 'mol-view/parameter'; const p1 = Vec3.zero() const p2 = Vec3.zero() @@ -110,9 +111,10 @@ async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, struct return builder.getMesh() } -export const DefaultNucleotideBlockProps = { - ...DefaultUnitsMeshProps +export const NucleotideBlockParams = { + ...UnitsMeshParams } +export const DefaultNucleotideBlockProps = paramDefaultValues(NucleotideBlockParams) export type NucleotideBlockProps = typeof DefaultNucleotideBlockProps export function NucleotideBlockVisual(): UnitsVisual<NucleotideBlockProps> { diff --git a/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts b/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts index db7428205..ebb2e9795 100644 --- a/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts +++ b/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts @@ -12,22 +12,26 @@ import { MeshBuilder } from '../../../geometry/mesh/mesh-builder'; import { PolymerBackboneIterator } from './util/polymer'; import { getElementLoci, markElement, StructureElementIterator } from './util/element'; import { Vec3 } from 'mol-math/linear-algebra'; -import { DefaultUnitsMeshProps, UnitsMeshVisual } from '../units-visual'; -import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; +import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual'; +import { SizeTheme, SizeThemeOptions, SizeThemeName } from 'mol-view/theme/size'; import { CylinderProps } from '../../../primitive/cylinder'; import { OrderedSet } from 'mol-data/int'; import { addCylinder } from '../../../geometry/mesh/builder/cylinder'; +import { paramDefaultValues, NumberParam, SelectParam } from 'mol-view/parameter'; -export interface PolymerBackboneCylinderProps { - sizeTheme: SizeThemeProps - radialSegments: number +export const PolymerBackboneCylinderParams = { + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1), + radialSegments: NumberParam('Radial Segments', '', 16, 3, 56, 1), } +export const DefaultPolymerBackboneCylinderProps = paramDefaultValues(PolymerBackboneCylinderParams) +export type PolymerBackboneCylinderProps = typeof DefaultPolymerBackboneCylinderProps async function createPolymerBackboneCylinderMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: PolymerBackboneCylinderProps, mesh?: Mesh) { const polymerElementCount = unit.polymerElements.length if (!polymerElementCount) return Mesh.createEmpty(mesh) - const sizeTheme = SizeTheme(props.sizeTheme) + const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue }) const { radialSegments } = props const vertexCountEstimate = radialSegments * 2 * polymerElementCount * 2 @@ -63,10 +67,11 @@ async function createPolymerBackboneCylinderMesh(ctx: RuntimeContext, unit: Unit return builder.getMesh() } -export const DefaultPolymerBackboneProps = { - ...DefaultUnitsMeshProps, - radialSegments: 16 +export const PolymerBackboneParams = { + ...UnitsMeshParams, + ...PolymerBackboneCylinderParams, } +export const DefaultPolymerBackboneProps = paramDefaultValues(PolymerBackboneParams) export type PolymerBackboneProps = typeof DefaultPolymerBackboneProps export function PolymerBackboneVisual(): UnitsVisual<PolymerBackboneProps> { diff --git a/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts b/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts index 0b6f96f88..99f02d120 100644 --- a/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts +++ b/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts @@ -12,9 +12,10 @@ import { MeshBuilder } from '../../../geometry/mesh/mesh-builder'; import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, markPolymerElement } from './util/polymer'; import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types'; -import { DefaultUnitsMeshProps, UnitsMeshVisual } from '../units-visual'; -import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; +import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual'; +import { SizeTheme, SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; import { Wedge } from '../../../primitive/wedge'; +import { SelectParam, NumberParam, paramDefaultValues } from 'mol-view/parameter'; const t = Mat4.identity() const sVec = Vec3.zero() @@ -28,15 +29,18 @@ const heightFactor = 6 const wedge = Wedge() -export interface PolymerDirectionWedgeProps { - sizeTheme: SizeThemeProps +export const PolymerDirectionWedgeParams = { + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1), } +export const DefaultPolymerDirectionWedgeProps = paramDefaultValues(PolymerDirectionWedgeParams) +export type PolymerDirectionWedgeProps = typeof DefaultPolymerDirectionWedgeProps async function createPolymerDirectionWedgeMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: PolymerDirectionWedgeProps, mesh?: Mesh) { const polymerElementCount = unit.polymerElements.length if (!polymerElementCount) return Mesh.createEmpty(mesh) - const sizeTheme = SizeTheme(props.sizeTheme) + const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue }) const vertexCount = polymerElementCount * 24 const builder = MeshBuilder.create(vertexCount, vertexCount / 10, mesh) @@ -85,9 +89,11 @@ async function createPolymerDirectionWedgeMesh(ctx: RuntimeContext, unit: Unit, return builder.getMesh() } -export const DefaultPolymerDirectionProps = { - ...DefaultUnitsMeshProps +export const PolymerDirectionParams = { + ...UnitsMeshParams, + ...PolymerDirectionWedgeParams } +export const DefaultPolymerDirectionProps = paramDefaultValues(PolymerDirectionParams) export type PolymerDirectionProps = typeof DefaultPolymerDirectionProps export function PolymerDirectionVisual(): UnitsVisual<PolymerDirectionProps> { diff --git a/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts b/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts index cab4cd394..b70b28171 100644 --- a/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts +++ b/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts @@ -11,24 +11,30 @@ import { Mesh } from '../../../geometry/mesh/mesh'; import { MeshBuilder } from '../../../geometry/mesh/mesh-builder'; import { PolymerGapIterator, PolymerGapLocationIterator, markPolymerGapElement, getPolymerGapElementLoci } from './util/polymer'; import { Vec3 } from 'mol-math/linear-algebra'; -import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual'; -import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; +import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual'; +import { SizeTheme, SizeThemeOptions, SizeThemeName } from 'mol-view/theme/size'; import { CylinderProps } from '../../../primitive/cylinder'; import { addSphere } from '../../../geometry/mesh/builder/sphere'; import { addFixedCountDashedCylinder } from '../../../geometry/mesh/builder/cylinder'; +import { SelectParam, NumberParam, paramDefaultValues } from 'mol-view/parameter'; +import { LinkCylinderParams } from './util/link'; const segmentCount = 10 -export interface PolymerGapCylinderProps { - sizeTheme: SizeThemeProps - radialSegments: number +export const PolymerGapCylinderParams = { + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1), + sizeFactor: NumberParam('Size Factor', '', 0.3, 0, 10, 0.1), + radialSegments: NumberParam('Radial Segments', '', 16, 3, 56, 1), } +export const DefaultPolymerGapCylinderProps = paramDefaultValues(PolymerGapCylinderParams) +export type PolymerGapCylinderProps = typeof DefaultPolymerGapCylinderProps async function createPolymerGapCylinderMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: PolymerGapCylinderProps, mesh?: Mesh) { const polymerGapCount = unit.gapElements.length if (!polymerGapCount) return Mesh.createEmpty(mesh) - const sizeTheme = SizeTheme(props.sizeTheme) + const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue, factor: props.sizeValue }) const { radialSegments } = props const vertexCountEstimate = segmentCount * radialSegments * 2 * polymerGapCount * 2 @@ -71,10 +77,21 @@ async function createPolymerGapCylinderMesh(ctx: RuntimeContext, unit: Unit, str return builder.getMesh() } -export const DefaultPolymerGapProps = { - ...DefaultUnitsMeshProps, - radialSegments: 16 +export const InterUnitLinkParams = { + ...UnitsMeshParams, + ...LinkCylinderParams, + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1), + sizeFactor: NumberParam('Size Factor', '', 0.3, 0, 10, 0.1), } +export const DefaultIntraUnitLinkProps = paramDefaultValues(InterUnitLinkParams) +export type IntraUnitLinkProps = typeof DefaultIntraUnitLinkProps + +export const PolymerGapParams = { + ...UnitsMeshParams, + ...PolymerGapCylinderParams +} +export const DefaultPolymerGapProps = paramDefaultValues(PolymerGapParams) export type PolymerGapProps = typeof DefaultPolymerGapProps export function PolymerGapVisual(): UnitsVisual<PolymerGapProps> { diff --git a/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts b/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts index 1fe0a7e37..a3d6e7b98 100644 --- a/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts +++ b/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts @@ -11,18 +11,23 @@ import { Mesh } from '../../../geometry/mesh/mesh'; import { MeshBuilder } from '../../../geometry/mesh/mesh-builder'; import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, markPolymerElement } from './util/polymer'; import { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types'; -import { UnitsMeshVisual, DefaultUnitsMeshProps } from '../units-visual'; -import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; +import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual'; +import { SizeTheme, SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; import { addSheet } from '../../../geometry/mesh/builder/sheet'; import { addTube } from '../../../geometry/mesh/builder/tube'; - -export interface PolymerTraceMeshProps { - sizeTheme: SizeThemeProps - linearSegments: number - radialSegments: number - aspectRatio: number - arrowFactor: number +import { SelectParam, NumberParam, paramDefaultValues } from 'mol-view/parameter'; + +export const PolymerTraceMeshParams = { + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1), + sizeFactor: NumberParam('Size Factor', '', 0.3, 0, 10, 0.1), + linearSegments: NumberParam('Linear Segments', '', 8, 1, 48, 1), + radialSegments: NumberParam('Radial Segments', '', 16, 3, 56, 1), + aspectRatio: NumberParam('Aspect Ratio', '', 5, 0.1, 5, 0.1), + arrowFactor: NumberParam('Arrow Factor', '', 1.5, 0.1, 5, 0.1), } +export const DefaultPolymerTraceMeshProps = paramDefaultValues(PolymerTraceMeshParams) +export type PolymerTraceMeshProps = typeof DefaultPolymerTraceMeshProps // TODO handle polymer ends properly @@ -30,7 +35,7 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, structure const polymerElementCount = unit.polymerElements.length if (!polymerElementCount) return Mesh.createEmpty(mesh) - const sizeTheme = SizeTheme(props.sizeTheme) + const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue, factor: props.sizeFactor }) const { linearSegments, radialSegments, aspectRatio, arrowFactor } = props const vertexCount = linearSegments * radialSegments * polymerElementCount + (radialSegments + 1) * polymerElementCount * 2 @@ -83,13 +88,11 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, structure return builder.getMesh() } -export const DefaultPolymerTraceProps = { - ...DefaultUnitsMeshProps, - linearSegments: 8, - radialSegments: 12, - aspectRatio: 5, - arrowFactor: 1.5 +export const PolymerTraceParams = { + ...UnitsMeshParams, + ...PolymerTraceMeshParams } +export const DefaultPolymerTraceProps = paramDefaultValues(PolymerTraceParams) export type PolymerTraceProps = typeof DefaultPolymerTraceProps export function PolymerTraceVisual(): UnitsVisual<PolymerTraceProps> { diff --git a/src/mol-geo/representation/structure/visual/util/element.ts b/src/mol-geo/representation/structure/visual/util/element.ts index a2b871fef..76dfc4a50 100644 --- a/src/mol-geo/representation/structure/visual/util/element.ts +++ b/src/mol-geo/representation/structure/visual/util/element.ts @@ -13,12 +13,13 @@ import { MeshBuilder } from '../../../../geometry/mesh/mesh-builder'; import { Loci, EmptyLoci } from 'mol-model/loci'; import { Interval, OrderedSet } from 'mol-data/int'; import { PickingId } from '../../../../geometry/picking'; -import { SizeTheme, SizeThemeProps } from 'mol-view/theme/size'; +import { SizeTheme, SizeThemeName } from 'mol-view/theme/size'; import { LocationIterator } from '../../../../util/location-iterator'; import { addSphere } from '../../../../geometry/mesh/builder/sphere'; export interface ElementSphereMeshProps { - sizeTheme: SizeThemeProps, + sizeTheme: SizeThemeName, + sizeValue: number, detail: number, } @@ -26,7 +27,7 @@ export async function createElementSphereMesh(ctx: RuntimeContext, unit: Unit, s const { detail } = props const { elements } = unit; - const sizeTheme = SizeTheme(props.sizeTheme) + const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue }) const elementCount = elements.length; const vertexCount = elementCount * sphereVertexCount(detail) const meshBuilder = MeshBuilder.create(vertexCount, vertexCount / 2, mesh) diff --git a/src/mol-geo/representation/structure/visual/util/link.ts b/src/mol-geo/representation/structure/visual/util/link.ts index cd782a7de..6cdbae8ab 100644 --- a/src/mol-geo/representation/structure/visual/util/link.ts +++ b/src/mol-geo/representation/structure/visual/util/link.ts @@ -9,19 +9,23 @@ import { RuntimeContext } from 'mol-task'; import { Mesh } from '../../../../geometry/mesh/mesh'; import { MeshBuilder } from '../../../../geometry/mesh/mesh-builder'; import { LinkType } from 'mol-model/structure/model/types'; -import { SizeThemeProps } from 'mol-view/theme/size'; +import { SizeThemeName, SizeThemeOptions } from 'mol-view/theme/size'; import { CylinderProps } from '../../../../primitive/cylinder'; import { LocationIterator } from '../../../../util/location-iterator'; import { Unit, StructureElement, Structure, Link } from 'mol-model/structure'; import { addFixedCountDashedCylinder, addCylinder, addDoubleCylinder } from '../../../../geometry/mesh/builder/cylinder'; - -export const DefaultLinkCylinderProps = { - sizeTheme: { name: 'uniform', value: 0.15 } as SizeThemeProps, - linkScale: 0.4, - linkSpacing: 1, - linkRadius: 0.25, - radialSegments: 16 +import { SelectParam, RangeParam, NumberParam, paramDefaultValues } from 'mol-view/parameter'; + +export const LinkCylinderParams = { + sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions), + sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1), + sizeFactor: NumberParam('Size Factor', '', 1, 0, 10, 0.1), + linkScale: RangeParam('Link Scale', '', 0.4, 0, 1, 0.1), + linkSpacing: RangeParam('Link Spacing', '', 1, 0, 2, 0.01), + linkRadius: RangeParam('Link Radius', '', 0.25, 0, 10, 0.05), + radialSegments: NumberParam('Radial Segments', '', 16, 3, 56, 1), } +export const DefaultLinkCylinderProps = paramDefaultValues(LinkCylinderParams) export type LinkCylinderProps = typeof DefaultLinkCylinderProps const tmpShiftV12 = Vec3.zero() diff --git a/src/mol-geo/representation/volume/index.ts b/src/mol-geo/representation/volume/index.ts index 8fb7051da..5f22df51d 100644 --- a/src/mol-geo/representation/volume/index.ts +++ b/src/mol-geo/representation/volume/index.ts @@ -12,14 +12,16 @@ import { PickingId } from '../../geometry/picking'; import { Loci, EmptyLoci } from 'mol-model/loci'; import { MarkerAction } from '../../geometry/marker-data'; import { Geometry } from '../../geometry/geometry'; +import { paramDefaultValues } from 'mol-view/parameter'; export interface VolumeVisual<P extends RepresentationProps = {}> extends Visual<VolumeData, P> { } export interface VolumeRepresentation<P extends RepresentationProps = {}> extends Representation<VolumeData, P> { } -export const DefaultVolumeProps = { - ...Geometry.DefaultProps +export const VolumeParams = { + ...Geometry.Params } +export const DefaultVolumeProps = paramDefaultValues(VolumeParams) export type VolumeProps = typeof DefaultVolumeProps export function VolumeRepresentation<P extends VolumeProps>(visualCtor: (volumeData: VolumeData) => VolumeVisual<P>): VolumeRepresentation<P> { @@ -43,6 +45,7 @@ export function VolumeRepresentation<P extends VolumeProps>(visualCtor: (volumeD return { label: 'Volume mesh', + params: VolumeParams, get renderObjects () { return renderObjects }, get props () { return _props }, createOrUpdate, diff --git a/src/mol-geo/representation/volume/surface.ts b/src/mol-geo/representation/volume/isosurface.ts similarity index 78% rename from src/mol-geo/representation/volume/surface.ts rename to src/mol-geo/representation/volume/isosurface.ts index b6c6b1fce..cc472eeab 100644 --- a/src/mol-geo/representation/volume/surface.ts +++ b/src/mol-geo/representation/volume/isosurface.ts @@ -18,6 +18,7 @@ import { LocationIterator } from '../../util/location-iterator'; import { NullLocation } from 'mol-model/location'; import { createIdentityTransform } from '../../geometry/transform-data'; import { createRenderableState } from '../../geometry/geometry'; +import { paramDefaultValues, NumberParam } from 'mol-view/parameter'; export function computeVolumeSurface(volume: VolumeData, isoValue: VolumeIsoValue) { return Task.create<Mesh>('Volume Surface', async ctx => { @@ -36,25 +37,26 @@ export function computeVolumeSurface(volume: VolumeData, isoValue: VolumeIsoValu }); } -export const DefaultSurfaceProps = { - ...Mesh.DefaultProps, - isoValue: VolumeIsoValue.relative({ min: 0, max: 0, mean: 0, sigma: 0 }, 0), +export const IsosurfaceParams = { + ...Mesh.Params, + isoValue: NumberParam('Iso Value', '', 2, -5, 5, 0.01), } -export type SurfaceProps = typeof DefaultSurfaceProps +export const DefaultIsosurfaceProps = paramDefaultValues(IsosurfaceParams) +export type IsosurfaceProps = typeof DefaultIsosurfaceProps -export default function SurfaceVisual(): VolumeVisual<SurfaceProps> { +export default function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> { let renderObject: MeshRenderObject - let currentProps = DefaultSurfaceProps + let currentProps = DefaultIsosurfaceProps return { get renderObject () { return renderObject }, - async createOrUpdate(ctx: RuntimeContext, props: Partial<SurfaceProps> = {}, volume?: VolumeData) { - currentProps = { ...DefaultSurfaceProps, ...props } + async createOrUpdate(ctx: RuntimeContext, props: Partial<IsosurfaceProps> = {}, volume?: VolumeData) { + currentProps = { ...DefaultIsosurfaceProps, ...props } console.log('MOINMOIN') if (!volume) return - const mesh = await computeVolumeSurface(volume, currentProps.isoValue).runAsChild(ctx) + const mesh = await computeVolumeSurface(volume, VolumeIsoValue.relative(volume.dataStats, currentProps.isoValue)).runAsChild(ctx) if (!props.flatShaded) { Mesh.computeNormalsImmediate(mesh) } diff --git a/src/mol-model/structure/structure/unit/gaussian-density.ts b/src/mol-model/structure/structure/unit/gaussian-density.ts index 1c9d5b4f1..caff5f4c3 100644 --- a/src/mol-model/structure/structure/unit/gaussian-density.ts +++ b/src/mol-model/structure/structure/unit/gaussian-density.ts @@ -9,12 +9,14 @@ import { SizeTheme } from 'mol-view/theme/size'; import { GaussianDensity } from 'mol-math/geometry/gaussian-density'; import { Task, RuntimeContext } from 'mol-task'; import { DensityData } from 'mol-math/geometry'; +import { NumberParam, paramDefaultValues } from 'mol-view/parameter'; -export const DefaultGaussianDensityProps = { - resolution: 1, - radiusOffset: 0, - smoothness: 1.5, +export const GaussianDensityParams = { + resolution: NumberParam('Resolution', '', 1, 0.1, 10, 0.1), + radiusOffset: NumberParam('Radius Offset', '', 0, 0, 10, 0.1), + smoothness: NumberParam('Smoothness', '', 1.5, 0, 4, 0.1), } +export const DefaultGaussianDensityProps = paramDefaultValues(GaussianDensityParams) export type GaussianDensityProps = typeof DefaultGaussianDensityProps function getConformation(unit: Unit) { diff --git a/src/mol-view/parameter.ts b/src/mol-view/parameter.ts index 93c95f649..eec8b7a9c 100644 --- a/src/mol-view/parameter.ts +++ b/src/mol-view/parameter.ts @@ -5,6 +5,7 @@ */ import { Color } from 'mol-util/color'; +import { Structure } from 'mol-model/structure'; export interface BaseParam<T> { label: string @@ -30,11 +31,11 @@ export function MultiSelectParam<E extends string, T = E[]>(label: string, descr return { type: 'multi-select', label, description, defaultValue, options } } -export interface CheckboxParam extends BaseParam<boolean> { - type: 'checkbox' +export interface BooleanParam extends BaseParam<boolean> { + type: 'boolean' } -export function CheckboxParam(label: string, description: string, defaultValue: boolean): CheckboxParam { - return { type: 'checkbox', label, description, defaultValue } +export function BooleanParam(label: string, description: string, defaultValue: boolean): BooleanParam { + return { type: 'boolean', label, description, defaultValue } } export interface RangeParam extends BaseParam<number> { @@ -73,9 +74,18 @@ export function NumberParam(label: string, description: string, defaultValue: nu return { type: 'number', label, description, defaultValue, min, max, step } } -export type Param = SelectParam<any> | MultiSelectParam<any> | CheckboxParam | RangeParam | TextParam | ColorParam | NumberParam +export interface StructureParam extends BaseParam<Structure> { + type: 'structure' +} +export function StructureParam(label: string, description: string, defaultValue: Structure): StructureParam { + return { type: 'structure', label, description, defaultValue } +} + +export type Param = SelectParam<any> | MultiSelectParam<any> | BooleanParam | RangeParam | TextParam | ColorParam | NumberParam | StructureParam + +export type Params = { [k: string]: Param } -export function paramDefaultValues<T extends { [k: string]: Param }>(params: T) { +export function paramDefaultValues<T extends Params>(params: T) { const d: { [k: string]: any } = {} Object.keys(params).forEach(k => d[k] = params[k].defaultValue) return d as { [k in keyof T]: T[k]['defaultValue'] } diff --git a/src/mol-view/stage.ts b/src/mol-view/stage.ts deleted file mode 100644 index 00e35a60c..000000000 --- a/src/mol-view/stage.ts +++ /dev/null @@ -1,188 +0,0 @@ -/** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import Viewer from './viewer' -import { StateContext } from './state/context'; -import { Progress } from 'mol-task'; -import { MmcifUrlToModel, ModelToStructure, StructureToSpacefill, StructureToBallAndStick, StructureToDistanceRestraint, StructureToCartoon, StructureToBackbone, StructureCenter, StructureToCarbohydrate } from './state/transform'; -import { UrlEntity } from './state/entity'; -import { SpacefillProps } from 'mol-geo/representation/structure/representation/spacefill'; -import { Context } from 'mol-app/context/context'; -import { BallAndStickProps } from 'mol-geo/representation/structure/representation/ball-and-stick'; -import { CartoonProps } from 'mol-geo/representation/structure/representation/cartoon'; -import { DistanceRestraintProps } from 'mol-geo/representation/structure/representation/distance-restraint'; -import { BackboneProps } from 'mol-geo/representation/structure/representation/backbone'; -import { CarbohydrateProps } from 'mol-geo/representation/structure/representation/carbohydrate'; -// import { Queries as Q, StructureProperties as SP, Query, Selection } from 'mol-model/structure'; - -const spacefillProps: Partial<SpacefillProps> = { - doubleSided: true, - colorTheme: 'chain-id', - sizeTheme: 'physical', - sizeValue: 1.0, - quality: 'auto', - useFog: false -} - -const ballAndStickProps: Partial<BallAndStickProps> = { - doubleSided: true, - colorTheme: 'chain-id', - sizeTheme: 'uniform', - sizeValue: 0.15, - quality: 'auto', - useFog: false -} - -const distanceRestraintProps: Partial<DistanceRestraintProps> = { - doubleSided: true, - colorTheme: 'cross-link', - sizeTheme: 'uniform', - sizeValue: 0.6, - quality: 'auto', - useFog: false -} - -const backboneProps: Partial<BackboneProps> = { - doubleSided: true, - colorTheme: 'chain-id', - sizeTheme: 'uniform', - sizeValue: 0.3, - quality: 'auto', - useFog: false, - alpha: 0.5 -} - -const cartoonProps: Partial<CartoonProps> = { - doubleSided: true, - colorTheme: 'chain-id', - sizeTheme: 'uniform', - sizeValue: 0.13, - aspectRatio: 8, - quality: 'auto', - useFog: false -} - -const carbohydrateProps: Partial<CarbohydrateProps> = { - doubleSided: true, - colorTheme: 'carbohydrate-symbol', - sizeTheme: 'uniform', - sizeValue: 1, - quality: 'highest', - useFog: false -} - -export class Stage { - viewer: Viewer - ctx = new StateContext(Progress.format) - - constructor(public globalContext: Context) { - - } - - initRenderer (canvas: HTMLCanvasElement, container: HTMLDivElement) { - this.viewer = Viewer.create(canvas, container) - this.viewer.animate() - this.ctx.viewer = this.viewer - - // this.loadPdbid('1jj2') - // this.loadPdbid('1grm') // helix-like sheets - // this.loadPdbid('4umt') // ligand has bond with order 3 - // this.loadPdbid('1crn') // small - // this.loadPdbid('1hrv') // viral assembly - // this.loadPdbid('1rb8') // virus - // this.loadPdbid('1blu') // metal coordination - // this.loadPdbid('3pqr') // inter unit bonds, two polymer chains, ligands, water, carbohydrates linked to protein - // this.loadPdbid('4v5a') // ribosome - // this.loadPdbid('3j3q') // ... - // this.loadPdbid('2np2') // dna - // this.loadPdbid('1d66') // dna - // this.loadPdbid('9dna') // A form dna - // this.loadPdbid('1bna') // B form dna - // this.loadPdbid('199d') // C form dna - // this.loadPdbid('4lb6') // Z form dna - // this.loadPdbid('1egk') // 4-way dna-rna junction - // this.loadPdbid('1y26') // rna - // this.loadPdbid('1xv6') // rna, modified nucleotides - // this.loadPdbid('3bbm') // rna with linker - // this.loadPdbid('1euq') // t-rna - // this.loadPdbid('2e2i') // rna, dna, protein - // this.loadPdbid('1gfl') // GFP, flourophore has carbonyl oxygen removed - // this.loadPdbid('1sfi') // contains cyclic peptid - // this.loadPdbid('3sn6') // discontinuous chains - // this.loadPdbid('2zex') // contains carbohydrate polymer - // this.loadPdbid('3sgj') // contains carbohydrate polymer - // this.loadPdbid('3ina') // contains GlcN and IdoA - // this.loadPdbid('1umz') // contains Xyl (Xyloglucan) - // this.loadPdbid('1mfb') // contains Abe - // this.loadPdbid('2gdu') // contains sucrose - // this.loadPdbid('2fnc') // contains maltotriose - // this.loadPdbid('4zs9') // contains raffinose - // this.loadPdbid('2yft') // contains kestose - // this.loadPdbid('2b5t') // contains large carbohydrate polymer - // this.loadPdbid('1b5f') // contains carbohydrate with alternate locations - // this.loadMmcifUrl(`../../examples/1cbs_full.bcif`) - // this.loadMmcifUrl(`../../examples/1cbs_updated.cif`) - // this.loadMmcifUrl(`../../examples/1crn.cif`) - // this.loadPdbid('5u0q') // mixed dna/rna in same polymer - // this.loadPdbid('1xj9') // PNA (peptide nucleic acid) - // this.loadPdbid('5eme') // PNA (peptide nucleic acid) and RNA - this.loadPdbid('2X3T') // temp - - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000001.cif`) // ok - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000002.cif`) // ok - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000003.cif`) // ok - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000004.cif`) // TODO issue with cross-link extraction, not shown - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000005.cif`) // ok - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000006.cif`) // ok - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000007.cif`) // ok - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000008.cif`) // ok - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000009.cif`) // TODO sequence related error - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000010.cif`) // ok - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000011.cif`) // ok - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000012.cif`) // ok - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000014.cif`) // ok - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000015.cif`) // TODO sequence related error - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000016.cif`) // ok - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000017.cif`) // ok - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000020.cif`) // ok - // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000021.cif`) // ok - } - - async loadMmcifUrl (url: string) { - const urlEntity = UrlEntity.ofUrl(this.ctx, url) - const modelEntity = await MmcifUrlToModel.apply(this.ctx, urlEntity) - const structureEntity = await ModelToStructure.apply(this.ctx, modelEntity) - - StructureToBallAndStick.apply(this.ctx, structureEntity, { ...ballAndStickProps, visible: true }) - StructureToSpacefill.apply(this.ctx, structureEntity, { ...spacefillProps, visible: false }) - StructureToDistanceRestraint.apply(this.ctx, structureEntity, { ...distanceRestraintProps, visible: true }) - StructureToBackbone.apply(this.ctx, structureEntity, { ...backboneProps, visible: false }) - StructureToCartoon.apply(this.ctx, structureEntity, { ...cartoonProps, visible: true }) - StructureToCarbohydrate.apply(this.ctx, structureEntity, { ...carbohydrateProps, visible: true }) - StructureCenter.apply(this.ctx, structureEntity) - - this.globalContext.components.sequenceView.setState({ structure: structureEntity.value }); - - // const structureEntity2 = await ModelToStructure.apply(this.ctx, modelEntity) - // const q1 = Q.generators.atoms({ - // residueTest: l => SP.residue.label_seq_id(l) < 7 - // }); - // structureEntity2.value = Selection.unionStructure(await Query(q1)(structureEntity2.value).run()); - // await StructureToBackbone.apply(this.ctx, structureEntity2, { ...backboneProps, visible: true }) - // await StructureToCartoon.apply(this.ctx, structureEntity2, { ...cartoonProps, visible: true }) - // await StructureToBallAndStick.apply(this.ctx, structureEntity2, { ...ballAndStickProps, visible: false }) - // StructureCenter.apply(this.ctx, structureEntity2) - } - - loadPdbid (pdbid: string) { - // return this.loadMmcifUrl(`http://www.ebi.ac.uk/pdbe/static/entry/${pdbid}_updated.cif`) - return this.loadMmcifUrl(`https://files.rcsb.org/download/${pdbid}.cif`) - } - - dispose () { - // TODO - } -} \ No newline at end of file -- GitLab