Skip to content
Browse files

First fully working version of the Dropbox sharing feature.

Also, upgrade to a newer version of XTK.
  • Loading branch information...
1 parent bd9ff65 commit 5af0fbbeb26b9d8b4199ac7a009bb88a98a24088 @haehn haehn committed Feb 24, 2013
Showing with 877 additions and 337 deletions.
  1. +8 −3 css/viewer.css
  2. +3 −1 index.html
  3. +36 −21 js/jquery.frontpage.js
  4. +4 −0 js/x.rendering.js
  5. +197 −0 js/x.scene.js
  6. +344 −27 js/x.share.js
  7. +285 −285 js/xtk.js
View
11 css/viewer.css
@@ -34,11 +34,16 @@
#share {
position: absolute;
- bottom: 10px;
- right: 30%;
+ top: 10px;
+ right: 25%;
+ width: 250px;
font-size: 10px;
cursor: pointer;
- opacity: 0.7;
+ background: rgba(0,0,0,0.3);
+}
+
+#sharemsg {
+ margin-left: 40px;
}
.threeDRenderer {
View
4 index.html
@@ -68,6 +68,7 @@
<script type='text/javascript' src='js/x.controller.js'></script>
<script type='text/javascript' src='js/x.examples.js'></script>
<script type='text/javascript' src='js/x.rendering.js'></script>
+<script type='text/javascript' src='js/x.scene.js'></script>
<script type='text/javascript' src='js/x.share.js'></script>
<script type='text/javascript' src='js/xtk.js'></script>
@@ -435,7 +436,8 @@
</div>
<div id='share'>
- <img src='gfx/dropbox.png'/>
+ <img id='sharelogo' src='gfx/dropbox.png'/>
+ <div id='sharemsg' style='display:none'></div>
</div>
</div>
View
57 js/jquery.frontpage.js
@@ -48,36 +48,51 @@ jQuery(document).ready(function() {
};
- // parse the url for variables which trigger demos immediately
- if (location.href.match(/(\?)(\w*.\w*)*/)) {
+ // from http://stackoverflow.com/a/7826782/1183453
+ var args = document.location.search.substring(1).split('&');
+ argsParsed = {};
+ for (var i=0; i < args.length; i++)
+ {
+ arg = unescape(args[i]);
+
+ if (arg.length == 0) continue;
+
+ if (arg.indexOf('=') == -1)
+ {
+ argsParsed[arg.replace(new RegExp('/$'),'').trim()] = true;
+ }
+ else
+ {
+ kvp = arg.split('=');
+ argsParsed[kvp[0].trim()] = kvp[1].replace(new RegExp('/$'),'').trim();
+ }
+ }
+
+ if ('14yrold' in argsParsed) {
- // this is any file
- var _file = location.href.match(/(\?)(\w*.\w*)*/)[0];
- _file = _file.replace('?', ''); // replace any ?
-
- // only replace the last /
- _file = _file.replace(new RegExp('/$'),'');
-
- if (_file == '14yrold') {
-
- load14yrold();
-
- } else if (_file == 'avf') {
+ load14yrold();
- loadAvf();
+ } else if ('avf' in argsParsed) {
- } else if (_file == '2yrold') {
+ loadAvf();
- load2yrold();
+ } else if ('2yrold' in argsParsed) {
- } else if (_file == 'brainstem') {
+ load2yrold();
- loadBrainstem();
+ } else if ('brainstem' in argsParsed) {
- } else {
+ loadBrainstem();
- loadFile(_file);
+ } else if ('scene' in argsParsed) {
+
+ console.log('Found scene ' + argsParsed['scene']);
+ loadScene(argsParsed['scene']);
+
+ } else {
+ for (var a in argsParsed) {
+ loadFile(a);
}
}
View
4 js/x.rendering.js
@@ -532,6 +532,10 @@ function onTouchEnd(rend,container) {
_touch_ended = Date.now();
+ if (typeof _touch_started == 'undefined') {
+ _touch_started = _touch_ended;
+ }
+
if (_touch_ended - _touch_started < 200) {
var _old_2d_content = eval('_current_' + container + '_content');
View
197 js/x.scene.js
@@ -0,0 +1,197 @@
+function loadScene(sceneUrl) {
+
+ // grab the JSON scene
+ $.ajax({
+ url: sceneUrl
+ }).done(function(scene) {
+
+ // now switch to the viewer
+ switchToViewer();
+
+ console.log(scene);
+
+ // init renderers
+ initializeRenderers();
+ createData();
+
+ if (scene.volume.file.length > 0) {
+
+ volume = new X.volume();
+ volume.file = scene.volume.file;
+ _data.volume.file = volume.file;
+
+ if (scene.labelmap.file.length > 0) {
+
+ volume.labelmap.file = scene.labelmap.file[0];
+ _data.labelmap.file = volume.labelmap.file;
+
+ if (scene.colortable.file.length > 0) {
+
+ volume.labelmap.colortable.file = scene.colortable.file[0];
+ _data.colortable.file = volume.labelmap.colortable.file;
+
+ }
+
+ }
+
+ ren3d.add(volume);
+
+ }
+
+ if (scene.mesh.file.length > 0) {
+
+ mesh = new X.mesh();
+ mesh.file = scene.mesh.file;
+ _data.mesh.file = mesh.file;
+
+ if (scene.scalars.file.length > 0) {
+
+ mesh.scalars.file = scene.scalars.file[0];
+ _data.scalars.file = mesh.scalars.file;
+
+ }
+
+ ren3d.add(mesh);
+
+ }
+
+ if (scene.fibers.file.length > 0) {
+
+ fibers = new X.fibers();
+ fibers.file = scene.fibers.file;
+ _data.fibers.file = fibers.file;
+ ren3d.add(fibers);
+
+ }
+
+ ren3d.render();
+
+ configurator = function() {
+
+ // restore the cameras
+ ren3d.camera.view = new Float32Array(scene.camera.ren3d);
+ sliceX.camera.view = new Float32Array(scene.camera.sliceX);
+ sliceY.camera.view = new Float32Array(scene.camera.sliceY);
+ sliceZ.camera.view = new Float32Array(scene.camera.sliceZ);
+
+ //
+ // restore the volume settings
+ //
+ if (_data.volume.file.length > 0) {
+
+ volume.indexX = scene.volume.indexX;
+ volume.indexY = scene.volume.indexY;
+ volume.indexZ = scene.volume.indexZ;
+ jQuery("#yellow_slider").slider("option", "value", volume.indexX);
+ jQuery("#red_slider").slider("option", "value", volume.indexY);
+ jQuery("#green_slider").slider("option", "value", volume.indexZ);
+
+ if (scene.volume.volumeRendering) {
+ volume.volumeRendering = true;
+ jQuery('#slicing').removeClass('ui-state-active');
+ jQuery('#volumerendering').addClass('ui-state-active');
+ jQuery('#windowlevel-label').hide();
+ jQuery('#windowlevel-volume').hide();
+ jQuery('#opacity-label').show();
+ jQuery('#opacity-volume').show();
+ }
+
+ volume.lowerThreshold = scene.volume.lowerThreshold;
+ volume.upperThreshold = scene.volume.upperThreshold;
+ jQuery('#threshold-volume').dragslider("option", "values", [volume.lowerThreshold, volume.upperThreshold]);
+
+ volume.windowLow = scene.volume.windowLow;
+ volume.windowHigh = scene.volume.windowHigh;
+ jQuery('#windowlevel-volume').dragslider("option", "values", [volume.windowLow, volume.windowHigh]);
+
+ volume.opacity = scene.volume.opacity;
+ jQuery('#opacity-volume').slider("option", "value", volume.opacity * 100);
+
+ volume.minColor = scene.volume.minColor;
+ var bgColor = ((1 << 24) + (volume.minColor[0] * 255 << 16) +
+ (volume.minColor[1] * 255 << 8) + volume.minColor[2] * 255)
+ .toString(16).substr(1);
+
+ volume.maxColor = scene.volume.maxColor;
+ var fgColor = ((1 << 24) + (volume.maxColor[0] * 255 << 16) +
+ (volume.maxColor[1] * 255 << 8) + volume.maxColor[2] * 255)
+ .toString(16).substr(1);
+
+ jQuery('#bgColorVolume').miniColors("value", bgColor);
+ jQuery('#fgColorVolume').miniColors("value", fgColor);
+
+ }
+
+ // restore the labelmap settings
+ if (_data.labelmap.file.length > 0) {
+ volume.labelmap.visible = scene.labelmap.visible;
+ if (!volume.labelmap.visible) {
+ $('#labelmapvisibility').removeClass('show-icon');
+ $('#labelmapvisibility').addClass('hide-icon');
+ }
+ volume.labelmap.opacity = scene.labelmap.opacity;
+ jQuery('#opacity-labelmap').slider("option", "value", volume.labelmap.opacity * 100);
+ }
+
+ //
+ // restore the mesh settings
+ //
+ if (_data.mesh.file.length > 0) {
+ mesh.visible = scene.mesh.visible;
+ if (!mesh.visible) {
+ $('#meshvisibility').removeClass('show-icon');
+ $('#meshvisibility').addClass('hide-icon');
+ }
+ mesh.opacity = scene.mesh.opacity;
+ jQuery('#opacity-mesh').slider("option", "value", mesh.opacity * 100);
+
+ mesh.color = scene.mesh.color;
+ var meshColor = ((1 << 24) + (mesh.color[0] * 255 << 16) +
+ (mesh.color[1] * 255 << 8) + mesh.color[2] * 255)
+ .toString(16).substr(1);
+ jQuery('#meshColor').miniColors("value", meshColor);
+
+ }
+
+ // restore the scalars settings
+ if (_data.scalars.file.length > 0) {
+ mesh.scalars.lowerThreshold = scene.scalars.lowerThreshold;
+ mesh.scalars.upperThreshold = scene.scalars.upperThreshold;
+ jQuery("#threshold-scalars").dragslider("option", "values",
+ [mesh.scalars.lowerThreshold * 100, mesh.scalars.upperThreshold * 100]);
+
+ mesh.scalars.minColor = scene.scalars.minColor;
+ var scalarsminColor = ((1 << 24) + (mesh.scalars.minColor[0] * 255 << 16) +
+ (mesh.scalars.minColor[1] * 255 << 8) + mesh.scalars.minColor[2] * 255)
+ .toString(16).substr(1);
+ jQuery('#scalarsMinColor').miniColors("value", scalarsminColor);
+
+ mesh.scalars.maxColor = scene.scalars.maxColor;
+ var scalarsmaxColor = ((1 << 24) + (mesh.scalars.maxColor[0] * 255 << 16) +
+ (mesh.scalars.maxColor[1] * 255 << 8) + mesh.scalars.maxColor[2] * 255)
+ .toString(16).substr(1);
+ jQuery('#scalarsMaxColor').miniColors("value", scalarsmaxColor);
+
+ }
+
+ //
+ // restore the fiber settings
+ //
+ if (_data.fibers.file.length > 0) {
+ fibers.visible = scene.fibers.visible;
+ if (!fibers.visible) {
+ $('#fibersvisibility').removeClass('show-icon');
+ $('#fibersvisibility').addClass('hide-icon');
+ }
+
+ fibers.scalars.lowerThreshold = scene.fibers.lowerThreshold;
+ fibers.scalars.upperThreshold = scene.fibers.upperThreshold;
+ jQuery('#threshold-fibers').dragslider("option", "values", [fibers.scalars.lowerThreshold, fibers.scalars.upperThreshold]);
+
+ }
+
+ };
+
+ });
+
+}
View
371 js/x.share.js
@@ -1,53 +1,369 @@
function initialize_sharing() {
- $('#share').click(share);
+ $('#sharelogo').click(share);
};
+// entry point when user clicks on "share via Dropbox"
function share() {
- client = new Dropbox.Client({
- key: "Q60cVmMyg/A=|pQC3I1F1qW3kRyF3Q2s78saA2VpYpQQwBez7IsFKgQ==",
- sandbox: true
+ var client = new Dropbox.Client({
+ key : "Q60cVmMyg/A=|pQC3I1F1qW3kRyF3Q2s78saA2VpYpQQwBez7IsFKgQ==",
+ sandbox : true
});
+ // first step is authorization with Dropbox
+ authorize(client);
+
+}
+
+function authorize(client) {
+
client.authDriver(new Dropbox.Drivers.Popup({
- receiverUrl: "http://chris/d/slicedrop.github.com/loginok.html",
- useQuery: true
+ receiverUrl : "http://slicedrop.com/loginok.html",
+ useQuery : true
}));
+ $('#sharemsg').show();
+ $('#sharemsg').html('Authorizing.. (Please enable pop-ups!)');
+
client.authenticate(function(error, client) {
- if (error) {
- // Replace with a call to your own error-handling code.
- //
- // Don't forget to return from the callback, so you don't execute the code
- // that assumes everything went well.
+ if ( error )
+ return showError(error);
+
+ // second step is to display user information
+ displayUserInfo(client);
+
+ });
+
+}
+
+function displayUserInfo(client) {
+
+ client.getUserInfo(function(error, userInfo) {
+
+ if ( error )
+ return showError(error);
+
+ $('#sharemsg').html('Hi, ' + userInfo.name + '!');
+
+ // third step is to create the scene folder
+ createSceneFolder(client);
+
+ });
+
+}
+
+function createSceneFolder(client) {
+
+ // create the scene folder (date and time stamped)
+ var now = new Date();
+ var foldername = now.getFullYear() + '-' + (now.getMonth() + 1) + '-'
+ + now.getDate() + '_' + now.getHours() + '-' + now.getMinutes() + '-'
+ + now.getSeconds();
+ client.mkdir(foldername, function(error) {
+
+ if ( error )
return showError(error);
+
+ // next step is to upload data
+ uploadData(client, foldername);
+
+ });
+
+}
+
+function uploadData(client, foldername) {
+
+ // now schedule the data to upload
+ var _toUpload = [];
+
+ for ( var d in _data) {
+
+ for ( var f in _data[d].filedata) {
+
+ var _filename = _data[d].file[f].name;
+
+ // if there is no file extension, be sure to pass .DCM
+ // since it is DICOM
+ var _fileExtension = _filename.split('.').pop().toUpperCase();
+
+ // check for files with no extension
+ if ( _fileExtension == _filename.toUpperCase() ) {
+
+ // this must be dicom
+ _filename += '.DCM';
+
+ }
+
+ _toUpload.push([ d, _filename, _data[d].file[f] ]);
+
}
- // Replace with a call to your own application code.
- //
- // The user authorized your app, and everything went well.
- // client is a Dropbox.Client instance that you can use to make API calls.
- client.getUserInfo(function(error, userInfo) {
+ }
+
+ if ( _toUpload.length == 0 ) {
+
+ // no data to upload
+ $('#sharemsg').html('Examples can not be shared.');
+ return;
+
+ }
+
+ var _pending = _toUpload.length;
+
+ $('#sharemsg').html($('#sharemsg').html() + '<br>Uploading... ');
+
+ // do the upload
+ for ( var u in _toUpload) {
+
+ u = _toUpload[u];
+
+ // write data
+ client.writeFile(foldername + '/' + u[1], u[2], function(error, stat) {
+
+ if ( error )
+ return showError(error);
+
+ _pending--;
+
+ if ( _pending == 0 ) {
+
+ // all data files uploaded
+ $('#sharemsg').html($('#sharemsg').html() + 'Done!');
+ grabSlicedrop(client, foldername, _toUpload);
- if (error) {
- return showError(error); // Something went wrong.
}
- console.log(userInfo.name);
- //window.document.body.innerHTML += "Hello, " + userInfo.name + "!<br><br>";
});
+ }
+
+}
+function grabSlicedrop(client, foldername, _toUpload) {
+
+ // first we store the current version of the slicedrop web app with the data
+ $.ajax({
+ url : window.location.href,
+ cache : false
+ }).done(function(html) {
+
+ // replace paths to point to slicedrop.com
+ html = html.replace(/'css/g, "'http://slicedrop.com/css");
+ html = html.replace(/'js/g, "'http://slicedrop.github.com/js");
+ html = html.replace(/'gfx/g, "'http://slicedrop.com/gfx");
+
+ client.writeFile(foldername + '/index.html', html, function(error, stat) {
+
+ if ( error )
+ return showError(error);
+
+ // now write the JSON scene
+ writeScene(client, foldername, _toUpload);
+
+ });
});
-};
+}
+
+/**
+ * Here we create the scene which later gets parsed by Slice:Drop again.
+ */
+function writeScene(client, foldername, _toUpload) {
+
+ // then we store a JSON scene
+ var _scene = {
+ camera : {
+ ren3d : Array.apply([], ren3d.camera.view),
+ sliceX : Array.apply([], sliceX.camera.view),
+ sliceY : Array.apply([], sliceY.camera.view),
+ sliceZ : Array.apply([], sliceZ.camera.view)
+ },
+ volume : {
+ file : []
+ },
+ labelmap : {
+ file : []
+ },
+ colortable : {
+ file : []
+ },
+ mesh : {
+ file : []
+ },
+ scalars : {
+ file : []
+ },
+ fibers : {
+ file : []
+ }
+ };
+
+ // store properties regarding volume, mesh, fibers
+ if ( _data.volume.file.length > 0 ) {
+
+ _scene.volume.indexX = volume.indexX;
+ _scene.volume.indexY = volume.indexY;
+ _scene.volume.indexZ = volume.indexZ;
+ _scene.volume.lowerThreshold = volume.lowerThreshold;
+ _scene.volume.upperThreshold = volume.upperThreshold;
+ _scene.volume.opacity = volume.opacity;
+ _scene.volume.windowLow = volume.windowLow;
+ _scene.volume.windowHigh = volume.windowHigh;
+ _scene.volume.minColor = volume.minColor;
+ _scene.volume.maxColor = volume.maxColor;
+ _scene.volume.volumeRendering = volume.volumeRendering;
+
+ }
+
+ if ( _data.labelmap.file.length > 0 ) {
+ _scene.labelmap.visible = volume.labelmap.visible;
+ _scene.labelmap.opacity = volume.labelmap.opacity;
+ }
+
+ if ( _data.mesh.file.length > 0 ) {
+ _scene.mesh.visible = mesh.visible;
+ _scene.mesh.opacity = mesh.opacity;
+ _scene.mesh.color = mesh.color;
+ }
+
+ if ( _data.scalars.file.length > 0 ) {
+ _scene.scalars.lowerThreshold = mesh.scalars.lowerThreshold;
+ _scene.scalars.upperThreshold = mesh.scalars.upperThreshold;
+ _scene.scalars.minColor = mesh.scalars.minColor;
+ _scene.scalars.maxColor = mesh.scalars.maxColor;
+ }
+
+ if ( _data.fibers.file.length > 0 ) {
+ _scene.fibers.visible = fibers.visible;
+ _scene.fibers.lowerThreshold = fibers.scalars.lowerThreshold;
+ _scene.fibers.upperThreshold = fibers.scalars.upperThreshold;
+ }
+
+ //
+ // now attach the files (as urls)..
+ //
+ var _pending = _toUpload.length;
+
+ // special case to detect a labelmap
+ var labelmap = '';
+ if ( _data.labelmap.file.length > 0 ) {
+ labelmap = _data.labelmap.file[0].name;
+ }
+
+ for ( var u in _toUpload) {
+
+ u = _toUpload[u];
+
+ // generate the urls for each file
+ client.makeUrl(foldername + '/' + u[1], {
+ downloadHack : true
+ }, function(error, url) {
+
+ if ( error )
+ return showError(error);
+
+ var _url = url.url;
+
+ var type = '';
+
+ // check if this is a labelmap
+ if ( labelmap != '' ) {
+
+ // compare the filename
+ if ( labelmap == _url.split('/').pop() ) {
+ type = 'labelmap';
+ }
+
+ }
+
+ if ( type == '' ) {
+
+ // we need to re-identify the type for scene storage
+ var _fileExtension = _url.split('.').pop().toUpperCase();
+
+ for ( var d in _data) {
+
+ if ( _data[d]['extensions'].indexOf(_fileExtension) >= 0 ) {
+ // found the type
+ type = d;
+ break;
+
+ }
+
+ }
+
+ }
+
+ // .. and store them
+ _scene[type].file.push(_url);
+
+ _pending--;
+
+ if ( _pending == 0 ) {
+
+ // all urls are collected, we write now the JSON scene
+ var _sceneJSON = JSON.stringify(_scene);
+ // HTTPS won't work without a warning during loading
+ _sceneJSON = _sceneJSON.replace(/https/g, 'http');
+
+ client.writeFile(foldername + '/scene.json', _sceneJSON, function(
+ error, stat) {
+
+ if ( error )
+ return showError(error);
+
+ client.makeUrl(foldername + '/scene.json', {
+ downloadHack : true
+ }, function(error, url) {
+
+ $('#sharemsg').html($('#sharemsg').html() + '<br>Scene stored!');
+
+ var _sceneUrl = url.url;
+
+ client.makeUrl(foldername + '/index.html', {
+ downloadHack : true
+ }, function(error, url) {
+
+ if ( error )
+ return showError(error);
+
+ createShortURL(url.url + '?scene=' + _sceneUrl);
+
+ });
+
+ });
+
+ });
+
+ }
+
+ });
+
+ }
+
+}
+
+function createShortURL(url) {
+ url = url.replace(/https/g, 'http');
+ // query for a short URL
+ $.ajax({
+ url : 'http://jvf.li/api/shorten?longurl=' + encodeURIComponent(url)
+ }).done(
+ function(shorturl) {
+ // display the short URL
+ $('#sharemsg').html(
+ $('#sharemsg').html() + '<br><a href="' + shorturl
+ + '" target=_blank><span style="font-size:14px;color:red;">'
+ + shorturl + '</span></a>');
+ });
+}
var showError = function(error) {
@@ -56,32 +372,32 @@ var showError = function(error) {
// If you're using dropbox.js, the only cause behind this error is that
// the user token expired.
// Get the user through the authentication flow again.
- console.log('INVALID TOKEN');
+ $('#sharemsg').html('Authorization failed!');
break;
case Dropbox.ApiError.NOT_FOUND:
// The file or folder you tried to access is not in the user's Dropbox.
// Handling this error is specific to your application.
- console.log('NOT FOUND');
+ $('#sharemsg').html('File not found!');
break;
case Dropbox.ApiError.OVER_QUOTA:
// The user is over their Dropbox quota.
// Tell them their Dropbox is full. Refreshing the page won't help.
- console.log('OVER QUOTA');
+ $('#sharemsg').html('Your dropbox is full!');
break;
case Dropbox.ApiError.RATE_LIMITED:
// Too many API requests. Tell the user to try again later.
// Long-term, optimize your code to use fewer API calls.
- console.log('RATE LIMITED');
+ $('#sharemsg').html('Too many API calls!');
break;
case Dropbox.ApiError.NETWORK_ERROR:
// An error occurred at the XMLHttpRequest layer.
// Most likely, the user's network connection is down.
// API calls will not succeed until the user gets back online.
- console.log('NETWORK ERROR');
+ $('#sharemsg').html('Network error!');
break;
case Dropbox.ApiError.INVALID_PARAM:
@@ -90,6 +406,7 @@ var showError = function(error) {
default:
// Caused by a bug in dropbox.js, in your application, or in Dropbox.
// Tell the user an error occurred, ask them to refresh the page.
- console.log('ERROR');
+ $('#sharemsg').html('There was an error!');
}
+ $('#sharemsg').html($('#sharemsg').html()+'<br>Please try again!');
};
View
570 js/xtk.js
285 additions, 285 deletions not shown because the diff is too large. Please use a local Git client to view these changes.

0 comments on commit 5af0fbb

Please sign in to comment.
Something went wrong with that request. Please try again.