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':'');
-}
+$(function(){
+ 'use strict';
+
+ /**
+ * static helper methods
+ */
+ var helper = {
-/**
- * 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) ) );
-}
-
-/**
- * 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));
-}
-
-/**
- * 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;
- }
- 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);
+ /**
+ * 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' : '');
}
- }
- }
-}
-
-/**
- * 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;
-}
-
-/**
- * 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);
-}
-
-/**
- * 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
");
- element.html('
'+html+''); - } - // for other (sane) browsers: - else { - element.text(text); - } -} - -/** - * 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). - } - - // 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); + if (seconds < 60 * 60) + { + var v = Math.floor(seconds / 60); + return v + ' minute' + ((v > 1) ? 's' : ''); } - var divComment = $('
'+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)
+ {}
+ }
+ }
+ 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
+
+ // Some stupid web 2.0 services and redirectors add data AFTER the anchor
+ // (such as &utm_source=...).
+ // We will strip any additional data.
+
+ // 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);
+ }
+
+ // If the equal sign was not present, some parameters may remain:
+ i = key.indexOf('&');
+ if (i > -1)
+ {
+ key = key.substring(0, i);
+ }
+
+ // Then add trailing equal sign if it's missing
+ if (key.charAt(key.length - 1) !== '=') key += '=';
+
+ return key;
+ },
+
+ /**
+ * ask the user for the password and return it
+ *
+ * @throws error when dialog canceled
+ * @return string password
+ */
+ requestPassword: function()
+ {
+ var password = prompt('Please enter the password for this paste:', '');
+ if (password == null) throw 'password prompt canceled';
+ if (password.length == 0) return this.requestPassword();
+ return password;
+ },
+
+ /**
+ * 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')
+ */
+ displayMessages: function(key, comments)
+ {
+ // Try to decrypt the paste.
+ var password = this.passwordInput.val();
+ if (!this.prettyPrint.hasClass('prettyprinted')) {
+ try
+ {
+ var cleartext = filter.decipher(key, password, comments[0].data);
+ if (cleartext.length == 0)
+ {
+ if (password.length == 0) password = this.requestPassword();
+ cleartext = filter.decipher(key, password, comments[0].data);
+ }
+ if (cleartext.length == 0) throw 'failed to decipher message';
+ this.passwordInput.val(password);
+
+ helper.setElementText(this.clearText, cleartext);
+ helper.setElementText(this.prettyPrint, cleartext);
+
+ // Convert URLs to clickable links.
+ helper.urls2links(this.clearText);
+ helper.urls2links(this.prettyPrint);
+ if (typeof prettyPrint == 'function') prettyPrint();
+ }
+ catch(err)
+ {
+ this.clearText.addClass('hidden');
+ this.prettyMessage.addClass('hidden');
+ this.cloneButton.addClass('hidden');
+ this.showError('Could not decrypt data (Wrong key?)');
+ return;
+ }
+ }
+
+ // Display paste expiration.
+ if (comments[0].meta.expire_date)
+ {
+ this.remainingTime.removeClass('foryoureyesonly')
+ .text('This document will expire in ' + helper.secondsToHuman(comments[0].meta.remaining_time) + '.')
+ .removeClass('hidden');
+ }
+ if (comments[0].meta.burnafterreading)
+ {
+ var parent = this;
+ $.get(this.scriptLocation() + '?pasteid=' + this.pasteID() + '&deletetoken=burnafterreading', 'json')
+ .fail(function() {
+ parent.showError('Could not delete the paste, it was not stored in burn after reading mode.');
+ });
+ this.remainingTime.addClass('foryoureyesonly')
+ .text('FOR YOUR EYES ONLY. Don\'t close this window, this message can\'t be displayed again.')
+ .removeClass('hidden');
+ // Discourage cloning (as it can't really be prevented).
+ this.cloneButton.addClass('hidden');
+ }
+
+ // If the discussion is opened on this paste, display it.
+ if (comments[0].meta.opendiscussion)
+ {
+ this.comments.html('');
+
+ // iterate over comments
+ for (var i = 1; i < comments.length; i++)
+ {
+ var place = this.comments;
+ var comment=comments[i];
+ var cleartext='[Could not decrypt comment; Wrong key?]';
+ try
+ {
+ cleartext = filter.decipher(key, password, comment.data);
+ }
+ catch(err)
+ {}
+ // 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(); + }, + + /** + * Clone the current paste. + * + * @param Event event + */ + clonePaste: function(event) + { + event.preventDefault(); + this.stateNewPaste(); + + // Erase the id and the key in url + history.replaceState(document.title, document.title, this.scriptLocation()); + + this.showStatus('', false); + this.message.text(this.clearText.text()); + }, + + /** + * 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 = ''; + this.status.prepend(img); + this.replyStatus.prepend(img); + } + }, + + /** + * bind events to DOM elements + */ + bindEvents: function() + { + this.burnAfterReading.change($.proxy(this.changeBurnAfterReading, this)); + this.sendButton.click($.proxy(this.sendData, this)); + this.cloneButton.click($.proxy(this.clonePaste, this)); + this.rawTextButton.click($.proxy(this.rawText, this)); + $('.reloadlink').click($.proxy(this.reloadPage, this)); + }, + + /** + * main application + */ + init: function() + { + // hide "no javascript" message + $('#noscript').hide(); + + // preload jQuery wrapped DOM elements and bind events + this.burnAfterReading = $('#burnafterreading'); + this.burnAfterReadingOption = $('#burnafterreadingoption'); + this.cipherData = $('#cipherdata'); + this.clearText = $('#cleartext'); + this.cloneButton = $('#clonebutton'); + this.comments = $('#comments'); + this.discussion = $('#discussion'); + this.errorMessage = $('#errormessage'); + this.expiration = $('#expiration'); + this.message = $('#message'); + this.newButton = $('#newbutton'); + this.openDisc = $('#opendisc'); + this.openDiscussion = $('#opendiscussion'); + this.password = $('#password'); + this.passwordInput = $('#passwordinput'); + this.pasteResult = $('#pasteresult'); + this.prettyMessage = $('#prettymessage'); + this.prettyPrint = $('#prettyprint'); + this.rawTextButton = $('#rawtextbutton'); + this.remainingTime = $('#remainingtime'); + this.replyStatus = $('#replystatus'); + this.sendButton = $('#sendbutton'); + this.status = $('#status'); + this.bindEvents(); + + // Display status returned by php code if any (eg. Paste was properly deleted.) + if (this.status.text().length > 0) + { + this.showStatus(this.status.text(), false); + return; + } + + // Keep line height even if content empty. + this.status.html(' '); + + // Display an existing paste + if (this.cipherData.text().length > 1) + { + // Missing decryption key in URL? + if (window.location.hash.length == 0) + { + this.showError('Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL ?)'); + return; + } + + // List of messages to display. + var messages = $.parseJSON(this.cipherData.text()); + + // Show proper elements on screen. + this.stateExistingPaste(); + + this.displayMessages(this.pageKey(), messages); + } + // Display error message from php code. + else if (this.errorMessage.text().length > 1) + { + this.showError(this.errorMessage.text()); + } + // Create a new paste. + else + { + this.newPaste(); + } } - }, 'json') - .fail(function() { - showError('Comment could not be sent (server error or not responding).'); - }); -} - -/** - * Send a new paste to server - */ -function send_data() { - // Do not send if no data. - if ($('#message').val().length == 0) { - return; } - // If sjcl has not collected enough entropy yet, display a message. - if (!sjcl.random.isReady()) - { - showStatus('Sending paste (Please move your mouse for more entropy)...', spin=true); - sjcl.random.addEventListener('seeded', function(){ send_data(); }); - return; - } - - showStatus('Sending paste...', spin=true); - - var randomkey = sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0); - var cipherdata = zeroCipher(randomkey, $('#message').val()); - var data_to_send = { data: cipherdata, - expire: $('#pasteExpiration').val(), - burnafterreading: $('#burnafterreading').is(':checked') ? 1 : 0, - opendiscussion: $('#opendiscussion').is(':checked') ? 1 : 0 - }; - $.post(scriptLocation(), data_to_send, function(data) { - if (data.status == 0) { - stateExistingPaste(); - var url = scriptLocation() + "?" + data.id + '#' + randomkey; - var deleteUrl = scriptLocation() + "?pasteid=" + data.id + '&deletetoken=' + data.deletetoken; - showStatus(''); - - $('#pastelink').html('Your paste is ' + url + ' (Hit CTRL+C to copy)'); - $('#deletelink').html('Delete data'); - $('#pasteresult').removeClass('hidden'); - selectText('pasteurl'); // We pre-select the link so that the user only has to CTRL+C the link. - - setElementText($('#cleartext'), $('#message').val()); - setElementText($('#prettyprint'), $('#message').val()); - // Convert URLs to clickable links. - urls2links($('#cleartext')); - urls2links($('#prettyprint')); - showStatus(''); - if (typeof prettyPrint == 'function') prettyPrint(); - } - else if (data.status==1) { - showError('Could not create paste: ' + data.message); - } - else { - showError('Could not create paste.'); - } - }, 'json') - .fail(function() { - showError('Data could not be sent (server error or not responding).'); - }); -} - -/** - * 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=""). - */ -function selectText(element) { - var doc = document - , text = doc.getElementById(element) - , range, selection - ; - if (doc.body.createTextRange) { // MS - range = doc.body.createTextRange(); - range.moveToElementText(text); - range.select(); - } else if (window.getSelection) { // all others - selection = window.getSelection(); - range = doc.createRange(); - range.selectNodeContents(text); - selection.removeAllRanges(); - selection.addRange(range); - } -} - -/** - * Put the screen in "New paste" mode. - */ -function stateNewPaste() { - $('#sendbutton').removeClass('hidden'); - $('#clonebutton').addClass('hidden'); - $('#rawtextbutton').addClass('hidden'); - $('#expiration').removeClass('hidden'); - $('#remainingtime').addClass('hidden'); - $('#burnafterreadingoption').removeClass('hidden'); - $('#opendisc').removeClass('hidden'); - $('#newbutton').removeClass('hidden'); - $('#pasteresult').addClass('hidden'); - $('#message').text(''); - $('#message').removeClass('hidden'); - $('#cleartext').addClass('hidden'); - $('#message').focus(); - $('#discussion').addClass('hidden'); - $('#prettymessage').addClass('hidden'); - // Show password field - $('#password').removeClass('hidden'); -} - -/** - * Put the screen in "Existing paste" mode. - */ -function stateExistingPaste() { - $('#sendbutton').addClass('hidden'); - - // No "clone" for IE<10. - if ($('#oldienotice').is(":visible")) { - $('#clonebutton').addClass('hidden'); - } - else { - $('#clonebutton').removeClass('hidden'); - } - $('#rawtextbutton').removeClass('hidden'); - - $('#expiration').addClass('hidden'); - $('#burnafterreadingoption').addClass('hidden'); - $('#opendisc').addClass('hidden'); - $('#newbutton').removeClass('hidden'); - $('#pasteresult').addClass('hidden'); - $('#message').addClass('hidden'); - $('#cleartext').addClass('hidden'); - $('#prettymessage').removeClass('hidden'); -} - -/** - * Return raw text - */ -function rawText() -{ - var paste = $('#cleartext').html(); - var newDoc = document.open('text/html', 'replace'); - newDoc.write('
'+paste+''); - newDoc.close(); -} - -/** - * 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()); -} - -/** - * Create a new paste. - */ -function newPaste() { - stateNewPaste(); - showStatus(''); - $('#message').text(''); -} - -/** - * 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); -} - -/** - * 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 = ''; - $('#status').prepend(img); - $('#replystatus').prepend(img); - } -} - -/** - * 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.
- */
-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'));
-}
-
-/**
- * Return the deciphering key stored in anchor part of the URL
- */
-function pageKey() {
- var key = window.location.hash.substring(1); // Get key
-
- // Some stupid web 2.0 services and redirectors add data AFTER the anchor
- // (such as &utm_source=...).
- // We will strip any additional data.
-
- // First, strip everything after the equal sign (=) which signals end of base64 string.
- i = key.indexOf('='); if (i>-1) { key = key.substring(0,i+1); }
-
- // If the equal sign was not present, some parameters may remain:
- i = key.indexOf('&'); if (i>-1) { key = key.substring(0,i); }
-
- // Then add trailing equal sign if it's missing
- if (key.charAt(key.length-1)!=='=') key+='=';
-
- return key;
-}
-
-/**
- * main application start, called when DOM is fully loaded
- */
-$(function() {
- // hide "no javascript" message
- $('#noscript').hide();
-
- // If "burn after reading" is checked, disable discussion.
- $('#burnafterreading').change(function() {
- if ($(this).is(':checked') ) {
- $('#opendisc').addClass('buttondisabled');
- $('#opendiscussion').attr({checked: false});
- $('#opendiscussion').attr('disabled',true);
- }
- else {
- $('#opendisc').removeClass('buttondisabled');
- $('#opendiscussion').removeAttr('disabled');
- }
- });
-
- // Display status returned by php code if any (eg. Paste was properly deleted.)
- if ($('#status').text().length > 0) {
- showStatus($('#status').text(),false);
- return;
- }
-
- $('#status').html(' '); // Keep line height even if content empty.
-
- // Display an existing paste
- if ($('#cipherdata').text().length > 1) {
- // Missing decryption key in URL ?
- if (window.location.hash.length == 0) {
- showError('Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL ?)');
- return;
- }
-
- // List of messages to display
- var messages = jQuery.parseJSON($('#cipherdata').text());
-
- // Show proper elements on screen.
- stateExistingPaste();
-
- displayMessages(pageKey(), messages);
- }
- // Display error message from php code.
- else if ($('#errormessage').text().length>1) {
- showError($('#errormessage').text());
- }
- // Create a new paste.
- else {
- newPaste();
- }
+ /**
+ * main application start, called when DOM is fully loaded
+ */
+ zerobin.init();
});
+
diff --git a/tpl/bootstrap.html b/tpl/bootstrap.html
index 28f0518..74c8100 100644
--- a/tpl/bootstrap.html
+++ b/tpl/bootstrap.html
@@ -33,25 +33,25 @@
- {function="t('ZeroBin')"}
+ {function="t('ZeroBin')"}
-