2012-04-22 06:45:45 -04:00
/ * *
2016-07-11 05:58:15 -04:00
* PrivateBin
2012-04-23 10:30:02 -04:00
*
2012-04-30 16:58:08 -04:00
* a zero - knowledge paste bin
*
2017-01-14 09:29:12 -05:00
* @ 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}
2016-12-26 06:13:50 -05:00
* @ version 1.1
2017-01-14 09:29:12 -05:00
* @ name PrivateBin
* @ namespace
2012-04-22 06:45:45 -04:00
* /
2012-04-21 15:59:45 -04:00
2016-07-11 09:47:42 -04:00
/** global: Base64 */
/** global: FileReader */
/** global: RawDeflate */
/** global: history */
/** global: navigator */
/** global: prettyPrint */
/** global: prettyPrintOne */
/** global: showdown */
/** global: sjcl */
2015-09-06 07:07:46 -04:00
2012-04-21 15:59:45 -04:00
// Immediately start random number generator collector.
sjcl . random . startCollectors ( ) ;
2017-02-14 16:21:55 -05:00
// main application start, called when DOM is fully loaded
jQuery ( document ) . ready ( function ( ) {
// run main controller
$ . PrivateBin . Controller . init ( ) ;
} ) ;
2017-02-08 14:12:22 -05:00
jQuery . PrivateBin = function ( $ , sjcl , Base64 , RawDeflate ) {
2017-02-12 12:08:08 -05:00
'use strict' ;
2017-02-08 14:11:04 -05:00
/ * *
2017-02-14 16:21:55 -05:00
* static Helper methods
2017-02-08 14:11:04 -05:00
*
2017-02-08 14:12:22 -05:00
* @ param { object } window
* @ param { object } document
* @ class
2017-02-08 14:11:04 -05:00
* /
2017-02-14 16:21:55 -05:00
var Helper = ( function ( window , document ) {
2017-02-08 14:12:22 -05:00
var me = { } ;
/ * *
* character to HTML entity lookup table
*
* @ see { @ link https : //github.com/janl/mustache.js/blob/master/mustache.js#L60}
* @ private
* @ enum { Object }
* @ readonly
* /
var entityMap = {
'&' : '&' ,
'<' : '<' ,
'>' : '>' ,
'"' : '"' ,
"'" : ''' ,
'/' : '/' ,
'`' : '`' ,
'=' : '='
} ;
2015-09-05 11:12:11 -04:00
2017-02-08 14:12:22 -05:00
/ * *
* cache for script location
*
* @ private
* @ enum { string | null }
* /
2017-02-14 16:21:55 -05:00
var baseUri = null ;
2017-02-08 14:12:22 -05:00
/ * *
* converts a duration ( in seconds ) into human friendly approximation
*
2017-02-14 16:21:55 -05:00
* @ name Helper . secondsToHuman
2017-02-08 14:12:22 -05:00
* @ function
* @ param { number } seconds
* @ return { Array }
* /
me . secondsToHuman = function ( seconds )
2017-02-08 14:11:04 -05:00
{
2017-02-08 14:12:22 -05:00
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}
2017-02-14 16:21:55 -05:00
* @ name Helper . selectText
2017-02-08 14:12:22 -05:00
* @ function
* @ param { HTMLElement } element
* /
me . selectText = function ( element )
2017-02-08 14:11:04 -05:00
{
2017-02-08 14:12:22 -05:00
var range , selection ;
2015-09-05 11:12:11 -04:00
2017-02-08 14:12:22 -05:00
// MS
2017-02-14 16:21:55 -05:00
if ( document . body . createTextRange ) {
2017-02-08 14:12:22 -05:00
range = document . body . createTextRange ( ) ;
range . moveToElementText ( element ) ;
range . select ( ) ;
2017-02-14 16:21:55 -05:00
} else if ( window . getSelection ) {
2017-02-08 14:12:22 -05:00
selection = window . getSelection ( ) ;
range = document . createRange ( ) ;
range . selectNodeContents ( element ) ;
selection . removeAllRanges ( ) ;
selection . addRange ( range ) ;
}
} ;
2015-09-05 11:12:11 -04:00
2017-02-08 14:12:22 -05:00
/ * *
* set text of a jQuery element ( required for IE ) ,
*
2017-02-14 16:21:55 -05:00
* @ name Helper . setElementText
2017-02-08 14:12:22 -05:00
* @ function
* @ param { jQuery } $element - a jQuery element
* @ param { string } text - the text to enter
* /
me . setElementText = function ( $element , text )
2015-09-12 11:33:16 -04:00
{
2017-02-08 14:12:22 -05:00
// For IE<10: Doesn't support white-space:pre-wrap; so we have to do this...
if ( $ ( '#oldienotice' ) . is ( ':visible' ) ) {
var html = me . htmlEntities ( text ) . replace ( /\n/ig , '\r\n<br>' ) ;
$element . html ( '<pre>' + html + '</pre>' ) ;
}
// for other (sane) browsers:
else
{
$element . text ( text ) ;
}
} ;
2015-09-12 11:33:16 -04:00
2017-02-08 14:12:22 -05:00
/ * *
* convert URLs to clickable links .
* URLs to handle :
* < pre >
* 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=
* < / p r e >
*
2017-02-14 16:21:55 -05:00
* @ name Helper . urls2links
2017-02-08 14:12:22 -05:00
* @ function
* @ param { Object } element - a jQuery DOM element
* /
2017-02-14 16:21:55 -05:00
me . urls2links = function ( $element )
2017-02-08 14:11:04 -05:00
{
2017-02-08 14:12:22 -05:00
var markup = '<a href="$1" rel="nofollow">$1</a>' ;
2017-02-14 16:21:55 -05:00
$element . html (
$element . html ( ) . replace (
2017-02-08 14:12:22 -05:00
/((http|https|ftp):\/\/[\w?=&.\/-;#@~%+-]+(?![\w\s?&.\/;#~%"=-]*>))/ig ,
markup
)
) ;
2017-02-14 16:21:55 -05:00
$element . html (
$element . html ( ) . replace (
2017-02-08 14:12:22 -05:00
/((magnet):[\w?=&.\/-;#@~%+-]+)/ig ,
markup
)
) ;
} ;
2017-02-08 14:11:04 -05:00
2017-02-08 14:12:22 -05:00
/ * *
* minimal sprintf emulation for % s and % d formats
*
* @ see { @ link https : //stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format#4795914}
2017-02-14 16:21:55 -05:00
* @ name Helper . sprintf
2017-02-08 14:12:22 -05:00
* @ function
* @ param { string } format
* @ param { ... * } args - one or multiple parameters injected into format string
* @ return { string }
* /
me . sprintf = function ( )
2017-02-08 14:11:04 -05:00
{
2017-02-14 16:21:55 -05:00
var args = Array . prototype . slice . call ( arguments ) ;
2017-02-08 14:12:22 -05:00
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}
2017-02-14 16:21:55 -05:00
* @ name Helper . getCookie
2017-02-08 14:12:22 -05:00
* @ function
* @ param { string } cname
* @ return { string }
* /
me . 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 ) === ' ' )
2016-07-11 09:47:42 -04:00
{
2017-02-08 14:12:22 -05:00
c = c . substring ( 1 ) ;
}
if ( c . indexOf ( name ) === 0 )
{
return c . substring ( name . length , c . length ) ;
2016-07-11 09:47:42 -04:00
}
2015-09-19 05:21:13 -04:00
}
2017-02-08 14:12:22 -05:00
return '' ;
} ;
2016-07-19 10:12:11 -04:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-14 16:21:55 -05:00
* get the current location ( without search or hash part of the URL ) ,
2017-02-08 14:12:22 -05:00
* eg . http : //example.com/path/?aaaa#bbbb --> http://example.com/path/
*
2017-02-14 16:21:55 -05:00
* @ name Helper . baseUri
2017-02-08 14:12:22 -05:00
* @ function
2017-02-14 16:21:55 -05:00
* @ return { string }
2017-02-08 14:12:22 -05:00
* /
2017-02-14 16:21:55 -05:00
me . baseUri = function ( )
2017-02-08 14:12:22 -05:00
{
// check for cached version
2017-02-14 16:21:55 -05:00
if ( baseUri !== null ) {
return baseUri ;
2017-02-05 08:47:03 -05:00
}
2017-02-08 07:20:51 -05:00
2017-02-14 16:21:55 -05:00
// get official base uri string, from base tag in head of HTML
baseUri = document . baseURI ;
2015-09-05 11:12:11 -04:00
2017-02-14 16:21:55 -05:00
// if base uri contains query string (when no base tag is present),
// it is unwanted
if ( baseUri . indexOf ( '?' ) ) {
// so we built our own baseuri
baseUri = window . location . origin + window . location . pathname ;
2017-02-08 14:12:22 -05:00
}
2017-02-08 14:11:04 -05:00
2017-02-14 16:21:55 -05:00
return baseUri ;
2017-02-08 14:12:22 -05:00
} ;
2017-02-08 14:11:04 -05:00
2017-02-08 14:12:22 -05:00
/ * *
* 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}
2017-02-14 16:21:55 -05:00
* @ name Helper . htmlEntities
2017-02-08 14:12:22 -05:00
* @ function
* @ param { string } str
* @ return { string } escaped HTML
* /
me . htmlEntities = function ( str ) {
return String ( str ) . replace (
/[&<>"'`=\/]/g , function ( s ) {
return entityMap [ s ] ;
} ) ;
} ;
2017-02-08 14:11:04 -05:00
2017-02-08 14:12:22 -05:00
return me ;
} ) ( window , document ) ;
2015-09-06 07:07:46 -04:00
2017-02-08 14:11:04 -05:00
/ * *
2017-02-14 16:21:55 -05:00
* internationalization module
2017-02-08 14:11:04 -05:00
*
2017-02-08 14:12:22 -05:00
* @ param { object } window
* @ param { object } document
* @ class
2017-02-08 14:11:04 -05:00
* /
2017-02-14 16:21:55 -05:00
var I18n = ( function ( window , document ) {
2017-02-08 14:12:22 -05:00
var me = { } ;
2017-02-14 16:21:55 -05:00
/ * *
* const for string of loaded language
*
* @ private
* @ prop { string }
* @ readonly
* /
var languageLoadedEvent = 'languageLoaded' ;
2017-02-08 14:12:22 -05:00
/ * *
* supported languages , minus the built in 'en'
*
* @ private
* @ prop { string [ ] }
* @ readonly
* /
var supportedLanguages = [ 'de' , 'es' , 'fr' , 'it' , 'no' , 'pl' , 'oc' , 'ru' , 'sl' , 'zh' ] ;
/ * *
* built in language
*
* @ private
2017-02-14 16:21:55 -05:00
* @ prop { string | null }
2017-02-08 14:12:22 -05:00
* /
2017-02-14 16:21:55 -05:00
var language = null ;
2017-02-08 14:12:22 -05:00
/ * *
* translation cache
*
* @ private
* @ enum { Object }
* /
var translations = { } ;
/ * *
2017-02-14 16:21:55 -05:00
* translate a string , alias for I18n . translate ( )
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* for a full description see me . translate
*
* @ name I18n . _
2017-02-08 14:12:22 -05:00
* @ function
2017-02-14 16:21:55 -05:00
* @ param { jQuery } $element - optional
2017-02-08 14:12:22 -05:00
* @ param { string } messageId
* @ param { ... * } args - one or multiple parameters injected into placeholders
* @ return { string }
* /
me . _ = function ( )
2017-02-08 14:11:04 -05:00
{
2017-02-14 16:21:55 -05:00
return me . translate . apply ( this , arguments ) ;
2017-02-08 14:12:22 -05:00
} ;
/ * *
* translate a string
*
2017-02-14 16:21:55 -05:00
* Optionally pass a jQuery element as the first parameter , to automatically
* let the text of this element be replaced . In case the ( asynchronously
* loaded ) language is not downloadet yet , this will make sure the string
* is replaced when it is actually loaded .
* So for easy translations passing the jQuery object to apply it to is
* more save , especially when they are loaded in the beginning .
*
* @ name I18n . translate
2017-02-08 14:12:22 -05:00
* @ function
2017-02-14 16:21:55 -05:00
* @ param { jQuery } $element - optional
2017-02-08 14:12:22 -05:00
* @ param { string } messageId
* @ param { ... * } args - one or multiple parameters injected into placeholders
* @ return { string }
* /
me . translate = function ( )
2017-02-08 14:11:04 -05:00
{
2017-02-14 16:21:55 -05:00
// convert parameters to array
var args = Array . prototype . slice . call ( arguments ) ,
messageId ,
$element = null ;
// parse arguments
if ( args [ 0 ] instanceof jQuery ) {
// optional jQuery element as first parameter
$element = args [ 0 ] ;
args . shift ( ) ;
2015-09-06 09:54:43 -04:00
}
2017-02-14 16:21:55 -05:00
// extract messageId from arguments
2017-02-08 14:12:22 -05:00
var usesPlurals = $ . isArray ( args [ 0 ] ) ;
2017-02-14 16:21:55 -05:00
if ( usesPlurals ) {
2017-02-08 14:12:22 -05:00
// use the first plural form as messageId, otherwise the singular
messageId = ( args [ 0 ] . length > 1 ? args [ 0 ] [ 1 ] : args [ 0 ] [ 0 ] ) ;
2017-02-14 16:21:55 -05:00
} else {
2017-02-08 14:12:22 -05:00
messageId = args [ 0 ] ;
}
2017-02-14 16:21:55 -05:00
if ( messageId . length === 0 ) {
2017-02-08 14:12:22 -05:00
return messageId ;
}
2017-02-14 16:21:55 -05:00
// if no translation string cannot be found (in translations object)
if ( ! translations . hasOwnProperty ( messageId ) ) {
// if language is still loading and we have an elemt assigned
if ( language === null && $element !== null ) {
// handle the error by attaching the language loaded event
var orgArguments = arguments ;
$ ( document ) . on ( languageLoadedEvent , function ( ) {
// re-execute this function
me . translate . apply ( this , orgArguments ) ;
// log to show that the previous error could be mitigated
console . log ( 'Fixed missing translation of \'' + messageId + '\' with now loaded language ' + language ) ;
} ) ;
// and fall back to English for now until the real language
// file is loaded
}
// for all other langauges than English for which thsi behaviour
// is expected as it is built-in, log error
if ( language !== 'en' ) {
console . error ( 'Missing translation for: \'' + messageId + '\' in language ' + language ) ;
// fallback to English
2017-02-08 14:12:22 -05:00
}
2017-02-14 16:21:55 -05:00
// save English translation (should be the same on both sides)
2017-02-08 14:12:22 -05:00
translations [ messageId ] = args [ 0 ] ;
}
2017-02-14 16:21:55 -05:00
// lookup plural translation
if ( usesPlurals && $ . isArray ( translations [ messageId ] ) ) {
2017-02-08 14:12:22 -05:00
var n = parseInt ( args [ 1 ] || 1 , 10 ) ,
key = me . getPluralForm ( n ) ,
maxKey = translations [ messageId ] . length - 1 ;
2017-02-14 16:21:55 -05:00
if ( key > maxKey ) {
2017-02-08 14:12:22 -05:00
key = maxKey ;
}
args [ 0 ] = translations [ messageId ] [ key ] ;
args [ 1 ] = n ;
2017-02-14 16:21:55 -05:00
} else {
// lookup singular translation
2017-02-08 14:12:22 -05:00
args [ 0 ] = translations [ messageId ] ;
}
2017-02-14 16:21:55 -05:00
// format string
var output = Helper . sprintf . apply ( this , args ) ;
// if $element is given, apply text to element
if ( $element !== null ) {
$element . text ( output ) ;
}
return output ;
2017-02-08 14:12:22 -05:00
} ;
2017-02-08 07:20:51 -05:00
2017-02-08 14:12:22 -05:00
/ * *
* per language functions to use to determine the plural form
*
* @ see { @ link http : //localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html}
2017-02-14 16:21:55 -05:00
* @ name I18n . getPluralForm
2017-02-08 14:12:22 -05:00
* @ function
* @ param { number } n
* @ return { number } array key
* /
me . getPluralForm = function ( n ) {
switch ( language )
{
case 'fr' :
case 'oc' :
case 'zh' :
return ( n > 1 ? 1 : 0 ) ;
case 'pl' :
return ( n === 1 ? 0 : ( n % 10 >= 2 && n % 10 <= 4 && ( n % 100 < 10 || n % 100 >= 20 ) ? 1 : 2 ) ) ;
case 'ru' :
return ( n % 10 === 1 && n % 100 !== 11 ? 0 : ( n % 10 >= 2 && n % 10 <= 4 && ( n % 100 < 10 || n % 100 >= 20 ) ? 1 : 2 ) ) ;
case 'sl' :
return ( n % 100 === 1 ? 1 : ( n % 100 === 2 ? 2 : ( n % 100 === 3 || n % 100 === 4 ? 3 : 0 ) ) ) ;
// de, en, es, it, no
default :
return ( n !== 1 ? 1 : 0 ) ;
}
} ;
2017-02-08 07:20:51 -05:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-14 16:21:55 -05:00
* load translations into cache
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name I18n . loadTranslations
2017-02-08 14:12:22 -05:00
* @ function
* /
me . loadTranslations = function ( )
2017-02-08 14:11:04 -05:00
{
2017-02-14 16:21:55 -05:00
var newLanguage = Helper . getCookie ( 'lang' ) ;
2017-02-08 07:20:51 -05:00
2017-02-08 14:12:22 -05:00
// auto-select language based on browser settings
2017-02-14 16:21:55 -05:00
if ( newLanguage . length === 0 ) {
2017-02-08 14:12:22 -05:00
newLanguage = ( navigator . language || navigator . userLanguage ) . substring ( 0 , 2 ) ;
}
2015-09-06 07:07:46 -04:00
2017-02-14 16:21:55 -05:00
// if language is already used skip update
2017-02-12 12:08:08 -05:00
if ( newLanguage === language ) {
2017-02-08 14:12:22 -05:00
return ;
}
2015-09-06 09:54:43 -04:00
2017-02-14 16:21:55 -05:00
// if language is built-in (English) skip update
if ( newLanguage === 'en' ) {
language = 'en' ;
return ;
}
2017-02-08 14:12:22 -05:00
// if language is not supported, show error
2017-02-12 12:08:08 -05:00
if ( supportedLanguages . indexOf ( newLanguage ) === - 1 ) {
2017-02-08 14:12:22 -05:00
console . error ( 'Language \'%s\' is not supported. Translation failed, fallback to English.' , newLanguage ) ;
2017-02-14 16:21:55 -05:00
language = 'en' ;
return ;
2017-02-08 14:12:22 -05:00
}
2017-02-08 14:11:04 -05:00
2017-02-14 16:21:55 -05:00
// load strings from JSON
2017-02-08 14:12:22 -05:00
$ . getJSON ( 'i18n/' + newLanguage + '.json' , function ( data ) {
language = newLanguage ;
translations = data ;
2017-02-14 16:21:55 -05:00
$ ( document ) . triggerHandler ( languageLoadedEvent ) ;
2017-02-08 14:12:22 -05:00
} ) . fail ( function ( data , textStatus , errorMsg ) {
console . error ( 'Language \'%s\' could not be loaded (%s: %s). Translation failed, fallback to English.' , newLanguage , textStatus , errorMsg ) ;
2017-02-14 16:21:55 -05:00
language = 'en' ;
2017-02-08 14:12:22 -05:00
} ) ;
} ;
2015-09-06 07:07:46 -04:00
2017-02-08 14:12:22 -05:00
return me ;
} ) ( window , document ) ;
2017-02-08 14:11:04 -05:00
/ * *
2017-02-12 15:13:04 -05:00
* handles everything related to en / decryption
2017-02-08 14:11:04 -05:00
*
2017-02-08 14:12:22 -05:00
* @ class
2017-02-08 14:11:04 -05:00
* /
2017-02-14 16:21:55 -05:00
var CryptTool = ( function ( ) {
2017-02-08 14:12:22 -05:00
var me = { } ;
/ * *
* compress a message ( deflate compression ) , returns base64 encoded data
*
2017-02-12 12:08:08 -05:00
* @ name cryptToolcompress
2017-02-08 14:12:22 -05:00
* @ function
2017-02-12 12:08:08 -05:00
* @ private
2017-02-08 14:12:22 -05:00
* @ param { string } message
* @ return { string } base64 data
* /
2017-02-12 12:08:08 -05:00
function compress ( message )
2015-09-05 11:12:11 -04:00
{
2017-02-08 14:12:22 -05:00
return Base64 . toBase64 ( RawDeflate . deflate ( Base64 . utob ( message ) ) ) ;
2017-02-12 12:08:08 -05:00
}
2017-02-08 14:12:22 -05:00
/ * *
2017-02-12 12:08:08 -05:00
* decompress a message compressed with cryptToolcompress ( )
2017-02-08 14:12:22 -05:00
*
2017-02-12 12:08:08 -05:00
* @ name cryptTooldecompress
2017-02-08 14:12:22 -05:00
* @ function
2017-02-12 12:08:08 -05:00
* @ private
2017-02-08 14:12:22 -05:00
* @ param { string } data - base64 data
* @ return { string } message
* /
2017-02-12 12:08:08 -05:00
function decompress ( data )
2015-09-05 11:12:11 -04:00
{
2017-02-08 14:12:22 -05:00
return Base64 . btou ( RawDeflate . inflate ( Base64 . fromBase64 ( data ) ) ) ;
2017-02-12 12:08:08 -05:00
}
2017-02-08 14:12:22 -05:00
/ * *
* compress , then encrypt message with given key and password
*
2017-02-14 16:21:55 -05:00
* @ name CryptTool . cipher
2017-02-08 14:12:22 -05:00
* @ function
* @ param { string } key
* @ param { string } password
* @ param { string } message
* @ return { string } data - JSON with encrypted data
* /
me . cipher = function ( key , password , message )
{
// Galois Counter Mode, keysize 256 bit, authentication tag 128 bit
2017-02-13 15:12:00 -05:00
var options = {
mode : 'gcm' ,
ks : 256 ,
ts : 128
} ;
2017-02-08 14:12:22 -05:00
if ( ( password || '' ) . trim ( ) . length === 0 )
2015-09-05 11:12:11 -04:00
{
2017-02-12 12:08:08 -05:00
return sjcl . encrypt ( key , compress ( message ) , options ) ;
2015-09-05 11:12:11 -04:00
}
2017-02-13 15:12:00 -05:00
return sjcl . encrypt ( key + sjcl . codec . hex . fromBits ( sjcl . hash . sha256 . hash ( password ) ) , compress ( message ) , options ) ;
2017-02-12 12:08:08 -05:00
} ;
2017-02-08 14:12:22 -05:00
/ * *
* decrypt message with key , then decompress
*
2017-02-14 16:21:55 -05:00
* @ name CryptTool . decipher
2017-02-08 14:12:22 -05:00
* @ function
* @ param { string } key
* @ param { string } password
* @ param { string } data - JSON with encrypted data
* @ return { string } decrypted message
* /
me . decipher = function ( key , password , data )
{
if ( data !== undefined )
2015-09-05 11:12:11 -04:00
{
try
{
2017-02-12 12:08:08 -05:00
return decompress ( sjcl . decrypt ( key , data ) ) ;
2015-09-05 11:12:11 -04:00
}
2017-02-08 14:12:22 -05:00
catch ( err )
2015-09-05 11:12:11 -04:00
{
2017-02-08 14:12:22 -05:00
try
{
2017-02-12 12:08:08 -05:00
return decompress ( sjcl . decrypt ( key + sjcl . codec . hex . fromBits ( sjcl . hash . sha256 . hash ( password ) ) , data ) ) ;
2017-02-08 14:12:22 -05:00
}
catch ( e )
{
// ignore error, because ????? @TODO
}
2015-08-31 15:14:12 -04:00
}
}
2017-02-08 14:12:22 -05:00
return '' ;
2017-02-12 12:08:08 -05:00
} ;
2017-02-08 07:20:51 -05:00
2017-02-13 15:12:00 -05:00
/ * *
* checks whether the crypt tool is ready .
*
2017-02-14 16:21:55 -05:00
* @ name CryptTool . isReady
2017-02-13 15:12:00 -05:00
* @ function
* @ return { bool }
* /
me . isEntropyReady = function ( )
{
return sjcl . random . isReady ( ) ;
} ;
/ * *
* checks whether the crypt tool is ready .
*
2017-02-14 16:21:55 -05:00
* @ name CryptTool . isReady
2017-02-13 15:12:00 -05:00
* @ function
2017-02-14 16:21:55 -05:00
* @ param { function } func
2017-02-13 15:12:00 -05:00
* /
me . addEntropySeedListener = function ( func )
{
sjcl . random . addEventListener ( 'seeded' , func ) ;
} ;
/ * *
* returns a random symmetric key
*
2017-02-14 16:21:55 -05:00
* @ name CryptTool . getSymmetricKey
2017-02-13 15:12:00 -05:00
* @ function
2017-02-14 16:21:55 -05:00
* @ return { string } func
2017-02-13 15:12:00 -05:00
* /
me . getSymmetricKey = function ( func )
{
return sjcl . codec . base64 . fromBits ( sjcl . random . randomWords ( 8 , 0 ) , 0 ) ;
} ;
/ * *
* initialize crypt tool
*
2017-02-14 16:21:55 -05:00
* @ name CryptTool . init
2017-02-13 15:12:00 -05:00
* @ function
* /
me . init = function ( )
{
// will fail earlier as sjcl is already passed as a parameter
// if (typeof sjcl !== 'object') {
2017-02-14 16:21:55 -05:00
// Alert.showError(
// I18n._('The library %s is not available.', 'sjcl') +
// I18n._('Messages cannot be decrypted or encrypted.')
2017-02-13 15:12:00 -05:00
// );
// }
} ;
2017-02-08 14:12:22 -05:00
return me ;
2017-02-12 12:08:08 -05:00
} ) ( ) ;
2017-02-08 14:11:04 -05:00
2017-02-12 15:13:04 -05:00
/ * *
2017-02-14 16:21:55 -05:00
* ( modal ) Data source ( aka MVC )
2017-02-12 15:13:04 -05:00
*
* @ param { object } window
* @ param { object } document
* @ class
* /
2017-02-14 16:21:55 -05:00
var Modal = ( function ( window , document ) {
2017-02-12 15:13:04 -05:00
var me = { } ;
var $cipherData ;
2017-02-14 16:21:55 -05:00
var id = null , symmetricKey = null ;
/ * *
* returns the expiration set in the HTML
*
* @ name Modal . getExpirationDefault
* @ function
* @ return string
* @ TODO the template can be simplified as # pasteExpiration is no longer modified ( only default value )
* /
me . getExpirationDefault = function ( )
{
return $ ( '#pasteExpiration' ) . val ( ) ;
} ;
/ * *
* returns the format set in the HTML
*
* @ name Modal . getFormatDefault
* @ function
* @ return string
* @ TODO the template can be simplified as # pasteFormatter is no longer modified ( only default value )
* /
me . getFormatDefault = function ( )
{
return $ ( '#pasteFormatter' ) . val ( ) ;
} ;
2017-02-12 15:13:04 -05:00
/ * *
* check if cipher data was supplied
*
2017-02-14 16:21:55 -05:00
* @ name Modal . getCipherData
2017-02-12 15:13:04 -05:00
* @ function
* @ return boolean
* /
me . hasCipherData = function ( )
{
return ( me . getCipherData ( ) . length > 0 ) ;
} ;
/ * *
* returns the cipher data
*
2017-02-14 16:21:55 -05:00
* @ name Modal . getCipherData
2017-02-12 15:13:04 -05:00
* @ function
* @ return string
* /
me . getCipherData = function ( )
{
return $cipherData . text ( ) ;
} ;
2017-02-13 05:35:04 -05:00
/ * *
2017-02-14 16:21:55 -05:00
* get the pastes unique identifier from the URL ,
* eg . http : //example.com/path/?c05354954c49a487#dfdsdgdgdfgdf returns c05354954c49a487
2017-02-13 05:35:04 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Modal . getPasteId
2017-02-13 05:35:04 -05:00
* @ function
2017-02-14 16:21:55 -05:00
* @ return { string } unique identifier
2017-02-13 05:35:04 -05:00
* /
2017-02-14 16:21:55 -05:00
me . getPasteId = function ( )
2017-02-13 05:35:04 -05:00
{
2017-02-14 16:21:55 -05:00
if ( id === null ) {
id = window . location . search . substring ( 1 ) ;
}
return id ;
2017-02-13 05:35:04 -05:00
} ;
/ * *
2017-02-14 16:21:55 -05:00
* return the deciphering key stored in anchor part of the URL
2017-02-13 05:35:04 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Modal . getPasteKey
2017-02-13 05:35:04 -05:00
* @ function
2017-02-14 16:21:55 -05:00
* @ return { string } key
2017-02-13 05:35:04 -05:00
* /
2017-02-14 16:21:55 -05:00
me . getPasteKey = function ( )
2017-02-13 05:35:04 -05:00
{
2017-02-14 16:21:55 -05:00
if ( symmetricKey === null ) {
symmetricKey = window . location . hash . substring ( 1 ) ;
// Some web 2.0 services and redirectors add data AFTER the anchor
// (such as &utm_source=...). We will strip any additional data.
var ampersandPos = symmetricKey . indexOf ( '&' ) ;
if ( ampersandPos > - 1 )
{
symmetricKey = symmetricKey . substring ( 0 , ampersandPos ) ;
}
}
return symmetricKey ;
2017-02-13 05:35:04 -05:00
} ;
2017-02-12 15:13:04 -05:00
/ * *
* init navigation manager
*
* preloads jQuery elements
*
2017-02-14 16:21:55 -05:00
* @ name Modal . init
2017-02-12 15:13:04 -05:00
* @ function
* /
me . init = function ( )
{
$cipherData = $ ( '#cipherdata' ) ;
} ;
return me ;
} ) ( window , document ) ;
2017-02-08 14:11:04 -05:00
/ * *
2017-02-14 16:21:55 -05:00
* Helper functions for user interface
*
* everything directly UI - related , which fits nowhere else
2017-02-08 14:11:04 -05:00
*
2017-02-08 14:12:22 -05:00
* @ param { object } window
* @ param { object } document
* @ class
2017-02-08 14:11:04 -05:00
* /
2017-02-14 16:21:55 -05:00
var UiHelper = ( function ( window , document ) {
2017-02-08 14:12:22 -05:00
var me = { } ;
/ * *
2017-02-13 05:35:04 -05:00
* handle history ( pop ) state changes
*
* currently this does only handle redirects to the home page .
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ private
2017-02-08 14:12:22 -05:00
* @ function
2017-02-13 05:35:04 -05:00
* @ param { Event } event
2017-02-08 14:12:22 -05:00
* /
2017-02-14 16:21:55 -05:00
function historyChange ( event )
2017-02-08 14:12:22 -05:00
{
2017-02-14 16:21:55 -05:00
var currentLocation = Helper . baseUri ( ) ;
2017-02-13 05:35:04 -05:00
if ( event . originalEvent . state === null && // no state object passed
event . originalEvent . target . location . href === currentLocation && // target location is home page
window . location . href === currentLocation // and we are not already on the home page
) {
// redirect to home page
window . location . href = currentLocation ;
}
} ;
2017-02-08 14:12:22 -05:00
2017-02-13 05:35:04 -05:00
/ * *
* reload the page
*
2017-02-14 16:21:55 -05:00
* This takes the user to the PrivateBin homepage .
2017-02-13 05:35:04 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name UiHelper . reloadHome
2017-02-13 05:35:04 -05:00
* @ function
* /
2017-02-14 16:21:55 -05:00
me . reloadHome = function ( )
2017-02-13 05:35:04 -05:00
{
2017-02-14 16:21:55 -05:00
window . location . href = Helper . baseUri ( ) ;
2017-02-13 05:35:04 -05:00
} ;
2017-02-08 07:20:51 -05:00
2017-02-13 05:35:04 -05:00
/ * *
2017-02-14 16:21:55 -05:00
* initialize
2017-02-13 05:35:04 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name UiHelper . init
2017-02-13 05:35:04 -05:00
* @ function
* /
me . init = function ( )
{
2017-02-14 16:21:55 -05:00
// update link to home page
$ ( '.reloadlink' ) . prop ( 'href' , Helper . baseUri ( ) ) ;
2017-02-13 05:35:04 -05:00
2017-02-14 16:21:55 -05:00
$ ( window ) . on ( 'popstate' , historyChange ) ;
2017-02-08 14:12:22 -05:00
} ;
2015-09-12 11:33:16 -04:00
2017-02-13 05:35:04 -05:00
return me ;
} ) ( window , document ) ;
/ * *
2017-02-14 16:21:55 -05:00
* Alert / error manager
2017-02-13 05:35:04 -05:00
*
* @ param { object } window
* @ param { object } document
* @ class
* /
2017-02-14 16:21:55 -05:00
var Alert = ( function ( window , document ) {
2017-02-13 05:35:04 -05:00
var me = { } ;
2017-02-14 16:21:55 -05:00
var $errorMessage ,
$status ;
2017-02-13 15:12:00 -05:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-13 15:12:00 -05:00
* display a status message
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Alert . showStatus
2017-02-08 14:12:22 -05:00
* @ function
2017-02-13 15:12:00 -05:00
* @ param { string } message - text to display
* @ param { boolean } [ spin = false ] - ( optional ) tell if the "spinning" animation should be displayed , defaults to false
2017-02-08 14:12:22 -05:00
* /
2017-02-13 15:12:00 -05:00
me . showStatus = function ( message , spin )
2017-02-08 14:12:22 -05:00
{
2017-02-13 15:12:00 -05:00
// spin is ignored for now
$status . text ( message ) ;
2017-02-13 05:35:04 -05:00
} ;
2017-02-08 14:12:22 -05:00
2017-02-13 05:35:04 -05:00
/ * *
2017-02-13 15:12:00 -05:00
* hides any status messages
2017-02-13 05:35:04 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Alert . hideMessages
2017-02-13 05:35:04 -05:00
* @ function
* /
2017-02-13 15:12:00 -05:00
me . hideMessages = function ( )
2017-02-13 05:35:04 -05:00
{
2017-02-13 15:12:00 -05:00
$status . html ( ' ' ) ;
2017-02-14 16:21:55 -05:00
$errorMessage . addClass ( 'hidden' ) ;
2017-02-13 05:35:04 -05:00
} ;
2017-02-08 14:12:22 -05:00
2017-02-13 05:35:04 -05:00
/ * *
2017-02-13 15:12:00 -05:00
* display an error message
2017-02-13 05:35:04 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Alert . showError
2017-02-13 05:35:04 -05:00
* @ function
2017-02-13 15:12:00 -05:00
* @ param { string } message - text to display
2017-02-13 05:35:04 -05:00
* /
2017-02-13 15:12:00 -05:00
me . showError = function ( message )
2017-02-13 05:35:04 -05:00
{
2017-02-14 16:21:55 -05:00
console . error ( 'Error shown: ' + message ) ;
2017-02-13 15:12:00 -05:00
$errorMessage . removeClass ( 'hidden' ) ;
2017-02-14 16:21:55 -05:00
$errorMessage . find ( ':last' ) . text ( ' ' + message ) ;
2017-02-13 05:35:04 -05:00
} ;
2017-02-08 14:12:22 -05:00
2017-02-13 05:35:04 -05:00
/ * *
2017-02-14 16:21:55 -05:00
* init status manager
2017-02-13 05:35:04 -05:00
*
2017-02-14 16:21:55 -05:00
* preloads jQuery elements
*
* @ name Alert . init
2017-02-13 05:35:04 -05:00
* @ function
* /
2017-02-14 16:21:55 -05:00
me . init = function ( )
2017-02-13 05:35:04 -05:00
{
2017-02-14 16:21:55 -05:00
// hide "no javascript" message
$ ( '#noscript' ) . hide ( ) ;
2017-02-13 15:12:00 -05:00
2017-02-14 16:21:55 -05:00
$errorMessage = $ ( '#errormessage' ) ;
$status = $ ( '#status' ) ;
2017-02-08 14:12:22 -05:00
2017-02-14 16:21:55 -05:00
// display status returned by php code, if any (eg. paste was properly deleted)
// @TODO remove this by handling errors in a different way
if ( $status . text ( ) . length > 0 )
{
me . showStatus ( $status . text ( ) ) ;
return ;
}
2017-02-08 14:12:22 -05:00
2017-02-14 16:21:55 -05:00
// keep line height even if content empty
$status . html ( ' ' ) ; // @TODO what? remove?
2015-09-05 11:12:11 -04:00
2017-02-14 16:21:55 -05:00
// display error message from php code
if ( $errorMessage . text ( ) . length > 1 ) {
Alert . showError ( $errorMessage . text ( ) ) ;
}
2017-02-12 12:08:08 -05:00
} ;
2017-02-08 14:12:22 -05:00
2017-02-14 16:21:55 -05:00
return me ;
} ) ( window , document ) ;
/ * *
* handles paste status / result
*
* @ param { object } window
* @ param { object } document
* @ class
* /
var PasteStatus = ( function ( window , document ) {
var me = { } ;
var $pasteSuccess ,
$shortenButton ,
$pasteUrl ,
$remainingTime ;
2017-02-12 12:08:08 -05:00
/ * *
2017-02-13 15:12:00 -05:00
* forward to URL shortener
2017-02-12 12:08:08 -05:00
*
2017-02-13 15:12:00 -05:00
* @ private
2017-02-12 12:08:08 -05:00
* @ function
2017-02-13 15:12:00 -05:00
* @ param { Event } event
2017-02-12 12:08:08 -05:00
* /
2017-02-13 15:12:00 -05:00
function sendToShortener ( event )
{
window . location . href = $shortenButton . data ( 'shortener' )
+ encodeURIComponent ( $pasteUrl . attr ( 'href' ) ) ;
}
/ * *
2017-02-14 16:21:55 -05:00
* Forces opening the paste if the link does not do this automatically .
*
* This is necessary as browsers will not reload the page when it is
* already loaded ( which is fake as it is set via history . pushState ( ) ) .
2017-02-13 15:12:00 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Controller . pasteLinkClick
* @ function
* @ param { Event } event
* /
function pasteLinkClick ( event )
{
// check if location is (already) shown in URL bar
if ( window . location . href === $pasteUrl . attr ( 'href' ) ) {
// if so we need to load link by reloading the current site
window . location . reload ( true ) ;
}
}
/ * *
* creates a notification after a successfull paste upload
2017-02-13 15:12:00 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name PasteStatus . createPasteNotification
2017-02-13 15:12:00 -05:00
* @ function
* @ param { string } url
* @ param { string } deleteUrl
* /
me . createPasteNotification = function ( url , deleteUrl )
2017-02-12 12:08:08 -05:00
{
2017-02-13 15:12:00 -05:00
$ ( '#pastelink' ) . find ( ':first' ) . html (
2017-02-14 16:21:55 -05:00
I18n . _ (
2017-02-13 15:12:00 -05:00
'Your paste is <a id="pasteurl" href="%s">%s</a> <span id="copyhint">(Hit [Ctrl]+[c] to copy)</span>' ,
url , url
)
) ;
// save newly created element
$pasteUrl = $ ( '#pasteurl' ) ;
// and add click event
$pasteUrl . click ( pasteLinkClick ) ;
// shorten button
2017-02-14 16:21:55 -05:00
$ ( '#deletelink' ) . html ( '<a href="' + deleteUrl + '">' + I18n . _ ( 'Delete data' ) + '</a>' ) ;
2017-02-13 05:35:04 -05:00
2017-02-13 15:12:00 -05:00
// show result
$pasteSuccess . removeClass ( 'hidden' ) ;
// we pre-select the link so that the user only has to [Ctrl]+[c] the link
2017-02-14 16:21:55 -05:00
Helper . selectText ( $pasteUrl [ 0 ] ) ;
2017-02-12 12:08:08 -05:00
} ;
2012-04-21 15:59:45 -04:00
2017-02-13 15:12:00 -05:00
/ * *
2017-02-14 16:21:55 -05:00
* shows the remaining time
2017-02-13 15:12:00 -05:00
*
* @ function
2017-02-14 16:21:55 -05:00
* @ param { object } pasteMetaData
2017-02-13 15:12:00 -05:00
* /
2017-02-14 16:21:55 -05:00
me . showRemainingTime = function ( pasteMetaData )
2017-02-13 15:12:00 -05:00
{
2017-02-14 16:21:55 -05:00
if ( pasteMetaData . burnafterreading ) {
// display paste "for your eyes only" if it is deleted
// actually remove paste, before we claim it is deleted
Controller . removePaste ( Modal . getPasteId ( ) , 'burnafterreading' ) ;
I18n . _ ( $remainingTime . find ( ':last' ) , "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again." ) ;
$remainingTime . addClass ( 'foryoureyesonly' ) ;
// discourage cloning (it cannot really be prevented)
TopNav . hideCloneButton ( ) ;
} else if ( pasteMetaData . expire _date ) {
// display paste expiration
var expiration = Helper . secondsToHuman ( pasteMetaData . remaining _time ) ,
expirationLabel = [
'This document will expire in %d ' + expiration [ 1 ] + '.' ,
'This document will expire in %d ' + expiration [ 1 ] + 's.'
] ;
I18n . _ ( $remainingTime . find ( ':last' ) , expirationLabel , expiration [ 0 ] ) ;
$remainingTime . removeClass ( 'foryoureyesonly' )
} else {
// never expires
return ;
2017-02-13 15:12:00 -05:00
}
2017-02-14 16:21:55 -05:00
// in the end, display notification
$remainingTime . removeClass ( 'hidden' ) ;
} ;
2017-02-13 15:12:00 -05:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-13 05:35:04 -05:00
* init status manager
2017-02-08 14:12:22 -05:00
*
2017-02-13 05:35:04 -05:00
* preloads jQuery elements
*
2017-02-14 16:21:55 -05:00
* @ name Alert . init
2017-02-08 14:12:22 -05:00
* @ function
* /
2017-02-12 12:08:08 -05:00
me . init = function ( )
2017-02-08 14:12:22 -05:00
{
2017-02-13 15:12:00 -05:00
$shortenButton = $ ( '#shortenbutton' ) ;
$pasteSuccess = $ ( '#pasteSuccess' ) ;
2017-02-14 16:21:55 -05:00
// $pasteUrl is saved in me.createPasteNotification() after creation
$remainingTime = $ ( '#remainingtime' ) ;
2017-02-08 14:12:22 -05:00
2017-02-13 15:12:00 -05:00
// bind elements
$shortenButton . click ( sendToShortener ) ;
2017-02-08 14:12:22 -05:00
} ;
2017-02-08 07:20:51 -05:00
2017-02-12 12:08:08 -05:00
return me ;
} ) ( window , document ) ;
/ * *
2017-02-14 16:21:55 -05:00
* password prompt
2017-02-12 12:08:08 -05:00
*
* @ param { object } window
* @ param { object } document
* @ class
* /
2017-02-14 16:21:55 -05:00
var Prompt = ( function ( window , document ) {
2017-02-12 12:08:08 -05:00
var me = { } ;
2017-02-13 15:12:00 -05:00
var $passwordModal ,
2017-02-13 05:35:04 -05:00
$passwordForm ,
$passwordDecrypt ;
2017-02-14 16:21:55 -05:00
var password = '' ,
passwordCallback = null ;
2017-02-08 14:12:22 -05:00
/ * *
2017-02-13 05:35:04 -05:00
* ask the user for the password and set it
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* the callback set via setPasswordCallback is executed
*
* @ name Prompt . requestPassword ( )
2017-02-08 14:12:22 -05:00
* @ function
* /
2017-02-13 05:35:04 -05:00
me . requestPassword = function ( )
2017-02-08 14:12:22 -05:00
{
2017-02-14 16:21:55 -05:00
// show new bootstrap method
$passwordModal . modal ( {
backdrop : 'static' ,
keyboard : false
} ) ;
} ;
/ * *
* get cached password or password from easy Prompt
*
* If you do not get a password with this function , use
* requestPassword
*
* @ name Prompt . getPassword
* @ function
* /
me . getPassword = function ( )
{
if ( password . length !== 0 ) {
return password ;
}
2017-02-13 05:35:04 -05:00
if ( $passwordModal . length === 0 ) {
2017-02-13 15:12:00 -05:00
// old method for page template
2017-02-14 16:21:55 -05:00
var newPassword = Prompt ( I18n . _ ( 'Please enter the password for this paste:' ) , '' ) ;
if ( newPassword === null ) {
throw 'password Prompt canceled' ;
2017-02-13 05:35:04 -05:00
}
2017-02-14 16:21:55 -05:00
if ( password . length === 0 ) {
2017-02-13 05:35:04 -05:00
// recursive…
2017-02-14 16:21:55 -05:00
me . getPassword ( ) ;
2017-02-13 05:35:04 -05:00
} else {
2017-02-14 16:21:55 -05:00
password = newPassword ;
2017-02-13 05:35:04 -05:00
}
}
2017-02-14 16:21:55 -05:00
return password ;
2017-02-08 14:12:22 -05:00
} ;
2017-02-05 16:09:46 -05:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-14 16:21:55 -05:00
* setsthe callback called when password is entered
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Prompt . setPasswordCallback
2017-02-08 14:12:22 -05:00
* @ function
2017-02-14 16:21:55 -05:00
* @ param { functions } setPasswordCallback
2017-02-08 14:12:22 -05:00
* /
2017-02-14 16:21:55 -05:00
me . setPasswordCallback = function ( callback )
2017-02-06 16:39:45 -05:00
{
2017-02-14 16:21:55 -05:00
passwordCallback = callback ;
2017-02-08 14:12:22 -05:00
} ;
/ * *
2017-02-14 16:21:55 -05:00
* submit a password in the Modal dialog
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ private
2017-02-08 14:12:22 -05:00
* @ function
2017-02-13 05:35:04 -05:00
* @ param { Event } event
2017-02-08 14:12:22 -05:00
* /
2017-02-14 16:21:55 -05:00
function submitPasswordModal ( event )
2015-09-05 11:12:11 -04:00
{
2017-02-14 16:21:55 -05:00
// get input
password = $passwordDecrypt . val ( ) ;
// hide modal
2017-02-13 05:35:04 -05:00
$passwordModal . modal ( 'hide' ) ;
2017-02-14 16:21:55 -05:00
if ( passwordCallback !== null ) {
passwordCallback ( ) ;
}
event . preventDefault ( ) ;
}
2012-04-21 15:59:45 -04:00
2017-02-13 05:35:04 -05:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-13 05:35:04 -05:00
* init status manager
2017-02-08 14:12:22 -05:00
*
2017-02-13 05:35:04 -05:00
* preloads jQuery elements
*
2017-02-14 16:21:55 -05:00
* @ name Controller . init
2017-02-08 14:12:22 -05:00
* @ function
* /
2017-02-13 05:35:04 -05:00
me . init = function ( )
2017-02-08 14:12:22 -05:00
{
2017-02-13 05:35:04 -05:00
$passwordModal = $ ( '#passwordmodal' ) ;
$passwordForm = $ ( '#passwordform' ) ;
$passwordDecrypt = $ ( '#passworddecrypt' ) ;
2017-02-08 14:12:22 -05:00
2017-02-13 05:35:04 -05:00
// bind events
2017-02-08 14:12:22 -05:00
2017-02-13 05:35:04 -05:00
// focus password input when it is shown
2017-02-14 16:21:55 -05:00
$passwordModal . on ( 'shown.bs.Modal' , function ( ) {
2017-02-13 05:35:04 -05:00
$passwordDecrypt . focus ( ) ;
} ) ;
2017-02-14 16:21:55 -05:00
// handle Modal password submission
$passwordForm . submit ( submitPasswordModal ) ;
2017-02-08 14:12:22 -05:00
} ;
2015-09-05 11:12:11 -04:00
2017-02-12 12:08:08 -05:00
return me ;
} ) ( window , document ) ;
/ * *
2017-02-14 16:21:55 -05:00
* Manage paste / message input , and preview tab
*
* Note that the actual preview is handled by PasteViewer .
2017-02-12 12:08:08 -05:00
*
* @ param { object } window
* @ param { object } document
* @ class
* /
2017-02-14 16:21:55 -05:00
var Editor = ( function ( window , document ) {
2017-02-12 12:08:08 -05:00
var me = { } ;
2017-02-13 05:35:04 -05:00
var $message ,
$messageEdit ,
$messagePreview ,
$editorTabs ;
var isPreview = false ;
2017-02-12 12:08:08 -05:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-13 05:35:04 -05:00
* support input of tab character
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Editor . supportTabs
2017-02-08 14:12:22 -05:00
* @ function
2017-02-13 05:35:04 -05:00
* @ param { Event } event
* @ TODO doc what is @ this here ?
* @ TODO replace this with $message ? ?
2017-02-08 14:12:22 -05:00
* /
2017-02-13 05:35:04 -05:00
function supportTabs ( event )
{
var keyCode = event . keyCode || event . which ;
// tab was pressed
if ( keyCode === 9 )
{
// prevent the textarea to lose focus
event . preventDefault ( ) ;
// get caret position & selection
var val = this . value ,
start = this . selectionStart ,
end = this . selectionEnd ;
// set textarea value to: text before caret + tab + text after caret
this . value = val . substring ( 0 , start ) + '\t' + val . substring ( end ) ;
// put caret at right position again
this . selectionStart = this . selectionEnd = start + 1 ;
}
}
/ * *
2017-02-14 16:21:55 -05:00
* view the Editor tab
2017-02-13 05:35:04 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Editor . viewEditor
2017-02-13 05:35:04 -05:00
* @ function
* @ param { Event } event - optional
* /
function viewEditor ( event )
{
// toggle buttons
$messageEdit . addClass ( 'active' ) ;
$messagePreview . removeClass ( 'active' ) ;
2017-02-14 16:21:55 -05:00
PasteViewer . hide ( ) ;
2017-02-13 05:35:04 -05:00
// reshow input
$message . removeClass ( 'hidden' ) ;
me . focusInput ( ) ;
// finish
isPreview = false ;
2017-02-14 16:21:55 -05:00
// prevent jumping of page to top
if ( typeof event !== 'undefined' ) {
event . preventDefault ( ) ;
}
2017-02-13 05:35:04 -05:00
}
/ * *
* view the preview tab
*
2017-02-14 16:21:55 -05:00
* @ name Editor . viewPreview
2017-02-13 05:35:04 -05:00
* @ function
* @ param { Event } event
* /
function viewPreview ( event )
{
// toggle buttons
$messageEdit . removeClass ( 'active' ) ;
$messagePreview . addClass ( 'active' ) ;
// hide input as now preview is shown
$message . addClass ( 'hidden' ) ;
// show preview
2017-02-13 15:12:00 -05:00
$ ( '#errormessage' ) . find ( ':last' )
2017-02-14 16:21:55 -05:00
PasteViewer . setText ( $message . val ( ) ) ;
PasteViewer . run ( ) ;
2017-02-13 05:35:04 -05:00
// finish
isPreview = true ;
2017-02-14 16:21:55 -05:00
// prevent jumping of page to top
if ( typeof event !== 'undefined' ) {
event . preventDefault ( ) ;
}
2017-02-13 05:35:04 -05:00
}
/ * *
* get the state of the preview
*
2017-02-14 16:21:55 -05:00
* @ name Editor . isPreview
2017-02-13 05:35:04 -05:00
* @ function
* /
me . isPreview = function ( )
{
return isPreview ;
}
/ * *
2017-02-14 16:21:55 -05:00
* reset the Editor view
2017-02-13 05:35:04 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Editor . resetInput
2017-02-13 05:35:04 -05:00
* @ function
* /
me . resetInput = function ( )
2015-09-05 11:12:11 -04:00
{
2017-02-13 05:35:04 -05:00
// go back to input
if ( isPreview ) {
viewEditor ( ) ;
}
// clear content
$message . val ( '' ) ;
2017-02-08 14:12:22 -05:00
} ;
2017-02-08 14:11:04 -05:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-14 16:21:55 -05:00
* shows the Editor
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Editor . show
2017-02-08 14:12:22 -05:00
* @ function
* /
2017-02-13 05:35:04 -05:00
me . show = function ( )
2016-08-11 05:31:34 -04:00
{
2017-02-13 05:35:04 -05:00
$message . removeClass ( 'hidden' ) ;
$editorTabs . removeClass ( 'hidden' ) ;
2017-02-08 14:12:22 -05:00
} ;
/ * *
2017-02-14 16:21:55 -05:00
* hides the Editor
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Editor . reset
2017-02-08 14:12:22 -05:00
* @ function
* /
2017-02-13 05:35:04 -05:00
me . hide = function ( )
2015-09-05 11:12:11 -04:00
{
2017-02-13 05:35:04 -05:00
$message . addClass ( 'hidden' ) ;
$editorTabs . addClass ( 'hidden' ) ;
2017-02-08 14:12:22 -05:00
} ;
2012-04-21 15:59:45 -04:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-13 05:35:04 -05:00
* focuses the message input
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Editor . focusInput
2017-02-08 14:12:22 -05:00
* @ function
* /
2017-02-13 05:35:04 -05:00
me . focusInput = function ( )
2017-02-08 14:12:22 -05:00
{
2017-02-13 05:35:04 -05:00
$message . focus ( ) ;
2017-02-08 14:12:22 -05:00
} ;
2012-04-21 15:59:45 -04:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-13 05:35:04 -05:00
* returns the current text
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Editor . getText
2017-02-08 14:12:22 -05:00
* @ function
2017-02-13 05:35:04 -05:00
* @ return { string }
2017-02-08 14:12:22 -05:00
* /
2017-02-13 05:35:04 -05:00
me . getText = function ( )
2017-02-08 14:12:22 -05:00
{
2017-02-13 05:35:04 -05:00
return $message . val ( )
2017-02-08 14:12:22 -05:00
} ;
2016-08-11 05:40:37 -04:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-12 12:08:08 -05:00
* init status manager
2017-02-08 14:12:22 -05:00
*
2017-02-12 12:08:08 -05:00
* preloads jQuery elements
*
2017-02-14 16:21:55 -05:00
* @ name Editor . init
2017-02-08 14:12:22 -05:00
* @ function
* /
2017-02-12 12:08:08 -05:00
me . init = function ( )
2017-02-08 14:12:22 -05:00
{
2017-02-13 05:35:04 -05:00
$message = $ ( '#message' ) ;
$editorTabs = $ ( '#editorTabs' ) ;
2017-02-12 12:08:08 -05:00
2017-02-13 05:35:04 -05:00
// bind events
$message . keydown ( supportTabs ) ;
2017-02-12 15:13:04 -05:00
2017-02-13 05:35:04 -05:00
// bind click events to tab switchers (a), but save parent of them
// (li)
$messageEdit = $ ( '#messageedit' ) . click ( viewEditor ) . parent ( ) ;
$messagePreview = $ ( '#messagepreview' ) . click ( viewPreview ) . parent ( ) ;
2017-02-08 14:12:22 -05:00
} ;
2016-07-11 05:09:41 -04:00
2017-02-12 12:08:08 -05:00
return me ;
} ) ( window , document ) ;
/ * *
2017-02-14 16:21:55 -05:00
* ( view ) Parse and show paste .
2017-02-12 12:08:08 -05:00
*
* @ param { object } window
* @ param { object } document
* @ class
* /
2017-02-14 16:21:55 -05:00
var PasteViewer = ( function ( window , document ) {
2017-02-12 12:08:08 -05:00
var me = { } ;
2017-02-14 16:21:55 -05:00
var $clonedFile ,
$plainText ,
2017-02-13 05:35:04 -05:00
$placeholder ,
$prettyMessage ,
2017-02-14 16:21:55 -05:00
$prettyPrint ;
2017-02-13 05:35:04 -05:00
var text ,
format = 'plaintext' ,
isDisplayed = false ,
isChanged = true ; // by default true as nothing was parsed yet
2017-02-12 12:08:08 -05:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-13 05:35:04 -05:00
* apply the set format on paste and displays it
2017-02-08 14:12:22 -05:00
*
2017-02-13 05:35:04 -05:00
* @ private
2017-02-08 14:12:22 -05:00
* @ function
* /
2017-02-13 05:35:04 -05:00
function parsePaste ( )
2016-07-11 05:09:41 -04:00
{
2017-02-13 05:35:04 -05:00
// skip parsing if no text is given
if ( text === '' ) {
return ;
2017-02-12 12:08:08 -05:00
}
2017-02-13 05:35:04 -05:00
// set text
2017-02-14 16:21:55 -05:00
Helper . setElementText ( $plainText , text ) ;
Helper . setElementText ( $prettyPrint , text ) ;
2017-02-13 05:35:04 -05:00
switch ( format ) {
case 'markdown' :
var converter = new showdown . Converter ( {
strikethrough : true ,
tables : true ,
tablesHeaderId : true
} ) ;
2017-02-14 16:21:55 -05:00
$plainText . html (
2017-02-13 05:35:04 -05:00
converter . makeHtml ( text )
) ;
// add table classes from bootstrap css
2017-02-14 16:21:55 -05:00
$plainText . find ( 'table' ) . addClass ( 'table-condensed table-bordered' ) ;
2017-02-13 05:35:04 -05:00
break ;
case 'syntaxhighlighting' :
// @TODO is this really needed or is "one" enough?
if ( typeof prettyPrint === 'function' )
{
prettyPrint ( ) ;
}
$prettyPrint . html (
prettyPrintOne (
2017-02-14 16:21:55 -05:00
Helper . htmlEntities ( text ) , null , true
2017-02-13 05:35:04 -05:00
)
) ;
// fall through, as the rest is the same
default : // = 'plaintext'
// convert URLs to clickable links
2017-02-14 16:21:55 -05:00
Helper . urls2links ( $plainText ) ;
Helper . urls2links ( $prettyPrint ) ;
2017-02-13 05:35:04 -05:00
$prettyPrint . css ( 'white-space' , 'pre-wrap' ) ;
$prettyPrint . css ( 'word-break' , 'normal' ) ;
$prettyPrint . removeClass ( 'prettyprint' ) ;
}
}
2017-02-05 08:47:03 -05:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-13 05:35:04 -05:00
* displays the paste
2017-02-08 14:12:22 -05:00
*
2017-02-13 05:35:04 -05:00
* @ private
2017-02-08 14:12:22 -05:00
* @ function
* /
2017-02-13 05:35:04 -05:00
function showPaste ( )
2017-02-05 15:22:09 -05:00
{
2017-02-13 05:35:04 -05:00
// instead of "nothing" better display a placeholder
if ( text === '' ) {
$placeholder . removeClass ( 'hidden' )
return ;
}
// otherwise hide the placeholder
$placeholder . addClass ( 'hidden' )
switch ( format ) {
case 'markdown' :
2017-02-14 16:21:55 -05:00
$plainText . removeClass ( 'hidden' ) ;
2017-02-13 05:35:04 -05:00
$prettyMessage . addClass ( 'hidden' ) ;
break ;
default :
2017-02-14 16:21:55 -05:00
$plainText . addClass ( 'hidden' ) ;
2017-02-13 05:35:04 -05:00
$prettyMessage . removeClass ( 'hidden' ) ;
break ;
}
}
2017-02-08 14:12:22 -05:00
/ * *
2017-02-13 05:35:04 -05:00
* sets the format in which the text is shown
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name PasteViewer . setFormat
2017-02-08 14:12:22 -05:00
* @ function
2017-02-13 05:35:04 -05:00
* @ param { string } the the new format
2017-02-08 14:12:22 -05:00
* /
2017-02-13 05:35:04 -05:00
me . setFormat = function ( newFormat )
2017-02-08 14:12:22 -05:00
{
2017-02-13 05:35:04 -05:00
if ( format !== newFormat ) {
format = newFormat ;
isChanged = true ;
}
} ;
2017-02-08 14:11:04 -05:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-13 05:35:04 -05:00
* returns the current format
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name PasteViewer . setFormat
2017-02-08 14:12:22 -05:00
* @ function
2017-02-13 05:35:04 -05:00
* @ return { string }
2017-02-08 14:12:22 -05:00
* /
2017-02-13 05:35:04 -05:00
me . getFormat = function ( )
2017-02-08 14:12:22 -05:00
{
2017-02-13 05:35:04 -05:00
return format ;
2017-02-08 14:12:22 -05:00
} ;
2017-02-08 14:11:04 -05:00
2017-02-14 16:21:55 -05:00
/ * *
* returns whether the current view is pretty printed
*
* @ name PasteViewer . isPrettyPrinted
* @ function
* @ return { bool }
* /
me . isPrettyPrinted = function ( )
{
return $prettyPrint . hasClass ( 'prettyprinted' ) ;
} ;
2017-02-08 14:12:22 -05:00
/ * *
2017-02-13 05:35:04 -05:00
* sets the text to show
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Editor . init
2017-02-08 14:12:22 -05:00
* @ function
2017-02-14 16:21:55 -05:00
* @ param { string } newText the text to show
2017-02-08 14:12:22 -05:00
* /
2017-02-13 05:35:04 -05:00
me . setText = function ( newText )
2017-02-08 14:12:22 -05:00
{
2017-02-13 05:35:04 -05:00
if ( text !== newText ) {
text = newText ;
isChanged = true ;
}
2017-02-08 14:12:22 -05:00
} ;
2017-02-08 14:11:04 -05:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-13 05:35:04 -05:00
* show / update the parsed text ( preview )
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name PasteViewer . run
2017-02-08 14:12:22 -05:00
* @ function
* /
2017-02-14 16:21:55 -05:00
me . run = function ( )
2017-02-08 14:12:22 -05:00
{
2017-02-13 05:35:04 -05:00
if ( isChanged ) {
parsePaste ( ) ;
isChanged = false ;
}
if ( ! isDisplayed ) {
showPaste ( ) ;
isDisplayed = true ;
}
2017-02-08 14:12:22 -05:00
} ;
2017-02-08 14:11:04 -05:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-13 05:35:04 -05:00
* hide parsed text ( preview )
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name PasteViewer . hide
2017-02-08 14:12:22 -05:00
* @ function
* /
2017-02-13 05:35:04 -05:00
me . hide = function ( )
2017-02-08 14:12:22 -05:00
{
2017-02-13 05:35:04 -05:00
if ( ! isDisplayed ) {
2017-02-14 16:21:55 -05:00
console . warn ( 'PasteViewer was called to hide the parsed view, but it is already hidden.' ) ;
2017-02-13 05:35:04 -05:00
}
2017-02-14 16:21:55 -05:00
$plainText . addClass ( 'hidden' ) ;
2017-02-13 05:35:04 -05:00
$prettyMessage . addClass ( 'hidden' ) ;
$placeholder . addClass ( 'hidden' ) ;
isDisplayed = false ;
2017-02-08 14:12:22 -05:00
} ;
2017-02-08 14:11:04 -05:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-12 12:08:08 -05:00
* init status manager
2017-02-08 14:12:22 -05:00
*
2017-02-12 12:08:08 -05:00
* preloads jQuery elements
*
2017-02-14 16:21:55 -05:00
* @ name Editor . init
2017-02-08 14:12:22 -05:00
* @ function
* /
2017-02-12 12:08:08 -05:00
me . init = function ( )
2017-02-08 14:12:22 -05:00
{
2017-02-14 16:21:55 -05:00
$plainText = $ ( '#plaintext' ) ;
2017-02-13 05:35:04 -05:00
$placeholder = $ ( '#placeholder' ) ;
$prettyMessage = $ ( '#prettymessage' ) ;
$prettyPrint = $ ( '#prettyprint' ) ;
2017-02-12 12:08:08 -05:00
2017-02-13 05:35:04 -05:00
// check requirements
if ( typeof prettyPrintOne !== 'function' ) {
2017-02-14 16:21:55 -05:00
Alert . showError (
I18n . _ ( 'The library %s is not available.' , 'pretty print' ) +
I18n . _ ( 'This may cause display errors.' )
2017-02-13 05:35:04 -05:00
) ;
}
if ( typeof showdown !== 'object' ) {
2017-02-14 16:21:55 -05:00
Alert . showError (
I18n . _ ( 'The library %s is not available.' , 'showdown' ) +
I18n . _ ( 'This may cause display errors.' )
2017-02-13 05:35:04 -05:00
) ;
}
// get default option from template/HTML or fall back to set value
2017-02-14 16:21:55 -05:00
format = Modal . getFormatDefault ( ) || format ;
} ;
return me ;
} ) ( window , document ) ;
/ * *
* ( view ) Show attachment and preview if possible
*
* @ param { object } window
* @ param { object } document
* @ class
* /
var AttachmentViewer = ( function ( window , document ) {
var me = { } ;
var $attachment ,
$attachmentLink ,
$clonedFile ,
$attachmentPreview ,
$fileWrap ;
var attachmentChanged = false ,
attachmentHasPreview = false ;
/ * *
* sets the attachment but does not yet show it
*
* @ name AttachmentViewer . setAttachment
* @ function
* @ param { string } attachmentData - base64 - encoded data of file
* @ param { string } fileName - optional , file name
* /
me . setAttachment = function ( attachmentData , fileName )
{
var imagePrefix = 'data:image/' ;
$attachmentLink . attr ( 'href' , attachmentData ) ;
if ( typeof fileName !== 'undefined' ) {
$attachmentLink . attr ( 'download' , fileName ) ;
}
// if the attachment is an image, display it
if ( attachmentData . substring ( 0 , imagePrefix . length ) === imagePrefix ) {
$attachmentPreview . html (
$ ( document . createElement ( 'img' ) )
. attr ( 'src' , attachmentData )
. attr ( 'class' , 'img-thumbnail' )
) ;
attachmentHasPreview = true ;
}
attachmentChanged = true ;
} ;
/ * *
* displays the attachment
*
* @ name AttachmentViewer . showAttachment
* @ function
* /
me . showAttachment = function ( )
{
$attachment . removeClass ( 'hidden' ) ;
if ( attachmentHasPreview ) {
$attachmentPreview . removeClass ( 'hidden' ) ;
}
}
/ * *
* removes the existing attachment
*
* @ name AttachmentViewer . removeAttachment
* @ function
* /
me . removeAttachment = function ( )
{
// (new)
$attachment . addClass ( 'hidden' ) ;
$attachmentPreview . addClass ( 'hidden' ) ;
$clonedFile . addClass ( 'hidden' ) ;
// removes the saved decrypted file data
$attachmentLink . attr ( 'href' , '' ) ;
// the only way to deselect the file is to recreate the input // @TODO really?
$fileWrap . html ( $fileWrap . html ( ) ) ;
$fileWrap . removeClass ( 'hidden' ) ;
// reset internal variables
} ;
/ * *
* checks if there is an attachment
*
* @ name AttachmentViewer . hasAttachment
* @ function
* /
me . hasAttachment = function ( )
{
return typeof $attachmentLink . attr ( 'href' ) !== 'undefined'
} ;
/ * *
* return the attachment
*
* @ name AttachmentViewer . getAttachment
* @ function
* @ returns { array }
* /
me . getAttachment = function ( )
{
return [
$attachmentLink . attr ( 'href' ) ,
$attachmentLink . attr ( 'download' )
] ;
} ;
/ * *
* initiate
*
* preloads jQuery elements
*
* @ name AttachmentViewer . init
* @ function
* /
me . init = function ( )
{
$attachmentPreview = $ ( '#attachmentPreview' ) ;
$attachment = $ ( '#attachment' ) ;
$attachmentLink = $ ( '#attachment a' ) ;
$clonedFile = $ ( '#clonedfile' ) ;
$fileWrap = $ ( '#filewrap' ) ;
} ;
return me ;
} ) ( window , document ) ;
/ * *
* ( view ) Shows discussion thread and handles replies
*
* @ param { object } window
* @ param { object } document
* @ class
* /
var DiscussionViewer = ( function ( window , document ) {
var me = { } ;
var $comments ,
$discussion ;
/ * *
* display a status message for replying to comments
*
* @ name Controller . showStatus
* @ function
* @ param { string } message - text to display
* @ param { boolean } [ spin = false ] - ( optional ) tell if the "spinning" animation should be displayed , defaults to false
* /
me . showReplyStatus = function ( message , spin )
{
if ( spin || false ) {
$replyalert . find ( '.spinner' ) . removeClass ( 'hidden' )
}
$replyalert . text ( message ) ;
} ;
/ * *
* display an error message
*
* @ name Alert . showError
* @ function
* @ param { string } message - text to display
* /
me . showReplyError = function ( message )
{
$replyalert . addClass ( 'Alert-danger' ) ;
$replyalert . addClass ( $errorMessage . attr ( 'class' ) ) ; // @TODO ????
$replyalert . text ( message ) ;
} ;
/ * *
* open the comment entry when clicking the "Reply" button of a comment
*
* @ name PasteViewer . openReply
* @ function
* @ param { Event } event
* /
me . openReply = function ( event )
{
event . preventDefault ( ) ;
// remove any other reply area
$ ( 'div.reply' ) . remove ( ) ;
var source = $ ( event . target ) ,
commentid = event . data . commentid ,
hint = I18n . _ ( 'Optional nickname...' ) ,
$reply = $ ( '#replytemplate' ) ;
$reply . find ( 'button' ) . click (
{ parentid : commentid } ,
me . sendComment
) ;
source . after ( $reply ) ;
$replyStatus = $ ( '#replystatus' ) ; // when ID --> put into HTML
$ ( '#replymessage' ) . focus ( ) ;
} ;
/ * *
* initiate
*
* preloads jQuery elements
*
* @ name AttachmentViewer . init
* @ function
* /
me . init = function ( )
{
$comments = $ ( '#comments' ) ;
$discussion = $ ( '#discussion' ) ;
// $replyStatus in openReply()
2017-02-08 14:12:22 -05:00
} ;
2017-02-08 14:11:04 -05:00
2017-02-12 12:08:08 -05:00
return me ;
} ) ( window , document ) ;
/ * *
* Manage top ( navigation ) bar
*
* @ param { object } window
* @ param { object } document
* @ class
* /
2017-02-14 16:21:55 -05:00
var TopNav = ( function ( window , document ) {
2017-02-12 12:08:08 -05:00
var me = { } ;
2017-02-12 15:13:04 -05:00
var createButtonsDisplayed = false ;
var viewButtonsDisplayed = false ;
2017-02-12 12:08:08 -05:00
var $attach ,
$burnAfterReading ,
$burnAfterReadingOption ,
$cloneButton ,
$expiration ,
$fileRemoveButton ,
$formatter ,
$newButton ,
2017-02-13 05:35:04 -05:00
$openDiscussionOption ,
2017-02-12 12:08:08 -05:00
$openDiscussion ,
2017-02-12 15:13:04 -05:00
$password ,
2017-02-13 15:12:00 -05:00
$passwordInput ,
2017-02-12 12:08:08 -05:00
$rawTextButton ,
2017-02-13 15:12:00 -05:00
$sendButton ,
$loadingIndicator ;
2017-02-12 12:08:08 -05:00
2017-02-13 05:35:04 -05:00
var pasteExpiration = '1week' ;
2017-02-12 12:08:08 -05:00
/ * *
2017-02-13 05:35:04 -05:00
* set the expiration on bootstrap templates in dropdown
2017-02-12 12:08:08 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . updateExpiration
2017-02-12 12:08:08 -05:00
* @ function
* @ param { Event } event
* /
2017-02-13 05:35:04 -05:00
function updateExpiration ( event )
2017-02-12 12:08:08 -05:00
{
2017-02-13 05:35:04 -05:00
// get selected option
2017-02-12 12:08:08 -05:00
var target = $ ( event . target ) ;
2017-02-13 05:35:04 -05:00
// update dropdown display and save new expiration time
2017-02-12 12:08:08 -05:00
$ ( '#pasteExpirationDisplay' ) . text ( target . text ( ) ) ;
2017-02-13 05:35:04 -05:00
pasteExpiration = target . data ( 'expiration' ) ;
event . preventDefault ( ) ;
2017-02-12 12:08:08 -05:00
}
/ * *
2017-02-13 05:35:04 -05:00
* set the format on bootstrap templates in dropdown
2017-02-12 12:08:08 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . updateFormat
2017-02-12 12:08:08 -05:00
* @ function
* @ param { Event } event
* /
2017-02-13 05:35:04 -05:00
function updateFormat ( event )
2017-02-12 12:08:08 -05:00
{
2017-02-13 05:35:04 -05:00
// get selected option
var $target = $ ( event . target ) ;
2017-02-12 12:08:08 -05:00
2017-02-13 05:35:04 -05:00
// update dropdown display and save new format
var newFormat = $target . data ( 'format' ) ;
$ ( '#pasteFormatterDisplay' ) . text ( $target . text ( ) ) ;
2017-02-14 16:21:55 -05:00
PasteViewer . setFormat ( newFormat ) ;
2017-02-13 05:35:04 -05:00
// update preview
2017-02-14 16:21:55 -05:00
if ( Editor . isPreview ( ) ) {
PasteViewer . run ( ) ;
2017-02-12 12:08:08 -05:00
}
2017-02-13 05:35:04 -05:00
2017-02-12 12:08:08 -05:00
event . preventDefault ( ) ;
2017-02-13 05:35:04 -05:00
}
2017-02-08 14:12:22 -05:00
/ * *
2017-02-12 12:08:08 -05:00
* when "burn after reading" is checked , disable discussion
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . changeBurnAfterReading
2017-02-08 14:12:22 -05:00
* @ function
* /
2017-02-12 12:08:08 -05:00
function changeBurnAfterReading ( )
2017-02-08 14:11:04 -05:00
{
2017-02-13 05:35:04 -05:00
if ( $burnAfterReading . is ( ':checked' ) ) {
$openDiscussionOption . addClass ( 'buttondisabled' ) ;
$openDiscussion . prop ( 'checked' , false ) ;
// if button is actually disabled, force-enable it and uncheck other button
$burnAfterReadingOption . removeClass ( 'buttondisabled' ) ;
} else {
$openDiscussionOption . removeClass ( 'buttondisabled' ) ;
2017-02-08 14:12:22 -05:00
}
2017-02-12 12:08:08 -05:00
}
2015-09-05 11:12:11 -04:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-12 12:08:08 -05:00
* when discussion is checked , disable "burn after reading"
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . changeOpenDiscussion
2017-02-08 14:12:22 -05:00
* @ function
* /
2017-02-13 05:35:04 -05:00
function changeOpenDiscussion ( )
2015-09-05 11:12:11 -04:00
{
2017-02-13 05:35:04 -05:00
if ( $openDiscussion . is ( ':checked' ) ) {
2017-02-12 12:08:08 -05:00
$burnAfterReadingOption . addClass ( 'buttondisabled' ) ;
2017-02-13 05:35:04 -05:00
$burnAfterReading . prop ( 'checked' , false ) ;
// if button is actually disabled, force-enable it and uncheck other button
$openDiscussionOption . removeClass ( 'buttondisabled' ) ;
} else {
2017-02-12 12:08:08 -05:00
$burnAfterReadingOption . removeClass ( 'buttondisabled' ) ;
2017-02-08 14:12:22 -05:00
}
2017-02-12 12:08:08 -05:00
}
/ * *
* return raw text
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . rawText
2017-02-12 12:08:08 -05:00
* @ function
* @ param { Event } event
* /
function rawText ( event )
{
2017-02-14 16:21:55 -05:00
var paste = PasteViewer . getFormat ( ) === 'markdown' ?
$prettyPrint . text ( ) : $plainText . text ( ) ;
2017-02-12 12:08:08 -05:00
history . pushState (
2017-02-14 16:21:55 -05:00
null , document . title , Helper . baseUri ( ) + '?' +
Modal . getPasteId ( ) + '#' + Modal . getPasteKey ( )
2017-02-12 12:08:08 -05:00
) ;
// we use text/html instead of text/plain to avoid a bug when
// reloading the raw text view (it reverts to type text/html)
var newDoc = document . open ( 'text/html' , 'replace' ) ;
2017-02-14 16:21:55 -05:00
newDoc . write ( '<pre>' + Helper . htmlEntities ( paste ) + '</pre>' ) ;
2017-02-12 12:08:08 -05:00
newDoc . close ( ) ;
event . preventDefault ( ) ;
}
/ * *
2017-02-14 16:21:55 -05:00
* saves the language in a cookie and reloads the page
2017-02-12 12:08:08 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . setLanguage
2017-02-12 12:08:08 -05:00
* @ function
* @ param { Event } event
* /
function setLanguage ( event )
{
document . cookie = 'lang=' + $ ( event . target ) . data ( 'lang' ) ;
2017-02-14 16:21:55 -05:00
UiHelper . reloadHome ( ) ;
2017-02-12 12:08:08 -05:00
}
2017-02-08 14:12:22 -05:00
/ * *
2017-02-12 12:08:08 -05:00
* Shows all elements belonging to viwing an existing pastes
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . hideAllElem
2017-02-08 14:12:22 -05:00
* @ function
* /
2017-02-12 12:08:08 -05:00
me . showViewButtons = function ( )
2015-09-05 11:12:11 -04:00
{
2017-02-12 15:13:04 -05:00
if ( viewButtonsDisplayed ) {
console . log ( 'showViewButtons: view buttons are already displayed' ) ;
return ;
}
2017-02-12 12:08:08 -05:00
$cloneButton . removeClass ( 'hidden' ) ;
$rawTextButton . removeClass ( 'hidden' ) ;
2017-02-12 15:13:04 -05:00
viewButtonsDisplayed = true ;
2017-02-12 12:08:08 -05:00
} ;
2017-02-08 14:12:22 -05:00
2017-02-12 12:08:08 -05:00
/ * *
* Hides all elements belonging to existing pastes
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . hideAllElem
2017-02-12 12:08:08 -05:00
* @ function
* /
me . hideViewButtons = function ( )
{
2017-02-12 15:13:04 -05:00
if ( ! viewButtonsDisplayed ) {
console . log ( 'hideViewButtons: view buttons are already hidden' ) ;
return ;
}
2017-02-14 16:21:55 -05:00
$newButton . removeClass ( 'hidden' ) ;
2017-02-12 12:08:08 -05:00
$cloneButton . addClass ( 'hidden' ) ;
$rawTextButton . addClass ( 'hidden' ) ;
2017-02-12 15:13:04 -05:00
viewButtonsDisplayed = false ;
2017-02-12 12:08:08 -05:00
} ;
2017-02-08 14:12:22 -05:00
2017-02-12 12:08:08 -05:00
/ * *
* shows all elements needed when creating a new paste
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . setLanguage
2017-02-12 12:08:08 -05:00
* @ function
* /
me . showCreateButtons = function ( )
{
2017-02-12 15:13:04 -05:00
if ( createButtonsDisplayed ) {
console . log ( 'showCreateButtons: create buttons are already displayed' ) ;
return ;
}
2017-02-12 12:08:08 -05:00
$sendButton . removeClass ( 'hidden' ) ;
$expiration . removeClass ( 'hidden' ) ;
$formatter . removeClass ( 'hidden' ) ;
$burnAfterReadingOption . removeClass ( 'hidden' ) ;
2017-02-13 05:35:04 -05:00
$openDiscussionOption . removeClass ( 'hidden' ) ;
2017-02-12 12:08:08 -05:00
$newButton . removeClass ( 'hidden' ) ;
$password . removeClass ( 'hidden' ) ;
$attach . removeClass ( 'hidden' ) ;
2017-02-13 05:35:04 -05:00
// $clonedFile.removeClass('hidden'); // @TODO
2017-02-12 15:13:04 -05:00
createButtonsDisplayed = true ;
2017-02-12 12:08:08 -05:00
} ;
2017-02-08 14:12:22 -05:00
2017-02-12 12:08:08 -05:00
/ * *
* shows all elements needed when creating a new paste
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . setLanguage
2017-02-12 12:08:08 -05:00
* @ function
* /
me . hideCreateButtons = function ( )
{
2017-02-12 15:13:04 -05:00
if ( ! createButtonsDisplayed ) {
console . log ( 'hideCreateButtons: create buttons are already hidden' ) ;
return ;
}
2017-02-14 16:21:55 -05:00
$newButton . addClass ( 'hidden' ) ;
2017-02-12 12:08:08 -05:00
$sendButton . addClass ( 'hidden' ) ;
$expiration . addClass ( 'hidden' ) ;
$formatter . addClass ( 'hidden' ) ;
$burnAfterReadingOption . addClass ( 'hidden' ) ;
2017-02-13 05:35:04 -05:00
$openDiscussionOption . addClass ( 'hidden' ) ;
2017-02-12 12:08:08 -05:00
$password . addClass ( 'hidden' ) ;
$attach . addClass ( 'hidden' ) ;
2017-02-13 05:35:04 -05:00
// $clonedFile.addClass('hidden'); // @TODO
2017-02-12 15:13:04 -05:00
2017-02-14 16:21:55 -05:00
createButtonsDisplayed = false ;
} ;
/ * *
* only shows the "new paste" button
*
* @ name TopNav . setLanguage
* @ function
* /
me . showNewPasteButton = function ( )
{
$newButton . removeClass ( 'hidden' ) ;
} ;
/ * *
* only hides the clone button
*
* @ name TopNav . hideCloneButton
* @ function
* /
me . hideCloneButton = function ( )
{
$cloneButton . addClass ( 'hidden' ) ;
2017-02-12 12:08:08 -05:00
} ;
2012-04-23 10:30:02 -04:00
2017-02-12 12:08:08 -05:00
/ * *
2017-02-14 16:21:55 -05:00
* only hides the raw text button
2017-02-12 12:08:08 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . hideRawButton
2017-02-12 12:08:08 -05:00
* @ function
* /
2017-02-14 16:21:55 -05:00
me . hideRawButton = function ( )
2017-02-12 12:08:08 -05:00
{
2017-02-14 16:21:55 -05:00
$rawTextButton . addClass ( 'hidden' ) ;
2017-02-08 14:12:22 -05:00
} ;
2012-04-23 10:30:02 -04:00
2017-02-08 14:12:22 -05:00
/ * *
2017-02-12 12:08:08 -05:00
* shows a loading message , optionally with a percentage
2017-02-08 14:12:22 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . showLoading
2017-02-08 14:12:22 -05:00
* @ function
2017-02-13 15:12:00 -05:00
* @ param { string } message optional , default : 'Loading…'
* @ param { int } percentage optional , default : null
2017-02-08 14:12:22 -05:00
* /
2017-02-12 12:08:08 -05:00
me . showLoading = function ( message , percentage )
2017-02-08 14:11:04 -05:00
{
2017-02-13 15:12:00 -05:00
// default message text
if ( typeof message === 'undefined' ) {
2017-02-14 16:21:55 -05:00
message = I18n . _ ( 'Loading…' ) ;
2017-02-13 15:12:00 -05:00
}
// currently percentage parameter is ignored
if ( message !== null ) {
$loadingIndicator . find ( ':last' ) . text ( message ) ;
}
2017-02-12 12:08:08 -05:00
$loadingIndicator . removeClass ( 'hidden' ) ;
} ;
/ * *
* hides the loading message
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . hideLoading
2017-02-12 12:08:08 -05:00
* @ function
* /
me . hideLoading = function ( )
{
2017-02-13 15:12:00 -05:00
$loadingIndicator . addClass ( 'hidden' ) ;
} ;
/ * *
* collapses the navigation bar if nedded
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . collapseBar
2017-02-13 15:12:00 -05:00
* @ function
* /
me . collapseBar = function ( )
{
var $bar = $ ( '.navbar-toggle' ) ;
// check if bar is expanded
if ( $bar . hasClass ( 'collapse in' ) ) {
// if so, toggle it
$bar . click ( ) ;
}
2017-02-12 12:08:08 -05:00
} ;
2017-02-08 14:12:22 -05:00
2017-02-13 05:35:04 -05:00
/ * *
* returns the currently set expiration time
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . getExpiration
2017-02-13 05:35:04 -05:00
* @ function
* @ return { int }
* /
me . getExpiration = function ( )
{
return pasteExpiration ;
} ;
2017-02-13 15:12:00 -05:00
/ * *
* returns the currently selected file ( s )
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . getFileList
2017-02-13 15:12:00 -05:00
* @ function
* @ return { FileList | null }
* /
me . getFileList = function ( )
{
var $file = $ ( '#file' ) ;
// if no file given, return null
if ( ! $file . length || ! $file [ 0 ] . files . length ) {
return null ;
}
// @TODO is this really necessary
if ( ! ( $file [ 0 ] . files && $file [ 0 ] . files [ 0 ] ) ) {
return null ;
}
return $file [ 0 ] . files ;
} ;
/ * *
* returns the state of the burn after reading checkbox
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . getExpiration
2017-02-13 15:12:00 -05:00
* @ function
* @ return { bool }
* /
me . getBurnAfterReading = function ( )
{
return $burnAfterReading . is ( ':checked' ) ;
} ;
/ * *
* returns the state of the discussion checkbox
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . getOpenDiscussion
2017-02-13 15:12:00 -05:00
* @ function
* @ return { bool }
* /
me . getOpenDiscussion = function ( )
{
return $openDiscussion . is ( ':checked' ) ;
} ;
/ * *
* returns the entered password
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . getPassword
2017-02-13 15:12:00 -05:00
* @ function
* @ return { string }
* /
me . getPassword = function ( )
{
return $passwordInput . val ( ) ;
} ;
2017-02-12 12:08:08 -05:00
/ * *
* init navigation manager
*
* preloads jQuery elements
*
2017-02-14 16:21:55 -05:00
* @ name TopNav . init
2017-02-12 12:08:08 -05:00
* @ function
* /
me . init = function ( )
{
2017-02-08 14:12:22 -05:00
$attach = $ ( '#attach' ) ;
$burnAfterReading = $ ( '#burnafterreading' ) ;
$burnAfterReadingOption = $ ( '#burnafterreadingoption' ) ;
$cloneButton = $ ( '#clonebutton' ) ;
$expiration = $ ( '#expiration' ) ;
$fileRemoveButton = $ ( '#fileremovebutton' ) ;
$formatter = $ ( '#formatter' ) ;
$newButton = $ ( '#newbutton' ) ;
2017-02-13 05:35:04 -05:00
$openDiscussionOption = $ ( '#opendiscussionoption' ) ;
2017-02-08 14:12:22 -05:00
$openDiscussion = $ ( '#opendiscussion' ) ;
2017-02-12 15:13:04 -05:00
$password = $ ( '#password' ) ;
2017-02-13 15:12:00 -05:00
$passwordInput = $ ( '#passwordinput' ) ;
2017-02-08 14:12:22 -05:00
$rawTextButton = $ ( '#rawtextbutton' ) ;
$sendButton = $ ( '#sendbutton' ) ;
2017-02-13 15:12:00 -05:00
$loadingIndicator = $ ( '#loadingindicator' ) ;
2013-10-31 20:15:14 -04:00
2017-02-12 12:08:08 -05:00
// bootstrap template drop down
$ ( '#language ul.dropdown-menu li a' ) . click ( me . setLanguage ) ;
// page template drop down
$ ( '#language select option' ) . click ( me . setLanguage ) ;
// bind events
$burnAfterReading . change ( changeBurnAfterReading ) ;
2017-02-13 05:35:04 -05:00
$openDiscussionOption . change ( changeOpenDiscussion ) ;
2017-02-14 16:21:55 -05:00
$newButton . click ( Controller . newPaste ) ;
$sendButton . click ( PasteEncrypter . submitPaste ) ;
$cloneButton . click ( Controller . clonePaste ) ;
2017-02-12 15:13:04 -05:00
$rawTextButton . click ( rawText ) ;
2017-02-12 12:08:08 -05:00
$fileRemoveButton . click ( me . removeAttachment ) ;
2017-02-13 05:35:04 -05:00
// bootstrap template drop downs
$ ( 'ul.dropdown-menu li a' , $ ( '#expiration' ) . parent ( ) ) . click ( updateExpiration ) ;
$ ( 'ul.dropdown-menu li a' , $ ( '#formatter' ) . parent ( ) ) . click ( updateFormat ) ;
2017-02-12 12:08:08 -05:00
// initiate default state of checkboxes
changeBurnAfterReading ( ) ;
2017-02-13 05:35:04 -05:00
changeOpenDiscussion ( ) ;
// get default value from template or fall back to set value
2017-02-14 16:21:55 -05:00
pasteExpiration = Modal . getExpirationDefault ( ) || pasteExpiration ;
2017-02-12 12:08:08 -05:00
} ;
return me ;
} ) ( window , document ) ;
/ * *
2017-02-13 15:12:00 -05:00
* Responsible for AJAX requests , transparently handles encryption …
2017-02-12 12:08:08 -05:00
*
* @ class
* /
2017-02-14 16:21:55 -05:00
var Uploader = ( function ( ) {
2017-02-12 12:08:08 -05:00
var me = { } ;
2017-02-13 15:12:00 -05:00
var successFunc = null ,
2017-02-14 16:21:55 -05:00
failureFunc = null ,
url ,
data ,
2017-02-13 15:12:00 -05:00
randomKey ,
password ;
2017-02-14 16:21:55 -05:00
/ * *
* public variable ( 'constant' ) for errors to prevent magic numbers
*
* @ readonly
* @ enum { Object }
* /
2017-02-13 15:12:00 -05:00
me . error = {
okay : 0 ,
custom : 1 ,
unknown : 2 ,
serverError : 3
} ;
2017-02-12 12:08:08 -05:00
/ * *
2017-02-13 15:12:00 -05:00
* ajaxHeaders to send in AJAX requests
2017-02-12 12:08:08 -05:00
*
* @ private
2017-02-13 15:12:00 -05:00
* @ readonly
2017-02-12 12:08:08 -05:00
* @ enum { Object }
* /
2017-02-13 15:12:00 -05:00
var ajaxHeaders = { 'X-Requested-With' : 'JSONHttpRequest' } ;
2017-02-12 12:08:08 -05:00
/ * *
2017-02-13 15:12:00 -05:00
* called after successful upload
2017-02-12 12:08:08 -05:00
*
2017-02-13 15:12:00 -05:00
* @ function
* @ param { int } status
* @ param { int } data - optional
2017-02-12 12:08:08 -05:00
* /
2017-02-13 15:12:00 -05:00
function success ( status , result )
{
// add useful data to result
result . encryptionKey = randomKey ;
result . requestData = data ;
if ( successFunc !== null ) {
successFunc ( status , result ) ;
}
}
2017-02-12 12:08:08 -05:00
/ * *
2017-02-13 15:12:00 -05:00
* called after a upload failure
2017-02-12 12:08:08 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Uploader . submitPasteUpload
2017-02-13 15:12:00 -05:00
* @ function
* @ param { int } status - internal code
* @ param { int } data - original error code
2017-02-12 12:08:08 -05:00
* /
2017-02-13 15:12:00 -05:00
function fail ( status , result )
{
if ( failureFunc !== null ) {
failureFunc ( status , result ) ;
}
}
/ * *
* actually uploads the data
*
2017-02-14 16:21:55 -05:00
* @ name Uploader . run
2017-02-13 15:12:00 -05:00
* @ function
* /
2017-02-14 16:21:55 -05:00
me . run = function ( )
2017-02-13 15:12:00 -05:00
{
$ . ajax ( {
type : 'POST' ,
url : url ,
data : data ,
dataType : 'json' ,
headers : ajaxHeaders ,
success : function ( result ) {
if ( result . status === 0 ) {
success ( 0 , result ) ;
} else if ( result . status === 1 ) {
fail ( 1 , result ) ;
} else {
fail ( 2 , result ) ;
}
}
} )
. fail ( function ( jqXHR , textStatus , errorThrown ) {
console . error ( textStatus , errorThrown ) ;
fail ( 3 , jqXHR ) ;
} ) ;
} ;
/ * *
* set success function
*
2017-02-14 16:21:55 -05:00
* @ name Uploader . setSuccess
* @ function
* @ param { function } func
* /
me . setUrl = function ( newUrl )
{
url = newUrl ;
} ;
/ * *
* set success function
*
* @ name Uploader . setSuccess
2017-02-13 15:12:00 -05:00
* @ function
* @ param { function } func
* /
me . setSuccess = function ( func )
{
successFunc = func ;
} ;
/ * *
* set failure function
*
2017-02-14 16:21:55 -05:00
* @ name Uploader . setSuccess
2017-02-13 15:12:00 -05:00
* @ function
* @ param { function } func
* /
me . setFailure = function ( func )
{
failureFunc = func ;
} ;
/ * *
* prepares a new upload
*
2017-02-14 16:21:55 -05:00
* @ name Uploader . prepare
2017-02-13 15:12:00 -05:00
* @ function
* @ param { string } newPassword
* @ return { object }
* /
me . prepare = function ( newPassword )
{
// set password
password = newPassword ;
2017-02-14 16:21:55 -05:00
// entropy should already be checked!
2017-02-13 15:12:00 -05:00
// generate a new random key
2017-02-14 16:21:55 -05:00
randomKey = CryptTool . getSymmetricKey ( ) ;
2017-02-13 15:12:00 -05:00
// reset data
2017-02-14 16:21:55 -05:00
successFunc = null ;
failureFunc = null ;
url = Helper . baseUri ( )
2017-02-13 15:12:00 -05:00
data = { } ;
} ;
/ * *
* encrypts and sets the data
*
2017-02-14 16:21:55 -05:00
* @ name Uploader . setData
2017-02-13 15:12:00 -05:00
* @ function
* @ param { string } index
* @ param { mixed } element
* /
me . setData = function ( index , element )
{
2017-02-14 16:21:55 -05:00
data [ index ] = CryptTool . cipher ( randomKey , password , element ) ;
2017-02-13 15:12:00 -05:00
} ;
/ * *
* set the additional metadata to send unencrypted
*
2017-02-14 16:21:55 -05:00
* @ name Uploader . setUnencryptedData
2017-02-13 15:12:00 -05:00
* @ function
* @ param { string } index
* @ param { mixed } element
* /
me . setUnencryptedData = function ( index , element )
{
data [ index ] = element ;
} ;
/ * *
* set the additional metadata to send unencrypted passed at once
*
2017-02-14 16:21:55 -05:00
* @ name Uploader . setUnencryptedData
2017-02-13 15:12:00 -05:00
* @ function
* @ param { object } newData
* /
me . setUnencryptedBulkData = function ( newData )
{
$ . extend ( data , newData ) ;
} ;
/ * *
2017-02-14 16:21:55 -05:00
* init Uploader
2017-02-13 15:12:00 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name Uploader . init
2017-02-13 15:12:00 -05:00
* @ function
* /
me . init = function ( )
{
// nothing yet
} ;
return me ;
} ) ( ) ;
/ * *
2017-02-14 16:21:55 -05:00
* ( controller ) Responsible for encrypting paste and sending it to server .
2017-02-13 15:12:00 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name state
2017-02-13 15:12:00 -05:00
* @ class
* /
2017-02-14 16:21:55 -05:00
var PasteEncrypter = ( function ( ) {
2017-02-13 15:12:00 -05:00
var me = { } ;
2017-02-14 16:21:55 -05:00
var requirementsChecked = false ;
/ * *
* checks whether there is a suitable amount of entrophy
*
* @ private
* @ function
* @ param { function } retryCallback - the callback to execute to retry the upload
* @ return { bool }
* /
function checkRequirements ( retryCallback ) {
// skip double requirement checks
if ( requirementsChecked === true ) {
return false ;
}
if ( ! CryptTool . isEntropyReady ( ) ) {
// display a message and wait
Alert . showStatus ( I18n . _ ( 'Please move your mouse for more entropy...' ) ) ;
CryptTool . addEntropySeedListener ( retryCallback ) ;
return false ;
}
requirementsChecked = true ;
return true ;
}
2017-02-13 15:12:00 -05:00
/ * *
* called after successful upload
*
2017-02-14 16:21:55 -05:00
* @ private
2017-02-13 15:12:00 -05:00
* @ function
* @ param { int } status
* @ param { int } data
* /
function showCreatedPaste ( status , data ) {
2017-02-14 16:21:55 -05:00
TopNav . hideLoading ( ) ;
2017-02-13 15:12:00 -05:00
2017-02-14 16:21:55 -05:00
var url = Helper . baseUri ( ) + '?' + data . id + '#' + data . encryptionKey ,
deleteUrl = Helper . baseUri ( ) + '?pasteid=' + data . id + '&deletetoken=' + data . deletetoken ;
2017-02-13 15:12:00 -05:00
2017-02-14 16:21:55 -05:00
Alert . hideMessages ( ) ;
2017-02-13 15:12:00 -05:00
// show notification
2017-02-14 16:21:55 -05:00
PasteStatus . createPasteNotification ( url , deleteUrl )
2017-02-13 15:12:00 -05:00
// show new URL in browser bar
history . pushState ( { type : 'newpaste' } , document . title , url ) ;
2017-02-14 16:21:55 -05:00
TopNav . showViewButtons ( ) ;
TopNav . hideRawButton ( ) ;
Editor . hide ( ) ;
2017-02-13 15:12:00 -05:00
// parse and show text
// (preparation already done in me.submitPaste())
2017-02-14 16:21:55 -05:00
PasteViewer . run ( ) ;
}
/ * *
* adds attachments to the Uploader
*
* @ private
* @ function
* @ param { File | null | undefined } file - optional , falls back to cloned attachment
* @ param { function } callback - excuted when action is successful
* /
function encryptAttachments ( file , callback ) {
if ( typeof file !== 'undefined' && file !== null ) {
// check file reader requirements for upload
if ( typeof FileReader === 'undefined' ) {
Alert . showError ( I18n . _ ( 'Your browser does not support uploading encrypted files. Please use a newer browser.' ) ) ;
// cancels process as it does not execute callback
return ;
}
var reader = new FileReader ( ) ;
// closure to capture the file information
reader . onload = function ( event ) {
Uploader . setData ( 'attachment' , event . target . result ) ;
Uploader . setData ( 'attachmentname' , file . name ) ;
// run callback
callback ( ) ;
} ;
// actually read first file
reader . readAsDataURL ( file ) ;
} else if ( AttachmentViewer . hasAttachment ( ) ) {
// fall back to cloned part
var attachment = AttachmentViewer . getAttachment ( ) ;
Uploader . setData ( 'attachment' , attachment [ 0 ] ) ;
Uploader . setUnencryptedData ( 'attachmentname' , attachment [ 1 ] ) ; // @TODO does not encrypt file name??!
callback ( ) ;
} else {
// if there are no attachments, this is of course still successful
callback ( ) ;
}
2017-02-13 15:12:00 -05:00
}
2017-02-12 12:08:08 -05:00
/ * *
* send a reply in a discussion
*
2017-02-14 16:21:55 -05:00
* @ name PasteEncrypter . sendComment
2017-02-12 12:08:08 -05:00
* @ function
* @ param { Event } event
2017-02-14 16:21:55 -05:00
* @ TODO WIP
2017-02-12 12:08:08 -05:00
* /
me . sendComment = function ( event )
{
event . preventDefault ( ) ;
$errorMessage . addClass ( 'hidden' ) ;
// do not send if no data
var replyMessage = $ ( '#replymessage' ) ;
if ( replyMessage . val ( ) . length === 0 )
{
return ;
}
2017-02-14 16:21:55 -05:00
me . showStatus ( I18n . _ ( 'Sending comment...' ) , true ) ;
2017-02-12 12:08:08 -05:00
var parentid = event . data . parentid ,
2017-02-14 16:21:55 -05:00
key = Modal . getPasteKey ( ) ,
cipherdata = CryptTool . cipher ( key , $passwordInput . val ( ) , replyMessage . val ( ) ) ,
2017-02-12 12:08:08 -05:00
ciphernickname = '' ,
nick = $ ( '#nickname' ) . val ( ) ;
if ( nick . length > 0 )
{
2017-02-14 16:21:55 -05:00
ciphernickname = CryptTool . cipher ( key , $passwordInput . val ( ) , nick ) ;
2017-02-12 12:08:08 -05:00
}
2017-02-13 15:12:00 -05:00
var dataToSend = {
2017-02-12 12:08:08 -05:00
data : cipherdata ,
parentid : parentid ,
2017-02-14 16:21:55 -05:00
pasteid : Modal . getPasteId ( ) ,
2017-02-12 12:08:08 -05:00
nickname : ciphernickname
} ;
$ . ajax ( {
type : 'POST' ,
2017-02-14 16:21:55 -05:00
url : Helper . baseUri ( ) ,
2017-02-13 15:12:00 -05:00
data : dataToSend ,
2017-02-12 12:08:08 -05:00
dataType : 'json' ,
2017-02-13 15:12:00 -05:00
headers : ajaxHeaders ,
success : function ( data ) {
2017-02-12 12:08:08 -05:00
if ( data . status === 0 )
{
2017-02-14 16:21:55 -05:00
status . showStatus ( I18n . _ ( 'Comment posted.' ) ) ;
2017-02-12 12:08:08 -05:00
$ . ajax ( {
type : 'GET' ,
2017-02-14 16:21:55 -05:00
url : Helper . baseUri ( ) + '?' + Modal . getPasteId ( ) ,
2017-02-12 12:08:08 -05:00
dataType : 'json' ,
2017-02-13 15:12:00 -05:00
headers : ajaxHeaders ,
success : function ( data ) {
2017-02-12 12:08:08 -05:00
if ( data . status === 0 )
{
2017-02-13 15:12:00 -05:00
me . displayMessages ( data ) ;
2017-02-12 12:08:08 -05:00
}
else if ( data . status === 1 )
{
2017-02-14 16:21:55 -05:00
Alert . showError ( I18n . _ ( 'Could not refresh display: %s' , data . message ) ) ;
2017-02-12 12:08:08 -05:00
}
else
{
2017-02-14 16:21:55 -05:00
Alert . showError ( I18n . _ ( 'Could not refresh display: %s' , I18n . _ ( 'unknown status' ) ) ) ;
2017-02-12 12:08:08 -05:00
}
}
} )
. fail ( function ( ) {
2017-02-14 16:21:55 -05:00
Alert . showError ( I18n . _ ( 'Could not refresh display: %s' , I18n . _ ( 'server error or not responding' ) ) ) ;
2017-02-12 12:08:08 -05:00
} ) ;
}
else if ( data . status === 1 )
{
2017-02-14 16:21:55 -05:00
Alert . showError ( I18n . _ ( 'Could not post comment: %s' , data . message ) ) ;
2017-02-12 12:08:08 -05:00
}
else
{
2017-02-14 16:21:55 -05:00
Alert . showError ( I18n . _ ( 'Could not post comment: %s' , I18n . _ ( 'unknown status' ) ) ) ;
2017-02-12 12:08:08 -05:00
}
}
} )
. fail ( function ( ) {
2017-02-14 16:21:55 -05:00
Alert . showError ( I18n . _ ( 'Could not post comment: %s' , I18n . _ ( 'server error or not responding' ) ) ) ;
2017-02-12 12:08:08 -05:00
} ) ;
} ;
/ * *
2017-02-13 15:12:00 -05:00
* sends a new paste to server
2017-02-12 12:08:08 -05:00
*
2017-02-14 16:21:55 -05:00
* @ name PasteEncrypter . submitPaste
2017-02-12 12:08:08 -05:00
* @ function
* /
2017-02-13 15:12:00 -05:00
me . submitPaste = function ( )
2017-02-12 12:08:08 -05:00
{
2017-02-13 15:12:00 -05:00
// UI loading state
2017-02-14 16:21:55 -05:00
TopNav . hideCreateButtons ( ) ;
TopNav . showLoading ( I18n . _ ( 'Sending paste...' ) , 0 ) ;
TopNav . collapseBar ( ) ;
2017-02-12 12:08:08 -05:00
2017-02-13 15:12:00 -05:00
// get data
2017-02-14 16:21:55 -05:00
var plainText = Editor . getText ( ) ,
format = PasteViewer . getFormat ( ) ,
files = TopNav . getFileList ( ) ;
2017-02-13 15:12:00 -05:00
// do not send if there is no data
if ( plainText . length === 0 && files === null ) {
// revert loading status…
2017-02-14 16:21:55 -05:00
TopNav . hideLoading ( ) ;
TopNav . showCreateButtons ( ) ;
2017-02-12 12:08:08 -05:00
return ;
}
2017-02-14 16:21:55 -05:00
TopNav . showLoading ( I18n . _ ( 'Sending paste...' ) , 10 ) ;
2017-02-13 15:12:00 -05:00
// check entropy
2017-02-14 16:21:55 -05:00
if ( ! checkRequirements ( function ( ) {
me . submitPaste ( ) ;
} ) ) {
return ; // to prevent multiple executions
2017-02-12 12:08:08 -05:00
}
2017-02-14 16:21:55 -05:00
// prepare Uploader
Uploader . prepare ( TopNav . getPassword ( ) ) ;
2017-02-13 15:12:00 -05:00
// set success/fail functions
2017-02-14 16:21:55 -05:00
Uploader . setSuccess ( showCreatedPaste ) ;
Uploader . setFailure ( function ( status , data ) {
2017-02-12 12:08:08 -05:00
// revert loading status…
2017-02-14 16:21:55 -05:00
TopNav . hideLoading ( ) ;
TopNav . showCreateButtons ( ) ;
2017-02-13 15:12:00 -05:00
// show error message
switch ( status ) {
2017-02-14 16:21:55 -05:00
case Uploader . error [ 'custom' ] :
Alert . showError ( I18n . _ ( 'Could not create paste: %s' , data . message ) ) ;
2017-02-13 15:12:00 -05:00
break ;
2017-02-14 16:21:55 -05:00
case Uploader . error [ 'unknown' ] :
Alert . showError ( I18n . _ ( 'Could not create paste: %s' , I18n . _ ( 'unknown status' ) ) ) ;
2017-02-13 15:12:00 -05:00
break ;
2017-02-14 16:21:55 -05:00
case Uploader . error [ 'serverError' ] :
Alert . showError ( I18n . _ ( 'Could not create paste: %s' , I18n . _ ( 'server error or not responding' ) ) ) ;
2017-02-13 15:12:00 -05:00
break ;
default :
2017-02-14 16:21:55 -05:00
Alert . showError ( I18n . _ ( 'Could not create paste: %s' , I18n . _ ( 'unknown error' ) ) ) ;
2017-02-13 15:12:00 -05:00
break ;
}
2017-02-12 12:08:08 -05:00
} ) ;
2017-02-13 15:12:00 -05:00
// fill it with unencrypted submitted options
2017-02-14 16:21:55 -05:00
Uploader . setUnencryptedBulkData ( {
expire : TopNav . getExpiration ( ) ,
2017-02-13 15:12:00 -05:00
formatter : format ,
2017-02-14 16:21:55 -05:00
burnafterreading : TopNav . getBurnAfterReading ( ) ? 1 : 0 ,
opendiscussion : TopNav . getOpenDiscussion ( ) ? 1 : 0
2017-02-13 15:12:00 -05:00
} ) ;
2017-02-12 12:08:08 -05:00
2017-02-13 15:12:00 -05:00
// prepare PasteViewer for later preview
2017-02-14 16:21:55 -05:00
PasteViewer . setText ( plainText ) ;
PasteViewer . setFormat ( format ) ;
// encrypt cipher data
Uploader . setData ( 'data' , plainText ) ;
// encrypt attachments
encryptAttachments (
files === null ? null : files [ 0 ] ,
function ( ) {
// send data
Uploader . run ( ) ;
}
) ;
} ;
/ * *
* initialize
*
* @ name PasteEncrypter . init
* @ function
* /
me . init = function ( )
{
// nothing yet
} ;
return me ;
} ) ( ) ;
/ * *
* ( controller ) Responsible for decrypting cipherdata and passing data to view .
*
* @ name state
* @ class
* /
var PasteDecrypter = ( function ( ) {
var me = { } ;
/ * *
* decrypt the actual paste text
*
* @ private
* @ function
* @ param { object } paste - paste data in object form
* @ param { string } key
* @ param { string } password
* @ return { bool } - whether action was successful
* /
function decryptPaste ( paste , key , password )
{
// try decryption without password
var plaintext = CryptTool . decipher ( key , password , paste . data ) ;
// if it fails, request password
if ( plaintext . length === 0 && password . length === 0 ) {
// get password
password = Prompt . getPassword ( ) ;
// if password is there, re-try
if ( password . length !== 0 ) {
// recursive
// note: an infinite loop is prevented as the previous if
// clause checks whether a password is already set and ignores
// error with password being passed
return decryptPaste ( paste , key , password ) ;
}
// trigger password request
Prompt . requestPassword ( ) ;
// the callback (via setPasswordCallback()) should have been set
// by parent function
return false ;
}
// if all tries failed, we can only throw an error
if ( plaintext . length === 0 ) {
throw 'failed to decipher message' ;
}
// on success show paste
PasteViewer . setFormat ( paste . meta . formatter ) ;
PasteViewer . setText ( plaintext ) ;
// trigger to show the text (attachment loaded afterwards)
PasteViewer . run ( ) ;
return true ;
}
/ * *
* decrypts any attachment
*
* @ private
* @ function
* @ param { object } paste - paste data in object form
* @ param { string } key
* @ param { string } password
* @ return { bool } - whether action was successful
* /
function decryptAttachment ( paste , key , password )
{
// decrypt attachment
var attachment = CryptTool . decipher ( key , password , paste . attachment ) ;
if ( attachment . length === 0 ) {
throw 'failed to decipher attachment' ;
}
// decrypt attachment name
var attachmentName ;
if ( paste . attachmentname ) {
attachmentName = attachmentName = CryptTool . decipher ( key , password , paste . attachmentname ) ;
if ( attachmentName . length === 0 ) {
// @TODO considering the buggy cloning (?, see other todo comment) this might affect previous pastes
throw 'failed to decipher attachment name' ;
}
}
AttachmentViewer . setAttachment ( attachment , attachmentName ) ;
AttachmentViewer . showAttachment ( ) ;
}
/ * *
* show decrypted text in the display area , including discussion ( if open )
*
* @ name PasteDecrypter . run
* @ function
* @ param { Object } [ paste ] - ( optional ) object including comments to display ( items = array with keys ( 'data' , 'meta' ) )
* /
me . run = function ( paste )
{
TopNav . showLoading ( 'Decrypting paste…' ) ;
if ( typeof paste === 'undefined' ) {
paste = $ . parseJSON ( Modal . getCipherData ( ) ) ;
}
var key = Modal . getPasteKey ( ) ,
password = Prompt . getPassword ( ) ;
if ( PasteViewer . isPrettyPrinted ( ) ) {
console . error ( 'Too pretty! (don\'t know why this check)' ) ; //@TODO
return ;
}
// try to decrypt the paste
try {
Prompt . setPasswordCallback ( function ( ) {
me . run ( paste ) ;
} ) ;
// try to decrypt paste and if it fails (because the password is
// missing) return to let JS continue and wait for user
if ( ! decryptPaste ( paste , key , password ) ) {
return ;
}
// decrypt attachments
if ( paste . attachment ) {
decryptAttachment ( paste , key , password ) ;
}
} catch ( err ) {
TopNav . hideLoading ( ) ;
// log and show error
console . error ( err ) ;
Alert . showError ( I18n . _ ( 'Could not decrypt data (Wrong key?)' ) ) ; // @TODO error is not translated
// still go on to potentially show potentially partially decrypted data
}
// shows the remaining time (until) deletion
PasteStatus . showRemainingTime ( paste . meta ) ;
// if the discussion is opened on this paste, display it
// @TODO BELOW
if ( paste . meta . opendiscussion ) {
$comments . html ( '' ) ;
var $divComment ;
// iterate over comments
for ( var i = 0 ; i < paste . comments . length ; ++ i )
{
var $place = $comments ,
comment = paste . comments [ i ] ,
commentText = CryptTool . decipher ( key , password , comment . data ) ,
$parentComment = $ ( '#comment_' + comment . parentid ) ;
$divComment = $ ( '<article><div class="comment" id="comment_' + comment . id
+ '"><div class="commentmeta"><span class="nickname"></span>'
+ '<span class="commentdate"></span></div>'
+ '<div class="commentdata"></div>'
+ '<button class="btn btn-default btn-sm">'
+ I18n . _ ( 'Reply' ) + '</button></div></article>' ) ;
var $divCommentData = $divComment . find ( 'div.commentdata' ) ;
// if parent comment exists
if ( $parentComment . length )
{
// shift comment to the right
$place = $parentComment ;
}
$divComment . find ( 'button' ) . click ( { commentid : comment . id } , me . openReply ) ;
Helper . setElementText ( $divCommentData , commentText ) ;
Helper . urls2links ( $divCommentData ) ;
// try to get optional nickname
var nick = CryptTool . decipher ( key , password , comment . meta . nickname ) ;
if ( nick . length > 0 )
{
$divComment . find ( 'span.nickname' ) . text ( nick ) ;
}
else
{
divComment . find ( 'span.nickname' ) . html ( '<i>' + I18n . _ ( 'Anonymous' ) + '</i>' ) ;
}
$divComment . find ( 'span.commentdate' )
. text ( ' (' + ( new Date ( comment . meta . postdate * 1000 ) . toLocaleString ( ) ) + ')' )
. attr ( 'title' , 'CommentID: ' + comment . id ) ;
// if an avatar is available, display it
if ( comment . meta . vizhash )
{
$divComment . find ( 'span.nickname' )
. before (
'<img src="' + comment . meta . vizhash + '" class="vizhash" title="' +
I18n . _ ( 'Anonymous avatar (Vizhash of the IP address)' ) + '" /> '
) ;
}
$place . append ( $divComment ) ;
}
// add 'add new comment' area
$divComment = $ (
'<div class="comment"><button class="btn btn-default btn-sm">' +
I18n . _ ( 'Add comment' ) + '</button></div>'
) ;
$divComment . find ( 'button' ) . click ( { commentid : Modal . getPasteId ( ) } , me . openReply ) ;
$comments . append ( $divComment ) ;
$discussion . removeClass ( 'hidden' ) ;
}
TopNav . hideLoading ( ) ;
TopNav . showViewButtons ( ) ;
} ;
2017-02-13 15:12:00 -05:00
2017-02-14 16:21:55 -05:00
/ * *
* initialize
*
* @ name PasteDecrypter . init
* @ function
* /
me . init = function ( )
{
// nothing yet
2017-02-12 12:08:08 -05:00
} ;
2017-02-14 16:21:55 -05:00
return me ;
} ) ( ) ;
/ * *
* ( controller ) main PrivateBin logic
*
* @ param { object } window
* @ param { object } document
* @ class
* /
var Controller = ( function ( window , document ) {
var me = { } ;
2017-02-12 15:13:04 -05:00
/ * *
* creates a new paste
*
2017-02-14 16:21:55 -05:00
* @ name Controller . newPaste
2017-02-12 15:13:04 -05:00
* @ function
* /
me . newPaste = function ( )
{
2017-02-14 16:21:55 -05:00
TopNav . hideViewButtons ( ) ;
TopNav . showCreateButtons ( ) ;
PasteViewer . hide ( ) ;
Editor . resetInput ( ) ;
Editor . show ( ) ;
Editor . focusInput ( ) ;
Alert . hideMessages ( ) ;
2017-02-12 15:13:04 -05:00
} ;
2017-02-12 12:08:08 -05:00
/ * *
* clone the current paste
*
2017-02-14 16:21:55 -05:00
* @ name Controller . clonePaste
2017-02-12 12:08:08 -05:00
* @ function
* @ param { Event } event
* /
me . clonePaste = function ( event )
{
me . stateNewPaste ( ) ;
// erase the id and the key in url
2017-02-14 16:21:55 -05:00
history . replaceState ( null , document . title , Helper . baseUri ( ) ) ;
2017-02-12 12:08:08 -05:00
2017-02-14 16:21:55 -05:00
Alert . hideMessages ( ) ;
2017-02-12 12:08:08 -05:00
if ( $attachmentLink . attr ( 'href' ) )
{
$clonedFile . removeClass ( 'hidden' ) ;
$fileWrap . addClass ( 'hidden' ) ;
}
2017-02-12 15:13:04 -05:00
$message . val (
2017-02-14 16:21:55 -05:00
PasteViewer . getFormat ( ) === 'markdown' ?
$prettyPrint . val ( ) : $plainText . val ( )
2017-02-12 12:08:08 -05:00
) ;
2017-02-14 16:21:55 -05:00
TopNav . collapseBar ( ) ;
} ;
2017-02-12 12:08:08 -05:00
2017-02-14 16:21:55 -05:00
/ * *
* removes a saved paste
*
* @ name Controller . removePaste
* @ function
* @ param { string } pasteId
* @ param { string } deleteToken
* /
me . removePaste = function ( pasteId , deleteToken ) {
// unfortunately many web servers don't support DELETE (and PUT) out of the box
// so we use a POST request
Uploader . prepare ( ) ;
Uploader . setUrl ( Helper . baseUri ( ) + '?' + pasteId ) ;
Uploader . setUnencryptedData ( 'deletetoken' , deleteToken ) ;
Uploader . setFailure ( function ( ) {
Controller . showError ( I18n . _ ( 'Could not delete the paste, it was not stored in burn after reading mode.' ) ) ;
} )
Uploader . run ( ) ;
2017-02-12 12:08:08 -05:00
} ;
/ * *
* application start
*
2017-02-14 16:21:55 -05:00
* @ name Controller . init
2017-02-12 12:08:08 -05:00
* @ function
* /
me . init = function ( )
{
// first load translations
2017-02-14 16:21:55 -05:00
I18n . loadTranslations ( ) ;
2017-02-12 12:08:08 -05:00
2017-02-12 15:13:04 -05:00
// initialize other modules/"classes"
2017-02-14 16:21:55 -05:00
Alert . init ( ) ;
Uploader . init ( ) ;
Modal . init ( ) ;
CryptTool . init ( ) ;
UiHelper . init ( ) ;
TopNav . init ( ) ;
Editor . init ( ) ;
PasteStatus . init ( ) ;
PasteViewer . init ( ) ;
AttachmentViewer . init ( ) ;
DiscussionViewer . init ( ) ;
PasteEncrypter . init ( ) ;
PasteDecrypter . init ( ) ;
Prompt . init ( ) ;
2017-02-08 14:11:04 -05:00
2017-02-08 14:12:22 -05:00
// display an existing paste
2017-02-14 16:21:55 -05:00
if ( Modal . hasCipherData ( ) ) {
2017-02-08 14:12:22 -05:00
// missing decryption key in URL?
2017-02-14 16:21:55 -05:00
if ( window . location . hash . length === 0 ) {
Alert . showError ( I18n . _ ( 'Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)' ) ) ;
2017-02-08 14:12:22 -05:00
return ;
}
// show proper elements on screen
2017-02-14 16:21:55 -05:00
PasteDecrypter . run ( ) ;
2017-02-13 15:12:00 -05:00
return ;
}
2017-02-12 15:13:04 -05:00
// otherwise create a new paste
me . newPaste ( ) ;
2017-02-08 14:12:22 -05:00
} ;
return me ;
} ) ( window , document ) ;
return {
2017-02-14 16:21:55 -05:00
Helper : Helper ,
I18n : I18n ,
CryptTool : CryptTool ,
TopNav : TopNav ,
Alert : Alert ,
Uploader : Uploader ,
Controller : Controller
2017-02-08 14:12:22 -05:00
} ;
} ( jQuery , sjcl , Base64 , RawDeflate ) ;