| @@ -9,6 +9,7 @@ | |||||
| "js-uuid": "0.0.6", | "js-uuid": "0.0.6", | ||||
| "js-yaml": "^3.13.1", | "js-yaml": "^3.13.1", | ||||
| "linkstate": "^1.1.1", | "linkstate": "^1.1.1", | ||||
| "papaya-viewer": "^1.0.1449", | |||||
| "popper.js": "^1.16.1", | "popper.js": "^1.16.1", | ||||
| "preact": "^8.2.9", | "preact": "^8.2.9", | ||||
| "preact-router": "^2.6.1", | "preact-router": "^2.6.1", | ||||
| @@ -51,6 +51,8 @@ export default { | |||||
| 'node_modules/streamsaver/sw.js': 'dist/sw.js', | 'node_modules/streamsaver/sw.js': 'dist/sw.js', | ||||
| 'src/js/thirdparty/StreamSaver.js': 'dist/js/StreamSaver.js', | 'src/js/thirdparty/StreamSaver.js': 'dist/js/StreamSaver.js', | ||||
| 'node_modules/web-streams-polyfill/dist/ponyfill.js': 'dist/js/web-streams-polyfill/ponyfill.js', | 'node_modules/web-streams-polyfill/dist/ponyfill.js': 'dist/js/web-streams-polyfill/ponyfill.js', | ||||
| 'node_modules/papaya-viewer/release/current/standard/papaya.js': 'dist/js/papaya.js', | |||||
| 'node_modules/papaya-viewer/release/current/standard/papaya.css': 'dist/css/papaya.css', | |||||
| verbose: true | verbose: true | ||||
| }), | }), | ||||
| buble({jsx: 'h'}), | buble({jsx: 'h'}), | ||||
| @@ -4,6 +4,7 @@ | |||||
| <link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css" /> | <link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css" /> | ||||
| <link rel="stylesheet" type="text/css" href="/css/all.min.css" /> | <link rel="stylesheet" type="text/css" href="/css/all.min.css" /> | ||||
| <link rel="stylesheet" type="text/css" href="/css/index.css" /> | <link rel="stylesheet" type="text/css" href="/css/index.css" /> | ||||
| <link rel="stylesheet" type="text/css" href="/css/papaya.css" /> | |||||
| <script language="javascript" src="/js/web-streams-polyfill/ponyfill.js"></script> | <script language="javascript" src="/js/web-streams-polyfill/ponyfill.js"></script> | ||||
| <script language="javascript"> | <script language="javascript"> | ||||
| window.process = { 'env': { 'NODE_ENV': 'production' } }; | window.process = { 'env': { 'NODE_ENV': 'production' } }; | ||||
| @@ -138,6 +138,20 @@ class WBCollectionContent extends Component { | |||||
| onclick={ () => { | onclick={ () => { | ||||
| alert('Not implemented.') | alert('Not implemented.') | ||||
| } }><i class="far fa-eye"></i></button> | } }><i class="far fa-eye"></i></button> | ||||
| { item[1].toLowerCase().endsWith('.nii') ? ( | |||||
| <button class="btn btn-outline-primary mx-1" title="View Image" | |||||
| onclick={ () => manifestWorker.postMessage([ 'getFile', | |||||
| '.' + collectionPath + '/' + item[1] ]).then(e => { | |||||
| const file = e.data[1]; | |||||
| const blob = new Blob([ | |||||
| JSON.stringify({ 'name': item[1], 'file': file }) | |||||
| ]); | |||||
| const blocksBlobUrl = URL.createObjectURL(blob); | |||||
| window.open('/image-viewer/' + encodeURIComponent(blocksBlobUrl), '_blank'); | |||||
| }) }><i class="far fa-eye"></i></button> | |||||
| ) : null } | |||||
| </div> | </div> | ||||
| ) : null) | ) : null) | ||||
| ] | ] | ||||
| @@ -12,6 +12,7 @@ import WBUsersPage from 'wb-users-page'; | |||||
| import WBWorkflowView from 'wb-workflow-view'; | import WBWorkflowView from 'wb-workflow-view'; | ||||
| import WBLaunchWorkflowPage from 'wb-launch-workflow-page'; | import WBLaunchWorkflowPage from 'wb-launch-workflow-page'; | ||||
| import WBDownloadPage from 'wb-download-page'; | import WBDownloadPage from 'wb-download-page'; | ||||
| import WBImageViewerPage from 'wb-image-viewer-page'; | |||||
| import arvadosTypeName from 'arvados-type-name'; | import arvadosTypeName from 'arvados-type-name'; | ||||
| class WBApp extends Component { | class WBApp extends Component { | ||||
| @@ -94,6 +95,8 @@ class WBApp extends Component { | |||||
| <WBLaunchWorkflowPage path="/workflow-launch/:workflowUuid" app={ this } /> | <WBLaunchWorkflowPage path="/workflow-launch/:workflowUuid" app={ this } /> | ||||
| <WBDownloadPage path="/download/:blocksBlobUrl/:inline?" app={ this } /> | <WBDownloadPage path="/download/:blocksBlobUrl/:inline?" app={ this } /> | ||||
| <WBImageViewerPage path="/image-viewer/:blobUrl" app={ this } /> | |||||
| </Router> | </Router> | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -0,0 +1,103 @@ | |||||
| import { h, Component } from 'preact'; | |||||
| import makeArvadosRequest from 'make-arvados-request'; | |||||
| function downloadFile(arvHost, arvToken, file) { | |||||
| const blockRefs = file[0]; | |||||
| let prom = makeArvadosRequest(arvHost, arvToken, | |||||
| '/arvados/v1/keep_services'); | |||||
| let proxy; | |||||
| prom = prom.then(xhr => { | |||||
| const services = xhr.response['items']; | |||||
| const proxies = services.filter(svc => (svc.service_type === 'proxy')); | |||||
| const n = Math.floor(Math.random() * proxies.length); | |||||
| proxy = proxies[n]; | |||||
| }); | |||||
| const blocks = []; | |||||
| for (let i = 0; i < blockRefs.length; i++) { | |||||
| let locator, start, end; | |||||
| prom = prom.then(() => { | |||||
| [ locator, start, end ] = blockRefs[i]; | |||||
| return makeArvadosRequest( | |||||
| proxy.service_host + ':' + proxy.service_port, | |||||
| arvToken, '/' + locator, | |||||
| { 'useSsl': proxy.service_ssl_flag, | |||||
| 'responseType': 'arraybuffer' } | |||||
| ); | |||||
| }); | |||||
| prom = prom.then(xhr => blocks.push(xhr.response.slice(start, end))); | |||||
| } | |||||
| prom = prom.then(() => { | |||||
| const url = URL.createObjectURL(new Blob(blocks)); | |||||
| const totalSize = blocks.reduce((a, b) => a.length + b.length); | |||||
| const big = new Uint8Array(totalSize); | |||||
| for (let i = 0, pos = 0; i < blocks.length; i++) { | |||||
| big.set(blocks[i], pos); | |||||
| pos += blocks[i].length; | |||||
| } | |||||
| // papayaContainers[0].startPapaya(); | |||||
| const poll = () => { | |||||
| setTimeout(() => { | |||||
| console.log('Polling Papaya startup...') | |||||
| if (true) { // window.papayaContainers && window.papayaContainers[0]) { | |||||
| console.log('Great, Papaya started!'); | |||||
| papaya.Container.startPapaya(); | |||||
| //papaya.Container.addImage(0, big.buffer); | |||||
| document.body.id = "bod"; | |||||
| document.body.style.background = "#555"; | |||||
| papaya.Container.addViewer("bod", { | |||||
| 'binaryImages': [ big.buffer ], | |||||
| 'noNewFiles': true, | |||||
| }); | |||||
| } else | |||||
| poll(); | |||||
| }, 1000); | |||||
| }; | |||||
| //setTimeout(poll, 10000); | |||||
| poll(); | |||||
| }); | |||||
| } | |||||
| class WBImageViewerPage extends Component { | |||||
| componentDidMount() { | |||||
| const { blobUrl, app } = this.props; | |||||
| const { arvHost, arvToken } = app.state; | |||||
| let prom = new Promise((accept, reject) => { | |||||
| const xhr = new XMLHttpRequest(); | |||||
| xhr.open('GET', blobUrl); | |||||
| xhr.onreadystatechange = () => { | |||||
| if (xhr.readyState !== 4) return; | |||||
| if (xhr.status !== 200) reject(xhr); | |||||
| else accept(xhr); | |||||
| }; | |||||
| xhr.responseType = 'blob'; | |||||
| xhr.send(); | |||||
| }); | |||||
| prom = prom.then(xhr => xhr.response.text()); | |||||
| prom = prom.then(data => { | |||||
| data = JSON.parse(data); | |||||
| downloadFile(arvHost, arvToken, data.file); | |||||
| }); | |||||
| } | |||||
| render() { | |||||
| return ( | |||||
| <div> | |||||
| <script language="javascript" src="/js/papaya.js"></script> | |||||
| <div id="papaya" style="width: auto; height: 100%; margin: 0;"></div> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| } | |||||
| export default WBImageViewerPage; | |||||