diff --git a/css/bootstrap/zerobin.css b/css/bootstrap/zerobin.css
index 3b660a3..a7cd0ff 100644
--- a/css/bootstrap/zerobin.css
+++ b/css/bootstrap/zerobin.css
@@ -21,12 +21,20 @@ body {
margin: 5px 0;
}
+#comments, #comments button {
+ margin-bottom: 10px;
+}
+
.comment {
border-left: 1px solid #ccc;
- padding: 5px 0 5px 5px;
+ padding: 5px 0 5px 10px;
white-space: pre-wrap;
}
-h4 {
+footer h4 {
margin-top: 0;
}
+
+li.L0, li.L1, li.L2, li.L3, li.L5, li.L6, li.L7, li.L8 {
+ list-style-type: decimal !important;
+}
\ No newline at end of file
diff --git a/css/zerobin.css b/css/zerobin.css
index d7c964f..0ee0d19 100644
--- a/css/zerobin.css
+++ b/css/zerobin.css
@@ -65,7 +65,6 @@ h3 {
}
#aboutbox {
- font-size: 1.2em;
color: #94a3b4;
padding: 4px 8px 4px 16px;
position: relative;
diff --git a/js/zerobin.js b/js/zerobin.js
index fe8576b..5407340 100644
--- a/js/zerobin.js
+++ b/js/zerobin.js
@@ -12,651 +12,904 @@
// Immediately start random number generator collector.
sjcl.random.startCollectors();
-/**
- * Converts a duration (in seconds) into human readable format.
- *
- * @param int seconds
- * @return string
- */
-function secondsToHuman(seconds)
-{
- if (seconds<60) { var v=Math.floor(seconds); return v+' second'+((v>1)?'s':''); }
- if (seconds<60*60) { var v=Math.floor(seconds/60); return v+' minute'+((v>1)?'s':''); }
- if (seconds<60*60*24) { var v=Math.floor(seconds/(60*60)); return v+' hour'+((v>1)?'s':''); }
- // If less than 2 months, display in days:
- if (seconds<60*60*24*60) { var v=Math.floor(seconds/(60*60*24)); return v+' day'+((v>1)?'s':''); }
- var v=Math.floor(seconds/(60*60*24*30)); return v+' month'+((v>1)?'s':'');
-}
-
-/**
- * Converts an associative array to an encoded string
- * for appending to the anchor.
- *
- * @param object associative_array Object to be serialized
- * @return string
- */
-function hashToParameterString(associativeArray)
-{
- var parameterString = "";
- for (key in associativeArray)
- {
- if( parameterString === "" )
- {
- parameterString = encodeURIComponent(key);
- parameterString += "=" + encodeURIComponent(associativeArray[key]);
- } else {
- parameterString += "&" + encodeURIComponent(key);
- parameterString += "=" + encodeURIComponent(associativeArray[key]);
- }
- }
- //padding for URL shorteners
- parameterString += "&p=p";
-
- return parameterString;
-}
-
-/**
- * Converts a string to an associative array.
- *
- * @param string parameter_string String containing parameters
- * @return object
- */
-function parameterStringToHash(parameterString)
-{
- var parameterHash = {};
- var parameterArray = parameterString.split("&");
- for (var i = 0; i < parameterArray.length; i++) {
- //var currentParamterString = decodeURIComponent(parameterArray[i]);
- var pair = parameterArray[i].split("=");
- var key = decodeURIComponent(pair[0]);
- var value = decodeURIComponent(pair[1]);
- parameterHash[key] = value;
- }
-
- return parameterHash;
-}
-
-/**
- * Get an associative array of the parameters found in the anchor
- *
- * @return object
- */
-function getParameterHash()
-{
- var hashIndex = window.location.href.indexOf("#");
- if (hashIndex >= 0) {
- return parameterStringToHash(window.location.href.substring(hashIndex + 1));
- } else {
- return {};
- }
-}
-
-/**
- * Compress a message (deflate compression). Returns base64 encoded data.
- *
- * @param string message
- * @return base64 string data
- */
-function compress(message) {
- return Base64.toBase64( RawDeflate.deflate( Base64.utob(message) ) );
-}
-
-/**
- * Decompress a message compressed with compress().
- */
-function decompress(data) {
- return Base64.btou( RawDeflate.inflate( Base64.fromBase64(data) ) );
-}
+$(function(){
+ 'use strict';
+
+ /**
+ * static helper methods
+ */
+ var helper = {
+
+ /**
+ * Converts a duration (in seconds) into human readable format.
+ *
+ * @param int seconds
+ * @return string
+ */
+ secondsToHuman: function(seconds)
+ {
+ if (seconds < 60)
+ {
+ var v = Math.floor(seconds);
+ return v + ' second' + ((v > 1) ? 's' : '');
+ }
+ if (seconds < 60 * 60)
+ {
+ var v = Math.floor(seconds / 60);
+ return v + ' minute' + ((v > 1) ? 's' : '');
+ }
+ if (seconds < 60 * 60 * 24)
+ {
+ var v = Math.floor(seconds / (60 * 60));
+ return v + ' hour' + ((v > 1) ? 's' : '');
+ }
+ // If less than 2 months, display in days:
+ if (seconds < 60 * 60 * 24 * 60)
+ {
+ var v = Math.floor(seconds / (60 * 60 * 24));
+ return v + ' day' + ((v > 1) ? 's' : '');
+ }
+ var v = Math.floor(seconds / (60 * 60 * 24 * 30));
+ return v + ' month' + ((v > 1) ? 's' : '');
+ },
+
+ /**
+ * Converts an associative array to an encoded string
+ * for appending to the anchor.
+ *
+ * @param object associative_array Object to be serialized
+ * @return string
+ */
+ hashToParameterString: function(associativeArray)
+ {
+ var parameterString = '';
+ for (key in associativeArray)
+ {
+ if(parameterString === '')
+ {
+ parameterString = encodeURIComponent(key);
+ parameterString += '=' + encodeURIComponent(associativeArray[key]);
+ }
+ else
+ {
+ parameterString += '&' + encodeURIComponent(key);
+ parameterString += '=' + encodeURIComponent(associativeArray[key]);
+ }
+ }
+ // padding for URL shorteners
+ parameterString += '&p=p';
+
+ return parameterString;
+ },
+
+ /**
+ * Converts a string to an associative array.
+ *
+ * @param string parameter_string String containing parameters
+ * @return object
+ */
+ parameterStringToHash: function(parameterString)
+ {
+ var parameterHash = {};
+ var parameterArray = parameterString.split('&');
+ for (var i = 0; i < parameterArray.length; i++)
+ {
+ var pair = parameterArray[i].split('=');
+ var key = decodeURIComponent(pair[0]);
+ var value = decodeURIComponent(pair[1]);
+ parameterHash[key] = value;
+ }
-/**
- * Compress, then encrypt message with key.
- *
- * @param string key
- * @param string message
- * @return encrypted string data
- */
-function zeroCipher(key, message) {
- if ($('#passwordinput').val().length == 0) {
- return sjcl.encrypt(key, compress(message));
- }
- return sjcl.encrypt(key + sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash($("#passwordinput").val())), compress(message));
-}
+ return parameterHash;
+ },
-/**
- * Decrypt message with key, then decompress.
- *
- * @param string key
- * @param encrypted string data
- * @return string readable message
- */
-function zeroDecipher(key, data) {
- if (data != undefined) {
- try {
- return decompress(sjcl.decrypt(key, data));
- } catch (err) {
- try {
- if ($('#passwordinput').val().length > 0) {
- password = $('#passwordinput').val();
- } else {
- password = prompt("Please enter the password for this paste:", "");
- if (password == null) return null;
+ /**
+ * Get an associative array of the parameters found in the anchor
+ *
+ * @return object
+ */
+ getParameterHash: function()
+ {
+ var hashIndex = window.location.href.indexOf('#');
+ if (hashIndex >= 0)
+ {
+ return this.parameterStringToHash(window.location.href.substring(hashIndex + 1));
+ }
+ else
+ {
+ return {};
+ }
+ },
+
+ /**
+ * Convert all applicable characters to HTML entities
+ *
+ * @param string str
+ * @returns string encoded string
+ */
+ htmlEntities: function(str)
+ {
+ return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"');
+ },
+
+ /**
+ * Text range selection.
+ * From: http://stackoverflow.com/questions/985272/jquery-selecting-text-in-an-element-akin-to-highlighting-with-your-mouse
+ *
+ * @param string element : Indentifier of the element to select (id="").
+ */
+ selectText: function(element)
+ {
+ var doc = document,
+ text = doc.getElementById(element),
+ range,
+ selection;
+
+ // MS
+ if (doc.body.createTextRange)
+ {
+ range = doc.body.createTextRange();
+ range.moveToElementText(text);
+ range.select();
+ }
+ // all others
+ else if (window.getSelection)
+ {
+ selection = window.getSelection();
+ range = doc.createRange();
+ range.selectNodeContents(text);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+ },
+
+ /**
+ * Set text of a DOM element (required for IE)
+ * This is equivalent to element.text(text)
+ *
+ * @param object element : a DOM element.
+ * @param string text : the text to enter.
+ */
+ setElementText: function(element, text)
+ {
+ // For IE<10: Doesn't support white-space:pre-wrap; so we have to do this BIG UGLY STINKING THING.
+ if ($('#oldienotice').is(':visible')) {
+ var html = this.htmlEntities(text).replace(/\n/ig,'\r\n
');
+ element.html('
'+html+''); + } + // for other (sane) browsers: + else + { + element.text(text); + } + }, + + /** + * Convert URLs to clickable links. + * URLs to handle: + *
+ * magnet:?xt.1=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C&xt.2=urn:sha1:TXGCZQTH26NL6OUQAJJPFALHG2LTGBC7
+ * http://localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
+ * http://user:password@localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
+ *
+ *
+ * @param object element : a jQuery DOM element.
+ */
+ urls2links: function(element)
+ {
+ element.html(
+ element.html().replace(
+ /((http|https|ftp):\/\/[\w?=&.\/-;#@~%+-]+(?![\w\s?&.\/;#~%"=-]*>))/ig,
+ '$1'
+ )
+ );
+ element.html(
+ element.html().replace(
+ /((magnet):[\w?=&.\/-;#@~%+-]+)/ig,
+ '$1'
+ )
+ );
+ }
+ };
+
+ /**
+ * filter methods
+ */
+ var filter = {
+ /**
+ * Compress a message (deflate compression). Returns base64 encoded data.
+ *
+ * @param string message
+ * @return base64 string data
+ */
+ compress: function(message)
+ {
+ return Base64.toBase64( RawDeflate.deflate( Base64.utob(message) ) );
+ },
+
+ /**
+ * Decompress a message compressed with compress().
+ *
+ * @param base64 string data
+ * @return string message
+ */
+ decompress: function(data)
+ {
+ return Base64.btou( RawDeflate.inflate( Base64.fromBase64(data) ) );
+ },
+
+ /**
+ * Compress, then encrypt message with key.
+ *
+ * @param string key
+ * @param string password
+ * @param string message
+ * @return encrypted string data
+ */
+ cipher: function(key, password, message)
+ {
+ password = password.trim();
+ if (password.length == 0)
+ {
+ return sjcl.encrypt(key, this.compress(message));
+ }
+ return sjcl.encrypt(key + sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(password)), this.compress(message));
+ },
+
+ /**
+ * Decrypt message with key, then decompress.
+ *
+ * @param string key
+ * @param string password
+ * @param encrypted string data
+ * @return string readable message
+ */
+ decipher: function(key, password, data)
+ {
+ if (data != undefined)
+ {
+ try
+ {
+ return this.decompress(sjcl.decrypt(key, data));
+ }
+ catch(err)
+ {
+ try
+ {
+ return this.decompress(sjcl.decrypt(key + sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(password)), data));
+ }
+ catch(err)
+ {}
}
- data = decompress(sjcl.decrypt(key + sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(password)), data));
- $('#passwordinput').val(password);
- return data;
- } catch (err) {
- return zeroDecipher(key, data);
}
+ return '';
}
- }
-}
+ };
+
+ var zerobin = {
+ /**
+ * Get the current script location (without search or hash part of the URL).
+ * eg. http://server.com/zero/?aaaa#bbbb --> http://server.com/zero/
+ *
+ * @return string current script location
+ */
+ scriptLocation: function()
+ {
+ var scriptLocation = window.location.href.substring(0,window.location.href.length
+ - window.location.search.length - window.location.hash.length);
+ var hashIndex = scriptLocation.indexOf('#');
+ if (hashIndex !== -1)
+ {
+ scriptLocation = scriptLocation.substring(0, hashIndex);
+ }
+ return scriptLocation;
+ },
+
+ /**
+ * Get the pastes unique identifier from the URL
+ * eg. http://server.com/zero/?c05354954c49a487#xxx --> c05354954c49a487
+ *
+ * @return string unique identifier
+ */
+ pasteID: function()
+ {
+ return window.location.search.substring(1);
+ },
+
+ /**
+ * Return the deciphering key stored in anchor part of the URL
+ *
+ * @return string key
+ */
+ pageKey: function()
+ {
+ var key = window.location.hash.substring(1); // Get key
-/**
- * Get the current script location (without search or hash part of the URL).
- * eg. http://server.com/zero/?aaaa#bbbb --> http://server.com/zero/
- *
- * @return string current script location
- */
-function scriptLocation() {
- var scriptLocation = window.location.href.substring(0,window.location.href.length
- - window.location.search.length - window.location.hash.length);
- var hashIndex = scriptLocation.indexOf("#");
- if (hashIndex !== -1) {
- scriptLocation = scriptLocation.substring(0, hashIndex);
- }
- return scriptLocation;
-}
+ // Some stupid web 2.0 services and redirectors add data AFTER the anchor
+ // (such as &utm_source=...).
+ // We will strip any additional data.
-/**
- * Get the pastes unique identifier from the URL
- * eg. http://server.com/zero/?c05354954c49a487#xxx --> c05354954c49a487
- *
- * @return string unique identifier
- */
-function pasteID() {
- return window.location.search.substring(1);
-}
+ // First, strip everything after the equal sign (=) which signals end of base64 string.
+ var i = key.indexOf('=');
+ if (i > -1)
+ {
+ key = key.substring(0, i + 1);
+ }
-/**
- * Convert all applicable characters to HTML entities
- *
- * @param string str
- * @returns string encoded string
- */
-function htmlEntities(str) {
- return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"');
-}
-/**
- * Set text of a DOM element (required for IE)
- * This is equivalent to element.text(text)
- *
- * @param object element : a DOM element.
- * @param string text : the text to enter.
- */
-function setElementText(element, text) {
- // For IE<10.
- if ($('#oldienotice').is(":visible")) {
- // IE<10 does not support white-space:pre-wrap; so we have to do this BIG UGLY STINKING THING.
- var html = htmlEntities(text).replace(/\n/ig,"\r\n'+html+''); - } - // for other (sane) browsers: - else { - element.text(text); - } -} + // If the equal sign was not present, some parameters may remain: + i = key.indexOf('&'); + if (i > -1) + { + key = key.substring(0, i); + } -/** - * Show decrypted text in the display area, including discussion (if open) - * - * @param string key : decryption key - * @param array comments : Array of messages to display (items = array with keys ('data','meta') - */ -function displayMessages(key, comments) { - try { // Try to decrypt the paste. - var cleartext = zeroDecipher(key, comments[0].data); - if (cleartext == null) throw "password prompt canceled"; - } catch(err) { - $('#cleartext').addClass('hidden'); - $('#prettymessage').addClass('hidden'); - $('#clonebutton').addClass('hidden'); - showError('Could not decrypt data (Wrong key ?)'); - return; - } - setElementText($('#cleartext'), cleartext); - setElementText($('#prettyprint'), cleartext); - // Convert URLs to clickable links. - urls2links($('#cleartext')); - urls2links($('#prettyprint')); - if (typeof prettyPrint == 'function') prettyPrint(); - - // Display paste expiration. - if (comments[0].meta.expire_date) $('#remainingtime').removeClass('foryoureyesonly').text('This document will expire in '+secondsToHuman(comments[0].meta.remaining_time)+'.').removeClass('hidden'); - if (comments[0].meta.burnafterreading) { - $.get(scriptLocation() + "?pasteid=" + pasteID() + '&deletetoken=burnafterreading', 'json') - .fail(function() { - showError('Could not delete the paste, it was not stored in burn after reading mode.'); - }); - $('#remainingtime').addClass('foryoureyesonly').text('FOR YOUR EYES ONLY. Don\'t close this window, this message can\'t be displayed again.').removeClass('hidden'); - $('#clonebutton').addClass('hidden'); // Discourage cloning (as it can't really be prevented). - } + // Then add trailing equal sign if it's missing + if (key.charAt(key.length - 1) !== '=') key += '='; - // If the discussion is opened on this paste, display it. - if (comments[0].meta.opendiscussion) { - $('#comments').html(''); - // iterate over comments - for (var i = 1; i < comments.length; i++) { - var comment=comments[i]; - var cleartext="[Could not decrypt comment ; Wrong key ?]"; - try { - cleartext = zeroDecipher(key, comment.data); - } catch(err) { } - var place = $('#comments'); - // If parent comment exists, display below (CSS will automatically shift it right.) - var cname = '#comment_'+comment.meta.parentid; - - // If the element exists in page - if ($(cname).length) { - place = $(cname); - } - var divComment = $('
'+paste+''); - newDoc.close(); -} + event.preventDefault(); + + // Do not send if no data. + if (this.message.val().length == 0) return; + + // If sjcl has not collected enough entropy yet, display a message. + if (!sjcl.random.isReady()) + { + this.showStatus('Sending paste (Please move your mouse for more entropy)...', true); + sjcl.random.addEventListener('seeded', function() { + this.sendData(event); + }); + return; + } -/** - * Clone the current paste. - */ -function clonePaste() { - stateNewPaste(); - - //Erase the id and the key in url - history.replaceState(document.title, document.title, scriptLocation()); - - showStatus(''); - $('#message').text($('#cleartext').text()); -} + this.showStatus('Sending paste...', true); + + var randomkey = sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0); + var cipherdata = filter.cipher(randomkey, this.passwordInput.val(), this.message.val()); + var data_to_send = { + data: cipherdata, + expire: $('#pasteExpiration').val(), + burnafterreading: this.burnAfterReading.is(':checked') ? 1 : 0, + opendiscussion: this.openDiscussion.is(':checked') ? 1 : 0 + }; + var parent = this; + $.post(this.scriptLocation(), data_to_send, function(data) + { + if (data.status == 0) { + parent.stateExistingPaste(); + var url = parent.scriptLocation() + '?' + data.id + '#' + randomkey; + var deleteUrl = parent.scriptLocation() + '?pasteid=' + data.id + '&deletetoken=' + data.deletetoken; + parent.showStatus('', false); + + $('#pastelink').html('Your paste is ' + url + ' (Hit CTRL+C to copy)'); + $('#deletelink').html('Delete data'); + parent.pasteResult.removeClass('hidden'); + // We pre-select the link so that the user only has to CTRL+C the link. + helper.selectText('pasteurl'); + + helper.setElementText(parent.clearText, parent.message.val()); + helper.setElementText(parent.prettyPrint, parent.message.val()); + // Convert URLs to clickable links. + helper.urls2links(parent.clearText); + helper.urls2links(parent.prettyPrint); + parent.showStatus('', false); + if (typeof prettyPrint == 'function') prettyPrint(); + } + else if (data.status==1) + { + parent.showError('Could not create paste: ' + data.message); + } + else + { + parent.showError('Could not create paste.'); + } + }, 'json') + .fail(function() { + parent.showError('Data could not be sent (server error or not responding).'); + }); + }, -/** - * Create a new paste. - */ -function newPaste() { - stateNewPaste(); - showStatus(''); - $('#message').text(''); -} + /** + * Put the screen in "New paste" mode. + */ + stateNewPaste: function() + { + this.message.text(''); + this.cloneButton.addClass('hidden'); + this.rawTextButton.addClass('hidden'); + this.remainingTime.addClass('hidden'); + this.pasteResult.addClass('hidden'); + this.clearText.addClass('hidden'); + this.discussion.addClass('hidden'); + this.prettyMessage.addClass('hidden'); + this.sendButton.removeClass('hidden'); + this.expiration.removeClass('hidden'); + this.burnAfterReadingOption.removeClass('hidden'); + this.openDisc.removeClass('hidden'); + this.newButton.removeClass('hidden'); + this.password.removeClass('hidden'); + this.message.removeClass('hidden'); + this.message.focus(); + }, + + /** + * Put the screen in "Existing paste" mode. + */ + stateExistingPaste: function() + { + this.sendButton.addClass('hidden'); -/** - * Display an error message - * (We use the same function for paste and reply to comments) - */ -function showError(message) { - if ($('#status').length) { - $('#status').addClass('errorMessage').text(message); - } else { - $('#errormessage').removeClass('hidden').append(message); - } - $('#replystatus').addClass('errorMessage').text(message); -} + // No "clone" for IE<10. + if ($('#oldienotice').is(":visible")) + { + this.cloneButton.addClass('hidden'); + } + else + { + this.cloneButton.removeClass('hidden'); + } + this.rawTextButton.removeClass('hidden'); + + this.expiration.addClass('hidden'); + this.burnAfterReadingOption.addClass('hidden'); + this.openDisc.addClass('hidden'); + this.newButton.removeClass('hidden'); + this.pasteResult.addClass('hidden'); + this.message.addClass('hidden'); + this.clearText.addClass('hidden'); + this.prettyMessage.removeClass('hidden'); + }, + + /** + * If "burn after reading" is checked, disable discussion. + */ + changeBurnAfterReading: function() + { + if (this.burnAfterReading.is(':checked') ) + { + this.openDisc.addClass('buttondisabled'); + this.openDiscussion.attr({checked: false, disabled: true}); + } + else + { + this.openDisc.removeClass('buttondisabled'); + this.openDiscussion.removeAttr('disabled'); + } + }, + + /** + * Reload the page + * + * @param Event event + */ + reloadPage: function(event) + { + event.preventDefault(); + window.location.href = this.scriptLocation(); + }, + + /** + * Return raw text + * + * @param Event event + */ + rawText: function(event) + { + event.preventDefault(); + var paste = this.clearText.html(); + var newDoc = document.open('text/html', 'replace'); + newDoc.write('
' + paste + ''); + newDoc.close(); + }, + + /** + * Clone the current paste. + * + * @param Event event + */ + clonePaste: function(event) + { + event.preventDefault(); + this.stateNewPaste(); -/** - * Display status - * (We use the same function for paste and reply to comments) - * - * @param string message : text to display - * @param boolean spin (optional) : tell if the "spinning" animation should be displayed. - */ -function showStatus(message, spin) { - $('#replystatus').removeClass('errorMessage'); - $('#replystatus').text(message); - if (!message) { - $('#status').html(' '); - return; - } - if (message == '') { - $('#status').html(' '); - return; - } - $('#status').removeClass('errorMessage'); - $('#status').text(message); - if (spin) { - var img = '
- * magnet:?xt.1=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C&xt.2=urn:sha1:TXGCZQTH26NL6OUQAJJPFALHG2LTGBC7
- * http://localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
- * http://user:password@localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
- *
- *
- * @param object element : a jQuery DOM element.
- */
-function urls2links(element) {
- var re = /((http|https|ftp):\/\/[\w?=&.\/-;#@~%+-]+(?![\w\s?&.\/;#~%"=-]*>))/ig;
- element.html(element.html().replace(re,'$1'));
- var re = /((magnet):[\w?=&.\/-;#@~%+-]+)/ig;
- element.html(element.html().replace(re,'$1'));
-}
+ this.showStatus('', false);
+ this.message.text(this.clearText.text());
+ },
-/**
- * Return the deciphering key stored in anchor part of the URL
- */
-function pageKey() {
- var key = window.location.hash.substring(1); // Get key
+ /**
+ * Create a new paste.
+ */
+ newPaste: function()
+ {
+ this.stateNewPaste();
+ this.showStatus('', false);
+ this.message.text('');
+ },
+
+ /**
+ * Display an error message
+ * (We use the same function for paste and reply to comments)
+ *
+ * @param string message : text to display
+ */
+ showError: function(message)
+ {
+ if (this.status.length)
+ {
+ this.status.addClass('errorMessage').text(message);
+ }
+ else
+ {
+ this.errorMessage.removeClass('hidden').append(message);
+ }
+ this.replyStatus.addClass('errorMessage').text(message);
+ },
+
+ /**
+ * Display a status message
+ * (We use the same function for paste and reply to comments)
+ *
+ * @param string message : text to display
+ * @param boolean spin (optional) : tell if the "spinning" animation should be displayed.
+ */
+ showStatus: function(message, spin)
+ {
+ this.replyStatus.removeClass('errorMessage').text(message);
+ if (!message)
+ {
+ this.status.html(' ');
+ return;
+ }
+ if (message == '')
+ {
+ this.status.html(' ');
+ return;
+ }
+ this.status.removeClass('errorMessage').text(message);
+ if (spin)
+ {
+ var img = '