mirror of
https://git.rwth-aachen.de/acs/public/villas/web/
synced 2025-03-09 00:00:01 +01:00
Merge branch 'develop' into '120-add-full-screen-support-for-visualizations'
# Conflicts: # src/styles/app.css
This commit is contained in:
commit
2eec87e0d3
63 changed files with 11839 additions and 357 deletions
13
Dockerfile
13
Dockerfile
|
@ -4,10 +4,17 @@ FROM node:8.2
|
|||
RUN mkdir -p /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# use changes to package.json to force Docker not to use the cache
|
||||
# when we change our application's nodejs dependencies:
|
||||
ADD package.json /usr/src/app
|
||||
RUN npm install
|
||||
|
||||
# Install app dependencies
|
||||
COPY . /usr/src/app
|
||||
RUN npm install && npm run build
|
||||
RUN npm run build
|
||||
|
||||
VOLUME /usr/src/app/build
|
||||
# Run the app in a local webserver
|
||||
RUN npm install -g serve
|
||||
EXPOSE 5000
|
||||
|
||||
CMD [ "true" ]
|
||||
CMD [ "serve", "-s", "build" ]
|
||||
|
|
9364
package-lock.json
generated
Normal file
9364
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -12,6 +12,7 @@
|
|||
"d3-shape": "^1.2.0",
|
||||
"d3-time-format": "^2.0.5",
|
||||
"es6-promise": "^4.0.5",
|
||||
"file-saver": "^1.3.3",
|
||||
"flux": "^3.1.2",
|
||||
"gaugeJS": "^1.3.2",
|
||||
"immutable": "^3.8.1",
|
||||
|
@ -28,11 +29,11 @@
|
|||
"react-router": "^4.1.2",
|
||||
"react-router-dom": "^4.1.2",
|
||||
"react-sortable-tree": "^0.1.19",
|
||||
"superagent": "^3.5.0"
|
||||
"superagent": "^3.5.0",
|
||||
"react-scripts": "1.0.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.1.0",
|
||||
"react-scripts": "1.0.10"
|
||||
"chai": "^4.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
|
|
@ -12,6 +12,7 @@ import EditWidgetSignalsControl from '../../../components/dialog/edit-widget-sig
|
|||
import EditWidgetOrientation from '../../../components/dialog/edit-widget-orientation';
|
||||
import EditWidgetTextSizeControl from '../../../components/dialog/edit-widget-text-size-control';
|
||||
import EditWidgetAspectControl from '../../../components/dialog/edit-widget-aspect-control';
|
||||
import EditWidgetCheckboxControl from '../../../components/dialog/edit-widget-checkbox-control';
|
||||
|
||||
describe('edit widget control creator', () => {
|
||||
it('should not return null', () => {
|
||||
|
@ -20,7 +21,7 @@ describe('edit widget control creator', () => {
|
|||
});
|
||||
|
||||
var runs = [
|
||||
{ args: { widgetType: 'Value' }, result: { controlNumber: 4, controlTypes: [EditWidgetTextControl, EditWidgetSimulatorControl, EditWidgetSignalControl, EditWidgetTextSizeControl] } },
|
||||
{ args: { widgetType: 'Value' }, result: { controlNumber: 5, controlTypes: [EditWidgetTextControl, EditWidgetSimulatorControl, EditWidgetSignalControl, EditWidgetTextSizeControl, EditWidgetCheckboxControl] } },
|
||||
{ args: { widgetType: 'Plot' }, result: { controlNumber: 4, controlTypes: [EditWidgetTimeControl, EditWidgetSimulatorControl, EditWidgetSignalsControl, EditWidgetTextControl] } },
|
||||
{ args: { widgetType: 'Table' }, result: { controlNumber: 1, controlTypes: [EditWidgetSimulatorControl] } },
|
||||
{ args: { widgetType: 'Image' }, result: { controlNumber: 2, controlTypes: [EditImageWidgetControl, EditWidgetAspectControl] } },
|
||||
|
|
|
@ -128,9 +128,9 @@ class RestAPI {
|
|||
});
|
||||
}
|
||||
|
||||
upload(url, data, token) {
|
||||
upload(url, data, token, progressCallback) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var req = request.post(url).send(data);
|
||||
const req = request.post(url).send(data).on('progress', progressCallback);
|
||||
|
||||
if (token != null) {
|
||||
req.set('x-access-token', token);
|
||||
|
|
|
@ -23,17 +23,26 @@ import React from 'react';
|
|||
import { Modal, Button } from 'react-bootstrap';
|
||||
|
||||
class Dialog extends React.Component {
|
||||
closeModal() {
|
||||
closeModal = (event) => {
|
||||
this.props.onClose(false);
|
||||
}
|
||||
|
||||
cancelModal() {
|
||||
cancelModal = (event) => {
|
||||
this.props.onClose(true);
|
||||
}
|
||||
|
||||
onKeyPress = (event) => {
|
||||
/*if (event.key === 'Enter') {
|
||||
// prevent input from submitting
|
||||
event.preventDefault();
|
||||
|
||||
this.closeModal(false);
|
||||
}*/
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Modal show={this.props.show} onEnter={this.props.onReset}>
|
||||
<Modal keyboard show={this.props.show} onEnter={this.props.onReset} onHide={this.cancelModal} onKeyPress={this.onKeyPress}>
|
||||
<Modal.Header>
|
||||
<Modal.Title>{this.props.title}</Modal.Title>
|
||||
</Modal.Header>
|
||||
|
@ -43,8 +52,8 @@ class Dialog extends React.Component {
|
|||
</Modal.Body>
|
||||
|
||||
<Modal.Footer>
|
||||
<Button onClick={() => this.cancelModal()}>Cancel</Button>
|
||||
<Button bsStyle="primary" type="submit" onClick={() => this.closeModal()} disabled={!this.props.valid}>{this.props.buttonTitle}</Button>
|
||||
<Button onClick={this.cancelModal}>Cancel</Button>
|
||||
<Button onClick={this.closeModal} disabled={!this.props.valid}>{this.props.buttonTitle}</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -41,7 +41,9 @@ class NewNodeDialog extends React.Component {
|
|||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
if (this.valid) {
|
||||
this.props.onClose(this.state);
|
||||
}
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
|
|
@ -39,7 +39,9 @@ class EditProjectDialog extends React.Component {
|
|||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
if (this.valid) {
|
||||
this.props.onClose(this.state);
|
||||
}
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
|
|
@ -42,7 +42,9 @@ class EditSimulationModelDialog extends React.Component {
|
|||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
if (this.valid) {
|
||||
this.props.onClose(this.state);
|
||||
}
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
|
|
@ -38,7 +38,9 @@ class EditSimulationDialog extends React.Component {
|
|||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
if (this.valid) {
|
||||
this.props.onClose(this.state);
|
||||
}
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
|
|
@ -37,7 +37,9 @@ class EditSimulatorDialog extends React.Component {
|
|||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
if (this.valid) {
|
||||
this.props.onClose(this.state);
|
||||
}
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
|
|
@ -40,7 +40,9 @@ class EditUserDialog extends React.Component {
|
|||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
if (this.valid) {
|
||||
this.props.onClose(this.state);
|
||||
}
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
|
|
@ -38,7 +38,9 @@ class EditVisualizationDialog extends React.Component {
|
|||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
if (this.valid) {
|
||||
this.props.onClose(this.state);
|
||||
}
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
|
45
src/components/dialog/edit-widget-checkbox-control.js
Normal file
45
src/components/dialog/edit-widget-checkbox-control.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* File: edit-widget-checkbox-control.js
|
||||
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
|
||||
* Date: 19.08.2017
|
||||
*
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { FormGroup, Checkbox } from 'react-bootstrap';
|
||||
|
||||
class EditWidgetCheckboxControl extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
widget: {}
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.setState({ widget: nextProps.widget });
|
||||
}
|
||||
|
||||
render() {
|
||||
return <FormGroup>
|
||||
<Checkbox id={this.props.controlId} checked={this.state.widget[this.props.controlId] || ''} onChange={e => this.props.handleChange(e)}>{this.props.text}</Checkbox>
|
||||
</FormGroup>;
|
||||
}
|
||||
}
|
||||
|
||||
export default EditWidgetCheckboxControl;
|
131
src/components/dialog/edit-widget-color-zones-control.js
Normal file
131
src/components/dialog/edit-widget-color-zones-control.js
Normal file
|
@ -0,0 +1,131 @@
|
|||
/**
|
||||
* File: edit-widget-color-zones-control.js
|
||||
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
|
||||
* Date: 20.08.2017
|
||||
*
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { FormGroup, ControlLabel, Button, Glyphicon } from 'react-bootstrap';
|
||||
|
||||
import Table from '../table';
|
||||
import TableColumn from '../table-column';
|
||||
|
||||
class EditWidgetColorZonesControl extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
widget: {
|
||||
zones: []
|
||||
},
|
||||
selectedZones: []
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.setState({ widget: nextProps.widget });
|
||||
}
|
||||
|
||||
addZone = () => {
|
||||
// add row
|
||||
const widget = this.state.widget;
|
||||
widget.zones.push({ strokeStyle: 'ffffff', min: 0, max: 100 });
|
||||
|
||||
this.setState({ widget });
|
||||
|
||||
this.sendEvent(widget);
|
||||
}
|
||||
|
||||
removeZones = () => {
|
||||
// remove zones
|
||||
const widget = this.state.widget;
|
||||
|
||||
this.state.selectedZones.forEach(row => {
|
||||
widget.zones.splice(row, 1);
|
||||
});
|
||||
|
||||
this.setState({ selectedZones: [], widget });
|
||||
|
||||
this.sendEvent(widget);
|
||||
}
|
||||
|
||||
changeCell = (event, row, column) => {
|
||||
// change row
|
||||
const widget = this.state.widget;
|
||||
|
||||
if (column === 1) {
|
||||
widget.zones[row].strokeStyle = event.target.value;
|
||||
} else if (column === 2) {
|
||||
widget.zones[row].min = event.target.value;
|
||||
} else if (column === 3) {
|
||||
widget.zones[row].max = event.target.value;
|
||||
}
|
||||
|
||||
this.setState({ widget });
|
||||
|
||||
this.sendEvent(widget);
|
||||
}
|
||||
|
||||
sendEvent(widget) {
|
||||
// create event
|
||||
const event = {
|
||||
target: {
|
||||
id: 'zones',
|
||||
value: widget.zones
|
||||
}
|
||||
};
|
||||
|
||||
this.props.handleChange(event);
|
||||
}
|
||||
|
||||
checkedCell = (row, event) => {
|
||||
// update selected rows
|
||||
const selectedZones = this.state.selectedZones;
|
||||
|
||||
if (event.target.checked) {
|
||||
if (selectedZones.indexOf(row) === -1) {
|
||||
selectedZones.push(row);
|
||||
}
|
||||
} else {
|
||||
let index = selectedZones.indexOf(row);
|
||||
if (row > -1) {
|
||||
selectedZones.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ selectedZones });
|
||||
}
|
||||
|
||||
render() {
|
||||
return <FormGroup>
|
||||
<ControlLabel>Color zones</ControlLabel>
|
||||
|
||||
<Table data={this.state.widget.zones}>
|
||||
<TableColumn width="20" checkbox onChecked={this.checkedCell} />
|
||||
<TableColumn title="Color" dataKey="strokeStyle" inlineEditable onInlineChange={this.changeCell} />
|
||||
<TableColumn title="Minimum" dataKey="min" inlineEditable onInlineChange={this.changeCell} />
|
||||
<TableColumn title="Maximum" dataKey="max" inlineEditable onInlineChange={this.changeCell} />
|
||||
</Table>
|
||||
|
||||
<Button onClick={this.addZone} disabled={!this.props.widget.colorZones}><Glyphicon glyph="plus" /> Add</Button>
|
||||
<Button onClick={this.removeZones} disabled={!this.props.widget.colorZones}><Glyphicon glyph="minus" /> Remove</Button>
|
||||
</FormGroup>;
|
||||
}
|
||||
}
|
||||
|
||||
export default EditWidgetColorZonesControl;
|
|
@ -31,6 +31,10 @@ import EditWidgetSignalsControl from './edit-widget-signals-control';
|
|||
import EditWidgetOrientation from './edit-widget-orientation';
|
||||
import EditWidgetAspectControl from './edit-widget-aspect-control';
|
||||
import EditWidgetTextSizeControl from './edit-widget-text-size-control';
|
||||
import EditWidgetCheckboxControl from './edit-widget-checkbox-control';
|
||||
import EditWidgetColorZonesControl from './edit-widget-color-zones-control';
|
||||
import EditWidgetMinMaxControl from './edit-widget-min-max-control';
|
||||
import EditWidgetHTMLContent from './edit-widget-html-content';
|
||||
|
||||
export default function createControls(widgetType = null, widget = null, sessionToken = null, files = null, validateForm, simulation, handleChange) {
|
||||
// Use a list to concatenate the controls according to the widget type
|
||||
|
@ -42,10 +46,11 @@ export default function createControls(widgetType = null, widget = null, session
|
|||
handleChange([e, {target: {id: 'signal', value: 0}}]);
|
||||
}
|
||||
dialogControls.push(
|
||||
<EditWidgetTextControl key={1} widget={widget} controlId={'name'} label={'Text'} placeholder={'Enter text'} validate={id => validateForm(id)} handleChange={e => handleChange(e)} />,
|
||||
<EditWidgetSimulatorControl key={2} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => valueBoundOnChange(e)} />,
|
||||
<EditWidgetSignalControl key={3} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetTextSizeControl key={4} widget={widget} handleChange={e => handleChange(e)} />
|
||||
<EditWidgetTextControl key={0} widget={widget} controlId={'name'} label={'Text'} placeholder={'Enter text'} validate={id => validateForm(id)} handleChange={e => handleChange(e)} />,
|
||||
<EditWidgetSimulatorControl key={1} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => valueBoundOnChange(e)} />,
|
||||
<EditWidgetSignalControl key={2} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetTextSizeControl key={3} widget={widget} handleChange={e => handleChange(e)} />,
|
||||
<EditWidgetCheckboxControl key={4} widget={widget} controlId={'showUnit'} text="Show unit" handleChange={e => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
case 'Plot':
|
||||
|
@ -53,21 +58,22 @@ export default function createControls(widgetType = null, widget = null, session
|
|||
handleChange([e, {target: {id: 'signals', value: []}}]);
|
||||
}
|
||||
dialogControls.push(
|
||||
<EditWidgetTimeControl key={1} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetSimulatorControl key={2} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => plotBoundOnChange(e)} />,
|
||||
<EditWidgetSignalsControl key={3} controlId={'signals'} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetTextControl key={4} controlId={'ylabel'} label={'Y-Axis name'} placeholder={'Enter a name for the y-axis'} widget={widget} handleChange={(e) => handleChange(e)} />
|
||||
<EditWidgetTimeControl key={0} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetSimulatorControl key={1} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => plotBoundOnChange(e)} />,
|
||||
<EditWidgetSignalsControl key={2} controlId={'signals'} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetTextControl key={3} controlId={'ylabel'} label={'Y-Axis name'} placeholder={'Enter a name for the y-axis'} widget={widget} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetMinMaxControl key={4} widget={widget} controlId="y" handleChange={e => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
case 'Table':
|
||||
dialogControls.push(
|
||||
<EditWidgetSimulatorControl key={1} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />
|
||||
<EditWidgetSimulatorControl key={0} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
case 'Image':
|
||||
dialogControls.push(
|
||||
<EditImageWidgetControl key={1} sessionToken={sessionToken} widget={widget} files={files} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetAspectControl key={2} widget={widget} handleChange={e => handleChange(e)} />
|
||||
<EditImageWidgetControl key={0} sessionToken={sessionToken} widget={widget} files={files} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetAspectControl key={1} widget={widget} handleChange={e => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
case 'Gauge':
|
||||
|
@ -75,9 +81,12 @@ export default function createControls(widgetType = null, widget = null, session
|
|||
handleChange([e, {target: {id: 'signal', value: ''}}]);
|
||||
}
|
||||
dialogControls.push(
|
||||
<EditWidgetTextControl key={1} widget={widget} controlId={'name'} label={'Text'} placeholder={'Enter text'} validate={id => validateForm(id)} handleChange={e => handleChange(e)} />,
|
||||
<EditWidgetSimulatorControl key={2} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => gaugeBoundOnChange(e) } />,
|
||||
<EditWidgetSignalControl key={3} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />
|
||||
<EditWidgetTextControl key={0} widget={widget} controlId={'name'} label={'Text'} placeholder={'Enter text'} validate={id => validateForm(id)} handleChange={e => handleChange(e)} />,
|
||||
<EditWidgetSimulatorControl key={1} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => gaugeBoundOnChange(e) } />,
|
||||
<EditWidgetSignalControl key={2} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetCheckboxControl key={3} widget={widget} controlId="colorZones" text="Show color zones" handleChange={e => handleChange(e)} />,
|
||||
<EditWidgetColorZonesControl key={4} widget={widget} handleChange={e => handleChange(e)} />,
|
||||
<EditWidgetMinMaxControl key={5} widget={widget} controlId="value" handleChange={e => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
case 'PlotTable':
|
||||
|
@ -85,25 +94,27 @@ export default function createControls(widgetType = null, widget = null, session
|
|||
handleChange([e, {target: {id: 'preselectedSignals', value: []}}]);
|
||||
}
|
||||
dialogControls.push(
|
||||
<EditWidgetSimulatorControl key={1} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => plotTableBoundOnChange(e)} />,
|
||||
<EditWidgetSignalsControl key={2} controlId={'preselectedSignals'} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetTextControl key={3} controlId={'ylabel'} label={'Y-Axis'} placeholder={'Enter a name for the Y-axis'} widget={widget} handleChange={(e) => handleChange(e)} />
|
||||
<EditWidgetSimulatorControl key={0} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => plotTableBoundOnChange(e)} />,
|
||||
<EditWidgetSignalsControl key={1} controlId={'preselectedSignals'} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetTextControl key={2} controlId={'ylabel'} label={'Y-Axis'} placeholder={'Enter a name for the Y-axis'} widget={widget} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetTimeControl key={3} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetMinMaxControl key={4} widget={widget} controlId="y" handleChange={e => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
case 'Slider':
|
||||
dialogControls.push(
|
||||
<EditWidgetOrientation key={1} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />
|
||||
<EditWidgetOrientation key={0} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
case 'Button':
|
||||
dialogControls.push(
|
||||
<EditWidgetColorControl key={1} widget={widget} controlId={'background_color'} label={'Background'} validate={(id) => validateForm(id)} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetColorControl key={2} widget={widget} controlId={'font_color'} label={'Font color'} validate={(id) => validateForm(id)} handleChange={(e) => handleChange(e)} />
|
||||
<EditWidgetColorControl key={0} widget={widget} controlId={'background_color'} label={'Background'} validate={(id) => validateForm(id)} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetColorControl key={1} widget={widget} controlId={'font_color'} label={'Font color'} validate={(id) => validateForm(id)} handleChange={(e) => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
case 'Box':
|
||||
dialogControls.push(
|
||||
<EditWidgetColorControl key={1} widget={widget} controlId={'border_color'} label={'Border color'} validate={(id) => validateForm(id)} handleChange={(e) => handleChange(e)} />
|
||||
<EditWidgetColorControl key={0} widget={widget} controlId={'border_color'} label={'Border color'} validate={(id) => validateForm(id)} handleChange={(e) => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
case 'Label':
|
||||
|
@ -113,6 +124,13 @@ export default function createControls(widgetType = null, widget = null, session
|
|||
<EditWidgetColorControl key={2} widget={widget} controlId={'fontColor'} label={'Text color'} handleChange={e => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
|
||||
case 'HTML':
|
||||
dialogControls.push(
|
||||
<EditWidgetHTMLContent key={0} widget={widget} placeholder='HTML Code' controlId='content' handleChange={e => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log('Non-valid widget type: ' + widgetType);
|
||||
}
|
||||
|
|
47
src/components/dialog/edit-widget-html-content.js
Normal file
47
src/components/dialog/edit-widget-html-content.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* File: edit-widget-html-content.js
|
||||
* Author: Ricardo Hernandez-Montoya <rhernandez@gridhound.de>
|
||||
* Date: 03.09.2017
|
||||
*
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
**********************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
|
||||
|
||||
class EditWidgetHTMLContent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
widget: {}
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// Update state's widget with props
|
||||
this.setState({ widget: nextProps.widget });
|
||||
}
|
||||
|
||||
render() {
|
||||
return <FormGroup controlId={this.props.controlId}>
|
||||
<ControlLabel>HTML Content</ControlLabel>
|
||||
<FormControl componentClass="textarea" style={{ height: 200 }} placeholder={this.props.placeholder} value={this.state.widget[this.props.controlId] || ''} onChange={e => this.props.handleChange(e)} />
|
||||
</FormGroup>;
|
||||
}
|
||||
}
|
||||
|
||||
export default EditWidgetHTMLContent;
|
|
@ -19,12 +19,12 @@
|
|||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { FormGroup, FormControl, ControlLabel, Button } from 'react-bootstrap';
|
||||
import React from 'react';
|
||||
import { FormGroup, FormControl, ControlLabel, Button, ProgressBar } from 'react-bootstrap';
|
||||
|
||||
import AppDispatcher from '../../app-dispatcher';
|
||||
|
||||
class EditImageWidgetControl extends Component {
|
||||
class EditImageWidgetControl extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
@ -32,7 +32,8 @@ class EditImageWidgetControl extends Component {
|
|||
widget: {
|
||||
file: ''
|
||||
},
|
||||
fileList: null
|
||||
fileList: null,
|
||||
progress: 0
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -40,11 +41,11 @@ class EditImageWidgetControl extends Component {
|
|||
this.setState({ widget: nextProps.widget });
|
||||
}
|
||||
|
||||
startFileUpload() {
|
||||
startFileUpload = () => {
|
||||
// get selected file
|
||||
var formData = new FormData();
|
||||
let formData = new FormData();
|
||||
|
||||
for (var key in this.state.fileList) {
|
||||
for (let key in this.state.fileList) {
|
||||
if (this.state.fileList.hasOwnProperty(key) && this.state.fileList[key] instanceof File) {
|
||||
formData.append(key, this.state.fileList[key]);
|
||||
}
|
||||
|
@ -54,39 +55,46 @@ class EditImageWidgetControl extends Component {
|
|||
AppDispatcher.dispatch({
|
||||
type: 'files/start-upload',
|
||||
data: formData,
|
||||
token: this.props.sessionToken
|
||||
token: this.props.sessionToken,
|
||||
progressCallback: this.uploadProgress,
|
||||
finishedCallback: this.clearProgress
|
||||
});
|
||||
}
|
||||
|
||||
uploadProgress = (e) => {
|
||||
this.setState({ progress: Math.round(e.percent) });
|
||||
}
|
||||
|
||||
clearProgress = () => {
|
||||
this.setState({ progress: 0 });
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<FormGroup controlId="file">
|
||||
<ControlLabel>Image</ControlLabel>
|
||||
<FormControl componentClass="select" value={this.state.widget.file} onChange={(e) => this.props.handleChange(e)}>
|
||||
{
|
||||
this.props.files.length === 0? (
|
||||
<option disabled value style={{ display: 'none' }}>No images found, please upload one first.</option>
|
||||
) : (
|
||||
this.props.files.reduce( (entries, file, index) => {
|
||||
entries.push(<option key={++index} value={file._id}>{file.name}</option>);
|
||||
return entries;
|
||||
}, [
|
||||
<option key={0} value=''>Please select one image</option>
|
||||
])
|
||||
)
|
||||
}
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
return <div>
|
||||
<FormGroup controlId="file">
|
||||
<ControlLabel>Image</ControlLabel>
|
||||
<FormControl componentClass="select" value={this.state.widget.file} onChange={(e) => this.props.handleChange(e)}>
|
||||
{this.props.files.length === 0 ? (
|
||||
<option disabled value style={{ display: 'none' }}>No images found, please upload one first.</option>
|
||||
) : (
|
||||
this.props.files.reduce((entries, file, index) => {
|
||||
entries.push(<option key={++index} value={file._id}>{file.name}</option>);
|
||||
return entries;
|
||||
}, [
|
||||
<option key={0} value=''>Please select one image</option>
|
||||
])
|
||||
)}
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup controlId="upload">
|
||||
<ControlLabel>Upload</ControlLabel>
|
||||
<FormControl type="file" onChange={(e) => this.setState({ fileList: e.target.files }) } />
|
||||
</FormGroup>
|
||||
<FormGroup controlId="upload">
|
||||
<ControlLabel>Upload</ControlLabel>
|
||||
<FormControl type="file" onChange={(e) => this.setState({ fileList: e.target.files }) } />
|
||||
</FormGroup>
|
||||
|
||||
<Button bsSize="small" onClick={() => this.startFileUpload() }>Upload</Button>
|
||||
</div>
|
||||
);
|
||||
<ProgressBar striped active now={this.state.progress} label={`${this.state.progress}%`} />
|
||||
<Button bsSize="small" onClick={this.startFileUpload}>Upload</Button>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
64
src/components/dialog/edit-widget-min-max-control.js
Normal file
64
src/components/dialog/edit-widget-min-max-control.js
Normal file
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* File: edit-widget-min-max-control.js
|
||||
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
|
||||
* Date: 30.08.2017
|
||||
*
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { FormGroup, FormControl, ControlLabel, Checkbox, Table } from 'react-bootstrap';
|
||||
|
||||
class EditWidgetMinMaxControl extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const widget = {};
|
||||
widget[props.controlID + "UseMinMax"] = false;
|
||||
widget[props.controlId + "Min"] = 0;
|
||||
widget[props.controlId + "Max"] = 1;
|
||||
|
||||
this.state = {
|
||||
widget
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.setState({ widget: nextProps.widget });
|
||||
}
|
||||
|
||||
render() {
|
||||
return <FormGroup>
|
||||
<ControlLabel>{this.props.label}</ControlLabel>
|
||||
<Checkbox id={this.props.controlId + "UseMinMax"} checked={this.state.widget[this.props.controlId + "UseMinMax"] || ''} onChange={e => this.props.handleChange(e)}>Enable min-max</Checkbox>
|
||||
|
||||
<Table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
Min: <FormControl type="number" id={this.props.controlId + "Min"} disabled={!this.state.widget[this.props.controlId + "UseMinMax"]} placeholder="Minimum value" value={this.state.widget[this.props.controlId + 'Min']} onChange={e => this.props.handleChange(e)} />
|
||||
</td>
|
||||
<td>
|
||||
Max: <FormControl type="number" id={this.props.controlId + "Max"} disabled={!this.state.widget[this.props.controlId + "UseMinMax"]} placeholder="Maximum value" value={this.state.widget[this.props.controlId + 'Max']} onChange={e => this.props.handleChange(e)} />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</Table>
|
||||
</FormGroup>;
|
||||
}
|
||||
}
|
||||
|
||||
export default EditWidgetMinMaxControl;
|
|
@ -43,7 +43,9 @@ class EditWidgetDialog extends React.Component {
|
|||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state.temporal);
|
||||
if (this.valid) {
|
||||
this.props.onClose(this.state.temporal);
|
||||
}
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
@ -51,7 +53,7 @@ class EditWidgetDialog extends React.Component {
|
|||
|
||||
assignAspectRatio(changeObject, fileId) {
|
||||
// get aspect ratio of file
|
||||
let file = this.props.files.find(element => element._id === fileId);
|
||||
const file = this.props.files.find(element => element._id === fileId);
|
||||
|
||||
// scale width to match aspect
|
||||
const aspectRatio = file.dimensions.width / file.dimensions.height;
|
||||
|
@ -90,6 +92,10 @@ class EditWidgetDialog extends React.Component {
|
|||
|
||||
// get file and update size
|
||||
changeObject = this.assignAspectRatio(changeObject, e.target.value);
|
||||
} else if (e.target.type === 'checkbox') {
|
||||
changeObject[e.target.id] = e.target.checked;
|
||||
} else if (e.target.type === 'number') {
|
||||
changeObject[e.target.id] = Number(e.target.value);
|
||||
} else {
|
||||
changeObject[e.target.id] = e.target.value;
|
||||
}
|
||||
|
@ -134,11 +140,6 @@ class EditWidgetDialog extends React.Component {
|
|||
return (
|
||||
<Dialog show={this.props.show} title="Edit Widget" buttonTitle="Save" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
|
||||
<form encType='multipart/form-data'>
|
||||
{/*<FormGroup controlId="name" validationState={this.validateForm('name')}>
|
||||
<ControlLabel>Name</ControlLabel>
|
||||
<FormControl type="text" placeholder="Enter name" value={this.state.temporal.name} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>*/}
|
||||
{ controls || '' }
|
||||
</form>
|
||||
</Dialog>
|
||||
|
|
125
src/components/dialog/import-node.js
Normal file
125
src/components/dialog/import-node.js
Normal file
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* File: import-node.js
|
||||
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
|
||||
* Date: 03.09.2017
|
||||
*
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
|
||||
|
||||
import Dialog from './dialog';
|
||||
|
||||
class ImportNodeDialog extends React.Component {
|
||||
valid = false;
|
||||
imported = false;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
name: '',
|
||||
endpoint: '',
|
||||
simulators: []
|
||||
};
|
||||
}
|
||||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
this.setState({ [e.target.id]: e.target.value });
|
||||
}
|
||||
|
||||
resetState() {
|
||||
this.setState({ name: '', endpoint: '' });
|
||||
|
||||
this.imported = false;
|
||||
}
|
||||
|
||||
loadFile(fileList) {
|
||||
// get file
|
||||
const file = fileList[0];
|
||||
if (!file.type.match('application/json')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create file reader
|
||||
var reader = new FileReader();
|
||||
var self = this;
|
||||
|
||||
reader.onload = function(event) {
|
||||
// read simulator
|
||||
const node = JSON.parse(event.target.result);
|
||||
self.imported = true;
|
||||
self.setState({ name: node.name, endpoint: node.endpoint, simulators: node.simulators });
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
validateForm(target) {
|
||||
// check all controls
|
||||
let endpoint = true;
|
||||
let name = true;
|
||||
|
||||
if (this.state.name === '' || this.props.nodes.find(node => node.name === this.state.name) !== undefined) {
|
||||
name = false;
|
||||
}
|
||||
|
||||
if (this.state.endpoint === '' || this.props.nodes.find(node => node.endpoint === this.state.endpoint) !== undefined) {
|
||||
endpoint = false;
|
||||
}
|
||||
|
||||
this.valid = endpoint && name;
|
||||
|
||||
// return state to control
|
||||
if (target === 'name') return name ? "success" : "error";
|
||||
else return endpoint ? "success" : "error";
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Dialog show={this.props.show} title="Import Simulator" buttonTitle="Import" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
|
||||
<form>
|
||||
<FormGroup controlId="file">
|
||||
<ControlLabel>Simulator File</ControlLabel>
|
||||
<FormControl type="file" onChange={(e) => this.loadFile(e.target.files)} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup controlId="name" validationState={this.validateForm('name')}>
|
||||
<ControlLabel>Name</ControlLabel>
|
||||
<FormControl readOnly={!this.imported} type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
<FormGroup controlId="endpoint" validationState={this.validateForm('endpoint')}>
|
||||
<ControlLabel>Endpoint</ControlLabel>
|
||||
<FormControl readOnly={!this.imported} type="text" placeholder="Enter endpoint" value={this.state.endpoint} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ImportNodeDialog;
|
189
src/components/dialog/import-simulation-model.js
Normal file
189
src/components/dialog/import-simulation-model.js
Normal file
|
@ -0,0 +1,189 @@
|
|||
/**
|
||||
* File: import-simulation-model.js
|
||||
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
|
||||
* Date: 03.09.2017
|
||||
*
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
|
||||
|
||||
import Table from '../table';
|
||||
import TableColumn from '../table-column';
|
||||
import Dialog from './dialog';
|
||||
|
||||
class ImportSimulationModelDialog extends React.Component {
|
||||
valid = false;
|
||||
imported = false;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
name: '',
|
||||
simulator: { node: '', simulator: '' },
|
||||
length: '1',
|
||||
mapping: [ { name: 'Signal', type: 'Type' } ]
|
||||
};
|
||||
}
|
||||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
resetState() {
|
||||
this.setState({
|
||||
name: '',
|
||||
simulator: { node: this.props.nodes[0] ? this.props.nodes[0]._id : '', simulator: this.props.nodes[0].simulators[0] ? 0 : '' },
|
||||
length: '1',
|
||||
mapping: [ { name: 'Signal', type: 'Type' } ]
|
||||
});
|
||||
|
||||
this.imported = false;
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
if (e.target.id === 'length') {
|
||||
// change mapping size
|
||||
if (e.target.value > this.state.mapping.length) {
|
||||
// add missing signals
|
||||
while (this.state.mapping.length < e.target.value) {
|
||||
this.state.mapping.push({ name: 'Signal', type: 'Type' });
|
||||
}
|
||||
} else {
|
||||
// remove signals
|
||||
this.state.mapping.splice(e.target.value, this.state.mapping.length - e.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
if (e.target.id === 'simulator') {
|
||||
this.setState({ simulator: JSON.parse(e.target.value) });
|
||||
} else {
|
||||
this.setState({ [e.target.id]: e.target.value });
|
||||
}
|
||||
}
|
||||
|
||||
handleMappingChange(event, row, column) {
|
||||
var mapping = this.state.mapping;
|
||||
|
||||
if (column === 1) {
|
||||
mapping[row].name = event.target.value;
|
||||
} else if (column === 2) {
|
||||
mapping[row].type = event.target.value;
|
||||
}
|
||||
|
||||
this.setState({ mapping: mapping });
|
||||
}
|
||||
|
||||
loadFile(fileList) {
|
||||
// get file
|
||||
const file = fileList[0];
|
||||
if (!file.type.match('application/json')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create file reader
|
||||
var reader = new FileReader();
|
||||
var self = this;
|
||||
|
||||
reader.onload = function(event) {
|
||||
// read simulator
|
||||
const model = JSON.parse(event.target.result);
|
||||
|
||||
self.imported = true;
|
||||
self.valid = true;
|
||||
self.setState({ name: model.name, mapping: model.mapping, length: model.length, simulator: { node: self.props.nodes[0]._id, simulator: 0 } });
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
validateForm(target) {
|
||||
// check all controls
|
||||
var name = true;
|
||||
var length = true;
|
||||
var simulator = true;
|
||||
|
||||
if (this.state.name === '') {
|
||||
name = false;
|
||||
}
|
||||
|
||||
if (this.state.simulator === '') {
|
||||
simulator = false;
|
||||
}
|
||||
|
||||
// test if simulatorid is a number (in a string, not type of number)
|
||||
if (!/^\d+$/.test(this.state.length)) {
|
||||
length = false;
|
||||
}
|
||||
|
||||
this.valid = name && length && simulator;
|
||||
|
||||
// return state to control
|
||||
if (target === 'name') return name ? "success" : "error";
|
||||
else if (target === 'length') return length ? "success" : "error";
|
||||
else if (target === 'simulator') return simulator ? "success" : "error";
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Dialog show={this.props.show} title="Import Simulation Model" buttonTitle="Import" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
|
||||
<form>
|
||||
<FormGroup controlId="file">
|
||||
<ControlLabel>Simulation Model File</ControlLabel>
|
||||
<FormControl type="file" onChange={(e) => this.loadFile(e.target.files)} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup controlId="name" validationState={this.validateForm('name')}>
|
||||
<ControlLabel>Name</ControlLabel>
|
||||
<FormControl readOnly={!this.imported} type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
<FormGroup controlId="simulator">
|
||||
<ControlLabel>Simulator</ControlLabel>
|
||||
<FormControl readOnly={!this.imported} componentClass="select" placeholder="Select simulator" value={JSON.stringify({ node: this.state.simulator.node, simulator: this.state.simulator.simulator})} onChange={(e) => this.handleChange(e)}>
|
||||
{this.props.nodes.map(node => (
|
||||
node.simulators.map((simulator, index) => (
|
||||
<option key={node._id + index} value={JSON.stringify({ node: node.name, simulator: simulator.name })}>{node.name}/{simulator.name}</option>
|
||||
))
|
||||
))}
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
<FormGroup controlId="length" validationState={this.validateForm('length')}>
|
||||
<ControlLabel>Length</ControlLabel>
|
||||
<FormControl readOnly={!this.imported} type="number" placeholder="Enter length" min="1" value={this.state.length} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
<FormGroup controlId="mapping">
|
||||
<ControlLabel>Mapping</ControlLabel>
|
||||
<Table data={this.state.mapping}>
|
||||
<TableColumn title='ID' width='60' dataIndex />
|
||||
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange(event, row, column)} />
|
||||
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange(event, row, column)} />
|
||||
</Table>
|
||||
</FormGroup>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ImportSimulationModelDialog;
|
140
src/components/dialog/import-simulation.js
Normal file
140
src/components/dialog/import-simulation.js
Normal file
|
@ -0,0 +1,140 @@
|
|||
/**
|
||||
* File: import-simulation.js
|
||||
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
|
||||
* Date: 03.09.2017
|
||||
*
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
|
||||
|
||||
import Dialog from './dialog';
|
||||
|
||||
class ImportSimulationDialog extends React.Component {
|
||||
valid = false;
|
||||
imported = false;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
name: '',
|
||||
models: [],
|
||||
};
|
||||
}
|
||||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
handleChange(e, index) {
|
||||
if (e.target.id === 'simulator') {
|
||||
const models = this.state.models;
|
||||
models[index].simulator = JSON.parse(e.target.value);
|
||||
|
||||
this.setState({ models });
|
||||
} else {
|
||||
this.setState({ [e.target.id]: e.target.value });
|
||||
}
|
||||
}
|
||||
|
||||
resetState() {
|
||||
this.setState({ name: '', models: [] });
|
||||
|
||||
this.imported = false;
|
||||
}
|
||||
|
||||
loadFile(fileList) {
|
||||
// get file
|
||||
const file = fileList[0];
|
||||
if (!file.type.match('application/json')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create file reader
|
||||
var reader = new FileReader();
|
||||
var self = this;
|
||||
|
||||
reader.onload = function(event) {
|
||||
// read simulator
|
||||
const simulation = JSON.parse(event.target.result);
|
||||
simulation.models.forEach(model => {
|
||||
model.simulator = {
|
||||
node: self.props.nodes[0]._id,
|
||||
simulator: 0
|
||||
}
|
||||
});
|
||||
|
||||
self.imported = true;
|
||||
self.setState({ name: simulation.name, models: simulation.models });
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
validateForm(target) {
|
||||
// check all controls
|
||||
let name = true;
|
||||
|
||||
if (this.state.name === '') {
|
||||
name = false;
|
||||
}
|
||||
|
||||
this.valid = name;
|
||||
|
||||
// return state to control
|
||||
if (target === 'name') return name ? "success" : "error";
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Dialog show={this.props.show} title="Import Simulation" buttonTitle="Import" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
|
||||
<form>
|
||||
<FormGroup controlId="file">
|
||||
<ControlLabel>Simulation File</ControlLabel>
|
||||
<FormControl type="file" onChange={(e) => this.loadFile(e.target.files)} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup controlId="name" validationState={this.validateForm('name')}>
|
||||
<ControlLabel>Name</ControlLabel>
|
||||
<FormControl readOnly={!this.imported} type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
|
||||
{this.state.models.map((model, index) => (
|
||||
<FormGroup controlId="simulator" key={index}>
|
||||
<ControlLabel>{model.name} - Simulator</ControlLabel>
|
||||
<FormControl componentClass="select" placeholder="Select simulator" value={JSON.stringify({ node: model.simulator.node, simulator: model.simulator.simulator})} onChange={(e) => this.handleChange(e, index)}>
|
||||
{this.props.nodes.map(node => (
|
||||
node.simulators.map((simulator, index) => (
|
||||
<option key={node._id + index} value={JSON.stringify({ node: node._id, simulator: index })}>{node.name}/{simulator.name}</option>
|
||||
))
|
||||
))}
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
))}
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ImportSimulationDialog;
|
133
src/components/dialog/import-visualization.js
Normal file
133
src/components/dialog/import-visualization.js
Normal file
|
@ -0,0 +1,133 @@
|
|||
/**
|
||||
* File: import-simulator.js
|
||||
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
|
||||
* Date: 04.04.2017
|
||||
*
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
|
||||
|
||||
import Dialog from './dialog';
|
||||
|
||||
class ImportVisualizationDialog extends React.Component {
|
||||
valid = false;
|
||||
imported = false;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
name: '',
|
||||
widgets: [],
|
||||
grid: 0
|
||||
};
|
||||
}
|
||||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
handleChange(e, index) {
|
||||
this.setState({ [e.target.id]: e.target.value });
|
||||
}
|
||||
|
||||
resetState() {
|
||||
this.setState({ name: '', widgets: [], grid: 0 });
|
||||
|
||||
this.imported = false;
|
||||
}
|
||||
|
||||
loadFile(fileList) {
|
||||
// get file
|
||||
const file = fileList[0];
|
||||
if (!file.type.match('application/json')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create file reader
|
||||
var reader = new FileReader();
|
||||
var self = this;
|
||||
|
||||
reader.onload = function(event) {
|
||||
// read simulator
|
||||
const visualization = JSON.parse(event.target.result);
|
||||
|
||||
let defaultSimulator = "";
|
||||
if (self.props.simulation.models != null) {
|
||||
defaultSimulator = self.props.simulation.models[0].simulator;
|
||||
}
|
||||
|
||||
visualization.widgets.forEach(widget => {
|
||||
switch (widget.type) {
|
||||
case 'Value':
|
||||
case 'Plot':
|
||||
case 'Table':
|
||||
case 'PlotTable':
|
||||
case 'Gauge':
|
||||
widget.simulator = defaultSimulator;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
self.imported = true;
|
||||
self.valid = true;
|
||||
self.setState({ name: visualization.name, widgets: visualization.widgets, grid: visualization.grid });
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
validateForm(target) {
|
||||
// check all controls
|
||||
let name = true;
|
||||
|
||||
if (this.state.name === '') {
|
||||
name = false;
|
||||
}
|
||||
|
||||
this.valid = name;
|
||||
|
||||
// return state to control
|
||||
if (target === 'name') return name ? "success" : "error";
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Dialog show={this.props.show} title="Import Visualization" buttonTitle="Import" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
|
||||
<form>
|
||||
<FormGroup controlId="file">
|
||||
<ControlLabel>Visualization File</ControlLabel>
|
||||
<FormControl type="file" onChange={(e) => this.loadFile(e.target.files)} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup controlId="name" validationState={this.validateForm('name')}>
|
||||
<ControlLabel>Name</ControlLabel>
|
||||
<FormControl readOnly={!this.imported} type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ImportVisualizationDialog;
|
|
@ -25,7 +25,7 @@ import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
|
|||
import Dialog from './dialog';
|
||||
|
||||
class NewNodeDialog extends React.Component {
|
||||
valid: false;
|
||||
valid = false;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -40,7 +40,9 @@ class NewNodeDialog extends React.Component {
|
|||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
if (this.valid) {
|
||||
this.props.onClose(this.state);
|
||||
}
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
|
|
@ -38,7 +38,9 @@ class NewProjectDialog extends React.Component {
|
|||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
if (this.valid) {
|
||||
this.props.onClose(this.state);
|
||||
}
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ import TableColumn from '../table-column';
|
|||
import Dialog from './dialog';
|
||||
|
||||
class NewSimulationModelDialog extends React.Component {
|
||||
valid: false;
|
||||
valid = false;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -42,7 +42,9 @@ class NewSimulationModelDialog extends React.Component {
|
|||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
if (this.valid) {
|
||||
this.props.onClose(this.state);
|
||||
}
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
|
|
@ -37,7 +37,9 @@ class NewSimulationDialog extends React.Component {
|
|||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
if (this.valid) {
|
||||
this.props.onClose(this.state);
|
||||
}
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
|
|
@ -37,7 +37,9 @@ class NewSimulatorDialog extends React.Component {
|
|||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
if (this.valid) {
|
||||
this.props.onClose(this.state);
|
||||
}
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
|
|
@ -40,7 +40,9 @@ class NewUserDialog extends React.Component {
|
|||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
if (this.valid) {
|
||||
this.props.onClose(this.state);
|
||||
}
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
|
|
@ -37,7 +37,9 @@ class NewVisualzationDialog extends React.Component {
|
|||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
if (this.valid) {
|
||||
this.props.onClose(this.state);
|
||||
}
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ class Footer extends Component {
|
|||
render() {
|
||||
return (
|
||||
<footer className="app-footer">
|
||||
Copyright © {new Date().getFullYear()}
|
||||
Copyright © {new Date().getFullYear()} - <a href="https://acs.eonerc.rwth-aachen.de">Institute for Automation of Complex Power Systems</a> - <a href="https://www.rwth-aachen.de">RWTH Aachen University</a>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ class SidebarMenu extends React.Component {
|
|||
<li><NavLink to="/simulations" activeClassName="active" title="Simulations">Simulations</NavLink></li>
|
||||
<li><NavLink to="/simulators" activeClassName="active" title="Simulators">Simulators</NavLink></li>
|
||||
{ this.props.currentRole === 'admin' ?
|
||||
<li><NavLink to="/users" activeClassName="active" title="User Management">User Management</NavLink></li> : ''
|
||||
<li><NavLink to="/users" activeClassName="active" title="User Management">Users</NavLink></li> : ''
|
||||
}
|
||||
<li><NavLink to="/logout" title="Logout">Logout</NavLink></li>
|
||||
</ul>
|
||||
|
|
|
@ -47,6 +47,7 @@ class NodeTree extends React.Component {
|
|||
buttons.push(<Button bsSize="small" onClick={() => this.props.onNodeAdd(rowInfo.node)}><Glyphicon glyph="plus" /></Button>);
|
||||
buttons.push(<Button bsSize="small" onClick={() => this.props.onNodeEdit(rowInfo.node)}><Glyphicon glyph="pencil" /></Button>);
|
||||
buttons.push(<Button bsSize="small" onClick={() => this.props.onNodeDelete(rowInfo.node)}><Glyphicon glyph="trash" /></Button>);
|
||||
buttons.push(<Button bsSize="small" onClick={() => this.props.onNodeExport(rowInfo.node)}><Glyphicon glyph="export" /></Button>);
|
||||
} else {
|
||||
// get child index
|
||||
var index = rowInfo.path[1] - rowInfo.path[0] - 1;
|
||||
|
|
|
@ -28,12 +28,15 @@ class TableColumn extends Component {
|
|||
width: null,
|
||||
editButton: false,
|
||||
deleteButton: false,
|
||||
exportButton: false,
|
||||
link: '/',
|
||||
linkKey: '',
|
||||
dataIndex: false,
|
||||
inlineEditable: false,
|
||||
clickable: false,
|
||||
labelKey: null
|
||||
labelKey: null,
|
||||
checkbox: false,
|
||||
checkboxKey: ''
|
||||
};
|
||||
|
||||
render() {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
******************************************************************************/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Table, Button, Glyphicon, FormControl, Label } from 'react-bootstrap';
|
||||
import { Table, Button, Glyphicon, FormControl, Label, Checkbox } from 'react-bootstrap';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
//import TableColumn from './table-column';
|
||||
|
@ -96,6 +96,16 @@ class CustomTable extends Component {
|
|||
cell.push(<Button bsClass='table-control-button' onClick={() => child.props.onDelete(index)}><Glyphicon glyph='remove' /></Button>);
|
||||
}
|
||||
|
||||
if (child.props.checkbox) {
|
||||
const checkboxKey = this.props.checkboxKey;
|
||||
|
||||
cell.push(<Checkbox className="table-control-checkbox" inline checked={checkboxKey ? data[checkboxKey] : null} onChange={e => child.props.onChecked(index, e)}></Checkbox>);
|
||||
}
|
||||
|
||||
if (child.props.exportButton) {
|
||||
cell.push(<Button bsClass='table-control-button' onClick={() => child.props.onExport(index)}><Glyphicon glyph='export' /></Button>);
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ class WidgetFactory {
|
|||
widget.height = 30;
|
||||
widget.textSize = 16;
|
||||
widget.name = 'Value';
|
||||
widget.showUnit = false;
|
||||
break;
|
||||
case 'Plot':
|
||||
widget.simulator = defaultSimulator;
|
||||
|
@ -46,6 +47,9 @@ class WidgetFactory {
|
|||
widget.minHeight = 200;
|
||||
widget.width = 400;
|
||||
widget.height = 200;
|
||||
widget.yMin = 0;
|
||||
widget.yMax = 10;
|
||||
widget.yUseMinMax = false;
|
||||
break;
|
||||
case 'Table':
|
||||
widget.simulator = defaultSimulator;
|
||||
|
@ -67,11 +71,14 @@ class WidgetFactory {
|
|||
widget.preselectedSignals = [];
|
||||
widget.signals = []; // initialize selected signals
|
||||
widget.ylabel = '';
|
||||
widget.minWidth = 400;
|
||||
widget.minHeight = 300;
|
||||
widget.width = 500;
|
||||
widget.height = 500;
|
||||
widget.minWidth = 200;
|
||||
widget.minHeight = 100;
|
||||
widget.width = 600;
|
||||
widget.height = 300;
|
||||
widget.time = 60;
|
||||
widget.yMin = 0;
|
||||
widget.yMax = 10;
|
||||
widget.yUseMinMax = false;
|
||||
break;
|
||||
case 'Image':
|
||||
widget.minWidth = 20;
|
||||
|
@ -104,10 +111,15 @@ class WidgetFactory {
|
|||
case 'Gauge':
|
||||
widget.simulator = defaultSimulator;
|
||||
widget.signal = 0;
|
||||
widget.minWidth = 200;
|
||||
widget.minWidth = 100;
|
||||
widget.minHeight = 150;
|
||||
widget.width = 200;
|
||||
widget.width = 150;
|
||||
widget.height = 150;
|
||||
widget.colorZones = false;
|
||||
widget.zones = [];
|
||||
widget.valueMin = 0;
|
||||
widget.valueMax = 1;
|
||||
widget.valueUseMinMax = false;
|
||||
break;
|
||||
case 'Box':
|
||||
widget.minWidth = 50;
|
||||
|
@ -117,6 +129,10 @@ class WidgetFactory {
|
|||
widget.border_color = 0;
|
||||
widget.z = 0;
|
||||
break;
|
||||
case 'HTML':
|
||||
widget.content = '<i>Hello World</i>';
|
||||
break;
|
||||
|
||||
default:
|
||||
widget.width = 100;
|
||||
widget.height = 100;
|
||||
|
|
|
@ -18,62 +18,31 @@ class WidgetGauge extends Component {
|
|||
this.gauge = null;
|
||||
|
||||
this.state = {
|
||||
value: 0
|
||||
value: 0,
|
||||
minValue: 0,
|
||||
maxValue: 1
|
||||
};
|
||||
}
|
||||
|
||||
staticLabels(widget_height) {
|
||||
let label_font_size = Math.floor(widget_height * 0.055); // font scaling factor, integer for performance
|
||||
return {
|
||||
font: label_font_size + 'px "Helvetica Neue"',
|
||||
labels: [0.0, 0.1, 0.5, 0.9, 1.0],
|
||||
color: "#000000",
|
||||
fractionDigits: 1
|
||||
}
|
||||
}
|
||||
|
||||
computeGaugeOptions(widget_height) {
|
||||
return {
|
||||
angle: -0.25,
|
||||
lineWidth: 0.2,
|
||||
pointer: {
|
||||
length: 0.6,
|
||||
strokeWidth: 0.035
|
||||
},
|
||||
radiusScale: 0.9,
|
||||
colorStart: '#6EA2B0',
|
||||
colorStop: '#6EA2B0',
|
||||
strokeColor: '#E0E0E0',
|
||||
highDpiSupport: true,
|
||||
staticLabels: this.staticLabels(widget_height)
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const opts = this.computeGaugeOptions(this.props.widget.height);
|
||||
this.gauge = new Gauge(this.gaugeCanvas).setOptions(opts);
|
||||
this.gauge.maxValue = 1;
|
||||
this.gauge.setMinValue(0);
|
||||
this.gauge = new Gauge(this.gaugeCanvas).setOptions(this.computeGaugeOptions(this.props.widget));
|
||||
this.gauge.maxValue = this.state.maxValue;
|
||||
this.gauge.setMinValue(this.state.minValue);
|
||||
this.gauge.animationSpeed = 30;
|
||||
this.gauge.set(this.state.value);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
|
||||
// Check if size changed, resize labels if it did (the canvas itself is scaled with css)
|
||||
if (this.props.widget.height !== nextProps.widget.height) {
|
||||
this.updateAfterResize(nextProps.widget.height);
|
||||
}
|
||||
|
||||
// signal component update only if the value changed
|
||||
return this.state.value !== nextState.value;
|
||||
this.updateLabels(this.state.minValue, this.state.maxValue);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// update value
|
||||
const simulator = nextProps.widget.simulator;
|
||||
|
||||
if (nextProps.data == null || nextProps.data[simulator.node][simulator.simulator] == null || nextProps.data[simulator.node][simulator.simulator].values == null) {
|
||||
if (nextProps.data == null || nextProps.data[simulator.node] == null
|
||||
|| nextProps.data[simulator.node][simulator.simulator] == null
|
||||
|| nextProps.data[simulator.node][simulator.simulator].length === 0
|
||||
|| nextProps.data[simulator.node][simulator.simulator].values.length === 0
|
||||
|| nextProps.data[simulator.node][simulator.simulator].values[0].length === 0) {
|
||||
this.setState({ value: 0 });
|
||||
return;
|
||||
}
|
||||
|
@ -83,36 +52,127 @@ class WidgetGauge extends Component {
|
|||
// Take just 3 decimal positions
|
||||
// Note: Favor this method over Number.toFixed(n) in order to avoid a type conversion, since it returns a String
|
||||
if (signal != null) {
|
||||
const new_value = Math.round( signal[signal.length - 1].y * 1e3 ) / 1e3;
|
||||
if (this.state.value !== new_value) {
|
||||
this.setState({ value: new_value });
|
||||
const value = Math.round( signal[signal.length - 1].y * 1e3 ) / 1e3;
|
||||
if (this.state.value !== value && value != null) {
|
||||
this.setState({ value });
|
||||
|
||||
// update min-max if needed
|
||||
let updateLabels = false;
|
||||
let minValue = this.state.minValue;
|
||||
let maxValue = this.state.maxValue;
|
||||
|
||||
if (nextProps.widget.valueUseMinMax) {
|
||||
if (this.state.minValue > nextProps.widget.valueMin) {
|
||||
minValue = nextProps.widget.valueMin;
|
||||
|
||||
this.setState({ minValue });
|
||||
this.gauge.setMinValue(minValue);
|
||||
|
||||
updateLabels = true;
|
||||
}
|
||||
|
||||
if (this.state.maxValue < nextProps.widget.valueMax) {
|
||||
maxValue = nextProps.widget.valueMax;
|
||||
|
||||
this.setState({ maxValue });
|
||||
this.gauge.maxValue = maxValue;
|
||||
|
||||
updateLabels = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updateLabels === false) {
|
||||
// check if min/max changed
|
||||
if (minValue > this.gauge.minValue) {
|
||||
minValue = this.gauge.minValue;
|
||||
updateLabels = true;
|
||||
|
||||
this.setState({ minValue });
|
||||
}
|
||||
|
||||
if (maxValue < this.gauge.maxValue) {
|
||||
maxValue = this.gauge.maxValue;
|
||||
updateLabels = true;
|
||||
|
||||
this.setState({ maxValue });
|
||||
}
|
||||
}
|
||||
|
||||
if (updateLabels) {
|
||||
this.updateLabels(minValue, maxValue);
|
||||
}
|
||||
|
||||
// update gauge's value
|
||||
this.gauge.set(new_value);
|
||||
this.gauge.set(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateAfterResize(newHeight) {
|
||||
// Update labels after resize
|
||||
this.gauge.setOptions({ staticLabels: this.staticLabels(newHeight) });
|
||||
updateLabels(minValue, maxValue, force) {
|
||||
// calculate labels
|
||||
const labels = [];
|
||||
const labelCount = 5;
|
||||
const labelStep = (maxValue - minValue) / (labelCount - 1);
|
||||
|
||||
for (let i = 0; i < labelCount; i++) {
|
||||
labels.push(minValue + labelStep * i);
|
||||
}
|
||||
|
||||
// calculate zones
|
||||
let zones = this.props.widget.colorZones ? this.props.widget.zones : null;
|
||||
if (zones != null) {
|
||||
// adapt range 0-100 to actual min-max
|
||||
const step = (maxValue - minValue) / 100;
|
||||
|
||||
zones = zones.map(zone => {
|
||||
return Object.assign({}, zone, { min: (zone.min * step) + +minValue, max: zone.max * step + +minValue, strokeStyle: '#' + zone.strokeStyle });
|
||||
});
|
||||
}
|
||||
|
||||
this.gauge.setOptions({
|
||||
staticLabels: {
|
||||
font: '10px "Helvetica Neue"',
|
||||
labels,
|
||||
color: "#000000",
|
||||
fractionDigits: 1
|
||||
},
|
||||
staticZones: zones
|
||||
});
|
||||
}
|
||||
|
||||
computeGaugeOptions(widget) {
|
||||
return {
|
||||
angle: -0.25,
|
||||
lineWidth: 0.2,
|
||||
pointer: {
|
||||
length: 0.6,
|
||||
strokeWidth: 0.035
|
||||
},
|
||||
radiusScale: 0.8,
|
||||
colorStart: '#6EA2B0',
|
||||
colorStop: '#6EA2B0',
|
||||
strokeColor: '#E0E0E0',
|
||||
highDpiSupport: true,
|
||||
limitMax: false,
|
||||
limitMin: false
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
var componentClass = this.props.editing ? "gauge-widget editing" : "gauge-widget";
|
||||
var signalType = null;
|
||||
const componentClass = this.props.editing ? "gauge-widget editing" : "gauge-widget";
|
||||
let signalType = null;
|
||||
|
||||
if (this.props.simulation) {
|
||||
var simulationModel = this.props.simulation.models.filter((model) => model.simulator.node === this.props.widget.simulator.node && model.simulator.simulator === this.props.widget.simulator.simulator)[0];
|
||||
const simulationModel = this.props.simulation.models.filter((model) => model.simulator.node === this.props.widget.simulator.node && model.simulator.simulator === this.props.widget.simulator.simulator)[0];
|
||||
signalType = (simulationModel != null && simulationModel.length > 0) ? simulationModel.mapping[this.props.widget.signal].type : '';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ componentClass }>
|
||||
<div className="gauge-name">{ this.props.widget.name }</div>
|
||||
<canvas ref={ (node) => this.gaugeCanvas = node } />
|
||||
<div className="gauge-unit">{ signalType }</div>
|
||||
<div className="gauge-value">{ this.state.value }</div>
|
||||
<div className={componentClass}>
|
||||
<div className="gauge-name">{this.props.widget.name}</div>
|
||||
<canvas ref={node => this.gaugeCanvas = node} />
|
||||
<div className="gauge-unit">{signalType}</div>
|
||||
<div className="gauge-value">{this.state.value}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* File: villas-store.js
|
||||
* File: widget-html.js
|
||||
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
|
||||
* Date: 02.03.2017
|
||||
* Date: 29.08.2017
|
||||
*
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
|
@ -19,25 +19,12 @@
|
|||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import { ReduceStore } from 'flux/utils';
|
||||
import React from 'react';
|
||||
|
||||
import AppDispatcher from '../app-dispatcher';
|
||||
|
||||
class VillasStore extends ReduceStore {
|
||||
constructor() {
|
||||
super(AppDispatcher);
|
||||
}
|
||||
|
||||
getInitialState() {
|
||||
return {};
|
||||
}
|
||||
|
||||
reduce(state, action) {
|
||||
switch (action.type) {
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
class WidgetHTML extends React.Component {
|
||||
render() {
|
||||
return <div dangerouslySetInnerHTML={{__html: this.props.widget.content }} />
|
||||
}
|
||||
}
|
||||
|
||||
export default new VillasStore();
|
||||
export default WidgetHTML;
|
|
@ -38,13 +38,15 @@ class WidgetImage extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
let file = this.props.files.find(file => file._id === this.props.widget.file);
|
||||
const file = this.props.files.find(file => file._id === this.props.widget.file);
|
||||
|
||||
return (
|
||||
<div className="full">
|
||||
{file &&
|
||||
{file ? (
|
||||
<img className="full" alt={file.name} src={'/' + config.publicPathBase + file.path} onDragStart={e => e.preventDefault()} />
|
||||
}
|
||||
) : (
|
||||
<img className="full" alt="questionmark" src={'/' + config.publicPathBase + 'missing-image.png'} onDragStart={e => e.preventDefault()} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ class WidgetPlotTable extends Component {
|
|||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
|
||||
// Update internal selected signals state with props (different array objects)
|
||||
if (this.props.widget.signals !== nextProps.widget.signals) {
|
||||
this.setState( {signals: nextProps.widget.signals});
|
||||
|
@ -158,6 +157,9 @@ class WidgetPlotTable extends Component {
|
|||
time={this.props.widget.time}
|
||||
width={this.props.widget.width - 100}
|
||||
height={this.props.widget.height - 55}
|
||||
yMin={this.props.widget.yMin}
|
||||
yMax={this.props.widget.yMax}
|
||||
yUseMinMax={this.props.widget.yUseMinMax}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -25,45 +25,59 @@ import Plot from './widget-plot/plot';
|
|||
import PlotLegend from './widget-plot/plot-legend';
|
||||
|
||||
class WidgetPlot extends React.Component {
|
||||
render() {
|
||||
const simulator = this.props.widget.simulator;
|
||||
const simulation = this.props.simulation;
|
||||
let legendSignals = [];
|
||||
let simulatorData = [];
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
data: [],
|
||||
legend: []
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const simulator = nextProps.widget.simulator;
|
||||
const simulation = nextProps.simulation;
|
||||
|
||||
// Proceed if a simulation with models and a simulator are available
|
||||
if (simulator && this.props.data[simulator.node] != null && this.props.data[simulator.node][simulator.simulator] != null && simulation && simulation.models.length > 0) {
|
||||
if (simulator && nextProps.data[simulator.node] != null && nextProps.data[simulator.node][simulator.simulator] != null && simulation && simulation.models.length > 0) {
|
||||
const model = simulation.models.find(model => model.simulator.node === simulator.node && model.simulator.simulator === simulator.simulator);
|
||||
const chosenSignals = this.props.widget.signals;
|
||||
const chosenSignals = nextProps.widget.signals;
|
||||
|
||||
simulatorData = this.props.data[simulator.node][simulator.simulator].values.filter((values, index) => (
|
||||
this.props.widget.signals.findIndex(value => value === index) !== -1
|
||||
const data = nextProps.data[simulator.node][simulator.simulator].values.filter((values, index) => (
|
||||
nextProps.widget.signals.findIndex(value => value === index) !== -1
|
||||
));
|
||||
|
||||
// Query the signals that will be displayed in the legend
|
||||
legendSignals = model.mapping.reduce( (accum, model_signal, signal_index) => {
|
||||
const legend = model.mapping.reduce( (accum, model_signal, signal_index) => {
|
||||
if (chosenSignals.includes(signal_index)) {
|
||||
accum.push({ index: signal_index, name: model_signal.name });
|
||||
}
|
||||
|
||||
return accum;
|
||||
}, []);
|
||||
|
||||
this.setState({ data, legend });
|
||||
} else {
|
||||
this.setState({ data: [], legend: [] });
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="plot-widget" ref="wrapper">
|
||||
<div className="widget-plot">
|
||||
<Plot
|
||||
data={simulatorData}
|
||||
height={this.props.widget.height - 55}
|
||||
width={this.props.widget.width - 20}
|
||||
time={this.props.widget.time}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<PlotLegend signals={legendSignals} />
|
||||
render() {
|
||||
return <div className="plot-widget" ref="wrapper">
|
||||
<div className="widget-plot">
|
||||
<Plot
|
||||
data={this.state.data}
|
||||
height={this.props.widget.height - 55}
|
||||
width={this.props.widget.width - 20}
|
||||
time={this.props.widget.time}
|
||||
yMin={this.props.widget.yMin}
|
||||
yMax={this.props.widget.yMax}
|
||||
yUseMinMax={this.props.widget.yUseMinMax}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
<PlotLegend signals={this.state.legend} />
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,25 +7,21 @@
|
|||
* Unauthorized copying of this file, via any medium is strictly prohibited.
|
||||
**********************************************************************************/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import { scaleOrdinal, schemeCategory10 } from 'd3-scale';
|
||||
|
||||
class PlotLegend extends Component {
|
||||
// constructor(props) {
|
||||
// super(props);
|
||||
// }
|
||||
|
||||
class PlotLegend extends React.Component {
|
||||
render() {
|
||||
var colorScale = scaleOrdinal(schemeCategory10);
|
||||
const colorScale = scaleOrdinal(schemeCategory10);
|
||||
|
||||
return (
|
||||
<div className="plot-legend">
|
||||
{ this.props.signals.map( (signal) =>
|
||||
<div key={signal.index} className="signal-legend"><span className="legend-color" style={{ background: colorScale(signal.index) }}> </span> {signal.name} </div>)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
return <div className="plot-legend">
|
||||
{this.props.signals.map(signal =>
|
||||
<div key={signal.index} className="signal-legend">
|
||||
<span className="legend-color" style={{ background: colorScale(signal.index) }}> </span>{signal.name}
|
||||
</div>
|
||||
)}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default PlotLegend;
|
||||
export default PlotLegend;
|
||||
|
|
|
@ -22,22 +22,38 @@ class Plot extends React.Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// create dummy axes
|
||||
const xScale = scaleTime().domain([Date.now(), Date.now() + 5 * 1000]).range([leftMargin, props.width]);
|
||||
const yScale = scaleLinear().domain([0, 10]).range([props.height, bottomMargin]);
|
||||
|
||||
const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(date => timeFormat("%M:%S")(date));
|
||||
const yAxis = axisLeft().scale(yScale).ticks(5);
|
||||
|
||||
this.state = {
|
||||
data: null
|
||||
data: null,
|
||||
xAxis,
|
||||
yAxis
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// check if data is valid
|
||||
if (nextProps.data == null || nextProps.data.length === 0 || nextProps.data[0].length === 0) {
|
||||
this.setState({ data: null });
|
||||
// create empty plot axes
|
||||
const xScale = scaleTime().domain([Date.now(), Date.now() + 5 * 1000]).range([leftMargin, nextProps.width]);
|
||||
const yScale = scaleLinear().domain([0, 10]).range([nextProps.height, bottomMargin]);
|
||||
|
||||
const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(date => timeFormat("%M:%S")(date));
|
||||
const yAxis = axisLeft().scale(yScale).ticks(5);
|
||||
|
||||
this.setState({ data: null, xAxis, yAxis });
|
||||
return;
|
||||
}
|
||||
|
||||
// only show data in requested time
|
||||
let data = nextProps.data;
|
||||
|
||||
const firstTimestamp = data[0][data[0].length - 1].x - this.props.time * 1000;
|
||||
const firstTimestamp = data[0][data[0].length - 1].x - nextProps.time * 1000;
|
||||
if (data[0][0].x < firstTimestamp) {
|
||||
// only show data in range (+100 ms)
|
||||
const index = data[0].findIndex(value => value.x >= firstTimestamp - 100);
|
||||
|
@ -50,20 +66,26 @@ class Plot extends React.Component {
|
|||
xRange[0] = xRange[1] - nextProps.time * 1000;
|
||||
}
|
||||
|
||||
let yRange = [0, 0];
|
||||
let yRange;
|
||||
|
||||
data.map(values => {
|
||||
const range = extent(values, p => p.y);
|
||||
if (range[0] < yRange[0]) yRange[0] = range[0];
|
||||
if (range[1] > yRange[1]) yRange[1] = range[1];
|
||||
if (nextProps.yUseMinMax) {
|
||||
yRange = [nextProps.yMin, nextProps.yMax];
|
||||
} else {
|
||||
yRange = [0, 0];
|
||||
|
||||
return values;
|
||||
});
|
||||
data.map(values => {
|
||||
const range = extent(values, p => p.y);
|
||||
if (range[0] < yRange[0]) yRange[0] = range[0];
|
||||
if (range[1] > yRange[1]) yRange[1] = range[1];
|
||||
|
||||
return values;
|
||||
});
|
||||
}
|
||||
|
||||
// create scale functions for both axes
|
||||
const xScale = scaleTime().domain(xRange).range([leftMargin, nextProps.width]);
|
||||
const yScale = scaleLinear().domain(yRange).range([nextProps.height, bottomMargin]);
|
||||
|
||||
|
||||
const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(date => timeFormat("%M:%S")(date));
|
||||
const yAxis = axisLeft().scale(yScale).ticks(5);
|
||||
|
||||
|
@ -77,18 +99,14 @@ class Plot extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
if (this.state.data == null) return false;
|
||||
return <svg width={this.props.width + leftMargin} height={this.props.height + bottomMargin}>
|
||||
<g ref={node => select(node).call(this.state.xAxis)} style={{ transform: `translateY(${this.props.height}px)` }} />
|
||||
<g ref={node => select(node).call(this.state.yAxis)} style={{ transform: `translateX(${leftMargin}px)`}} />
|
||||
|
||||
return(
|
||||
<svg width={this.props.width + leftMargin} height={this.props.height + bottomMargin}>
|
||||
<g ref={node => select(node).call(this.state.xAxis)} style={{ transform: `translateY(${this.props.height}px)` }} />
|
||||
<g ref={node => select(node).call(this.state.yAxis)} style={{ transform: `translateX(${leftMargin}px)`}} />
|
||||
|
||||
<g>
|
||||
{this.state.data}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
<g>
|
||||
{this.state.data}
|
||||
</g>
|
||||
</svg>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,8 @@ class WidgetValue extends Component {
|
|||
super(props);
|
||||
|
||||
this.state = {
|
||||
value: ''
|
||||
value: '',
|
||||
unit: ''
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -35,17 +36,23 @@ class WidgetValue extends Component {
|
|||
const simulator = nextProps.widget.simulator.simulator;
|
||||
const node = nextProps.widget.simulator.node;
|
||||
|
||||
//console.log(nextProps.widget.simulator);
|
||||
|
||||
if (nextProps.data == null || nextProps.data[node] == null || nextProps.data[node][simulator] == null || nextProps.data[node][simulator].values == null) {
|
||||
this.setState({ value: '' });
|
||||
return;
|
||||
}
|
||||
|
||||
// get unit from simulation model
|
||||
let unit = '';
|
||||
|
||||
if (nextProps.simulation) {
|
||||
const simulationModel = nextProps.simulation.models.find(model => model.simulator.node === node && model.simulator.simulator === simulator);
|
||||
unit = simulationModel.mapping[nextProps.widget.signal].type;
|
||||
}
|
||||
|
||||
// check if value has changed
|
||||
const signal = nextProps.data[node][simulator].values[nextProps.widget.signal];
|
||||
if (signal != null && this.state.value !== signal[signal.length - 1].y) {
|
||||
this.setState({ value: signal[signal.length - 1].y });
|
||||
this.setState({ value: signal[signal.length - 1].y, unit });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +60,11 @@ class WidgetValue extends Component {
|
|||
var value_to_render = Number(this.state.value);
|
||||
return (
|
||||
<div className="single-value-widget">
|
||||
<strong style={{ fontSize: this.props.widget.textSize + 'px' }}>{this.props.widget.name}</strong> <span style={{ fontSize: this.props.widget.textSize + 'px' }}>{ Number.isNaN(value_to_render)? NaN : value_to_render.toFixed(3) } </span>
|
||||
<strong style={{ fontSize: this.props.widget.textSize + 'px' }}>{this.props.widget.name}</strong>
|
||||
<span style={{ fontSize: this.props.widget.textSize + 'px' }}>{Number.isNaN(value_to_render) ? NaN : value_to_render.toFixed(3)}</span>
|
||||
{this.props.widget.showUnit &&
|
||||
<span style={{ fontSize: this.props.widget.textSize + 'px' }}>[{this.state.unit}]</span>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
|
||||
const config = {
|
||||
publicPathBase: 'public/'
|
||||
publicPathBase: 'public/',
|
||||
instance: 'frontend of the Global RT-SuperLab Demonstration',
|
||||
admin: {
|
||||
name: 'Steffen Vogel',
|
||||
mail: 'stvogel@eonerc.rwth-aachen.de'
|
||||
}
|
||||
}
|
||||
|
||||
export default config
|
||||
export default config
|
||||
|
|
|
@ -22,23 +22,74 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Container } from 'flux/utils';
|
||||
|
||||
// import AppDispatcher from '../app-dispatcher';
|
||||
import VillasStore from '../stores/villas-store';
|
||||
import AppDispatcher from '../app-dispatcher';
|
||||
|
||||
import NodeStore from '../stores/node-store';
|
||||
import SimulationStore from '../stores/simulation-store';
|
||||
import ProjectStore from '../stores/project-store';
|
||||
|
||||
import config from '../config';
|
||||
|
||||
class Home extends Component {
|
||||
static getStores() {
|
||||
return [ VillasStore ];
|
||||
return [ NodeStore, SimulationStore, ProjectStore ];
|
||||
}
|
||||
|
||||
static calculateState() {
|
||||
return {
|
||||
villas: VillasStore.getState()
|
||||
nodes: NodeStore.getState(),
|
||||
projects: ProjectStore.getState(),
|
||||
simulations: SimulationStore.getState(),
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'projects/start-load',
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'simulations/start-load',
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<h1>Home</h1>
|
||||
<div className="home-container">
|
||||
<img style={{height: 120, float: 'right'}} src={require('../img/villas_web.svg')} alt="Logo VILLASweb" />
|
||||
<h1>Home</h1>
|
||||
<p>
|
||||
Welcome to <b>{config.instance}</b>!<br />
|
||||
VILLASweb is a frontend for distributed real-time simulation hosted by <a href={"mailto:" + config.admin.mail}>{config.admin.name}</a>.
|
||||
</p>
|
||||
<p>
|
||||
This instance is hosting {this.state.projects.length} projects consisting of {this.state.nodes.length} nodes and {this.state.simulations.length} simulations.<br />
|
||||
</p>
|
||||
<h3>Credits</h3>
|
||||
<p>VILLASweb is developed by the <a href="http://acs.eonerc.rwth-aachen.de">Institute for Automation of Complex Power Systems</a> at the <a href="https;//www.rwth-aachen.de">RWTH Aachen University</a>.</p>
|
||||
<ul>
|
||||
<li><a href="mailto:mgrigull@eonerc.rwth-aachen.de">Markus Grigull</a></li>
|
||||
<li><a href="mailto:stvogel@eonerc.rwth-aachen.de">Steffen Vogel</a></li>
|
||||
<li><a href="mailto:mstevic@eonerc.rwth-aachen.de">Marija Stevic</a></li>
|
||||
</ul>
|
||||
<h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="http://fein-aachen.org/projects/villas-framework/">Project Page</a></li>
|
||||
<li><a href="https://villas.fein-aachen.org/doc/web.html">Documentation</a></li>
|
||||
<li><a href="https://git.rwth-aachen.de/VILLASframework/VILLASweb">Source Code</a></li>
|
||||
</ul>
|
||||
<h3>Funding</h3>
|
||||
<p>The development of <a href="http://fein-aachen.org/projects/villas-framework/">VILLASframework</a> projects have received funding from</p>
|
||||
<ul>
|
||||
<li><a href="http://www.re-serve.eu">RESERVE</a> a European Union’s Horizon 2020 research and innovation programme under grant agreement No 727481</li>
|
||||
<li><a href="http://www.jara.org/en/research/energy">JARA-ENERGY</a>. Jülich-Aachen Research Alliance (JARA) is an initiative of RWTH Aachen University and Forschungszentrum Jülich.</li>
|
||||
</ul>
|
||||
<img height={60} src={require('../img/eonerc_rwth.svg')} alt="Logo ACS" />
|
||||
<img height={70} src={require('../img/jara.svg')} alt="Logo JARA" />
|
||||
<img height={100} src={require('../img/european_commission.svg')} alt="Logo EU" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,98 +22,80 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Container } from 'flux/utils';
|
||||
import { Button, Modal, Glyphicon } from 'react-bootstrap';
|
||||
import FileSaver from 'file-saver';
|
||||
|
||||
import AppDispatcher from '../app-dispatcher';
|
||||
import ProjectStore from '../stores/project-store';
|
||||
import UserStore from '../stores/user-store';
|
||||
import VisualizationStore from '../stores/visualization-store';
|
||||
import SimulationStore from '../stores/simulation-store';
|
||||
|
||||
import CustomTable from '../components/table';
|
||||
import TableColumn from '../components/table-column';
|
||||
import NewVisualzationDialog from '../components/dialog/new-visualization';
|
||||
import EditVisualizationDialog from '../components/dialog/edit-visualization';
|
||||
import ImportVisualizationDialog from '../components/dialog/import-visualization';
|
||||
|
||||
class Visualizations extends Component {
|
||||
static getStores() {
|
||||
return [ ProjectStore, VisualizationStore, UserStore ];
|
||||
return [ ProjectStore, VisualizationStore, UserStore, SimulationStore ];
|
||||
}
|
||||
|
||||
static calculateState(prevState, props) {
|
||||
prevState = prevState || {};
|
||||
|
||||
let currentProjects = ProjectStore.getState();
|
||||
let currentVisualizations = VisualizationStore.getState();
|
||||
let sessionToken = UserStore.getState().token;
|
||||
// load project
|
||||
const sessionToken = UserStore.getState().token;
|
||||
|
||||
if (prevState) {
|
||||
var projectUpdate = prevState.project;
|
||||
let project = ProjectStore.getState().find(project => project._id === props.match.params.project);
|
||||
if (project == null) {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'projects/start-load',
|
||||
data: props.match.params.project,
|
||||
token: sessionToken
|
||||
});
|
||||
|
||||
// Compare content of the visualizations array, reload projects if changed
|
||||
if (JSON.stringify(prevState.visualizations) !== JSON.stringify(currentVisualizations)) {
|
||||
Visualizations.loadProjects(sessionToken);
|
||||
}
|
||||
|
||||
// Compare content of the projects array, update visualizations if changed
|
||||
if (JSON.stringify(prevState.projects) !== JSON.stringify(currentProjects)) {
|
||||
projectUpdate = Visualizations.findProjectInState(currentProjects, props.match.params.project);
|
||||
Visualizations.loadVisualizations(projectUpdate.visualizations, sessionToken);
|
||||
}
|
||||
|
||||
return {
|
||||
projects: currentProjects,
|
||||
visualizations: currentVisualizations,
|
||||
sessionToken,
|
||||
|
||||
newModal: prevState.newModal,
|
||||
deleteModal: prevState.deleteModal,
|
||||
editModal: prevState.editModal,
|
||||
modalData: prevState.modalData,
|
||||
|
||||
project: projectUpdate
|
||||
};
|
||||
} else {
|
||||
|
||||
let initialProject = Visualizations.findProjectInState(currentProjects, props.match.params.project);
|
||||
// If projects have been loaded already but visualizations not (redirect from Projects page)
|
||||
if (initialProject && (!currentVisualizations || currentVisualizations.length === 0)) {
|
||||
Visualizations.loadVisualizations(initialProject.visualizations, sessionToken);
|
||||
}
|
||||
|
||||
return {
|
||||
projects: currentProjects,
|
||||
visualizations: currentVisualizations,
|
||||
sessionToken,
|
||||
|
||||
newModal: false,
|
||||
deleteModal: false,
|
||||
editModal: false,
|
||||
modalData: {},
|
||||
|
||||
project: initialProject || {}
|
||||
};
|
||||
project = {};
|
||||
}
|
||||
|
||||
// load simulation
|
||||
let simulation = {};
|
||||
|
||||
if (project.simulation != null) {
|
||||
simulation = SimulationStore.getState().find(simulation => simulation._id === project.simulation);
|
||||
}
|
||||
|
||||
// load visualizations
|
||||
let visualizations = [];
|
||||
|
||||
if (project.visualizations != null) {
|
||||
visualizations = VisualizationStore.getState().filter(visualization => project.visualizations.includes(visualization._id));
|
||||
}
|
||||
|
||||
return {
|
||||
visualizations,
|
||||
project,
|
||||
simulation,
|
||||
sessionToken,
|
||||
|
||||
newModal: prevState.newModal || false,
|
||||
deleteModal: prevState.deleteModal || false,
|
||||
editModal: prevState.editModal || false,
|
||||
importModal: prevState.importModal || false,
|
||||
modalData: prevState.modalData || {}
|
||||
};
|
||||
}
|
||||
|
||||
static findProjectInState(projects, projectId) {
|
||||
return projects.find((project) => project._id === projectId);
|
||||
}
|
||||
|
||||
static loadProjects(token) {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'projects/start-load',
|
||||
token
|
||||
});
|
||||
}
|
||||
|
||||
static loadVisualizations(visualizations, token) {
|
||||
componentDidMount() {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'visualizations/start-load',
|
||||
data: visualizations,
|
||||
token
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
Visualizations.loadProjects(this.state.sessionToken);
|
||||
AppDispatcher.dispatch({
|
||||
type: 'simulations/start-load',
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
}
|
||||
|
||||
closeNewModal(data) {
|
||||
|
@ -153,33 +135,78 @@ class Visualizations extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
// get visualizations for this project
|
||||
var visualizations = [];
|
||||
if (this.state.visualizations && this.state.project.visualizations) {
|
||||
visualizations = this.state.visualizations.filter(
|
||||
(visualization) => this.state.project.visualizations.includes(visualization._id)
|
||||
).sort(
|
||||
(visA, visB) => visA.name.localeCompare(visB.name)
|
||||
);
|
||||
}
|
||||
closeImportModal(data) {
|
||||
this.setState({ importModal: false });
|
||||
|
||||
if (data) {
|
||||
data.project = this.state.project._id;
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'visualizations/start-add',
|
||||
data,
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
|
||||
this.setState({ project: {} }, () => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'projects/start-load',
|
||||
data: this.props.match.params.project,
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
exportVisualization(index) {
|
||||
// filter properties
|
||||
let visualization = Object.assign({}, this.state.visualizations[index]);
|
||||
delete visualization._id;
|
||||
delete visualization.project;
|
||||
delete visualization.user;
|
||||
|
||||
visualization.widgets.forEach(widget => {
|
||||
delete widget.simulator;
|
||||
});
|
||||
|
||||
// show save dialog
|
||||
const blob = new Blob([JSON.stringify(visualization, null, 2)], { type: 'application/json' });
|
||||
FileSaver.saveAs(blob, 'visualization - ' + visualization.name + '.json');
|
||||
}
|
||||
|
||||
onModalKeyPress = (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
|
||||
this.confirmDeleteModal();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='section'>
|
||||
<h1>{this.state.project.name}</h1>
|
||||
|
||||
<CustomTable data={visualizations}>
|
||||
<CustomTable data={this.state.visualizations}>
|
||||
<TableColumn title='Name' dataKey='name' link='/visualizations/' linkKey='_id' />
|
||||
<TableColumn width='70' editButton deleteButton onEdit={(index) => this.setState({ editModal: true, modalData: visualizations[index] })} onDelete={(index) => this.setState({ deleteModal: true, modalData: visualizations[index] })} />
|
||||
<TableColumn
|
||||
width='100'
|
||||
editButton
|
||||
deleteButton
|
||||
exportButton
|
||||
onEdit={(index) => this.setState({ editModal: true, modalData: this.state.visualizations[index] })}
|
||||
onDelete={(index) => this.setState({ deleteModal: true, modalData: this.state.visualizations[index] })}
|
||||
onExport={index => this.exportVisualization(index)}
|
||||
/>
|
||||
</CustomTable>
|
||||
|
||||
<Button onClick={() => this.setState({ newModal: true })}><Glyphicon glyph="plus" /> Visualization</Button>
|
||||
<Button onClick={() => this.setState({ importModal: true })}><Glyphicon glyph="import" /> Import</Button>
|
||||
|
||||
<NewVisualzationDialog show={this.state.newModal} onClose={(data) => this.closeNewModal(data)} />
|
||||
|
||||
<EditVisualizationDialog show={this.state.editModal} onClose={(data) => this.closeEditModal(data)} visualization={this.state.modalData} />
|
||||
<ImportVisualizationDialog show={this.state.importModal} onClose={data => this.closeImportModal(data)} simulation={this.state.simulation} />
|
||||
|
||||
<Modal show={this.state.deleteModal}>
|
||||
<Modal keyboard show={this.state.deleteModal} onHide={() => this.setState({ deleteModal: false })} onKeyPress={this.onModalKeyPress}>
|
||||
<Modal.Header>
|
||||
<Modal.Title>Delete Visualization</Modal.Title>
|
||||
</Modal.Header>
|
||||
|
|
|
@ -115,6 +115,14 @@ class Projects extends React.Component {
|
|||
return simulations.length > 0;
|
||||
}
|
||||
|
||||
onModalKeyPress = (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
|
||||
this.confirmDeleteModal();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='section'>
|
||||
|
@ -135,7 +143,7 @@ class Projects extends React.Component {
|
|||
<NewProjectDialog show={this.state.newModal} onClose={(data) => this.closeNewModal(data)} simulations={this.state.simulations} />
|
||||
<EditProjectDialog show={this.state.editModal} onClose={(data) => this.closeEditModal(data)} project={this.state.modalData} simulations={this.state.simulations} />
|
||||
|
||||
<Modal show={this.state.deleteModal}>
|
||||
<Modal keyboard show={this.state.deleteModal} onHide={() => this.setState({ deleteModal: false })} onKeyPress={this.onModalKeyPress}>
|
||||
<Modal.Header>
|
||||
<Modal.Title>Delete Project</Modal.Title>
|
||||
</Modal.Header>
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
import React from 'react';
|
||||
import { Container } from 'flux/utils';
|
||||
import { Button, Modal, Glyphicon } from 'react-bootstrap';
|
||||
import FileSaver from 'file-saver';
|
||||
|
||||
import SimulationStore from '../stores/simulation-store';
|
||||
import NodeStore from '../stores/node-store';
|
||||
|
@ -32,6 +33,7 @@ import Table from '../components/table';
|
|||
import TableColumn from '../components/table-column';
|
||||
import NewSimulationModelDialog from '../components/dialog/new-simulation-model';
|
||||
import EditSimulationModelDialog from '../components/dialog/edit-simulation-model';
|
||||
import ImportSimulationModelDialog from '../components/dialog/import-simulation-model';
|
||||
|
||||
class Simulation extends React.Component {
|
||||
static getStores() {
|
||||
|
@ -47,6 +49,7 @@ class Simulation extends React.Component {
|
|||
newModal: false,
|
||||
deleteModal: false,
|
||||
editModal: false,
|
||||
importModal: false,
|
||||
modalData: {},
|
||||
modalIndex: null,
|
||||
|
||||
|
@ -126,6 +129,20 @@ class Simulation extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
closeImportModal(data) {
|
||||
this.setState({ importModal: false });
|
||||
|
||||
if (data) {
|
||||
this.state.simulation.models.push(data);
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'simulations/start-edit',
|
||||
data: this.state.simulation,
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getSimulatorName(simulator) {
|
||||
var name = "undefined";
|
||||
|
||||
|
@ -138,6 +155,24 @@ class Simulation extends React.Component {
|
|||
return name;
|
||||
}
|
||||
|
||||
exportModel(index) {
|
||||
// filter properties
|
||||
let simulationModel = Object.assign({}, this.state.simulation.models[index]);
|
||||
delete simulationModel.simulator;
|
||||
|
||||
// show save dialog
|
||||
const blob = new Blob([JSON.stringify(simulationModel, null, 2)], { type: 'application/json' });
|
||||
FileSaver.saveAs(blob, 'simulation model - ' + simulationModel.name + '.json');
|
||||
}
|
||||
|
||||
onModalKeyPress = (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
|
||||
this.confirmDeleteModal();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='section'>
|
||||
|
@ -147,16 +182,26 @@ class Simulation extends React.Component {
|
|||
<TableColumn title='Name' dataKey='name' />
|
||||
<TableColumn title='Simulator' dataKey='simulator' width='180' modifier={(simulator) => this.getSimulatorName(simulator)} />
|
||||
<TableColumn title='Length' dataKey='length' width='100' />
|
||||
<TableColumn title='' width='70' editButton deleteButton onEdit={(index) => this.setState({ editModal: true, modalData: this.state.simulation.models[index], modalIndex: index })} onDelete={(index) => this.setState({ deleteModal: true, modalData: this.state.simulation.models[index], modalIndex: index })} />
|
||||
<TableColumn
|
||||
title=''
|
||||
width='100'
|
||||
editButton
|
||||
deleteButton
|
||||
exportButton
|
||||
onEdit={(index) => this.setState({ editModal: true, modalData: this.state.simulation.models[index], modalIndex: index })}
|
||||
onDelete={(index) => this.setState({ deleteModal: true, modalData: this.state.simulation.models[index], modalIndex: index })}
|
||||
onExport={index => this.exportModel(index)}
|
||||
/>
|
||||
</Table>
|
||||
|
||||
<Button onClick={() => this.setState({ newModal: true })}><Glyphicon glyph="plus" /> Simulation Model</Button>
|
||||
<Button onClick={() => this.setState({ importModal: true })}><Glyphicon glyph="import" /> Import</Button>
|
||||
|
||||
<NewSimulationModelDialog show={this.state.newModal} onClose={(data) => this.closeNewModal(data)} nodes={this.state.nodes} />
|
||||
|
||||
<EditSimulationModelDialog show={this.state.editModal} onClose={(data) => this.closeEditModal(data)} data={this.state.modalData} nodes={this.state.nodes} />
|
||||
<ImportSimulationModelDialog show={this.state.importModal} onClose={data => this.closeImportModal(data)} nodes={this.state.nodes} />
|
||||
|
||||
<Modal show={this.state.deleteModal}>
|
||||
<Modal keyboard show={this.state.deleteModal} onHide={() => this.setState({ deleteModal: false })} onKeyPress={this.onModalKeyPress}>
|
||||
<Modal.Header>
|
||||
<Modal.Title>Delete Simulation Model</Modal.Title>
|
||||
</Modal.Header>
|
||||
|
|
|
@ -22,29 +22,34 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Container } from 'flux/utils';
|
||||
import { Button, Modal, Glyphicon } from 'react-bootstrap';
|
||||
import FileSaver from 'file-saver';
|
||||
|
||||
import AppDispatcher from '../app-dispatcher';
|
||||
import SimulationStore from '../stores/simulation-store';
|
||||
import UserStore from '../stores/user-store';
|
||||
import NodeStore from '../stores/node-store';
|
||||
|
||||
import Table from '../components/table';
|
||||
import TableColumn from '../components/table-column';
|
||||
import NewSimulationDialog from '../components/dialog/new-simulation';
|
||||
import EditSimulationDialog from '../components/dialog/edit-simulation';
|
||||
import ImportSimulationDialog from '../components/dialog/import-simulation';
|
||||
|
||||
class Simulations extends Component {
|
||||
static getStores() {
|
||||
return [ SimulationStore, UserStore ];
|
||||
return [ SimulationStore, UserStore, NodeStore ];
|
||||
}
|
||||
|
||||
static calculateState() {
|
||||
return {
|
||||
simulations: SimulationStore.getState(),
|
||||
nodes: NodeStore.getState(),
|
||||
sessionToken: UserStore.getState().token,
|
||||
|
||||
newModal: false,
|
||||
deleteModal: false,
|
||||
editModal: false,
|
||||
importModal: false,
|
||||
modalSimulation: {}
|
||||
};
|
||||
}
|
||||
|
@ -116,6 +121,43 @@ class Simulations extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
closeImportModal(data) {
|
||||
this.setState({ importModal: false });
|
||||
|
||||
if (data) {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'simulations/start-add',
|
||||
data,
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onModalKeyPress = (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
|
||||
this.confirmDeleteModal();
|
||||
}
|
||||
}
|
||||
|
||||
exportSimulation(index) {
|
||||
// filter properties
|
||||
let simulation = Object.assign({}, this.state.simulations[index]);
|
||||
delete simulation._id;
|
||||
delete simulation.projects;
|
||||
delete simulation.running;
|
||||
delete simulation.user;
|
||||
|
||||
simulation.models.forEach(model => {
|
||||
delete model.simulator;
|
||||
});
|
||||
|
||||
// show save dialog
|
||||
const blob = new Blob([JSON.stringify(simulation, null, 2)], { type: 'application/json' });
|
||||
FileSaver.saveAs(blob, 'simulation - ' + simulation.name + '.json');
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='section'>
|
||||
|
@ -123,16 +165,25 @@ class Simulations extends Component {
|
|||
|
||||
<Table data={this.state.simulations}>
|
||||
<TableColumn title='Name' dataKey='name' link='/simulations/' linkKey='_id' />
|
||||
<TableColumn width='70' editButton deleteButton onEdit={index => this.setState({ editModal: true, modalSimulation: this.state.simulations[index] })} onDelete={index => this.setState({ deleteModal: true, modalSimulation: this.state.simulations[index] })} />
|
||||
<TableColumn
|
||||
width='100'
|
||||
editButton
|
||||
deleteButton
|
||||
exportButton
|
||||
onEdit={index => this.setState({ editModal: true, modalSimulation: this.state.simulations[index] })}
|
||||
onDelete={index => this.setState({ deleteModal: true, modalSimulation: this.state.simulations[index] })}
|
||||
onExport={index => this.exportSimulation(index)}
|
||||
/>
|
||||
</Table>
|
||||
|
||||
<Button onClick={() => this.setState({ newModal: true })}><Glyphicon glyph="plus" /> Simulation</Button>
|
||||
<Button onClick={() => this.setState({ importModal: true })}><Glyphicon glyph="import" /> Import</Button>
|
||||
|
||||
<NewSimulationDialog show={this.state.newModal} onClose={(data) => this.closeNewModal(data)} />
|
||||
|
||||
<EditSimulationDialog show={this.state.editModal} onClose={(data) => this.closeEditModal(data)} simulation={this.state.modalSimulation} />
|
||||
<ImportSimulationDialog show={this.state.importModal} onClose={data => this.closeImportModal(data)} nodes={this.state.nodes} />
|
||||
|
||||
<Modal show={this.state.deleteModal}>
|
||||
<Modal keyboard show={this.state.deleteModal} onHide={() => this.setState({ deleteModal: false })} onKeyPress={this.onModalKeyPress}>
|
||||
<Modal.Header>
|
||||
<Modal.Title>Delete Simulation</Modal.Title>
|
||||
</Modal.Header>
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Container } from 'flux/utils';
|
||||
import { Button, Modal, Glyphicon } from 'react-bootstrap';
|
||||
import FileSaver from 'file-saver';
|
||||
|
||||
import AppDispatcher from '../app-dispatcher';
|
||||
import NodeStore from '../stores/node-store';
|
||||
|
@ -32,6 +33,7 @@ import EditNodeDialog from '../components/dialog/edit-node';
|
|||
import NewSimulatorDialog from '../components/dialog/new-simulator';
|
||||
import EditSimulatorDialog from '../components/dialog/edit-simulator';
|
||||
import NodeTree from '../components/node-tree';
|
||||
import ImportNodeDialog from '../components/dialog/import-node';
|
||||
|
||||
class Simulators extends Component {
|
||||
static getStores() {
|
||||
|
@ -46,6 +48,8 @@ class Simulators extends Component {
|
|||
newNodeModal: false,
|
||||
deleteNodeModal: false,
|
||||
editNodeModal: false,
|
||||
importModal: false,
|
||||
exportModal: false,
|
||||
|
||||
addSimulatorModal: false,
|
||||
editSimulatorModal: false,
|
||||
|
@ -186,6 +190,41 @@ class Simulators extends Component {
|
|||
token: this.state.sessionToken
|
||||
});
|
||||
}
|
||||
|
||||
closeImportNodeModal(data) {
|
||||
this.setState({ importNodeModal: false });
|
||||
|
||||
if (data) {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'nodes/start-add',
|
||||
data,
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
exportNode(data) {
|
||||
const node = this.state.nodes.find((element) => {
|
||||
return element._id === data.id;
|
||||
});
|
||||
|
||||
// filter properties
|
||||
let simulator = Object.assign({}, node);
|
||||
delete simulator._id;
|
||||
|
||||
simulator.simulators.forEach(simulator => {
|
||||
delete simulator.id;
|
||||
});
|
||||
|
||||
// show save dialog
|
||||
const blob = new Blob([JSON.stringify(simulator, null, 2)], { type: 'application/json' });
|
||||
FileSaver.saveAs(blob, 'node - ' + node.name + '.json');
|
||||
}
|
||||
|
||||
labelStyle(value) {
|
||||
if (value === true) return 'success';
|
||||
else return 'warning';
|
||||
}
|
||||
|
||||
onTreeDataChange(nodes) {
|
||||
// update all at once
|
||||
|
@ -198,27 +237,74 @@ class Simulators extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
onNodeModalKeyPress = (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
|
||||
this.confirmDeleteNodeModal();
|
||||
}
|
||||
}
|
||||
|
||||
onSimulatorModalKeyPress = (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
|
||||
this.confirmDeleteSimulatorModal();
|
||||
}
|
||||
}
|
||||
|
||||
loadFile(fileList) {
|
||||
// get file
|
||||
const file = fileList[0];
|
||||
if (!file.type.match('application/json')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create file reader
|
||||
var reader = new FileReader();
|
||||
var self = this;
|
||||
|
||||
reader.onload = function(event) {
|
||||
// read simulator
|
||||
const simulator = JSON.parse(event.target.result);
|
||||
self.setState({ importModal: true, modalSimulator: simulator });
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='section'>
|
||||
<h1>Simulators</h1>
|
||||
|
||||
<Button onClick={() => this.setState({ newNodeModal: true })}><Glyphicon glyph="plus" /> Node</Button>
|
||||
<Button onClick={() => this.setState({ importNodeModal: true })}><Glyphicon glyph="import" /> Import</Button>
|
||||
|
||||
<br />
|
||||
<small><i>Hint: Node names must be unique. Simulator names must be unique on a node.</i></small>
|
||||
|
||||
<NodeTree data={this.state.nodes} onDataChange={(treeData) => this.onTreeDataChange(treeData)} onNodeDelete={(node) => this.showDeleteNodeModal(node)} onNodeEdit={(node) => this.showEditNodeModal(node)} onNodeAdd={(node) => this.showAddSimulatorModal(node)} onSimulatorEdit={(node, index) => this.showEditSimulatorModal(node, index)} onSimulatorDelete={(node, index) => this.showDeleteSimulatorModal(node, index)} />
|
||||
<NodeTree
|
||||
data={this.state.nodes}
|
||||
onDataChange={(treeData) => this.onTreeDataChange(treeData)}
|
||||
onNodeDelete={(node) => this.showDeleteNodeModal(node)}
|
||||
onNodeEdit={(node) => this.showEditNodeModal(node)}
|
||||
onNodeAdd={(node) => this.showAddSimulatorModal(node)}
|
||||
onNodeExport={node => this.exportNode(node)}
|
||||
onSimulatorEdit={(node, index) => this.showEditSimulatorModal(node, index)}
|
||||
onSimulatorDelete={(node, index) => this.showDeleteSimulatorModal(node, index)}
|
||||
/>
|
||||
|
||||
<NewNodeDialog show={this.state.newNodeModal} onClose={(data) => this.closeNewNodeModal(data)} nodes={this.state.nodes} />
|
||||
<EditNodeDialog node={this.state.modalData} show={this.state.editNodeModal} onClose={(data) => this.closeEditNodeModal(data)} nodes={this.state.nodes} />
|
||||
<NewSimulatorDialog show={this.state.addSimulatorModal} onClose={(data) => this.closeAddSimulatorModal(data)} node={this.state.modalData}/>
|
||||
<ImportNodeDialog show={this.state.importNodeModal} onClose={data => this.closeImportNodeModal(data)} nodes={this.state.nodes} />
|
||||
|
||||
{this.state.editSimulatorModal &&
|
||||
<EditSimulatorDialog simulator={this.state.modalData.simulators[this.state.modalIndex]} show={this.state.editSimulatorModal} onClose={(data) => this.closeEditSimulatorModal(data)} node={this.state.modalData} />
|
||||
}
|
||||
|
||||
<Modal show={this.state.deleteNodeModal}>
|
||||
<Modal keyboard show={this.state.deleteNodeModal} onHide={() => this.setState({ deleteNodeModal: false })} onKeyPress={this.onNodeModalKeyPress}>
|
||||
<Modal.Header>
|
||||
<Modal.Title>Delete Node</Modal.Title>
|
||||
</Modal.Header>
|
||||
|
@ -235,7 +321,7 @@ class Simulators extends Component {
|
|||
</Modal.Footer>
|
||||
</Modal>
|
||||
|
||||
<Modal show={this.state.deleteSimulatorModal}>
|
||||
<Modal keyboard show={this.state.deleteSimulatorModal} onHide={() => this.setState({ deleteSimulatorModal: false })} onKeyPress={this.onSimulatorModalKeyPress}>
|
||||
<Modal.Header>
|
||||
<Modal.Title>Delete Simulator</Modal.Title>
|
||||
</Modal.Header>
|
||||
|
|
|
@ -100,6 +100,14 @@ class Users extends Component {
|
|||
return HUMAN_ROLE_NAMES.hasOwnProperty(role_key)? HUMAN_ROLE_NAMES[role_key] : '';
|
||||
}
|
||||
|
||||
onModalKeyPress = (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
|
||||
this.confirmDeleteModal();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
return (
|
||||
|
@ -119,7 +127,7 @@ class Users extends Component {
|
|||
|
||||
<EditUserDialog show={this.state.editModal} onClose={(data) => this.closeEditModal(data)} user={this.state.modalData} />
|
||||
|
||||
<Modal show={this.state.deleteModal}>
|
||||
<Modal keyboard show={this.state.deleteModal} onHide={() => this.setState({ deleteModal: false })} onKeyPress={this.onModalKeyPress}>
|
||||
<Modal.Header>
|
||||
<Modal.Title>Delete user</Modal.Title>
|
||||
</Modal.Header>
|
||||
|
|
|
@ -467,6 +467,7 @@ class Visualization extends React.Component {
|
|||
<ToolboxItem name="Slider" type="widget" disabled />
|
||||
<ToolboxItem name="Gauge" type="widget" />
|
||||
<ToolboxItem name="Box" type="widget" />
|
||||
<ToolboxItem name="HTML" type="html" />
|
||||
</div>
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ import WidgetNumberInput from '../components/widget-number-input';
|
|||
import WidgetSlider from '../components/widget-slider';
|
||||
import WidgetGauge from '../components/widget-gauge';
|
||||
import WidgetBox from '../components/widget-box';
|
||||
import WidgetHTML from '../components/widget-html';
|
||||
|
||||
import '../styles/widgets.css';
|
||||
|
||||
|
@ -173,6 +174,8 @@ class Widget extends Component {
|
|||
element = <WidgetGauge widget={widget} data={this.state.simulatorData} editing={this.props.editing} simulation={this.props.simulation} />
|
||||
} else if (widget.type === 'Box') {
|
||||
element = <WidgetBox widget={widget} editing={this.props.editing} />
|
||||
} else if (widget.type === 'HTML') {
|
||||
element = <WidgetHTML widget={widget} editing={this.props.editing} />
|
||||
}
|
||||
|
||||
const widgetClasses = classNames({
|
||||
|
|
|
@ -28,20 +28,25 @@ class FilesDataManager extends RestDataManager {
|
|||
super('file', '/files');
|
||||
}
|
||||
|
||||
upload(file, token = null) {
|
||||
RestAPI.upload(this.makeURL('/upload'), file, token).then(response => {
|
||||
upload(file, token = null, progressCallback = null, finishedCallback = null) {
|
||||
RestAPI.upload(this.makeURL('/upload'), file, token, progressCallback).then(response => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/uploaded'
|
||||
});
|
||||
|
||||
// Trigger a files reload
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/start-load',
|
||||
token: token
|
||||
token
|
||||
});
|
||||
|
||||
if (finishedCallback) {
|
||||
finishedCallback();
|
||||
}
|
||||
}).catch(error => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/upload-error',
|
||||
error: error
|
||||
error
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,4 +21,4 @@
|
|||
|
||||
import RestDataManager from './rest-data-manager';
|
||||
|
||||
export default new RestDataManager('simulation', '/simulations');
|
||||
export default new RestDataManager('simulation', '/simulations', [ '_id', 'name', 'projects', 'models' ]);
|
||||
|
|
152
src/img/eonerc_rwth.svg
Normal file
152
src/img/eonerc_rwth.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 27 KiB |
272
src/img/european_commission.svg
Normal file
272
src/img/european_commission.svg
Normal file
|
@ -0,0 +1,272 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
width="300"
|
||||
height="207.72354"
|
||||
id="svg15656">
|
||||
<defs
|
||||
id="defs15658" />
|
||||
<metadata
|
||||
id="metadata15661">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
transform="translate(-206.87716,-301.85899)"
|
||||
id="layer1">
|
||||
<g
|
||||
transform="matrix(4.5286013,0,0,4.5286013,-1067.5668,-2047.9402)"
|
||||
id="g15525">
|
||||
<path
|
||||
d="m 281.42172,530.9313 c 0,0 19.6275,-2.54 20.18125,-2.63 0.85,-0.13875 1.60375,-0.30125 2.3075,-0.495 1.5875,-0.435 3.04875,-1.09875 4.3425,-1.97375 1.24125,-0.83125 2.395,-2.04125 3.53875,-3.36875 0.72625,-0.845 1.49,-1.94625 2.2025,-3.00875 l 0,-0.51375 c -0.845,1.25125 -1.6775,2.33 -2.53375,3.28125 -1.135,1.26375 -2.325,2.29625 -3.53875,3.07125 -1.2525,0.8075 -2.66125,1.40875 -4.19,1.79 -0.685,0.1725 -1.4175,0.3125 -2.2425,0.4275 -0.5425,0.0762 -1.1025,0.135 -1.64375,0.19125 -0.21625,0.0238 -18.42375,1.955 -18.42375,1.955 l 0,1.27375 z"
|
||||
id="path5555"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 307.74272,527.64055 c -1.22875,0.70625 -2.6475,1.22375 -4.2175,1.53625 -0.67,0.13625 -1.40125,0.24625 -2.23375,0.33625 -0.49,0.0537 -0.9825,0.0962 -1.47625,0.13875 -0.26625,0.0225 -0.5325,0.0462 -0.79875,0.07 -6.015,0.54 -11.99875,1.11375 -17.595,1.65625 l 0,1.27375 c 5.64,-0.68 11.66625,-1.40125 17.67875,-2.08125 0.2675,-0.0312 0.535,-0.0612 0.8025,-0.09 0.4925,-0.0538 0.98625,-0.10875 1.48,-0.17375 0.85625,-0.11375 1.605,-0.245 2.28875,-0.4 1.63125,-0.365 3.10375,-0.94 4.3775,-1.71125 1.265,-0.75875 2.3825,-1.8225 3.585,-3.1225 0.74875,-0.80875 1.58125,-1.95125 2.36125,-3.02375 l 0,-0.4675 c -0.91875,1.245 -1.79,2.29125 -2.65125,3.18375 -1.17375,1.21625 -2.385,2.1825 -3.60125,2.875"
|
||||
id="path5559"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 311.1776,527.32868 c -1.1975,1.09875 -2.4475,1.9625 -3.71625,2.5675 -1.24125,0.59875 -2.62875,1.01875 -4.245,1.28375 -0.66625,0.11125 -1.39125,0.20125 -2.215,0.27625 l -19.58,1.6075 0,1.27 17.405,-1.84625 2.25125,-0.23625 c 0.84875,-0.0987 1.58875,-0.20875 2.26125,-0.33625 1.67375,-0.31625 3.11,-0.7875 4.38875,-1.4425 1.3075,-0.66125 2.61375,-1.76125 3.82875,-2.92625 0.815,-0.78125 1.6,-1.79 2.43875,-2.8475 l 0,-0.42375 c -0.96875,1.205 -1.89375,2.2075 -2.8175,3.05375"
|
||||
id="path5563"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 306.4431,533.92568 c -1.14,0.6375 -1.9925,1.005 -3.62,1.175 -0.98375,0.10625 -1.995,0.15625 -2.97375,0.205 -0.4725,0.0237 -0.94625,0.0475 -1.42,0.0775 -2.9575,0.16875 -5.8625,0.35625 -8.82625,0.54875 l -8.18125,0.55125 0,1.27 8.26375,-0.7575 c 2.67,-0.24 5.7725,-0.51625 8.80125,-0.76375 l 2.2025,-0.17 c 0.86,-0.0662 1.565,-0.13625 2.2175,-0.22125 1.675,-0.21625 3.10625,-0.555 4.37375,-1.03375 1.365,-0.51 2.6525,-1.3025 3.94125,-2.235 1.1625,-0.84 2.77125,-2.49625 2.7725,-2.5175 l 0,-0.44875 c -1.05125,1.025 -1.65625,1.60125 -2.8075,2.36 -1.37625,0.90625 -3.72125,1.38875 -4.74375,1.96"
|
||||
id="path5567"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 306.85935,536.33993 c -1.235,0.3625 -2.625,0.6075 -4.25,0.74875 -0.65875,0.0575 -1.36875,0.10125 -2.17125,0.13375 l -2.19,0.0962 c -5.44125,0.2525 -10.8575,0.535 -16.82625,0.85125 l 0,1.26625 c 5.7025,-0.44125 11.29,-0.87 16.87625,-1.2675 l 2.1825,-0.14875 c 0.84375,-0.0563 1.5425,-0.11625 2.2,-0.19 1.665,-0.18625 3.09,-0.47375 4.355,-0.87875 1.40125,-0.44375 2.7675,-1.0875 4.06,-1.9125 0.95375,-0.6075 1.90875,-1.3325 2.89875,-2.19625 l 0,-0.0225 c -1.09125,0.93125 -2.13125,1.13875 -3.1675,1.76375 -1.27,0.77 -2.605,1.36 -3.9675,1.75625"
|
||||
id="path5571"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 306.65735,538.44468 c -1.24375,0.29125 -2.5925,0.4775 -4.24375,0.58875 -0.63625,0.0425 -1.32125,0.0738 -2.155,0.1 l -2.16875,0.0775 c -5.465,0.20625 -11.00625,0.44375 -16.66875,0.69 l 0,1.26625 c 5.62625,-0.38375 11.145,-0.7575 16.71125,-1.105 l 2.16375,-0.13 c 0.84875,-0.0488 1.54,-0.0975 2.17625,-0.15625 1.68125,-0.155 3.0575,-0.38125 4.33,-0.71 1.4275,-0.3675 2.815,-0.9075 4.12375,-1.6025 1.0125,-0.53625 2.0225,-1.18125 3.06875,-1.95875 l 0,-0.54125 c -1.13125,0.81625 -2.21625,1.4825 -3.30125,2.025 -1.28625,0.64375 -2.64375,1.13375 -4.03625,1.45625"
|
||||
id="path5575"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 306.4721,540.50831 c -1.24875,0.22875 -2.5925,0.375 -4.23125,0.45875 -0.61625,0.0325 -1.27625,0.055 -2.13875,0.075 l -18.68,0.505 0,1.26625 16.56375,-0.865 2.14625,-0.10875 c 0.8375,-0.04 1.5225,-0.08 2.15625,-0.12875 1.66125,-0.1275 3.0275,-0.31 4.3,-0.57625 1.445,-0.3 2.8475,-0.7375 4.1675,-1.3 1.06375,-0.45125 2.12875,-1.00125 3.23875,-1.67 l 0,-0.50875 c -1.185,0.69 -2.3125,1.2425 -3.4325,1.68625 -1.30125,0.5175 -2.6775,0.91 -4.09,1.16625"
|
||||
id="path5579"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 306.32547,542.58081 c -2.2925,0.30625 -4.65125,0.34125 -6.93375,0.375 -0.5225,0.007 -1.04625,0.015 -1.56875,0.0263 l -16.40125,0.295 0,1.26375 16.43,-0.7075 c 0.52,-0.0237 1.0425,-0.045 1.565,-0.065 2.2975,-0.0925 4.67375,-0.1875 6.9975,-0.55625 1.47,-0.23125 2.885,-0.575 4.20625,-1.01875 1.09375,-0.36625 2.22625,-0.84 3.37375,-1.4 l 0,-0.485 c -1.20625,0.56125 -2.3925,1.02875 -3.53125,1.37875 -1.305,0.4025 -2.69625,0.70375 -4.1375,0.89375"
|
||||
id="path5583"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 306.19522,544.6663 c -2.1475,0.1775 -4.345,0.1775 -6.47125,0.1775 -0.66125,0 -1.3225,0 -1.98375,0.005 -5.43375,0.0238 -10.82375,0.0688 -16.31875,0.11625 l 0,1.26375 c 5.23375,-0.1775 10.785,-0.3625 16.3375,-0.5275 0.65875,-0.0225 1.3175,-0.0387 1.9775,-0.0562 2.1375,-0.0537 4.3475,-0.10875 6.5175,-0.34375 1.515,-0.1625 2.895,-0.40125 4.21625,-0.7275 1.1525,-0.28375 2.33625,-0.66125 3.525,-1.115 l 0,-0.465 c -1.2275,0.44 -2.45,0.8 -3.64125,1.0625 -1.30875,0.28875 -2.66875,0.4875 -4.15875,0.61"
|
||||
id="path5587"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 306.11223,546.72218 c -0.895,0.035 -1.86375,0.0525 -2.9625,0.0525 -0.415,0 -0.83125,-0.002 -1.245,-0.006 l -4.2275,-0.0512 c -2.88375,-0.0213 -5.7575,-0.0263 -8.45375,-0.0287 l -7.80125,0.007 0,1.2625 7.82,-0.20375 c 2.68625,-0.0662 5.5675,-0.135 8.44375,-0.18625 l 4.22375,-0.0563 c 1.15875,-0.0175 2.6875,-0.0525 4.23625,-0.15375 1.5175,-0.0987 2.89875,-0.25 4.2225,-0.46125 1.19125,-0.19 2.40875,-0.44625 3.62625,-0.7575 l 0,-0.4475 c -1.25125,0.2875 -2.495,0.51875 -3.70125,0.68 -1.31625,0.17625 -2.68375,0.29 -4.18125,0.35"
|
||||
id="path5591"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 301.8556,548.7088 -4.2125,-0.045 c -5.375,-0.0763 -10.74375,-0.17375 -16.22125,-0.27625 l 0,1.26375 c 5.2275,-0.0337 10.765,-0.0738 16.225,-0.135 l 4.215,-0.0625 c 1.42,-0.025 2.81125,-0.0512 4.2175,-0.0975 2.89625,-0.0925 5.49125,-0.29625 7.915,-0.6175 l 0,-0.44 c -2.43375,0.2625 -5.03125,0.4 -7.93,0.41875 -1.40125,0.0125 -2.805,0.003 -4.20875,-0.009"
|
||||
id="path5595"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 313.99473,550.94356 -32.5725,-0.82625 0,1.26375 32.5725,0 0,-0.4375 z"
|
||||
id="path5599"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 311.06173,529.71581 c -1.2375,0.99375 -2.1925,1.7 -3.7075,2.30125 -1.31875,0.525 -2.735,0.73875 -4.35375,0.96 -1.04125,0.14625 -2.13,0.36125 -3.17,0.43125 -0.4275,0.0287 -0.855,0.0562 -1.28125,0.09 l -17.1275,1.24875 0,1.2675 17.19625,-1.6675 2.23,-0.20875 c 0.82625,-0.0788 1.56125,-0.16875 2.24375,-0.28 1.66625,-0.2675 3.1025,-0.675 4.39,-1.24875 1.335,-0.58875 2.68625,-1.55375 3.9425,-2.61 0.85375,-0.7175 1.67625,-1.62 2.57,-2.60625 l 0,-0.45375 c -1.08125,1.1175 -1.97875,2.01125 -2.9325,2.77625"
|
||||
id="path5603"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 294.96035,563.49518 28.5325,0 0,-12.68875 -28.5325,0 0,12.68875 z"
|
||||
id="path5605"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 323.49785,532.29018 -28.5375,0 0,19.09 28.5375,0 0,-19.09 z"
|
||||
id="path5607"
|
||||
style="fill:#254aa5;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 309.53872,535.00493 0.9925,0 -0.8,0.62375 0.31125,0.98875 -0.7975,-0.60875 -0.7975,0.60875 0.315,-0.98875 -0.8075,-0.62375 0.99,0 0.3,-0.965 0.29375,0.965 z"
|
||||
id="path5611"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 309.57373,547.73581 0.995,0 -0.80125,0.60125 0.31125,0.95 -0.7975,-0.585 -0.79625,0.585 0.31375,-0.95 -0.80875,-0.60125 0.99125,0 0.3,-0.9275 0.2925,0.9275 z"
|
||||
id="path5615"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 312.72285,546.8928 0.995,0 -0.80125,0.6 0.31125,0.95125 -0.7975,-0.5875 -0.79625,0.5875 0.31375,-0.95125 -0.80875,-0.6 0.99125,0 0.3,-0.92875 0.2925,0.92875 z"
|
||||
id="path5619"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 312.72285,535.85418 0.995,0 -0.80125,0.6 0.31125,0.95125 -0.7975,-0.58625 -0.79625,0.58625 0.31375,-0.95125 -0.80875,-0.6 0.99125,0 0.3,-0.9275 0.2925,0.9275 z"
|
||||
id="path5623"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 315.06097,538.19205 0.9925,0 -0.8,0.59875 0.31125,0.9525 -0.7975,-0.58625 -0.795,0.58625 0.31375,-0.9525 -0.80875,-0.59875 0.99,0 0.3,-0.92875 0.29375,0.92875 z"
|
||||
id="path5627"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 315.06097,544.58668 0.9925,0 -0.8,0.60125 0.31125,0.9525 -0.7975,-0.5875 -0.795,0.5875 0.31375,-0.9525 -0.80875,-0.60125 0.99,0 0.3,-0.92875 0.29375,0.92875 z"
|
||||
id="path5631"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 315.93698,541.34055 0.995,0 -0.80125,0.6 0.31125,0.9525 -0.7975,-0.5875 -0.79625,0.5875 0.31375,-0.9525 -0.80875,-0.6 0.99,0 0.30125,-0.9275 0.2925,0.9275 z"
|
||||
id="path5635"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 306.3281,535.86193 0.9925,0 -0.8,0.60125 0.31125,0.95125 -0.7975,-0.5875 -0.7975,0.5875 0.315,-0.95125 -0.8075,-0.60125 0.99,0 0.3,-0.9275 0.29375,0.9275 z"
|
||||
id="path5639"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 304.05485,538.1998 0.995,0 -0.80125,0.6 0.31,0.9525 -0.79625,-0.5875 -0.79625,0.5875 0.3125,-0.9525 -0.8075,-0.6 0.99125,0 0.3,-0.9275 0.2925,0.9275 z"
|
||||
id="path5643"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 303.2111,541.34843 0.99375,0 -0.8,0.6 0.31,0.9525 -0.79625,-0.58625 -0.7975,0.58625 0.31375,-0.9525 -0.80625,-0.6 0.99,0 0.3,-0.9275 0.2925,0.9275 z"
|
||||
id="path5647"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 304.05485,544.59456 0.995,0 -0.80125,0.60125 0.31,0.9525 -0.79625,-0.5875 -0.79625,0.5875 0.3125,-0.9525 -0.8075,-0.60125 0.99125,0 0.3,-0.92625 0.2925,0.92625 z"
|
||||
id="path5651"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 306.36122,546.90068 0.9925,0 -0.8,0.6 0.30875,0.95125 -0.795,-0.58625 -0.7975,0.58625 0.315,-0.95125 -0.8075,-0.6 0.99,0 0.3,-0.92875 0.29375,0.92875 z"
|
||||
id="path5655"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 294.96035,564.74893 28.54,0 0,-1.62875 -28.54,0 0,1.62875 z"
|
||||
id="path5657"
|
||||
style="fill:#254aa5;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 327.55922,518.87968 c 0,0 5.95,9.26625 7.03875,10.97 1.0875,1.7025 2.33125,3.05 6.73375,3.965 4.4025,0.915 6.31875,1.3475 6.31875,1.3475 l 0,0.33 c 0,0 -2.7975,-0.585 -6.3975,-1.3725 -3.59875,-0.7875 -5.07,-1.05375 -6.71125,-3.29125 -1.36625,-1.8625 -6.9825,-9.77875 -6.9825,-9.77875 l 0,-2.17 z"
|
||||
id="path5661"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 327.55922,521.51443 c 0,0 6.14875,8.80875 6.965,9.88875 0.81625,1.08 1.915,2.82 6.775,3.8475 1.255,0.265 6.3675,1.27125 6.3675,1.27125 l 0,0.27875 c 0,0 -3.885,-0.74875 -6.3675,-1.27125 -2.48375,-0.5225 -5.0925,-0.99875 -6.7575,-3.09875 -1.5125,-1.9075 -6.99375,-8.9425 -6.99375,-8.9425 l 0.0112,-1.97375 z"
|
||||
id="path5665"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 327.55922,524.16068 c 0,0 5.79375,7.48625 6.965,8.98375 1.17,1.49625 2.78375,2.7325 6.775,3.49875 2.30125,0.4425 6.35,1.18375 6.35,1.18375 l 0,0.27875 c 0,0 -3.5475,-0.66125 -6.35,-1.16625 -2.8025,-0.505 -5.11625,-0.99375 -6.775,-2.855 -1.455,-1.63375 -6.97625,-8.055 -6.97625,-8.055 l 0.0112,-1.86875 z"
|
||||
id="path5669"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 327.54773,526.76056 c 0,0 5.86625,6.79125 6.99375,8.0025 1.75625,1.88625 2.6075,2.53625 6.7575,3.325 1.66,0.31625 6.35,1.1325 6.35,1.1325 l 0,0.295 c 0,0 -3.88375,-0.69625 -6.35,-1.13125 -2.465,-0.435 -4.55125,-0.5975 -6.775,-2.73375 -1.69375,-1.62625 -6.965,-7.1375 -6.965,-7.1375 l -0.0113,-1.7525 z"
|
||||
id="path5673"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 327.55922,529.46493 c 0,0 5.41625,5.56375 6.9825,7.16125 1.66125,1.69375 3.79,2.32125 6.74,2.8375 2.81625,0.4925 6.3675,1.0625 6.3675,1.0625 l 0,0.3125 c 0,0 -3.335,-0.535 -6.3675,-1.04375 -2.97375,-0.5 -5.055,-0.7775 -6.7575,-2.31625 -1.525,-1.3775 -6.965,-6.3775 -6.965,-6.3775 l 0,-1.63625 z"
|
||||
id="path5677"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 327.55922,531.9718 c 0,0 5.15125,4.68875 6.9825,6.29125 1.685,1.47375 3.05125,1.9325 6.7575,2.57625 3.7075,0.64375 6.35,1.04375 6.35,1.04375 l 0,0.33125 c 0,0 -3.3525,-0.54 -6.3675,-1.01 -3.015,-0.47 -4.93625,-0.655 -6.74,-2.08875 -2.08125,-1.655 -6.9825,-5.61125 -6.9825,-5.61125 l 0,-1.5325 z"
|
||||
id="path5681"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 327.54748,534.65293 c 0,0 5.87625,4.5675 6.97625,5.3675 1.1,0.80125 2.55375,1.735 6.775,2.33375 4.18625,0.5925 6.3325,0.8875 6.3325,0.8875 l 0,0.34875 c 0,0 -3.795,-0.54125 -6.3325,-0.85375 -2.53625,-0.31375 -4.48125,-0.44625 -6.7575,-1.9325 -2.21625,-1.44625 -6.9825,-4.735 -6.9825,-4.735 l -0.0113,-1.41625 z"
|
||||
id="path5685"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 327.54748,537.21793 c 0,0 5.89375,3.8475 7.01125,4.4575 1.11875,0.60875 2.43125,1.60125 6.74,2.14125 4.31,0.53875 6.3325,0.7825 6.3325,0.7825 l 0,0.33125 c 0,0 -3.565,-0.3825 -6.35,-0.67875 -2.785,-0.29625 -4.6825,-0.69625 -6.7575,-1.81125 -2.075,-1.11375 -6.965,-3.91125 -6.965,-3.91125 l -0.0113,-1.31125 z"
|
||||
id="path5689"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 327.55922,539.78293 c 0,0 5.21625,2.78125 6.9825,3.65 1.96875,0.96875 3.67125,1.43375 6.7575,1.7925 3.03,0.355 6.35,0.6975 6.35,0.6975 l 0,0.31375 c 0,0 -3.07,-0.3225 -6.35,-0.6275 -3.25125,-0.30125 -4.41,-0.44125 -6.7575,-1.4625 -2.1,-0.9125 -6.99375,-3.145 -6.99375,-3.145 l 0.0112,-1.21875 z"
|
||||
id="path5693"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 327.55922,542.39431 c 0,0 4.10375,1.7225 7,2.84875 2.85,1.10875 5.02625,1.2025 6.7575,1.39375 0.7625,0.0838 6.315,0.64375 6.315,0.64375 l 0,0.3125 c 0,0 -3.3175,-0.26 -6.3325,-0.52125 -3.015,-0.26125 -4.31625,-0.37125 -6.7575,-1.16625 -2.47125,-0.80375 -6.99375,-2.35 -6.99375,-2.35 l 0.0112,-1.16125 z"
|
||||
id="path5697"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 327.55922,545.04056 c 0,0 4.1625,1.265 7,1.99625 2.8375,0.73125 5.445,1.0275 6.7575,1.11375 1.3125,0.0875 6.315,0.4875 6.315,0.4875 l 0,0.29625 c 0,0 -2.8375,-0.19 -6.3325,-0.435 -2.82625,-0.1975 -5.09625,-0.40625 -6.7575,-0.76625 -1.84125,-0.39875 -6.99375,-1.60125 -6.99375,-1.60125 l 0.0112,-1.09125 z"
|
||||
id="path5701"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 327.54773,547.65218 c 0,0 3.415,0.57625 7.01125,1.16 2.69625,0.43875 6.35,0.715 6.74,0.73125 0.39,0.0175 6.35,0.41875 6.35,0.41875 l 0,0.3125 c 0,0 -3.81375,-0.1925 -6.35,-0.33 -2.64125,-0.14375 -5.26625,-0.3 -6.7575,-0.4525 -3.34,-0.3425 -6.99375,-0.795 -6.99375,-0.795 l 0,-1.045 z"
|
||||
id="path5705"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 327.55922,550.35593 c 0,0 5.36875,0.1975 7,0.2325 1.63125,0.035 13.09,0.4875 13.09,0.4875 l 0,0.31375 -20.09,-0.0125 0,-1.02125 z"
|
||||
id="path5709"
|
||||
style="fill:#bdbcbc;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 296.6286,553.26193 c 0,-0.0588 0.0275,-0.09 0.09,-0.09 l 1.65625,0 c 0.0538,0 0.0863,0.0312 0.0863,0.085 l 0,0.28 c 0,0.0538 -0.0225,0.0813 -0.09,0.0813 l -1.22875,0 0,0.77375 1.08875,0 c 0.0587,0 0.09,0.0275 0.09,0.0812 l 0,0.27875 c 0,0.0538 -0.0225,0.0812 -0.09,0.0812 l -1.08875,0 0,0.88625 1.26,0 c 0.0537,0 0.0813,0.0275 0.0813,0.0863 l 0,0.27375 c 0,0.0588 -0.0225,0.0812 -0.0863,0.0812 l -1.67875,0 c -0.0625,0 -0.09,-0.0275 -0.09,-0.0812 l 0,-2.8175 z"
|
||||
id="path5713"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 300.5711,556.16093 -0.32,0 c -0.0675,0 -0.085,-0.0312 -0.085,-0.085 l 0,-0.14 -0.009,-0.005 c -0.17625,0.1225 -0.4825,0.27 -0.75625,0.27 -0.6075,0 -0.66625,-0.36 -0.66625,-0.8325 l 0,-1.32375 c 0,-0.0537 0.0225,-0.0888 0.085,-0.0888 l 0.32875,0 c 0.0588,0 0.0813,0.0312 0.0813,0.0888 l 0,1.265 c 0,0.27125 0.0537,0.44125 0.32875,0.44125 0.19375,0 0.49,-0.14375 0.6075,-0.2025 l 0,-1.50375 c 0,-0.0537 0.0175,-0.0888 0.085,-0.0888 l 0.32,0 c 0.0625,0 0.085,0.0312 0.085,0.0888 l 0,2.03125 c 0,0.0575 -0.0225,0.085 -0.085,0.085"
|
||||
id="path5717"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 301.5336,556.07543 c 0,0.0538 -0.0225,0.085 -0.085,0.085 l -0.325,0 c -0.0625,0 -0.085,-0.0263 -0.085,-0.085 l 0,-2.03 c 0,-0.0588 0.0225,-0.09 0.085,-0.09 l 0.32,0 c 0.0675,0 0.09,0.0362 0.09,0.09 l 0,0.1975 0.009,0.009 c 0.135,-0.125 0.455,-0.27875 0.66625,-0.31875 0.0588,-0.0138 0.0987,0 0.10875,0.0762 l 0.0312,0.30125 c 0.004,0.0638 0.009,0.09 -0.0862,0.10875 -0.25125,0.0488 -0.57125,0.13875 -0.72875,0.20625 l 0,1.45 z"
|
||||
id="path5721"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 303.42835,554.3423 c -0.3825,0 -0.46875,0.24375 -0.46875,0.71125 0,0.4775 0.0862,0.71125 0.46375,0.71125 0.41,0 0.49125,-0.23 0.49125,-0.71125 0,-0.4775 -0.0763,-0.71125 -0.48625,-0.71125 m -0.005,1.855 c -0.90375,0 -0.9625,-0.6625 -0.9625,-1.175 0,-0.42375 0.0937,-1.1125 0.9625,-1.1125 0.87375,0 0.995,0.58125 0.995,1.1125 0,0.5125 -0.0625,1.175 -0.995,1.175"
|
||||
id="path5725"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 305.76373,554.36943 c -0.185,0 -0.46875,0.13 -0.60375,0.2025 l 0,1.125 c 0.2025,0.0588 0.365,0.0763 0.51875,0.0763 0.32375,0 0.43625,-0.16625 0.43625,-0.7325 0,-0.5725 -0.1125,-0.67125 -0.35125,-0.67125 m -0.0362,1.8325 c -0.14875,0 -0.355,-0.0275 -0.5625,-0.1 l -0.005,0.005 0,0.7925 c 0,0.0487 -0.0125,0.09 -0.085,0.09 l -0.32,0 c -0.0712,0 -0.085,-0.0312 -0.085,-0.09 l 0,-2.85375 c 0,-0.0587 0.0225,-0.09 0.085,-0.09 l 0.32,0 c 0.0638,0 0.0813,0.035 0.0813,0.09 l 0,0.135 0.0138,0.009 c 0.16125,-0.1125 0.45,-0.27 0.71125,-0.27 0.58875,0 0.7425,0.445 0.7425,1.125 0,0.73375 -0.185,1.1575 -0.89625,1.1575"
|
||||
id="path5729"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 307.4016,554.85555 0.89125,0 c 0,-0.275 -0.0725,-0.54 -0.445,-0.54 -0.33375,0 -0.42375,0.2025 -0.44625,0.54 m -0.005,0.3375 c 0.0138,0.455 0.17625,0.58125 0.545,0.58125 0.1625,0 0.41375,-0.0325 0.585,-0.05 0.0813,-0.009 0.10875,-0.009 0.1225,0.0763 l 0.0263,0.14875 c 0.0138,0.0675 0.005,0.10375 -0.0762,0.14 -0.17625,0.0675 -0.50375,0.1125 -0.725,0.1125 -0.86,0 -0.98625,-0.54875 -0.98625,-1.135 0,-0.43625 0.0813,-1.15625 0.96,-1.15625 0.805,0 0.94875,0.5225 0.9625,1.0075 0.005,0.1625 -0.04,0.275 -0.22,0.275 l -1.19375,0 z"
|
||||
id="path5733"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 310.40323,555.15693 -0.54875,0 c -0.225,0 -0.37,0.0813 -0.37,0.37875 0,0.1975 0.0675,0.26125 0.28,0.26125 0.19375,0 0.4675,-0.11375 0.63875,-0.2125 l 0,-0.4275 z m 0.0275,0.7475 c -0.225,0.17125 -0.5275,0.2925 -0.82875,0.2925 -0.4775,0 -0.61625,-0.26125 -0.61625,-0.63875 -0.005,-0.5275 0.27875,-0.7525 0.84625,-0.7525 l 0.57625,0 0,-0.11625 c 0,-0.27 -0.1,-0.365 -0.46875,-0.365 -0.14875,0 -0.4325,0.0225 -0.6125,0.04 -0.09,0.0138 -0.1125,0.009 -0.125,-0.0537 l -0.0412,-0.16625 c -0.009,-0.0588 0.005,-0.095 0.10375,-0.135 0.19375,-0.0675 0.54875,-0.0987 0.76125,-0.0987 0.77875,0 0.8725,0.31875 0.8725,0.82375 l 0,0.86875 c 0,0.18375 0.0225,0.18875 0.1625,0.20625 0.0675,0.005 0.085,0.0187 0.085,0.0675 l 0,0.19 c 0,0.0488 -0.0312,0.08 -0.1075,0.0937 -0.0725,0.0138 -0.14375,0.0225 -0.21125,0.0225 -0.2125,0 -0.3425,-0.0625 -0.3875,-0.27375 l -0.009,-0.005 z"
|
||||
id="path5737"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 313.10785,556.16093 -0.32375,0 c -0.0588,0 -0.0863,-0.0275 -0.0863,-0.085 l 0.005,-1.265 c 0,-0.26625 -0.0625,-0.44125 -0.32375,-0.44125 -0.18,0 -0.495,0.14375 -0.6175,0.2025 l 0,1.50375 c 0,0.0538 -0.0225,0.085 -0.085,0.085 l -0.32,0 c -0.0625,0 -0.085,-0.0275 -0.085,-0.085 l 0,-2.03125 c 0,-0.0575 0.0225,-0.0888 0.085,-0.0888 l 0.32,0 c 0.0625,0 0.085,0.0312 0.085,0.0888 l 0,0.13625 c 0.005,0 0.01,0.004 0.0138,0.004 0.16625,-0.11625 0.48125,-0.27 0.75625,-0.27 0.6075,0 0.6625,0.40125 0.6625,0.855 l 0,1.30625 c 0,0.0538 -0.0188,0.085 -0.0863,0.085"
|
||||
id="path5741"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 297.80335,557.89043 c 0.22,0 0.585,0.0312 0.82375,0.13 0.0725,0.0325 0.0987,0.0688 0.085,0.14 l -0.04,0.185 c -0.0138,0.0538 -0.0362,0.0763 -0.1175,0.0675 -0.22,-0.0275 -0.49,-0.0587 -0.72375,-0.0587 -0.6125,0 -0.7025,0.495 -0.7025,1.08 0,0.59 0.1125,1.05375 0.7025,1.05375 0.26,0 0.48125,-0.0275 0.72375,-0.0588 0.0863,-0.009 0.10375,0.0138 0.1225,0.0725 l 0.035,0.17125 c 0.0187,0.0713 -0.004,0.1125 -0.0763,0.14375 -0.22,0.0937 -0.6125,0.135 -0.8325,0.135 -1.00375,0 -1.215,-0.7425 -1.215,-1.50875 0,-0.76875 0.18875,-1.5525 1.215,-1.5525"
|
||||
id="path5745"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 299.73385,559.0923 c -0.3825,0 -0.46875,0.24375 -0.46875,0.71125 0,0.4775 0.0863,0.71125 0.46375,0.71125 0.41,0 0.49125,-0.23 0.49125,-0.71125 0,-0.4775 -0.0763,-0.71125 -0.48625,-0.71125 m -0.005,1.855 c -0.90375,0 -0.9625,-0.6625 -0.9625,-1.175 0,-0.42375 0.0937,-1.1125 0.9625,-1.1125 0.87375,0 0.995,0.58125 0.995,1.1125 0,0.5125 -0.0625,1.175 -0.995,1.175"
|
||||
id="path5749"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 304.12648,560.91093 -0.32375,0 c -0.0637,0 -0.0863,-0.0275 -0.0863,-0.085 l 0,-1.27875 c 0,-0.33375 -0.08,-0.4275 -0.30625,-0.4275 -0.16625,0 -0.43625,0.13 -0.575,0.2025 0,0.045 0.004,0.12625 0.004,0.22875 l 0,1.275 c 0,0.0538 -0.0175,0.085 -0.085,0.085 l -0.32875,0 c -0.0587,0 -0.0763,-0.0275 -0.0763,-0.085 l 0,-1.30125 c 0,-0.275 -0.0725,-0.405 -0.2975,-0.405 -0.1575,0 -0.41,0.1125 -0.58125,0.2025 l 0,1.50375 c 0,0.0538 -0.0225,0.085 -0.085,0.085 l -0.32375,0 c -0.0588,0 -0.0813,-0.0275 -0.0813,-0.085 l 0,-2.03125 c 0,-0.0575 0.0225,-0.0888 0.0813,-0.0888 l 0.31875,0 c 0.0675,0 0.09,0.035 0.09,0.0888 l 0,0.13125 0.005,0.005 c 0.175,-0.1225 0.4225,-0.2575 0.67125,-0.26625 0.26,0 0.47625,0.0538 0.61625,0.315 0.225,-0.16625 0.50375,-0.315 0.7925,-0.315 0.59375,0 0.6525,0.3875 0.6525,0.8375 l 0,1.32375 c 0,0.0538 -0.0188,0.085 -0.0813,0.085"
|
||||
id="path5753"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 307.65447,560.91093 -0.32375,0 c -0.0637,0 -0.0863,-0.0275 -0.0863,-0.085 l 0,-1.27875 c 0,-0.33375 -0.08,-0.4275 -0.30625,-0.4275 -0.16625,0 -0.43625,0.13 -0.575,0.2025 0,0.045 0.004,0.12625 0.004,0.22875 l 0,1.275 c 0,0.0538 -0.0175,0.085 -0.085,0.085 l -0.32875,0 c -0.0587,0 -0.0763,-0.0275 -0.0763,-0.085 l 0,-1.30125 c 0,-0.275 -0.0725,-0.405 -0.2975,-0.405 -0.1575,0 -0.41,0.1125 -0.58125,0.2025 l 0,1.50375 c 0,0.0538 -0.0225,0.085 -0.085,0.085 l -0.32375,0 c -0.0588,0 -0.0813,-0.0275 -0.0813,-0.085 l 0,-2.03125 c 0,-0.0575 0.0225,-0.0888 0.0813,-0.0888 l 0.31875,0 c 0.0675,0 0.09,0.035 0.09,0.0888 l 0,0.13125 0.005,0.005 c 0.175,-0.1225 0.4225,-0.2575 0.67125,-0.26625 0.26,0 0.47625,0.0538 0.61625,0.315 0.225,-0.16625 0.50375,-0.315 0.7925,-0.315 0.59375,0 0.6525,0.3875 0.6525,0.8375 l 0,1.32375 c 0,0.0538 -0.0188,0.085 -0.0813,0.085"
|
||||
id="path5757"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 308.59422,560.82543 c 0,0.0488 -0.0175,0.085 -0.0813,0.085 l -0.32,0 c -0.0625,0 -0.09,-0.0263 -0.09,-0.085 l 0,-2.035 c 0,-0.0625 0.0275,-0.085 0.09,-0.085 l 0.32,0 c 0.0638,0 0.0813,0.0362 0.0813,0.085 l 0,2.035 z m -0.23875,-2.44375 c -0.23375,0 -0.265,-0.13125 -0.265,-0.26125 0,-0.14375 0.0488,-0.2575 0.265,-0.2575 0.22125,0 0.26625,0.10375 0.26625,0.2575 0,0.13875 -0.0412,0.26125 -0.26625,0.26125"
|
||||
id="path5761"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 309.71023,560.94705 c -0.225,0 -0.52625,-0.0312 -0.6975,-0.0987 -0.09,-0.0362 -0.10875,-0.0775 -0.095,-0.14875 l 0.0312,-0.14875 c 0.0138,-0.0725 0.045,-0.0762 0.1175,-0.0675 0.1975,0.0312 0.48625,0.0538 0.63875,0.0538 0.28875,0 0.40625,-0.0763 0.40625,-0.25625 0,-0.2075 -0.0775,-0.2525 -0.36125,-0.2925 -0.44125,-0.0638 -0.8275,-0.1575 -0.8275,-0.64875 0,-0.445 0.34125,-0.67875 0.84625,-0.67875 0.18375,0 0.495,0.0263 0.68375,0.0987 0.0763,0.0312 0.10875,0.0675 0.095,0.135 l -0.0362,0.16125 c -0.0138,0.0638 -0.04,0.0725 -0.12125,0.0638 -0.19375,-0.0225 -0.44625,-0.05 -0.6125,-0.05 -0.2875,0 -0.355,0.0812 -0.355,0.2525 0,0.175 0.1075,0.1975 0.3825,0.2425 0.4275,0.0637 0.81375,0.14875 0.81375,0.685 0,0.52625 -0.445,0.6975 -0.90875,0.6975"
|
||||
id="path5765"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 311.59573,560.94705 c -0.225,0 -0.52625,-0.0312 -0.6975,-0.0987 -0.09,-0.0362 -0.10875,-0.0775 -0.095,-0.14875 l 0.0312,-0.14875 c 0.0138,-0.0725 0.045,-0.0762 0.1175,-0.0675 0.1975,0.0312 0.48625,0.0538 0.63875,0.0538 0.28875,0 0.40625,-0.0763 0.40625,-0.25625 0,-0.2075 -0.0775,-0.2525 -0.36125,-0.2925 -0.44125,-0.0638 -0.8275,-0.1575 -0.8275,-0.64875 0,-0.445 0.34125,-0.67875 0.84625,-0.67875 0.18375,0 0.495,0.0263 0.68375,0.0987 0.0763,0.0312 0.10875,0.0675 0.095,0.135 l -0.0362,0.16125 c -0.0137,0.0638 -0.04,0.0725 -0.12125,0.0638 -0.19375,-0.0225 -0.44625,-0.05 -0.6125,-0.05 -0.2875,0 -0.355,0.0812 -0.355,0.2525 0,0.175 0.1075,0.1975 0.3825,0.2425 0.4275,0.0637 0.81375,0.14875 0.81375,0.685 0,0.52625 -0.445,0.6975 -0.90875,0.6975"
|
||||
id="path5769"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 313.29685,560.82543 c 0,0.0488 -0.0175,0.085 -0.0813,0.085 l -0.32,0 c -0.0625,0 -0.09,-0.0263 -0.09,-0.085 l 0,-2.035 c 0,-0.0625 0.0275,-0.085 0.09,-0.085 l 0.32,0 c 0.0638,0 0.0813,0.0362 0.0813,0.085 l 0,2.035 z m -0.23875,-2.44375 c -0.23375,0 -0.265,-0.13125 -0.265,-0.26125 0,-0.14375 0.0488,-0.2575 0.265,-0.2575 0.22125,0 0.26625,0.10375 0.26625,0.2575 0,0.13875 -0.0412,0.26125 -0.26625,0.26125"
|
||||
id="path5773"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 314.59735,559.0923 c -0.3825,0 -0.4675,0.24375 -0.4675,0.71125 0,0.4775 0.085,0.71125 0.4625,0.71125 0.41,0 0.49125,-0.23 0.49125,-0.71125 0,-0.4775 -0.0763,-0.71125 -0.48625,-0.71125 m -0.005,1.855 c -0.90375,0 -0.9625,-0.6625 -0.9625,-1.175 0,-0.42375 0.0937,-1.1125 0.9625,-1.1125 0.87375,0 0.995,0.58125 0.995,1.1125 0,0.5125 -0.0625,1.175 -0.995,1.175"
|
||||
id="path5777"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
d="m 317.66673,560.91093 -0.32375,0 c -0.0588,0 -0.0863,-0.0275 -0.0863,-0.085 l 0.005,-1.265 c 0,-0.26625 -0.0625,-0.44125 -0.32375,-0.44125 -0.18,0 -0.495,0.14375 -0.6175,0.2025 l 0,1.50375 c 0,0.0538 -0.0225,0.085 -0.085,0.085 l -0.32,0 c -0.0625,0 -0.085,-0.0275 -0.085,-0.085 l 0,-2.03125 c 0,-0.0575 0.0225,-0.0888 0.085,-0.0888 l 0.32,0 c 0.0625,0 0.085,0.0312 0.085,0.0888 l 0,0.13625 c 0.005,0 0.009,0.004 0.0138,0.004 0.16625,-0.11625 0.4825,-0.27 0.75625,-0.27 0.6075,0 0.6625,0.40125 0.6625,0.855 l 0,1.30625 c 0,0.0538 -0.0187,0.085 -0.0863,0.085"
|
||||
id="path5781"
|
||||
style="fill:#5a5758;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 30 KiB |
157
src/img/jara.svg
Normal file
157
src/img/jara.svg
Normal file
|
@ -0,0 +1,157 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
width="400"
|
||||
height="105"
|
||||
id="svg2985"
|
||||
xml:space="preserve"><metadata
|
||||
id="metadata2991"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs2989"><linearGradient
|
||||
x1="-128"
|
||||
y1="0"
|
||||
x2="128"
|
||||
y2="0"
|
||||
id="linearGradient3509"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0,0.0852966,2.3663025,0,343.026,855.076)"><stop
|
||||
id="stop3511"
|
||||
style="stop-color:white;stop-opacity:1"
|
||||
offset="0" /><stop
|
||||
id="stop3513"
|
||||
style="stop-color:#f9ac73;stop-opacity:1"
|
||||
offset="0.5" /><stop
|
||||
id="stop3515"
|
||||
style="stop-color:#f36717;stop-opacity:1"
|
||||
offset="1" /></linearGradient><linearGradient
|
||||
x1="-128"
|
||||
y1="0"
|
||||
x2="128"
|
||||
y2="0"
|
||||
id="linearGradient3593"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0,0.0852966,2.3663025,0,-260.886,855.076)"><stop
|
||||
id="stop3595"
|
||||
style="stop-color:white;stop-opacity:1"
|
||||
offset="0" /><stop
|
||||
id="stop3597"
|
||||
style="stop-color:#f9ac73;stop-opacity:1"
|
||||
offset="0.5" /><stop
|
||||
id="stop3599"
|
||||
style="stop-color:#f36717;stop-opacity:1"
|
||||
offset="1" /></linearGradient></defs><g
|
||||
transform="matrix(1.2232225,0,0,-1.2234375,0,105)"
|
||||
id="g2993"><g
|
||||
transform="matrix(2.6332484,0,0,2.6332484,-1060.8763,-547.07195)"
|
||||
id="g5001"
|
||||
style="fill:#3f80b0;fill-opacity:1"><path
|
||||
d="m 483.97399,225.734 c 0.45,0 0.601,0.014 0.779,0.079 0.25,0.093 0.4,0.329 0.4,0.629 0,0.243 -0.107,0.443 -0.307,0.558 -0.186,0.107 -0.315,0.121 -0.936,0.121 l -0.265,0 0,-1.387 0.329,0 z m -0.058,2.166 c 0.844,0 1.165,-0.05 1.495,-0.228 0.429,-0.229 0.679,-0.687 0.679,-1.237 0,-0.622 -0.286,-1.073 -0.844,-1.323 l 1.094,-1.981 -1.065,0 -0.973,1.824 -0.657,0 0,-1.824 -0.916,0 0,4.769 1.187,0"
|
||||
id="path3313"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 488.90699,225.262 c -0.071,0.458 -0.286,0.679 -0.672,0.679 -0.386,0 -0.63,-0.221 -0.744,-0.679 l 1.416,0 z m -1.437,-0.643 c 0.028,-0.558 0.329,-0.873 0.822,-0.873 0.322,0 0.544,0.122 0.658,0.379 l 0.779,-0.036 c -0.15,-0.65 -0.708,-1.051 -1.459,-1.051 -1.029,0 -1.68,0.68 -1.68,1.752 0,1.101 0.658,1.831 1.652,1.831 0.958,0 1.552,-0.701 1.552,-1.831 l 0,-0.171 -2.324,0"
|
||||
id="path3317"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 492.324,225.498 c -0.043,0.322 -0.214,0.472 -0.543,0.472 -0.272,0 -0.472,-0.15 -0.472,-0.351 0,-0.1 0.05,-0.193 0.143,-0.243 0.1,-0.064 0.172,-0.086 0.686,-0.236 0.515,-0.15 0.73,-0.257 0.866,-0.436 0.128,-0.15 0.192,-0.35 0.192,-0.572 0,-0.686 -0.507,-1.094 -1.38,-1.094 -0.993,0 -1.508,0.422 -1.508,1.23 l 0.829,0 c 0.029,-0.4 0.222,-0.565 0.644,-0.565 0.336,0 0.536,0.143 0.536,0.365 0,0.136 -0.057,0.2 -0.243,0.279 -0.1,0.043 -0.1,0.043 -0.7,0.221 -0.372,0.115 -0.566,0.208 -0.701,0.343 -0.15,0.151 -0.236,0.372 -0.236,0.615 0,0.658 0.543,1.102 1.358,1.102 0.83,0 1.33,-0.415 1.38,-1.13 l -0.851,0"
|
||||
id="path3321"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 496.057,225.262 c -0.071,0.458 -0.286,0.679 -0.672,0.679 -0.386,0 -0.63,-0.221 -0.744,-0.679 l 1.416,0 z m -1.437,-0.643 c 0.028,-0.558 0.329,-0.873 0.822,-0.873 0.322,0 0.544,0.122 0.658,0.379 l 0.779,-0.036 c -0.15,-0.65 -0.708,-1.051 -1.459,-1.051 -1.029,0 -1.68,0.68 -1.68,1.752 0,1.101 0.658,1.831 1.652,1.831 0.958,0 1.552,-0.701 1.552,-1.831 l 0,-0.171 -2.324,0"
|
||||
id="path3325"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 499.525,224.747 c -0.579,-0.071 -0.801,-0.129 -0.951,-0.236 -0.129,-0.093 -0.201,-0.229 -0.201,-0.372 0,-0.236 0.215,-0.407 0.515,-0.407 0.265,0 0.487,0.128 0.573,0.329 0.05,0.1 0.064,0.243 0.064,0.493 l 0,0.193 z m -1.931,0.765 c 0.05,0.386 0.158,0.601 0.4,0.786 0.258,0.194 0.644,0.301 1.081,0.301 0.585,0 1.008,-0.186 1.164,-0.508 0.101,-0.2 0.137,-0.407 0.137,-0.836 l 0,-1.366 c 0,-0.407 0.028,-0.579 0.114,-0.758 l -0.801,0 c -0.035,0.136 -0.042,0.215 -0.042,0.386 -0.186,-0.307 -0.487,-0.457 -0.937,-0.457 -0.779,0 -1.237,0.357 -1.237,0.965 0,0.472 0.272,0.829 0.772,1.015 0.308,0.122 0.357,0.129 1.28,0.293 l 0,0.057 c 0,0.372 -0.143,0.515 -0.515,0.515 -0.336,0 -0.529,-0.128 -0.579,-0.393 l -0.837,0"
|
||||
id="path3329"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 502.128,226.527 c 0.036,-0.15 0.057,-0.336 0.057,-0.529 0,-0.036 0,-0.1 -0.008,-0.179 0.273,0.551 0.566,0.78 0.981,0.78 0.093,0 0.121,-0.008 0.264,-0.043 l 0,-0.894 c -0.165,0.107 -0.343,0.164 -0.53,0.164 -0.464,0 -0.679,-0.357 -0.679,-1.115 l 0,-1.58 -0.879,0 0,2.352 c 0,0.15 0,0.351 -0.007,0.601 0,0.193 -0.007,0.257 -0.036,0.443 l 0.837,0"
|
||||
id="path3333"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 505.953,225.40499 c -0.051,0.336 -0.237,0.508 -0.544,0.508 -0.45,0 -0.743,-0.429 -0.743,-1.087 0,-0.679 0.272,-1.08 0.729,-1.08 0.351,0 0.543,0.194 0.593,0.594 l 0.851,0 c -0.043,-0.78 -0.622,-1.301 -1.444,-1.301 -0.973,0 -1.63,0.722 -1.63,1.787 0,1.08 0.657,1.795 1.644,1.795 0.808,0 1.302,-0.429 1.409,-1.216 l -0.865,0"
|
||||
id="path3337"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 508.40499,227.929 0,-1.773 c 0.265,0.293 0.586,0.443 0.98,0.443 0.429,0 0.815,-0.193 0.993,-0.5 0.129,-0.215 0.158,-0.415 0.158,-1.009 l 0,-1.959 -0.88,0 0,1.766 c 0,0.343 -0.021,0.508 -0.093,0.644 -0.093,0.186 -0.264,0.286 -0.486,0.286 -0.25,0 -0.479,-0.143 -0.586,-0.372 -0.057,-0.136 -0.086,-0.307 -0.086,-0.586 l 0,-1.738 -0.879,0 0,4.798 0.879,0"
|
||||
id="path3341"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 484.302,218.26899 -0.608,-1.945 1.223,0 -0.615,1.945 z m 0.465,1.051 1.622,-4.769 -0.95,0 -0.293,0.994 -1.681,0 -0.3,-0.994 -0.936,0 1.608,4.769 0.93,0"
|
||||
id="path3345"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 486.955,214.551 0.879,0 0,4.798 -0.879,0 0,-4.798 z"
|
||||
id="path3347"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 488.82,214.551 0.88,0 0,4.798 -0.88,0 0,-4.798 z"
|
||||
id="path3349"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 490.68701,217.94799 0.879,0 0,-3.397 -0.879,0 0,3.397 z m 0.879,0 z m 0.071,0.958 c 0,-0.315 -0.2,-0.515 -0.507,-0.515 -0.315,0 -0.514,0.193 -0.514,0.501 0,0.314 0.214,0.521 0.528,0.521 0.293,0 0.493,-0.207 0.493,-0.507"
|
||||
id="path3353"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 494.39801,216.167 c -0.579,-0.071 -0.801,-0.128 -0.952,-0.236 -0.128,-0.093 -0.2,-0.229 -0.2,-0.371 0,-0.237 0.215,-0.409 0.515,-0.409 0.265,0 0.486,0.129 0.573,0.33 0.049,0.1 0.064,0.243 0.064,0.493 l 0,0.193 z m -1.931,0.765 c 0.05,0.386 0.157,0.601 0.4,0.787 0.258,0.193 0.644,0.3 1.081,0.3 0.585,0 1.008,-0.186 1.164,-0.508 0.101,-0.2 0.137,-0.407 0.137,-0.836 l 0,-1.366 c 0,-0.407 0.028,-0.579 0.114,-0.758 l -0.801,0 c -0.036,0.136 -0.042,0.215 -0.042,0.387 -0.186,-0.308 -0.487,-0.458 -0.938,-0.458 -0.779,0 -1.237,0.357 -1.237,0.965 0,0.472 0.273,0.829 0.773,1.015 0.307,0.122 0.357,0.129 1.28,0.293 l 0,0.057 c 0,0.373 -0.143,0.516 -0.515,0.516 -0.336,0 -0.529,-0.129 -0.579,-0.394 l -0.837,0"
|
||||
id="path3357"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 496.957,217.94799 c 0.05,-0.107 0.072,-0.25 0.072,-0.436 0.25,0.336 0.593,0.507 1.008,0.507 0.422,0 0.793,-0.186 0.973,-0.493 0.128,-0.229 0.156,-0.4 0.156,-1.015 l 0,-1.96 -0.879,0 0,1.766 c 0,0.358 -0.022,0.53 -0.085,0.659 -0.086,0.178 -0.251,0.271 -0.466,0.271 -0.25,0 -0.471,-0.136 -0.564,-0.365 -0.065,-0.129 -0.079,-0.271 -0.079,-0.593 l 0,-1.738 -0.88,0 0,2.181 c 0,0.823 -0.007,0.887 -0.107,1.216 l 0.851,0"
|
||||
id="path3361"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 502.06201,216.82499 c -0.05,0.336 -0.236,0.508 -0.543,0.508 -0.45,0 -0.743,-0.43 -0.743,-1.087 0,-0.679 0.271,-1.08 0.729,-1.08 0.35,0 0.543,0.193 0.593,0.593 l 0.851,0 c -0.043,-0.779 -0.622,-1.3 -1.444,-1.3 -0.973,0 -1.631,0.721 -1.631,1.787 0,1.08 0.658,1.795 1.645,1.795 0.808,0 1.301,-0.43 1.408,-1.216 l -0.865,0"
|
||||
id="path3365"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 505.74501,216.682 c -0.072,0.458 -0.287,0.679 -0.673,0.679 -0.386,0 -0.629,-0.221 -0.743,-0.679 l 1.416,0 z m -1.438,-0.643 c 0.029,-0.559 0.329,-0.873 0.823,-0.873 0.322,0 0.543,0.122 0.658,0.379 l 0.779,-0.036 c -0.151,-0.651 -0.708,-1.051 -1.459,-1.051 -1.03,0 -1.68,0.679 -1.68,1.752 0,1.101 0.658,1.83 1.652,1.83 0.958,0 1.551,-0.701 1.551,-1.83 l 0,-0.171 -2.324,0"
|
||||
id="path3369"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 484.47599,236.28199 0,-3.797 c 0,-0.879 -0.029,-1.094 -0.172,-1.358 -0.221,-0.393 -0.665,-0.572 -1.43,-0.572 -0.057,0 -0.15,0 -0.271,0.007 l 0,0.765 0.156,0 c 0.415,0.007 0.637,0.072 0.723,0.222 0.071,0.107 0.079,0.214 0.079,0.915 l 0,3.818 0.915,0"
|
||||
id="path3373"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 488.122,235.917 c 0,-0.265 -0.215,-0.486 -0.486,-0.486 -0.265,0 -0.486,0.221 -0.486,0.486 0,0.272 0.215,0.486 0.493,0.486 0.264,0 0.479,-0.222 0.479,-0.486 z m -1.273,0 c 0,-0.265 -0.214,-0.486 -0.486,-0.486 -0.264,0 -0.486,0.221 -0.486,0.486 0,0.272 0.214,0.486 0.493,0.486 0.265,0 0.479,-0.222 0.479,-0.486 z m -0.479,-1.008 0,-1.931 c 0,-0.536 0.172,-0.765 0.58,-0.765 0.45,0 0.672,0.286 0.672,0.88 l 0,1.816 0.879,0 0,-2.345 c 0,-0.751 0,-0.751 0.086,-1.051 l -0.829,0 c -0.036,0.078 -0.065,0.236 -0.072,0.393 -0.265,-0.336 -0.536,-0.465 -0.951,-0.465 -0.465,0 -0.865,0.208 -1.065,0.544 -0.122,0.2 -0.179,0.522 -0.179,0.944 l 0,1.98 0.879,0"
|
||||
id="path3377"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 489.488,231.513 0.88,0 0,4.798 -0.88,0 0,-4.798 z"
|
||||
id="path3379"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 491.354,234.90901 0.879,0 0,-3.396 -0.879,0 0,3.396 z m 0.879,0 z m 0.072,0.958 c 0,-0.314 -0.2,-0.515 -0.508,-0.515 -0.315,0 -0.514,0.193 -0.514,0.501 0,0.314 0.214,0.522 0.529,0.522 0.293,0 0.493,-0.208 0.493,-0.508"
|
||||
id="path3383"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 495.151,233.787 c -0.051,0.336 -0.237,0.508 -0.543,0.508 -0.451,0 -0.744,-0.429 -0.744,-1.087 0,-0.679 0.272,-1.08 0.729,-1.08 0.351,0 0.544,0.193 0.593,0.594 l 0.851,0 c -0.043,-0.78 -0.622,-1.301 -1.444,-1.301 -0.972,0 -1.63,0.722 -1.63,1.787 0,1.08 0.658,1.795 1.645,1.795 0.807,0 1.301,-0.429 1.408,-1.216 l -0.865,0"
|
||||
id="path3387"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 497.602,236.311 0,-1.773 c 0.265,0.293 0.586,0.443 0.98,0.443 0.429,0 0.815,-0.193 0.993,-0.5 0.129,-0.215 0.158,-0.415 0.158,-1.009 l 0,-1.959 -0.88,0 0,1.766 c 0,0.343 -0.021,0.508 -0.093,0.644 -0.092,0.186 -0.264,0.286 -0.485,0.286 -0.251,0 -0.479,-0.143 -0.587,-0.372 -0.057,-0.136 -0.086,-0.307 -0.086,-0.586 l 0,-1.738 -0.879,0 0,4.798 0.879,0"
|
||||
id="path3391"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 503.58,235.231 -0.607,-1.945 1.222,0 -0.615,1.945 z m 0.465,1.051 1.623,-4.769 -0.951,0 -0.293,0.994 -1.68,0 -0.3,-0.994 -0.937,0 1.609,4.769 0.929,0"
|
||||
id="path3395"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 508.03401,233.12899 c -0.579,-0.071 -0.801,-0.129 -0.951,-0.236 -0.129,-0.093 -0.2,-0.229 -0.2,-0.372 0,-0.236 0.214,-0.407 0.515,-0.407 0.264,0 0.485,0.129 0.572,0.329 0.05,0.1 0.064,0.243 0.064,0.493 l 0,0.193 z m -1.93,0.765 c 0.05,0.386 0.156,0.601 0.4,0.786 0.257,0.194 0.644,0.301 1.08,0.301 0.586,0 1.008,-0.186 1.165,-0.508 0.1,-0.2 0.136,-0.407 0.136,-0.836 l 0,-1.366 c 0,-0.407 0.028,-0.579 0.114,-0.758 l -0.801,0 c -0.035,0.136 -0.042,0.215 -0.042,0.386 -0.186,-0.307 -0.487,-0.457 -0.938,-0.457 -0.778,0 -1.236,0.357 -1.236,0.965 0,0.472 0.272,0.829 0.772,1.015 0.308,0.122 0.358,0.129 1.28,0.293 l 0,0.057 c 0,0.372 -0.143,0.515 -0.515,0.515 -0.336,0 -0.529,-0.128 -0.578,-0.393 l -0.837,0"
|
||||
id="path3399"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 511.781,233.787 c -0.05,0.336 -0.236,0.508 -0.543,0.508 -0.45,0 -0.744,-0.429 -0.744,-1.087 0,-0.679 0.272,-1.08 0.73,-1.08 0.35,0 0.543,0.193 0.593,0.594 l 0.851,0 c -0.043,-0.78 -0.622,-1.301 -1.444,-1.301 -0.973,0 -1.631,0.722 -1.631,1.787 0,1.08 0.658,1.795 1.645,1.795 0.808,0 1.301,-0.429 1.408,-1.216 l -0.865,0"
|
||||
id="path3403"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 514.23201,236.311 0,-1.773 c 0.265,0.293 0.587,0.443 0.98,0.443 0.429,0 0.815,-0.193 0.993,-0.5 0.13,-0.215 0.158,-0.415 0.158,-1.009 l 0,-1.959 -0.879,0 0,1.766 c 0,0.343 -0.022,0.508 -0.093,0.644 -0.093,0.186 -0.265,0.286 -0.486,0.286 -0.251,0 -0.479,-0.143 -0.587,-0.372 -0.057,-0.136 -0.086,-0.307 -0.086,-0.586 l 0,-1.738 -0.879,0 0,4.798 0.879,0"
|
||||
id="path3407"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 519.38101,233.64399 c -0.071,0.458 -0.287,0.679 -0.672,0.679 -0.387,0 -0.629,-0.221 -0.744,-0.679 l 1.416,0 z m -1.438,-0.643 c 0.029,-0.558 0.33,-0.873 0.823,-0.873 0.322,0 0.544,0.122 0.657,0.379 l 0.78,-0.035 c -0.15,-0.651 -0.708,-1.052 -1.459,-1.052 -1.029,0 -1.68,0.68 -1.68,1.752 0,1.101 0.658,1.831 1.652,1.831 0.958,0 1.552,-0.701 1.552,-1.831 l 0,-0.171 -2.325,0"
|
||||
id="path3411"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 521.747,234.90901 c 0.049,-0.107 0.071,-0.25 0.071,-0.436 0.25,0.336 0.594,0.508 1.009,0.508 0.421,0 0.793,-0.186 0.972,-0.494 0.129,-0.228 0.157,-0.4 0.157,-1.015 l 0,-1.959 -0.879,0 0,1.766 c 0,0.357 -0.022,0.529 -0.086,0.658 -0.086,0.178 -0.25,0.271 -0.465,0.271 -0.25,0 -0.472,-0.136 -0.565,-0.364 -0.064,-0.129 -0.078,-0.272 -0.078,-0.594 l 0,-1.737 -0.88,0 0,2.181 c 0,0.822 -0.007,0.886 -0.106,1.215 l 0.85,0"
|
||||
id="path3415"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 414.82,236.759 0,-17.911 c 0,-4.149 -0.135,-5.162 -0.81,-6.409 -1.045,-1.855 -3.137,-2.698 -6.747,-2.698 -0.269,0 -0.707,0 -1.281,0.033 l 0,3.609 0.742,0 c 1.956,0.034 3.002,0.338 3.408,1.047 0.337,0.505 0.37,1.01 0.37,4.317 l 0,18.012 4.318,0"
|
||||
id="path3419"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 426.61599,231.8 -2.867,-9.175 5.769,0 -2.902,9.175 z m 2.194,4.959 7.656,-22.5 -4.485,0 -1.385,4.689 -7.926,0 -1.417,-4.689 -4.419,0 7.591,22.5 4.385,0"
|
||||
id="path3423"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 443.721,226.53801 c 2.126,0 2.834,0.068 3.677,0.371 1.181,0.438 1.889,1.551 1.889,2.969 0,1.146 -0.506,2.091 -1.45,2.631 -0.877,0.506 -1.485,0.573 -4.419,0.573 l -1.248,0 0,-6.544 1.551,0 z m -0.27,10.221 c 3.98,0 5.499,-0.236 7.051,-1.08 2.023,-1.078 3.204,-3.238 3.204,-5.835 0,-2.935 -1.349,-5.06 -3.98,-6.24 l 5.16,-9.345 -5.025,0 -4.588,8.603 -3.103,0 0,-8.603 -4.318,0 0,22.5 5.599,0"
|
||||
id="path3427"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 465.72399,231.8 -2.867,-9.175 5.768,0 -2.901,9.175 z m 2.193,4.959 7.657,-22.5 -4.486,0 -1.384,4.689 -7.926,0 -1.416,-4.689 -4.421,0 7.591,22.5 4.385,0"
|
||||
id="path3431"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 478.65901,238.25801 0,-25.377"
|
||||
id="path3435"
|
||||
style="fill:#3f80b0;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 478.65901,238.25801 0,-25.377"
|
||||
id="path3439"
|
||||
style="fill:#3f80b0;fill-opacity:1;stroke:#467ab0;stroke-width:0.20886751;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g></g></svg>
|
After Width: | Height: | Size: 17 KiB |
158
src/img/villas_web.svg
Normal file
158
src/img/villas_web.svg
Normal file
|
@ -0,0 +1,158 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="149.71165mm"
|
||||
height="149.71165mm"
|
||||
viewBox="0 0 149.71165 149.71165"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r"
|
||||
sodipodi:docname="villasweb.svg"
|
||||
inkscape:export-filename="/home/markus/Development/Projects/VILLASweb/doc/villasweb.svg.png"
|
||||
inkscape:export-xdpi="21.299999"
|
||||
inkscape:export-ydpi="21.299999">
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4611">
|
||||
<stop
|
||||
style="stop-color:#00a2b0;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4607" />
|
||||
<stop
|
||||
style="stop-color:#6ec5b0;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4609" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4611"
|
||||
id="linearGradient4613"
|
||||
x1="65.497406"
|
||||
y1="153.88686"
|
||||
x2="65.497406"
|
||||
y2="13.88037"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.0941748"
|
||||
inkscape:cx="96.131516"
|
||||
inkscape:cy="285.10587"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="g5005"
|
||||
showgrid="false"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:snap-intersection-paths="false"
|
||||
inkscape:bbox-paths="false"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="951"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:measure-start="37.9039,424.38"
|
||||
inkscape:measure-end="101.386,461.031"
|
||||
inkscape:snap-global="true" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(9.2667999,-9.6854979)">
|
||||
<g
|
||||
id="g5005"
|
||||
transform="matrix(0.9623191,0,0,0.9623191,2.7484651,3.9385684)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4329"
|
||||
d="M 65.519531,7.6875 C 30.952342,7.2288821 -1.7559504,32.928216 -9.0985572,66.77029 c -8.9441948,34.36253 9.91032274,73.19316 42.4331002,87.43912 31.90533,15.5955 73.697367,4.8306 94.125297,-24.21303 C 149.0773,101.82081 146.84579,58.724642 122.42805,32.933531 108.17012,16.922072 86.915542,7.695183 65.519531,7.6875 Z"
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:3.70000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:none;stroke:url(#linearGradient4613);stroke-width:20.38569832;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4572"
|
||||
sodipodi:sides="6"
|
||||
sodipodi:cx="65.497406"
|
||||
sodipodi:cy="83.983902"
|
||||
sodipodi:r1="58.333843"
|
||||
sodipodi:r2="50.518589"
|
||||
sodipodi:arg1="-0.52359878"
|
||||
sodipodi:arg2="-4.4017012e-09"
|
||||
inkscape:flatsided="true"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 116.016,54.81698 0,58.33384 -50.518594,29.16693 -50.51859,-29.16693 0,-58.333839 50.51859,-29.166922 z"
|
||||
transform="matrix(0.83725065,0,0,0.85461295,10.659664,12.436674)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#007da9;fill-opacity:1;stroke:none;stroke-width:17.67682076;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4576-3"
|
||||
sodipodi:sides="3"
|
||||
sodipodi:cx="-80.89846"
|
||||
sodipodi:cy="-21.019157"
|
||||
sodipodi:r1="15.401051"
|
||||
sodipodi:r2="7.7005253"
|
||||
sodipodi:arg1="2.0943951"
|
||||
sodipodi:arg2="3.1415927"
|
||||
inkscape:flatsided="true"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -88.598986,-7.6814564 0,-26.6754016 23.101576,13.337701 z"
|
||||
transform="matrix(-1,0,0,-1.0094199,3.7691891,-0.07235845)"
|
||||
inkscape:transform-center-x="3.7051813" />
|
||||
<path
|
||||
d="m 42.395834,161.35432 0,-26.6754 23.101576,13.3377 z"
|
||||
inkscape:randomized="0"
|
||||
inkscape:rounded="0"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:arg2="3.1415927"
|
||||
sodipodi:arg1="2.0943951"
|
||||
sodipodi:r2="7.7005253"
|
||||
sodipodi:r1="15.401051"
|
||||
sodipodi:cy="148.01662"
|
||||
sodipodi:cx="50.096359"
|
||||
sodipodi:sides="3"
|
||||
id="path4593"
|
||||
style="fill:#007da9;fill-opacity:1;stroke:none;stroke-width:17.67682076;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:type="star"
|
||||
inkscape:transform-center-x="-3.7051807"
|
||||
transform="matrix(1,0,0,1.0094199,-4.2717477,-1.5199394)"
|
||||
inkscape:transform-center-y="1.8921547e-06" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.8 KiB |
|
@ -30,11 +30,11 @@ class FileStore extends ArrayStore {
|
|||
reduce(state, action) {
|
||||
switch (action.type) {
|
||||
case 'files/start-upload':
|
||||
FilesDataManager.upload(action.data, action.token);
|
||||
FilesDataManager.upload(action.data, action.token, action.progressCallback, action.finishedCallback);
|
||||
return state;
|
||||
|
||||
case 'files/uploaded':
|
||||
console.log('ready uploaded');
|
||||
//console.log('ready uploaded');
|
||||
return state;
|
||||
|
||||
case 'files/upload-error':
|
||||
|
|
|
@ -84,7 +84,11 @@ body {
|
|||
clear: both;
|
||||
}
|
||||
|
||||
.app-body-spacing {
|
||||
.app-footer a {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.app-body {
|
||||
padding: 15px 5px 0px 5px;
|
||||
}
|
||||
|
||||
|
@ -144,7 +148,7 @@ body {
|
|||
.menu-sidebar ul {
|
||||
padding-top: 10px;
|
||||
list-style: none;
|
||||
white-space: nowrap;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.menu-sidebar a::after {
|
||||
|
@ -201,6 +205,19 @@ body {
|
|||
text-decoration: none;
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Home page
|
||||
*/
|
||||
.home-container > ul {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
.home-container > img {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Login form
|
||||
*/
|
||||
|
@ -238,6 +255,11 @@ body {
|
|||
color: #888;
|
||||
}
|
||||
|
||||
.table-control-checkbox input {
|
||||
position: relative !important;
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.unselectable {
|
||||
-webkit-touch-callout: none !important; /* iOS Safari */
|
||||
-webkit-user-select: none !important; /* Safari */
|
||||
|
|
Loading…
Add table
Reference in a new issue