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

jQuery fully removed, clipboard fixed

This commit is contained in:
ksamuel 2020-08-11 11:55:29 +02:00
parent 722fb45b51
commit e13cead89b
15 changed files with 13219 additions and 1749 deletions

View File

@ -10,9 +10,7 @@
be pasted in it. The idea is that one can (probably...) not be legally entitled be pasted in it. The idea is that one can (probably...) not be legally entitled
to `moderate the pastebin content`_ as they have no way to decrypt it. to `moderate the pastebin content`_ as they have no way to decrypt it.
It's an Python implementation of the It's an Python implementation of the `zerobin project`_, created by sebsauvage, under the `WTF licence`_.
`zerobin project`_ under the `WTF licence`_. It's easy to
install even if you know nothing about Python.
For now tested with IE9, and the last opera, safari, chrome and FF. For now tested with IE9, and the last opera, safari, chrome and FF.
@ -22,7 +20,7 @@ but in short::
pip install zerobin pip install zerobin
zerobin zerobin
0bin runs on Python 2.7 and Python 3.4. 0bin runs on Python 3.7.
How it works How it works
============= =============
@ -74,7 +72,6 @@ Technologies used
- Cherrypy_ (server only) - Cherrypy_ (server only)
- `node.js`_ (for optional command-line tool only) - `node.js`_ (for optional command-line tool only)
Known issues Known issues
============ ============

View File

@ -31,6 +31,7 @@ def runserver(
server="cherrypy", server="cherrypy",
): ):
debug = True
if version: if version:
print("0bin V%s" % settings.VERSION) print("0bin V%s" % settings.VERSION)
sys.exit(0) sys.exit(0)

View File

@ -2,6 +2,7 @@
# coding: utf-8 # coding: utf-8
from __future__ import unicode_literals, absolute_import, print_function from __future__ import unicode_literals, absolute_import, print_function
import pdb
""" """
Script including controller, rooting, and dependency management. Script including controller, rooting, and dependency management.
@ -24,59 +25,63 @@ from datetime import datetime, timedelta
# add project dir and libs dir to the PYTHON PATH to ensure they are # add project dir and libs dir to the PYTHON PATH to ensure they are
# importable # importable
from zerobin.utils import (settings, SettingsValidationError, from zerobin.utils import settings, SettingsValidationError, drop_privileges, dmerge
drop_privileges, dmerge)
import bottle import bottle
from bottle import (Bottle, run, static_file, view, request) from bottle import Bottle, run, static_file, view, request
from zerobin.paste import Paste from zerobin.paste import Paste
app = Bottle() app = Bottle()
GLOBAL_CONTEXT = { GLOBAL_CONTEXT = {
'settings': settings, "settings": settings,
'pastes_count': Paste.get_pastes_count(), "pastes_count": Paste.get_pastes_count(),
'refresh_counter': datetime.now() "refresh_counter": datetime.now(),
} }
@app.route('/') @app.route("/")
@view('home') @view("home")
def index(): def index():
return GLOBAL_CONTEXT return GLOBAL_CONTEXT
@app.route('/faq/') @app.route("/faq/")
@view('faq') @view("faq")
def faq(): def faq():
return GLOBAL_CONTEXT return GLOBAL_CONTEXT
@app.route('/paste/create', method='POST') @app.route("/paste/create", method="POST")
def create_paste(): def create_paste():
try: try:
body = urlparse.parse_qs(request.body.read(int(settings.MAX_SIZE * 1.1))) body = urlparse.parse_qs(request.body.read(int(settings.MAX_SIZE * 1.1)))
except ValueError: except ValueError:
return {'status': 'error', 'message': "Wrong data payload."} return {"status": "error", "message": "Wrong data payload."}
try: try:
content = "".join(x.decode("utf8") for x in body[b"content"])
content = "".join(x.decode('utf8') for x in body[b'content'])
except (UnicodeDecodeError, KeyError): except (UnicodeDecodeError, KeyError):
return {'status': 'error', return {
'message': "Encoding error: the paste couldn't be saved."} "status": "error",
"message": "Encoding error: the paste couldn't be saved.",
}
if '{"iv":' not in content: # reject silently non encrypted content if '{"iv":' not in content: # reject silently non encrypted content
return {'status': 'error', 'message': "Wrong data payload."} return {"status": "error", "message": "Wrong data payload."}
# check size of the paste. if more than settings return error # check size of the paste. if more than settings return error
# without saving paste. prevent from unusual use of the # without saving paste. prevent from unusual use of the
# system. need to be improved # system. need to be improved
if 0 < len(content) < settings.MAX_SIZE: if 0 < len(content) < settings.MAX_SIZE:
expiration = body.get(b'expiration', ['burn_after_reading'])[0] expiration = body.get(b"expiration", [b"burn_after_reading"])[0]
paste = Paste(expiration=expiration.decode('utf8'), content=content, paste = Paste(
uuid_length=settings.PASTE_ID_LENGTH) expiration=expiration.decode("utf8"),
content=content,
uuid_length=settings.PASTE_ID_LENGTH,
)
paste.save() paste.save()
# display counter # display counter
@ -87,21 +92,23 @@ def create_paste():
# if refresh time elapsed pick up new counter value # if refresh time elapsed pick up new counter value
now = datetime.now() now = datetime.now()
timeout = (GLOBAL_CONTEXT['refresh_counter'] timeout = GLOBAL_CONTEXT["refresh_counter"] + timedelta(
+ timedelta(seconds=settings.REFRESH_COUNTER)) seconds=settings.REFRESH_COUNTER
)
if timeout < now: if timeout < now:
GLOBAL_CONTEXT['pastes_count'] = Paste.get_pastes_count() GLOBAL_CONTEXT["pastes_count"] = Paste.get_pastes_count()
GLOBAL_CONTEXT['refresh_counter'] = now GLOBAL_CONTEXT["refresh_counter"] = now
return {'status': 'ok', 'paste': paste.uuid} return {"status": "ok", "paste": paste.uuid}
return {'status': 'error', return {
'message': "Serveur error: the paste couldn't be saved. " "status": "error",
"Please try later."} "message": "Serveur error: the paste couldn't be saved. " "Please try later.",
}
@app.route('/paste/:paste_id') @app.route("/paste/:paste_id")
@view('paste') @view("paste")
def display_paste(paste_id): def display_paste(paste_id):
now = datetime.now() now = datetime.now()
@ -115,9 +122,8 @@ def display_paste(paste_id):
# we don't delete the paste because it means it's the redirection # we don't delete the paste because it means it's the redirection
# to the paste that happens during the paste creation # to the paste that happens during the paste creation
try: try:
keep_alive = paste.expiration.split('#')[1] keep_alive = paste.expiration.split("#")[1]
keep_alive = datetime.strptime(keep_alive, keep_alive = datetime.strptime(keep_alive, "%Y-%m-%d %H:%M:%S.%f")
'%Y-%m-%d %H:%M:%S.%f')
keep_alive = now < keep_alive + timedelta(seconds=10) keep_alive = now < keep_alive + timedelta(seconds=10)
except IndexError: except IndexError:
keep_alive = False keep_alive = False
@ -131,35 +137,34 @@ def display_paste(paste_id):
except (TypeError, ValueError): except (TypeError, ValueError):
return error404(ValueError) return error404(ValueError)
context = {'paste': paste, 'keep_alive': keep_alive} context = {"paste": paste, "keep_alive": keep_alive}
return dmerge(context, GLOBAL_CONTEXT) return dmerge(context, GLOBAL_CONTEXT)
@app.error(404) @app.error(404)
@view('404') @view("404")
def error404(code): def error404(code):
return GLOBAL_CONTEXT return GLOBAL_CONTEXT
@app.route('/static/<filename:path>') @app.route("/static/<filename:path>")
def server_static(filename): def server_static(filename):
return static_file(filename, root=settings.STATIC_FILES_ROOT) return static_file(filename, root=settings.STATIC_FILES_ROOT)
def get_app(debug=None, settings_file='', def get_app(debug=None, settings_file="", compressed_static=None, settings=settings):
compressed_static=None, settings=settings):
""" """
Return a tuple (settings, app) configured using passed Return a tuple (settings, app) configured using passed
parameters and/or a setting file. parameters and/or a setting file.
""" """
settings_file = settings_file or os.environ.get('ZEROBIN_SETTINGS_FILE') settings_file = settings_file or os.environ.get("ZEROBIN_SETTINGS_FILE")
if settings_file: if settings_file:
settings.update_with_file(os.path.realpath(settings_file)) settings.update_with_file(os.path.realpath(settings_file))
if settings.PASTE_ID_LENGTH < 4: if settings.PASTE_ID_LENGTH < 4:
raise SettingsValidationError('PASTE_ID_LENGTH cannot be lower than 4') raise SettingsValidationError("PASTE_ID_LENGTH cannot be lower than 4")
if compressed_static is not None: if compressed_static is not None:
settings.COMPRESSED_STATIC_FILES = compressed_static settings.COMPRESSED_STATIC_FILES = compressed_static

View File

@ -1,5 +1,14 @@
/* logo */ [v-cloak] {
display: none
}
.noscript {
position: relative;
top: 25%;
border: 25px solid red;
}
/* logo */
.brand { .brand {
font-size: 38px !important; font-size: 38px !important;
@ -46,6 +55,7 @@ body {
padding-top: 60px; padding-top: 60px;
padding-bottom: 40px; padding-bottom: 40px;
} }
.sidebar-nav { .sidebar-nav {
padding: 9px 0; padding: 9px 0;
} }
@ -62,7 +72,8 @@ label {
color: #888; color: #888;
} }
ul, ol { ul,
ol {
padding: 0; padding: 0;
margin: 0; margin: 0;
} }
@ -71,7 +82,6 @@ li {
margin-left: -9px; margin-left: -9px;
} }
p { p {
margin: 0 0 20px; margin: 0 0 20px;
} }
@ -152,7 +162,6 @@ html.no-file-upload p.file-upload {
display: none; display: none;
} }
input.btn-upload { input.btn-upload {
position: relative; position: relative;
left: -6px; left: -6px;
@ -175,7 +184,6 @@ input.hide-upload {
height: 49px; height: 49px;
} }
/* Paste Page */ /* Paste Page */
#paste-content { #paste-content {
@ -183,7 +191,6 @@ input.hide-upload {
padding: 1em; padding: 1em;
} }
#paste-content.linenums { #paste-content.linenums {
padding-left: 0; padding-left: 0;
} }
@ -201,9 +208,16 @@ a#clip-button.hover{
text-decoration: underline; text-decoration: underline;
} }
li.L0, li.L1, li.L2, li.L3, li.L4, li.L0,
li.L5, li.L6, li.L7, li.L8, li.L9 li.L1,
{ li.L2,
li.L3,
li.L4,
li.L5,
li.L6,
li.L7,
li.L8,
li.L9 {
list-style-type: decimal; list-style-type: decimal;
background: inherit; background: inherit;
} }
@ -216,8 +230,10 @@ li.L5, li.L6, li.L7, li.L8, li.L9
/* Specify class=linenums on a pre to get line numbering */ /* Specify class=linenums on a pre to get line numbering */
ol.linenums { ol.linenums {
margin: 0 0 0 55px; /* IE indents via margin-left */ margin: 0 0 0 55px;
/* IE indents via margin-left */
} }
ol.linenums li { ol.linenums li {
color: #bebec5; color: #bebec5;
line-height: 18px; line-height: 18px;
@ -241,7 +257,9 @@ pre {
color: #66F; color: #66F;
} }
.pun, .opn, .clo { .pun,
.opn,
.clo {
color: #0A0; color: #0A0;
} }
@ -253,7 +271,6 @@ color: #933;
color: #C0C; color: #C0C;
} }
/* Common css */ /* Common css */
form { form {
@ -266,7 +283,8 @@ form textarea {
min-height: 250px; min-height: 250px;
} }
button.btn, input[type="submit"].btn { button.btn,
input[type="submit"].btn {
margin-left: 5px; margin-left: 5px;
} }
@ -286,19 +304,16 @@ button.btn, input[type="submit"].btn {
margin-left: 5px; margin-left: 5px;
} }
.btn-primary, .btn-danger { .btn-primary,
.btn-danger {
position: relative; position: relative;
top: -4px; top: -4px;
} }
#alert-template {
display: none;
}
/** Progress bar */ /** Progress bar */
.progress { .progress {
display:none; width: 100%;
} }
.progress .bar { .progress .bar {
@ -312,7 +327,6 @@ button.btn, input[type="submit"].btn {
margin-right: 10px; margin-right: 10px;
} }
/* Previous paste list */ /* Previous paste list */
.previous-pastes .item { .previous-pastes .item {
@ -345,10 +359,6 @@ canvas {
border: 1px solid white; border: 1px solid white;
} }
#wrap-content {
display: none;
}
.noscript { .noscript {
text-align: center; text-align: center;
color: red; color: red;

View File

@ -1,311 +0,0 @@
// Simple Set Clipboard System
// Author: Joseph Huckaby
var ZeroClipboard = {
version: "1.0.7",
clients: {}, // registered upload clients on page, indexed by id
moviePath: 'ZeroClipboard.swf', // URL to movie
nextId: 1, // ID of next movie
$: function(thingy) {
// simple DOM lookup utility function
if (typeof(thingy) == 'string') thingy = document.getElementById(thingy);
if (!thingy.addClass) {
// extend element with a few useful methods
thingy.hide = function() { this.style.display = 'none'; };
thingy.show = function() { this.style.display = ''; };
thingy.addClass = function(name) { this.removeClass(name); this.className += ' ' + name; };
thingy.removeClass = function(name) {
var classes = this.className.split(/\s+/);
var idx = -1;
for (var k = 0; k < classes.length; k++) {
if (classes[k] == name) { idx = k; k = classes.length; }
}
if (idx > -1) {
classes.splice( idx, 1 );
this.className = classes.join(' ');
}
return this;
};
thingy.hasClass = function(name) {
return !!this.className.match( new RegExp("\\s*" + name + "\\s*") );
};
}
return thingy;
},
setMoviePath: function(path) {
// set path to ZeroClipboard.swf
this.moviePath = path;
},
dispatch: function(id, eventName, args) {
// receive event from flash movie, send to client
var client = this.clients[id];
if (client) {
client.receiveEvent(eventName, args);
}
},
register: function(id, client) {
// register new client to receive events
this.clients[id] = client;
},
getDOMObjectPosition: function(obj, stopObj) {
// get absolute coordinates for dom element
var info = {
left: 0,
top: 0,
width: obj.width ? obj.width : obj.offsetWidth,
height: obj.height ? obj.height : obj.offsetHeight
};
while (obj && (obj != stopObj)) {
info.left += obj.offsetLeft;
info.top += obj.offsetTop;
obj = obj.offsetParent;
}
return info;
},
Client: function(elem) {
// constructor for new simple upload client
this.handlers = {};
// unique ID
this.id = ZeroClipboard.nextId++;
this.movieId = 'ZeroClipboardMovie_' + this.id;
// register client with singleton to receive flash events
ZeroClipboard.register(this.id, this);
// create movie
if (elem) this.glue(elem);
}
};
ZeroClipboard.Client.prototype = {
id: 0, // unique ID for us
ready: false, // whether movie is ready to receive events or not
movie: null, // reference to movie object
clipText: '', // text to copy to clipboard
handCursorEnabled: true, // whether to show hand cursor, or default pointer cursor
cssEffects: true, // enable CSS mouse effects on dom container
handlers: null, // user event handlers
glue: function(elem, appendElem, stylesToAdd) {
// glue to DOM element
// elem can be ID or actual DOM element object
this.domElement = ZeroClipboard.$(elem);
// float just above object, or zIndex 99 if dom element isn't set
var zIndex = 99;
if (this.domElement.style.zIndex) {
zIndex = parseInt(this.domElement.style.zIndex, 10) + 1;
}
if (typeof(appendElem) == 'string') {
appendElem = ZeroClipboard.$(appendElem);
}
else if (typeof(appendElem) == 'undefined') {
appendElem = document.getElementsByTagName('body')[0];
}
// find X/Y position of domElement
var box = ZeroClipboard.getDOMObjectPosition(this.domElement, appendElem);
// create floating DIV above element
this.div = document.createElement('div');
var style = this.div.style;
style.position = 'absolute';
style.left = '' + box.left + 'px';
style.top = '' + box.top + 'px';
style.width = '' + box.width + 'px';
style.height = '' + box.height + 'px';
style.zIndex = zIndex;
if (typeof(stylesToAdd) == 'object') {
for (addedStyle in stylesToAdd) {
style[addedStyle] = stylesToAdd[addedStyle];
}
}
//style.backgroundColor = '#f00'; // debug
appendElem.appendChild(this.div);
this.div.innerHTML = this.getHTML( box.width, box.height );
},
getHTML: function(width, height) {
// return HTML for movie
var html = '';
var flashvars = 'id=' + this.id +
'&width=' + width +
'&height=' + height;
if (navigator.userAgent.match(/MSIE/)) {
// IE gets an OBJECT tag
var protocol = location.href.match(/^https/i) ? 'https://' : 'http://';
html += '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="'+protocol+'download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="'+width+'" height="'+height+'" id="'+this.movieId+'" align="middle"><param name="allowScriptAccess" value="always" /><param name="allowFullScreen" value="false" /><param name="movie" value="'+ZeroClipboard.moviePath+'" /><param name="loop" value="false" /><param name="menu" value="false" /><param name="quality" value="best" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="'+flashvars+'"/><param name="wmode" value="transparent"/></object>';
}
else {
// all other browsers get an EMBED tag
html += '<embed id="'+this.movieId+'" src="'+ZeroClipboard.moviePath+'" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="'+width+'" height="'+height+'" name="'+this.movieId+'" align="middle" allowScriptAccess="always" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="'+flashvars+'" wmode="transparent" />';
}
return html;
},
hide: function() {
// temporarily hide floater offscreen
if (this.div) {
this.div.style.left = '-2000px';
}
},
show: function() {
// show ourselves after a call to hide()
this.reposition();
},
destroy: function() {
// destroy control and floater
if (this.domElement && this.div) {
this.hide();
this.div.innerHTML = '';
var body = document.getElementsByTagName('body')[0];
try { body.removeChild( this.div ); } catch(e) {;}
this.domElement = null;
this.div = null;
}
},
reposition: function(elem) {
// reposition our floating div, optionally to new container
// warning: container CANNOT change size, only position
if (elem) {
this.domElement = ZeroClipboard.$(elem);
if (!this.domElement) this.hide();
}
if (this.domElement && this.div) {
var box = ZeroClipboard.getDOMObjectPosition(this.domElement);
var style = this.div.style;
style.left = '' + box.left + 'px';
style.top = '' + box.top + 'px';
}
},
setText: function(newText) {
// set text to be copied to clipboard
this.clipText = newText;
if (this.ready) this.movie.setText(newText);
},
addEventListener: function(eventName, func) {
// add user event listener for event
// event types: load, queueStart, fileStart, fileComplete, queueComplete, progress, error, cancel
eventName = eventName.toString().toLowerCase().replace(/^on/, '');
if (!this.handlers[eventName]) this.handlers[eventName] = [];
this.handlers[eventName].push(func);
},
setHandCursor: function(enabled) {
// enable hand cursor (true), or default arrow cursor (false)
this.handCursorEnabled = enabled;
if (this.ready) this.movie.setHandCursor(enabled);
},
setCSSEffects: function(enabled) {
// enable or disable CSS effects on DOM container
this.cssEffects = !!enabled;
},
receiveEvent: function(eventName, args) {
// receive event from flash
eventName = eventName.toString().toLowerCase().replace(/^on/, '');
// special behavior for certain events
switch (eventName) {
case 'load':
// movie claims it is ready, but in IE this isn't always the case...
// bug fix: Cannot extend EMBED DOM elements in Firefox, must use traditional function
this.movie = document.getElementById(this.movieId);
if (!this.movie) {
var self = this;
setTimeout( function() { self.receiveEvent('load', null); }, 1 );
return;
}
// firefox on pc needs a "kick" in order to set these in certain cases
if (!this.ready && navigator.userAgent.match(/Firefox/) && navigator.userAgent.match(/Windows/)) {
var self = this;
setTimeout( function() { self.receiveEvent('load', null); }, 100 );
this.ready = true;
return;
}
this.ready = true;
this.movie.setText( this.clipText );
this.movie.setHandCursor( this.handCursorEnabled );
break;
case 'mouseover':
if (this.domElement && this.cssEffects) {
this.domElement.addClass('hover');
if (this.recoverActive) this.domElement.addClass('active');
}
break;
case 'mouseout':
if (this.domElement && this.cssEffects) {
this.recoverActive = false;
if (this.domElement.hasClass('active')) {
this.domElement.removeClass('active');
this.recoverActive = true;
}
this.domElement.removeClass('hover');
}
break;
case 'mousedown':
if (this.domElement && this.cssEffects) {
this.domElement.addClass('active');
}
break;
case 'mouseup':
if (this.domElement && this.cssEffects) {
this.domElement.removeClass('active');
this.recoverActive = false;
}
break;
} // switch eventName
if (this.handlers[eventName]) {
for (var idx = 0, len = this.handlers[eventName].length; idx < len; idx++) {
var func = this.handlers[eventName][idx];
if (typeof(func) == 'function') {
// actual function reference
func(this, args);
}
else if ((typeof(func) == 'object') && (func.length == 2)) {
// PHP style object + method, i.e. [myObject, 'myMethod']
func[0][ func[1] ](this, args);
}
else if (typeof(func) == 'string') {
// name of function
window[func](this, args);
}
} // foreach event handler defined
} // user defined handler for event
}
};

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,162 +0,0 @@
/**
* @name Elastic
* @descripton Elastic is jQuery plugin that grow and shrink your textareas automatically
* @version 1.6.11
* @requires jQuery 1.2.6+
*
* @author Jan Jarfalk
* @author-email jan.jarfalk@unwrongest.com
* @author-website http://www.unwrongest.com
*
* @licence MIT License - http://www.opensource.org/licenses/mit-license.php
*/
(function($){
jQuery.fn.extend({
elastic: function() {
// We will create a div clone of the textarea
// by copying these attributes from the textarea to the div.
var mimics = [
'paddingTop',
'paddingRight',
'paddingBottom',
'paddingLeft',
'fontSize',
'lineHeight',
'fontFamily',
'width',
'fontWeight',
'border-top-width',
'border-right-width',
'border-bottom-width',
'border-left-width',
'borderTopStyle',
'borderTopColor',
'borderRightStyle',
'borderRightColor',
'borderBottomStyle',
'borderBottomColor',
'borderLeftStyle',
'borderLeftColor'
];
return this.each( function() {
// Elastic only works on textareas
if ( this.type !== 'textarea' ) {
return false;
}
var $textarea = jQuery(this),
$twin = jQuery('<div />').css({
'position' : 'absolute',
'display' : 'none',
'word-wrap' : 'break-word',
'white-space' :'pre-wrap'
}),
lineHeight = parseInt($textarea.css('line-height'),10) || parseInt($textarea.css('font-size'),'10'),
minheight = parseInt($textarea.css('height'),10) || lineHeight*3,
maxheight = parseInt($textarea.css('max-height'),10) || Number.MAX_VALUE,
goalheight = 0;
// Opera returns max-height of -1 if not set
if (maxheight < 0) { maxheight = Number.MAX_VALUE; }
// Append the twin to the DOM
// We are going to meassure the height of this, not the textarea.
$twin.appendTo($textarea.parent());
// Copy the essential styles (mimics) from the textarea to the twin
var i = mimics.length;
while(i--){
$twin.css(mimics[i].toString(),$textarea.css(mimics[i].toString()));
}
// Updates the width of the twin. (solution for textareas with widths in percent)
function setTwinWidth(){
var curatedWidth = Math.floor(parseInt($textarea.width(),10));
if($twin.width() !== curatedWidth){
$twin.css({'width': curatedWidth + 'px'});
// Update height of textarea
update(true);
}
}
// Sets a given height and overflow state on the textarea
function setHeightAndOverflow(height, overflow){
var curratedHeight = Math.floor(parseInt(height,10));
if($textarea.height() !== curratedHeight){
$textarea.css({'height': curratedHeight + 'px','overflow':overflow});
}
}
// This function will update the height of the textarea if necessary
function update(forced) {
// Get curated content from the textarea.
var textareaContent = $textarea.val().replace(/&/g,'&amp;').replace(/ {2}/g, '&nbsp;').replace(/<|>/g, '&gt;').replace(/\n/g, '<br />');
// Compare curated content with curated twin.
var twinContent = $twin.html().replace(/<br>/ig,'<br />');
if(forced || textareaContent+'&nbsp;' !== twinContent){
// Add an extra white space so new rows are added when you are at the end of a row.
$twin.html(textareaContent+'&nbsp;');
// Change textarea height if twin plus the height of one line differs more than 3 pixel from textarea height
if(Math.abs($twin.height() + lineHeight - $textarea.height()) > 3){
var goalheight = $twin.height()+lineHeight;
if(goalheight >= maxheight) {
setHeightAndOverflow(maxheight,'auto');
} else if(goalheight <= minheight) {
setHeightAndOverflow(minheight,'hidden');
} else {
setHeightAndOverflow(goalheight,'hidden');
}
}
}
}
// Hide scrollbars
$textarea.css({'overflow':'hidden'});
// Update textarea size on keyup, change, cut and paste
$textarea.bind('keyup change cut paste', function(){
update();
});
// Update width of twin if browser or textarea is resized (solution for textareas with widths in percent)
$(window).bind('resize', setTwinWidth);
$textarea.bind('resize', setTwinWidth);
$textarea.bind('update', update);
// Compact textarea on blur
$textarea.bind('blur',function(){
if($twin.height() < maxheight){
if($twin.height() > minheight) {
$textarea.height($twin.height());
} else {
$textarea.height(minheight);
}
}
});
// And this line is to catch the browser paste event
$textarea.bind('input paste',function(e){ setTimeout( update, 250); });
// Run update once when elastic is initialized
update();
});
}
});
})(jQuery);

File diff suppressed because one or more lines are too long

11965
zerobin/static/js/vue.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
</script> </script>
<p class="alert alert-error"> <p class="alert alert-error">
<a class="close" data-dismiss="alert" href="#">×</a> <a class="close" data-dismiss="alert" href="#" @click.prevent="$event.target.parentNode.remove()">×</a>
<strong class="title">404 Error!</strong> <strong class="title">404 Error!</strong>
<span class="message"> <span class="message">
Either this paste has expired or this page never existed. Either this paste has expired or this page never existed.
@ -11,28 +11,38 @@
</p> </p>
<p class="file-upload"> <p class="file-upload">
<input type="button" class="btn btn-upload" value="Upload File"> <input type="button" class="btn btn-upload" :value="isUploading ? 'Uploading...': 'Upload file'"
<input type="file" class="hide-upload" id="file-upload" > :disabled="isUploading">
<input type="file" class="hide-upload" id="file-upload" @change="handleUpload($event.target.files)">
</p> </p>
<form class="well" method="post" action="/paste/create"> <form class="well" method="post" action="/paste/create">
<p class="paste-option"> <p class="paste-option">
<label for="expiration">Expiration:</label> <label for="expiration">Expiration:</label>
<select id="expiration" name="expiration"> <select id="expiration" name="expiration" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option> <option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">1 day</option> <option selected value="1_day">1 day</option>
<option value="1_month">1 month</option> <option value="1_month">1 month</option>
<option value="never">Never</option> <option value="never">Never</option>
</select> </select>
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary" @click="encryptAndSendPaste($event)">Submit</button>
</p> </p>
<p> <p>
<div class="progress progress-striped active"> <div class="progress progress-striped active" v-show="isLoading">
<div class="bar"></div> <div class="bar"></div>
</div> </div>
<textarea rows="10" style="width:100%;" <textarea rows="10" style="width:100%;" class="input-xlarge" id="content" name="content" autofocus
class="input-xlarge" v-on:keydown.ctrl.enter="encryptAndSendPaste($event)"></textarea>
id="content" name="content"></textarea> </p>
<p class="paste-option down" v-if="displayBottomToolBar">
<label for="expiration">Expiration:</label>
<select id="expiration" name="expiration" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">1 day</option>
<option value="1_month">1 month</option>
<option value="never">Never</option>
</select>
<button type="submit" class="btn btn-primary" @click="encryptAndSendPaste($event)">Submit</button>
</p> </p>
</form> </form>

View File

@ -1,24 +1,22 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>0bin - encrypted pastebin</title> <title>0bin - encrypted pastebin</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" <meta name="description" content="0bin is a client-side-encrypted
content="0bin is a client-side-encrypted
pastebin featuring burn after reading, history, and pastebin featuring burn after reading, history, and
a clipboard"> a clipboard">
<link rel="shortcut icon" href="/favicon.ico"> <link rel="shortcut icon" href="/favicon.ico">
%if settings.COMPRESSED_STATIC_FILES: %if settings.COMPRESSED_STATIC_FILES:
<link href="/static/css/style.min.css?{{ settings.VERSION }}" <link href="/static/css/style.min.css?{{ settings.VERSION }}" rel="stylesheet" />
rel="stylesheet" />
%else: %else:
<link href="/static/css/prettify.css" rel="stylesheet" /> <link href="/static/css/prettify.css" rel="stylesheet" />
<link href="/static/css/bootstrap.css" rel="stylesheet"> <link href="/static/css/bootstrap.css" rel="stylesheet">
<link href="/static/css/style.css?{{ settings.VERSION }}" <link href="/static/css/style.css?{{ settings.VERSION }}" rel="stylesheet">
rel="stylesheet">
%end %end
<!-- Le HTML5 shim, for IE7-8 support of HTML5 elements --> <!-- Le HTML5 shim, for IE7-8 support of HTML5 elements -->
@ -26,27 +24,14 @@
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]--> <![endif]-->
%if settings.COMPRESSED_STATIC_FILES:
<script src="/static/js/main.min.js?{{ settings.VERSION }}"></script>
%else:
<script src="/static/js/jquery-1.7.2.min.js"></script>
<script src="/static/js/sjcl.js"></script>
<script src="/static/js/behavior.js?{{ settings.VERSION }}"></script>
%end
<script type="text/javascript">
zerobin.max_size = {{ settings.MAX_SIZE }};
</script>
</head> </head>
<body> <body>
<div class="navbar navbar-fixed-top"> <div class="navbar navbar-fixed-top" id="menu-top">
<div class="navbar-inner"> <div class="navbar-inner">
<div class="container"> <div class="container">
<a class="btn btn-navbar" data-toggle="collapse" <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
data-target=".nav-collapse">
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
@ -56,16 +41,11 @@
<ul class="nav"> <ul class="nav">
%for i, entry in enumerate(settings.MENU): %for i, entry in enumerate(settings.MENU):
<li <li>
%if not i:
class="active"
%end
>
%if "mailto:" in entry[1]: %if "mailto:" in entry[1]:
<span title="{{ entry[1].replace('mailto:', '').replace('@', '__AT__') }}" <a :href="formatEmail('{{ entry[1].replace('mailto:', '').replace('@', '__AT__') }}')" class="email-link">
class="email-link" >
{{ entry[0] }} {{ entry[0] }}
</span> </a>
%else: %else:
<a href="{{ entry[1] }}">{{ entry[0] }}</a> <a href="{{ entry[1] }}">{{ entry[0] }}</a>
%end %end
@ -78,24 +58,30 @@
"A client side encrypted PasteBin"<br> "A client side encrypted PasteBin"<br>
<span>All pastes are AES256 encrypted, we cannot know what you paste...</span> <span>All pastes are AES256 encrypted, we cannot know what you paste...</span>
</p> </p>
</div><!--/.nav-collapse --> </div>
<!--/.nav-collapse -->
</div> </div>
</div> </div>
</div> </div>
<noscript class="container noscript"> <noscript class="noscript">
<p>This pastebin uses client-side encryption. Therefore, it needs JavaScript enabled.</p>
<p>It seems like your browser doesn't have JavaScript enable.</p> <div class="container jumbotron">
<h1 class="display-4">This site requires Javascript</h1>
<p class="lead">This pastebin uses client-side encryption, and therefore, it needs JavaScript to work.</p>
<p>It seems like your browser doesn't have JavaScript enabled.</p>
<p>Please enable JavaScript for this website or use a JavaScript-capable web browser.</p> <p>Please enable JavaScript for this website or use a JavaScript-capable web browser.</p>
</div>
</noscript> </noscript>
<div class="container" id="wrap-content"> <div class="container app" id="wrap-content" v-cloak>
<div class="row"> <div class="row">
<div class="span2"> <div class="span2">
<div class="well sidebar-nav"> <div class="well sidebar-nav">
<ul class="nav nav-list previous-pastes"> <ul class="nav nav-list previous-pastes">
<li class="nav-header">Previous pastes</li> <li class="nav-header">Previous pastes</li>
<li class="item local-storage"> <li class="item local-storage" v-if="previousPastes.length === 0">
<em class="grey"> <em class="grey">
Your previous pastes will be saved in your browser using Your previous pastes will be saved in your browser using
<a href="http://www.w3.org/TR/webstorage/">localStorage</a>. <a href="http://www.w3.org/TR/webstorage/">localStorage</a>.
@ -108,22 +94,43 @@
We cannot display your previous pastes. We cannot display your previous pastes.
</em> </em>
</li> </li>
<li :class="{item: true, active: paste.isCurrent}" v-for="paste in previousPastes">
<a :href="paste.link" @click="forceLoadPaste(paste.link)">
{% paste.prefix %}{% paste.displayDate %}
</a>
</li>
</ul> </ul>
</div><!--/.well --> </div>
</div><!--/span--> <!--/.well -->
</div>
<!--/span-->
<div id='main' class="span10"> <div id='main' class="span10">
<p :class="'alert alert-' + msg.type" v-for="msg in messages">
<a class="close" data-dismiss="alert" href="#" @click="$event.target.parentNode.remove()">×</a>
<strong class="title" v-if="msg.title" v-html="msg.title"></strong>
<span class="message" v-html="msg.content"></span>
<a v-if="msg.action.message" href="#"
@click.once.prevent="msg.action.callback($event)">{% msg.action.message %}</a>
</p>
{{!base}} {{!base}}
</div><!--/span--> </div>
<!--/span-->
</div><!--/row--> </div>
<!--/row-->
<hr> <hr>
<footer> <footer>
<blockquote> <blockquote>
<p>“Few persons can be made to believe that it is not quite an easy thing to invent a method of secret writing which shall baffle investigation. Yet it may be roundly asserted that human ingenuity cannot concoct a cipher which human ingenuity cannot resolve...”</p> <p>“Few persons can be made to believe that it is not quite an easy thing to invent a method of secret writing
which shall baffle investigation. Yet it may be roundly asserted that human ingenuity cannot concoct a cipher
which human ingenuity cannot resolve...”</p>
<small>Edgar Allan Poe</small> <small>Edgar Allan Poe</small>
</blockquote> </blockquote>
@ -136,7 +143,7 @@
%end %end
</br> <br>
<p class="greetings span12"> <p class="greetings span12">
Based on an original idea from Based on an original idea from
<a href="http://sebsauvage.net/paste/">sebsauvage.net</a><br> <a href="http://sebsauvage.net/paste/">sebsauvage.net</a><br>
@ -144,22 +151,30 @@
</p> </p>
</footer> </footer>
</div>
<!--/wrap-content-->
<script src="/static/js/vue.js"></script>
%if settings.COMPRESSED_STATIC_FILES:
<script src="/static/js/main.min.js?{{ settings.VERSION }}"></script>
%else:
<script src="/static/js/sjcl.js"></script>
<script src="/static/js/behavior.js?{{ settings.VERSION }}"></script>
%end
<script type="text/javascript">
zerobin.max_size = {{ settings.MAX_SIZE }};
</script>
%if settings.COMPRESSED_STATIC_FILES: %if settings.COMPRESSED_STATIC_FILES:
<script src="/static/js/additional.min.js?{{ settings.VERSION }}"></script> <script src="/static/js/additional.min.js?{{ settings.VERSION }}"></script>
%else: %else:
<script src="/static/js/jquery.elastic.source.js"></script>
<script src="/static/js/lzw.js"></script> <script src="/static/js/lzw.js"></script>
<script src="/static/js/prettify.min.js"></script> <script src="/static/js/prettify.min.js"></script>
<script src="/static/js/ZeroClipboard.js"></script>
%end %end
<p id="alert-template">
<a class="close" data-dismiss="alert" href="#">×</a>
<strong class="title"></strong>
<span class="message"></span>
</p>
</div><!--/wrap-content-->
</body> </body>
</html> </html>

View File

@ -1,26 +1,37 @@
<p class="file-upload"> <p class="file-upload" v-if="support.fileUpload">
<input type="button" class="btn btn-upload" value="Upload File"> <input type="button" class="btn btn-upload" value="Upload File" :value="isUploading ? 'Uploading...': 'Upload file'"
<input type="file" class="hide-upload" id="file-upload" > :disabled="isUploading">
<input type="file" class="hide-upload" id="file-upload" @change="handleUpload($event.target.files)">
</p> </p>
<form class="well" method="post" action="/paste/create"> <form class="well" method="post" action="/paste/create">
<p class="paste-option"> <p class="paste-option">
<label for="expiration">Expiration:</label> <label for="expiration">Expiration:</label>
<select id="expiration" name="expiration"> <select id="expiration" name="expiration" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option> <option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">1 day</option> <option selected value="1_day">1 day</option>
<option value="1_month">1 month</option> <option value="1_month">1 month</option>
<option value="never">Never</option> <option value="never">Never</option>
</select> </select>
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary" @click="encryptAndSendPaste($event)">Submit</button>
</p> </p>
<p> <p>
<div class="progress progress-striped active"> <div class="progress progress-striped active" v-show="isLoading">
<div class="bar"></div> <div class="bar"></div>
</div> </div>
<textarea rows="10" style="width:100%;" <textarea rows="10" style="width:100%" class="input-xlarge" id="content" name="content" autofocus
class="input-xlarge" v-on:keydown.ctrl.enter="encryptAndSendPaste($event)"></textarea>
id="content" name="content"></textarea> </p>
<p class="paste-option down" v-if="displayBottomToolBar">
<label for="expiration">Expiration:</label>
<select id="expiration" name="expiration" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">1 day</option>
<option value="1_month">1 month</option>
<option value="never">Never</option>
</select>
<button type="submit" class="btn btn-primary" @click="encryptAndSendPaste($event)">Submit</button>
</p> </p>
</form> </form>

View File

@ -1,7 +1,7 @@
%if "burn_after_reading" in str(paste.expiration): %if "burn_after_reading" in str(paste.expiration):
%if keep_alive: %if keep_alive:
<p class="alert alert-info"> <p class="alert alert-info">
<a class="close" data-dismiss="alert" href="#">×</a> <a class="close" data-dismiss="alert" href="#" @click="$event.target.parentNode.remove()">×</a>
<strong class="title">Ok!</strong> <strong class="title">Ok!</strong>
<span class="message"> <span class="message">
This paste will be deleted the next time it is read. This paste will be deleted the next time it is read.
@ -9,7 +9,7 @@
</p> </p>
%else: %else:
<p class="alert"> <p class="alert">
<a class="close" data-dismiss="alert" href="#">×</a> <a class="close" data-dismiss="alert" href="#" @click="$event.target.parentNode.remove()">×</a>
<strong class="title">Warning!</strong> <strong class="title">Warning!</strong>
<span class="message"> <span class="message">
This paste has self-destructed. If you close this window, This paste has self-destructed. If you close this window,
@ -22,17 +22,22 @@
<div class="well paste-form"> <div class="well paste-form">
<form action="/" method="get" accept-charset="utf-8"> <form action="/" method="get" accept-charset="utf-8">
<p class="lnk-option"> <p class="lnk-option">
<a id="clip-button" href="#">Copy To Clipboard</a> | <a id="clip-button" v-if="support.clipboard" href="#" @click.prevent="copyToClipboard()">Copy To Clipboard</a> |
<a id="short-url" href="#">Get short url</a> |
<a id="email-link" href="#">Email this</a> <a id="email-link" href="#" @click="handleSendByEmail($event)">Email this</a>
<span class="paste-option btn-group top"> <span class="paste-option btn-group top">
<button class="btn btn-clone"><i class="icon-camera"></i>&nbsp;Clone</button> <button class="btn btn-clone" @click.prevent="handleClone()"><i class="icon-camera"></i>&nbsp;Clone</button>
<button class="btn" v-if="downloadLink.url">
<a :href="downloadLink.url" :download="downloadLink.name"><i class="icon-download"></i> Download</a>
</button>
<button class="btn">New Paste</button> <button class="btn">New Paste</button>
</span> </span>
</p> </p>
<div class="progress progress-striped active"> <div class="progress progress-striped active" v-show="isLoading">
<div class="bar"></div> <div class="bar"></div>
</div> </div>
@ -50,7 +55,12 @@
</p> </p>
<p class="paste-option btn-group bottom"> <p class="paste-option btn-group bottom">
<button class="btn btn-clone"><i class="icon-camera"></i>&nbsp;Clone</button> <button class="btn btn-clone" @click.prevent="handleClone()"><i class="icon-camera"></i>&nbsp;Clone</button>
<button class="btn" v-if="downloadLink.url">
<a :href="downloadLink.url" :download="downloadLink.name"><i class="icon-download"></i> Download</a>
</button>
<button class="btn">New Paste</button> <button class="btn">New Paste</button>
</p> </p>
@ -62,26 +72,37 @@
<form class="well" method="post" action="/paste/create"> <form class="well" method="post" action="/paste/create">
<p class="paste-option"> <p class="paste-option">
<label for="expiration">Expiration:</label> <label for="expiration">Expiration:</label>
<select id="expiration" name="expiration"> <select id="expiration" name="expiration" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option> <option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">1 day</option> <option selected value="1_day">1 day</option>
<option value="1_month">1 month</option> <option value="1_month">1 month</option>
<option value="never">Never</option> <option value="never">Never</option>
</select> </select>
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary" @click="encryptAndSendPaste($event)">Submit</button>
<button class="btn btn-danger">Cancel clone</button> <button class="btn btn-danger" @click.prevent="handleCancelClone()">Cancel clone</button>
</p> </p>
<div> <div>
<div class="progress progress-striped active"> <div class="progress progress-striped active" v-show="isLoading">
<div class="bar"></div> <div class="bar"></div>
</div> </div>
<textarea rows="10" style="width:100%;" <textarea rows="10" style="width:100%;" class="input-xlarge" id="content" name="content" autofocus
class="input-xlarge" v-on:keydown.ctrl.enter="encryptAndSendPaste($event)"></textarea>
id="content" name="content"></textarea>
</div> </div>
<p class="paste-option down" v-if="displayBottomToolBar">
<label for="expiration">Expiration:</label>
<select id="expiration" name="expiration" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">1 day</option>
<option value="1_month">1 month</option>
<option value="never">Never</option>
</select>
<button type="submit" class="btn btn-primary" @click="encryptAndSendPaste($event)">Submit</button>
</p>
</form> </form>
</div> </div>
% rebase("base", settings=settings, pastes_count=pastes_count) % rebase("base", settings=settings, pastes_count=pastes_count)