Merge branch 'imjoy-support' into 'master'
Added imjoy support Closes #157 See merge request openflexure/openflexure-microscope-server!120
This commit is contained in:
commit
5b63745ad0
18 changed files with 1656 additions and 95 deletions
|
|
@ -1,3 +1,7 @@
|
||||||
|
# Unreleased
|
||||||
|
* Added support for ImJoy
|
||||||
|
* Added a setting to disable the gallery
|
||||||
|
|
||||||
# [v2.10.0b0](https://gitlab.com/openflexure/openflexure-microscope-server/compare/v2.9.3...v2.10.0b0) (2021-05-18)
|
# [v2.10.0b0](https://gitlab.com/openflexure/openflexure-microscope-server/compare/v2.9.3...v2.10.0b0) (2021-05-18)
|
||||||
|
|
||||||
## New features
|
## New features
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
|
VUE_APP_MODE=development
|
||||||
VUE_APP_PLATFORM=web
|
VUE_APP_PLATFORM=web
|
||||||
VUE_APP_TARGET=web
|
VUE_APP_TARGET=web
|
||||||
|
VUE_APP_ENABLE_IMJOY=true
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
|
VUE_APP_MODE=production
|
||||||
VUE_APP_PLATFORM=web
|
VUE_APP_PLATFORM=web
|
||||||
VUE_APP_TARGET=web
|
VUE_APP_TARGET=web
|
||||||
|
VUE_APP_ENABLE_IMJOY=true
|
||||||
|
|
|
||||||
402
openflexure_microscope/api/static/package-lock.json
generated
402
openflexure_microscope/api/static/package-lock.json
generated
|
|
@ -2276,6 +2276,11 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/component-emitter": {
|
||||||
|
"version": "1.2.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz",
|
||||||
|
"integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg=="
|
||||||
|
},
|
||||||
"@types/connect": {
|
"@types/connect": {
|
||||||
"version": "3.4.33",
|
"version": "3.4.33",
|
||||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
|
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
|
||||||
|
|
@ -3578,6 +3583,11 @@
|
||||||
"integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==",
|
"integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"after": {
|
||||||
|
"version": "0.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
|
||||||
|
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
|
||||||
|
},
|
||||||
"aggregate-error": {
|
"aggregate-error": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
||||||
|
|
@ -3592,7 +3602,6 @@
|
||||||
"version": "6.10.0",
|
"version": "6.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
|
||||||
"integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
|
"integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"fast-deep-equal": "^2.0.1",
|
"fast-deep-equal": "^2.0.1",
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
|
|
@ -3705,11 +3714,15 @@
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"sprintf-js": "~1.0.2"
|
"sprintf-js": "~1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"argsarray": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/argsarray/-/argsarray-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-bnIHtOzbObCviDA/pa4ivajfYcs="
|
||||||
|
},
|
||||||
"arr-diff": {
|
"arr-diff": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
|
||||||
|
|
@ -3755,6 +3768,11 @@
|
||||||
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
|
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"arraybuffer.slice": {
|
||||||
|
"version": "0.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
|
||||||
|
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
|
||||||
|
},
|
||||||
"asn1": {
|
"asn1": {
|
||||||
"version": "0.2.4",
|
"version": "0.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
||||||
|
|
@ -4047,6 +4065,11 @@
|
||||||
"resolve": "^1.4.0"
|
"resolve": "^1.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"backo2": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
|
||||||
|
},
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||||
|
|
@ -4108,6 +4131,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"base64-arraybuffer": {
|
||||||
|
"version": "0.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
|
||||||
|
"integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
|
||||||
|
},
|
||||||
"base64-js": {
|
"base64-js": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||||
|
|
@ -4163,6 +4191,11 @@
|
||||||
"file-uri-to-path": "1.0.0"
|
"file-uri-to-path": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"blob": {
|
||||||
|
"version": "0.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
|
||||||
|
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
|
||||||
|
},
|
||||||
"bluebird": {
|
"bluebird": {
|
||||||
"version": "3.7.2",
|
"version": "3.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||||
|
|
@ -5076,11 +5109,20 @@
|
||||||
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"component-bind": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
|
||||||
|
},
|
||||||
"component-emitter": {
|
"component-emitter": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
|
||||||
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
|
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
|
||||||
"dev": true
|
},
|
||||||
|
"component-inherit": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
|
||||||
|
"integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
|
||||||
},
|
},
|
||||||
"compressible": {
|
"compressible": {
|
||||||
"version": "2.0.18",
|
"version": "2.0.18",
|
||||||
|
|
@ -6091,6 +6133,11 @@
|
||||||
"domelementtype": "1"
|
"domelementtype": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dompurify": {
|
||||||
|
"version": "2.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.8.tgz",
|
||||||
|
"integrity": "sha512-9H0UL59EkDLgY3dUFjLV6IEUaHm5qp3mxSqWw7Yyx4Zhk2Jn2cmLe+CNPP3xy13zl8Bqg+0NehQzkdMoVhGRww=="
|
||||||
|
},
|
||||||
"domutils": {
|
"domutils": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
|
||||||
|
|
@ -6224,6 +6271,50 @@
|
||||||
"once": "^1.4.0"
|
"once": "^1.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"engine.io-client": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-jPFpw2HLL0lhZ2KY0BpZhIJdleQcUO9W1xkIpo0h3d6s+5D6+EV/xgQw9qWOmymszv2WXef/6KUUehyxEKomlQ==",
|
||||||
|
"requires": {
|
||||||
|
"base64-arraybuffer": "0.1.4",
|
||||||
|
"component-emitter": "~1.3.0",
|
||||||
|
"debug": "~4.3.1",
|
||||||
|
"engine.io-parser": "~4.0.1",
|
||||||
|
"has-cors": "1.1.0",
|
||||||
|
"parseqs": "0.0.6",
|
||||||
|
"parseuri": "0.0.6",
|
||||||
|
"ws": "~7.4.2",
|
||||||
|
"yeast": "0.1.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "4.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||||
|
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
},
|
||||||
|
"ws": {
|
||||||
|
"version": "7.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
|
||||||
|
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"engine.io-parser": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==",
|
||||||
|
"requires": {
|
||||||
|
"base64-arraybuffer": "0.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"enhanced-resolve": {
|
"enhanced-resolve": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz",
|
||||||
|
|
@ -6660,8 +6751,7 @@
|
||||||
"esprima": {
|
"esprima": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"esquery": {
|
"esquery": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
|
|
@ -6974,8 +7064,7 @@
|
||||||
"fast-deep-equal": {
|
"fast-deep-equal": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
|
||||||
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
|
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"fast-diff": {
|
"fast-diff": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
|
|
@ -7000,8 +7089,7 @@
|
||||||
"fast-json-stable-stringify": {
|
"fast-json-stable-stringify": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
|
||||||
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
|
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"fast-levenshtein": {
|
"fast-levenshtein": {
|
||||||
"version": "2.0.6",
|
"version": "2.0.6",
|
||||||
|
|
@ -7089,6 +7177,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"file-saver": {
|
||||||
|
"version": "1.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz",
|
||||||
|
"integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg=="
|
||||||
|
},
|
||||||
"file-uri-to-path": {
|
"file-uri-to-path": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||||
|
|
@ -7236,8 +7329,7 @@
|
||||||
"follow-redirects": {
|
"follow-redirects": {
|
||||||
"version": "1.13.0",
|
"version": "1.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
|
||||||
"integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==",
|
"integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"for-in": {
|
"for-in": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
|
@ -8087,6 +8179,26 @@
|
||||||
"ansi-regex": "^2.0.0"
|
"ansi-regex": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"has-binary2": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
|
||||||
|
"requires": {
|
||||||
|
"isarray": "2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"isarray": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
||||||
|
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"has-cors": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
|
||||||
|
},
|
||||||
"has-flag": {
|
"has-flag": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
|
|
@ -8483,6 +8595,77 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"imjoy-core": {
|
||||||
|
"version": "0.13.74",
|
||||||
|
"resolved": "https://registry.npmjs.org/imjoy-core/-/imjoy-core-0.13.74.tgz",
|
||||||
|
"integrity": "sha512-eXKRw4vpK8E8DnKrc1G8dLq8zGQsOddLubVpomIzs5CtOktAhQOqr5kOEucmM4V3uEDd24UGbMydv1MzP2KWBQ==",
|
||||||
|
"requires": {
|
||||||
|
"ajv": "^6.9.1",
|
||||||
|
"axios": "^0.21.1",
|
||||||
|
"dompurify": "^2.0.8",
|
||||||
|
"file-saver": "^1.3.3",
|
||||||
|
"imjoy-rpc": "^0.3.6",
|
||||||
|
"js-yaml": "^3.13.1",
|
||||||
|
"lodash": "^4.17.15",
|
||||||
|
"lz-string": "^1.4.4",
|
||||||
|
"minibus": "^3.1.0",
|
||||||
|
"pouchdb-browser": "^7.0.0",
|
||||||
|
"socket.io-client": "^2.3.0",
|
||||||
|
"spark-md5": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": {
|
||||||
|
"version": "0.21.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||||
|
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.10.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"imjoy-rpc": {
|
||||||
|
"version": "0.3.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/imjoy-rpc/-/imjoy-rpc-0.3.9.tgz",
|
||||||
|
"integrity": "sha512-dWyEIFlHKEGkyTLhGvKMnY7Tus26cOgfPMj3plEfcwNIfiatD8kfwdmdu4yoWUTdOnOUY4KtY06c9UnNT04k/Q==",
|
||||||
|
"requires": {
|
||||||
|
"socket.io-client": "^4.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "4.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||||
|
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
},
|
||||||
|
"socket.io-client": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-avzRzFZIkmyNxqvhmm5ns0Itq5dgEkesDPB6Tl0Yben47U08MvdFnVXAuFDULQhDXjuYdCb6QUEILYLUKQEuGg==",
|
||||||
|
"requires": {
|
||||||
|
"@types/component-emitter": "^1.2.10",
|
||||||
|
"backo2": "~1.0.2",
|
||||||
|
"component-emitter": "~1.3.0",
|
||||||
|
"debug": "~4.3.1",
|
||||||
|
"engine.io-client": "~5.1.1",
|
||||||
|
"parseuri": "0.0.6",
|
||||||
|
"socket.io-parser": "~4.0.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"immediate": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q=="
|
||||||
|
},
|
||||||
"import-cwd": {
|
"import-cwd": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
|
||||||
|
|
@ -8539,6 +8722,11 @@
|
||||||
"integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
|
"integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"indexof": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
|
||||||
|
},
|
||||||
"infer-owner": {
|
"infer-owner": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
|
||||||
|
|
@ -8558,8 +8746,7 @@
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"inquirer": {
|
"inquirer": {
|
||||||
"version": "7.3.3",
|
"version": "7.3.3",
|
||||||
|
|
@ -9154,7 +9341,6 @@
|
||||||
"version": "3.13.1",
|
"version": "3.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
|
||||||
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
|
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"argparse": "^1.0.7",
|
"argparse": "^1.0.7",
|
||||||
"esprima": "^4.0.0"
|
"esprima": "^4.0.0"
|
||||||
|
|
@ -9193,8 +9379,7 @@
|
||||||
"json-schema-traverse": {
|
"json-schema-traverse": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"json-stable-stringify-without-jsonify": {
|
"json-stable-stringify-without-jsonify": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
|
|
@ -9446,8 +9631,7 @@
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.20",
|
"version": "4.17.20",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"lodash.defaultsdeep": {
|
"lodash.defaultsdeep": {
|
||||||
"version": "4.6.1",
|
"version": "4.6.1",
|
||||||
|
|
@ -9524,6 +9708,11 @@
|
||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"lz-string": {
|
||||||
|
"version": "1.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz",
|
||||||
|
"integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY="
|
||||||
|
},
|
||||||
"make-dir": {
|
"make-dir": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
||||||
|
|
@ -9754,6 +9943,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"minibus": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minibus/-/minibus-3.1.0.tgz",
|
||||||
|
"integrity": "sha1-/UKe2XWj/U5VbwxumqyaTqy80ZM="
|
||||||
|
},
|
||||||
"minimalistic-assert": {
|
"minimalistic-assert": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||||
|
|
@ -10607,6 +10801,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"parseqs": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
|
||||||
|
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
|
||||||
|
},
|
||||||
|
"parseuri": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
|
||||||
|
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
|
||||||
|
},
|
||||||
"parseurl": {
|
"parseurl": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
|
|
@ -11409,6 +11613,26 @@
|
||||||
"integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
|
"integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"pouchdb-browser": {
|
||||||
|
"version": "7.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/pouchdb-browser/-/pouchdb-browser-7.2.2.tgz",
|
||||||
|
"integrity": "sha512-90pkngk9/F95knUwOhGQ0xuNzG51uIecF5kD5AfNdhqqrV9wbngz+I3QA7u/9eVyZRVyOrTrT3oaKcNqAB7Brg==",
|
||||||
|
"requires": {
|
||||||
|
"argsarray": "0.0.1",
|
||||||
|
"immediate": "3.3.0",
|
||||||
|
"inherits": "2.0.4",
|
||||||
|
"spark-md5": "3.0.1",
|
||||||
|
"uuid": "8.1.0",
|
||||||
|
"vuvuzela": "1.0.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"uuid": {
|
||||||
|
"version": "8.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.1.0.tgz",
|
||||||
|
"integrity": "sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"prelude-ls": {
|
"prelude-ls": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||||
|
|
@ -11562,8 +11786,7 @@
|
||||||
"punycode": {
|
"punycode": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"q": {
|
"q": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
|
|
@ -12456,6 +12679,114 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"socket.io-client": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==",
|
||||||
|
"requires": {
|
||||||
|
"backo2": "1.0.2",
|
||||||
|
"component-bind": "1.0.0",
|
||||||
|
"component-emitter": "~1.3.0",
|
||||||
|
"debug": "~3.1.0",
|
||||||
|
"engine.io-client": "~3.5.0",
|
||||||
|
"has-binary2": "~1.0.2",
|
||||||
|
"indexof": "0.0.1",
|
||||||
|
"parseqs": "0.0.6",
|
||||||
|
"parseuri": "0.0.6",
|
||||||
|
"socket.io-parser": "~3.3.0",
|
||||||
|
"to-array": "0.1.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"engine.io-client": {
|
||||||
|
"version": "3.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.2.tgz",
|
||||||
|
"integrity": "sha512-QEqIp+gJ/kMHeUun7f5Vv3bteRHppHH/FMBQX/esFj/fuYfjyUKWGMo3VCvIP/V8bE9KcjHmRZrhIz2Z9oNsDA==",
|
||||||
|
"requires": {
|
||||||
|
"component-emitter": "~1.3.0",
|
||||||
|
"component-inherit": "0.0.3",
|
||||||
|
"debug": "~3.1.0",
|
||||||
|
"engine.io-parser": "~2.2.0",
|
||||||
|
"has-cors": "1.1.0",
|
||||||
|
"indexof": "0.0.1",
|
||||||
|
"parseqs": "0.0.6",
|
||||||
|
"parseuri": "0.0.6",
|
||||||
|
"ws": "~7.4.2",
|
||||||
|
"xmlhttprequest-ssl": "~1.6.2",
|
||||||
|
"yeast": "0.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"engine.io-parser": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==",
|
||||||
|
"requires": {
|
||||||
|
"after": "0.8.2",
|
||||||
|
"arraybuffer.slice": "~0.0.7",
|
||||||
|
"base64-arraybuffer": "0.1.4",
|
||||||
|
"blob": "0.0.5",
|
||||||
|
"has-binary2": "~1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isarray": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
||||||
|
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
},
|
||||||
|
"socket.io-parser": {
|
||||||
|
"version": "3.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.2.tgz",
|
||||||
|
"integrity": "sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==",
|
||||||
|
"requires": {
|
||||||
|
"component-emitter": "~1.3.0",
|
||||||
|
"debug": "~3.1.0",
|
||||||
|
"isarray": "2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ws": {
|
||||||
|
"version": "7.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
|
||||||
|
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"socket.io-parser": {
|
||||||
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==",
|
||||||
|
"requires": {
|
||||||
|
"@types/component-emitter": "^1.2.10",
|
||||||
|
"component-emitter": "~1.3.0",
|
||||||
|
"debug": "~4.3.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "4.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||||
|
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"sockjs": {
|
"sockjs": {
|
||||||
"version": "0.3.20",
|
"version": "0.3.20",
|
||||||
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz",
|
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz",
|
||||||
|
|
@ -12567,6 +12898,11 @@
|
||||||
"integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
|
"integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"spark-md5": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-0tF3AGSD1ppQeuffsLDIOWlKUd3lS92tFxcsrh5Pe3ZphhnoK+oXIBTzOAThZCiuINZLvpiLH/1VS1/ANEJVig=="
|
||||||
|
},
|
||||||
"spdx-correct": {
|
"spdx-correct": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
|
||||||
|
|
@ -12651,8 +12987,7 @@
|
||||||
"sprintf-js": {
|
"sprintf-js": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"sshpk": {
|
"sshpk": {
|
||||||
"version": "1.16.1",
|
"version": "1.16.1",
|
||||||
|
|
@ -13136,6 +13471,11 @@
|
||||||
"os-tmpdir": "~1.0.2"
|
"os-tmpdir": "~1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"to-array": {
|
||||||
|
"version": "0.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
|
||||||
|
"integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
|
||||||
|
},
|
||||||
"to-arraybuffer": {
|
"to-arraybuffer": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
|
||||||
|
|
@ -13464,7 +13804,6 @@
|
||||||
"version": "4.2.2",
|
"version": "4.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
|
||||||
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
|
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
|
|
@ -13848,6 +14187,11 @@
|
||||||
"integrity": "sha512-W74OO2vCJPs9/YjNjW8lLbj+jzT24waTo2KShI8jLvJW8OaIkgb3wuAMA7D+ZiUxDOx3ubwSZTaJBip9G8a3aQ==",
|
"integrity": "sha512-W74OO2vCJPs9/YjNjW8lLbj+jzT24waTo2KShI8jLvJW8OaIkgb3wuAMA7D+ZiUxDOx3ubwSZTaJBip9G8a3aQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"vuvuzela": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/vuvuzela/-/vuvuzela-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-O+FF5YJxxzylUnndhR8SpoIRSws="
|
||||||
|
},
|
||||||
"watchpack": {
|
"watchpack": {
|
||||||
"version": "1.7.4",
|
"version": "1.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz",
|
||||||
|
|
@ -14517,6 +14861,11 @@
|
||||||
"async-limiter": "~1.0.0"
|
"async-limiter": "~1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"xmlhttprequest-ssl": {
|
||||||
|
"version": "1.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.2.tgz",
|
||||||
|
"integrity": "sha512-tYOaldF/0BLfKuoA39QMwD4j2m8lq4DIncqj1yuNELX4vz9+z/ieG/vwmctjJce+boFHXstqhWnHSxc4W8f4qg=="
|
||||||
|
},
|
||||||
"xtend": {
|
"xtend": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
|
|
@ -14653,6 +15002,11 @@
|
||||||
"decamelize": "^1.2.0"
|
"decamelize": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"yeast": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
|
||||||
|
},
|
||||||
"yorkie": {
|
"yorkie": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yorkie/-/yorkie-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yorkie/-/yorkie-2.0.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,14 @@
|
||||||
"author": "OpenFlexure <contact@openflexure.org> (https://www.openflexure.org)",
|
"author": "OpenFlexure <contact@openflexure.org> (https://www.openflexure.org)",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve --mode development",
|
"serve": "vue-cli-service serve --mode development",
|
||||||
|
"serve-without-imjoy": "VUE_APP_ENABLE_IMJOY=false vue-cli-service serve --mode development",
|
||||||
"build": "vue-cli-service build --mode production",
|
"build": "vue-cli-service build --mode production",
|
||||||
|
"build-without-imjoy": "VUE_APP_ENABLE_IMJOY=false vue-cli-service build --mode production",
|
||||||
"lint": "vue-cli-service lint",
|
"lint": "vue-cli-service lint",
|
||||||
"lint:fix": "vue-cli-service lint --fix"
|
"lint:fix": "vue-cli-service lint --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"imjoy-core": "^0.13.74",
|
||||||
"material-design-icons": "^3.0.1",
|
"material-design-icons": "^3.0.1",
|
||||||
"mousetrap": "^1.6.5",
|
"mousetrap": "^1.6.5",
|
||||||
"vue-observe-visibility": "^0.4.6"
|
"vue-observe-visibility": "^0.4.6"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<docs>
|
||||||
|
[TODO: write documentation for this plugin.]
|
||||||
|
</docs>
|
||||||
|
|
||||||
|
<config lang="json">
|
||||||
|
{
|
||||||
|
"name": "List services",
|
||||||
|
"type": "web-worker",
|
||||||
|
"tags": [],
|
||||||
|
"ui": "",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"cover": "",
|
||||||
|
"description": "List the available ImJoy services in the console",
|
||||||
|
"icon": "extension",
|
||||||
|
"inputs": null,
|
||||||
|
"outputs": null,
|
||||||
|
"api_version": "0.1.8",
|
||||||
|
"env": "",
|
||||||
|
"permissions": [],
|
||||||
|
"requirements": [],
|
||||||
|
"dependencies": []
|
||||||
|
}
|
||||||
|
</config>
|
||||||
|
|
||||||
|
<script lang="javascript">
|
||||||
|
class ImJoyPlugin {
|
||||||
|
async setup() {
|
||||||
|
api.log('initialized')
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(ctx) {
|
||||||
|
// get a list of service
|
||||||
|
const services = await api.getServices({name: "OpenFlexure"});
|
||||||
|
console.log("OpenFlexure services:");
|
||||||
|
console.log(services);
|
||||||
|
const allservices = await api.getServices();
|
||||||
|
console.log("All services:");
|
||||||
|
console.log(allservices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.export(new ImJoyPlugin())
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
<config lang="json">
|
||||||
|
{
|
||||||
|
"name": "Script Editor",
|
||||||
|
"type": "web-worker",
|
||||||
|
"tags": [],
|
||||||
|
"ui": "",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"cover": "",
|
||||||
|
"description": "A Script Editor for Controlling OpenFlexure Microscope with ImJoy",
|
||||||
|
"icon": "extension",
|
||||||
|
"inputs": null,
|
||||||
|
"outputs": null,
|
||||||
|
"api_version": "0.1.8",
|
||||||
|
"env": "",
|
||||||
|
"permissions": [],
|
||||||
|
"requirements": [],
|
||||||
|
"dependencies": []
|
||||||
|
}
|
||||||
|
</config>
|
||||||
|
|
||||||
|
<script lang="javascript">
|
||||||
|
class ImJoyPlugin {
|
||||||
|
async setup() {
|
||||||
|
api.log('initialized')
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(ctx) {
|
||||||
|
let pluginInEditor, stopped, editorWindow;
|
||||||
|
const config = {lang: 'javascript'}
|
||||||
|
config.templates = [
|
||||||
|
{
|
||||||
|
name: "New",
|
||||||
|
url: null,
|
||||||
|
lang: 'javascript',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Snap Image Template",
|
||||||
|
url: "./OpenFlexureSnapImageTemplate.imjoy.html",
|
||||||
|
lang: "html",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
config.ui_elements = {
|
||||||
|
save: {
|
||||||
|
_rintf: true,
|
||||||
|
type: 'button',
|
||||||
|
label: "Save",
|
||||||
|
visible: false,
|
||||||
|
icon: "content-save",
|
||||||
|
callback(content) {
|
||||||
|
console.log(content)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
run: {
|
||||||
|
_rintf: true,
|
||||||
|
type: 'button',
|
||||||
|
label: "Run",
|
||||||
|
icon: "play",
|
||||||
|
visible: true,
|
||||||
|
shortcut: 'Shift-Enter',
|
||||||
|
async callback(content) {
|
||||||
|
try {
|
||||||
|
editorWindow.setLoader(true);
|
||||||
|
editorWindow.updateUIElement('stop', {
|
||||||
|
visible: true
|
||||||
|
})
|
||||||
|
api.showProgress(0);
|
||||||
|
pluginInEditor = await api.getPlugin({src: content, hot_reloading: true})
|
||||||
|
|
||||||
|
if (stopped) {
|
||||||
|
pluginInEditor = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pluginInEditor && pluginInEditor.run) {
|
||||||
|
return await pluginInEditor.run({
|
||||||
|
config: {},
|
||||||
|
data: {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (stopped) {
|
||||||
|
pluginInEditor = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
api.showMessage("Failed to load plugin, error: " + e.toString());
|
||||||
|
} finally {
|
||||||
|
editorWindow.updateUIElement('stop', {
|
||||||
|
visible: false
|
||||||
|
})
|
||||||
|
editorWindow.setLoader(false);
|
||||||
|
api.showProgress(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
stop: {
|
||||||
|
_rintf: true,
|
||||||
|
type: 'button',
|
||||||
|
label: "Stop",
|
||||||
|
style: "color: #ff0080cf;",
|
||||||
|
icon: "stop",
|
||||||
|
visible: false,
|
||||||
|
async callback() {
|
||||||
|
stopped = true;
|
||||||
|
await editorWindow.setLoader(false);
|
||||||
|
await editorWindow.updateUIElement('stop', {
|
||||||
|
visible: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
export: {
|
||||||
|
_rintf: true,
|
||||||
|
type: 'button',
|
||||||
|
label: "Export",
|
||||||
|
icon: "file-download-outline",
|
||||||
|
visible: true,
|
||||||
|
async callback(content) {
|
||||||
|
const fileName = (pluginInEditor && pluginInEditor.config.name && pluginInEditor.config.name + '.imjoy.html') || config.name + '.imjoy.html' || "myPlugin.imjoy.html";
|
||||||
|
await api.exportFile(content, fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editorWindow = await api.createWindow({
|
||||||
|
src: 'https://if.imjoy.io',
|
||||||
|
name: (ctx && ctx.data && ctx.data.name) ||'OpenFlexure Script Editor',
|
||||||
|
config,
|
||||||
|
data: {code: ctx && ctx.data && ctx.data.code}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.export(new ImJoyPlugin())
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<docs>
|
||||||
|
[TODO: write documentation for this plugin.]
|
||||||
|
</docs>
|
||||||
|
|
||||||
|
<config lang="json">
|
||||||
|
{
|
||||||
|
"name": "OpenFlexureSnapImageTemplate",
|
||||||
|
"type": "web-worker",
|
||||||
|
"tags": [],
|
||||||
|
"ui": "",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"cover": "",
|
||||||
|
"description": "[TODO: describe this plugin with one sentence.]",
|
||||||
|
"icon": "extension",
|
||||||
|
"inputs": null,
|
||||||
|
"outputs": null,
|
||||||
|
"api_version": "0.1.8",
|
||||||
|
"env": "",
|
||||||
|
"permissions": [],
|
||||||
|
"requirements": [],
|
||||||
|
"dependencies": []
|
||||||
|
}
|
||||||
|
</config>
|
||||||
|
|
||||||
|
<script lang="javascript">
|
||||||
|
class ImJoyPlugin {
|
||||||
|
async setup() {
|
||||||
|
api.log('initialized')
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(ctx) {
|
||||||
|
// get a list of service
|
||||||
|
const services = await api.getServices({name: "OpenFlexure"});
|
||||||
|
// get the first service
|
||||||
|
const ofm = services[0];
|
||||||
|
// snap an image
|
||||||
|
await ofm.snapImage();
|
||||||
|
console.log("snapped an image");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.export(new ImJoyPlugin())
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
<docs>
|
||||||
|
[TODO: write documentation for this plugin.]
|
||||||
|
</docs>
|
||||||
|
|
||||||
|
<config lang="json">
|
||||||
|
{
|
||||||
|
"name": "Test OFM Stage",
|
||||||
|
"type": "web-worker",
|
||||||
|
"tags": [],
|
||||||
|
"ui": "",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"cover": "",
|
||||||
|
"description": "Move the OFM stage to test the API works",
|
||||||
|
"icon": "extension",
|
||||||
|
"inputs": null,
|
||||||
|
"outputs": null,
|
||||||
|
"api_version": "0.1.8",
|
||||||
|
"env": "",
|
||||||
|
"permissions": [],
|
||||||
|
"requirements": [],
|
||||||
|
"dependencies": []
|
||||||
|
}
|
||||||
|
</config>
|
||||||
|
|
||||||
|
<script lang="javascript">
|
||||||
|
class ImJoyPlugin {
|
||||||
|
async setup() {
|
||||||
|
api.log('initialized')
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(ctx) {
|
||||||
|
// get a list of service
|
||||||
|
const services = await api.getServices({name: "OpenFlexure"});
|
||||||
|
// get the first service
|
||||||
|
const ofm = services[0];
|
||||||
|
// snap an image
|
||||||
|
const start = await ofm.getXYZPosition();
|
||||||
|
console.log(`Starting position: ${start}`);
|
||||||
|
// move the Z axis up and down
|
||||||
|
await ofm.setPosition(start[2] + 100);
|
||||||
|
console.log(`Moved Z to ${await ofm.getPosition()}`);
|
||||||
|
await ofm.setPosition(start[2]);
|
||||||
|
console.log(`Moved Z to ${await ofm.getPosition()}`);
|
||||||
|
await ofm.fullFocus();
|
||||||
|
console.log("Ran a full autofocus");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.export(new ImJoyPlugin())
|
||||||
|
</script>
|
||||||
|
|
@ -10,8 +10,8 @@
|
||||||
// Colors
|
// Colors
|
||||||
//
|
//
|
||||||
|
|
||||||
@global-color: #666;
|
@global-color: #000;
|
||||||
@global-emphasis-color: #333;
|
@global-emphasis-color: #C32280;
|
||||||
@global-muted-color: #666;
|
@global-muted-color: #666;
|
||||||
|
|
||||||
@global-primary-background: #C32280;
|
@global-primary-background: #C32280;
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
@global-medium-font-size: 20px;
|
@global-medium-font-size: 20px;
|
||||||
@global-small-font-size: 14px;
|
@global-small-font-size: 14px;
|
||||||
|
|
||||||
@global-emphasis-color: #222;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Base
|
// Base
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,14 @@
|
||||||
:class="item.class"
|
:class="item.class"
|
||||||
@set-tab="setTab"
|
@set-tab="setTab"
|
||||||
>
|
>
|
||||||
<i class="material-icons">{{ item.icon }}</i>
|
<img
|
||||||
|
v-if="item.iconURL"
|
||||||
|
style="filter: grayscale(100%);width: 22px;margin-top: 5px;margin-bottom: 8px;"
|
||||||
|
:src="item.iconURL"
|
||||||
|
/>
|
||||||
|
<i v-if="!item.iconURL" class="material-icons">
|
||||||
|
{{ item.icon }}
|
||||||
|
</i>
|
||||||
</tabIcon>
|
</tabIcon>
|
||||||
<!-- Add a divider if item.divide is true -->
|
<!-- Add a divider if item.divide is true -->
|
||||||
<hr v-if="item.divide" :key="'tab-divider-' + index" />
|
<hr v-if="item.divide" :key="'tab-divider-' + index" />
|
||||||
|
|
@ -48,6 +55,25 @@
|
||||||
<i class="material-icons">{{ plugin.icon || "extension" }}</i>
|
<i class="material-icons">{{ plugin.icon || "extension" }}</i>
|
||||||
</tabIcon>
|
</tabIcon>
|
||||||
|
|
||||||
|
<tabIcon
|
||||||
|
v-for="imjoyTab in imjoyTabs"
|
||||||
|
:key="imjoyTab.id"
|
||||||
|
:tab-i-d="'ImJoy-Plugin-' + imjoyTab.id"
|
||||||
|
:title="imjoyTab.name"
|
||||||
|
:require-connection="false"
|
||||||
|
:current-tab="currentTab"
|
||||||
|
@set-tab="setTab"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-if="imjoyTab.iconURL"
|
||||||
|
style="filter: grayscale(100%);width: 22px;margin-top: 5px;margin-bottom: 8px;"
|
||||||
|
:src="imjoyTab.iconURL"
|
||||||
|
/>
|
||||||
|
<i v-if="!imjoyTab.iconURL" class="material-icons">
|
||||||
|
{{ imjoyTab.iconName || "extension" }}
|
||||||
|
</i>
|
||||||
|
</tabIcon>
|
||||||
|
|
||||||
<hr id="extension-tab-divider" />
|
<hr id="extension-tab-divider" />
|
||||||
|
|
||||||
<!-- For each bottom tab -->
|
<!-- For each bottom tab -->
|
||||||
|
|
@ -104,6 +130,16 @@
|
||||||
/>
|
/>
|
||||||
</tabContent>
|
</tabContent>
|
||||||
|
|
||||||
|
<tabContent
|
||||||
|
v-for="imjoyTab in imjoyTabs"
|
||||||
|
:key="imjoyTab.id"
|
||||||
|
:tab-i-d="'ImJoy-Plugin-' + imjoyTab.id"
|
||||||
|
:require-connection="false"
|
||||||
|
:current-tab="currentTab"
|
||||||
|
>
|
||||||
|
<div :id="imjoyTab.window_id" class="window-container">Loading...</div>
|
||||||
|
</tabContent>
|
||||||
|
|
||||||
<!-- For each bottom tab -->
|
<!-- For each bottom tab -->
|
||||||
<tabContent
|
<tabContent
|
||||||
v-for="item in bottomTabs"
|
v-for="item in bottomTabs"
|
||||||
|
|
@ -121,6 +157,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { mapState } from "vuex";
|
||||||
|
|
||||||
// Import generic components
|
// Import generic components
|
||||||
import tabIcon from "./genericComponents/tabIcon";
|
import tabIcon from "./genericComponents/tabIcon";
|
||||||
|
|
@ -132,15 +169,22 @@ import captureContent from "./tabContentComponents/captureContent.vue";
|
||||||
import slideScanContent from "./tabContentComponents/slideScanContent.vue";
|
import slideScanContent from "./tabContentComponents/slideScanContent.vue";
|
||||||
import viewContent from "./tabContentComponents/viewContent.vue";
|
import viewContent from "./tabContentComponents/viewContent.vue";
|
||||||
import settingsContent from "./tabContentComponents/settingsContent.vue";
|
import settingsContent from "./tabContentComponents/settingsContent.vue";
|
||||||
import galleryContent from "./tabContentComponents/galleryContent.vue";
|
|
||||||
import extensionContent from "./tabContentComponents/extensionContent.vue";
|
import extensionContent from "./tabContentComponents/extensionContent.vue";
|
||||||
import aboutContent from "./tabContentComponents/aboutContent.vue";
|
import aboutContent from "./tabContentComponents/aboutContent.vue";
|
||||||
import loggingContent from "./tabContentComponents/loggingContent.vue";
|
import loggingContent from "./tabContentComponents/loggingContent.vue";
|
||||||
|
// ImJoy and the gallery are loaded asynchronously to allow them to be disabled if needed
|
||||||
|
const galleryContent = () =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "gallery" */ "./tabContentComponents/galleryContent.vue"
|
||||||
|
);
|
||||||
|
const imjoyContent = () =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "imjoy" */ "./tabContentComponents/imjoyContent.vue"
|
||||||
|
);
|
||||||
|
|
||||||
// Import modal components for device initialisation
|
// Import modal components for device initialisation
|
||||||
import calibrationModal from "./modalComponents/calibrationModal.vue";
|
import calibrationModal from "./modalComponents/calibrationModal.vue";
|
||||||
|
import TabIcon from "./genericComponents/tabIcon.vue";
|
||||||
import { mapState } from "vuex";
|
|
||||||
|
|
||||||
// Export main app
|
// Export main app
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -158,38 +202,14 @@ export default {
|
||||||
extensionContent,
|
extensionContent,
|
||||||
calibrationModal,
|
calibrationModal,
|
||||||
aboutContent,
|
aboutContent,
|
||||||
loggingContent
|
loggingContent,
|
||||||
|
TabIcon,
|
||||||
|
imjoyContent
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
plugins: [],
|
plugins: [],
|
||||||
currentTab: "view",
|
currentTab: "view",
|
||||||
unwatchStoreFunction: null,
|
|
||||||
topTabs: [
|
|
||||||
{
|
|
||||||
id: "view",
|
|
||||||
icon: "visibility",
|
|
||||||
component: viewContent
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "gallery",
|
|
||||||
icon: "photo_library",
|
|
||||||
component: galleryContent,
|
|
||||||
divide: true // Add a divider after this tab icon
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "navigate",
|
|
||||||
icon: "gamepad",
|
|
||||||
component: navigateContent
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "capture",
|
|
||||||
icon: "camera_alt",
|
|
||||||
component: captureContent,
|
|
||||||
divide: true // Add a divider after this tab icon
|
|
||||||
}
|
|
||||||
],
|
|
||||||
bottomTabs: [
|
bottomTabs: [
|
||||||
{
|
{
|
||||||
id: "settings",
|
id: "settings",
|
||||||
|
|
@ -241,25 +261,64 @@ export default {
|
||||||
return ind;
|
return ind;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
topTabs: function() {
|
||||||
|
let tabs = [
|
||||||
|
{
|
||||||
|
id: "view",
|
||||||
|
icon: "visibility",
|
||||||
|
component: viewContent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "gallery",
|
||||||
|
icon: "photo_library",
|
||||||
|
component: galleryContent,
|
||||||
|
divide: true // Add a divider after this tab icon
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "navigate",
|
||||||
|
icon: "gamepad",
|
||||||
|
component: navigateContent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "capture",
|
||||||
|
icon: "camera_alt",
|
||||||
|
component: captureContent,
|
||||||
|
divide: true // Add a divider after this tab icon
|
||||||
|
}
|
||||||
|
];
|
||||||
|
if (!this.$store.state.galleryEnabled) {
|
||||||
|
tabs = tabs.filter(tab => tab.id != "gallery");
|
||||||
|
}
|
||||||
|
if (this.$store.state.IHIEnabled) {
|
||||||
|
tabs.push({
|
||||||
|
id: "slidescan",
|
||||||
|
icon: "settings_overscan",
|
||||||
|
component: slideScanContent,
|
||||||
|
divide: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (this.$store.state.imjoyEnabled) {
|
||||||
|
tabs.push({
|
||||||
|
id: "imjoy",
|
||||||
|
iconURL: "https://imjoy.io/static/img/imjoy-icon.svg",
|
||||||
|
component: imjoyContent,
|
||||||
|
divide: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return tabs;
|
||||||
|
},
|
||||||
|
|
||||||
currentTabIndex: function() {
|
currentTabIndex: function() {
|
||||||
return this.tabOrder.indexOf(this.currentTab);
|
return this.tabOrder.indexOf(this.currentTab);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Map the setting for IHI's interface so we can watch it
|
// Map the tabs from ImJoy's store module so we can display them
|
||||||
...mapState(["IHIEnabled"])
|
...mapState("imjoy", { imjoyTabs: "tabs" }),
|
||||||
},
|
...mapState({ imjoyEnabled: "imjoyEnabled" })
|
||||||
|
|
||||||
watch: {
|
|
||||||
// Update the interface when the IHI interface is enabled/disabled
|
|
||||||
IHIEnabled: function(newValue) {
|
|
||||||
this.updateTopTabs(newValue);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
created: function() {
|
created: function() {
|
||||||
if (this.$store.getters.ready) {
|
if (this.$store.getters.ready) {
|
||||||
// Update top tabs
|
|
||||||
this.updateTopTabs(this.$store.state.IHIEnabled);
|
|
||||||
// Update plugins
|
// Update plugins
|
||||||
this.updatePlugins().then(() => {
|
this.updatePlugins().then(() => {
|
||||||
// Start initialisation modals
|
// Start initialisation modals
|
||||||
|
|
@ -284,19 +343,6 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
updateTopTabs: function(IHIEnabled) {
|
|
||||||
if (IHIEnabled) {
|
|
||||||
this.topTabs.push({
|
|
||||||
id: "slidescan",
|
|
||||||
icon: "settings_overscan",
|
|
||||||
component: slideScanContent,
|
|
||||||
divide: true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// If the connection is now disconnected, empty capture list
|
|
||||||
this.topTabs = this.topTabs.filter(tab => tab.id != "slidescan");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updatePlugins: function() {
|
updatePlugins: function() {
|
||||||
return axios
|
return axios
|
||||||
.get(this.pluginsUri)
|
.get(this.pluginsUri)
|
||||||
|
|
@ -331,6 +377,10 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
.window-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
#component-left {
|
#component-left {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,26 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="uk-card-footer uk-padding-small">
|
<div class="uk-card-footer uk-padding-small">
|
||||||
|
<button
|
||||||
|
v-if="openInImjoyMenuItems.length != 0"
|
||||||
|
class="uk-icon"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
style="width:25px;"
|
||||||
|
src="https://imjoy.io/static/img/imjoy-icon.svg"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<div uk-dropdown="pos: top-center">
|
||||||
|
<ul class="uk-nav uk-dropdown-nav">
|
||||||
|
<li v-for="item in openInImjoyMenuItems" :key="item.name">
|
||||||
|
<a href="#" @click="item.callback(name, imgURL)"
|
||||||
|
><i class="material-icons">launch</i>{{ item.title }}</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-for="tag in tags" :key="tag" class="uk-display-inline">
|
<div v-for="tag in tags" :key="tag" class="uk-display-inline">
|
||||||
<span
|
<span
|
||||||
v-if="tag === 'temporary'"
|
v-if="tag === 'temporary'"
|
||||||
|
|
@ -147,6 +167,7 @@
|
||||||
<script>
|
<script>
|
||||||
import UIkit from "uikit";
|
import UIkit from "uikit";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { mapState } from "vuex";
|
||||||
|
|
||||||
import keyvalList from "../../fieldComponents/keyvalList";
|
import keyvalList from "../../fieldComponents/keyvalList";
|
||||||
|
|
||||||
|
|
@ -233,7 +254,8 @@ export default {
|
||||||
},
|
},
|
||||||
captureURL: function() {
|
captureURL: function() {
|
||||||
return this.links.self.href;
|
return this.links.self.href;
|
||||||
}
|
},
|
||||||
|
...mapState("imjoy", { openInImjoyMenuItems: "openImageMenu" })
|
||||||
},
|
},
|
||||||
|
|
||||||
created: function() {},
|
created: function() {},
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,29 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="uk-card-footer uk-padding-small">
|
<div class="uk-card-footer uk-padding-small">
|
||||||
|
<button
|
||||||
|
v-if="openInImjoyMenuItems.length != 0"
|
||||||
|
class="uk-icon"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
style="width:25px;"
|
||||||
|
src="https://imjoy.io/static/img/imjoy-icon.svg"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<div uk-dropdown="pos: top-center">
|
||||||
|
<ul class="uk-nav uk-dropdown-nav">
|
||||||
|
<li v-for="item in openInImjoyMenuItems" :key="item.name">
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
style="color:black"
|
||||||
|
@click="item.callback(name, allURLs)"
|
||||||
|
><i class="material-icons">launch</i>{{ item.title }}</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-for="tag in tags"
|
v-for="tag in tags"
|
||||||
:key="tag"
|
:key="tag"
|
||||||
|
|
@ -53,6 +76,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { mapState } from "vuex";
|
||||||
|
|
||||||
// Export main app
|
// Export main app
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -100,7 +124,8 @@ export default {
|
||||||
urls.push(capture.links.self.href);
|
urls.push(capture.links.self.href);
|
||||||
}
|
}
|
||||||
return urls;
|
return urls;
|
||||||
}
|
},
|
||||||
|
...mapState("imjoy", { openInImjoyMenuItems: "openScanMenu" })
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="uk-card uk-card-hover uk-card-small uk-card-default uk-margin"
|
||||||
|
:class="{ 'uk-card-secondary': $store.state.darkMode }"
|
||||||
|
>
|
||||||
|
<div class="uk-card-body">
|
||||||
|
<div class="uk-grid-small uk-flex-middle" uk-grid>
|
||||||
|
<div class="uk-width-auto">
|
||||||
|
<img
|
||||||
|
v-if="iconURL"
|
||||||
|
style="height:50px"
|
||||||
|
:data-src="iconURL"
|
||||||
|
:alt="name + ' icon'"
|
||||||
|
uk-img
|
||||||
|
/>
|
||||||
|
<i v-if="!iconURL" class="material-icons" style="font-size:50px">
|
||||||
|
{{ iconName }}
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-expand">
|
||||||
|
<h3 class="uk-card-title uk-margin-remove-bottom">{{ name }}</h3>
|
||||||
|
<p class="uk-text-meta uk-margin-remove-top">
|
||||||
|
<slot name="subtitle"></slot>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-auto">
|
||||||
|
<button class="uk-button" @click="$emit('click')">
|
||||||
|
<slot name="buttonText">Launch</slot>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ImjoyPluginCard",
|
||||||
|
|
||||||
|
props: {
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
iconURL: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: () => ""
|
||||||
|
},
|
||||||
|
iconName: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: () => "extension"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,644 @@
|
||||||
|
<template>
|
||||||
|
<!-- Grid managing tab content -->
|
||||||
|
<div class="uk-height-1-1 view-component">
|
||||||
|
<progress
|
||||||
|
ref="imjoyProgressbar"
|
||||||
|
style="height: 4px; margin-bottom:0!important"
|
||||||
|
class="uk-progress"
|
||||||
|
max="100"
|
||||||
|
></progress>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="uk-flex uk-flex-column uk-margin uk-margin-left uk-margin-right"
|
||||||
|
>
|
||||||
|
<imjoy-plugin-card
|
||||||
|
name="Load From URL"
|
||||||
|
icon-u-r-l="https://imjoy.io/static/img/imjoy-icon.svg"
|
||||||
|
@click="loadPluginDialog()"
|
||||||
|
>
|
||||||
|
<template v-slot:buttonText>Open</template>
|
||||||
|
</imjoy-plugin-card>
|
||||||
|
<imjoy-plugin-card
|
||||||
|
name="ImageJ.JS"
|
||||||
|
icon-u-r-l="https://ij.imjoy.io/assets/icons/chrome/chrome-installprocess-128-128.png"
|
||||||
|
@click="openImageJ()"
|
||||||
|
/>
|
||||||
|
<imjoy-plugin-card
|
||||||
|
name="Kaibu"
|
||||||
|
icon-u-r-l="https://kaibu.org/static/img/kaibu-icon.svg"
|
||||||
|
@click="startKaibu()"
|
||||||
|
/>
|
||||||
|
<imjoy-plugin-card
|
||||||
|
v-for="(p, name) in loadedPlugins"
|
||||||
|
:key="p.id"
|
||||||
|
:name="name"
|
||||||
|
icon-name="extension"
|
||||||
|
@click="runPlugin(p)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="loading" class="progress uk-margin-small">
|
||||||
|
<div class="indeterminate"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as imjoyCore from "imjoy-core";
|
||||||
|
import axios from "axios";
|
||||||
|
import UIkit from "uikit";
|
||||||
|
import { mapState } from "vuex";
|
||||||
|
import ImjoyPluginCard from "./imjoyComponents/imjoyPluginCard.vue";
|
||||||
|
|
||||||
|
// TODO: move this to a module or something...
|
||||||
|
async function pollAction(response) {
|
||||||
|
// Poll an action until its status is completed
|
||||||
|
// TODO: raise an error if it fails or is cancelled
|
||||||
|
// For ease, this accepts and returns a response object from
|
||||||
|
// axios.get
|
||||||
|
// FIXME: this could be an infinite loop if the task is cancelled/errored
|
||||||
|
let r = response;
|
||||||
|
while ((r.data.status == "running") | (r.data.status == "pending")) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
r = await axios.get(r.data.href);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Image element and wait for it to load.
|
||||||
|
*
|
||||||
|
* This returns a Promise, so you can "await" the image
|
||||||
|
* element rather than needing to add a callback.
|
||||||
|
*/
|
||||||
|
function loadImageFromUrl(src) {
|
||||||
|
// Create an Image, and return it once it has loaded
|
||||||
|
// see https://stackoverflow.com/a/66180709
|
||||||
|
// no need to declare async, as it returns a Promise
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const img = new Image();
|
||||||
|
// We must set crossOrigin="anonymous" in order
|
||||||
|
// to avoid security errors when we get the pixel
|
||||||
|
// data
|
||||||
|
img.crossOrigin = "anonymous";
|
||||||
|
img.onload = () => resolve(img);
|
||||||
|
img.onerror = reject;
|
||||||
|
img.src = src;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an image from a URL, and return the RGB data
|
||||||
|
* in a format that can be cast to an ndarray
|
||||||
|
*
|
||||||
|
* This follows a slightly convoluted recipe, where we
|
||||||
|
* "draw" the image, and then extract the pixels from the
|
||||||
|
* 2D canvas.
|
||||||
|
*/
|
||||||
|
async function imageUrlToNdarray(url) {
|
||||||
|
// see https://stackoverflow.com/questions/934012/get-image-data-url-in-javascript
|
||||||
|
const img = await loadImageFromUrl(url);
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
ctx.drawImage(img, 0, 0, img.width, img.height);
|
||||||
|
const imageData = ctx.getImageData(0, 0, img.width, img.height);
|
||||||
|
const rgbaData = imageData.data; // This gets the RGBA values as an ArrayBuffer
|
||||||
|
// convert RGBA to RGB
|
||||||
|
const rgbData = new Uint8Array(new ArrayBuffer(img.height * img.width * 3));
|
||||||
|
for (let i = 0; i < img.height; i++) {
|
||||||
|
for (let j = 0; j < img.width; j++) {
|
||||||
|
const pos = i * img.width + j;
|
||||||
|
rgbData[pos * 3] = rgbaData[pos * 4];
|
||||||
|
rgbData[pos * 3 + 1] = rgbaData[pos * 4 + 1];
|
||||||
|
rgbData[pos * 3 + 2] = rgbaData[pos * 4 + 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
_rtype: "ndarray",
|
||||||
|
_rdtype: "uint8",
|
||||||
|
_rshape: [img.height, img.width, 3],
|
||||||
|
_rvalue: rgbData.buffer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ImJoyContent",
|
||||||
|
|
||||||
|
components: { ImjoyPluginCard },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
loadedPlugins: {},
|
||||||
|
viewers: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
captureActionUri: function() {
|
||||||
|
return `${this.$store.getters.baseUri}/api/v2/actions/camera/capture`;
|
||||||
|
},
|
||||||
|
snapshotStreamUri: function() {
|
||||||
|
return `${this.$store.getters.baseUri}/api/v2/streams/snapshot`;
|
||||||
|
},
|
||||||
|
getPositionUri: function() {
|
||||||
|
return `${this.$store.getters.baseUri}/api/v2/instrument/state/stage/position`;
|
||||||
|
},
|
||||||
|
moveActionUri: function() {
|
||||||
|
return `${this.$store.getters.baseUri}/api/v2/actions/stage/move`;
|
||||||
|
},
|
||||||
|
baseUri: function() {
|
||||||
|
return this.$store.getters.baseUri;
|
||||||
|
},
|
||||||
|
...mapState("imjoy", { windows: "tabs" })
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const self = this;
|
||||||
|
const imjoy = new imjoyCore.ImJoy({
|
||||||
|
// We will load the ImJoy plugin base frame via http (instead of https)
|
||||||
|
// such that imjoy plugins can access insecured urls
|
||||||
|
// however, some browser features won't work (e.g. webrtc)
|
||||||
|
// unless we explicitly set `base_frame` in the plugin to
|
||||||
|
// https://lib.imjoy.io/default_base_frame.html
|
||||||
|
default_base_frame: "http://lib.imjoy.io/default_base_frame.html",
|
||||||
|
imjoy_api: {
|
||||||
|
async showDialog(plugin, config, exta_config) {
|
||||||
|
return await imjoy.pm.createWindow(plugin, config, exta_config);
|
||||||
|
},
|
||||||
|
async showSnackbar(plugin, msg, duration) {
|
||||||
|
duration = duration || 5;
|
||||||
|
UIkit.notification.closeAll();
|
||||||
|
UIkit.notification({
|
||||||
|
message: msg.slice(0, 100),
|
||||||
|
status: "primary",
|
||||||
|
pos: "bottom-center",
|
||||||
|
timeout: duration * 1000
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async showMessage(plugin, msg) {
|
||||||
|
imjoy.imjoy_api.showSnackbar(plugin, msg, 5);
|
||||||
|
},
|
||||||
|
async showStatus(plugin, status) {
|
||||||
|
imjoy.imjoy_api.showSnackbar(plugin, status, 5);
|
||||||
|
},
|
||||||
|
async showProgress(plugin, p) {
|
||||||
|
p = p || 0;
|
||||||
|
if (p < 1.0) p = p * 100;
|
||||||
|
if (p > 100) p = 100;
|
||||||
|
if (p) {
|
||||||
|
self.$refs.imjoyProgressbar.style.display = "block";
|
||||||
|
self.$refs.imjoyProgressbar.value = parseInt(p);
|
||||||
|
} else {
|
||||||
|
self.$refs.imjoyProgressbar.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
imjoy.start({ workspace: "default" }).then(async () => {
|
||||||
|
console.log("ImJoy started");
|
||||||
|
imjoy.event_bus.on("add_window", w => {
|
||||||
|
this.addWindow(w);
|
||||||
|
});
|
||||||
|
this.setupMicroscopeAPI();
|
||||||
|
this.setupViewers();
|
||||||
|
const origin = window.location.origin;
|
||||||
|
const localPlugins = [
|
||||||
|
"OpenFlexureSnapImageTemplate",
|
||||||
|
"OpenFlexureScriptEditor",
|
||||||
|
"OpenFlexureTestMoveStage",
|
||||||
|
"ListServices"
|
||||||
|
];
|
||||||
|
for (let fname of localPlugins) {
|
||||||
|
await this.loadPlugin(`${origin}/${fname}.imjoy.html`);
|
||||||
|
}
|
||||||
|
this.setupPluginEngine();
|
||||||
|
});
|
||||||
|
this.imjoy = imjoy;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setupPluginEngine() {
|
||||||
|
// setup a plugin engine for Python plugins
|
||||||
|
this.imjoy.pm
|
||||||
|
.reloadPluginRecursively({
|
||||||
|
uri:
|
||||||
|
"https://imjoy-team.github.io/jupyter-engine-manager/Jupyter-Engine-Manager.imjoy.html"
|
||||||
|
})
|
||||||
|
.then(enginePlugin => {
|
||||||
|
// TODO: currently the url query are not preserved for ImJoy
|
||||||
|
const queryString = window.location.search;
|
||||||
|
const urlParams = new URLSearchParams(queryString);
|
||||||
|
const engine = urlParams.get("engine");
|
||||||
|
const spec = urlParams.get("spec");
|
||||||
|
if (engine) {
|
||||||
|
enginePlugin.api
|
||||||
|
.createEngine({
|
||||||
|
name: "MyCustomEngine",
|
||||||
|
nbUrl: engine,
|
||||||
|
url: engine.split("?")[0]
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
console.log("Jupyter Engine connected!");
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
console.error("Failed to connect to Jupyter Engine", e);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
enginePlugin.api
|
||||||
|
.createEngine({
|
||||||
|
name: "MyBinder Engine",
|
||||||
|
url: "https://mybinder.org",
|
||||||
|
spec: spec || "oeway/imjoy-binder-image/master"
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
console.log("Binder Engine connected!");
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
console.error("Failed to connect to MyBinder Engine", e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async setupMicroscopeAPI() {
|
||||||
|
// Here we register a set of API for controlling the microscope as a service
|
||||||
|
// For interoperatability, it might be good to reuse a subset of the function names defined in MicroManager
|
||||||
|
// See here: https://valelab4.ucsf.edu/~MM/doc/MMCore/html/class_c_m_m_core.html
|
||||||
|
const snapshotUri = this.snapshotStreamUri;
|
||||||
|
const captureActionUri = this.captureActionUri;
|
||||||
|
const modalError = this.modalError;
|
||||||
|
const getPositionAsObject = this.getStagePosition;
|
||||||
|
const setPositionAsObject = this.setStagePosition;
|
||||||
|
const getInstrumentSettings = this.getInstrumentSettings;
|
||||||
|
const setInstrumentSettings = this.setInstrumentSettings;
|
||||||
|
const runAutofocus = this.runAutofocus;
|
||||||
|
|
||||||
|
const service = {
|
||||||
|
_rintf: true, // this will make sure the function can be called multiple times
|
||||||
|
type: "#microscope-control",
|
||||||
|
name: "OpenFlexure",
|
||||||
|
lastSnapResponse: null,
|
||||||
|
snapPreviewImage() {
|
||||||
|
// The "proper" capture method is more involved - this one pulls a frame out of the preview stream
|
||||||
|
return axios
|
||||||
|
.get(snapshotUri)
|
||||||
|
.then(response => {
|
||||||
|
return response.data;
|
||||||
|
})
|
||||||
|
.catch(modalError);
|
||||||
|
},
|
||||||
|
snapImage() {
|
||||||
|
// Do capture
|
||||||
|
return axios.post(captureActionUri, {}).then(response => {
|
||||||
|
service.lastSnapResponse = response;
|
||||||
|
// TODO: send the events to flash the stream and update captures
|
||||||
|
// Flash the stream (capture animation)
|
||||||
|
//this.$root.$emit("globalFlashStream");
|
||||||
|
// Update the global capture list
|
||||||
|
//this.$root.$emit("globalUpdateCaptures");
|
||||||
|
});
|
||||||
|
//.catch(modalError);
|
||||||
|
},
|
||||||
|
async getImage() {
|
||||||
|
// FIXME: handle nicely getImage() being called before snapImage()
|
||||||
|
// Wait until the capture has completed and we can retrieve it
|
||||||
|
service.lastSnapResponse = await pollAction(service.lastSnapResponse);
|
||||||
|
let capture = service.lastSnapResponse.data.output;
|
||||||
|
// TODO: check links.download.href exists and is JPEG
|
||||||
|
let jpeg = await axios
|
||||||
|
.get(capture.links.download.href, {
|
||||||
|
responseType: "arraybuffer",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "image/jpeg"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(r => r.data);
|
||||||
|
console.log(
|
||||||
|
"Downloaded ",
|
||||||
|
jpeg.byteLength,
|
||||||
|
" bytes of JPEG image data"
|
||||||
|
);
|
||||||
|
return jpeg; //TODO: what is the expected output format???
|
||||||
|
},
|
||||||
|
setPosition(z) {
|
||||||
|
// The C api suggests this defaults to just Z, and can accept a "label" to
|
||||||
|
// select the stage.
|
||||||
|
return setPositionAsObject({
|
||||||
|
z: z,
|
||||||
|
absolute: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getPosition() {
|
||||||
|
const { z } = await getPositionAsObject();
|
||||||
|
return z;
|
||||||
|
},
|
||||||
|
async setXYPosition(x, y) {
|
||||||
|
return setPositionAsObject({
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
absolute: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getXYPosition() {
|
||||||
|
const { x, y } = await getPositionAsObject();
|
||||||
|
return [x, y];
|
||||||
|
},
|
||||||
|
async getXYZPosition() {
|
||||||
|
const { x, y, z } = await getPositionAsObject();
|
||||||
|
return [x, y, z];
|
||||||
|
},
|
||||||
|
async setXYZPosition(x, y, z) {
|
||||||
|
return setPositionAsObject({
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
z: z,
|
||||||
|
absolute: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getExposure() {
|
||||||
|
const settings = await getInstrumentSettings();
|
||||||
|
if (settings.camera.picamera) {
|
||||||
|
if (settings.camera.picamera.shutter_speed) {
|
||||||
|
return settings.camera.picamera.shutter_speed / 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
async setExposure(exposure) {
|
||||||
|
setInstrumentSettings({
|
||||||
|
camera: {
|
||||||
|
picamera: {
|
||||||
|
shutter_speed: exposure * 1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async fullFocus() {
|
||||||
|
return runAutofocus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const ref = await this.imjoy.pm.registerService(
|
||||||
|
{ type: "#microscope-control", name: "OpenFlexure" },
|
||||||
|
service
|
||||||
|
);
|
||||||
|
console.log("registered service");
|
||||||
|
console.log(ref);
|
||||||
|
},
|
||||||
|
closeWindow(w) {
|
||||||
|
if (w) {
|
||||||
|
w.api.close();
|
||||||
|
this.$store.commit("imjoy/removeTab", w);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Activate a window - you can pass the window object, its name, or its id.
|
||||||
|
*/
|
||||||
|
selectWindow(window) {
|
||||||
|
for (let w of this.windows) {
|
||||||
|
if ((w === window) | (w.name === window) | (w.id === window)) {
|
||||||
|
// Make the relevant window visible by switching to its tab
|
||||||
|
this.$root.$emit("globalSwitchTab", `ImJoy-Plugin-${w.id}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setWindowIconUrl(window, iconURL) {
|
||||||
|
this.$store.commit("imjoy/setTabProperty", {
|
||||||
|
tab: window,
|
||||||
|
key: "iconURL",
|
||||||
|
value: iconURL
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async startKaibu() {
|
||||||
|
this.viewers.kaibu = await this.imjoy.pm.createWindow(null, {
|
||||||
|
name: "Kaibu",
|
||||||
|
src: "https://kaibu.org/#/app"
|
||||||
|
});
|
||||||
|
this.viewers.kaibu.on("close", () => {
|
||||||
|
delete this.viewers.kaibu;
|
||||||
|
});
|
||||||
|
this.setWindowIconUrl(
|
||||||
|
this.viewers.kaibu.config.id,
|
||||||
|
"https://kaibu.org/static/img/kaibu-icon.svg"
|
||||||
|
);
|
||||||
|
this.viewers.kaibu.set_mode("full");
|
||||||
|
},
|
||||||
|
async startImageJ() {
|
||||||
|
this.viewers.imagej = await this.imjoy.pm.createWindow(null, {
|
||||||
|
name: "ImageJ.JS",
|
||||||
|
src: "https://ij.imjoy.io"
|
||||||
|
});
|
||||||
|
this.viewers.imagej.on("close", () => {
|
||||||
|
delete this.viewers.imagej;
|
||||||
|
});
|
||||||
|
this.setWindowIconUrl(
|
||||||
|
this.viewers.imagej.config.id,
|
||||||
|
"https://ij.imjoy.io/assets/icons/chrome/chrome-installprocess-128-128.png"
|
||||||
|
);
|
||||||
|
// load the ITK/VTK viewer for imagej.js
|
||||||
|
this.loadPlugin(
|
||||||
|
"https://gist.githubusercontent.com/oeway/e5c980fbf6582f25fde795262a7e33ec/raw/itk-vtk-viewer-imagej.imjoy.html"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Switch to the ImageJ window, starting it if necessary.
|
||||||
|
*/
|
||||||
|
async openImageJ() {
|
||||||
|
if (!this.viewers.imagej) {
|
||||||
|
await this.startImageJ();
|
||||||
|
}
|
||||||
|
this.selectWindow("ImageJ.JS");
|
||||||
|
return this.viewers.imagej;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Switch to the Kaibu window, starting it if necessary.
|
||||||
|
*/
|
||||||
|
async openKaibu() {
|
||||||
|
if (!this.viewers.kaibu) {
|
||||||
|
await this.startKaibu();
|
||||||
|
}
|
||||||
|
this.selectWindow("Kaibu"); // TODO: make this behave more nicely when we have multiple Kaibu windows
|
||||||
|
return this.viewers.kaibu;
|
||||||
|
},
|
||||||
|
async setupViewers() {
|
||||||
|
this.loading = true;
|
||||||
|
// TODO: improve this using the ImJoy `api.getServices`
|
||||||
|
// See here: https://imjoy.io/docs/#/api?id=apigetservices
|
||||||
|
// The idea is that plugins can register custom services via `api.registerService`
|
||||||
|
// For example, we can have a image visualization service
|
||||||
|
// Then other plugins can use `api.getServices` to get a list of services
|
||||||
|
// And this can be used for render our "Open In ImJoy" menu
|
||||||
|
try {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
this.$store.commit("imjoy/addOpenImageItem", {
|
||||||
|
title: "Kaibu",
|
||||||
|
async callback(name, imageUrl) {
|
||||||
|
const viewer = await self.openKaibu();
|
||||||
|
await viewer.view_image(imageUrl, { type: "2d-image", name });
|
||||||
|
await viewer.add_shapes([]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$store.commit("imjoy/addOpenImageItem", {
|
||||||
|
title: "ImageJ.JS",
|
||||||
|
async callback(name, imageUrl) {
|
||||||
|
const viewer = await self.openImageJ();
|
||||||
|
const response = await fetch(imageUrl);
|
||||||
|
const buffer = await response.arrayBuffer();
|
||||||
|
await viewer.viewImage(buffer, {
|
||||||
|
name: name.replace(".jpeg", ".jpg")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.$store.commit("imjoy/addOpenImageItem", {
|
||||||
|
title: "ImageJ.JS [ndarray]",
|
||||||
|
async callback(name, imageUrl) {
|
||||||
|
const viewer = await self.openImageJ();
|
||||||
|
const ndarray = await imageUrlToNdarray(imageUrl);
|
||||||
|
await viewer.viewImage(ndarray, {
|
||||||
|
name: name.replace(".jpeg", "")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.$store.commit("imjoy/addOpenScanItem", {
|
||||||
|
title: "ImageJ.JS [stack]",
|
||||||
|
async callback(name, allURLs) {
|
||||||
|
const viewer = await self.openImageJ();
|
||||||
|
// allURLs is a list of URLs for *capture objects*, so
|
||||||
|
// first we need to retrieve them, then extract image URLs
|
||||||
|
// and finally turn image URLs into "numpy arrays" (RGB data).
|
||||||
|
const imagesAsNdarrays = await Promise.all(
|
||||||
|
allURLs.map(async url => {
|
||||||
|
const r = await axios.get(url);
|
||||||
|
return await imageUrlToNdarray(r.data.links.download.href);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// Construct an array to hold all the images, based on the first image
|
||||||
|
// TODO: assert that _rdtype is uint8, _rshape is the same, _rtype is ndarray
|
||||||
|
const shape = imagesAsNdarrays[0]._rshape;
|
||||||
|
const totalSize = imagesAsNdarrays.reduce(
|
||||||
|
(total, image) => total + image._rvalue.byteLength,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const stackData = new Uint8Array(new ArrayBuffer(totalSize));
|
||||||
|
const transferredBytes = imagesAsNdarrays.reduce((total, image) => {
|
||||||
|
console.log(`inserting image at ${total} bytes`);
|
||||||
|
// TODO: is it bad to populate stackData as a "side effect"?
|
||||||
|
stackData.set(new Uint8Array(image._rvalue), total);
|
||||||
|
//assert image._rshape == shape;
|
||||||
|
return total + image._rvalue.byteLength;
|
||||||
|
}, 0);
|
||||||
|
const stackNdarray = {
|
||||||
|
_rtype: "ndarray",
|
||||||
|
_rdtype: "uint8",
|
||||||
|
_rshape: [imagesAsNdarrays.length, shape[0], shape[1], shape[2]],
|
||||||
|
_rvalue: stackData.buffer
|
||||||
|
};
|
||||||
|
console.log(
|
||||||
|
`Stack is ${transferredBytes} and will have shape: ${stackNdarray._rshape}`
|
||||||
|
);
|
||||||
|
await viewer.viewImage(stackNdarray, {
|
||||||
|
name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
alert(`Failed to setup viewers, error: ${e}`);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async addWindow(w) {
|
||||||
|
// Here we can add check w.standalone if we want to allow the plugin to decide whether to open in a new tab.
|
||||||
|
this.$store.commit("imjoy/addTab", w);
|
||||||
|
await this.$nextTick();
|
||||||
|
//this.$forceUpdate();
|
||||||
|
this.selectWindow(w);
|
||||||
|
},
|
||||||
|
loadPlugin(uri) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.loading = true;
|
||||||
|
this.imjoy.pm
|
||||||
|
.reloadPluginRecursively({
|
||||||
|
uri
|
||||||
|
})
|
||||||
|
.then(async plugin => {
|
||||||
|
this.loadedPlugins[plugin.name] = plugin;
|
||||||
|
this.loading = false;
|
||||||
|
resolve(plugin);
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
console.error(e);
|
||||||
|
this.loading = false;
|
||||||
|
reject(e);
|
||||||
|
alert(`failed to load the plugin, error: ${e}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async runPlugin(plugin) {
|
||||||
|
await plugin.api.run();
|
||||||
|
},
|
||||||
|
loadPluginDialog() {
|
||||||
|
const uri = prompt(
|
||||||
|
"Paste the ImJoy plugin url here",
|
||||||
|
"imjoy-team/imjoy-plugins:Welcome"
|
||||||
|
);
|
||||||
|
if (uri) {
|
||||||
|
this.loadPlugin(uri);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async getStagePosition() {
|
||||||
|
return axios.get(this.getPositionUri).then(r => r.data);
|
||||||
|
},
|
||||||
|
async setStagePosition(payload) {
|
||||||
|
return axios.post(this.moveActionUri, payload).then(pollAction);
|
||||||
|
},
|
||||||
|
async getInstrumentSettings() {
|
||||||
|
return axios
|
||||||
|
.get(`${this.baseUri}/api/v2/instrument/settings/`)
|
||||||
|
.then(r => r.data);
|
||||||
|
},
|
||||||
|
async setInstrumentSettings(payload) {
|
||||||
|
return axios.put(`${this.baseUri}/api/v2/instrument/settings/`, payload);
|
||||||
|
},
|
||||||
|
runAutofocus() {
|
||||||
|
// This mechanism is also used by the keybinding for autofocus.
|
||||||
|
// It means we don't have a way to poll, but that's not a problem.
|
||||||
|
this.$root.$emit("globalFastAutofocusEvent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.window-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imjoy-container-card {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-title {
|
||||||
|
height: 26px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
background-color: #efefef;
|
||||||
|
color: #482727;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugins-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 1px;
|
||||||
|
height: 26px;
|
||||||
|
line-height: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 100px;
|
||||||
|
height: 26px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -14,6 +14,36 @@
|
||||||
for use in clinics acquiring blood smear samples using a 100x
|
for use in clinics acquiring blood smear samples using a 100x
|
||||||
oil-immersion objective, with a Raspberry Pi Camera v2.
|
oil-immersion objective, with a Raspberry Pi Camera v2.
|
||||||
</p>
|
</p>
|
||||||
|
<p class="uk-margin-small" :class="{ 'uk-text-muted': !imjoyPermitted }">
|
||||||
|
<label :disabled="!imjoyPermitted">
|
||||||
|
<input
|
||||||
|
v-model="imjoyEnabled"
|
||||||
|
class="uk-checkbox"
|
||||||
|
type="checkbox"
|
||||||
|
:disabled="!imjoyPermitted"
|
||||||
|
/>
|
||||||
|
Enable ImJoy plugin engine
|
||||||
|
</label>
|
||||||
|
</p>
|
||||||
|
<p v-if="imjoyPermitted" class="uk-margin-small">
|
||||||
|
<a href="https://imjoy.io/">ImJoy</a> enables integration with a wide
|
||||||
|
variety of microscopy and image analysis applications, including ImageJ.JS
|
||||||
|
and Kaibu. Support for ImJoy within the OFM software is currently
|
||||||
|
experimental, and it may slow down loading of the application.
|
||||||
|
</p>
|
||||||
|
<p v-if="!imjoyPermitted" class="uk-margin-small uk-text-muted">
|
||||||
|
ImJoy plugins are disabled in this build of the OpenFlexure software.
|
||||||
|
</p>
|
||||||
|
<p class="uk-margin-small">
|
||||||
|
<label
|
||||||
|
><input v-model="galleryEnabled" class="uk-checkbox" type="checkbox" />
|
||||||
|
Enable Gallery</label
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<p class="uk-margin-small">
|
||||||
|
The "gallery" tab can cause performance issues; un-tick the box above to
|
||||||
|
disable it.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -34,18 +64,50 @@ export default {
|
||||||
set(value) {
|
set(value) {
|
||||||
this.$store.commit("changeIHIEnabled", value);
|
this.$store.commit("changeIHIEnabled", value);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
imjoyEnabled: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.imjoyEnabled;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$store.commit("changeImjoyEnabled", value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
galleryEnabled: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.galleryEnabled;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$store.commit("changeGalleryEnabled", value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
imjoyPermitted: function() {
|
||||||
|
// ImJoy may be disabled using an environment variable.
|
||||||
|
// If this function returns false, it is never possible to
|
||||||
|
// use ImJoy, so we should gray out the option.
|
||||||
|
return process.env.VUE_APP_ENABLE_IMJOY === "true";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
IHIEnabled: function() {
|
IHIEnabled: function() {
|
||||||
this.setLocalStorageObj("IHIEnabled", this.IHIEnabled);
|
this.setLocalStorageObj("IHIEnabled", this.IHIEnabled);
|
||||||
|
},
|
||||||
|
imjoyEnabled: function() {
|
||||||
|
this.setLocalStorageObj("imjoyEnabled", this.imjoyEnabled);
|
||||||
|
},
|
||||||
|
galleryEnabled: function() {
|
||||||
|
this.setLocalStorageObj("galleryEnabled", this.galleryEnabled);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
// Try loading settings from localStorage. If null, don't change.
|
// Try loading settings from localStorage. If null, don't change.
|
||||||
this.IHIEnabled = this.getLocalStorageObj("IHIEnabled") || this.IHIEnabled;
|
this.IHIEnabled = this.getLocalStorageObj("IHIEnabled") || this.IHIEnabled;
|
||||||
|
this.imjoyEnabled =
|
||||||
|
this.getLocalStorageObj("imjoyEnabled") || this.imjoyEnabled;
|
||||||
|
this.galleryEnabled =
|
||||||
|
this.getLocalStorageObj("galleryEnabled") || this.galleryEnabled;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -95,21 +95,12 @@ export default {
|
||||||
// Cache the stream settings to local storage for persistence
|
// Cache the stream settings to local storage for persistence
|
||||||
// (the next 3 functions all relate to this)
|
// (the next 3 functions all relate to this)
|
||||||
disableStream: function(newValue) {
|
disableStream: function(newValue) {
|
||||||
console.log(
|
|
||||||
`disableStream updated to ${newValue} and saved in local storage`
|
|
||||||
);
|
|
||||||
this.setLocalStorageObj("disableStream", newValue);
|
this.setLocalStorageObj("disableStream", newValue);
|
||||||
},
|
},
|
||||||
autoGpuPreview: function(newValue) {
|
autoGpuPreview: function(newValue) {
|
||||||
console.log(
|
|
||||||
`GPU preview updated to ${newValue} and saved in local storage`
|
|
||||||
);
|
|
||||||
this.setLocalStorageObj("autoGpuPreview", newValue);
|
this.setLocalStorageObj("autoGpuPreview", newValue);
|
||||||
},
|
},
|
||||||
trackWindow: function(newValue) {
|
trackWindow: function(newValue) {
|
||||||
console.log(
|
|
||||||
`trackWindow updated to ${newValue} and saved in local storage`
|
|
||||||
);
|
|
||||||
this.setLocalStorageObj("trackWindow", newValue);
|
this.setLocalStorageObj("trackWindow", newValue);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,64 @@ import Vuex from "vuex";
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
|
const moduleImjoy = {
|
||||||
|
namespaced: true,
|
||||||
|
state: () => ({
|
||||||
|
tabs: [],
|
||||||
|
openImageMenu: [],
|
||||||
|
openScanMenu: []
|
||||||
|
}),
|
||||||
|
mutations: {
|
||||||
|
addOpenImageItem(state, newItem) {
|
||||||
|
state.openImageMenu.push(newItem);
|
||||||
|
}, // TODO: add a mutation to remove items when plugins are unloaded
|
||||||
|
addOpenScanItem(state, newItem) {
|
||||||
|
state.openScanMenu.push(newItem);
|
||||||
|
}, // TODO: add a mutation to remove items when plugins are unloaded
|
||||||
|
clearMenus(state) {
|
||||||
|
// This is primarily useful to reset the state in hot-reloads of
|
||||||
|
// imjoyContent.vue
|
||||||
|
state.openImageMenu = [];
|
||||||
|
state.openScanMenu = [];
|
||||||
|
},
|
||||||
|
addTab(state, newItem) {
|
||||||
|
// Add a tab to the list of ImJoy tabs
|
||||||
|
state.tabs.push(newItem);
|
||||||
|
},
|
||||||
|
removeTab(state, tabToRemove) {
|
||||||
|
const index = state.tabs.indexOf(tabToRemove);
|
||||||
|
if (index > -1) {
|
||||||
|
state.tabs.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`Attempted to remove a non-existing ImJoy tab ${tabToRemove}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Set a parameter on the tab with a given id
|
||||||
|
*
|
||||||
|
* Payload should contain:
|
||||||
|
* tab: ID of the tab to modify (not window_id)
|
||||||
|
* key: name of the property to add/change
|
||||||
|
* value: value of the property to add/change
|
||||||
|
*/
|
||||||
|
setTabProperty(state, payload) {
|
||||||
|
let tab = payload.tab;
|
||||||
|
let key = payload.key;
|
||||||
|
let value = payload.value;
|
||||||
|
state.tabs = state.tabs.map(t => {
|
||||||
|
if (t.id === tab) {
|
||||||
|
t[key] = value;
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {},
|
||||||
|
getters: {}
|
||||||
|
};
|
||||||
|
|
||||||
function getOriginFromLocation() {
|
function getOriginFromLocation() {
|
||||||
// This will default to the same origin that's serving
|
// This will default to the same origin that's serving
|
||||||
// the web app - but can be overridden by the URL.
|
// the web app - but can be overridden by the URL.
|
||||||
|
|
@ -17,6 +75,9 @@ function getOriginFromLocation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
|
modules: {
|
||||||
|
imjoy: moduleImjoy
|
||||||
|
},
|
||||||
state: {
|
state: {
|
||||||
origin: getOriginFromLocation(),
|
origin: getOriginFromLocation(),
|
||||||
available: false,
|
available: false,
|
||||||
|
|
@ -26,6 +87,8 @@ export default new Vuex.Store({
|
||||||
autoGpuPreview: false,
|
autoGpuPreview: false,
|
||||||
trackWindow: true,
|
trackWindow: true,
|
||||||
IHIEnabled: false,
|
IHIEnabled: false,
|
||||||
|
imjoyEnabled: false,
|
||||||
|
galleryEnabled: true,
|
||||||
appTheme: "system",
|
appTheme: "system",
|
||||||
activeStreams: {}
|
activeStreams: {}
|
||||||
},
|
},
|
||||||
|
|
@ -52,6 +115,20 @@ export default new Vuex.Store({
|
||||||
changeIHIEnabled(state, enabled) {
|
changeIHIEnabled(state, enabled) {
|
||||||
state.IHIEnabled = enabled;
|
state.IHIEnabled = enabled;
|
||||||
},
|
},
|
||||||
|
changeImjoyEnabled(state, enabled) {
|
||||||
|
if (process.env.VUE_APP_ENABLE_IMJOY === "true") {
|
||||||
|
state.imjoyEnabled = enabled;
|
||||||
|
} else {
|
||||||
|
state.imjoyEnabled = false;
|
||||||
|
if (enabled)
|
||||||
|
console.warn(
|
||||||
|
"Attempted to enable ImJoy, but it's disabled by VUE_APP_ENABLE_IMJOY."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
changeGalleryEnabled(state, enabled) {
|
||||||
|
state.galleryEnabled = enabled;
|
||||||
|
},
|
||||||
resetState(state) {
|
resetState(state) {
|
||||||
state.waiting = false;
|
state.waiting = false;
|
||||||
state.available = false;
|
state.available = false;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue