zerobin.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. /**
  2. * ZeroBin
  3. *
  4. * a zero-knowledge paste bin
  5. *
  6. * @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
  7. * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
  8. * @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
  9. * @version 0.20
  10. */
  11. // Immediately start random number generator collector.
  12. sjcl.random.startCollectors();
  13. /**
  14. * Converts a duration (in seconds) into human readable format.
  15. *
  16. * @param int seconds
  17. * @return string
  18. */
  19. function secondsToHuman(seconds)
  20. {
  21. if (seconds<60) { var v=Math.floor(seconds); return v+' second'+((v>1)?'s':''); }
  22. if (seconds<60*60) { var v=Math.floor(seconds/60); return v+' minute'+((v>1)?'s':''); }
  23. if (seconds<60*60*24) { var v=Math.floor(seconds/(60*60)); return v+' hour'+((v>1)?'s':''); }
  24. // If less than 2 months, display in days:
  25. if (seconds<60*60*24*60) { var v=Math.floor(seconds/(60*60*24)); return v+' day'+((v>1)?'s':''); }
  26. var v=Math.floor(seconds/(60*60*24*30)); return v+' month'+((v>1)?'s':'');
  27. }
  28. /**
  29. * Converts an associative array to an encoded string
  30. * for appending to the anchor.
  31. *
  32. * @param object associative_array Object to be serialized
  33. * @return string
  34. */
  35. function hashToParameterString(associativeArray)
  36. {
  37. var parameterString = "";
  38. for (key in associativeArray)
  39. {
  40. if( parameterString === "" )
  41. {
  42. parameterString = encodeURIComponent(key);
  43. parameterString += "=" + encodeURIComponent(associativeArray[key]);
  44. } else {
  45. parameterString += "&" + encodeURIComponent(key);
  46. parameterString += "=" + encodeURIComponent(associativeArray[key]);
  47. }
  48. }
  49. //padding for URL shorteners
  50. parameterString += "&p=p";
  51. return parameterString;
  52. }
  53. /**
  54. * Converts a string to an associative array.
  55. *
  56. * @param string parameter_string String containing parameters
  57. * @return object
  58. */
  59. function parameterStringToHash(parameterString)
  60. {
  61. var parameterHash = {};
  62. var parameterArray = parameterString.split("&");
  63. for (var i = 0; i < parameterArray.length; i++) {
  64. //var currentParamterString = decodeURIComponent(parameterArray[i]);
  65. var pair = parameterArray[i].split("=");
  66. var key = decodeURIComponent(pair[0]);
  67. var value = decodeURIComponent(pair[1]);
  68. parameterHash[key] = value;
  69. }
  70. return parameterHash;
  71. }
  72. /**
  73. * Get an associative array of the parameters found in the anchor
  74. *
  75. * @return object
  76. */
  77. function getParameterHash()
  78. {
  79. var hashIndex = window.location.href.indexOf("#");
  80. if (hashIndex >= 0) {
  81. return parameterStringToHash(window.location.href.substring(hashIndex + 1));
  82. } else {
  83. return {};
  84. }
  85. }
  86. /**
  87. * Compress a message (deflate compression). Returns base64 encoded data.
  88. *
  89. * @param string message
  90. * @return base64 string data
  91. */
  92. function compress(message) {
  93. return Base64.toBase64( RawDeflate.deflate( Base64.utob(message) ) );
  94. }
  95. /**
  96. * Decompress a message compressed with compress().
  97. */
  98. function decompress(data) {
  99. return Base64.btou( RawDeflate.inflate( Base64.fromBase64(data) ) );
  100. }
  101. /**
  102. * Compress, then encrypt message with key.
  103. *
  104. * @param string key
  105. * @param string message
  106. * @return encrypted string data
  107. */
  108. function zeroCipher(key, message) {
  109. if ($('#passwordinput').val().length == 0) {
  110. return sjcl.encrypt(key, compress(message));
  111. }
  112. return sjcl.encrypt(key + sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash($("#passwordinput").val())), compress(message));
  113. }
  114. /**
  115. * Decrypt message with key, then decompress.
  116. *
  117. * @param string key
  118. * @param encrypted string data
  119. * @return string readable message
  120. */
  121. function zeroDecipher(key, data) {
  122. if (data != undefined) {
  123. try {
  124. return decompress(sjcl.decrypt(key, data));
  125. } catch (err) {
  126. try {
  127. if ($('#passwordinput').val().length > 0) {
  128. password = $('#passwordinput').val();
  129. } else {
  130. password = prompt("Please enter the password for this paste:", "");
  131. if (password == null) return null;
  132. }
  133. data = decompress(sjcl.decrypt(key + sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(password)), data));
  134. $('#passwordinput').val(password);
  135. return data;
  136. } catch (err) {
  137. return zeroDecipher(key, data);
  138. }
  139. }
  140. }
  141. }
  142. /**
  143. * Get the current script location (without search or hash part of the URL).
  144. * eg. http://server.com/zero/?aaaa#bbbb --> http://server.com/zero/
  145. *
  146. * @return string current script location
  147. */
  148. function scriptLocation() {
  149. var scriptLocation = window.location.href.substring(0,window.location.href.length
  150. - window.location.search.length - window.location.hash.length);
  151. var hashIndex = scriptLocation.indexOf("#");
  152. if (hashIndex !== -1) {
  153. scriptLocation = scriptLocation.substring(0, hashIndex);
  154. }
  155. return scriptLocation;
  156. }
  157. /**
  158. * Get the pastes unique identifier from the URL
  159. * eg. http://server.com/zero/?c05354954c49a487#xxx --> c05354954c49a487
  160. *
  161. * @return string unique identifier
  162. */
  163. function pasteID() {
  164. return window.location.search.substring(1);
  165. }
  166. /**
  167. * Convert all applicable characters to HTML entities
  168. *
  169. * @param string str
  170. * @returns string encoded string
  171. */
  172. function htmlEntities(str) {
  173. return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
  174. }
  175. /**
  176. * Set text of a DOM element (required for IE)
  177. * This is equivalent to element.text(text)
  178. *
  179. * @param object element : a DOM element.
  180. * @param string text : the text to enter.
  181. */
  182. function setElementText(element, text) {
  183. // For IE<10.
  184. if ($('#oldienotice').is(":visible")) {
  185. // IE<10 does not support white-space:pre-wrap; so we have to do this BIG UGLY STINKING THING.
  186. var html = htmlEntities(text).replace(/\n/ig,"\r\n<br>");
  187. element.html('<pre>'+html+'</pre>');
  188. }
  189. // for other (sane) browsers:
  190. else {
  191. element.text(text);
  192. }
  193. }
  194. /**
  195. * Show decrypted text in the display area, including discussion (if open)
  196. *
  197. * @param string key : decryption key
  198. * @param array comments : Array of messages to display (items = array with keys ('data','meta')
  199. */
  200. function displayMessages(key, comments) {
  201. try { // Try to decrypt the paste.
  202. var cleartext = zeroDecipher(key, comments[0].data);
  203. if (cleartext == null) throw "password prompt canceled";
  204. } catch(err) {
  205. $('#cleartext').addClass('hidden');
  206. $('#prettymessage').addClass('hidden');
  207. $('#clonebutton').addClass('hidden');
  208. showError('Could not decrypt data (Wrong key ?)');
  209. return;
  210. }
  211. setElementText($('#cleartext'), cleartext);
  212. setElementText($('#prettyprint'), cleartext);
  213. // Convert URLs to clickable links.
  214. urls2links($('#cleartext'));
  215. urls2links($('#prettyprint'));
  216. if (typeof prettyPrint == 'function') prettyPrint();
  217. // Display paste expiration.
  218. if (comments[0].meta.expire_date) $('#remainingtime').removeClass('foryoureyesonly').text('This document will expire in '+secondsToHuman(comments[0].meta.remaining_time)+'.').removeClass('hidden');
  219. if (comments[0].meta.burnafterreading) {
  220. $.get(scriptLocation() + "?pasteid=" + pasteID() + '&deletetoken=burnafterreading', 'json')
  221. .fail(function() {
  222. showError('Could not delete the paste, it was not stored in burn after reading mode.');
  223. });
  224. $('#remainingtime').addClass('foryoureyesonly').text('FOR YOUR EYES ONLY. Don\'t close this window, this message can\'t be displayed again.').removeClass('hidden');
  225. $('#clonebutton').addClass('hidden'); // Discourage cloning (as it can't really be prevented).
  226. }
  227. // If the discussion is opened on this paste, display it.
  228. if (comments[0].meta.opendiscussion) {
  229. $('#comments').html('');
  230. // iterate over comments
  231. for (var i = 1; i < comments.length; i++) {
  232. var comment=comments[i];
  233. var cleartext="[Could not decrypt comment ; Wrong key ?]";
  234. try {
  235. cleartext = zeroDecipher(key, comment.data);
  236. } catch(err) { }
  237. var place = $('#comments');
  238. // If parent comment exists, display below (CSS will automatically shift it right.)
  239. var cname = '#comment_'+comment.meta.parentid;
  240. // If the element exists in page
  241. if ($(cname).length) {
  242. place = $(cname);
  243. }
  244. var divComment = $('<article><div class="comment" id="comment_' + comment.meta.commentid+'">'
  245. + '<div class="commentmeta"><span class="nickname"></span><span class="commentdate"></span></div><div class="commentdata"></div>'
  246. + '<button onclick="open_reply($(this),\'' + comment.meta.commentid + '\');return false;" class="btn btn-default">Reply</button>'
  247. + '</div></article>');
  248. setElementText(divComment.find('div.commentdata'), cleartext);
  249. // Convert URLs to clickable links in comment.
  250. urls2links(divComment.find('div.commentdata'));
  251. divComment.find('span.nickname').html('<i>(Anonymous)</i>');
  252. // Try to get optional nickname:
  253. try {
  254. divComment.find('span.nickname').text(zeroDecipher(key, comment.meta.nickname));
  255. } catch(err) { }
  256. divComment.find('span.commentdate').text(' ('+(new Date(comment.meta.postdate*1000).toString())+')').attr('title','CommentID: ' + comment.meta.commentid);
  257. // If an avatar is available, display it.
  258. if (comment.meta.vizhash) {
  259. divComment.find('span.nickname').before('<img src="' + comment.meta.vizhash + '" class="vizhash" title="Anonymous avatar (Vizhash of the IP address)" /> ');
  260. }
  261. place.append(divComment);
  262. }
  263. $('#comments').append('<div class="comment"><button onclick="open_reply($(this),\'' + pasteID() + '\');return false;" class="btn btn-default">Add comment</button></div>');
  264. $('#discussion').removeClass('hidden');
  265. }
  266. }
  267. /**
  268. * Open the comment entry when clicking the "Reply" button of a comment.
  269. *
  270. * @param object source : element which emitted the event.
  271. * @param string commentid = identifier of the comment we want to reply to.
  272. */
  273. function open_reply(source, commentid) {
  274. $('div.reply').remove(); // Remove any other reply area.
  275. source.after('<div class="reply">'
  276. + '<input type="text" id="nickname" class="form-control" title="Optional nickname..." value="Optional nickname..." />'
  277. + '<textarea id="replymessage" class="replymessage form-control" cols="80" rows="7"></textarea>'
  278. + '<br /><button id="replybutton" onclick="send_comment(\'' + commentid + '\');return false;" class="btn btn-default">Post comment</button>'
  279. + '<div id="replystatus"> </div>'
  280. + '</div>');
  281. $('#nickname').focus(function() {
  282. if ($(this).val() == $(this).attr('title')) {
  283. $(this).val('');
  284. }
  285. });
  286. $('#replymessage').focus();
  287. }
  288. /**
  289. * Send a reply in a discussion.
  290. *
  291. * @param string parentid : the comment identifier we want to send a reply to.
  292. */
  293. function send_comment(parentid) {
  294. // Do not send if no data.
  295. if ($('#replymessage').val().length==0) {
  296. return;
  297. }
  298. showStatus('Sending comment...', spin=true);
  299. var cipherdata = zeroCipher(pageKey(), $('#replymessage').val());
  300. var ciphernickname = '';
  301. var nick = $('#nickname').val();
  302. if (nick != '' && nick != 'Optional nickname...') {
  303. ciphernickname = zeroCipher(pageKey(), nick);
  304. }
  305. var data_to_send = { data:cipherdata,
  306. parentid: parentid,
  307. pasteid: pasteID(),
  308. nickname: ciphernickname
  309. };
  310. $.post(scriptLocation(), data_to_send, function(data) {
  311. if (data.status == 0) {
  312. showStatus('Comment posted.');
  313. $.get(scriptLocation() + "?" + pasteID() + "&json", function(data) {
  314. if (data.status == 0) {
  315. displayMessages(pageKey(), data.messages);
  316. }
  317. else if (data.status == 1) {
  318. showError('Could not refresh display: ' + data.message);
  319. }
  320. else
  321. {
  322. showError('Could not refresh display: unknown status');
  323. }
  324. }, 'json')
  325. .fail(function() {
  326. showError('Could not refresh display (server error or not responding).');
  327. });
  328. }
  329. else if (data.status == 1) {
  330. showError('Could not post comment: ' + data.message);
  331. }
  332. else
  333. {
  334. showError('Could not post comment: unknown status');
  335. }
  336. }, 'json')
  337. .fail(function() {
  338. showError('Comment could not be sent (server error or not responding).');
  339. });
  340. }
  341. /**
  342. * Send a new paste to server
  343. */
  344. function send_data() {
  345. // Do not send if no data.
  346. if ($('#message').val().length == 0) {
  347. return;
  348. }
  349. // If sjcl has not collected enough entropy yet, display a message.
  350. if (!sjcl.random.isReady())
  351. {
  352. showStatus('Sending paste (Please move your mouse for more entropy)...', spin=true);
  353. sjcl.random.addEventListener('seeded', function(){ send_data(); });
  354. return;
  355. }
  356. showStatus('Sending paste...', spin=true);
  357. var randomkey = sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0);
  358. var cipherdata = zeroCipher(randomkey, $('#message').val());
  359. var data_to_send = { data: cipherdata,
  360. expire: $('#pasteExpiration').val(),
  361. burnafterreading: $('#burnafterreading').is(':checked') ? 1 : 0,
  362. opendiscussion: $('#opendiscussion').is(':checked') ? 1 : 0
  363. };
  364. $.post(scriptLocation(), data_to_send, function(data) {
  365. if (data.status == 0) {
  366. stateExistingPaste();
  367. var url = scriptLocation() + "?" + data.id + '#' + randomkey;
  368. var deleteUrl = scriptLocation() + "?pasteid=" + data.id + '&deletetoken=' + data.deletetoken;
  369. showStatus('');
  370. $('#pastelink').html('Your paste is <a id="pasteurl" href="' + url + '">' + url + '</a> <span id="copyhint">(Hit CTRL+C to copy)</span>');
  371. $('#deletelink').html('<a href="' + deleteUrl + '">Delete data</a>');
  372. $('#pasteresult').removeClass('hidden');
  373. selectText('pasteurl'); // We pre-select the link so that the user only has to CTRL+C the link.
  374. setElementText($('#cleartext'), $('#message').val());
  375. setElementText($('#prettyprint'), $('#message').val());
  376. // Convert URLs to clickable links.
  377. urls2links($('#cleartext'));
  378. urls2links($('#prettyprint'));
  379. showStatus('');
  380. if (typeof prettyPrint == 'function') prettyPrint();
  381. }
  382. else if (data.status==1) {
  383. showError('Could not create paste: ' + data.message);
  384. }
  385. else {
  386. showError('Could not create paste.');
  387. }
  388. }, 'json')
  389. .fail(function() {
  390. showError('Data could not be sent (server error or not responding).');
  391. });
  392. }
  393. /**
  394. * Text range selection.
  395. * From: http://stackoverflow.com/questions/985272/jquery-selecting-text-in-an-element-akin-to-highlighting-with-your-mouse
  396. *
  397. * @param string element : Indentifier of the element to select (id="").
  398. */
  399. function selectText(element) {
  400. var doc = document
  401. , text = doc.getElementById(element)
  402. , range, selection
  403. ;
  404. if (doc.body.createTextRange) { // MS
  405. range = doc.body.createTextRange();
  406. range.moveToElementText(text);
  407. range.select();
  408. } else if (window.getSelection) { // all others
  409. selection = window.getSelection();
  410. range = doc.createRange();
  411. range.selectNodeContents(text);
  412. selection.removeAllRanges();
  413. selection.addRange(range);
  414. }
  415. }
  416. /**
  417. * Put the screen in "New paste" mode.
  418. */
  419. function stateNewPaste() {
  420. $('#sendbutton').removeClass('hidden');
  421. $('#clonebutton').addClass('hidden');
  422. $('#rawtextbutton').addClass('hidden');
  423. $('#expiration').removeClass('hidden');
  424. $('#remainingtime').addClass('hidden');
  425. $('#burnafterreadingoption').removeClass('hidden');
  426. $('#opendisc').removeClass('hidden');
  427. $('#newbutton').removeClass('hidden');
  428. $('#pasteresult').addClass('hidden');
  429. $('#message').text('');
  430. $('#message').removeClass('hidden');
  431. $('#cleartext').addClass('hidden');
  432. $('#message').focus();
  433. $('#discussion').addClass('hidden');
  434. $('#prettymessage').addClass('hidden');
  435. // Show password field
  436. $('#password').removeClass('hidden');
  437. }
  438. /**
  439. * Put the screen in "Existing paste" mode.
  440. */
  441. function stateExistingPaste() {
  442. $('#sendbutton').addClass('hidden');
  443. // No "clone" for IE<10.
  444. if ($('#oldienotice').is(":visible")) {
  445. $('#clonebutton').addClass('hidden');
  446. }
  447. else {
  448. $('#clonebutton').removeClass('hidden');
  449. }
  450. $('#rawtextbutton').removeClass('hidden');
  451. $('#expiration').addClass('hidden');
  452. $('#burnafterreadingoption').addClass('hidden');
  453. $('#opendisc').addClass('hidden');
  454. $('#newbutton').removeClass('hidden');
  455. $('#pasteresult').addClass('hidden');
  456. $('#message').addClass('hidden');
  457. $('#cleartext').addClass('hidden');
  458. $('#prettymessage').removeClass('hidden');
  459. }
  460. /**
  461. * Return raw text
  462. */
  463. function rawText()
  464. {
  465. var paste = $('#cleartext').html();
  466. var newDoc = document.open('text/html', 'replace');
  467. newDoc.write('<pre>'+paste+'</pre>');
  468. newDoc.close();
  469. }
  470. /**
  471. * Clone the current paste.
  472. */
  473. function clonePaste() {
  474. stateNewPaste();
  475. //Erase the id and the key in url
  476. history.replaceState(document.title, document.title, scriptLocation());
  477. showStatus('');
  478. $('#message').text($('#cleartext').text());
  479. }
  480. /**
  481. * Create a new paste.
  482. */
  483. function newPaste() {
  484. stateNewPaste();
  485. showStatus('');
  486. $('#message').text('');
  487. }
  488. /**
  489. * Display an error message
  490. * (We use the same function for paste and reply to comments)
  491. */
  492. function showError(message) {
  493. if ($('#status').length) {
  494. $('#status').addClass('errorMessage').text(message);
  495. } else {
  496. $('#errormessage').removeClass('hidden').append(message);
  497. }
  498. $('#replystatus').addClass('errorMessage').text(message);
  499. }
  500. /**
  501. * Display status
  502. * (We use the same function for paste and reply to comments)
  503. *
  504. * @param string message : text to display
  505. * @param boolean spin (optional) : tell if the "spinning" animation should be displayed.
  506. */
  507. function showStatus(message, spin) {
  508. $('#replystatus').removeClass('errorMessage');
  509. $('#replystatus').text(message);
  510. if (!message) {
  511. $('#status').html(' ');
  512. return;
  513. }
  514. if (message == '') {
  515. $('#status').html(' ');
  516. return;
  517. }
  518. $('#status').removeClass('errorMessage');
  519. $('#status').text(message);
  520. if (spin) {
  521. var img = '<img src="img/busy.gif" style="width:16px;height:9px;margin:0px 4px 0px 0px;" />';
  522. $('#status').prepend(img);
  523. $('#replystatus').prepend(img);
  524. }
  525. }
  526. /**
  527. * Convert URLs to clickable links.
  528. * URLs to handle:
  529. * <code>
  530. * magnet:?xt.1=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C&xt.2=urn:sha1:TXGCZQTH26NL6OUQAJJPFALHG2LTGBC7
  531. * http://localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
  532. * http://user:password@localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
  533. * </code>
  534. *
  535. * @param object element : a jQuery DOM element.
  536. */
  537. function urls2links(element) {
  538. var re = /((http|https|ftp):\/\/[\w?=&.\/-;#@~%+-]+(?![\w\s?&.\/;#~%"=-]*>))/ig;
  539. element.html(element.html().replace(re,'<a href="$1" rel="nofollow">$1</a>'));
  540. var re = /((magnet):[\w?=&.\/-;#@~%+-]+)/ig;
  541. element.html(element.html().replace(re,'<a href="$1">$1</a>'));
  542. }
  543. /**
  544. * Return the deciphering key stored in anchor part of the URL
  545. */
  546. function pageKey() {
  547. var key = window.location.hash.substring(1); // Get key
  548. // Some stupid web 2.0 services and redirectors add data AFTER the anchor
  549. // (such as &utm_source=...).
  550. // We will strip any additional data.
  551. // First, strip everything after the equal sign (=) which signals end of base64 string.
  552. i = key.indexOf('='); if (i>-1) { key = key.substring(0,i+1); }
  553. // If the equal sign was not present, some parameters may remain:
  554. i = key.indexOf('&'); if (i>-1) { key = key.substring(0,i); }
  555. // Then add trailing equal sign if it's missing
  556. if (key.charAt(key.length-1)!=='=') key+='=';
  557. return key;
  558. }
  559. /**
  560. * main application start, called when DOM is fully loaded
  561. */
  562. $(function() {
  563. // hide "no javascript" message
  564. $('#noscript').hide();
  565. // If "burn after reading" is checked, disable discussion.
  566. $('#burnafterreading').change(function() {
  567. if ($(this).is(':checked') ) {
  568. $('#opendisc').addClass('buttondisabled');
  569. $('#opendiscussion').attr({checked: false});
  570. $('#opendiscussion').attr('disabled',true);
  571. }
  572. else {
  573. $('#opendisc').removeClass('buttondisabled');
  574. $('#opendiscussion').removeAttr('disabled');
  575. }
  576. });
  577. // Display status returned by php code if any (eg. Paste was properly deleted.)
  578. if ($('#status').text().length > 0) {
  579. showStatus($('#status').text(),false);
  580. return;
  581. }
  582. $('#status').html(' '); // Keep line height even if content empty.
  583. // Display an existing paste
  584. if ($('#cipherdata').text().length > 1) {
  585. // Missing decryption key in URL ?
  586. if (window.location.hash.length == 0) {
  587. showError('Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL ?)');
  588. return;
  589. }
  590. // List of messages to display
  591. var messages = jQuery.parseJSON($('#cipherdata').text());
  592. // Show proper elements on screen.
  593. stateExistingPaste();
  594. displayMessages(pageKey(), messages);
  595. }
  596. // Display error message from php code.
  597. else if ($('#errormessage').text().length>1) {
  598. showError($('#errormessage').text());
  599. }
  600. // Create a new paste.
  601. else {
  602. newPaste();
  603. }
  604. });