1
0
mirror of https://github.com/Tygs/0bin.git synced 2023-08-10 21:13:00 +03:00
0bin/static/js/behavior.js

636 lines
18 KiB
JavaScript
Raw Normal View History

2012-04-26 13:38:55 +04:00
;
2012-04-26 23:04:36 +04:00
2012-04-30 00:15:11 +04:00
/* Start random number generator seeding ASAP */
2012-04-24 22:15:38 +04:00
sjcl.random.startCollectors();
2012-04-28 19:11:32 +04:00
/* Ensure jquery use cache for ajax requests */
$.ajaxSetup({ cache: true });
2012-04-24 22:15:38 +04:00
/** Create a function that create inline callbacks.
We use it to create callbacks for onliners with static arguments
E.G:
$('stuff').hide(function()(console.log(1, 2, 3)))
Becomes:
$('stuff').hide(mkcb(console.log, 1, 2, 3))
*/
function mkcb(func){
var args = arguments;
return function(){
return func.apply(func, Array.prototype.slice.call(args, 1))
}
}
/***************************
**** 0bin utilities ***
****************************/
2012-04-30 00:15:11 +04:00
zerobin = {
/** Base64 + compress + encrypt, with callbacks before each operation,
and all of them are executed in a timed continuation to give
a change to the UI to respond.
*/
version: '0.1',
encrypt: function(key, content, toBase64Callback,
compressCallback, encryptCallback, doneCallback) {
setTimeout (function(){
content = sjcl.codec.utf8String.toBits(content);
if (toBase64Callback) {toBase64Callback()}
setTimeout(function(){
content = sjcl.codec.base64.fromBits(content);
if (compressCallback) {compressCallback()}
setTimeout(function(){
content = lzw.compress(content);
if (encryptCallback) {encryptCallback()}
setTimeout(function(){
content = sjcl.encrypt(key, content);
if (doneCallback) {doneCallback(content)}
}, 250);
}, 250);
}, 250);
}, 250);
2012-04-26 23:04:36 +04:00
},
/** Base64 decoding + uncompress + decrypt, with callbacks before each operation,
and all of them are executed in a timed continuation to give
a change to the UI to respond.
This is where using a library to fake synchronicity could start to be
useful, this code is starting be difficult to read. If anyone read this
and got a suggestion, by all means, speak your mind.
*/
decrypt: function(key, content, errorCallback, uncompressCallback,
fromBase64Callback, toStringCallback, doneCallback) {
/* Decrypt */
setTimeout(function(){
try {
content = sjcl.decrypt(key, content);
if (uncompressCallback) {uncompressCallback()}
/* Decompress */
setTimeout(function(){
try {
content = lzw.decompress(content);
if (fromBase64Callback) {fromBase64Callback()}
/* From base 64 to bits */
setTimeout(function(){
try {
content = sjcl.codec.base64.toBits(content);
if (toStringCallback) {toStringCallback()}
/* From bits to string */
setTimeout(function(){
try {
content = sjcl.codec.utf8String.fromBits(content);
if (doneCallback) {doneCallback(content)}
} catch (err) {
errorCallback(err);
}
}, 250); /* "End of from bits to string" */
} catch (err) {
errorCallback(err);
}
}, 250); /* End of "from base 64 to bits" */
} catch (err) {
errorCallback(err);
}
}, 250); /* End of "decompress" */
} catch (err) {
errorCallback(err);
}
}, 250); /* End of "decrypt" */
2012-04-26 23:04:36 +04:00
},
/** Create a random base64 string long enought to be suitable as
an encryption key */
2012-04-29 22:10:36 +04:00
makeKey: function() {
2012-04-26 17:26:58 +04:00
return sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0);
},
getFormatedDate: function(date){
var date = date || new Date();
return ((date.getMonth() +1 ) + '-' +
date.getDate() + '-' + date.getFullYear());
},
getFormatedTime: function(date){
var date = date || new Date();
var h = date.getHours();
var m = date.getMinutes();
var s = date.getSeconds();
if (h < 10) {h = "0" + h}
if (m < 10) {m = "0" + m}
if (s < 10) {s = "0" + s}
return h + ":" + m + ":" + s;
},
numOrdA: function(a, b){
return (a-b);
},
2012-04-29 22:10:36 +04:00
getKeys: function(){
var keys = [];
for(i=0; i <= localStorage.length; i++){
if(localStorage.key(i) != null)
keys[i] = parseInt(localStorage.key(i),10);
}
return keys.sort(zerobin.numOrdA);
},
2012-04-28 23:13:32 +04:00
/** Get a tinyurl using JSONP */
getTinyURL: function(longURL, success) {
var api = 'http://json-tinyurl.appspot.com/?url=';
2012-05-01 19:36:51 +04:00
$.getJSON(api + encodeURIComponent(longURL) + '&callback=?', function(data){
success(data.tinyurl);
});
2012-04-28 23:13:32 +04:00
},
/** Check for browser support of the named featured. Store the result
and add a class to the html tag with the result */
2012-04-29 22:10:36 +04:00
support: {
localStorage: (function(){
var val = !!(localStorage);
$('html').addClass((val ? '' : 'no-') + 'local-storage');
return val;
})(),
history: (function(){
var val = !!(window.history && history.pushState);
$('html').addClass((val ? '' : 'no-') + 'history');
return val;
})(),
fileUpload: (function(){
var w = window;
var val = !!(w.File && w.FileReader && w.FileList && w.Blob);
$('html').addClass((val ? '' : 'no-') + 'file-upload');
return val;
})(),
},
2012-04-29 22:10:36 +04:00
storatePaste: function(url){
if (zerobin.support.localStorage){
var paste = (zerobin.getFormatedDate() + " " +
zerobin.getFormatedTime() + ";" + url);
2012-04-29 22:10:36 +04:00
var keys = zerobin.getKeys();
if(keys.length < 1){keys[0] = 0}
if (localStorage.length > 19) {
void localStorage.removeItem(keys[0]);
}
localStorage.setItem(keys.reverse()[0]+1, paste);
}
},
/** Return a list of the previous paste url with creation date */
getPreviousPastes: function(){
var pastes = [],
keys = zerobin.getKeys(),
date = zerobin.getFormatedDate();
keys.reverse();
for (i=0; i <= keys.length-1; i++) {
var paste = localStorage.getItem(keys[i]).split(';');
var displayDate = paste[0].split(' ')[0];
var prefix = 'the ';
if (displayDate == date){
displayDate = paste[0].split(' ')[1];
prefix = 'at ';
}
pastes.push({displayDate: displayDate, prefix: prefix, link: paste[1]});
}
return pastes
},
/** Return an link object with the URL as href so you can extract host,
protocol, hash, etc*/
parseUrl: function(url){
var a = document.createElement('a');
a.href = url
return a
},
getPasteId: function(url){
loc = url ? zerobin.parseUrl(url) : window.location
return loc.pathname.replace(/[/]|paste/g, '');
2012-04-29 01:51:54 +04:00
},
/** Return the paste content stripted from any code coloration */
getPasteContent: function(){
var copy = '' ;
2012-04-29 01:51:54 +04:00
$("#paste-content li").each(function(index) {
copy = copy + $(this).text() + '\n';
2012-04-29 01:51:54 +04:00
});
return copy;
2012-04-29 22:10:04 +04:00
},
/** Return an approximate estimate the number of bytes in a text */
2012-04-30 00:15:11 +04:00
count: function(text, options) {
2012-04-29 22:10:04 +04:00
// Set option defaults
var crlf = /(\r?\n|\r)/g;
var whitespace = /(\r?\n|\r|\s+)/g;
options = options || {};
2012-04-30 00:15:11 +04:00
options.lineBreaks = options.lineBreaks || 1;
2012-04-29 22:10:04 +04:00
var length = text.length,
nonAscii = length - text.replace(/[\u0100-\uFFFF]/g, '').length,
2012-04-30 00:15:11 +04:00
lineBreaks = length - text.replace(crlf, '').length;
return length + nonAscii + Math.max(0, options.lineBreaks * (lineBreaks - 1));
},
/** Create a message, style it and insert it in the alert box */
2012-04-30 00:15:11 +04:00
message: function(type, message, title, flush, callback) {
2012-05-01 17:39:31 +04:00
$(window).scrollTop(0);
2012-04-30 00:15:11 +04:00
if (flush) {$('.alert-'+type).remove()}
$message = $('#alert-template').clone().attr('id', null)
.addClass('alert alert-' + type);
$('.message', $message).html(message);
if (title) {$('.title', $message).html(title)}
else {$('.title', $message).remove()}
$message.prependTo($('#main')).show('fadeUp', callback);
},
/** Return a progress bar object */
progressBar: function(selector){
var $container = $(selector);
var bar = {container: $container, elem: $container.find('.bar')};
bar.set = function(text, rate){bar.elem.text(text).css('width', rate)};
return bar;
2012-04-26 23:04:36 +04:00
}
};
2012-04-26 13:38:55 +04:00
/***************************
**** On document ready ***
****************************/
2012-04-26 17:26:58 +04:00
$(function(){
2012-04-26 13:38:55 +04:00
2012-04-28 19:11:32 +04:00
/**
On the create paste page:
On click on the send button, compress and encrypt data before
posting it using ajax. Then redirect to the address of the
newly created paste, adding the key in the hash.
*/
$('.btn-primary').on("click", function(e){
2012-04-26 13:38:55 +04:00
e.preventDefault();
var paste = $('textarea').val();
var sizebytes = zerobin.count($('#content').val());
2012-04-29 22:35:28 +04:00
var oversized = sizebytes > zerobin.max_size;
var readableFsize = Math.round(sizebytes / 1024);
var readableMaxsize = Math.round(zerobin.max_size / 1024);
2012-04-29 22:35:28 +04:00
if (oversized){
2012-04-30 00:15:11 +04:00
zerobin.message('error',
('Your file is <strong class="file-size">' + readablFfsize +
2012-04-30 00:15:11 +04:00
'</strong>KB. You have reached the maximum size limit of ' +
readableMaxsize + 'KB.'),
2012-04-30 00:15:11 +04:00
'Warning!', true)
2012-04-29 22:35:28 +04:00
}
if (!oversized && paste.trim()) {
2012-04-30 00:15:11 +04:00
$form = $('input, textarea, select, button').prop('disabled', true);
// set up progress bar
var bar = zerobin.progressBar('form.well .progress');
bar.container.show();
bar.set('Converting paste to bits...', '25%');
/* Encode, compress, encrypt and send the paste then redirect the user
to the new paste. We ensure a loading animation is updated
during the process by passing callbacks.
*/
try {
var expiration = $('#expiration').val();
2012-04-29 22:10:36 +04:00
var key = zerobin.makeKey();
zerobin.encrypt(key, paste,
mkcb(bar.set, 'Encoding to base64...', '45%'),
mkcb(bar.set, 'Compressing...', '65%'),
mkcb(bar.set, 'Encrypting...', '85%'),
/* This block deal with sending the data, redirection or error handling */
function(content){
bar.set('Sending...', '95%');
var data = {content: content, expiration: expiration};
$.post('/paste/create', data)
.error(function(error) {
2012-04-30 00:15:11 +04:00
$form.prop('disabled', false);
bar.container.hide();
2012-04-30 00:15:11 +04:00
zerobin.message(
'error',
'Paste could not be saved. Please try again later.',
'Error'
);
})
.success(function(data) {
bar.set('Redirecting to new paste...', '100%');
if (data['status'] == 'error') {
zerobin.message('error', data['message'], 'Error');
$form.prop('disabled', false);
bar.container.hide();
} else {
var paste_url = '/paste/' + data['paste'] + '#' + key;
zerobin.storatePaste(paste_url);
window.location = (paste_url);
}
});
}
);
} catch (err) {
2012-04-30 00:15:11 +04:00
$form.prop('disabled', false);
bar.container.hide();
2012-04-30 00:15:11 +04:00
zerobin.message('error', 'Paste could not be encrypted. Aborting.',
'Error');
}
2012-04-26 13:38:55 +04:00
}
2012-04-24 22:15:38 +04:00
2012-04-26 13:38:55 +04:00
});
2012-04-24 22:15:38 +04:00
2012-04-30 00:15:11 +04:00
/**
DECRYPTION:
On the display paste page, decrypt and decompress the paste content,
add syntax coloration then setup the copy to clipboard button.
Also calculate and set the paste visual hash.
2012-04-28 19:11:32 +04:00
*/
2012-04-26 17:26:58 +04:00
var content = $('#paste-content').text().trim();
var key = window.location.hash.substring(1);
2012-04-28 21:57:18 +04:00
var error = false;
2012-04-26 17:26:58 +04:00
if (content && key) {
2012-04-28 21:57:18 +04:00
/* Load the lib for visual canvas, create one from the paste id and
insert it */
$.getScript("/static/js/vizhash.min.js").done(function(script, textStatus) {
if (vizhash.supportCanvas) {
var vhash = vizhash.canvasHash(zerobin.getPasteId(), 24, 24);
$('<a class="vhash" href="#"></a>').click(function(e){
e.preventDefault();
if(confirm("This picture is unique to your paste so you can identify" +
" it quickly. \n\n Do you want to know more about this?")){
window.open("http://is.gd/IJaMRG", "_blank");
}
}).prependTo('.lnk-option').append(vhash.canvas);
}
});
$form = $('input, textarea, select, button').prop('disabled', true);
var bar = zerobin.progressBar('.well form .progress');
bar.container.show();
bar.set('Decrypting paste...', '25%');
zerobin.decrypt(key, content,
/* On error*/
function(){
bar.container.hide();
zerobin.message('error', 'Could not decrypt data (Wrong key ?)', 'Error');
},
/* Update progress bar */
mkcb(bar.set, 'Decompressing...', '45%'),
mkcb(bar.set, 'Base64 decoding...', '65%'),
mkcb(bar.set, 'From bits to string...', '85%'),
/* When done */
function(content){
/* Decrypted content goes back to initial container*/
$('#paste-content').text(content);
content = '';
bar.set('Code coloration...', '95%');
/* Add a continuation to let the UI redraw */
setTimeout(function(){
/* Setup flash clipboard button */
2012-04-29 22:10:36 +04:00
ZeroClipboard.setMoviePath('/static/js/ZeroClipboard.swf');
2012-04-28 21:57:18 +04:00
var clip = new ZeroClipboard.Client();
// Callback to reposition the clibpboad flash animation overlay
var reposition = function(){clip.reposition()};
clip.addEventListener('mouseup', function(){
clip.setText(zerobin.getPasteContent());
});
clip.addEventListener('complete',
mkcb(zerobin.message, 'info',
'The paste is now in your clipboard',
'', true, reposition)
);
clip.glue('clip-button');
2012-04-28 23:13:32 +04:00
window.onresize = reposition;
2012-04-28 21:57:18 +04:00
/* Setup link to get the paste short url*/
$('#short-url').click(function(e) {
e.preventDefault();
$('#short-url').text('Loading short url...');
zerobin.getTinyURL(window.location.toString(), function(tinyurl){
clip.setText(tinyurl);
$('#copy-success').hide();
zerobin.message('success',
'<a href="' + tinyurl + '">' + tinyurl + '</a>',
'Short url', true, reposition
)
$('#short-url').text('Get short url');
});
});
/* Remap the message close handler to include the clipboard
flash reposition */
$(".close").off().on('click', function(e){
e.preventDefault();
$(this).parent().fadeOut(reposition);
});
/** Syntaxic coloration */
prettyPrint();
2012-04-28 21:57:18 +04:00
/* Class to switch to paste content style with coloration done */
$('#paste-content').addClass('done');
2012-05-06 15:54:57 +04:00
/* Display result */
bar.set('Done', '100%');
bar.container.hide();
$form.prop('disabled', false);
}, 250);
2012-04-28 21:57:18 +04:00
}
);
} /* End of "DECRYPTION" */
2012-04-28 21:57:18 +04:00
2012-04-28 19:11:32 +04:00
/* Synchronize expiration select boxes value */
$('.paste-option select').on('change', function(){
var $this = $(this);
$this.val($this.val());
});
2012-04-28 19:11:32 +04:00
/* Resize Textarea according to content */
$('#content').elastic();
2012-04-28 15:25:43 +04:00
/* Display bottom paste option buttons when needed */
$('#content').on('keyup change', function(){
2012-04-28 19:11:32 +04:00
if($('#content').height() < 400 ){
$('.paste-option.down').remove();
}
else {
if ($('.paste-option').length == 1) {
$('.paste-option').clone().addClass('down').appendTo('form.well');
}
}
2012-04-30 00:15:11 +04:00
});
/* Display previous pastes */
if (zerobin.support.localStorage){
2012-04-28 21:58:30 +04:00
var $container = $('.previous-pastes'),
pastes = zerobin.getPreviousPastes();
2012-04-28 21:55:00 +04:00
if (pastes.length){
2012-04-28 21:55:00 +04:00
$.getScript("/static/js/vizhash.min.js").done(function(script, textStatus) {
2012-04-29 19:36:26 +04:00
$container.find('.item').remove();
$.each(zerobin.getPreviousPastes(), function(i, paste){
2012-04-30 00:15:11 +04:00
var $li = $('<li class="item"></li>').appendTo($container);
var $link = $('<a></a>').attr('href', paste.link)
.text(paste.prefix + paste.displayDate)
.appendTo($li);
2012-04-29 19:36:26 +04:00
if (vizhash.supportCanvas) {
var pasteId = zerobin.getPasteId(paste.link);
var vhash = vizhash.canvasHash(pasteId, 24, 24).canvas;
$link.prepend($(vhash).addClass('vhash'));
}
2012-04-29 19:36:26 +04:00
// hightlite the current link and make sure clicking the link
// does redirect to the page
if (paste.link.replace(/#[^#]+/, '') == window.location.pathname){
$li.addClass('active');
$link.click(function(){window.location.reload()});
}
});
});
}
2012-04-29 19:36:26 +04:00
}
2012-04-28 21:58:30 +04:00
/* Event handler for "clone paste" button */
2012-04-28 21:55:00 +04:00
$('.btn-clone').click(function(e){
e.preventDefault();
$('.submit-form').show();
$('.paste-form').hide();
$('#content').val(zerobin.getPasteContent()).trigger('change');
});
2012-04-28 21:55:00 +04:00
$('.clone .btn-danger').click(function(e){
e.preventDefault();
$('.submit-form').hide();
$('.paste-form').show();
2012-04-28 21:55:00 +04:00
});
2012-04-29 19:36:26 +04:00
/* Upload file using HTML5 File API */
if (zerobin.support.fileUpload) {
var upload = function(files) {
var reader = new FileReader();
reader.onload = function(event) {
$('#content').val(event.target.result).trigger('change');
};
reader.readAsText(files[0]);
}
2012-04-30 00:15:11 +04:00
var $buttonOverlay = $('#file-upload');
var $button = $('.btn-upload');
2012-04-29 19:36:26 +04:00
try {
$button.val('Uploading...');
$button.prop('disabled', true);
$buttonOverlay.change(function(){upload(this.files)});
}
catch (e) {
zerobin.message('error', 'Could no upload the file', 'Error');
$button.val('Upload File');
$button.prop('disabled', false);
}
2012-04-29 19:36:26 +04:00
$button.prop('disabled', false);
$button.val('Upload File');
$buttonOverlay.mouseover(mkcb($(this).css, 'cursor', 'pointer'));
2012-04-29 19:36:26 +04:00
}
2012-04-29 22:10:04 +04:00
/* Alerts */
$(".close").on('click', function(e){
e.preventDefault();
2012-04-29 22:10:04 +04:00
$(this).parent().fadeOut();
2012-04-28 21:55:00 +04:00
});
}); /* End of "document ready" jquery callback */