@@ -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; |