/**
* PrivateBin
*
* a zero-knowledge paste bin
*
* @see {@link https://github.com/PrivateBin/PrivateBin}
* @copyright 2012 Sébastien SAUVAGE ({@link http://sebsauvage.net})
* @license {@link https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License}
* @version 1.1
* @name PrivateBin
* @namespace
*/
'use strict';
/** global: Base64 */
/** global: FileReader */
/** global: RawDeflate */
/** global: history */
/** global: navigator */
/** global: prettyPrint */
/** global: prettyPrintOne */
/** global: showdown */
/** global: sjcl */
// Immediately start random number generator collector.
sjcl.random.startCollectors();
jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
/**
* static helper methods
*
* @name helper
* @class
*/
var helper = {
/**
* converts a duration (in seconds) into human friendly approximation
*
* @name helper.secondsToHuman
* @function
* @param {number} seconds
* @return {Array}
*/
secondsToHuman: function(seconds)
{
var v;
if (seconds < 60)
{
v = Math.floor(seconds);
return [v, 'second'];
}
if (seconds < 60 * 60)
{
v = Math.floor(seconds / 60);
return [v, 'minute'];
}
if (seconds < 60 * 60 * 24)
{
v = Math.floor(seconds / (60 * 60));
return [v, 'hour'];
}
// If less than 2 months, display in days:
if (seconds < 60 * 60 * 24 * 60)
{
v = Math.floor(seconds / (60 * 60 * 24));
return [v, 'day'];
}
v = Math.floor(seconds / (60 * 60 * 24 * 30));
return [v, 'month'];
},
/**
* text range selection
*
* @see {@link https://stackoverflow.com/questions/985272/jquery-selecting-text-in-an-element-akin-to-highlighting-with-your-mouse}
* @name helper.selectText
* @function
* @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)
*
* @name helper.setElementText
* @function
* @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...
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); } }, /** * replace last child of element with message * * @name helper.setMessage * @function * @param {Object} element - a jQuery wrapped DOM element * @param {string} message - the message to append */ setMessage: function(element, message) { var content = element.contents(); if (content.length > 0) { content[content.length - 1].nodeValue = ' ' + message; } else { this.setElementText(element, message); } }, /** * convert URLs to clickable links. * URLs to handle: *
* magnet:?xt.1=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C&xt.2=urn:sha1:TXGCZQTH26NL6OUQAJJPFALHG2LTGBC7 * http://example.com:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM= * http://user:example.com@localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM= ** * @name helper.urls2links * @function * @param {Object} element - a jQuery DOM element */ urls2links: function(element) { var markup = '$1'; element.html( element.html().replace( /((http|https|ftp):\/\/[\w?=&.\/-;#@~%+-]+(?![\w\s?&.\/;#~%"=-]*>))/ig, markup ) ); element.html( element.html().replace( /((magnet):[\w?=&.\/-;#@~%+-]+)/ig, markup ) ); }, /** * minimal sprintf emulation for %s and %d formats * * @see {@link https://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format#4795914} * @name helper.sprintf * @function * @param {string} format * @param {...*} args - one or multiple parameters injected into format string * @return {string} */ sprintf: function() { var args = arguments; if (typeof arguments[0] === 'object') { args = arguments[0]; } var format = args[0], i = 1; return format.replace(/%((%)|s|d)/g, function (m) { // m is the matched format, e.g. %s, %d var val; if (m[2]) { val = m[2]; } else { val = args[i]; // A switch statement so that the formatter can be extended. switch (m) { case '%d': val = parseFloat(val); if (isNaN(val)) { val = 0; } break; default: // Default is %s } ++i; } return val; }); }, /** * get value of cookie, if it was set, empty string otherwise * * @see {@link http://www.w3schools.com/js/js_cookies.asp} * @name helper.getCookie * @function * @param {string} cname * @return {string} */ getCookie: function(cname) { var name = cname + '=', ca = document.cookie.split(';'); for (var i = 0; i < ca.length; ++i) { var c = ca[i]; while (c.charAt(0) === ' ') { c = c.substring(1); } if (c.indexOf(name) === 0) { return c.substring(name.length, c.length); } } return ''; }, /** * get the current script location (without search or hash part of the URL), * eg. http://example.com/path/?aaaa#bbbb --> http://example.com/path/ * * @name helper.scriptLocation * @function * @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 ), hashIndex = scriptLocation.indexOf('?'); if (hashIndex !== -1) { scriptLocation = scriptLocation.substring(0, hashIndex); } return scriptLocation; }, /** * get the pastes unique identifier from the URL, * eg. http://example.com/path/?c05354954c49a487#c05354954c49a487 returns c05354954c49a487 * * @name helper.pasteId * @function * @return {string} unique identifier */ pasteId: function() { return window.location.search.substring(1); }, /** * return the deciphering key stored in anchor part of the URL * * @name helper.pageKey * @function * @return {string} key */ pageKey: function() { var key = window.location.hash.substring(1), i = key.indexOf('&'); // Some web 2.0 services and redirectors add data AFTER the anchor // (such as &utm_source=...). We will strip any additional data. if (i > -1) { key = key.substring(0, i); } return key; }, /** * convert all applicable characters to HTML entities * * @see {@link https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.231_-_HTML_Escape_Before_Inserting_Untrusted_Data_into_HTML_Element_Content} * @name helper.htmlEntities * @function * @param {string} str * @return {string} escaped HTML */ htmlEntities: function(str) { return String(str).replace( /[&<>"'`=\/]/g, function(s) { return helper.entityMap[s]; }); }, /** * character to HTML entity lookup table * * @see {@link https://github.com/janl/mustache.js/blob/master/mustache.js#L60} * @name helper.entityMap * @enum {Object} * @readonly */ entityMap: { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/', '`': '`', '=': '=' } }; /** * static attachment helper methods * * @name helper * @class */ var attachmentHelpers = { attachmentData: undefined, file: undefined, /* * Read file data as dataURL using the FileReader API * https://developer.mozilla.org/en-US/docs/Web/API/FileReader#readAsDataURL() */ readFileData: function (file) { if (typeof FileReader === undefined) { // revert loading status… this.stateNewPaste(); this.showError(i18n._('Your browser does not support uploading encrypted files. Please use a newer browser.')); return; } var fr = new FileReader(); if (file === undefined) { file = controller.fileInput[0].files[0]; $('#dragAndDropFileName').text(''); } else { $('#dragAndDropFileName').text(file.name); } attachmentHelpers.file = file; fr.onload = function (e) { var dataURL = e.target.result; attachmentHelpers.attachmentData = dataURL; if (controller.messagePreview.parent().hasClass('active')) { attachmentHelpers.handleFilePreviews(controller.attachmentPreview, dataURL); } }; fr.readAsDataURL(file); }, /** * Handle the preview of files. * @argument {DOM Element} element where the preview should be appended. * @argument {File Data} data of the file to be displayed. */ handleFilePreviews: function (element, data) { if (data) { var mimeType = this.getMimeTypeFromDataURL(data); if (mimeType.match(/image\//i)) { this.showImagePreview(element, data); } else if (mimeType.match(/video\//i)) { this.showVideoPreview(element, data, mimeType); } else if (mimeType.match(/audio\//i)) { this.showAudioPreview(element, data, mimeType); } else if (mimeType.match(/\/pdf/i)) { this.showPDFPreview(element, data); } //else { //console.log("file but no image/video/audio/pdf"); //} } }, /** * Get Mime Type from a DataURL * * @param {type} dataURL * @returns Mime Type from a dataURL as obtained for a file using the FileReader API https://developer.mozilla.org/en-US/docs/Web/API/FileReader#readAsDataURL() */ getMimeTypeFromDataURL: function (dataURL) { return dataURL.slice(dataURL.indexOf('data:') + 5, dataURL.indexOf(';base64,')); }, showImagePreview: function (element, image) { element.html( $(document.createElement('img')) .attr('src', image) .attr('class', 'img-thumbnail') ); element.removeClass('hidden'); }, showVideoPreview: function (element, video, mimeType) { var videoPlayer = $(document.createElement('video')) .attr('controls', 'true') .attr('autoplay', 'true') .attr('loop', 'true') .attr('class', 'img-thumbnail'); videoPlayer.append($(document.createElement('source')) .attr('type', mimeType) .attr('src', video)); element.html(videoPlayer); element.removeClass('hidden'); }, showAudioPreview: function (element, audio, mimeType) { var audioPlayer = $(document.createElement('audio')) .attr('controls', 'true') .attr('autoplay', 'true'); audioPlayer.append($(document.createElement('source')) .attr('type', mimeType) .attr('src', audio)); element.html(audioPlayer); element.removeClass('hidden'); }, showPDFPreview: function (element, pdf) { //PDFs are only displayed if the filesize is smaller than about 1MB (after base64 encoding). //Bigger filesizes currently cause crashes in various browsers. //See also: https://code.google.com/p/chromium/issues/detail?id=69227 //Firefox crashes with files that are about 1.5MB //The performance with 1MB files is bareable if (pdf.length < 1398488) { //Fallback for browsers, that don't support the vh unit var clientHeight = $(window).height(); element.html( $(document.createElement('embed')) .attr('src', pdf) .attr('type', 'application/pdf') .attr('class', 'pdfPreview') .css('height', clientHeight) ); element.removeClass('hidden'); } else { controller.showError(i18n._('File too large, to display a preview. Please download the attachment.')); } }, addDragDropHandler: function () { var fileInput = controller.fileInput; if (fileInput.length === 0) { return; } function ignoreDragDrop(e) { e.stopPropagation(); e.preventDefault(); } function drop(e) { e.stopPropagation(); e.preventDefault(); if (fileInput) { var file = e.dataTransfer.files[0]; //Clear the file input: fileInput.wrap('