Merge branch 'master' of https://gitlab.com/openflexure/openflexure-microscope-server
This commit is contained in:
commit
ce57d37dc9
44 changed files with 1218 additions and 933 deletions
|
|
@ -1,24 +1,30 @@
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
|
|
||||||
env: {
|
env: {
|
||||||
node: true
|
node: true
|
||||||
},
|
},
|
||||||
|
|
||||||
extends: [
|
extends: [
|
||||||
"plugin:vue/recommended",
|
"plugin:vue/recommended",
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
"prettier/vue",
|
"prettier/vue",
|
||||||
"plugin:prettier/recommended"
|
"plugin:prettier/recommended",
|
||||||
|
"plugin:vue/essential",
|
||||||
|
"@vue/prettier"
|
||||||
],
|
],
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
"vue/component-name-in-template-casing": ["error", "PascalCase"],
|
"vue/component-name-in-template-casing": ["error", "PascalCase"],
|
||||||
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
|
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||||
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
|
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off"
|
||||||
},
|
},
|
||||||
|
|
||||||
globals: {
|
globals: {
|
||||||
$nuxt: true
|
$nuxt: true
|
||||||
},
|
},
|
||||||
|
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
parser: "babel-eslint"
|
parser: "babel-eslint"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [
|
presets: ["@vue/app"]
|
||||||
'@vue/app'
|
};
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
|
||||||
475
openflexure_microscope/api/static/package-lock.json
generated
475
openflexure_microscope/api/static/package-lock.json
generated
|
|
@ -2618,6 +2618,101 @@
|
||||||
"webpack": "^4.0.0"
|
"webpack": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@vue/cli-plugin-eslint": {
|
||||||
|
"version": "4.5.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-eslint/-/cli-plugin-eslint-4.5.8.tgz",
|
||||||
|
"integrity": "sha512-1lg3K2D/harXbN4FvRoA1b0X9947H+G4Ql/43rxJY39OqswfK/d1ck438Fo9M4l1+zhBSUNDmcjn7Q2EH6qWmA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@vue/cli-shared-utils": "^4.5.8",
|
||||||
|
"eslint-loader": "^2.2.1",
|
||||||
|
"globby": "^9.2.0",
|
||||||
|
"inquirer": "^7.1.0",
|
||||||
|
"webpack": "^4.0.0",
|
||||||
|
"yorkie": "^2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/cli-shared-utils": {
|
||||||
|
"version": "4.5.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.5.8.tgz",
|
||||||
|
"integrity": "sha512-pa6oenhBO/5HeDLRSokiwVN01gROACEDy3ESXWuPmragOREGNmmFKtkPHlqeYavGEX6LFp7f0VK3uMX6UYS5mQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@hapi/joi": "^15.0.1",
|
||||||
|
"chalk": "^2.4.2",
|
||||||
|
"execa": "^1.0.0",
|
||||||
|
"launch-editor": "^2.2.1",
|
||||||
|
"lru-cache": "^5.1.1",
|
||||||
|
"node-ipc": "^9.1.1",
|
||||||
|
"open": "^6.3.0",
|
||||||
|
"ora": "^3.4.0",
|
||||||
|
"read-pkg": "^5.1.1",
|
||||||
|
"request": "^2.88.2",
|
||||||
|
"semver": "^6.1.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"version": "2.88.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
|
||||||
|
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"aws-sign2": "~0.7.0",
|
||||||
|
"aws4": "^1.8.0",
|
||||||
|
"caseless": "~0.12.0",
|
||||||
|
"combined-stream": "~1.0.6",
|
||||||
|
"extend": "~3.0.2",
|
||||||
|
"forever-agent": "~0.6.1",
|
||||||
|
"form-data": "~2.3.2",
|
||||||
|
"har-validator": "~5.1.3",
|
||||||
|
"http-signature": "~1.2.0",
|
||||||
|
"is-typedarray": "~1.0.0",
|
||||||
|
"isstream": "~0.1.2",
|
||||||
|
"json-stringify-safe": "~5.0.1",
|
||||||
|
"mime-types": "~2.1.19",
|
||||||
|
"oauth-sign": "~0.9.0",
|
||||||
|
"performance-now": "^2.1.0",
|
||||||
|
"qs": "~6.5.2",
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"tough-cookie": "~2.5.0",
|
||||||
|
"tunnel-agent": "^0.6.0",
|
||||||
|
"uuid": "^3.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tough-cookie": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"psl": "^1.1.28",
|
||||||
|
"punycode": "^2.1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@vue/cli-plugin-router": {
|
"@vue/cli-plugin-router": {
|
||||||
"version": "4.5.6",
|
"version": "4.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-4.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-4.5.6.tgz",
|
||||||
|
|
@ -3157,6 +3252,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@vue/eslint-config-prettier": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-wFQmv45c3ige5EA+ngijq40YpVcIkAy0Lihupnsnd1Dao5CBbPyfCzqtejFLZX1EwH/kCJdpz3t6s+5wd3+KxQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"eslint-config-prettier": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@vue/preload-webpack-plugin": {
|
"@vue/preload-webpack-plugin": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz",
|
||||||
|
|
@ -3436,6 +3540,23 @@
|
||||||
"integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==",
|
"integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ansi-escapes": {
|
||||||
|
"version": "4.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz",
|
||||||
|
"integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"type-fest": "^0.11.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"type-fest": {
|
||||||
|
"version": "0.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz",
|
||||||
|
"integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ansi-html": {
|
"ansi-html": {
|
||||||
"version": "0.0.7",
|
"version": "0.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
|
||||||
|
|
@ -4576,6 +4697,12 @@
|
||||||
"tslib": "^1.9.0"
|
"tslib": "^1.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ci-info": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"cipher-base": {
|
"cipher-base": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
||||||
|
|
@ -6402,6 +6529,19 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"eslint-loader": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-RLgV9hoCVsMLvOxCuNjdqOrUqIj9oJg8hF44vzJaYqsAHuY9G2YAeN3joQ9nxP0p5Th9iFSIpKo+SD8KISxXRg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"loader-fs-cache": "^1.0.0",
|
||||||
|
"loader-utils": "^1.0.2",
|
||||||
|
"object-assign": "^4.0.1",
|
||||||
|
"object-hash": "^1.1.4",
|
||||||
|
"rimraf": "^2.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"eslint-plugin-prettier": {
|
"eslint-plugin-prettier": {
|
||||||
"version": "3.1.4",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz",
|
||||||
|
|
@ -6833,6 +6973,15 @@
|
||||||
"integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==",
|
"integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"figures": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"escape-string-regexp": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"file-entry-cache": {
|
"file-entry-cache": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
|
||||||
|
|
@ -8344,6 +8493,162 @@
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"inquirer": {
|
||||||
|
"version": "7.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz",
|
||||||
|
"integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-escapes": "^4.2.1",
|
||||||
|
"chalk": "^4.1.0",
|
||||||
|
"cli-cursor": "^3.1.0",
|
||||||
|
"cli-width": "^3.0.0",
|
||||||
|
"external-editor": "^3.0.3",
|
||||||
|
"figures": "^3.0.0",
|
||||||
|
"lodash": "^4.17.19",
|
||||||
|
"mute-stream": "0.0.8",
|
||||||
|
"run-async": "^2.4.0",
|
||||||
|
"rxjs": "^6.6.0",
|
||||||
|
"string-width": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.0",
|
||||||
|
"through": "^2.3.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chalk": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cli-cursor": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"restore-cursor": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cli-width": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"has-flag": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"is-fullwidth-code-point": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"mimic-fn": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"onetime": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"mimic-fn": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"restore-cursor": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"onetime": "^5.1.0",
|
||||||
|
"signal-exit": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"run-async": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"string-width": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"supports-color": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"has-flag": "^4.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"internal-ip": {
|
"internal-ip": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz",
|
||||||
|
|
@ -8452,6 +8757,15 @@
|
||||||
"integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
|
"integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"is-ci": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ci-info": "^1.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"is-color-stop": {
|
"is-color-stop": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz",
|
||||||
|
|
@ -8966,6 +9280,57 @@
|
||||||
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
|
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"loader-fs-cache": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ldcgZpjNJj71n+2Mf6yetz+c9bM4xpKtNds4LbqXzU/PTdeAX0g3ytnU1AJMEcTk2Lex4Smpe3Q/eCTsvUBxbA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"find-cache-dir": "^0.1.1",
|
||||||
|
"mkdirp": "^0.5.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"find-cache-dir": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz",
|
||||||
|
"integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"commondir": "^1.0.1",
|
||||||
|
"mkdirp": "^0.5.1",
|
||||||
|
"pkg-dir": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"find-up": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"path-exists": "^2.0.0",
|
||||||
|
"pinkie-promise": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path-exists": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
|
||||||
|
"integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"pinkie-promise": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pkg-dir": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"find-up": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"loader-runner": {
|
"loader-runner": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
|
||||||
|
|
@ -9475,6 +9840,12 @@
|
||||||
"integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
|
"integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"mute-stream": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
|
||||||
|
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"mz": {
|
"mz": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
||||||
|
|
@ -9722,6 +10093,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"object-hash": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"object-inspect": {
|
"object-inspect": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
|
||||||
|
|
@ -10891,8 +11268,7 @@
|
||||||
"version": "1.19.1",
|
"version": "1.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
|
||||||
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
|
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
|
||||||
"dev": true,
|
"dev": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"prettier-linter-helpers": {
|
"prettier-linter-helpers": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
|
@ -11509,6 +11885,15 @@
|
||||||
"aproba": "^1.1.1"
|
"aproba": "^1.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"rxjs": {
|
||||||
|
"version": "6.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
|
||||||
|
"integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^1.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
|
@ -12415,6 +12800,12 @@
|
||||||
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
|
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"strip-indent": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"stylehacks": {
|
"stylehacks": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz",
|
||||||
|
|
@ -13292,9 +13683,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vue-loader-v16": {
|
"vue-loader-v16": {
|
||||||
"version": "npm:vue-loader@16.0.0-beta.8",
|
"version": "npm:vue-loader@16.0.0-beta.9",
|
||||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.8.tgz",
|
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.9.tgz",
|
||||||
"integrity": "sha512-oouKUQWWHbSihqSD7mhymGPX1OQ4hedzAHyvm8RdyHh6m3oIvoRF+NM45i/bhNOlo8jCnuJhaSUf/6oDjv978g==",
|
"integrity": "sha512-mu9pg6554GbXDSO8LlxkQM6qUJzUkb/A0FJc9LgRqnU9MCnhzEXwCt1Zx5NObvFpzs2mH2dH/uUCDwL8Qaz9sA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
|
@ -13446,6 +13837,12 @@
|
||||||
"vue": "^2.6.10"
|
"vue": "^2.6.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"vuejs-paginate": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vuejs-paginate/-/vuejs-paginate-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-gnwyXlmCiDOu9MLWxN5UJ4PGijKGNOMpHG8ujsrynCzTJljn/rp7Jq0WiDGDAMi5/u0AHuYIHhced+tUW4jblA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"vuex": {
|
"vuex": {
|
||||||
"version": "3.5.1",
|
"version": "3.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.5.1.tgz",
|
||||||
|
|
@ -14263,6 +14660,74 @@
|
||||||
"camelcase": "^5.0.0",
|
"camelcase": "^5.0.0",
|
||||||
"decamelize": "^1.2.0"
|
"decamelize": "^1.2.0"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"yorkie": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yorkie/-/yorkie-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-jcKpkthap6x63MB4TxwCyuIGkV0oYP/YRyuQU5UO0Yz/E/ZAu+653/uov+phdmO54n6BcvFRyyt0RRrWdN2mpw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"execa": "^0.8.0",
|
||||||
|
"is-ci": "^1.0.10",
|
||||||
|
"normalize-path": "^1.0.0",
|
||||||
|
"strip-indent": "^2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cross-spawn": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||||
|
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"lru-cache": "^4.0.1",
|
||||||
|
"shebang-command": "^1.2.0",
|
||||||
|
"which": "^1.2.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"execa": {
|
||||||
|
"version": "0.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz",
|
||||||
|
"integrity": "sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"cross-spawn": "^5.0.1",
|
||||||
|
"get-stream": "^3.0.0",
|
||||||
|
"is-stream": "^1.1.0",
|
||||||
|
"npm-run-path": "^2.0.0",
|
||||||
|
"p-finally": "^1.0.0",
|
||||||
|
"signal-exit": "^3.0.0",
|
||||||
|
"strip-eof": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"get-stream": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"lru-cache": {
|
||||||
|
"version": "4.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
|
||||||
|
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"pseudomap": "^1.0.2",
|
||||||
|
"yallist": "^2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"normalize-path": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-MtDkcvkf80VwHBWoMRAY07CpA3k=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"yallist": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||||
|
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,11 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "User client for the OpenFlexure Microscope Server",
|
"description": "User client for the OpenFlexure Microscope Server",
|
||||||
"author": "OpenFlexure <contact@openflexure.org> (https://www.openflexure.org)",
|
"author": "OpenFlexure <contact@openflexure.org> (https://www.openflexure.org)",
|
||||||
"homepage": "https://gitlab.com/openflexure/openflexure-microscope-jsclient",
|
|
||||||
"publisher": "Bath Open Instrumentation Group",
|
|
||||||
"displayName": "OpenFlexure Microscope",
|
|
||||||
"license": "GNU General Public License v3.0 ",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve --mode development",
|
||||||
"build": "vue-cli-service build --mode production",
|
"build": "vue-cli-service build --mode production",
|
||||||
"serve": "vue-cli-service serve --mode development"
|
"lint": "vue-cli-service lint",
|
||||||
|
"prettier": "vue-cli-service lint --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"material-design-icons": "^3.0.1",
|
"material-design-icons": "^3.0.1",
|
||||||
|
|
@ -19,7 +17,9 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^3.12.1",
|
"@vue/cli-plugin-babel": "^3.12.1",
|
||||||
|
"@vue/cli-plugin-eslint": "^4.5.8",
|
||||||
"@vue/cli-service": "^4.5.6",
|
"@vue/cli-service": "^4.5.6",
|
||||||
|
"@vue/eslint-config-prettier": "^6.0.0",
|
||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"css-loader": "^3.6.0",
|
"css-loader": "^3.6.0",
|
||||||
|
|
@ -30,12 +30,14 @@
|
||||||
"less": "^3.12.2",
|
"less": "^3.12.2",
|
||||||
"less-loader": "^5.0.0",
|
"less-loader": "^5.0.0",
|
||||||
"mdns-js": "^1.0.3",
|
"mdns-js": "^1.0.3",
|
||||||
|
"prettier": "^1.19.1",
|
||||||
"uikit": "^3.5.7",
|
"uikit": "^3.5.7",
|
||||||
"vue": "^2.6.12",
|
"vue": "^2.6.12",
|
||||||
"vue-friendly-iframe": "^0.17.0",
|
"vue-friendly-iframe": "^0.17.0",
|
||||||
"vue-plugin-load-script": "^1.3.2",
|
"vue-plugin-load-script": "^1.3.2",
|
||||||
"vue-template-compiler": "^2.6.12",
|
"vue-template-compiler": "^2.6.12",
|
||||||
"vue-tour": "^1.5.0",
|
"vue-tour": "^1.5.0",
|
||||||
|
"vuejs-paginate": "^2.1.0",
|
||||||
"vuex": "^3.5.1"
|
"vuex": "^3.5.1"
|
||||||
},
|
},
|
||||||
"postcss": {
|
"postcss": {
|
||||||
|
|
@ -47,5 +49,9 @@
|
||||||
"> 1%",
|
"> 1%",
|
||||||
"last 2 versions",
|
"last 2 versions",
|
||||||
"not ie <= 8"
|
"not ie <= 8"
|
||||||
]
|
],
|
||||||
|
"displayName": "OpenFlexure Microscope",
|
||||||
|
"homepage": "https://gitlab.com/openflexure/openflexure-microscope-jsclient",
|
||||||
|
"license": "GNU General Public License v3.0 ",
|
||||||
|
"publisher": "Bath Open Instrumentation Group"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ import loadingContent from "./components/loadingContent.vue";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
var Mousetrap = require("mousetrap");
|
var Mousetrap = require("mousetrap");
|
||||||
|
|
||||||
Mousetrap.prototype.stopCallback = function(e, element, combo) {
|
Mousetrap.prototype.stopCallback = function(e, element) {
|
||||||
// if the element has the class "mousetrap" then no need to stop
|
// if the element has the class "mousetrap" then no need to stop
|
||||||
if ((" " + element.className + " ").indexOf(" mousetrap ") > -1) {
|
if ((" " + element.className + " ").indexOf(" mousetrap ") > -1) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
#container-left {
|
#container-left {
|
||||||
overflow: hidden;
|
overflow: auto;
|
||||||
background-color: rgba(180, 180, 180, 0.025);
|
background-color: rgba(180, 180, 180, 0.025);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||||
<p v-html="content"></p>
|
<p v-html="content"></p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -200,13 +200,13 @@ export default {
|
||||||
// If task ends with an error
|
// If task ends with an error
|
||||||
else if (result == "error") {
|
else if (result == "error") {
|
||||||
// Pass the error string back with reject
|
// Pass the error string back with reject
|
||||||
console.log("Rejecting pollTask due to error")
|
console.log("Rejecting pollTask due to error");
|
||||||
reject(new Error(response.data.output));
|
reject(new Error(response.data.output));
|
||||||
}
|
}
|
||||||
// If task ends with termination
|
// If task ends with termination
|
||||||
else if (result == "cancelled") {
|
else if (result == "cancelled") {
|
||||||
// Pass a generic termination error back with reject
|
// Pass a generic termination error back with reject
|
||||||
console.log("Rejecting pollTask due to cancellation")
|
console.log("Rejecting pollTask due to cancellation");
|
||||||
reject(new Error("Task cancelled"));
|
reject(new Error("Task cancelled"));
|
||||||
} else {
|
} else {
|
||||||
// Since the task is still running, we can update the progress bar
|
// Since the task is still running, we can update the progress bar
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import devTools from "./controlComponents/devTools.vue";
|
import devTools from "./tabContentComponents/aboutComponents/devTools.vue";
|
||||||
|
|
||||||
// Export main app
|
// Export main app
|
||||||
export default {
|
export default {
|
||||||
|
|
|
||||||
|
|
@ -148,9 +148,9 @@
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
import cameraCalibrationSettings from "../viewComponents/settingsComponents/cameraSettingsComponents/cameraCalibrationSettings.vue";
|
import cameraCalibrationSettings from "../tabContentComponents/settingsComponents/cameraSettingsComponents/cameraCalibrationSettings.vue";
|
||||||
import CSMCalibrationSettings from "../viewComponents/settingsComponents/CSMSettingsComponents/CSMCalibrationSettings.vue";
|
import CSMCalibrationSettings from "../tabContentComponents/settingsComponents/CSMSettingsComponents/CSMCalibrationSettings.vue";
|
||||||
import miniStreamDisplay from "../viewComponents/miniStreamDisplay.vue";
|
import miniStreamDisplay from "../genericComponents/miniStreamDisplay.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "CalibrationModal",
|
name: "CalibrationModal",
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import devTools from "../controlComponents/devTools.vue";
|
import devTools from "./aboutComponents/devTools.vue";
|
||||||
import statusPane from "../viewComponents/statusPane.vue";
|
import statusPane from "./aboutComponents/statusPane.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "AboutContent",
|
name: "AboutContent",
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,6 @@
|
||||||
<option>Number</option>
|
<option>Number</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -269,10 +268,10 @@
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
import tagList from "../fieldComponents/tagList";
|
import tagList from "../../fieldComponents/tagList";
|
||||||
import keyvalList from "../fieldComponents/keyvalList";
|
import keyvalList from "../../fieldComponents/keyvalList";
|
||||||
|
|
||||||
import taskSubmitter from "../genericComponents/taskSubmitter";
|
import taskSubmitter from "../../genericComponents/taskSubmitter";
|
||||||
|
|
||||||
// Export main app
|
// Export main app
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -11,8 +11,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import paneCapture from "../controlComponents/paneCapture";
|
import paneCapture from "./captureComponents/paneCapture";
|
||||||
import streamDisplay from "../viewComponents/streamDisplay.vue";
|
import streamDisplay from "./streamContent.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "NavigateContent",
|
name: "NavigateContent",
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="view-component uk-width-expand">
|
<div class="view-component uk-width-expand">
|
||||||
<galleryDisplay v-if="viewPanel == 'gallery'" />
|
<galleryContent v-if="viewPanel == 'gallery'" />
|
||||||
<settingsDisplay v-else-if="viewPanel == 'settings'" />
|
<settingsContent v-else-if="viewPanel == 'settings'" />
|
||||||
<streamDisplay v-else />
|
<streamDisplay v-else />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -38,9 +38,9 @@
|
||||||
<script>
|
<script>
|
||||||
import JsonForm from "../pluginComponents/JsonForm";
|
import JsonForm from "../pluginComponents/JsonForm";
|
||||||
import WebComponentLoader from "../pluginComponents/WebComponentLoader";
|
import WebComponentLoader from "../pluginComponents/WebComponentLoader";
|
||||||
import streamDisplay from "../viewComponents/streamDisplay.vue";
|
import streamDisplay from "./streamContent.vue";
|
||||||
import galleryDisplay from "../viewComponents/galleryDisplay.vue";
|
import galleryContent from "../tabContentComponents/galleryContent.vue";
|
||||||
import settingsDisplay from "../viewComponents/settingsDisplay.vue";
|
import settingsContent from "../tabContentComponents/settingsContent.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ExtensionContent",
|
name: "ExtensionContent",
|
||||||
|
|
@ -49,8 +49,8 @@ export default {
|
||||||
JsonForm,
|
JsonForm,
|
||||||
WebComponentLoader,
|
WebComponentLoader,
|
||||||
streamDisplay,
|
streamDisplay,
|
||||||
galleryDisplay,
|
galleryContent,
|
||||||
settingsDisplay
|
settingsContent
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
|
|
|
||||||
|
|
@ -212,8 +212,9 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
created: function() {
|
created: function() {
|
||||||
this.getTagRequest();
|
// Get initial metadata from prop
|
||||||
this.getAnnotationsRequest();
|
this.tags = this.captureState.metadata.image.tags;
|
||||||
|
this.annotations = this.captureState.metadata.annotations;
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
<a href="#">
|
<a href="#">
|
||||||
<img
|
<img
|
||||||
class="uk-width-1-1"
|
class="uk-width-1-1"
|
||||||
:data-src="thumbnail"
|
:data-src="scanState.thumbnail"
|
||||||
:alt="metadata.image.id"
|
:alt="scanState.metadata.image.id"
|
||||||
width="300"
|
width="300"
|
||||||
height="225"
|
height="225"
|
||||||
uk-img
|
uk-img
|
||||||
@click="$root.$emit('globalUpdateCaptureFolder', metadata.image.id)"
|
@click="onClick"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -22,7 +22,8 @@
|
||||||
uk-grid
|
uk-grid
|
||||||
>
|
>
|
||||||
<div class="uk-margin-remove-top uk-padding-remove uk-width-expand">
|
<div class="uk-margin-remove-top uk-padding-remove uk-width-expand">
|
||||||
<b>{{ metadata.type || "Dataset" }}: </b> {{ metadata.image.name }}
|
<b>{{ scanState.metadata.type || "Dataset" }}: </b>
|
||||||
|
{{ scanState.metadata.image.name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="uk-margin-remove-top uk-padding-remove uk-width-auto">
|
<div class="uk-margin-remove-top uk-padding-remove uk-width-auto">
|
||||||
<a href="#" class="uk-icon" @click="delAllConfirm()">
|
<a href="#" class="uk-icon" @click="delAllConfirm()">
|
||||||
|
|
@ -34,7 +35,7 @@
|
||||||
<div
|
<div
|
||||||
class="uk-text-meta uk-margin-remove-top uk-padding-remove uk-width-expand"
|
class="uk-text-meta uk-margin-remove-top uk-padding-remove uk-width-expand"
|
||||||
>
|
>
|
||||||
<time>{{ metadata.image.acquisitionDate }}</time>
|
<time>{{ scanState.metadata.image.acquisitionDate }}</time>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="uk-text-meta uk-margin-remove-top uk-padding-remove uk-width-auto"
|
class="uk-text-meta uk-margin-remove-top uk-padding-remove uk-width-auto"
|
||||||
|
|
@ -45,7 +46,7 @@
|
||||||
|
|
||||||
<div class="uk-card-footer uk-padding-small">
|
<div class="uk-card-footer uk-padding-small">
|
||||||
<span
|
<span
|
||||||
v-for="tag in metadata.image.tags"
|
v-for="tag in scanState.metadata.image.tags"
|
||||||
:key="tag"
|
:key="tag"
|
||||||
class="uk-label uk-margin-small-right deletable-label"
|
class="uk-label uk-margin-small-right deletable-label"
|
||||||
>
|
>
|
||||||
|
|
@ -56,13 +57,16 @@
|
||||||
<div :id="metadataModalID" uk-modal>
|
<div :id="metadataModalID" uk-modal>
|
||||||
<div class="uk-modal-dialog uk-modal-body">
|
<div class="uk-modal-dialog uk-modal-body">
|
||||||
<button class="uk-modal-close-default" type="button" uk-close></button>
|
<button class="uk-modal-close-default" type="button" uk-close></button>
|
||||||
<h2 class="uk-modal-title">{{ metadata.image.name }}</h2>
|
<h2 class="uk-modal-title">{{ scanState.metadata.image.name }}</h2>
|
||||||
<p><b>Time: </b>{{ metadata.image.acquisitionDate }}</p>
|
<p><b>Time: </b>{{ scanState.metadata.image.acquisitionDate }}</p>
|
||||||
<p><b>ID: </b>{{ metadata.image.id }}</p>
|
<p><b>ID: </b>{{ scanState.metadata.image.id }}</p>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div v-for="(value, key) in metadata.image.annotations" :key="key">
|
<div
|
||||||
|
v-for="(value, key) in scanState.metadata.image.annotations"
|
||||||
|
:key="key"
|
||||||
|
>
|
||||||
<p>
|
<p>
|
||||||
<b>{{ key }}: </b>{{ value }}
|
<b>{{ key }}: </b>{{ value }}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -80,17 +84,9 @@ export default {
|
||||||
name: "ScanCard",
|
name: "ScanCard",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
metadata: {
|
scanState: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
},
|
|
||||||
thumbnail: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
captures: {
|
|
||||||
type: Array,
|
|
||||||
required: true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -109,7 +105,7 @@ export default {
|
||||||
},
|
},
|
||||||
allURLs: function() {
|
allURLs: function() {
|
||||||
var urls = [];
|
var urls = [];
|
||||||
for (var capture of this.captures) {
|
for (var capture of this.scanState.captures) {
|
||||||
urls.push(capture.links.self.href);
|
urls.push(capture.links.self.href);
|
||||||
}
|
}
|
||||||
return urls;
|
return urls;
|
||||||
|
|
@ -117,8 +113,12 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
onClick: function() {
|
||||||
|
this.$emit("selectFolder", this.scanState.metadata.image.id);
|
||||||
|
},
|
||||||
|
|
||||||
makeModalName: function(prefix) {
|
makeModalName: function(prefix) {
|
||||||
return prefix + this.metadata.image.id;
|
return prefix + this.scanState.metadata.image.id;
|
||||||
},
|
},
|
||||||
|
|
||||||
delAllConfirm: function() {
|
delAllConfirm: function() {
|
||||||
|
|
@ -131,18 +131,10 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteAll: function() {
|
deleteAll: function() {
|
||||||
axios
|
axios.all(this.allURLs.map(l => axios.delete(l))).then(() => {
|
||||||
.all(this.allURLs.map(l => axios.delete(l)))
|
// Emit signal to update capture list
|
||||||
.then
|
this.$root.$emit("globalUpdateCaptures");
|
||||||
//axios.spread(function(...res) {
|
});
|
||||||
// all requests are now complete
|
|
||||||
//console.log(res);
|
|
||||||
//})
|
|
||||||
()
|
|
||||||
.then(() => {
|
|
||||||
// Emit signal to update capture list
|
|
||||||
this.$root.$emit("globalUpdateCaptures");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1,19 +1,437 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- Grid managing tab content -->
|
<div class="galleryDisplay uk-padding uk-padding-remove-top">
|
||||||
|
<!-- Gallery nav bar -->
|
||||||
|
<nav
|
||||||
|
class="gallery-navbar uk-navbar-container uk-navbar-transparent"
|
||||||
|
uk-navbar="mode: click"
|
||||||
|
>
|
||||||
|
<!-- Left side controls -->
|
||||||
|
<div
|
||||||
|
class="uk-navbar-left uk-padding-remove-top uk-padding-remove-bottom"
|
||||||
|
>
|
||||||
|
<ul class="uk-navbar-nav">
|
||||||
|
<li :class="[sortDescending ? 'uk-active' : '']">
|
||||||
|
<a class="uk-icon" href="#" @click="sortDescending = true">
|
||||||
|
<i class="material-icons">keyboard_arrow_down</i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li :class="[!sortDescending ? 'uk-active' : '']">
|
||||||
|
<a class="uk-icon" href="#" @click="sortDescending = false">
|
||||||
|
<i class="material-icons">keyboard_arrow_up</i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#">Filter</a>
|
||||||
|
<div
|
||||||
|
:class="{
|
||||||
|
'uk-light uk-background-secondary':
|
||||||
|
$store.state.globalSettings.darkMode
|
||||||
|
}"
|
||||||
|
class="uk-navbar-dropdown"
|
||||||
|
>
|
||||||
|
<ul class="uk-nav uk-navbar-dropdown-nav">
|
||||||
|
<form class="uk-form-stacked">
|
||||||
|
<div
|
||||||
|
v-for="tag in allTags"
|
||||||
|
:key="tag"
|
||||||
|
class="uk-margin-small"
|
||||||
|
>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
:id="tag"
|
||||||
|
v-model="checkedTags"
|
||||||
|
:value="tag"
|
||||||
|
checked
|
||||||
|
class="uk-checkbox"
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
{{ tag }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="view-component uk-height-1-1 uk-width-expand">
|
<!-- Right side buttons -->
|
||||||
<galleryDisplay />
|
<div class="uk-navbar-right">
|
||||||
|
<div class="uk-grid">
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class="uk-button uk-button-default uk-width-1-1"
|
||||||
|
type="button"
|
||||||
|
@click="updateCaptures()"
|
||||||
|
>
|
||||||
|
Refresh Captures
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ZipDownloader :capture-ids="Object.keys(filteredCaptures)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Gallery -->
|
||||||
|
<div
|
||||||
|
v-if="$store.getters.ready"
|
||||||
|
class="uk-padding-remove-top"
|
||||||
|
uk-lightbox="toggle: .lightbox-link"
|
||||||
|
>
|
||||||
|
<!-- Folder heading -->
|
||||||
|
<div
|
||||||
|
v-if="galleryFolder"
|
||||||
|
class="gallery-folder-heading uk-flex uk-flex-middle"
|
||||||
|
>
|
||||||
|
<a class="uk-icon uk-margin-remove" href="#" @click="galleryBack()">
|
||||||
|
<i class="material-icons">arrow_back</i>
|
||||||
|
</a>
|
||||||
|
<div class="uk-margin-left">
|
||||||
|
<h3 class="uk-margin-remove uk-margin-left">
|
||||||
|
<b>SCAN</b>
|
||||||
|
{{ allScans[galleryFolder].metadata.image.name }}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Gallery capture cards -->
|
||||||
|
<div class="gallery-grid">
|
||||||
|
<div v-for="item in pagedItems" :key="item.metadata.id">
|
||||||
|
<scanCard
|
||||||
|
v-if="'isScan' in item"
|
||||||
|
:scan-state="item"
|
||||||
|
@selectFolder="selectFolder"
|
||||||
|
/>
|
||||||
|
<captureCard v-else :capture-state="item" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Paginate
|
||||||
|
v-model="page"
|
||||||
|
:page-count="numberOfPages"
|
||||||
|
:page-range="3"
|
||||||
|
:margin-pages="1"
|
||||||
|
:container-class="'uk-pagination uk-flex-center'"
|
||||||
|
:prev-text="'Prev'"
|
||||||
|
:next-text="'Next'"
|
||||||
|
:page-class="'page-item'"
|
||||||
|
:disabled-class="'uk-disabled'"
|
||||||
|
>
|
||||||
|
</Paginate>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import galleryDisplay from "../viewComponents/galleryDisplay.vue";
|
import axios from "axios";
|
||||||
|
import Paginate from "vuejs-paginate";
|
||||||
|
|
||||||
|
import captureCard from "./galleryComponents/captureCard.vue";
|
||||||
|
import scanCard from "./galleryComponents/scanCard.vue";
|
||||||
|
import ZipDownloader from "./galleryComponents/zipDownloader";
|
||||||
|
|
||||||
|
// Export main app
|
||||||
export default {
|
export default {
|
||||||
name: "NavigateContent",
|
name: "GalleryDisplay",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
galleryDisplay
|
captureCard,
|
||||||
|
scanCard,
|
||||||
|
ZipDownloader,
|
||||||
|
Paginate
|
||||||
|
},
|
||||||
|
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
captures: [],
|
||||||
|
checkedTags: [],
|
||||||
|
sortDescending: true,
|
||||||
|
galleryFolder: "",
|
||||||
|
scanTag: "scan",
|
||||||
|
unwatchStoreFunction: null,
|
||||||
|
maxitems: 10,
|
||||||
|
page: 1
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
capturesUri: function() {
|
||||||
|
return `${this.$store.getters.baseUri}/api/v2/captures`;
|
||||||
|
},
|
||||||
|
allTags: function() {
|
||||||
|
// Return an array of unique tags across all captures
|
||||||
|
var tags = [];
|
||||||
|
for (var capture of this.captures) {
|
||||||
|
for (var tag of capture.metadata.image.tags) {
|
||||||
|
if (!tags.includes(tag)) {
|
||||||
|
tags.push(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tags.sort();
|
||||||
|
},
|
||||||
|
|
||||||
|
noScanCaptures: function() {
|
||||||
|
// List of captures that are not part of a scan
|
||||||
|
var captures = [];
|
||||||
|
for (var capture of this.captures) {
|
||||||
|
// Add to capture list if matched
|
||||||
|
if (!capture.metadata.dataset) {
|
||||||
|
captures.push(capture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return captures;
|
||||||
|
},
|
||||||
|
|
||||||
|
allScans: function() {
|
||||||
|
// List of scans as capture-like objects
|
||||||
|
var scans = {};
|
||||||
|
|
||||||
|
for (var capture of this.captures) {
|
||||||
|
var annotations = capture.metadata.image.annotations;
|
||||||
|
var tags = capture.metadata.image.tags;
|
||||||
|
var dataset = capture.metadata.dataset;
|
||||||
|
|
||||||
|
if (dataset) {
|
||||||
|
var id = dataset["id"];
|
||||||
|
|
||||||
|
// If this scan ID hasn't been seen before
|
||||||
|
if (!(id in scans)) {
|
||||||
|
scans[id] = {};
|
||||||
|
scans[id].isScan = true;
|
||||||
|
scans[id].captures = [];
|
||||||
|
scans[id].metadata = {
|
||||||
|
image: {
|
||||||
|
name: dataset.name,
|
||||||
|
acquisitionDate: dataset.acquisitionDate,
|
||||||
|
id: dataset.id,
|
||||||
|
tags: [],
|
||||||
|
annotations: {}
|
||||||
|
},
|
||||||
|
type: dataset.type
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the capture object to the scan
|
||||||
|
scans[id].captures.push(capture);
|
||||||
|
|
||||||
|
// Add missing scan metadata, prioritising first capture
|
||||||
|
for (var key of Object.keys(annotations)) {
|
||||||
|
if (!(key in scans[id].metadata.image.annotations)) {
|
||||||
|
scans[id].metadata.image.annotations[key] = annotations[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append missing tags
|
||||||
|
for (var tag of tags) {
|
||||||
|
if (!scans[id].metadata.image.tags.includes(tag)) {
|
||||||
|
scans[id].metadata.image.tags.push(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a preview thumbnail
|
||||||
|
if (!("thumbnail" in scans[id])) {
|
||||||
|
scans[
|
||||||
|
id
|
||||||
|
].thumbnail = `${capture.links.download.href}?thumbnail=true`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scans;
|
||||||
|
},
|
||||||
|
|
||||||
|
scanList: function() {
|
||||||
|
// List of scans, obtained from this.allScans values
|
||||||
|
return Object.values(this.allScans);
|
||||||
|
},
|
||||||
|
|
||||||
|
itemList: function() {
|
||||||
|
// Get list of current items to show
|
||||||
|
// If galleryFolder (ie inside a scan folder), show scan captures
|
||||||
|
// Otherwise, show root captures and scan cards
|
||||||
|
if (this.galleryFolder) {
|
||||||
|
return this.allScans[this.galleryFolder].captures;
|
||||||
|
} else {
|
||||||
|
return this.noScanCaptures.concat(this.scanList);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
filteredItems: function() {
|
||||||
|
// Filter itemList by checkedTags
|
||||||
|
return this.filterCaptures(this.itemList, this.checkedTags);
|
||||||
|
},
|
||||||
|
|
||||||
|
filteredCaptures: function() {
|
||||||
|
var captures = {};
|
||||||
|
|
||||||
|
for (var item of this.filteredItems) {
|
||||||
|
// If it's a dataset
|
||||||
|
if ("captures" in item) {
|
||||||
|
for (var capture of item.captures) {
|
||||||
|
// Get the ID of each capture in the set
|
||||||
|
captures[capture.metadata.image.id] = capture;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If it's a single capture, get the ID of the capture
|
||||||
|
captures[item.metadata.image.id] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return captures;
|
||||||
|
},
|
||||||
|
|
||||||
|
sortedItems: function() {
|
||||||
|
// Sort filteredItems using sortCaptures function
|
||||||
|
return this.sortCaptures(this.filteredItems);
|
||||||
|
},
|
||||||
|
|
||||||
|
pagedItems: function() {
|
||||||
|
let startIndex = (this.page - 1) * this.maxitems;
|
||||||
|
return this.sortedItems.slice(startIndex, startIndex + this.maxitems);
|
||||||
|
},
|
||||||
|
|
||||||
|
numberOfPages: function() {
|
||||||
|
return Math.floor(this.sortedItems.length / this.maxitems);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
// Update on mount (does nothing if not connected)
|
||||||
|
this.updateCaptures();
|
||||||
|
// A global signal listener to perform a gallery refresh
|
||||||
|
this.$root.$on("globalUpdateCaptures", () => {
|
||||||
|
this.updateCaptures();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
created: function() {
|
||||||
|
// Watch for host 'ready', then update status
|
||||||
|
this.unwatchStoreFunction = this.$store.watch(
|
||||||
|
(state, getters) => {
|
||||||
|
return getters.ready;
|
||||||
|
},
|
||||||
|
ready => {
|
||||||
|
if (ready) {
|
||||||
|
// If the connection is now ready, update capture list
|
||||||
|
this.updateCaptures();
|
||||||
|
} else {
|
||||||
|
// If the connection is now disconnected, empty capture list
|
||||||
|
this.captures = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
// Remove global signal listener to perform a gallery refresh
|
||||||
|
this.$root.$off("globalUpdateCaptures");
|
||||||
|
// Then we call that function here to unwatch
|
||||||
|
if (this.unwatchStoreFunction) {
|
||||||
|
this.unwatchStoreFunction();
|
||||||
|
this.unwatchStoreFunction = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
updateCaptures: function() {
|
||||||
|
if (this.$store.state.available) {
|
||||||
|
console.log("Updating capture list...");
|
||||||
|
axios
|
||||||
|
.get(this.capturesUri)
|
||||||
|
.then(response => {
|
||||||
|
this.captures = response.data;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.modalError(error); // Let mixin handle error
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("Delaying capture update until connection is available");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
filterCaptures: function(list, filterTags) {
|
||||||
|
// Filter a list of captures by an array of tags
|
||||||
|
var result = [];
|
||||||
|
for (var capture of list) {
|
||||||
|
// Assume exclusion
|
||||||
|
var includeCapture = false;
|
||||||
|
|
||||||
|
// Filter by selected tags
|
||||||
|
var tags = capture.metadata.image.tags;
|
||||||
|
let checker = (arr, target) => target.every(v => arr.includes(v));
|
||||||
|
// True if all tags match
|
||||||
|
includeCapture = checker(tags, filterTags);
|
||||||
|
|
||||||
|
// Add to capture list if matched
|
||||||
|
if (includeCapture == true) {
|
||||||
|
result.push(capture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
sortCaptures: function(list) {
|
||||||
|
// Sort a list of captures by metadata time
|
||||||
|
function compare(a, b) {
|
||||||
|
if (a.metadata.image.acquisitionDate < b.metadata.image.acquisitionDate)
|
||||||
|
return -1;
|
||||||
|
if (a.metadata.image.acquisitionDate > b.metadata.image.acquisitionDate)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.sortDescending == true) {
|
||||||
|
return list.sort(compare).reverse();
|
||||||
|
} else {
|
||||||
|
return list.sort(compare);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
galleryBack: function() {
|
||||||
|
this.galleryFolder = "";
|
||||||
|
this.page = 1;
|
||||||
|
},
|
||||||
|
|
||||||
|
selectFolder: function(folderID) {
|
||||||
|
console.log(folderID);
|
||||||
|
this.galleryFolder = folderID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.gallery-navbar {
|
||||||
|
border-width: 0 0 1px 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: rgba(180, 180, 180, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-navbar,
|
||||||
|
.gallery-folder-heading {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, 320px);
|
||||||
|
justify-content: center;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-grid > div {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ .capture-card {
|
||||||
|
width: 300px;
|
||||||
|
height: 100%; // Used to have all cards in a row match their heights
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import taskSubmitter from "../genericComponents/taskSubmitter";
|
import taskSubmitter from "../../genericComponents/taskSubmitter";
|
||||||
|
|
||||||
// Export main app
|
// Export main app
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -11,8 +11,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import paneNavigate from "../controlComponents/paneNavigate";
|
import paneNavigate from "./navigateComponents/paneNavigate";
|
||||||
import streamDisplay from "../viewComponents/streamDisplay.vue";
|
import streamDisplay from "./streamContent.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "NavigateContent",
|
name: "NavigateContent",
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import CSMCalibrationSettings from "./CSMSettingsComponents/CSMCalibrationSettings.vue";
|
import CSMCalibrationSettings from "./CSMSettingsComponents/CSMCalibrationSettings.vue";
|
||||||
import miniStreamDisplay from "../miniStreamDisplay.vue";
|
import miniStreamDisplay from "../../genericComponents/miniStreamDisplay.vue";
|
||||||
|
|
||||||
// Export main app
|
// Export main app
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -69,7 +69,7 @@
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import cameraCalibrationSettings from "./cameraSettingsComponents/cameraCalibrationSettings.vue";
|
import cameraCalibrationSettings from "./cameraSettingsComponents/cameraCalibrationSettings.vue";
|
||||||
import miniStreamDisplay from "../miniStreamDisplay.vue";
|
import miniStreamDisplay from "../../genericComponents/miniStreamDisplay.vue";
|
||||||
|
|
||||||
// Export main app
|
// Export main app
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -8,11 +8,11 @@
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
<p class="uk-margin-small uk-width-xlarge">
|
<p class="uk-margin-small uk-width-xlarge">
|
||||||
Enabling IHI Slide Scan will add a new tab designed to simplify
|
Enabling IHI Slide Scan will add a new tab designed to simplify acquiring
|
||||||
acquiring blood smear tile scans. </br>
|
blood smear tile scans. <br />
|
||||||
While this may be useful for other applications, it is primarily designed
|
While this may be useful for other applications, it is primarily designed
|
||||||
for use in clinics acquiring blood smear samples using a 100x oil-immersion
|
for use in clinics acquiring blood smear samples using a 100x
|
||||||
objective, with a Raspberry Pi Camera v2.
|
oil-immersion objective, with a Raspberry Pi Camera v2.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<h3>Stage settings</h3>
|
<h3>Stage settings</h3>
|
||||||
<p>
|
<p>
|
||||||
<label class="uk-form-label">
|
<label class="uk-form-label">
|
||||||
Stage Geometry
|
Stage geometry
|
||||||
<div v-if="stageType != 'MissingStage'">
|
<div v-if="stageType != 'MissingStage'">
|
||||||
<form @submit.prevent="setStageType">
|
<form @submit.prevent="setStageType">
|
||||||
<div class="uk-margin-small">
|
<div class="uk-margin-small">
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
type="submit"
|
type="submit"
|
||||||
class="uk-button uk-button-primary uk-width-1-1"
|
class="uk-button uk-button-primary uk-width-1-1"
|
||||||
>
|
>
|
||||||
Change stage type
|
Change stage geometry
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
@ -70,11 +70,15 @@ export default {
|
||||||
setStageType: function() {
|
setStageType: function() {
|
||||||
console.log("Setting stage type");
|
console.log("Setting stage type");
|
||||||
axios
|
axios
|
||||||
.put(this.stageTypeUri, this.stageType)
|
.put(this.stageTypeUri, this.stageType,{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.stageType = response.data;
|
this.stageType = response.data;
|
||||||
console.log("Stage type set to " + this.stageType);
|
console.log("Stage type set to " + this.stageType);
|
||||||
this.modalNotify("Stage type changed.");
|
this.modalNotify("Stage geometry changed.");
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
this.modalError(error);
|
this.modalError(error);
|
||||||
|
|
@ -1,20 +1,224 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- Grid managing tab content -->
|
<!-- Grid managing tab content -->
|
||||||
<div uk-grid class="uk-height-1-1 uk-margin-remove uk-padding-remove">
|
<div uk-grid class="uk-height-1-1 uk-margin-remove uk-padding-remove">
|
||||||
|
<div class="settings-nav">
|
||||||
|
<ul class="uk-nav uk-nav-default">
|
||||||
|
<li class="uk-nav-header">Application settings</li>
|
||||||
|
<li>
|
||||||
|
<tabIcon
|
||||||
|
id="settings-display-icon"
|
||||||
|
tab-i-d="display"
|
||||||
|
:show-title="false"
|
||||||
|
:show-tooltip="false"
|
||||||
|
:require-connection="false"
|
||||||
|
:current-tab="currentTab"
|
||||||
|
@set-tab="setTab"
|
||||||
|
>
|
||||||
|
Display
|
||||||
|
</tabIcon>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<tabIcon
|
||||||
|
id="settings-features-icon"
|
||||||
|
tab-i-d="features"
|
||||||
|
:show-title="false"
|
||||||
|
:show-tooltip="false"
|
||||||
|
:require-connection="false"
|
||||||
|
:current-tab="currentTab"
|
||||||
|
@set-tab="setTab"
|
||||||
|
>
|
||||||
|
Features
|
||||||
|
</tabIcon>
|
||||||
|
</li>
|
||||||
|
<li class="uk-nav-header">Microscope settings</li>
|
||||||
|
<li>
|
||||||
|
<tabIcon
|
||||||
|
id="settings-camera-icon"
|
||||||
|
tab-i-d="camera"
|
||||||
|
:show-title="false"
|
||||||
|
:show-tooltip="false"
|
||||||
|
:require-connection="true"
|
||||||
|
:current-tab="currentTab"
|
||||||
|
@set-tab="setTab"
|
||||||
|
>
|
||||||
|
Camera
|
||||||
|
</tabIcon>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<tabIcon
|
||||||
|
id="settings-stage-icon"
|
||||||
|
tab-i-d="stage"
|
||||||
|
:show-title="false"
|
||||||
|
:show-tooltip="false"
|
||||||
|
:require-connection="true"
|
||||||
|
:current-tab="currentTab"
|
||||||
|
@set-tab="setTab"
|
||||||
|
>
|
||||||
|
Stage
|
||||||
|
</tabIcon>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<tabIcon
|
||||||
|
id="settings-mapping-icon"
|
||||||
|
tab-i-d="mapping"
|
||||||
|
:show-title="false"
|
||||||
|
:show-tooltip="false"
|
||||||
|
:require-connection="true"
|
||||||
|
:current-tab="currentTab"
|
||||||
|
@set-tab="setTab"
|
||||||
|
>
|
||||||
|
Camera/stage mapping
|
||||||
|
</tabIcon>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<tabIcon
|
||||||
|
id="settings-microscope-icon"
|
||||||
|
tab-i-d="microscope"
|
||||||
|
:show-title="false"
|
||||||
|
:show-tooltip="false"
|
||||||
|
:require-connection="true"
|
||||||
|
:current-tab="currentTab"
|
||||||
|
@set-tab="setTab"
|
||||||
|
>
|
||||||
|
General
|
||||||
|
</tabIcon>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
<div class="view-component uk-width-expand uk-padding-small">
|
<div class="view-component uk-width-expand uk-padding-small">
|
||||||
<settingsDisplay />
|
<tabContent
|
||||||
|
tab-i-d="display"
|
||||||
|
:require-connection="false"
|
||||||
|
:current-tab="currentTab"
|
||||||
|
>
|
||||||
|
<div class="settings-pane uk-padding-small">
|
||||||
|
<appSettings />
|
||||||
|
<streamSettings />
|
||||||
|
</div>
|
||||||
|
</tabContent>
|
||||||
|
|
||||||
|
<tabContent
|
||||||
|
tab-i-d="features"
|
||||||
|
:require-connection="false"
|
||||||
|
:current-tab="currentTab"
|
||||||
|
>
|
||||||
|
<div class="settings-pane uk-padding-small">
|
||||||
|
<featuresSettings />
|
||||||
|
</div>
|
||||||
|
</tabContent>
|
||||||
|
|
||||||
|
<tabContent
|
||||||
|
tab-i-d="camera"
|
||||||
|
:require-connection="true"
|
||||||
|
:current-tab="currentTab"
|
||||||
|
>
|
||||||
|
<div class="settings-pane uk-padding-small">
|
||||||
|
<cameraSettings />
|
||||||
|
</div>
|
||||||
|
</tabContent>
|
||||||
|
|
||||||
|
<tabContent
|
||||||
|
tab-i-d="stage"
|
||||||
|
:require-connection="true"
|
||||||
|
:current-tab="currentTab"
|
||||||
|
>
|
||||||
|
<div class="settings-pane uk-padding-small">
|
||||||
|
<stageSettings />
|
||||||
|
</div>
|
||||||
|
</tabContent>
|
||||||
|
|
||||||
|
<tabContent
|
||||||
|
tab-i-d="mapping"
|
||||||
|
:require-connection="true"
|
||||||
|
:current-tab="currentTab"
|
||||||
|
>
|
||||||
|
<div class="settings-pane uk-padding-small">
|
||||||
|
<h3>Camera/stage mapping</h3>
|
||||||
|
<p>
|
||||||
|
Camera/stage mapping allows the stage to move relative to the camera
|
||||||
|
view. This enables functions like click-to-move, and more precise
|
||||||
|
tile scans.
|
||||||
|
</p>
|
||||||
|
<CSMSettings />
|
||||||
|
</div>
|
||||||
|
</tabContent>
|
||||||
|
|
||||||
|
<tabContent
|
||||||
|
tab-i-d="microscope"
|
||||||
|
:require-connection="true"
|
||||||
|
:current-tab="currentTab"
|
||||||
|
>
|
||||||
|
<div class="settings-pane uk-padding-small">
|
||||||
|
<h3>Microscope settings</h3>
|
||||||
|
<microscopeSettings />
|
||||||
|
</div>
|
||||||
|
</tabContent>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import settingsDisplay from "../viewComponents/settingsDisplay.vue";
|
import streamSettings from "./settingsComponents/streamSettings.vue";
|
||||||
|
import microscopeSettings from "./settingsComponents/microscopeSettings.vue";
|
||||||
|
import cameraSettings from "./settingsComponents/cameraSettings.vue";
|
||||||
|
import appSettings from "./settingsComponents/appSettings.vue";
|
||||||
|
import featuresSettings from "./settingsComponents/featuresSettings.vue";
|
||||||
|
import CSMSettings from "./settingsComponents/CSMSettings.vue";
|
||||||
|
import stageSettings from "./settingsComponents/stageSettings.vue";
|
||||||
|
// Import generic components
|
||||||
|
import tabIcon from "../genericComponents/tabIcon";
|
||||||
|
import tabContent from "../genericComponents/tabContent";
|
||||||
|
|
||||||
|
// Export main app
|
||||||
export default {
|
export default {
|
||||||
name: "NavigateContent",
|
name: "SettingsContent",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
settingsDisplay
|
streamSettings,
|
||||||
|
cameraSettings,
|
||||||
|
stageSettings,
|
||||||
|
microscopeSettings,
|
||||||
|
CSMSettings,
|
||||||
|
appSettings,
|
||||||
|
featuresSettings,
|
||||||
|
tabIcon,
|
||||||
|
tabContent
|
||||||
|
},
|
||||||
|
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
selected: "display",
|
||||||
|
currentTab: "display"
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
setTab: function(event, tab) {
|
||||||
|
if (!(this.currentTab == tab)) {
|
||||||
|
this.currentTab = tab;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
// Custom UIkit CSS modifications
|
||||||
|
@import "../../assets/less/theme.less";
|
||||||
|
|
||||||
|
.settings-nav {
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
width: 250px;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: rgba(180, 180, 180, 0.03);
|
||||||
|
border-width: 0 1px 0 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: rgba(180, 180, 180, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-nav li > a {
|
||||||
|
padding-left: 6px !important;
|
||||||
|
border-radius: @button-border-radius;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import taskSubmitter from "../genericComponents/taskSubmitter";
|
import taskSubmitter from "../../genericComponents/taskSubmitter";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -11,8 +11,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import paneSlideScan from "../controlComponents/paneSlideScan";
|
import paneSlideScan from "./slideScanComponents/paneSlideScan";
|
||||||
import streamDisplay from "../viewComponents/streamDisplay.vue";
|
import streamDisplay from "./streamContent.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "SlideScanContent",
|
name: "SlideScanContent",
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import streamDisplay from "../viewComponents/streamDisplay.vue";
|
import streamDisplay from "./streamContent.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ViewContent",
|
name: "ViewContent",
|
||||||
|
|
|
||||||
|
|
@ -1,168 +0,0 @@
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
class="hostCard uk-card uk-card-default uk-padding-remove uk-width-medium"
|
|
||||||
:class="{ 'uk-card-secondary': $store.state.globalSettings.darkMode }"
|
|
||||||
>
|
|
||||||
<div class="uk-card-media-top">
|
|
||||||
<div class="snapshot-container uk-width-1-1 uk-height-auto">
|
|
||||||
<img
|
|
||||||
class="uk-width-1-1"
|
|
||||||
:data-src="snapshotSrc"
|
|
||||||
:src="snapshotSrc"
|
|
||||||
width="300"
|
|
||||||
height="225"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
v-if="!snapshotAvailable"
|
|
||||||
class="uk-position-cover uk-flex uk-flex-center uk-flex-middle"
|
|
||||||
>
|
|
||||||
Snapshot unavailable
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="uk-card-body uk-padding-small">
|
|
||||||
<div
|
|
||||||
class="uk-margin-small uk-margin-remove-left uk-margin-remove-right uk-flex uk-flex-middle"
|
|
||||||
>
|
|
||||||
<div class="uk-width-expand host-description">
|
|
||||||
<div class="host-description">
|
|
||||||
<b>{{ name }}</b>
|
|
||||||
</div>
|
|
||||||
<div class="host-description">{{ hostname }}:{{ port }}</div>
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
v-if="deletable"
|
|
||||||
href="#"
|
|
||||||
class="uk-icon uk-width-auto host-delete"
|
|
||||||
@click="$emit('delete')"
|
|
||||||
><i class="material-icons">delete</i></a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="uk-card-footer uk-padding-small">
|
|
||||||
<button
|
|
||||||
class="uk-button uk-button-default uk-form-small uk-float-right uk-margin uk-margin-remove-top uk-width-1-1"
|
|
||||||
@click="$emit('connect')"
|
|
||||||
>
|
|
||||||
Connect
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
// Export main app
|
|
||||||
export default {
|
|
||||||
name: "HostCard",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
hostname: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
port: {
|
|
||||||
type: Number,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
deletable: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data: function() {
|
|
||||||
return {
|
|
||||||
snapshotRule: "/api/v2/streams/snapshot",
|
|
||||||
snapshotSrc: "",
|
|
||||||
snapshotAvailable: false,
|
|
||||||
polling: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
routesURL: function() {
|
|
||||||
return `http://${this.hostname}:${this.port}/routes`;
|
|
||||||
},
|
|
||||||
snapshotURL: function() {
|
|
||||||
return `http://${this.hostname}:${this.port}${this.snapshotRule}`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
// Try to load the microscope snapshot when mounted
|
|
||||||
this.checkSnapshotAvailable();
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
// Clear the polling timer
|
|
||||||
if (this.polling !== null) {
|
|
||||||
clearInterval(this.polling);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
checkSnapshotAvailable: function() {
|
|
||||||
// Fetches the list of routes from the host, and starts
|
|
||||||
// a snapshot timer if the snapshot route is available
|
|
||||||
|
|
||||||
// Send tag request
|
|
||||||
axios
|
|
||||||
.get(this.routesURL)
|
|
||||||
.then(response => {
|
|
||||||
// If the host has a snapshot route available
|
|
||||||
if (this.snapshotRule in response.data) {
|
|
||||||
// Tell the view that a snapshot is available
|
|
||||||
this.snapshotAvailable = true;
|
|
||||||
// Update the snapshot URL
|
|
||||||
this.updateSnapshotURL();
|
|
||||||
// If not already polling, start polling
|
|
||||||
if (this.polling === null) {
|
|
||||||
this.updateSnapshotPoll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// Tell the view that a snapshot is not available, and ignore
|
|
||||||
this.snapshotAvailable = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
updateSnapshotURL: function() {
|
|
||||||
// Set the snapshot image src to the snapshot URL,
|
|
||||||
// adding a timestamp argument to act as a cache-breaker
|
|
||||||
this.snapshotSrc = `${this.snapshotURL}?${Date.now()}`;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateSnapshotPoll: function() {
|
|
||||||
// Start a timer to call updateSnapshotURL periodically
|
|
||||||
this.polling = setInterval(() => {
|
|
||||||
this.updateSnapshotURL();
|
|
||||||
}, 15000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.host-description {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.snapshot-container {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
img[data-src][src*="data:image"] {
|
|
||||||
background: rgba(127, 127, 127, 0.1);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,409 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="galleryDisplay uk-padding uk-padding-remove-top">
|
|
||||||
<!-- Gallery nav bar -->
|
|
||||||
<nav
|
|
||||||
class="gallery-navbar uk-navbar-container uk-navbar-transparent"
|
|
||||||
uk-navbar="mode: click"
|
|
||||||
>
|
|
||||||
<!-- Left side controls -->
|
|
||||||
<div
|
|
||||||
class="uk-navbar-left uk-padding-remove-top uk-padding-remove-bottom"
|
|
||||||
>
|
|
||||||
<ul class="uk-navbar-nav">
|
|
||||||
<li :class="[sortDescending ? 'uk-active' : '']">
|
|
||||||
<a class="uk-icon" href="#" @click="sortDescending = true">
|
|
||||||
<i class="material-icons">keyboard_arrow_down</i>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li :class="[!sortDescending ? 'uk-active' : '']">
|
|
||||||
<a class="uk-icon" href="#" @click="sortDescending = false">
|
|
||||||
<i class="material-icons">keyboard_arrow_up</i>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#">Filter</a>
|
|
||||||
<div
|
|
||||||
class="uk-navbar-dropdown"
|
|
||||||
:class="{
|
|
||||||
'uk-light uk-background-secondary':
|
|
||||||
$store.state.globalSettings.darkMode
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<ul class="uk-nav uk-navbar-dropdown-nav">
|
|
||||||
<form class="uk-form-stacked">
|
|
||||||
<div
|
|
||||||
v-for="tag in allTags"
|
|
||||||
:key="tag"
|
|
||||||
class="uk-margin-small"
|
|
||||||
>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
:id="tag"
|
|
||||||
v-model="checkedTags"
|
|
||||||
class="uk-checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
:value="tag"
|
|
||||||
checked
|
|
||||||
/>
|
|
||||||
{{ tag }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Right side buttons -->
|
|
||||||
<div class="uk-navbar-right">
|
|
||||||
<div class="uk-grid">
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="uk-button uk-button-default uk-width-1-1"
|
|
||||||
@click="updateCaptures()"
|
|
||||||
>
|
|
||||||
Refresh Captures
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<ZipDownloader :capture-ids="Object.keys(filteredCaptures)" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- Gallery -->
|
|
||||||
<div
|
|
||||||
v-if="$store.getters.ready"
|
|
||||||
class="uk-padding-remove-top"
|
|
||||||
uk-lightbox="toggle: .lightbox-link"
|
|
||||||
>
|
|
||||||
<!-- Folder heading -->
|
|
||||||
<div
|
|
||||||
v-if="galleryFolder"
|
|
||||||
class="gallery-folder-heading uk-flex uk-flex-middle"
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
class="uk-icon uk-margin-remove"
|
|
||||||
@click="galleryFolder = ''"
|
|
||||||
>
|
|
||||||
<i class="material-icons">arrow_back</i>
|
|
||||||
</a>
|
|
||||||
<div class="uk-margin-left">
|
|
||||||
<h3 class="uk-margin-remove uk-margin-left">
|
|
||||||
<b>SCAN</b>
|
|
||||||
{{ allScans[galleryFolder].metadata.image.name }}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Gallery capture cards -->
|
|
||||||
<div class="gallery-grid">
|
|
||||||
<div v-for="item in sortedItems" :key="item.metadata.id">
|
|
||||||
<scanCard
|
|
||||||
v-if="'isScan' in item"
|
|
||||||
:metadata="item.metadata"
|
|
||||||
:thumbnail="item.thumbnail"
|
|
||||||
:captures="item.captures"
|
|
||||||
/>
|
|
||||||
<captureCard v-else :capture-state="item" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import axios from "axios";
|
|
||||||
import captureCard from "./galleryComponents/captureCard.vue";
|
|
||||||
import scanCard from "./galleryComponents/scanCard.vue";
|
|
||||||
|
|
||||||
import ZipDownloader from "./galleryComponents/zipDownloader";
|
|
||||||
|
|
||||||
// Export main app
|
|
||||||
export default {
|
|
||||||
name: "GalleryDisplay",
|
|
||||||
|
|
||||||
components: {
|
|
||||||
captureCard,
|
|
||||||
scanCard,
|
|
||||||
ZipDownloader
|
|
||||||
},
|
|
||||||
|
|
||||||
data: function() {
|
|
||||||
return {
|
|
||||||
captures: [],
|
|
||||||
checkedTags: [],
|
|
||||||
sortDescending: true,
|
|
||||||
galleryFolder: "",
|
|
||||||
scanTag: "scan",
|
|
||||||
unwatchStoreFunction: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
capturesUri: function() {
|
|
||||||
return `${this.$store.getters.baseUri}/api/v2/captures`;
|
|
||||||
},
|
|
||||||
allTags: function() {
|
|
||||||
// Return an array of unique tags across all captures
|
|
||||||
var tags = [];
|
|
||||||
for (var capture of this.captures) {
|
|
||||||
for (var tag of capture.metadata.image.tags) {
|
|
||||||
if (!tags.includes(tag)) {
|
|
||||||
tags.push(tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tags.sort();
|
|
||||||
},
|
|
||||||
|
|
||||||
noScanCaptures: function() {
|
|
||||||
// List of captures that are not part of a scan
|
|
||||||
var captures = [];
|
|
||||||
for (var capture of this.captures) {
|
|
||||||
// Add to capture list if matched
|
|
||||||
if (!capture.metadata.dataset) {
|
|
||||||
captures.push(capture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return captures;
|
|
||||||
},
|
|
||||||
|
|
||||||
allScans: function() {
|
|
||||||
// List of scans as capture-like objects
|
|
||||||
var scans = {};
|
|
||||||
|
|
||||||
for (var capture of this.captures) {
|
|
||||||
var annotations = capture.metadata.image.annotations;
|
|
||||||
var tags = capture.metadata.image.tags;
|
|
||||||
var dataset = capture.metadata.dataset;
|
|
||||||
|
|
||||||
if (dataset) {
|
|
||||||
var id = dataset["id"];
|
|
||||||
|
|
||||||
// If this scan ID hasn't been seen before
|
|
||||||
if (!(id in scans)) {
|
|
||||||
scans[id] = {};
|
|
||||||
scans[id].isScan = true;
|
|
||||||
scans[id].captures = [];
|
|
||||||
scans[id].metadata = {
|
|
||||||
image: {
|
|
||||||
name: dataset.name,
|
|
||||||
acquisitionDate: dataset.acquisitionDate,
|
|
||||||
id: dataset.id,
|
|
||||||
tags: [],
|
|
||||||
annotations: {}
|
|
||||||
},
|
|
||||||
type: dataset.type
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the capture object to the scan
|
|
||||||
scans[id].captures.push(capture);
|
|
||||||
|
|
||||||
// Add missing scan metadata, prioritising first capture
|
|
||||||
for (var key of Object.keys(annotations)) {
|
|
||||||
if (!(key in scans[id].metadata.image.annotations)) {
|
|
||||||
scans[id].metadata.image.annotations[key] = annotations[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append missing tags
|
|
||||||
for (var tag of tags) {
|
|
||||||
if (!scans[id].metadata.image.tags.includes(tag)) {
|
|
||||||
scans[id].metadata.image.tags.push(tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a preview thumbnail
|
|
||||||
if (!("thumbnail" in scans[id])) {
|
|
||||||
scans[
|
|
||||||
id
|
|
||||||
].thumbnail = `${capture.links.download.href}?thumbnail=true`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return scans;
|
|
||||||
},
|
|
||||||
|
|
||||||
scanList: function() {
|
|
||||||
// List of scans, obtained from this.allScans values
|
|
||||||
return Object.values(this.allScans);
|
|
||||||
},
|
|
||||||
|
|
||||||
itemList: function() {
|
|
||||||
// Get list of current items to show
|
|
||||||
// If galleryFolder (ie inside a scan folder), show scan captures
|
|
||||||
// Otherwise, show root captures and scan cards
|
|
||||||
if (this.galleryFolder) {
|
|
||||||
console.log(this.allScans[this.galleryFolder].captures);
|
|
||||||
return this.allScans[this.galleryFolder].captures;
|
|
||||||
} else {
|
|
||||||
return this.noScanCaptures.concat(this.scanList);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
filteredItems: function() {
|
|
||||||
// Filter itemList by checkedTags
|
|
||||||
return this.filterCaptures(this.itemList, this.checkedTags);
|
|
||||||
},
|
|
||||||
|
|
||||||
filteredCaptures: function() {
|
|
||||||
var captures = {};
|
|
||||||
|
|
||||||
for (var item of this.filteredItems) {
|
|
||||||
// If it's a dataset
|
|
||||||
if ("captures" in item) {
|
|
||||||
for (var capture of item.captures) {
|
|
||||||
// Get the ID of each capture in the set
|
|
||||||
captures[capture.metadata.image.id] = capture;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If it's a single capture, get the ID of the capture
|
|
||||||
captures[item.metadata.image.id] = item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return captures;
|
|
||||||
},
|
|
||||||
|
|
||||||
sortedItems: function() {
|
|
||||||
// Sort filteredItems using sortCaptures function
|
|
||||||
return this.sortCaptures(this.filteredItems);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
// Update on mount (does nothing if not connected)
|
|
||||||
this.updateCaptures();
|
|
||||||
// A global signal listener to perform a gallery refresh
|
|
||||||
this.$root.$on("globalUpdateCaptures", () => {
|
|
||||||
this.updateCaptures();
|
|
||||||
});
|
|
||||||
// A global signal listener to set the gallery folder
|
|
||||||
this.$root.$on("globalUpdateCaptureFolder", folder => {
|
|
||||||
this.galleryFolder = folder;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
created: function() {
|
|
||||||
// Watch for host 'ready', then update status
|
|
||||||
this.unwatchStoreFunction = this.$store.watch(
|
|
||||||
(state, getters) => {
|
|
||||||
return getters.ready;
|
|
||||||
},
|
|
||||||
ready => {
|
|
||||||
if (ready) {
|
|
||||||
// If the connection is now ready, update capture list
|
|
||||||
this.updateCaptures();
|
|
||||||
} else {
|
|
||||||
// If the connection is now disconnected, empty capture list
|
|
||||||
this.captures = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
// Remove global signal listener to perform a gallery refresh
|
|
||||||
this.$root.$off("globalUpdateCaptures");
|
|
||||||
// Remove global signal listener to set the gallery folder
|
|
||||||
this.$root.$off("globalUpdateCaptureFolder");
|
|
||||||
// Then we call that function here to unwatch
|
|
||||||
if (this.unwatchStoreFunction) {
|
|
||||||
this.unwatchStoreFunction();
|
|
||||||
this.unwatchStoreFunction = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
updateCaptures: function() {
|
|
||||||
if (this.$store.state.available) {
|
|
||||||
console.log("Updating capture list...");
|
|
||||||
axios
|
|
||||||
.get(this.capturesUri)
|
|
||||||
.then(response => {
|
|
||||||
this.captures = response.data;
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
this.modalError(error); // Let mixin handle error
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.log("Delaying capture update until connection is available");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
filterCaptures: function(list, filterTags) {
|
|
||||||
// Filter a list of captures by an array of tags
|
|
||||||
var result = [];
|
|
||||||
for (var capture of list) {
|
|
||||||
// Assume exclusion
|
|
||||||
var includeCapture = false;
|
|
||||||
|
|
||||||
// Filter by selected tags
|
|
||||||
var tags = capture.metadata.image.tags;
|
|
||||||
let checker = (arr, target) => target.every(v => arr.includes(v));
|
|
||||||
// True if all tags match
|
|
||||||
includeCapture = checker(tags, filterTags);
|
|
||||||
|
|
||||||
// Add to capture list if matched
|
|
||||||
if (includeCapture == true) {
|
|
||||||
result.push(capture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
sortCaptures: function(list) {
|
|
||||||
// Sort a list of captures by metadata time
|
|
||||||
function compare(a, b) {
|
|
||||||
if (a.metadata.image.acquisitionDate < b.metadata.image.acquisitionDate)
|
|
||||||
return -1;
|
|
||||||
if (a.metadata.image.acquisitionDate > b.metadata.image.acquisitionDate)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.sortDescending == true) {
|
|
||||||
return list.sort(compare).reverse();
|
|
||||||
} else {
|
|
||||||
return list.sort(compare);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="less">
|
|
||||||
.gallery-navbar {
|
|
||||||
border-width: 0 0 1px 0;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: rgba(180, 180, 180, 0.25);
|
|
||||||
}
|
|
||||||
.gallery-navbar,
|
|
||||||
.gallery-folder-heading {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
.gallery-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, 320px);
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.gallery-grid > div {
|
|
||||||
display: inline-block;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/deep/ .capture-card {
|
|
||||||
width: 300px;
|
|
||||||
height: 100%; // Used to have all cards in a row match their heights
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,224 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- Grid managing tab content -->
|
|
||||||
<div uk-grid class="uk-height-1-1 uk-margin-remove uk-padding-remove">
|
|
||||||
<div class="settings-nav">
|
|
||||||
<ul class="uk-nav uk-nav-default">
|
|
||||||
<li class="uk-nav-header">Application settings</li>
|
|
||||||
<li>
|
|
||||||
<tabIcon
|
|
||||||
id="settings-display-icon"
|
|
||||||
tab-i-d="display"
|
|
||||||
:show-title="false"
|
|
||||||
:show-tooltip="false"
|
|
||||||
:require-connection="false"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
@set-tab="setTab"
|
|
||||||
>
|
|
||||||
Display
|
|
||||||
</tabIcon>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<tabIcon
|
|
||||||
id="settings-features-icon"
|
|
||||||
tab-i-d="features"
|
|
||||||
:show-title="false"
|
|
||||||
:show-tooltip="false"
|
|
||||||
:require-connection="false"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
@set-tab="setTab"
|
|
||||||
>
|
|
||||||
Features
|
|
||||||
</tabIcon>
|
|
||||||
</li>
|
|
||||||
<li class="uk-nav-header">Microscope settings</li>
|
|
||||||
<li>
|
|
||||||
<tabIcon
|
|
||||||
id="settings-camera-icon"
|
|
||||||
tab-i-d="camera"
|
|
||||||
:show-title="false"
|
|
||||||
:show-tooltip="false"
|
|
||||||
:require-connection="true"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
@set-tab="setTab"
|
|
||||||
>
|
|
||||||
Camera
|
|
||||||
</tabIcon>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<tabIcon
|
|
||||||
id="settings-stage-icon"
|
|
||||||
tab-i-d="stage"
|
|
||||||
:show-title="false"
|
|
||||||
:show-tooltip="false"
|
|
||||||
:require-connection="true"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
@set-tab="setTab"
|
|
||||||
>
|
|
||||||
Stage
|
|
||||||
</tabIcon>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<tabIcon
|
|
||||||
id="settings-mapping-icon"
|
|
||||||
tab-i-d="mapping"
|
|
||||||
:show-title="false"
|
|
||||||
:show-tooltip="false"
|
|
||||||
:require-connection="true"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
@set-tab="setTab"
|
|
||||||
>
|
|
||||||
Camera/stage mapping
|
|
||||||
</tabIcon>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<tabIcon
|
|
||||||
id="settings-microscope-icon"
|
|
||||||
tab-i-d="microscope"
|
|
||||||
:show-title="false"
|
|
||||||
:show-tooltip="false"
|
|
||||||
:require-connection="true"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
@set-tab="setTab"
|
|
||||||
>
|
|
||||||
General
|
|
||||||
</tabIcon>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="view-component uk-width-expand uk-padding-small">
|
|
||||||
<tabContent
|
|
||||||
tab-i-d="display"
|
|
||||||
:require-connection="false"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
>
|
|
||||||
<div class="settings-pane uk-padding-small">
|
|
||||||
<appSettings />
|
|
||||||
<streamSettings />
|
|
||||||
</div>
|
|
||||||
</tabContent>
|
|
||||||
|
|
||||||
<tabContent
|
|
||||||
tab-i-d="features"
|
|
||||||
:require-connection="false"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
>
|
|
||||||
<div class="settings-pane uk-padding-small">
|
|
||||||
<featuresSettings />
|
|
||||||
</div>
|
|
||||||
</tabContent>
|
|
||||||
|
|
||||||
<tabContent
|
|
||||||
tab-i-d="camera"
|
|
||||||
:require-connection="true"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
>
|
|
||||||
<div class="settings-pane uk-padding-small">
|
|
||||||
<cameraSettings />
|
|
||||||
</div>
|
|
||||||
</tabContent>
|
|
||||||
|
|
||||||
<tabContent
|
|
||||||
tab-i-d="stage"
|
|
||||||
:require-connection="true"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
>
|
|
||||||
<div class="settings-pane uk-padding-small">
|
|
||||||
<stageSettings />
|
|
||||||
</div>
|
|
||||||
</tabContent>
|
|
||||||
|
|
||||||
<tabContent
|
|
||||||
tab-i-d="mapping"
|
|
||||||
:require-connection="true"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
>
|
|
||||||
<div class="settings-pane uk-padding-small">
|
|
||||||
<h3>Camera/stage mapping</h3>
|
|
||||||
<p>
|
|
||||||
Camera/stage mapping allows the stage to move relative to the camera
|
|
||||||
view. This enables functions like click-to-move, and more precise
|
|
||||||
tile scans.
|
|
||||||
</p>
|
|
||||||
<CSMSettings />
|
|
||||||
</div>
|
|
||||||
</tabContent>
|
|
||||||
|
|
||||||
<tabContent
|
|
||||||
tab-i-d="microscope"
|
|
||||||
:require-connection="true"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
>
|
|
||||||
<div class="settings-pane uk-padding-small">
|
|
||||||
<h3>Microscope settings</h3>
|
|
||||||
<microscopeSettings />
|
|
||||||
</div>
|
|
||||||
</tabContent>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import streamSettings from "./settingsComponents/streamSettings.vue";
|
|
||||||
import microscopeSettings from "./settingsComponents/microscopeSettings.vue";
|
|
||||||
import cameraSettings from "./settingsComponents/cameraSettings.vue";
|
|
||||||
import appSettings from "./settingsComponents/appSettings.vue";
|
|
||||||
import featuresSettings from "./settingsComponents/featuresSettings.vue";
|
|
||||||
import CSMSettings from "./settingsComponents/CSMSettings.vue";
|
|
||||||
import stageSettings from "./settingsComponents/stageSettings.vue";
|
|
||||||
// Import generic components
|
|
||||||
import tabIcon from "../genericComponents/tabIcon";
|
|
||||||
import tabContent from "../genericComponents/tabContent";
|
|
||||||
|
|
||||||
// Export main app
|
|
||||||
export default {
|
|
||||||
name: "SettingsDisplay",
|
|
||||||
|
|
||||||
components: {
|
|
||||||
streamSettings,
|
|
||||||
cameraSettings,
|
|
||||||
stageSettings,
|
|
||||||
microscopeSettings,
|
|
||||||
CSMSettings,
|
|
||||||
appSettings,
|
|
||||||
featuresSettings,
|
|
||||||
tabIcon,
|
|
||||||
tabContent
|
|
||||||
},
|
|
||||||
|
|
||||||
data: function() {
|
|
||||||
return {
|
|
||||||
selected: "display",
|
|
||||||
currentTab: "display"
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
setTab: function(event, tab) {
|
|
||||||
if (!(this.currentTab == tab)) {
|
|
||||||
this.currentTab = tab;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
// Custom UIkit CSS modifications
|
|
||||||
@import "../../assets/less/theme.less";
|
|
||||||
|
|
||||||
.settings-nav {
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
width: 250px;
|
|
||||||
padding: 10px;
|
|
||||||
background-color: rgba(180, 180, 180, 0.03);
|
|
||||||
border-width: 0 1px 0 0;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: rgba(180, 180, 180, 0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-nav li > a {
|
|
||||||
padding-left: 6px !important;
|
|
||||||
border-radius: @button-border-radius;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -484,6 +484,7 @@ class PiCameraStreamer(BaseCamera):
|
||||||
use_video_port: bool = False,
|
use_video_port: bool = False,
|
||||||
resize: Tuple[int, int] = None,
|
resize: Tuple[int, int] = None,
|
||||||
bayer: bool = True,
|
bayer: bool = True,
|
||||||
|
thumbnail: tuple = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Capture a still image to a StreamObject.
|
Capture a still image to a StreamObject.
|
||||||
|
|
@ -515,6 +516,7 @@ class PiCameraStreamer(BaseCamera):
|
||||||
resize=resize,
|
resize=resize,
|
||||||
bayer=(not use_video_port) and bayer,
|
bayer=(not use_video_port) and bayer,
|
||||||
use_video_port=use_video_port,
|
use_video_port=use_video_port,
|
||||||
|
thumbnail=thumbnail,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set resolution and start stream recording if necessary
|
# Set resolution and start stream recording if necessary
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,12 @@ import uuid
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
from openflexure_microscope.camera import piexif
|
from openflexure_microscope.camera import piexif
|
||||||
from openflexure_microscope.camera.piexif._exceptions import InvalidImageDataError
|
from openflexure_microscope.camera.piexif._exceptions import InvalidImageDataError
|
||||||
from openflexure_microscope.config import JSONEncoder
|
from openflexure_microscope.config import JSONEncoder
|
||||||
|
|
||||||
PIL_FORMATS = ["JPG", "JPEG", "PNG", "TIF", "TIFF"]
|
|
||||||
EXIF_FORMATS = ["JPG", "JPEG", "TIF", "TIFF"]
|
EXIF_FORMATS = ["JPG", "JPEG", "TIF", "TIFF"]
|
||||||
THUMBNAIL_SIZE = (200, 150)
|
|
||||||
|
|
||||||
|
|
||||||
def pull_usercomment_dict(filepath):
|
def pull_usercomment_dict(filepath):
|
||||||
|
|
@ -145,9 +142,6 @@ class CaptureObject(object):
|
||||||
# List for storing tags
|
# List for storing tags
|
||||||
self.tags = []
|
self.tags = []
|
||||||
|
|
||||||
# Thumbnail (populated only for PIL captures)
|
|
||||||
self.thumb_bytes = None
|
|
||||||
|
|
||||||
def write(self, s):
|
def write(self, s):
|
||||||
logging.debug("Writing to %s", self)
|
logging.debug("Writing to %s", self)
|
||||||
self.stream.write(s)
|
self.stream.write(s)
|
||||||
|
|
@ -349,21 +343,12 @@ class CaptureObject(object):
|
||||||
"""
|
"""
|
||||||
Returns a thumbnail of the capture data, for supported image formats.
|
Returns a thumbnail of the capture data, for supported image formats.
|
||||||
"""
|
"""
|
||||||
# If no thumbnail exists, try and make one
|
exif_dict = piexif.load(self.file)
|
||||||
if not self.thumb_bytes:
|
thumbnail = exif_dict.pop("thumbnail")
|
||||||
logging.info("Building thumbnail")
|
if thumbnail:
|
||||||
self.thumb_bytes = io.BytesIO()
|
return io.BytesIO(thumbnail)
|
||||||
if self.format.upper() in PIL_FORMATS:
|
# If no thumbnail exists, serve the full image
|
||||||
im = Image.open(self.data)
|
return self.data.getvalue()
|
||||||
im.thumbnail(THUMBNAIL_SIZE)
|
|
||||||
im.save(self.thumb_bytes, self.format)
|
|
||||||
self.thumb_bytes.seek(0)
|
|
||||||
else:
|
|
||||||
self.thumb_bytes.seek(0)
|
|
||||||
|
|
||||||
# Copy the buffer, to avoid closing the file
|
|
||||||
data = io.BytesIO(self.thumb_bytes.getbuffer())
|
|
||||||
return data
|
|
||||||
|
|
||||||
def save(self) -> None:
|
def save(self) -> None:
|
||||||
"""Write stream to file, and save/update metadata file"""
|
"""Write stream to file, and save/update metadata file"""
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,9 @@ class Microscope:
|
||||||
try:
|
try:
|
||||||
logging.info("Trying SangaStage")
|
logging.info("Trying SangaStage")
|
||||||
self.stage = SangaStage(port=stage_port)
|
self.stage = SangaStage(port=stage_port)
|
||||||
|
logging.info("Saving new SangaStage type configuration")
|
||||||
|
configuration["stage"]["type"] = stage_type
|
||||||
|
self.configuration_file.save(configuration)
|
||||||
except Exception as e: # pylint: disable=W0703
|
except Exception as e: # pylint: disable=W0703
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
logging.warning("No compatible Sangaboard hardware found.")
|
logging.warning("No compatible Sangaboard hardware found.")
|
||||||
|
|
@ -151,17 +154,15 @@ class Microscope:
|
||||||
try:
|
try:
|
||||||
logging.info("Trying SangaDeltaStage")
|
logging.info("Trying SangaDeltaStage")
|
||||||
self.stage = SangaDeltaStage(port=stage_port)
|
self.stage = SangaDeltaStage(port=stage_port)
|
||||||
|
logging.info("Saving new SangaDeltaStage type configuration")
|
||||||
|
configuration["stage"]["type"] = stage_type
|
||||||
|
self.configuration_file.save(configuration)
|
||||||
except Exception as e: # pylint: disable=W0703
|
except Exception as e: # pylint: disable=W0703
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
logging.warning("No compatible Sangaboard hardware found.")
|
logging.warning("No compatible Sangaboard hardware found.")
|
||||||
else:
|
else:
|
||||||
logging.warning("The stage type is incorrectly defined.")
|
logging.warning("The stage type is incorrectly defined.")
|
||||||
|
|
||||||
logging.info("Saving new stage type configuration")
|
|
||||||
configuration["stage"]["type"] = stage_type
|
|
||||||
self.configuration_file.save(configuration)
|
|
||||||
|
|
||||||
def has_real_stage(self) -> bool:
|
def has_real_stage(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if a real (non-mock) stage is currently attached.
|
Check if a real (non-mock) stage is currently attached.
|
||||||
|
|
@ -384,6 +385,9 @@ class Microscope:
|
||||||
)
|
)
|
||||||
|
|
||||||
# Capture to output object
|
# Capture to output object
|
||||||
|
extras = {}
|
||||||
|
if fmt == "jpeg":
|
||||||
|
extras["thumbnail"] = (200, 150, 85)
|
||||||
logging.info("Starting microscope capture %s", output.file)
|
logging.info("Starting microscope capture %s", output.file)
|
||||||
self.camera.capture(
|
self.camera.capture(
|
||||||
output,
|
output,
|
||||||
|
|
@ -391,6 +395,7 @@ class Microscope:
|
||||||
resize=resize,
|
resize=resize,
|
||||||
bayer=bayer,
|
bayer=bayer,
|
||||||
fmt=fmt,
|
fmt=fmt,
|
||||||
|
**extras,
|
||||||
)
|
)
|
||||||
|
|
||||||
output.put_and_save(tags, annotations, full_metadata)
|
output.put_and_save(tags, annotations, full_metadata)
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ pylint = "^2.3"
|
||||||
black = {version = "^18.3-alpha.0",allow-prereleases = true}
|
black = {version = "^18.3-alpha.0",allow-prereleases = true}
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
exclude = '(\.eggs|\.git|\.venv)'
|
exclude = '(\.eggs|\.git|\.venv|node_modules/)'
|
||||||
|
|
||||||
[tool.isort]
|
[tool.isort]
|
||||||
multi_line_output = 3
|
multi_line_output = 3
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue