From 65d75f2faf30903875097d7865139e7fc2dd9e62 Mon Sep 17 00:00:00 2001 From: sam Date: Mon, 30 Apr 2012 00:50:39 +0700 Subject: [PATCH] Loading bar works and encryption/decryption is now a serie of continuations --- static/css/style.css | 17 +++ static/js/behavior.js | 311 +++++++++++++++++++++++++++++++----------- views/base.tpl | 2 +- views/home.tpl | 3 + views/paste.tpl | 4 + 5 files changed, 253 insertions(+), 84 deletions(-) diff --git a/static/css/style.css b/static/css/style.css index fc551ba..0671396 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -165,3 +165,20 @@ text-decoration:underline; display:none; } +/** Progress bar */ + +.progress { + display:none; +} + +.progress .bar { + width: 25%; + text-indent: 10px; + text-align:left; +} + +-webkit-transition: width 0.1s ease; +-moz-transition: width 0.1s ease; +-ms-transition: width 0.1s ease; +-o-transition: width 0.1s ease; +transition: width 0.1s ease; \ No newline at end of file diff --git a/static/js/behavior.js b/static/js/behavior.js index c8d5c52..9d64b81 100644 --- a/static/js/behavior.js +++ b/static/js/behavior.js @@ -5,24 +5,119 @@ sjcl.random.startCollectors(); /* Ensure jquery use cache for ajax requests */ $.ajaxSetup({ cache: true }); - - zerobin = { - encrypt: function(key, content) { - content = sjcl.codec.base64.fromBits(sjcl.codec.utf8String.toBits(content)); - return sjcl.encrypt(key, lzw.compress(content)); + + /** Base64 + compress + encrypt, with callbacks before each operation, + and all of them are executed in a timed continuation to give + a change to the UI to respond. + */ + encrypt: function(key, content, toBase64Callback, + compressCallback, encryptCallback, doneCallback) { + + setTimeout (function(){ + + content = sjcl.codec.utf8String.toBits(content); + if (toBase64Callback) {toBase64Callback()} + + setTimeout(function(){ + + content = sjcl.codec.base64.fromBits(content); + if (compressCallback) {compressCallback()} + + setTimeout(function(){ + + content = lzw.compress(content); + if (encryptCallback) {encryptCallback()} + + setTimeout(function(){ + content = sjcl.encrypt(key, content); + if (doneCallback) {doneCallback(content)} + }, 250); + + }, 250); + + }, 250); + + }, 250); }, - decrypt: function(key, content) { - content = lzw.decompress(sjcl.decrypt(key, content)); - return sjcl.codec.utf8String.fromBits(sjcl.codec.base64.toBits(content)); + + /** Base64 decoding + uncompress + decrypt, with callbacks before each operation, + and all of them are executed in a timed continuation to give + a change to the UI to respond. + + This is where using a library to fake synchronicity could start to be + useful, this code is starting be difficult to read. If anyone read this + and got a suggestion, by all means, speak your mind. + */ + decrypt: function(key, content, errorCallback, uncompressCallback, + fromBase64Callback, toStringCallback, doneCallback) { + + /* Decrypt */ + setTimeout(function(){ + + try { + + content = sjcl.decrypt(key, content); + if (uncompressCallback) {uncompressCallback()} + + /* Decompress */ + setTimeout(function(){ + + try { + + content = lzw.decompress(content); + if (fromBase64Callback) {fromBase64Callback()} + + /* From base 64 to bits */ + setTimeout(function(){ + + try { + + content = sjcl.codec.base64.toBits(content); + if (toStringCallback) {toStringCallback()} + + /* From bits to string */ + setTimeout(function(){ + + try { + content = sjcl.codec.utf8String.fromBits(content); + if (doneCallback) {doneCallback(content)} + } catch (err) { + errorCallback(err); + } + + }, 250); /* "End of from bits to string" */ + + } catch (err) { + errorCallback(err); + } + + }, 250); /* End of "from base 64 to bits" */ + + } catch (err) { + errorCallback(err); + } + + }, 250); /* End of "decompress" */ + + } catch (err) { + errorCallback(err); + } + + }, 250); /* End of "decrypt" */ }, + + /** Create a random base64 string long enought to be suitable as + an encryption key */ make_key: function() { return sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0); }, + get_date: function(){ var date = new Date(); return date.getDate()+"-"+(date.getMonth()+1)+"-"+date.getFullYear(); }, + get_time: function(){ var date = new Date(); var h=date.getHours(); @@ -33,9 +128,11 @@ zerobin = { if (s<10) {s = "0" + s} return h+":"+m+":"+s; }, - numOrdA: function(a, b){ - return (a-b); + + numOrdA: function(a, b){ + return (a-b); }, + get_keys: function(){ var keys = new Array(); for(i=0; i<=localStorage.length; i++){ @@ -44,6 +141,7 @@ zerobin = { } return keys.sort(zerobin.numOrdA); }, + /** Get a tinyurl using JSONP */ getTinyURL: function(longURL, success) { @@ -56,6 +154,7 @@ zerobin = { var api = 'http://json-tinyurl.appspot.com/?url='; $.getJSON(api + encodeURIComponent(longURL) + '&callback=' + callback); }, + support_localstorage: function(){ if (localStorage){ return true; @@ -63,6 +162,7 @@ zerobin = { return false; } }, + store_paste: function(url){ if (zerobin.support_localstorage){ var date = new Date(); @@ -77,9 +177,10 @@ zerobin = { localStorage.setItem(keys.reverse()[0]+1, paste); } }, + get_pastes: function(){ if (zerobin.support_localstorage){ - var pastes = ''; + var pastes = ''; var keys = zerobin.get_keys(); keys.reverse(); @@ -94,7 +195,7 @@ zerobin = { var on_at = 'on '; } pastes = pastes + '
  • ' + on_at + display_date + '
  • '; - } + } if (!pastes){ return 'Your previous pastes will be saved in your browser using localStorage.'; } @@ -103,7 +204,8 @@ zerobin = { return 'Sorry your browser does not support LocalStorage, We cannot display your previous pastes.'; } }, - get_paste_content: function(){ + + getPasteContent: function(){ var content_clone = '' ; $("#paste-content li").each(function(index) { content_clone = content_clone + $(this).text() + '\n'; @@ -127,73 +229,137 @@ $('button[type=submit]').live("click", function(e){ var paste = $('textarea').val(); if (paste.trim()) { - var expiration = $('#expiration').val(); - var key = zerobin.make_key(); - var data = {content: zerobin.encrypt(key, paste), expiration: expiration} - $.post('/paste/create', data) - .error(function(error) { - alert('Paste could not be saved. Please try again later.'); - }) - .success(function(data) { - var paste_url = '/paste/' + data['paste'] + '#' + key; - window.location = (paste_url); - zerobin.store_paste(paste_url); - }); + $('form.well p').hide(); + $loading = $('form.well .progress').show(); + var $loading = $('form.well .progress .bar') + .css('width', '25%') + .text('Converting paste to bits...'); + + /* Encode, compress, encrypt and send the paste then redirect the user + to the new paste. We ensure a loading animation is updated + during the process by passing callbacks. + */ + try { + + var expiration = $('#expiration').val(); + var key = zerobin.make_key(); + + zerobin.encrypt(key, paste, + + function(){$loading.text('Encoding to base64...').css('width', '45%')}, + function(){$loading.text('Compressing...').css('width', '65%')}, + function(){$loading.text('Encrypting...').css('width', '85%')}, + + /* This block deal with sending the data, redirection or error handling */ + function(content){ + + $loading.text('Sending...').css('width', '95%'); + var data = {content: content, expiration: expiration}; + + $.post('/paste/create', data) + .error(function(error) { + $('form.well p').show(); + $loading.hide(); + alert('Error: paste could not be saved. Please try again later.'); + }) + .success(function(data) { + $loading.text('Redirecting to new paste...').css('width', '100%'); + var paste_url = '/paste/' + data['paste'] + '#' + key; + zerobin.store_paste(paste_url); + window.location = (paste_url); + }); + } + ); + } catch (err) { + $('form.well p').show(); + $loading.hide(); + alert('Error: paste could not be encrypted. Aborting.'); + } } }); -/** On the display paste page. - Decrypt and decompress the paste content, add syntax coloration then - setup the copy to clipboard button. +/** + DECRYPTION: + On the display paste page, decrypt and decompress the paste content, + add syntax coloration then setup the copy to clipboard button. */ var content = $('#paste-content').text().trim(); var key = window.location.hash.substring(1); var error = false; if (content && key) { - try { - $('#paste-content').text(zerobin.decrypt(key, content)); - } catch(err) { - error = true; - alert('Could not decrypt data (Wrong key ?)'); - } - content = ''; + var $bar = $('.well form .progress').show(); + var $loading = $('.well form .progress .bar').css('width', '25%') + .text('Decrypting paste...'); - if (!error) { + zerobin.decrypt(key, content, - $('#short-url').click(function(e) { - e.preventDefault(); - $('#short-url').text('Loading short url...'); - zerobin.getTinyURL(window.location.toString(), function(tinyurl){ - clip.setText(tinyurl); - $('#copy-success').hide(); - $('#short-url-success') - .html('Short url: ' + tinyurk + '') - .show('fadeUp'); - $('#short-url').text('Get short url'); + /* On error*/ + function(){ + $bar.hide(); + alert('Could not decrypt data (Wrong key ?)'); + }, + + /* Update progress bar */ + function(){$loading.text('Decompressing...').css('width', '45%')}, + function(){$loading.text('Base64 decoding...').css('width', '65%')}, + function(){$loading.text('From bits to string...').css('width', '85%')}, + + /* When done */ + function(content){ + + /* Decrypted content goes back to initial container*/ + $('#paste-content').text(content); + content = ''; + + $loading.text('Code coloration...').css('width', '95%'); + + /* Add a continuation to let the UI redraw */ + setTimeout(function(){ + + /* Setup link to get the paste short url*/ + $('#short-url').click(function(e) { + e.preventDefault(); + $('#short-url').text('Loading short url...'); + zerobin.getTinyURL(window.location.toString(), function(tinyurl){ + clip.setText(tinyurl); + $('#copy-success').hide(); + $('#short-url-success') + .html('Short url: ' + tinyurk + '') + .show('fadeUp'); + $('#short-url').text('Get short url'); + }); }); - }); - prettyPrint(); + /* Setup flash clipboard button */ + ZeroClipboard.setMoviePath('/static/js/ZeroClipboard.swf' ); - /* Setup flash clipboard button */ - ZeroClipboard.setMoviePath('/static/js/ZeroClipboard.swf' ); + var clip = new ZeroClipboard.Client(); + clip.addEventListener('mouseup', function(){ + clip.setText(zerobin.getPasteContent()); + }); + clip.addEventListener('complete', function(){ + $('#copy-success').show('fadeUp', function(){clip.reposition()}); + }); + clip.glue('clip-button'); - var clip = new ZeroClipboard.Client(); - clip.addEventListener('mouseup', function(){ - clip.setText(zerobin.get_paste_content()); - }); - clip.addEventListener('complete', function(){ - $('#copy-success').show('fadeUp', function(){clip.reposition()}); - }); - clip.glue('clip-button'); + window.onresize = clip.reposition; + + /** Syntaxic coloration */ + prettyPrint(); + + /* Display result */ + $loading.text('Done').css('width', '100%'); + $bar.hide(); + }, 250); - window.onresize = clip.reposition; } + ); + +} /* End of "DECRYPTION" */ -} /* Synchronize expiration select boxes value */ $('.paste-option select').live('change', function(){ @@ -201,11 +367,9 @@ $('.paste-option select').live('change', function(){ $('.paste-option select').val(value); }); - /* Resize Textarea according to content */ $('#content').elastic(); - /* Display bottom paste option buttons when needed */ $('#content').live('keyup change', function(){ if($('#content').height() < 400 ){ @@ -221,33 +385,14 @@ $('#content').live('keyup change', function(){ /* Display previous pastes */ $('.previous-pastes .items').html(zerobin.get_pastes()); - - /* clone a paste */ $('.btn-clone').click(function(e){ e.preventDefault(); - var content_clone = zerobin.get_paste_content(); + var content_clone = zerobin.getPasteContent(); $('.submit-form').show(); $('.paste-form').remove(); $('#content').val(content_clone); $('#content').trigger('change'); - }); -}); - - - - - - - - - - - - - - - - +}); /* End of "document ready" jquery callback */ diff --git a/views/base.tpl b/views/base.tpl index d6e6d04..1eb3ae0 100644 --- a/views/base.tpl +++ b/views/base.tpl @@ -9,7 +9,7 @@ pastebin featuring burn after reading, an history and a clipboard"> - + diff --git a/views/home.tpl b/views/home.tpl index ca70732..48bd6bc 100644 --- a/views/home.tpl +++ b/views/home.tpl @@ -14,6 +14,9 @@ class="input-xlarge" id="content" name="content">

    +
    +
    +
    diff --git a/views/paste.tpl b/views/paste.tpl index 5cc1942..057d6c2 100644 --- a/views/paste.tpl +++ b/views/paste.tpl @@ -33,6 +33,10 @@

    +
    +
    +
    +