diff options
-rw-r--r-- | share/static/css/style.css | 72 | ||||
-rw-r--r-- | share/static/img/ajax.gif | bin | 0 -> 847 bytes | |||
-rw-r--r-- | share/static/js/receive-noscript.js | 2 | ||||
-rw-r--r-- | share/static/js/receive.js | 218 | ||||
-rw-r--r-- | share/templates/receive.html | 69 |
5 files changed, 226 insertions, 135 deletions
diff --git a/share/static/css/style.css b/share/static/css/style.css index 73d3e6f9..65bafba5 100644 --- a/share/static/css/style.css +++ b/share/static/css/style.css @@ -9,7 +9,7 @@ body { margin: 0; - font-family: Helvetica; + font-family: Helvetica, sans-serif; } header { @@ -103,58 +103,96 @@ table.file-list td:last-child { } .upload-wrapper { - display: flex; align-items: center; justify-content: center; min-height: 400px; -} - -.upload { text-align: center; } -.upload img { +.upload-wrapper img.logo { width: 120px; height: 120px; } -.upload .upload-header { +.upload-wrapper .upload-header { font-size: 30px; font-weight: normal; color: #666666; margin: 0 0 10px 0; } -.upload .upload-description { +.upload-wrapper .upload-description { color: #666666; margin: 0 0 20px 0; } +div#uploads { + width: 800px; + max-width: 90%; + margin: 0 auto; +} + +div#uploads .upload { + border: 1px solid #DDDDDD; + margin: 20px 0; + padding: 10px; + text-align: left; +} + +div#uploads .upload .upload-filename { + font-weight: bold; + font-family: monospace; + font-size: 1.1em; + margin-bottom: 5px; +} + +div#uploads .upload .upload-status { + color: #999999; + font-size: 0.9em; + margin-bottom: 5px; +} + +div#uploads .upload input.cancel { + color: #f24537; + border: 0; + background: none; + box-shadow: none; + border-radius: 0px; + cursor: pointer; + font-family: sans-serif; + font-size: 12px; + text-decoration: none; + display: inline-block; + float:right; +} + +div#uploads .upload progress { + width: 100%; + height: 20px; +} + ul.flashes { list-style: none; margin: 0; padding: 0; - color: #cc0000; - text-align: left; + width: 800px; + max-width: 90%; + margin: 0 auto; } ul.flashes li { margin: 0 0 5px 0; - padding: 10px; + padding: 5px; list-style: none; - border: 0; - border-radius: 3px; text-align: left; } li.error { - color: #ffffff; - background-color: #c90c0c; + color: #dd4040; } li.info { - color: #000000; - background-color: #a9e26c; + color: #3e933f; } .closed-wrapper { diff --git a/share/static/img/ajax.gif b/share/static/img/ajax.gif Binary files differnew file mode 100644 index 00000000..01d955aa --- /dev/null +++ b/share/static/img/ajax.gif diff --git a/share/static/js/receive-noscript.js b/share/static/js/receive-noscript.js new file mode 100644 index 00000000..0f4ac1bc --- /dev/null +++ b/share/static/js/receive-noscript.js @@ -0,0 +1,2 @@ +// Hide the noscript div, because our javascript is executing +document.getElementById('noscript').style.display = 'none'; diff --git a/share/static/js/receive.js b/share/static/js/receive.js index 45cbc9dc..0de80952 100644 --- a/share/static/js/receive.js +++ b/share/static/js/receive.js @@ -1,94 +1,140 @@ -// Hide the noscript div, because our javascript is executing -document.getElementById('noscript').style.display = 'none'; - -var form = document.getElementById('send'); -var fileSelect = document.getElementById('file-select'); -var uploadButton = document.getElementById('send-button'); -var flashes = document.getElementById('flashes'); - -// Add a flash message -function flash(category, message) { - var el = document.createElement('li'); - el.innerText = message; - el.className = category; - flashes.appendChild(el); -} - -form.onsubmit = function(event) { - event.preventDefault(); - - // Disable button, and update text - uploadButton.innerHTML = 'Uploading ...'; - uploadButton.disabled = true; - fileSelect.disabled = true; - - // Create form data - var files = fileSelect.files; - var formData = new FormData(); - for (var i = 0; i < files.length; i++) { - var file = files[i]; - formData.append('file[]', file, file.name); - } - - // Set up the request - var ajax = new XMLHttpRequest(); - - ajax.upload.addEventListener('progress', function(event){ - console.log('upload progress', 'uploaded '+event.loaded+' bytes / '+event.total+' bytes'); - var percent = parseInt((event.loaded / event.total) * 100, 10); - uploadButton.innerHTML = 'Uploading '+percent+'%'; - }, false); - - ajax.addEventListener('load', function(event){ - console.log('upload finished', ajax.response); - if(ajax.status == 200) { - // Parse response - try { - var response = JSON.parse(ajax.response); - - // The 'new_body' response replaces the whole HTML document and ends - if('new_body' in response) { - document.body.innerHTML = response['new_body']; - return; +$(function(){ + // Add a flash message + var flash = function(category, message) { + $('#flashes').append($('<li>').addClass(category).text(message)); + }; + + // Add an upload + var new_upload_div = function(xhr, filenames) { + /* + The DOM for an upload looks something like this: + + <div class="upload"> + <div class="upload-meta"> + <input class="cancel" type="button" value="Cancel" /> + <div class="upload-filename">educational-video.mp4, secret-plans.pdf</div> + <div class="upload-status">Sending to first Tor node ...</div> + </div> + <progress value="25" max="100"></progress> + </div> + */ + var $progress = $('<progress>').attr({ value: '0', max: 100 }); + var $cancel_button = $('<input>').addClass('cancel').attr({ type: 'button', value: 'Cancel' }); + var $upload_filename = $('<div>').addClass('upload-filename').text(filenames.join(', ')); + var $upload_status = $('<div>').addClass('upload-status').text('Sending data to initial Tor node ...'); + + var $upload_div = $('<div>').addClass('upload') + .append( + $('<div>').addClass('upload-meta') + .append($cancel_button) + .append($upload_filename) + .append($upload_status) + ) + .append($progress); + + $cancel_button.click(function(){ + // Abort the upload, and remove the upload div + xhr.abort(); + $upload_div.remove() + }); + + return $upload_div; + }; + + // Intercept submitting the form + $('#send').submit(function(event){ + event.preventDefault(); + + // Create form data, and list of filenames + var files = $('#file-select').get(0).files; + var filenames = []; + var formData = new FormData(); + for(var i = 0; i < files.length; i++) { + var file = files[i]; + filenames.push(file.name); + formData.append('file[]', file, file.name); + } + + // Reset the upload form + $('#send').get(0).reset(); + + // Start upload + xhr = $.ajax({ + method: 'POST', + url: window.location.pathname + '/upload-ajax', + data: formData, + // Tell jQuery not to process data or worry about content-type + cache: false, + contentType: false, + processData: false, + // Custom XMLHttpRequest + xhr: function() { + var xhr = $.ajaxSettings.xhr(); + if(xhr.upload) { + xhr.upload.addEventListener('progress', function(event) { + // Update progress bar for this specific upload + if(event.lengthComputable) { + console.log('upload progress', ''+event.loaded+' bytes / '+event.total+' bytes'); + $('progress', this.$upload_div).attr({ + value: event.loaded, + max: event.total, + }); + } + + // If it's finished sending all data to the first Tor node, remove cancel button + // and update the status + if(event.loaded == event.total) { + console.log('upload progress', 'complete'); + $('.cancel', this.$upload_div).remove(); + $('.upload-status', this.$upload_div).html('<img src="/static/img/ajax.gif" alt="" /> Waiting for data to finish traversing Tor network ...'); + } + }, false); } + return xhr; + }, + success: function(data, textStatus, xhr){ + console.log('upload finished', data); + + // Remove the upload div + xhr.$upload_div.remove(); + + // Parse response + try { + var response = JSON.parse(data); - // Show error flashes - if('error_flashes' in response) { - for(var i=0; i<response['error_flashes'].length; i++) { - flash('error', response['error_flashes'][i]); + // The 'new_body' response replaces the whole HTML document and ends + if('new_body' in response) { + $('body').html(response['new_body']); + return; + } + + // Show error flashes + if('error_flashes' in response) { + for(var i=0; i<response['error_flashes'].length; i++) { + flash('error', response['error_flashes'][i]); + } } - } - // Show info flashes - if('info_flashes' in response) { - for(var i=0; i<response['info_flashes'].length; i++) { - flash('info', response['info_flashes'][i]); + // Show info flashes + if('info_flashes' in response) { + for(var i=0; i<response['info_flashes'].length; i++) { + flash('info', response['info_flashes'][i]); + } } + } catch(e) { + console.log('invalid response'); + flash('error', 'Invalid response from server: '+data); } - } catch(e) { - console.log('invalid response', ajax.response); - flash('error', 'Invalid response from server: '+ajax.response); + }, + error: function(xhr, textStatus, errorThrown){ + console.log('error', errorThrown); + flash('error', 'Error uploading: ' + errorThrown); } + }); + console.log('upload started', filenames); - // Re-enable button, and update text - uploadButton.innerHTML = 'Send Files'; - uploadButton.disabled = false; - fileSelect.disabled = false; - } - }, false); - - ajax.addEventListener('error', function(event){ - console.log('error', event); - flash('error', 'Error uploading'); - }, false); - - ajax.addEventListener('abort', function(event){ - console.log('abort', event); - flash('error', 'Upload aborted'); - }, false); - - // Send the request - ajax.open('POST', window.location.pathname + '/upload-ajax', true); - ajax.send(formData); - console.log('upload started'); -} + // Make the upload div + xhr.$upload_div = new_upload_div(xhr, filenames); + $('#uploads').append(xhr.$upload_div); + }); +}); diff --git a/share/templates/receive.html b/share/templates/receive.html index 65aeff7a..a557a967 100644 --- a/share/templates/receive.html +++ b/share/templates/receive.html @@ -13,41 +13,46 @@ </header> <div class="upload-wrapper"> - <div class="upload"> - <p><img class="logo" src="/static/img/logo_large.png" title="OnionShare"></p> - <p class="upload-header">Send Files</p> - <p class="upload-description">Select the files you want to send, then click "Send Files"...</p> - <form id="send" method="post" enctype="multipart/form-data" action="{{ upload_action }}"> - <p><input type="file" id="file-select" name="file[]" multiple /></p> - <p><button type="submit" id="send-button" class="button">Send Files</button></p> - <div> - <ul id="flashes" class="flashes"> - {% with messages = get_flashed_messages(with_categories=true) %} - {% if messages %} - {% for category, message in messages %} - <li class="{{ category }}">{{ message }}</li> - {% endfor %} - {% endif %} - {% endwith %} - </ul> - </div> - </form> - - <!-- - We are not using a <noscript> tag because it only works when the security slider is set to - Safest, not Safer: https://trac.torproject.org/projects/tor/ticket/29506 - --> - <div id="noscript"> - <p> - <strong>Warning:</strong> Due to a bug in Tor Browser and Firefox, uploads - sometimes never finish. To upload reliably, either set your Tor Browser - <a rel="noreferrer" target="_blank" href="https://tb-manual.torproject.org/en-US/security-slider/">security slider</a> - to Standard or - <a target="_blank" href="/noscript-xss-instructions">turn off your Tor Browser's NoScript XSS setting</a>.</p> - </div> + <p><img class="logo" src="/static/img/logo_large.png" title="OnionShare"></p> + <p class="upload-header">Send Files</p> + <p class="upload-description">Select the files you want to send, then click "Send Files"...</p> + + <form id="send" method="post" enctype="multipart/form-data" action="{{ upload_action }}"> + <p><input type="file" id="file-select" name="file[]" multiple /></p> + <p><button type="submit" id="send-button" class="button">Send Files</button></p> + </form> + + <div id="uploads"></div> + + <div> + <ul id="flashes" class="flashes"> + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + <li class="{{ category }}">{{ message }}</li> + {% endfor %} + {% endif %} + {% endwith %} + </ul> </div> + + <!-- + We are not using a <noscript> tag because it only works when the security slider is set to + Safest, not Safer: https://trac.torproject.org/projects/tor/ticket/29506 + --> + <div id="noscript"> + <p> + <strong>Warning:</strong> Due to a bug in Tor Browser and Firefox, uploads + sometimes never finish. To upload reliably, either set your Tor Browser + <a rel="noreferrer" target="_blank" href="https://tb-manual.torproject.org/en-US/security-slider/">security slider</a> + to Standard or + <a target="_blank" href="/noscript-xss-instructions">turn off your Tor Browser's NoScript XSS setting</a>.</p> + </div> + <script src="/static/js/receive-noscript.js"></script> + </div> + <script src="/static/js/jquery-3.3.1.min.js"></script> <script src="/static/js/receive.js"></script> </body> </html> |