Initial vue commit
This commit is contained in:
parent
b561513c4f
commit
859ffc2496
21 changed files with 11115 additions and 651 deletions
22
.gitignore
vendored
22
.gitignore
vendored
|
|
@ -1 +1,21 @@
|
||||||
.vscode/
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw*
|
||||||
|
|
|
||||||
29
README.md
Normal file
29
README.md
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# vue3
|
||||||
|
|
||||||
|
## Project setup
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and hot-reloads for development
|
||||||
|
```
|
||||||
|
npm run serve
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and minifies for production
|
||||||
|
```
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run your tests
|
||||||
|
```
|
||||||
|
npm run test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lints and fixes files
|
||||||
|
```
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customize configuration
|
||||||
|
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||||
5
babel.config.js
Normal file
5
babel.config.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/app'
|
||||||
|
]
|
||||||
|
}
|
||||||
117
index.html
117
index.html
|
|
@ -1,117 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>OpenFlexure Microscope API Demo</title>
|
|
||||||
|
|
||||||
<link rel= "stylesheet" type= "text/css" href="./static/style.css">
|
|
||||||
<script type="text/javascript" src="./static/main.js"></script>
|
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimal-ui, maximum-scale=1.0, user-scalable=0">
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="column left" id="left-sb">
|
|
||||||
<h2>Settings</h2>
|
|
||||||
Host: <input type="text" id="microscopeHostInput" value="192.168.1.126"><br>
|
|
||||||
Port: <input type="number" id="microscopePortInput" value=5000><br>
|
|
||||||
<button type="button" onclick="setHostFromInput();">Connect</button>
|
|
||||||
|
|
||||||
<h3>Position</h3>
|
|
||||||
<p>
|
|
||||||
x: <input type="number" id="x_abs"><br>
|
|
||||||
y: <input type="number" id="y_abs"><br>
|
|
||||||
z: <input type="number" id="z_abs"><br>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<button type="button" onclick="setStagePositionsFromInput();">GO</button>
|
|
||||||
<button type="button" onclick="getStagePositions();">Update</button>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Doubleclick to position: <input type="checkbox" id="clickPositionCheck" checked><br>
|
|
||||||
<div class="flexbox">
|
|
||||||
<div class="flexgrow">FOV width:</div>
|
|
||||||
<div>
|
|
||||||
<input type="number" id="fovXText" onchange="fovX = Number(this.value); updateTextBoxes();" style="width: 7em" >
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flexbox">
|
|
||||||
<div class="flexgrow">FOV height:</div>
|
|
||||||
<div>
|
|
||||||
<input type="number" id="fovYText" onchange="fovY = Number(this.value); updateTextBoxes();" style="width: 7em" >
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h3>Move</h3>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<b>Up/down/left/right:</b> <br>
|
|
||||||
Move stage in x-y. <br>
|
|
||||||
<b>PgUp/PgDn: </b> <br>
|
|
||||||
Move stage in z/focus.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h4>Velocity:</h4>
|
|
||||||
<div class="flexbox">
|
|
||||||
<div class="flexgrow">x-y:</div>
|
|
||||||
<div>
|
|
||||||
<input type="range" id="stageVelocityInput" min="50" max="200" oninput="stageVelocity = Number(this.value); updateTextBoxes();">
|
|
||||||
<input type="text" id="stageVelocityText" size="3" disabled>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flexbox">
|
|
||||||
<div class="flexgrow">z:</div>
|
|
||||||
<div>
|
|
||||||
<input type="range" id="focusVelocityInput" min="10" max="100" oninput="focusVelocity = Number(this.value); updateTextBoxes();">
|
|
||||||
<input type="text" id="focusVelocityText" size="3" disabled>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<p>Scroll to focus: <input type="checkbox" id="scrollFocusCheck" checked></p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="column middle">
|
|
||||||
<div id="left-sb-btn" class="sb-btn">
|
|
||||||
<button type="button" onclick="toggleClassToDiv('left-sb', 'hidden');">
|
|
||||||
<img src="./static/icons/baseline-settings-20px.svg">
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div id="right-sb-btn" class="sb-btn">
|
|
||||||
<button type="button" onclick="toggleClassToDiv('right-sb', 'hidden');">
|
|
||||||
<img src="./static/icons/baseline-camera_alt-24px.svg">
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="scrolltarget" id="streambox">No active stream</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="column right" id="right-sb">
|
|
||||||
<h2>Capture</h2>
|
|
||||||
Filename: <br>
|
|
||||||
<input type="text" id="captureFilenameInput" placeholder="Leave blank for default"><br>
|
|
||||||
Store on Pi: <input type="checkbox" id="captureKeepOnDiskCheck"><br>
|
|
||||||
Full resolution: <input type="checkbox" id="captureFullResolutionCheck"><br>
|
|
||||||
Keep raw data: <input type="checkbox" id="captureBayerCheck"><br>
|
|
||||||
Resize capture: <input type="checkbox" id="captureResizeCheck" onclick="newCaptureResizeToggle(this);"><br>
|
|
||||||
<input type="number" id="captureWidthInput" value="640" style="width: 7em" disabled>
|
|
||||||
<input type="number" id="captureHeightInput" value="480" style="width: 7em" disabled><br>
|
|
||||||
|
|
||||||
<button type="button" onclick="newCaptureFromInput();">Capture</button>
|
|
||||||
|
|
||||||
<h2>Captures</h2>
|
|
||||||
<button type="button" onclick="getCaptures();">Update</button>
|
|
||||||
<button type="button" onclick="deleteAllCaptures();">Delete all</button>
|
|
||||||
<div id="captures"></div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
10289
package-lock.json
generated
Normal file
10289
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
33
package.json
Normal file
33
package.json
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"name": "vue3",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.18.0",
|
||||||
|
"uikit": "^3.0.2",
|
||||||
|
"vue": "^2.5.21",
|
||||||
|
"vuex": "^3.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vue/cli-plugin-babel": "^3.3.0",
|
||||||
|
"@vue/cli-service": "^3.3.0",
|
||||||
|
"css-loader": "^2.1.0",
|
||||||
|
"less": "^3.9.0",
|
||||||
|
"less-loader": "^4.1.0",
|
||||||
|
"vue-template-compiler": "^2.5.21"
|
||||||
|
},
|
||||||
|
"postcss": {
|
||||||
|
"plugins": {
|
||||||
|
"autoprefixer": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not ie <= 8"
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
17
public/index.html
Normal file
17
public/index.html
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title>vue3</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but vue3 doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
43
src/App.vue
Normal file
43
src/App.vue
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<IpInput msg="Is your name good or bad?."/>
|
||||||
|
<img alt="Vue logo" src="./assets/images/logo.png">
|
||||||
|
<IpDisplay/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Import axios for HTTP requests
|
||||||
|
import axios from 'axios'
|
||||||
|
// Import basic UIkit
|
||||||
|
import UIkit from 'uikit';
|
||||||
|
// Import UIkit icon set
|
||||||
|
import Icons from 'uikit/dist/js/uikit-icons';
|
||||||
|
UIkit.use(Icons);
|
||||||
|
|
||||||
|
// Import components
|
||||||
|
import IpInput from './components/IpInput.vue'
|
||||||
|
import IpDisplay from './components/IpDisplay'
|
||||||
|
|
||||||
|
// Export main app
|
||||||
|
export default {
|
||||||
|
name: 'app',
|
||||||
|
components: {
|
||||||
|
IpInput,
|
||||||
|
IpDisplay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
// Basic UIkit CSS
|
||||||
|
@import "../node_modules/uikit/src/less/uikit.less";
|
||||||
|
// Custom UIkit CSS modifications
|
||||||
|
@import "./assets/less/theme.less";
|
||||||
|
|
||||||
|
#app {
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
BIN
src/assets/images/logo.png
Normal file
BIN
src/assets/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
51
src/assets/less/highlight.less
Normal file
51
src/assets/less/highlight.less
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* UIkit Theme for Highlight.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
display: block;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-comment,
|
||||||
|
.hljs-meta { color: #969896; }
|
||||||
|
|
||||||
|
.hljs-string,
|
||||||
|
.hljs-variable,
|
||||||
|
.hljs-template-variable,
|
||||||
|
.hljs-strong,
|
||||||
|
.hljs-emphasis,
|
||||||
|
.hljs-quote { color: @global-danger-background; }
|
||||||
|
|
||||||
|
.hljs-keyword,
|
||||||
|
.hljs-selector-tag,
|
||||||
|
.hljs-type { color: #1f34aa; }
|
||||||
|
|
||||||
|
.hljs-literal,
|
||||||
|
.hljs-symbol,
|
||||||
|
.hljs-bullet,
|
||||||
|
.hljs-attribute { color: #0086b3; }
|
||||||
|
|
||||||
|
.hljs-section,
|
||||||
|
.hljs-name { color: #63a35c; }
|
||||||
|
|
||||||
|
.hljs-tag { color: #333; }
|
||||||
|
|
||||||
|
.hljs-title,
|
||||||
|
.hljs-attr,
|
||||||
|
.hljs-selector-id,
|
||||||
|
.hljs-selector-class,
|
||||||
|
.hljs-selector-attr,
|
||||||
|
.hljs-selector-pseudo { color: #795da3; }
|
||||||
|
|
||||||
|
.hljs-addition {
|
||||||
|
color: #55a532;
|
||||||
|
background-color: #eaffea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-deletion {
|
||||||
|
color: #bd2c00;
|
||||||
|
background-color: #ffecec;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-link { text-decoration: underline; }
|
||||||
507
src/assets/less/theme.less
Normal file
507
src/assets/less/theme.less
Normal file
|
|
@ -0,0 +1,507 @@
|
||||||
|
// UIkit Core
|
||||||
|
@import "../../../node_modules/uikit/src/less/uikit.theme.less";
|
||||||
|
|
||||||
|
// Highlight.js
|
||||||
|
@import "./highlight.less";
|
||||||
|
|
||||||
|
|
||||||
|
// UIkit
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
@global-font-family: ProximaNova, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
|
@global-font-size: 15px;
|
||||||
|
|
||||||
|
@global-xxlarge-font-size: 38px;
|
||||||
|
@global-xlarge-font-size: 30px;
|
||||||
|
@global-large-font-size: 24px;
|
||||||
|
@global-medium-font-size: 20px;
|
||||||
|
@global-small-font-size: 14px;
|
||||||
|
|
||||||
|
@global-emphasis-color: #222;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Base
|
||||||
|
//
|
||||||
|
|
||||||
|
@base-code-font-family: 'Roboto Mono', monospace;
|
||||||
|
@base-code-font-size: 12px;
|
||||||
|
|
||||||
|
@base-heading-font-weight: 300;
|
||||||
|
|
||||||
|
@base-pre-font-size: 12px;
|
||||||
|
@base-pre-padding: 25px;
|
||||||
|
@base-pre-background: @global-muted-background;
|
||||||
|
@base-pre-border-width: 0;
|
||||||
|
@base-pre-border-radius: 0;
|
||||||
|
|
||||||
|
.hook-base-body() {
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Container
|
||||||
|
//
|
||||||
|
|
||||||
|
@container-max-width: 1380px;
|
||||||
|
@container-small-max-width: 650px;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Navbar
|
||||||
|
//
|
||||||
|
|
||||||
|
@inverse-navbar-nav-item-color: @inverse-global-color;
|
||||||
|
@inverse-navbar-nav-item-hover-color: @inverse-global-emphasis-color;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Nav
|
||||||
|
//
|
||||||
|
|
||||||
|
@nav-header-font-size: 12px;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Subnav
|
||||||
|
//
|
||||||
|
|
||||||
|
@inverse-subnav-item-color: @inverse-global-color;
|
||||||
|
@inverse-subnav-item-hover-color: @inverse-global-emphasis-color;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Tab
|
||||||
|
//
|
||||||
|
|
||||||
|
@tab-item-padding-horizontal: 20px;
|
||||||
|
@tab-item-padding-vertical: 9px;
|
||||||
|
@tab-item-border-width: 2px;
|
||||||
|
@tab-item-font-size: 12px;
|
||||||
|
|
||||||
|
.hook-tab-item() { line-height: 20px; }
|
||||||
|
|
||||||
|
//
|
||||||
|
// Table
|
||||||
|
//
|
||||||
|
|
||||||
|
@table-header-cell-font-size: 12px;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Label
|
||||||
|
//
|
||||||
|
|
||||||
|
@label-font-size: 12px;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Text
|
||||||
|
//
|
||||||
|
|
||||||
|
.hook-text-lead() { font-weight: 300; }
|
||||||
|
.hook-text-large() { font-weight: 300; }
|
||||||
|
|
||||||
|
//
|
||||||
|
// Utility
|
||||||
|
//
|
||||||
|
|
||||||
|
@inverse-logo-color: @inverse-global-emphasis-color;
|
||||||
|
@inverse-logo-hover-color: @inverse-global-emphasis-color;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Off-canvas
|
||||||
|
//
|
||||||
|
|
||||||
|
@offcanvas-bar-background: #fff;
|
||||||
|
@offcanvas-bar-color-mode: dark;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Inverse
|
||||||
|
//
|
||||||
|
|
||||||
|
@inverse-global-color: fade(@global-inverse-color, 80%);
|
||||||
|
@inverse-global-muted-color: fade(@global-inverse-color, 60%);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
Theme
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
@sidebar-left-width: 240px;
|
||||||
|
@sidebar-left-width-xl: 300px;
|
||||||
|
|
||||||
|
@sidebar-right-width: 200px;
|
||||||
|
@sidebar-right-left: 0px;
|
||||||
|
@sidebar-right-left-xl: 60px;
|
||||||
|
|
||||||
|
|
||||||
|
/* HTML
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Force vertical scrollbar
|
||||||
|
* 1. `overflow-x` has to be on the `body` element for Safari to prevent horizontal scrolling on touch
|
||||||
|
This causes a random bug showing a vertical scrollbar in combination with some fonts like `Poppins`
|
||||||
|
This is why `overflow-y` must also be set to `hidden`
|
||||||
|
*/
|
||||||
|
|
||||||
|
html { overflow-y: scroll; }
|
||||||
|
|
||||||
|
/* 1 */
|
||||||
|
body { overflow: hidden; }
|
||||||
|
|
||||||
|
|
||||||
|
/* Documentation Sidebars
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sidebar Left
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tm-sidebar-left {
|
||||||
|
position: fixed;
|
||||||
|
top: @navbar-nav-item-height;
|
||||||
|
bottom: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: @sidebar-left-width !important;
|
||||||
|
padding: 40px 40px 60px 40px;
|
||||||
|
border-right: 1px @global-border solid;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sidebar Right
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tm-sidebar-right {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: ~'calc(100% + @{sidebar-right-left})';
|
||||||
|
width: @sidebar-right-width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tablet landscape and bigger */
|
||||||
|
@media (min-width: @breakpoint-medium) {
|
||||||
|
|
||||||
|
.tm-sidebar-left + .tm-main { padding-left: @sidebar-left-width; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Desktop and bigger */
|
||||||
|
@media (min-width: @breakpoint-large) {
|
||||||
|
|
||||||
|
.tm-sidebar-right { left: ~'calc(100% + @{sidebar-right-left})'; }
|
||||||
|
|
||||||
|
.tm-sidebar-left + .tm-main { padding-left: @sidebar-left-width - (@sidebar-right-width + @sidebar-right-left); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Large screen and bigger */
|
||||||
|
@media (min-width: 1400px) {
|
||||||
|
|
||||||
|
.tm-sidebar-left {
|
||||||
|
width: @sidebar-left-width-xl !important;
|
||||||
|
padding: 45px 45px 60px 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-sidebar-right { left: ~'calc(100% + @{sidebar-right-left-xl})'; }
|
||||||
|
|
||||||
|
.tm-sidebar-left + .tm-main { padding-left: @sidebar-left-width-xl - (@sidebar-right-width + @sidebar-right-left-xl); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* UIkit modifier
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Navbar
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tm-navbar-container:not(.uk-navbar-transparent) {
|
||||||
|
background: linear-gradient(to left, #28a5f5, @global-primary-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-navbar-container .uk-navbar-nav > li > a,
|
||||||
|
.tm-navbar-container .uk-navbar-item:not(.uk-logo),
|
||||||
|
.tm-navbar-container .uk-navbar-toggle {
|
||||||
|
height: 70px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color Mode
|
||||||
|
.tm-navbar-container:not(.uk-navbar-transparent):extend(.uk-light all) {}
|
||||||
|
|
||||||
|
// Line Mode
|
||||||
|
@navbar-nav-item-line-margin-vertical: 20px;
|
||||||
|
@navbar-nav-item-line-margin-horizontal: @navbar-nav-item-padding-horizontal;
|
||||||
|
@navbar-nav-item-line-height: 1px;
|
||||||
|
@navbar-nav-item-line-background: currentColor;
|
||||||
|
@navbar-nav-item-line-transition-duration: 0.3s;
|
||||||
|
|
||||||
|
.tm-navbar-container .uk-navbar-nav > li > a {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
left: @navbar-nav-item-line-margin-horizontal;
|
||||||
|
right: ~'calc(102% - @{navbar-nav-item-line-margin-horizontal})';
|
||||||
|
bottom: @navbar-nav-item-line-margin-vertical;
|
||||||
|
height: @navbar-nav-item-line-height;
|
||||||
|
background-color: @navbar-nav-item-line-background;
|
||||||
|
transition: @navbar-nav-item-line-transition-duration ease-in-out;
|
||||||
|
transition-property: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-navbar-container .uk-navbar-nav > li > a:hover::before { right: @navbar-nav-item-line-margin-horizontal; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nav
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tm-nav > li.uk-active > a { position: relative; }
|
||||||
|
|
||||||
|
.tm-nav > li.uk-active > a:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
left: -25px;
|
||||||
|
width: 15px;
|
||||||
|
border-top: 1px solid @global-primary-background;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-nav .uk-nav-header {
|
||||||
|
padding: 8px 0;
|
||||||
|
border-bottom: 1px solid @global-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Subnav
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Tablet landscape and bigger */
|
||||||
|
@media (min-width: @breakpoint-medium) {
|
||||||
|
|
||||||
|
.tm-subnav { margin-left: -50px; }
|
||||||
|
.tm-subnav > * { padding-left: 50px; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-subnav > * > :first-child { text-transform: capitalize; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Label (Changelog()
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tm-label-changelog {
|
||||||
|
width: 80px;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Button
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tm-button-default,
|
||||||
|
.tm-button-primary { border-radius: 500px; }
|
||||||
|
|
||||||
|
.tm-button-default {}
|
||||||
|
|
||||||
|
.tm-button-large { line-height: 48px; }
|
||||||
|
|
||||||
|
.tm-button-primary {}
|
||||||
|
|
||||||
|
.hook-inverse() {
|
||||||
|
|
||||||
|
.tm-button-default {
|
||||||
|
color: @inverse-global-color;
|
||||||
|
border-color: @inverse-global-muted-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-button-primary {
|
||||||
|
background: #fff !important;
|
||||||
|
color: @global-primary-background !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-button-primary:hover { box-shadow: 0 10px 40px rgba(30,135,240,1); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Section
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tm-section-primary {
|
||||||
|
background: linear-gradient(to left top, #28a5f5, @global-primary-background) 0 0 no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-section-intro {
|
||||||
|
background: linear-gradient(to left top, #28a5f5, @global-primary-background) 0 0 no-repeat,
|
||||||
|
#fff;
|
||||||
|
background-size: 100% 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Heading
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tm-h4,
|
||||||
|
.tm-h5,
|
||||||
|
.tm-h6 {
|
||||||
|
font-family: Montserrat;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
color: @global-muted-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-h4 { font-size: 14px; }
|
||||||
|
.tm-h5 { font-size: 12px; }
|
||||||
|
.tm-h6 { font-size: 11px; }
|
||||||
|
|
||||||
|
.hook-inverse() {
|
||||||
|
|
||||||
|
.tm-h4,
|
||||||
|
.tm-h5,
|
||||||
|
.tm-h6 { color: rgba(255, 255, 255, 0.7); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Text
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tm-text-large { font-size: 18px; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Box shadow
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tm-box-shadow-medium {
|
||||||
|
box-shadow: 2px 12px 35px rgba(0,0,0,0.1),
|
||||||
|
0 1px 6px rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-box-shadow-large {
|
||||||
|
box-shadow: -40px 40px 160px 0 rgba(0,0,0,0.08),
|
||||||
|
-8px 8px 15px 0 rgba(120,120,120,0.04),
|
||||||
|
3px 3px 30px 0 rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-box-shadow-modal { box-shadow: 3px 3px 40px 0 rgba(0,0,0,0.06); }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Overlay
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tm-overlay-default { background: fade(@global-background, 50%); }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Modal
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tm-modal-dialog { background: @global-muted-background; }
|
||||||
|
|
||||||
|
.tm-modal-header { padding: 20px 30px; }
|
||||||
|
.tm-modal-body { padding: 0; }
|
||||||
|
|
||||||
|
/* Phone landscape and bigger */
|
||||||
|
@media (min-width: @breakpoint-small) {
|
||||||
|
|
||||||
|
.tm-modal-header { padding: 25px 50px; }
|
||||||
|
.tm-modal-body { padding: 0 50px; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Desktop and bigger */
|
||||||
|
@media (min-width: @breakpoint-xlarge) {
|
||||||
|
|
||||||
|
.tm-modal-header { padding: 50px 120px; }
|
||||||
|
.tm-modal-body { padding: 0 120px; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.tm-modal-close-full {
|
||||||
|
background: transparent;
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Phone landscape and bigger */
|
||||||
|
@media (min-width: @breakpoint-small) {
|
||||||
|
|
||||||
|
.tm-modal-close-full { padding: @global-medium-margin; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Utilities
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Heading fragment
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tm-heading-fragment > a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Phone landscape and bigger */
|
||||||
|
@media (min-width: @breakpoint-small) {
|
||||||
|
|
||||||
|
h2.tm-heading-fragment > a:before {
|
||||||
|
content: "#";
|
||||||
|
position: absolute;
|
||||||
|
width: 25px;
|
||||||
|
margin-left: -25px;
|
||||||
|
color: @global-muted-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Reset container for docs
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.tm-main .uk-container {
|
||||||
|
padding-left: @container-padding-horizontal;
|
||||||
|
padding-right: @container-padding-horizontal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Phone landscape and bigger */
|
||||||
|
@media (min-width: @breakpoint-small) {
|
||||||
|
|
||||||
|
.tm-main .uk-container {
|
||||||
|
padding-left: @container-padding-horizontal-s;
|
||||||
|
padding-right: @container-padding-horizontal-s;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tablet landscape and bigger */
|
||||||
|
@media (min-width: @breakpoint-medium) {
|
||||||
|
|
||||||
|
.tm-main .uk-container {
|
||||||
|
padding-left: @container-padding-horizontal-m;
|
||||||
|
padding-right: @container-padding-horizontal-m;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Pro
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/* Desktop and bigger */
|
||||||
|
@media (min-width: @breakpoint-large) {
|
||||||
|
|
||||||
|
.tm-intro-text { margin-top: 30px; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Large screen and bigger */
|
||||||
|
@media (min-width: @breakpoint-xlarge) {
|
||||||
|
|
||||||
|
.tm-intro-text { margin-top: 90px; }
|
||||||
|
.tm-intro-image { margin-right: -75px; }
|
||||||
|
|
||||||
|
}
|
||||||
5
src/components/IpDisplay.vue
Normal file
5
src/components/IpDisplay.vue
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p>Current hostname is: {{ $store.getters.host }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
67
src/components/IpInput.vue
Normal file
67
src/components/IpInput.vue
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
<template>
|
||||||
|
<div class="hello">
|
||||||
|
{{ msg }}
|
||||||
|
<div class="uk-margin">
|
||||||
|
<form @submit.prevent="handleSubmit">
|
||||||
|
<div class="uk-inline">
|
||||||
|
<span class="uk-form-icon" uk-icon="icon: server"></span>
|
||||||
|
<input @input="IpChanged" v-bind:class="IpFormClasses" class="uk-input uk-form-width-medium uk-form-small" type="text" name="flavor" placeholder="localhost">
|
||||||
|
</div>
|
||||||
|
<button class="uk-button uk-button-default uk-form-small">Connect</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'HelloWorld',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
msg: String
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
IpChanged: function(event) {
|
||||||
|
if (!(event.target.value == this.$store.getters.host)) {
|
||||||
|
this.hostname = event.target.value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSubmit: function(event) {
|
||||||
|
if (this.hostname != "badhost") {
|
||||||
|
this.$store.commit('changeHost', this.hostname)
|
||||||
|
alert(`Connecting to ${this.$store.getters.host}`)
|
||||||
|
this.$store.commit('changeConnected', true)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
alert("I won't connect to a bad host!")
|
||||||
|
this.$store.commit('changeConnected', false)
|
||||||
|
this.$store.commit('changeAvailable', false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
hostname: "localhost"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
IpFormClasses: function () {
|
||||||
|
return {
|
||||||
|
'uk-form-danger': !this.$store.getters.available,
|
||||||
|
'uk-form-success': this.$store.getters.connected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
<style scoped lang="less">
|
||||||
|
</style>
|
||||||
10
src/main.js
Normal file
10
src/main.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import store from './store'
|
||||||
|
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
store,
|
||||||
|
render: h => h(App)
|
||||||
|
}).$mount('#app')
|
||||||
35
src/store.js
Normal file
35
src/store.js
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import Vuex from 'vuex'
|
||||||
|
|
||||||
|
Vue.use(Vuex)
|
||||||
|
|
||||||
|
export default new Vuex.Store({
|
||||||
|
|
||||||
|
state: {
|
||||||
|
host: '',
|
||||||
|
connected: '',
|
||||||
|
available: ''
|
||||||
|
},
|
||||||
|
|
||||||
|
mutations: {
|
||||||
|
changeHost(state, host) {
|
||||||
|
state.host = host
|
||||||
|
},
|
||||||
|
changeConnected(state, connected) {
|
||||||
|
state.connected = connected
|
||||||
|
},
|
||||||
|
changeAvailable(state, available) {
|
||||||
|
state.available = available
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getters: {
|
||||||
|
host: state => state.host,
|
||||||
|
connected: state => state.connected,
|
||||||
|
available: state => state.available
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3.2"/><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 324 B |
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><path fill="none" d="M0 0h20v20H0V0z"/><path d="M15.95 10.78c.03-.25.05-.51.05-.78s-.02-.53-.06-.78l1.69-1.32c.15-.12.19-.34.1-.51l-1.6-2.77c-.1-.18-.31-.24-.49-.18l-1.99.8c-.42-.32-.86-.58-1.35-.78L12 2.34c-.03-.2-.2-.34-.4-.34H8.4c-.2 0-.36.14-.39.34l-.3 2.12c-.49.2-.94.47-1.35.78l-1.99-.8c-.18-.07-.39 0-.49.18l-1.6 2.77c-.1.18-.06.39.1.51l1.69 1.32c-.04.25-.07.52-.07.78s.02.53.06.78L2.37 12.1c-.15.12-.19.34-.1.51l1.6 2.77c.1.18.31.24.49.18l1.99-.8c.42.32.86.58 1.35.78l.3 2.12c.04.2.2.34.4.34h3.2c.2 0 .37-.14.39-.34l.3-2.12c.49-.2.94-.47 1.35-.78l1.99.8c.18.07.39 0 .49-.18l1.6-2.77c.1-.18.06-.39-.1-.51l-1.67-1.32zM10 13c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 774 B |
399
static/main.js
399
static/main.js
|
|
@ -1,399 +0,0 @@
|
||||||
var apiVersion = "v1"
|
|
||||||
var hostName
|
|
||||||
var hostPort
|
|
||||||
var baseURI
|
|
||||||
|
|
||||||
// Key IDs
|
|
||||||
var pgupKeyID = 33;
|
|
||||||
var pgdnKeyID = 34;
|
|
||||||
|
|
||||||
var leftKeyID = 37;
|
|
||||||
var upKeyID = 38;
|
|
||||||
var rightKeyID = 39;
|
|
||||||
var downKeyID = 40;
|
|
||||||
|
|
||||||
var enterKeyID = 13;
|
|
||||||
var escKeyID = 27;
|
|
||||||
|
|
||||||
var keysDown = {}; //Array of keys down
|
|
||||||
|
|
||||||
var stageVelocity = 50;
|
|
||||||
var focusVelocity = 20;
|
|
||||||
|
|
||||||
var requestLock = false;
|
|
||||||
|
|
||||||
// Microscope client-side callibration
|
|
||||||
//TODO: Move to serverside microscope property
|
|
||||||
var fovX = 4100;
|
|
||||||
var fovY = 3146;
|
|
||||||
|
|
||||||
window.onload = function() {
|
|
||||||
setHostFromInput()
|
|
||||||
|
|
||||||
// TODO: Use getConfig as a connection check. Handle connection timeout.
|
|
||||||
getConfig(updateConfigValues)
|
|
||||||
getState(updateStateValues)
|
|
||||||
updateTextBoxes()
|
|
||||||
getCaptures()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Cosmetic methods
|
|
||||||
function toggleClassToDiv(div_id, class_name) {
|
|
||||||
document.getElementById(div_id).classList.toggle(class_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup methods
|
|
||||||
function setHostFromInput() {
|
|
||||||
hostName = document.getElementById('microscopeHostInput').value;
|
|
||||||
hostPort = document.getElementById('microscopePortInput').value;
|
|
||||||
|
|
||||||
baseURI = `http://${hostName}:${hostPort}/api/${apiVersion}`
|
|
||||||
|
|
||||||
streamContent = `<img src="${baseURI}/stream" ondblclick="clickHotspotImage(event);">`
|
|
||||||
document.getElementById("streambox").innerHTML = streamContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Microscope methods
|
|
||||||
|
|
||||||
function deleteCapture(capture_id) {
|
|
||||||
function deleteCaptureCallback(response, status) {
|
|
||||||
console.log(status);
|
|
||||||
getCaptures();
|
|
||||||
}
|
|
||||||
var r = confirm("Warning! This will delete all copies of this capture from the Raspberry Pi. Click OK to proceed.");
|
|
||||||
if (r == true) {
|
|
||||||
safeRequest("DELETE", baseURI+"/camera/capture/"+capture_id+"/", null, deleteCaptureCallback, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteAllCaptures() {
|
|
||||||
function deleteAllCapturesCallback(response, status) {
|
|
||||||
console.log(status);
|
|
||||||
getCaptures();
|
|
||||||
}
|
|
||||||
var r = confirm("Warning! This will delete all copies of all captures from the Raspberry Pi. Click OK to proceed.");
|
|
||||||
if (r == true) {
|
|
||||||
safeRequest("DELETE", baseURI+"/camera/capture/", null, deleteAllCapturesCallback, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCaptures() {
|
|
||||||
function updateCapturesCallback(response, status) {
|
|
||||||
console.log(status);
|
|
||||||
updateCaptureList(response);
|
|
||||||
}
|
|
||||||
safeRequest("GET", baseURI+"/camera/capture", null, updateCapturesCallback, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateCaptureList(response) {
|
|
||||||
// Clear captures list
|
|
||||||
var capturesNode = document.getElementById("captures");
|
|
||||||
while (capturesNode.firstChild) {
|
|
||||||
capturesNode.removeChild(capturesNode.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each capture in the response array
|
|
||||||
response.forEach(function(element) {
|
|
||||||
// Store the capture object URI
|
|
||||||
element_uri = `${baseURI}/camera/capture/${element.metadata.id}`
|
|
||||||
|
|
||||||
// Generate inner HTML from capture object
|
|
||||||
html = `
|
|
||||||
<div class="capture-thumb"><img src="${element_uri}/download?thumbnail=true"></div>
|
|
||||||
<div class="capture-actions">
|
|
||||||
<div class='capture-heading'>${element.metadata.filename}</div>
|
|
||||||
<br>
|
|
||||||
<button type="button" class="capture-button" onclick="window.open('${element_uri}/download');">Image</button>
|
|
||||||
<button type="button" class="capture-button" onclick="window.open('${element_uri}/metadata');">Metadata</button>
|
|
||||||
<br>
|
|
||||||
<button type="button" class="capture-button" onclick="window.open('${element_uri}/', '_blank');">JSON</button>
|
|
||||||
<button type="button" class="capture-button" onclick="deleteCapture('${element.metadata.id}');">Delete</button>
|
|
||||||
<br>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
|
|
||||||
// Create a new capture div, and append HTML
|
|
||||||
var div = document.createElement("div");
|
|
||||||
div.className = "capture";
|
|
||||||
div.innerHTML = html;
|
|
||||||
capturesNode.appendChild(div);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateTextBoxes() {
|
|
||||||
document.getElementById('stageVelocityText').value = stageVelocity;
|
|
||||||
document.getElementById('stageVelocityInput').value = stageVelocity;
|
|
||||||
document.getElementById('focusVelocityText').value = focusVelocity;
|
|
||||||
document.getElementById('focusVelocityInput').value = focusVelocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePositionValues(state) {
|
|
||||||
document.getElementById('x_abs').value = state.stage.position.x;
|
|
||||||
document.getElementById('y_abs').value = state.stage.position.y;
|
|
||||||
document.getElementById('z_abs').value = state.stage.position.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateFovValues(config) {
|
|
||||||
document.getElementById('fovXText').value = config.fov.x;
|
|
||||||
document.getElementById('fovYText').value = config.fov.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateStateValues(state){
|
|
||||||
updatePositionValues(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateConfigValues(config){
|
|
||||||
updateFovValues(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getState(callback) {
|
|
||||||
function getStateCallback(response, status) {
|
|
||||||
callback(response);
|
|
||||||
}
|
|
||||||
safeRequest("GET", baseURI+"/state", null, getStateCallback, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getConfig(callback) {
|
|
||||||
function getConfigCallback(response, status) {
|
|
||||||
callback(response);
|
|
||||||
}
|
|
||||||
safeRequest("GET", baseURI+"/config", null, getConfigCallback, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Capture methods
|
|
||||||
// TODO: Explicit callback. Roll into class.
|
|
||||||
function newCapture(filename, keep_on_disk, use_video_port, bayer, resizeWidth=null, resizeHeight=null) {
|
|
||||||
// Make a position request
|
|
||||||
function newCaptureCallback(response, status) {
|
|
||||||
if (status != 200) {
|
|
||||||
alert("Capture failed.");
|
|
||||||
}
|
|
||||||
console.log(status, response);
|
|
||||||
getCaptures(); // Update full list of captures when done
|
|
||||||
}
|
|
||||||
|
|
||||||
payload = {
|
|
||||||
"filename": filename,
|
|
||||||
"keep_on_disk": keep_on_disk,
|
|
||||||
"use_video_port": use_video_port,
|
|
||||||
"bayer": bayer
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((resizeWidth) && (resizeHeight)) {
|
|
||||||
payload.size = {
|
|
||||||
"width": resizeWidth,
|
|
||||||
"height": resizeHeight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(payload);
|
|
||||||
safeRequest("POST", baseURI+"/camera/capture/", payload, newCaptureCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function newCaptureFromInput() {
|
|
||||||
captureFilenameInput = document.getElementById('captureFilenameInput');
|
|
||||||
captureKeepOnDiskCheck = document.getElementById('captureKeepOnDiskCheck');
|
|
||||||
captureFullResolutionCheck = document.getElementById('captureFullResolutionCheck');
|
|
||||||
captureBayerCheck = document.getElementById('captureBayerCheck')
|
|
||||||
|
|
||||||
captureResizeCheck = document.getElementById('captureResizeCheck');
|
|
||||||
captureWidthInput = document.getElementById('captureWidthInput')
|
|
||||||
captureHeightInput = document.getElementById('captureHeightInput')
|
|
||||||
|
|
||||||
// Get filename if one is given
|
|
||||||
if (captureFilenameInput.value === "") {
|
|
||||||
filename = null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
filename = captureFilenameInput.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (captureResizeCheck.checked) {
|
|
||||||
resizeWidth = Number(captureWidthInput.value);
|
|
||||||
resizeHeight = Number(captureHeightInput.value);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
resizeWidth = null;
|
|
||||||
resizeHeight = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
newCapture(
|
|
||||||
filename,
|
|
||||||
captureKeepOnDiskCheck.checked,
|
|
||||||
!(captureFullResolutionCheck.checked),
|
|
||||||
captureBayerCheck.checked,
|
|
||||||
resizeWidth,
|
|
||||||
resizeHeight
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function newCaptureResizeToggle(checkbox) {
|
|
||||||
captureWidthInput = document.getElementById('captureWidthInput')
|
|
||||||
captureHeightInput = document.getElementById('captureHeightInput')
|
|
||||||
|
|
||||||
captureWidthInput.disabled = !(checkbox.checked)
|
|
||||||
captureHeightInput.disabled = !(checkbox.checked)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move stage methods
|
|
||||||
// TODO: Combine setStagePositions and moveStagePositions, with bool argument for 'absolute'
|
|
||||||
|
|
||||||
function setStagePositions(x_abs, y_abs, z_abs) {
|
|
||||||
// Make a position request
|
|
||||||
function absMoveCallback(response, status) {
|
|
||||||
if (status != 200) {
|
|
||||||
alert("Error moving stage.");
|
|
||||||
}
|
|
||||||
console.log(status, response);
|
|
||||||
updatePositionValues(response);
|
|
||||||
}
|
|
||||||
safeRequest("POST", baseURI+"/stage/position", { "absolute": true, "x": x_abs, "y": y_abs, "z": z_abs}, absMoveCallback)
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveStagePositions(x_rel, y_rel, z_rel) {
|
|
||||||
// Make a position request
|
|
||||||
function relMoveCallback(response, status) {
|
|
||||||
if (status != 200) {
|
|
||||||
alert("Error moving stage.");
|
|
||||||
}
|
|
||||||
console.log(status, response);
|
|
||||||
updatePositionValues(response);
|
|
||||||
}
|
|
||||||
safeRequest("POST", baseURI+"/stage/position", { "absolute": false, "x": x_rel, "y": y_rel, "z": z_rel}, relMoveCallback)
|
|
||||||
}
|
|
||||||
|
|
||||||
function setStagePositionsFromInput() {
|
|
||||||
x_abs = Number(document.getElementById('x_abs').value);
|
|
||||||
y_abs = Number(document.getElementById('y_abs').value);
|
|
||||||
z_abs = Number(document.getElementById('z_abs').value);
|
|
||||||
setStagePositions(x_abs, y_abs, z_abs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Standard callback for user-controller movement
|
|
||||||
function keyMoveCallback(response, status) {
|
|
||||||
console.log(status, response);
|
|
||||||
updatePositionValues(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods for input events
|
|
||||||
|
|
||||||
// Click-to-move
|
|
||||||
function clickHotspotImage(event) {
|
|
||||||
if (document.getElementById("clickPositionCheck").checked == true) {
|
|
||||||
xCoordinate = event.offsetX;
|
|
||||||
yCoordinate = event.offsetY;
|
|
||||||
console.log(xCoordinate, yCoordinate)
|
|
||||||
xRelative = (0.5*event.target.offsetWidth - xCoordinate)/event.target.offsetWidth;
|
|
||||||
yRelative = (0.5*event.target.offsetHeight - yCoordinate)/event.target.offsetHeight;
|
|
||||||
console.log(xRelative, yRelative)
|
|
||||||
xSteps = xRelative * fovX;
|
|
||||||
ySteps = yRelative * fovY;
|
|
||||||
console.log(xSteps, ySteps, 0)
|
|
||||||
// Make a position request
|
|
||||||
moveStagePositions(xSteps, ySteps, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scroll-to-focus
|
|
||||||
window.addEventListener('wheel', function(e) {
|
|
||||||
multiplier = 1/100;
|
|
||||||
z_delta = e.deltaY * multiplier * focusVelocity;
|
|
||||||
if ((document.getElementById("scrollFocusCheck").checked == true) && (e.target.parentNode.classList.value == "scrolltarget")) {
|
|
||||||
console.log(z_delta);
|
|
||||||
console.log(e.target.parentNode.classList.value);
|
|
||||||
// Make a position request
|
|
||||||
safeRequest("POST", baseURI+"/stage/position", { "absolute": false, "z": z_delta}, keyMoveCallback)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log("Scroll-to-focus inactive")
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Keyboard to move
|
|
||||||
addEventListener("keydown", function (e) {
|
|
||||||
keysDown[e.keyCode] = true; //Add key to array
|
|
||||||
|
|
||||||
// If not currently in an input box
|
|
||||||
if (!(e.target instanceof HTMLInputElement)) {
|
|
||||||
|
|
||||||
// TODO: Remove this if condition? Pointless?
|
|
||||||
// If stage movement keys are pressed
|
|
||||||
if ((leftKeyID in keysDown) || (rightKeyID in keysDown) || (upKeyID in keysDown) || (downKeyID in keysDown) || (pgupKeyID in keysDown) || (pgdnKeyID in keysDown)) {
|
|
||||||
// Calculate movement array
|
|
||||||
x_rel = 0;
|
|
||||||
y_rel = 0;
|
|
||||||
z_rel = 0;
|
|
||||||
if (leftKeyID in keysDown) {
|
|
||||||
x_rel = x_rel + stageVelocity;
|
|
||||||
}
|
|
||||||
if (rightKeyID in keysDown) {
|
|
||||||
x_rel = x_rel - stageVelocity;
|
|
||||||
}
|
|
||||||
if (upKeyID in keysDown) {
|
|
||||||
y_rel = y_rel + stageVelocity;
|
|
||||||
}
|
|
||||||
if (downKeyID in keysDown) {
|
|
||||||
y_rel = y_rel - stageVelocity;
|
|
||||||
}
|
|
||||||
if (pgupKeyID in keysDown) {
|
|
||||||
z_rel = z_rel - focusVelocity;
|
|
||||||
}
|
|
||||||
if (pgdnKeyID in keysDown) {
|
|
||||||
z_rel = z_rel + focusVelocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a position request
|
|
||||||
moveStagePositions(x_rel, y_rel, z_rel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
addEventListener("keyup", function (e) {
|
|
||||||
delete keysDown[e.keyCode]; //Remove key from array
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
// Methods for making requests
|
|
||||||
function safeRequest(method, url, payload, callback, lock=true, force=false) {
|
|
||||||
if (force == true) {
|
|
||||||
allowRequest = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
allowRequest = !(requestLock);
|
|
||||||
}
|
|
||||||
if (allowRequest == true) {
|
|
||||||
var xhr = new XMLHttpRequest(); // new HttpRequest instance
|
|
||||||
xhr.open(method, url);
|
|
||||||
|
|
||||||
if (lock == true) { // If request is using the lock
|
|
||||||
requestLock = true; // Hold the lock
|
|
||||||
}
|
|
||||||
|
|
||||||
xhr.onload = function (e) {
|
|
||||||
if (xhr.readyState === 4) {
|
|
||||||
if (lock == true) { // If request is using the lock
|
|
||||||
requestLock = false; // Release the lock
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xhr.status !== 200) {
|
|
||||||
console.error(xhr.statusText);
|
|
||||||
}
|
|
||||||
callback(JSON.parse(xhr.responseText), xhr.status);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (method == "POST" || method == "PUT") {
|
|
||||||
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
|
||||||
}
|
|
||||||
|
|
||||||
xhr.send(JSON.stringify(payload));
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log("Cannot start the following request while the previous requets is pending:")
|
|
||||||
console.log(method, url, payload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
132
static/style.css
132
static/style.css
|
|
@ -1,132 +0,0 @@
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Preview sizing */
|
|
||||||
.middle img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: contain
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create columns that float next to each other */
|
|
||||||
.column {
|
|
||||||
padding: 10px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left, .right {
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
overflow-y: scroll;
|
|
||||||
flex: 0;
|
|
||||||
flex-shrink: 0;
|
|
||||||
flex-basis: 250px;
|
|
||||||
-webkit-transition: all 0.2s ease-in-out;
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left{
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right{
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden{
|
|
||||||
flex-basis: 0;
|
|
||||||
padding:0
|
|
||||||
}
|
|
||||||
|
|
||||||
.middle {
|
|
||||||
position: relative;
|
|
||||||
flex-grow: 1;
|
|
||||||
background-color: #d3d3d3;
|
|
||||||
}
|
|
||||||
|
|
||||||
#left-sb-btn {
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#right-sb-btn {
|
|
||||||
right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sb-btn {
|
|
||||||
top: 20px;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sb-btn button{
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
margin: 0;
|
|
||||||
padding:5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear floats after the columns */
|
|
||||||
.row{
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Flexboxes */
|
|
||||||
.flexbox {
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexgrow {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Data divs */
|
|
||||||
|
|
||||||
#captures {
|
|
||||||
font-size:12px;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.capture {
|
|
||||||
height: 75px;
|
|
||||||
width: 240px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
background-color: #d3d3d3;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.capture-thumb {
|
|
||||||
width: 80px;
|
|
||||||
height: 75px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.capture-heading {
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 5px 0 5px 0;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.capture-actions {
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.capture img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.capture-button {
|
|
||||||
width: 75px;
|
|
||||||
margin-bottom: 3px;
|
|
||||||
height: 22px;
|
|
||||||
}
|
|
||||||
3
vue.config.js
Normal file
3
vue.config.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
publicPath: ''
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue