2020-09-22 18:57:19 +00:00
|
|
|
|
<template>
|
|
|
|
|
<div id="app">
|
|
|
|
|
<div class="notificationContainer">
|
|
|
|
|
<div class="notification" :class="{ error: notification != null && notification.isError }" v-if="notification != null" @click.prevent="hideNotification">
|
|
|
|
|
<span class="message">{{ notification.message }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div id="container">
|
|
|
|
|
<div class="header">
|
|
|
|
|
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6N0E2MTY4NzJGQjFBMTFFQThERTJFM0JGMzk3MjRDRDUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6N0E2MTY4NzNGQjFBMTFFQThERTJFM0JGMzk3MjRDRDUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3QTYxNjg3MEZCMUExMUVBOERFMkUzQkYzOTcyNENENSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3QTYxNjg3MUZCMUExMUVBOERFMkUzQkYzOTcyNENENSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgI/6YAAAAfkSURBVHjazFpbbBRV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" />
|
|
|
|
|
<h1>{{ $t('title') }}</h1>
|
|
|
|
|
<h2>{{ $t('systemID') }}{{ status.systemID || '...' }}</h2>
|
|
|
|
|
|
|
|
|
|
<div class="wifistatus">
|
|
|
|
|
<div class="connection">
|
|
|
|
|
<div class="indicator" :data-status="wifiStatus.ap.enabled ? 'connected' : 'disconnected'"></div> {{ $t('wifiStatus.accesspoint.title') }} {{ wifiStatus.ap.enabled ? wifiStatus.ap.ip : $t('wifiStatus.accesspoint.disabled') }}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="connection">
|
|
|
|
|
<div class="indicator" :data-status="getWiFiStationStatus()"></div> {{ $t('wifiStatus.stationmode.title') }} {{ getWiFiStationStatusText() }}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-if="loading" class="loading">
|
|
|
|
|
<LoadingIndicator></LoadingIndicator>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-else>
|
|
|
|
|
<div class="warning" v-if="hasResetError">
|
|
|
|
|
<p>
|
|
|
|
|
{{ $t('error.resetError') }}
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<p class="resetReason">
|
|
|
|
|
{{ $t('error.resetReason.' + status.resetReason) }}
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<p v-if="status.stackTrace">
|
|
|
|
|
{{ $t('error.stackTrace') }}
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<a class="button button-primary" href="/api/stacktrace/get" v-if="status.stackTrace">{{ $t('error.stackTraceDownload') }}</a>
|
|
|
|
|
<a class="button" @click="deleteStackTrace">{{ $t('error.stackTraceDelete') }}</a>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="navigation tabs">
|
2020-09-23 18:09:40 +00:00
|
|
|
|
<router-link to="/" class="button" active-class="active" :exact="true">{{ $t('status.tabTitle') }}</router-link><router-link to="/connection" class="button" active-class="active">{{ $t('connection.tabTitle') }}</router-link><router-link to="/system" class="button" active-class="active">{{ $t('system.tabTitle') }}</router-link>
|
2020-09-22 18:57:19 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<router-view/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="clearfix"></div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="version">
|
|
|
|
|
{{ $t('copyright') }}<br>
|
|
|
|
|
{{ $t('firmwareVersion') }}{{ status.version || '...' }}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import axios from 'axios'
|
|
|
|
|
import LoadingIndicator from '@/components/loadingIndicator.vue';
|
2020-09-23 18:09:40 +00:00
|
|
|
|
import BaseVM from '@/BaseVM';
|
2020-09-22 18:57:19 +00:00
|
|
|
|
|
|
|
|
|
export default {
|
2020-09-23 18:09:40 +00:00
|
|
|
|
mixins: [BaseVM],
|
|
|
|
|
|
2020-09-22 18:57:19 +00:00
|
|
|
|
components: {
|
|
|
|
|
LoadingIndicator
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
data()
|
|
|
|
|
{
|
|
|
|
|
return {
|
|
|
|
|
loading: true,
|
|
|
|
|
|
|
|
|
|
status: {
|
|
|
|
|
systemID: null,
|
|
|
|
|
version: null,
|
|
|
|
|
resetReason: null,
|
|
|
|
|
stackTrace: false
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
wifiStatus: {
|
|
|
|
|
ap: {
|
|
|
|
|
enabled: false,
|
|
|
|
|
ip: '0.0.0.0'
|
|
|
|
|
},
|
|
|
|
|
station: {
|
|
|
|
|
enabled: false,
|
|
|
|
|
status: 0,
|
|
|
|
|
ip: '0.0.0.0'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mounted()
|
|
|
|
|
{
|
|
|
|
|
const self = this;
|
|
|
|
|
|
|
|
|
|
self.updateStatus()
|
|
|
|
|
.then(() =>
|
|
|
|
|
{
|
|
|
|
|
self.updateWiFiStatus()
|
|
|
|
|
.then(() =>
|
|
|
|
|
{
|
|
|
|
|
self.loading = false;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
computed: {
|
|
|
|
|
notification() { return this.$store.state.notification; },
|
|
|
|
|
|
|
|
|
|
hasResetError()
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
REASON_DEFAULT_RST = 0 normal startup by power on
|
|
|
|
|
REASON_WDT_RST = 1 hardware watch dog reset
|
|
|
|
|
REASON_EXCEPTION_RST = 2 exception reset, GPIO status won’t change
|
|
|
|
|
REASON_SOFT_WDT_RST = 3 software watch dog reset, GPIO status won’t change
|
|
|
|
|
REASON_SOFT_RESTART = 4 software restart ,system_restart , GPIO status won’t change
|
|
|
|
|
REASON_DEEP_SLEEP_AWAKE = 5 wake up from deep-sleep
|
|
|
|
|
REASON_EXT_SYS_RST = 6 system reset
|
|
|
|
|
*/
|
|
|
|
|
return (this.status.resetReason === 1 ||
|
|
|
|
|
this.status.resetReason === 2 ||
|
|
|
|
|
this.status.resetReason === 3 ||
|
|
|
|
|
this.status.stackTrace);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
methods: {
|
|
|
|
|
getWiFiStationStatus()
|
|
|
|
|
{
|
|
|
|
|
if (!this.wifiStatus.station.enabled)
|
|
|
|
|
return 'disconnected';
|
|
|
|
|
|
|
|
|
|
switch (this.wifiStatus.station.status)
|
|
|
|
|
{
|
|
|
|
|
case 0: // WL_IDLE_STATUS
|
|
|
|
|
case 2: // WL_SCAN_COMPLETED
|
|
|
|
|
return 'connecting';
|
|
|
|
|
|
|
|
|
|
case 1: // WL_NO_SSID_AVAIL
|
|
|
|
|
case 4: // WL_CONNECT_FAILED
|
|
|
|
|
case 5: // WL_CONNECTION_LOST
|
|
|
|
|
return 'error';
|
|
|
|
|
|
|
|
|
|
case 3: // WL_CONNECTED
|
|
|
|
|
return 'connected';
|
|
|
|
|
|
|
|
|
|
case 6: // WL_DISCONNECTED
|
|
|
|
|
default:
|
|
|
|
|
return 'disconnected';
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getWiFiStationStatusText()
|
|
|
|
|
{
|
|
|
|
|
if (!this.wifiStatus.station.enabled)
|
|
|
|
|
return this.$i18n.t('wifiStatus.stationmode.disabled');
|
|
|
|
|
|
|
|
|
|
switch (this.wifiStatus.station.status)
|
|
|
|
|
{
|
|
|
|
|
case 0: // WL_IDLE_STATUS
|
|
|
|
|
return this.$i18n.t('wifiStatus.stationmode.idle');
|
|
|
|
|
|
|
|
|
|
case 1: // WL_NO_SSID_AVAIL
|
|
|
|
|
return this.$i18n.t('wifiStatus.stationmode.noSSID');
|
|
|
|
|
|
|
|
|
|
case 2: // WL_SCAN_COMPLETED
|
|
|
|
|
return this.$i18n.t('wifiStatus.stationmode.scanCompleted');
|
|
|
|
|
|
|
|
|
|
case 3: // WL_CONNECTED
|
|
|
|
|
return this.wifiStatus.station.ip;
|
|
|
|
|
|
|
|
|
|
case 4: // WL_CONNECT_FAILED
|
|
|
|
|
return this.$i18n.t('wifiStatus.stationmode.connectFailed');
|
|
|
|
|
|
|
|
|
|
case 5: // WL_CONNECTION_LOST
|
|
|
|
|
return this.$i18n.t('wifiStatus.stationmode.connectionLost');
|
|
|
|
|
|
|
|
|
|
case 6: // WL_DISCONNECTED
|
|
|
|
|
default:
|
|
|
|
|
return this.$i18n.t('wifiStatus.stationmode.disconnected');
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateStatus()
|
|
|
|
|
{
|
2020-09-23 18:09:40 +00:00
|
|
|
|
const self = this;
|
2020-09-22 18:57:19 +00:00
|
|
|
|
|
|
|
|
|
return axios.get('/api/status', { retry: 10, retryDelay: 1000 })
|
|
|
|
|
.then(response =>
|
|
|
|
|
{
|
|
|
|
|
if (typeof response.data == 'object')
|
|
|
|
|
self.status = response.data;
|
|
|
|
|
})
|
|
|
|
|
.catch(e => self.handleAPIError('error.loadStatus', e));
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateWiFiStatus()
|
|
|
|
|
{
|
|
|
|
|
const self = this;
|
|
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) =>
|
|
|
|
|
{
|
|
|
|
|
if (!self.saving)
|
|
|
|
|
{
|
|
|
|
|
axios.get('/api/connection/status', { retry: 10, retryDelay: 1000 })
|
|
|
|
|
.then(response =>
|
|
|
|
|
{
|
|
|
|
|
if (typeof response.data == 'object')
|
|
|
|
|
self.wifiStatus = response.data;
|
|
|
|
|
})
|
|
|
|
|
.catch(e =>
|
|
|
|
|
{
|
|
|
|
|
self.handleAPIError('error.updateWiFiStatus', e);
|
|
|
|
|
reject(e);
|
|
|
|
|
})
|
|
|
|
|
.then(function()
|
|
|
|
|
{
|
|
|
|
|
setTimeout(self.updateWiFiStatus, 5000);
|
|
|
|
|
resolve();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
setTimeout(self.updateWiFiStatus, 5000);
|
|
|
|
|
resolve();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
deleteStackTrace()
|
|
|
|
|
{
|
|
|
|
|
const self = this;
|
|
|
|
|
|
|
|
|
|
return axios.get('/api/stacktrace/delete', { retry: 10, retryDelay: 1000 })
|
|
|
|
|
.then(response =>
|
|
|
|
|
{
|
|
|
|
|
self.status.resetReason = 0;
|
|
|
|
|
self.status.stackTrace = false;
|
|
|
|
|
})
|
|
|
|
|
.catch(e => self.handleAPIError('error.stackTraceDeleteError', e));
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hideNotification()
|
|
|
|
|
{
|
|
|
|
|
this.$store.dispatch('hideNotification');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss">
|
|
|
|
|
@import "variables.scss";
|
|
|
|
|
|
2020-09-23 18:09:40 +00:00
|
|
|
|
// TODO check which parts are app-wide and which should be moved to components/view
|
2020-09-22 18:57:19 +00:00
|
|
|
|
|
|
|
|
|
html
|
|
|
|
|
{
|
|
|
|
|
overscroll-behavior-x: contain;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
font-size: 62.5%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*, *:before, *:after
|
|
|
|
|
{
|
|
|
|
|
box-sizing: inherit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body
|
|
|
|
|
{
|
|
|
|
|
overscroll-behavior-x: contain;
|
|
|
|
|
background-color: rgb(20, 20, 20);
|
|
|
|
|
color: white;
|
|
|
|
|
font-family: 'Verdana', 'Arial', sans-serif;
|
|
|
|
|
font-size: 1.3em;
|
|
|
|
|
font-weight: 300;
|
|
|
|
|
letter-spacing: .01em;
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
|
|
|
|
|
padding-bottom: 3rem;
|
|
|
|
|
|
|
|
|
|
@media #{$mediumScreen}
|
|
|
|
|
{
|
|
|
|
|
padding-top: 3rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a
|
|
|
|
|
{
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#container
|
|
|
|
|
{
|
|
|
|
|
background: $containerBackground;
|
|
|
|
|
margin-top: 2rem;
|
|
|
|
|
padding: 1rem;
|
|
|
|
|
|
|
|
|
|
box-shadow: 0 0 50px $containerShadowColor;
|
|
|
|
|
border: solid 1px black;
|
|
|
|
|
|
|
|
|
|
@media #{$mediumScreen}
|
|
|
|
|
{
|
|
|
|
|
width: 768px;
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
margin-right: auto;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.header
|
|
|
|
|
{
|
|
|
|
|
position: relative;
|
|
|
|
|
|
|
|
|
|
img
|
|
|
|
|
{
|
|
|
|
|
float: left;
|
|
|
|
|
margin-right: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.wifistatus
|
|
|
|
|
{
|
|
|
|
|
@media #{$smallScreen}
|
|
|
|
|
{
|
|
|
|
|
clear: both;
|
|
|
|
|
margin-top: 3rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media #{$mediumScreen}
|
|
|
|
|
{
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: 0;
|
|
|
|
|
top: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.indicator
|
|
|
|
|
{
|
|
|
|
|
display: inline-block;
|
|
|
|
|
width: 1rem;
|
|
|
|
|
height: 1rem;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
margin-right: 0.5rem;
|
|
|
|
|
|
|
|
|
|
&[data-status=connected] { background-color: #339966; }
|
|
|
|
|
&[data-status=disconnected] { border: solid 1px #808080; }
|
|
|
|
|
&[data-status=connecting] { background-color: #ff9933; }
|
|
|
|
|
&[data-status=error] { background-color: #cc0000; }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
%outset
|
|
|
|
|
{
|
|
|
|
|
border: 1px solid #111111;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
box-shadow: inset 0 1px rgba(255,255,255,0.1), inset 0 -1px 3px rgba(0,0,0,0.3), inset 0 0 0 1px rgba(255,255,255,0.08), 0 1px 2px rgba(0,0,0,0.15);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
%inset
|
|
|
|
|
{
|
|
|
|
|
border: 1px solid #111111;
|
|
|
|
|
border-color: black #111111 #111111;
|
|
|
|
|
box-shadow: inset 0 1px 2px rgba(0,0,0,0.25),0 1px rgba(255,255,255,0.08);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
button, input
|
|
|
|
|
{
|
|
|
|
|
font-family: 'Verdana', 'Arial', sans-serif;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@mixin removeSafariStyling
|
|
|
|
|
{
|
|
|
|
|
-webkit-appearance: none;
|
|
|
|
|
-moz-appearance: none;
|
|
|
|
|
appearance: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input
|
|
|
|
|
{
|
|
|
|
|
@include removeSafariStyling;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
button, .button, input[type=submit]
|
|
|
|
|
{
|
|
|
|
|
@extend %outset;
|
|
|
|
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
padding: 0 12px;
|
|
|
|
|
color: $buttonTextColor;
|
|
|
|
|
background: $buttonBackground;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
line-height: 3rem;
|
|
|
|
|
|
|
|
|
|
&:hover, &:focus, &.focus
|
|
|
|
|
{
|
|
|
|
|
color: $buttonHoverTextColor;
|
|
|
|
|
background: $buttonHoverBackground;
|
|
|
|
|
outline: none
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&:active, &.active
|
|
|
|
|
{
|
|
|
|
|
@extend %inset;
|
|
|
|
|
|
|
|
|
|
color: $buttonActiveTextColor;
|
|
|
|
|
background: $buttonActiveBackground;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input[type=submit], .button-primary
|
|
|
|
|
{
|
|
|
|
|
background: $buttonPrimaryBackground;
|
|
|
|
|
|
|
|
|
|
&:hover, &:focus, &.focus
|
|
|
|
|
{
|
|
|
|
|
background: $buttonPrimaryHoverBackground;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a.button
|
|
|
|
|
{
|
|
|
|
|
text-decoration: none
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.navigation
|
|
|
|
|
{
|
|
|
|
|
clear: both;
|
|
|
|
|
margin-top: 3rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tabs
|
|
|
|
|
{
|
|
|
|
|
&>.button
|
|
|
|
|
{
|
|
|
|
|
margin-left: -1px;
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
|
|
|
|
|
&:first-child
|
|
|
|
|
{
|
|
|
|
|
margin-left: 0;
|
|
|
|
|
border-radius: 3px 0 0 3px
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&:last-child
|
|
|
|
|
{
|
|
|
|
|
border-radius: 0 3px 3px 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&:focus
|
|
|
|
|
{
|
|
|
|
|
position: relative;
|
|
|
|
|
z-index: 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.version
|
|
|
|
|
{
|
|
|
|
|
color: $versionTextColor;
|
|
|
|
|
font-size: 8pt;
|
|
|
|
|
text-align: center;
|
|
|
|
|
margin-top: 2rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.notificationContainer
|
|
|
|
|
{
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 2rem;
|
|
|
|
|
z-index: 666;
|
|
|
|
|
|
|
|
|
|
@media #{$mediumScreen}
|
|
|
|
|
{
|
|
|
|
|
width: 512px;
|
|
|
|
|
left: 50%;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.notification
|
|
|
|
|
{
|
|
|
|
|
@extend %outset;
|
|
|
|
|
|
|
|
|
|
background: $notificationBackground;
|
|
|
|
|
/* border: solid 1px $notificationBorderColor;*/
|
|
|
|
|
box-shadow: 0 0 10px black;
|
|
|
|
|
color: white;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: .5em;
|
|
|
|
|
margin-bottom: 2rem;
|
|
|
|
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
|
|
|
|
@media #{$mediumScreen}
|
|
|
|
|
{
|
|
|
|
|
left: -50%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message
|
|
|
|
|
{
|
|
|
|
|
white-space: pre;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
&.error
|
|
|
|
|
{
|
|
|
|
|
background: $notificationErrorBackground;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.check, .radio
|
|
|
|
|
{
|
|
|
|
|
display: inline-block;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
user-select: none;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
margin-top: .5em;
|
|
|
|
|
margin-bottom: .5em;
|
|
|
|
|
|
|
|
|
|
.control
|
|
|
|
|
{
|
|
|
|
|
@extend %outset;
|
|
|
|
|
background: $checkRadioBackground;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
width: 16px;
|
|
|
|
|
height: 16px;
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.label
|
|
|
|
|
{
|
|
|
|
|
display: inline-block;
|
|
|
|
|
margin-left: .5em;
|
|
|
|
|
vertical-align: top;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.checked
|
|
|
|
|
{
|
|
|
|
|
.control
|
|
|
|
|
{
|
|
|
|
|
background: $checkRadioSelectedBackground;
|
|
|
|
|
|
|
|
|
|
.inner
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.disabled
|
|
|
|
|
{
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
|
|
|
|
|
.label
|
|
|
|
|
{
|
|
|
|
|
color: $inputDisabledTextColor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.radio
|
|
|
|
|
{
|
|
|
|
|
.control, .control .inner
|
|
|
|
|
{
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.control .inner
|
|
|
|
|
{
|
|
|
|
|
color: black;
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 4px;
|
|
|
|
|
left: 4px;
|
|
|
|
|
width: 6px;
|
|
|
|
|
height: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.checked .control .inner
|
|
|
|
|
{
|
|
|
|
|
background: #cccccc;
|
|
|
|
|
box-shadow: 0 1px rgba(0,0,0,0.5);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.check
|
|
|
|
|
{
|
|
|
|
|
.control .inner
|
|
|
|
|
{
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 5px;
|
|
|
|
|
left: 4px;
|
|
|
|
|
width: 6px;
|
|
|
|
|
height: 3px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.checked .control .inner
|
|
|
|
|
{
|
|
|
|
|
border: solid rgba(255,255,255,0.8);
|
|
|
|
|
border-width: 0 0 2px 2px;
|
|
|
|
|
transform: rotate(-45deg);
|
|
|
|
|
box-shadow: -1px 0 rgba(0,0,0,0.2), 0 1px rgba(0,0,0,0.5)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.form-control
|
|
|
|
|
{
|
|
|
|
|
margin-top: 1em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
input[type=text], input[type=number], input[type=password], textarea
|
|
|
|
|
{
|
|
|
|
|
@extend %inset;
|
|
|
|
|
background: $inputBackground;
|
|
|
|
|
color: $inputTextColor;
|
|
|
|
|
padding: .5em;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
select
|
|
|
|
|
{
|
|
|
|
|
@extend %outset;
|
|
|
|
|
background: $selectBackground;
|
|
|
|
|
color: $inputTextColor;
|
|
|
|
|
font-family: 'Verdana', 'Arial', sans-serif;
|
|
|
|
|
padding: .5em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input[type=range]
|
|
|
|
|
{
|
|
|
|
|
margin-top: 1rem;
|
|
|
|
|
margin-bottom: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h1
|
|
|
|
|
{
|
|
|
|
|
font-size: 2rem;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h2
|
|
|
|
|
{
|
|
|
|
|
color: #c0c0c0;
|
|
|
|
|
font-size: 1.2rem;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h3
|
|
|
|
|
{
|
|
|
|
|
@extend %outset;
|
|
|
|
|
color: $sectionHeaderTextColor;
|
|
|
|
|
background: $sectionHeaderBackground;
|
|
|
|
|
font-size: 1.2rem;
|
|
|
|
|
padding: .5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h4
|
|
|
|
|
{
|
|
|
|
|
font-size: 1.4rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
input[disabled]
|
|
|
|
|
{
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
color: $inputDisabledTextColor;
|
|
|
|
|
background: $inputDisabledBackground;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
label
|
|
|
|
|
{
|
|
|
|
|
display: block;
|
|
|
|
|
margin-top: .5em;
|
|
|
|
|
margin-bottom: .5em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.label-inline
|
|
|
|
|
{
|
|
|
|
|
margin-right: 2rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@media #{$mediumScreen}
|
|
|
|
|
{
|
|
|
|
|
.horizontal
|
|
|
|
|
{
|
|
|
|
|
clear: both;
|
|
|
|
|
|
|
|
|
|
label
|
|
|
|
|
{
|
|
|
|
|
display: inline-block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input[type=text], input[type=number], input[type=password], textarea
|
|
|
|
|
{
|
|
|
|
|
display: inline-block;
|
|
|
|
|
float: right;
|
|
|
|
|
width: 50%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&:after
|
|
|
|
|
{
|
|
|
|
|
clear: both;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.hint
|
|
|
|
|
{
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 8pt;
|
|
|
|
|
color: #808080;
|
|
|
|
|
margin-bottom: 1.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.loading
|
|
|
|
|
{
|
|
|
|
|
margin-top: 3rem;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.suboptions
|
|
|
|
|
{
|
|
|
|
|
margin-left: 5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.buttons
|
|
|
|
|
{
|
|
|
|
|
clear: both;
|
|
|
|
|
text-align: center;
|
|
|
|
|
margin-top: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.sliders
|
|
|
|
|
{
|
|
|
|
|
margin-top: 2rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.slidercontainer
|
|
|
|
|
{
|
|
|
|
|
margin-top: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$sliderRedThumbColor: #ce3636;
|
|
|
|
|
$sliderGreenThumbColor: #32b732;
|
|
|
|
|
$sliderBlueThumbColor: #4646cc;
|
|
|
|
|
$sliderWhiteThumbColor: #fcf6cf;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.slider
|
|
|
|
|
{
|
|
|
|
|
-webkit-appearance: none;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: $sliderBarSize;
|
|
|
|
|
border-radius: $sliderBarSize / 2;
|
|
|
|
|
background: $sliderBarColor;
|
|
|
|
|
outline: none;
|
|
|
|
|
|
|
|
|
|
&::-webkit-slider-thumb
|
|
|
|
|
{
|
|
|
|
|
-webkit-appearance: none;
|
|
|
|
|
appearance: none;
|
|
|
|
|
width: $sliderThumbSize;
|
|
|
|
|
height: $sliderThumbSize;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: $sliderThumbColor;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&::-moz-range-thumb
|
|
|
|
|
{
|
|
|
|
|
width: $sliderThumbSize;
|
|
|
|
|
height: $sliderThumbSize;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: $sliderThumbColor;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
&.red
|
|
|
|
|
{
|
|
|
|
|
&::-webkit-slider-thumb { background: $sliderRedThumbColor; }
|
|
|
|
|
&::-moz-range-thumb { background: $sliderRedThumbColor; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.green
|
|
|
|
|
{
|
|
|
|
|
&::-webkit-slider-thumb { background: $sliderGreenThumbColor; }
|
|
|
|
|
&::-moz-range-thumb { background: $sliderGreenThumbColor; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.blue
|
|
|
|
|
{
|
|
|
|
|
&::-webkit-slider-thumb { background: $sliderBlueThumbColor; }
|
|
|
|
|
&::-moz-range-thumb { background: $sliderBlueThumbColor; }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.warning
|
|
|
|
|
{
|
|
|
|
|
@extend %outset;
|
|
|
|
|
background: #973a38;
|
|
|
|
|
padding: .5em;
|
|
|
|
|
margin-bottom: 2rem;
|
|
|
|
|
margin-top: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.nodata
|
|
|
|
|
{
|
|
|
|
|
color: #808080;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.clear
|
|
|
|
|
{
|
|
|
|
|
clear: both;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.panel
|
|
|
|
|
{
|
|
|
|
|
margin-bottom: 2rem;
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
|
|
|
|
.panel-header
|
|
|
|
|
{
|
|
|
|
|
@extend %outset;
|
|
|
|
|
border-radius: 3px 3px 0 0;
|
|
|
|
|
border-bottom-width: 0;
|
|
|
|
|
padding: .5em;
|
|
|
|
|
|
|
|
|
|
label {
|
|
|
|
|
font-size: 1em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
background: $panelHeaderBackground;
|
|
|
|
|
color: $panelHeaderTextColor;
|
|
|
|
|
|
|
|
|
|
.actions
|
|
|
|
|
{
|
|
|
|
|
float: right;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a, .label
|
|
|
|
|
{
|
|
|
|
|
color: $panelHeaderLinkColor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.panel-body
|
|
|
|
|
{
|
|
|
|
|
@extend %outset;
|
|
|
|
|
border-radius: 0 0 3px 3px;
|
|
|
|
|
|
|
|
|
|
background: $panelBodyBackground;
|
|
|
|
|
padding: 2rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
&.active
|
|
|
|
|
{
|
|
|
|
|
.panel-header
|
|
|
|
|
{
|
|
|
|
|
background: $panelActiveHeaderBackground;
|
|
|
|
|
color: $panelActiveHeaderTextColor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.inline
|
|
|
|
|
{
|
|
|
|
|
display: inline-block;
|
|
|
|
|
width: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.fade-enter-active, .fade-leave-active
|
|
|
|
|
{
|
|
|
|
|
transition: opacity .5s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.fade-enter, .fade-leave-to
|
|
|
|
|
{
|
|
|
|
|
opacity: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.range
|
|
|
|
|
{
|
|
|
|
|
clear: both;
|
|
|
|
|
|
|
|
|
|
.start
|
|
|
|
|
{
|
|
|
|
|
position: relative;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
width: 49%;
|
|
|
|
|
|
|
|
|
|
.slidercontainer
|
|
|
|
|
{
|
|
|
|
|
margin-right: 4em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.value
|
|
|
|
|
{
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: 0;
|
|
|
|
|
top: 1.5rem;
|
|
|
|
|
color: $sliderValueColor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.end
|
|
|
|
|
{
|
|
|
|
|
position: relative;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
float: right;
|
|
|
|
|
width: 50%;
|
|
|
|
|
|
|
|
|
|
.slidercontainer
|
|
|
|
|
{
|
|
|
|
|
margin-left: 4em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.value
|
|
|
|
|
{
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 0;
|
|
|
|
|
top: 1.5rem;
|
|
|
|
|
color: $sliderValueColor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&:after
|
|
|
|
|
{
|
|
|
|
|
clear: both;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.resetReason
|
|
|
|
|
{
|
|
|
|
|
margin-left: 2em;
|
|
|
|
|
}
|
|
|
|
|
</style>
|