First commit. Working.

This commit is contained in:
Luc Didry 2015-09-21 02:02:13 +02:00
commit 91e07880b6
63 changed files with 7533 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
local/*
files/*
lufi.conf
lufi.db
*.swp
script/hypnotoad.pid

23
Makefile Normal file
View File

@ -0,0 +1,23 @@
EXTRACTFILES=utilities/locales_files.txt
EN=lib/Lufi/I18N/en.po
XGETTEXT=carton exec local/bin/xgettext.pl
CARTON=carton exec
REAL_LUFI=script/application
LUFI=script/lufi
locales:
$(XGETTEXT) -f $(EXTRACTFILES) -o $(EN)
test:
$(CARTON) $(REAL_LUFI) test
clean:
rm -rf lufi.db files/
dev: clean
$(CARTON) morbo $(LUFI) --listen http://0.0.0.0:3000
devlog:
multitail log/development.log
prod:
$(CARTON) hypnotoad $(LUFI)

9
cpanfile Normal file
View File

@ -0,0 +1,9 @@
requires "Mojolicious";
requires "ORLite";
requires "Mojolicious::Plugin::DebugDumperHelper";
requires 'Mojolicious::Plugin::I18N';
requires 'EV';
requires 'Filesys::DiskUsage';
requires 'Switch';
requires 'Locale::Maketext';
requires 'Locale::Maketext::Extract';

483
cpanfile.snapshot Normal file
View File

@ -0,0 +1,483 @@
# carton snapshot format: version 1.0
DISTRIBUTIONS
CPAN-Meta-2.150005
pathname: D/DA/DAGOLDEN/CPAN-Meta-2.150005.tar.gz
provides:
CPAN::Meta 2.150005
CPAN::Meta::Converter 2.150005
CPAN::Meta::Feature 2.150005
CPAN::Meta::History 2.150005
CPAN::Meta::Merge 2.150005
CPAN::Meta::Prereqs 2.150005
CPAN::Meta::Spec 2.150005
CPAN::Meta::Validator 2.150005
requirements:
CPAN::Meta::Requirements 2.121
CPAN::Meta::YAML 0.008
Carp 0
ExtUtils::MakeMaker 6.17
JSON::PP 2.27200
Parse::CPAN::Meta 1.4414
Scalar::Util 0
perl 5.008
strict 0
version 0.88
warnings 0
Canary-Stability-2006
pathname: M/ML/MLEHMANN/Canary-Stability-2006.tar.gz
provides:
Canary::Stability 2006
requirements:
ExtUtils::MakeMaker 0
DBD-SQLite-1.48
pathname: I/IS/ISHIGAKI/DBD-SQLite-1.48.tar.gz
provides:
DBD::SQLite 1.48
DBD::SQLite::Constants undef
DBD::SQLite::VirtualTable 1.48
DBD::SQLite::VirtualTable::Cursor 1.48
DBD::SQLite::VirtualTable::FileContent undef
DBD::SQLite::VirtualTable::FileContent::Cursor undef
DBD::SQLite::VirtualTable::PerlData undef
DBD::SQLite::VirtualTable::PerlData::Cursor undef
requirements:
DBI 1.57
ExtUtils::MakeMaker 0
File::Spec 0.82
Test::Builder 0.86
Test::More 0.47
Tie::Hash 0
perl 5.006
DBI-1.634
pathname: T/TI/TIMB/DBI-1.634.tar.gz
provides:
Bundle::DBI 12.008696
DBD::DBM 0.08
DBD::DBM::Statement 0.08
DBD::DBM::Table 0.08
DBD::DBM::db 0.08
DBD::DBM::dr 0.08
DBD::DBM::st 0.08
DBD::ExampleP 12.014311
DBD::ExampleP::db 12.014311
DBD::ExampleP::dr 12.014311
DBD::ExampleP::st 12.014311
DBD::File 0.44
DBD::File::DataSource::File 0.44
DBD::File::DataSource::Stream 0.44
DBD::File::Statement 0.44
DBD::File::Table 0.44
DBD::File::TableSource::FileSystem 0.44
DBD::File::db 0.44
DBD::File::dr 0.44
DBD::File::st 0.44
DBD::Gofer 0.015327
DBD::Gofer::Policy::Base 0.010088
DBD::Gofer::Policy::classic 0.010088
DBD::Gofer::Policy::pedantic 0.010088
DBD::Gofer::Policy::rush 0.010088
DBD::Gofer::Transport::Base 0.014121
DBD::Gofer::Transport::corostream undef
DBD::Gofer::Transport::null 0.010088
DBD::Gofer::Transport::pipeone 0.010088
DBD::Gofer::Transport::stream 0.014599
DBD::Gofer::db 0.015327
DBD::Gofer::dr 0.015327
DBD::Gofer::st 0.015327
DBD::NullP 12.014715
DBD::NullP::db 12.014715
DBD::NullP::dr 12.014715
DBD::NullP::st 12.014715
DBD::Proxy 0.2004
DBD::Proxy::RPC::PlClient 0.2004
DBD::Proxy::db 0.2004
DBD::Proxy::dr 0.2004
DBD::Proxy::st 0.2004
DBD::Sponge 12.010003
DBD::Sponge::db 12.010003
DBD::Sponge::dr 12.010003
DBD::Sponge::st 12.010003
DBDI 12.015129
DBI 1.634
DBI::Const::GetInfo::ANSI 2.008697
DBI::Const::GetInfo::ODBC 2.011374
DBI::Const::GetInfoReturn 2.008697
DBI::Const::GetInfoType 2.008697
DBI::DBD 12.015129
DBI::DBD::Metadata 2.014214
DBI::DBD::SqlEngine 0.06
DBI::DBD::SqlEngine::DataSource 0.06
DBI::DBD::SqlEngine::Statement 0.06
DBI::DBD::SqlEngine::Table 0.06
DBI::DBD::SqlEngine::TableSource 0.06
DBI::DBD::SqlEngine::TieMeta 0.06
DBI::DBD::SqlEngine::TieTables 0.06
DBI::DBD::SqlEngine::db 0.06
DBI::DBD::SqlEngine::dr 0.06
DBI::DBD::SqlEngine::st 0.06
DBI::FAQ 1.014935
DBI::Gofer::Execute 0.014283
DBI::Gofer::Request 0.012537
DBI::Gofer::Response 0.011566
DBI::Gofer::Serializer::Base 0.009950
DBI::Gofer::Serializer::DataDumper 0.009950
DBI::Gofer::Serializer::Storable 0.015586
DBI::Gofer::Transport::Base 0.012537
DBI::Gofer::Transport::pipeone 0.012537
DBI::Gofer::Transport::stream 0.012537
DBI::Profile 2.015065
DBI::ProfileData 2.010008
DBI::ProfileDumper 2.015325
DBI::ProfileDumper::Apache 2.014121
DBI::ProfileSubs 0.009396
DBI::ProxyServer 0.3005
DBI::ProxyServer::db 0.3005
DBI::ProxyServer::dr 0.3005
DBI::ProxyServer::st 0.3005
DBI::SQL::Nano 1.015544
DBI::SQL::Nano::Statement_ 1.015544
DBI::SQL::Nano::Table_ 1.015544
DBI::Util::CacheMemory 0.010315
DBI::Util::_accessor 0.009479
DBI::common 1.634
requirements:
ExtUtils::MakeMaker 6.48
Test::Simple 0.90
perl 5.008
EV-4.21
pathname: M/ML/MLEHMANN/EV-4.21.tar.gz
provides:
EV 4.21
EV::MakeMaker undef
requirements:
Canary::Stability 0
ExtUtils::MakeMaker 6.52
common::sense 0
File-Remove-1.52
pathname: A/AD/ADAMK/File-Remove-1.52.tar.gz
provides:
File::Remove 1.52
requirements:
Cwd 3.29
ExtUtils::MakeMaker 6.36
File::Spec 3.29
Test::More 0.42
perl 5.00503
Filesys-DiskUsage-0.08
pathname: S/SZ/SZABGAB/Filesys-DiskUsage-0.08.tar.gz
provides:
Filesys::DiskUsage 0.08
requirements:
ExtUtils::MakeMaker 0
File::Basename 0
File::Find 0
File::Temp 0
Test::More 0
Test::Warn 0
IPC-Run3-0.048
pathname: R/RJ/RJBS/IPC-Run3-0.048.tar.gz
provides:
IPC::Run3 0.048
requirements:
ExtUtils::MakeMaker 0
Test::More 0.31
Time::HiRes 0
Locale-Maketext-Lexicon-1.00
pathname: D/DR/DRTECH/Locale-Maketext-Lexicon-1.00.tar.gz
provides:
Locale::Maketext::Extract 1.00
Locale::Maketext::Extract::Plugin::Base 1.00
Locale::Maketext::Extract::Plugin::FormFu 1.00
Locale::Maketext::Extract::Plugin::FormFu::Extractor 1.00
Locale::Maketext::Extract::Plugin::Generic 1.00
Locale::Maketext::Extract::Plugin::Haml 1.00
Locale::Maketext::Extract::Plugin::Mason 1.00
Locale::Maketext::Extract::Plugin::PPI 1.00
Locale::Maketext::Extract::Plugin::Perl 1.00
Locale::Maketext::Extract::Plugin::TT2 1.00
Locale::Maketext::Extract::Plugin::TT2::Directive 1.00
Locale::Maketext::Extract::Plugin::TT2::Parser 1.00
Locale::Maketext::Extract::Plugin::TextTemplate 1.00
Locale::Maketext::Extract::Plugin::TextTemplate::Parser 1.00
Locale::Maketext::Extract::Plugin::YAML 1.00
Locale::Maketext::Extract::Plugin::YAML::Extractor 1.00
Locale::Maketext::Extract::Run 1.00
Locale::Maketext::Lexicon 1.00
Locale::Maketext::Lexicon::Auto 1.00
Locale::Maketext::Lexicon::Gettext 1.00
Locale::Maketext::Lexicon::Msgcat 1.00
Locale::Maketext::Lexicon::Tie 1.00
requirements:
ExtUtils::MakeMaker 6.30
Locale::Maketext 1.17
Module-Build-0.4214
pathname: L/LE/LEONT/Module-Build-0.4214.tar.gz
provides:
Module::Build 0.4214
Module::Build::Base 0.4214
Module::Build::Compat 0.4214
Module::Build::Config 0.4214
Module::Build::Cookbook 0.4214
Module::Build::Dumper 0.4214
Module::Build::Notes 0.4214
Module::Build::PPMMaker 0.4214
Module::Build::Platform::Default 0.4214
Module::Build::Platform::MacOS 0.4214
Module::Build::Platform::Unix 0.4214
Module::Build::Platform::VMS 0.4214
Module::Build::Platform::VOS 0.4214
Module::Build::Platform::Windows 0.4214
Module::Build::Platform::aix 0.4214
Module::Build::Platform::cygwin 0.4214
Module::Build::Platform::darwin 0.4214
Module::Build::Platform::os2 0.4214
Module::Build::PodParser 0.4214
requirements:
CPAN::Meta 2.142060
CPAN::Meta::YAML 0.003
Cwd 0
Data::Dumper 0
ExtUtils::CBuilder 0.27
ExtUtils::Install 0
ExtUtils::Manifest 0
ExtUtils::Mkbootstrap 0
ExtUtils::ParseXS 2.21
File::Basename 0
File::Compare 0
File::Copy 0
File::Find 0
File::Path 0
File::Spec 0.82
File::Temp 0.15
Getopt::Long 0
Module::Metadata 1.000002
Parse::CPAN::Meta 1.4401
Perl::OSType 1
Pod::Man 2.17
Test::Harness 3.16
Test::More 0.49
Text::Abbrev 0
Text::ParseWords 0
perl 5.008000
version 0.87
Mojolicious-6.17
pathname: S/SR/SRI/Mojolicious-6.17.tar.gz
provides:
Mojo undef
Mojo::Asset undef
Mojo::Asset::File undef
Mojo::Asset::Memory undef
Mojo::Base undef
Mojo::ByteStream undef
Mojo::Cache undef
Mojo::Collection undef
Mojo::Content undef
Mojo::Content::MultiPart undef
Mojo::Content::Single undef
Mojo::Cookie undef
Mojo::Cookie::Request undef
Mojo::Cookie::Response undef
Mojo::DOM undef
Mojo::DOM::CSS undef
Mojo::DOM::HTML undef
Mojo::Date undef
Mojo::EventEmitter undef
Mojo::Exception undef
Mojo::Headers undef
Mojo::HelloWorld undef
Mojo::Home undef
Mojo::IOLoop undef
Mojo::IOLoop::Client undef
Mojo::IOLoop::Delay undef
Mojo::IOLoop::Server undef
Mojo::IOLoop::Stream undef
Mojo::JSON undef
Mojo::JSON::Pointer undef
Mojo::JSON::_Bool undef
Mojo::Loader undef
Mojo::Log undef
Mojo::Message undef
Mojo::Message::Request undef
Mojo::Message::Response undef
Mojo::Parameters undef
Mojo::Path undef
Mojo::Reactor undef
Mojo::Reactor::EV undef
Mojo::Reactor::Poll undef
Mojo::Server undef
Mojo::Server::CGI undef
Mojo::Server::Daemon undef
Mojo::Server::Hypnotoad undef
Mojo::Server::Morbo undef
Mojo::Server::PSGI undef
Mojo::Server::PSGI::_IO undef
Mojo::Server::Prefork undef
Mojo::Template undef
Mojo::Transaction undef
Mojo::Transaction::HTTP undef
Mojo::Transaction::WebSocket undef
Mojo::URL undef
Mojo::Upload undef
Mojo::UserAgent undef
Mojo::UserAgent::CookieJar undef
Mojo::UserAgent::Proxy undef
Mojo::UserAgent::Server undef
Mojo::UserAgent::Transactor undef
Mojo::Util undef
Mojolicious 6.17
Mojolicious::Command undef
Mojolicious::Command::cgi undef
Mojolicious::Command::cpanify undef
Mojolicious::Command::daemon undef
Mojolicious::Command::eval undef
Mojolicious::Command::generate undef
Mojolicious::Command::generate::app undef
Mojolicious::Command::generate::lite_app undef
Mojolicious::Command::generate::makefile undef
Mojolicious::Command::generate::plugin undef
Mojolicious::Command::get undef
Mojolicious::Command::inflate undef
Mojolicious::Command::prefork undef
Mojolicious::Command::psgi undef
Mojolicious::Command::routes undef
Mojolicious::Command::test undef
Mojolicious::Command::version undef
Mojolicious::Commands undef
Mojolicious::Controller undef
Mojolicious::Lite undef
Mojolicious::Plugin undef
Mojolicious::Plugin::Charset undef
Mojolicious::Plugin::Config undef
Mojolicious::Plugin::Config::Sandbox undef
Mojolicious::Plugin::DefaultHelpers undef
Mojolicious::Plugin::EPLRenderer undef
Mojolicious::Plugin::EPRenderer undef
Mojolicious::Plugin::HeaderCondition undef
Mojolicious::Plugin::JSONConfig undef
Mojolicious::Plugin::Mount undef
Mojolicious::Plugin::PODRenderer undef
Mojolicious::Plugin::TagHelpers undef
Mojolicious::Plugins undef
Mojolicious::Renderer undef
Mojolicious::Routes undef
Mojolicious::Routes::Match undef
Mojolicious::Routes::Pattern undef
Mojolicious::Routes::Route undef
Mojolicious::Sessions undef
Mojolicious::Static undef
Mojolicious::Types undef
Mojolicious::Validator undef
Mojolicious::Validator::Validation undef
Test::Mojo undef
ojo undef
requirements:
ExtUtils::MakeMaker 0
IO::Socket::IP 0.26
Pod::Simple 3.09
Time::Local 1.2
Mojolicious-Plugin-DebugDumperHelper-0.02
pathname: L/LD/LDIDRY/Mojolicious-Plugin-DebugDumperHelper-0.02.tar.gz
provides:
Mojolicious::Plugin::DebugDumperHelper 0.02
requirements:
ExtUtils::MakeMaker 0
Mojolicious 6.11
Mojolicious-Plugin-I18N-1.6
pathname: S/SH/SHARIFULN/Mojolicious-Plugin-I18N-1.6.tar.gz
provides:
Mojolicious::Plugin::I18N 1.6
requirements:
I18N::LangTags 0.35
Module::Build 0.42
Mojolicious 5
Test::More 0
perl 5.010001
ORLite-1.98
pathname: A/AD/ADAMK/ORLite-1.98.tar.gz
provides:
ORLite 1.98
requirements:
DBD::SQLite 1.27
DBI 1.607
ExtUtils::MakeMaker 6.59
File::Path 2.08
File::Remove 1.40
File::Spec 0.80
File::Temp 0.20
Params::Util 1.00
Test::More 0.47
Test::Script 1.06
perl 5.006
Params-Util-1.07
pathname: A/AD/ADAMK/Params-Util-1.07.tar.gz
provides:
Params::Util 1.07
requirements:
ExtUtils::CBuilder 0.27
ExtUtils::MakeMaker 6.52
File::Spec 0.80
Scalar::Util 1.18
Test::More 0.42
perl 5.00503
Probe-Perl-0.03
pathname: K/KW/KWILLIAMS/Probe-Perl-0.03.tar.gz
provides:
Probe::Perl 0.03
requirements:
Config 0
ExtUtils::MakeMaker 6.30
File::Spec 0
strict 0
Sub-Uplevel-0.25
pathname: D/DA/DAGOLDEN/Sub-Uplevel-0.25.tar.gz
provides:
Sub::Uplevel 0.25
requirements:
Carp 0
ExtUtils::MakeMaker 6.17
constant 0
perl 5.006
strict 0
warnings 0
Switch-2.17
pathname: C/CH/CHORNY/Switch-2.17.tar.gz
provides:
Switch 2.17
requirements:
ExtUtils::MakeMaker 0
Filter::Util::Call 0
Text::Balanced 2
if 0
perl 5.005
Test-Script-1.10
pathname: P/PL/PLICEASE/Test-Script-1.10.tar.gz
provides:
Test::Script 1.10
requirements:
ExtUtils::MakeMaker 0
File::Spec 0.80
IPC::Run3 0.034
Probe::Perl 0.01
Test::Builder 0.32
Test::More 0.96
perl 5.006
Test-Warn-0.30
pathname: C/CH/CHORNY/Test-Warn-0.30.tar.gz
provides:
Test::Warn 0.30
Test::Warn::Categorization 0.30
requirements:
Carp 1.22
ExtUtils::MakeMaker 0
File::Spec 0
Sub::Uplevel 0.12
Test::Builder 0.13
Test::Builder::Tester 1.02
Test::More 0
perl 5.006
common-sense-3.74
pathname: M/ML/MLEHMANN/common-sense-3.74.tar.gz
provides:
common::sense 3.74
requirements:
ExtUtils::MakeMaker 0

172
lib/Lufi.pm Normal file
View File

@ -0,0 +1,172 @@
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
package Lufi;
use Mojo::Base 'Mojolicious';
use LufiDB;
$ENV{MOJO_MAX_WEBSOCKET_SIZE} = 100485760; # 10 * 1024 * 1024 = 10MiB
# This method will run once at server start
sub startup {
my $self = shift;
my $config = $self->plugin('Config' => {
default => {
provisioning => 100,
provis_step => 5,
length => 10,
token_length => 32,
secret => ['hfudsifdsih'],
default_delay => 0,
max_delay => 0
}
});
die "You need to provide a contact information in lufi.conf!" unless (defined($config->{contact}));
# Internationalization
$self->plugin('I18N');
# Debug
$self->plugin('DebugDumperHelper');
$self->secrets($config->{secrets});
# Helpers
$self->helper(
provisioning => sub {
my $c = shift;
# Create some short patterns for provisioning
if (LufiDB::Files->count('WHERE created_at IS NULL') < $c->config('provisioning')) {
for (my $i = 0; $i < $c->config('provis_step'); $i++) {
if (LufiDB->begin) {
my $short;
do {
$short= $c->shortener($c->config('length'));
} while (LufiDB::Files->count('WHERE short = ?', $short));
LufiDB::Files->create(
short => $short
);
LufiDB->commit;
}
}
}
}
);
$self->helper(
get_empty => sub {
my $c = shift;
my @records = LufiDB::Files->select('WHERE created_at IS NULL LIMIT 1');
return $records[0];
}
);
$self->helper(
shortener => sub {
my $c = shift;
my $length = shift;
my @chars = ('a'..'z','A'..'Z','0'..'9', '-', '_');
my $result = '';
foreach (1..$length) {
$result .= $chars[rand scalar(@chars)];
}
return $result;
}
);
$self->helper(
ip => sub {
my $c = shift;
my $proxy = $c->req->headers->header('X-Forwarded-For');
my $ip = ($proxy) ? $proxy : $c->tx->remote_address;
my $remote_port = (defined($c->req->headers->header('X-Remote-Port'))) ? $c->req->headers->header('X-Remote-Port') : $c->tx->remote_port;
return "$ip remote port:$remote_port";
}
);
$self->helper(
default_delay => sub {
my $c = shift;
return $c->config('default_delay') if ($c->config('default_delay') >= 0);
warn "default_delay set to a negative value. Default to 0.";
return 0;
}
);
$self->helper(
is_selected => sub {
my $c = shift;
my $num = shift;
return ($num == $c->default_delay) ? 'selected="selected"' : '';
}
);
# Hooks
$self->hook(
after_dispatch => sub {
shift->provisioning();
}
);
# For the first launch (after, this isn't really useful)
$self->provisioning();
# Create directory if needed
mkdir('files', 0700) unless (-d 'files');
# Default layout
$self->defaults(layout => 'default');
# Router
my $r = $self->routes;
# Page for files uploading
$r->get('/' => sub {
shift->render(template => 'index');
})->name('index');
# Get a file
$r->get('/r/:short')
->to('Files#r')
->name('render');
# List of files (use localstorage, so the server know nothing about files
$r->get('/files' => sub {
shift->render(template => 'files');
})->name('files');
# Get counter informations about a file
$r->post('/c')->
to('Files#get_counter')->
name('counter');
# Get counter informations about a file
$r->get('/d/:short/:token')->
to('Files#delete')->
name('delete');
# About page
$r->get('/about' => sub {
shift->render(template => 'about');
})->name('about');
# Upload files websocket
$r->websocket('/upload')
->to('Files#upload')
->name('upload');
# Get files websocket
$r->websocket('/download/:short')
->to('Files#download')
->name('download');
}
1;

View File

@ -0,0 +1,204 @@
package Lufi::Controller::Files;
use Mojo::Base 'Mojolicious::Controller';
use Mojo::JSON qw(encode_json decode_json true false);
use Mojo::Util qw(slurp spurt encode);
use LufiDB;
use Lufi::File;
use Lufi::Slice;
use File::Spec::Functions;
sub upload {
my $c = shift;
$c->inactivity_timeout(300000);
$c->debug('Client connected');
$c->on(
message => sub {
my ($ws, $text) = @_;
my ($json) = split('XXMOJOXX', $text, 2);
$json = encode 'UTF-8', $json;
$json = decode_json $json;
$c->debug('Got message');
my $f;
if (defined($json->{id})) {
my @records = LufiDB::Files->select('WHERE short = ?', $json->{id});
$f = Lufi::File->new(record => $records[0]);
} else {
$f = Lufi::File->new(
record => $c->get_empty,
created_by => $c->ip,
delete_at_first_view => ($json->{del_at_first_view}) ? 1 : 0,
delete_at_day => $json->{delay},
mediatype => $json->{type},
filename => $json->{name},
filesize => $json->{size},
nbslices => $json->{total},
mod_token => $c->shortener($c->config('token_length'))
);
$f->write;
}
# Create directory
my $dir = catdir('files', $f->short);
mkdir($dir, 0700) unless (-d $dir);
# Create slice file
my $file = catfile($dir, $json->{part}.'.part');
my $s = Lufi::Slice->new(
short => $f->short,
j => $json->{part},
path => $file
);
spurt $text, $file;
push @{$f->slices}, $s;
if (($json->{part} + 1) == $json->{total}) {
$f->complete(1);
$c->provisioning;
}
$f->write;
$ws->send(sprintf('{"success": true, "i": %d, "j": %d, "parts": %d, "short": "%s", "name": "%s", "size": %d, "del_at_first_view": %s, "created_at": %d, "delay": %d, "token": "%s"}', $json->{i}, $json->{part}, $json->{total}, $f->short, $f->filename, $f->filesize, (($f->delete_at_first_view) ? 'true' : 'false'), $f->created_at, $f->delete_at_day, $f->mod_token));
}
);
$c->on(
finish => sub {
$c->debug('Client disconnected');
}
);
}
sub download {
my $c = shift;
my $short = $c->param('short');
$c->inactivity_timeout(300000);
$c->debug('Client connected');
my @records = LufiDB::Files->select('WHERE short = ?', $short);
my $f = Lufi::File->new(record => $records[0]);
$c->on(
message => sub {
my ($ws, $json) = @_;
$json = decode_json $json;
if (defined($json->{part})) {
# Make $num an integer instead of a string
my $num = $json->{part} + 0;
my $e = $f->slices->[$num];
my $text = slurp $e->path;
$c->send($text);
} elsif (defined($json->{ended}) && $json->{ended}) {
$f->counter($f->counter + 1);
$f->last_access_at(time);
if ($f->delete_at_first_view) {
$f->delete;
} else {
$f->write;
}
}
}
);
$c->on(
finish => sub {
$c->debug('Client disconnected');
}
);
}
sub r {
my $c = shift;
my $short = $c->param('short');
my @records = LufiDB::Files->select('WHERE short = ?', $short);
if (scalar @records) {
my $f = Lufi::File->new(record => $records[0]);
my $msg = $c->l('The file has been deleted and is no more available.') if $f->deleted;
return $c->render(
template => 'render',
f => $f,
msg => $msg
);
} else {
return $c->render(
template => 'render',
f => undef,
msg => $c->l('Could not find the file. Are you sure of the URL?')
);
}
}
sub get_counter {
my $c = shift;
my $short = $c->param('short');
my $token = $c->param('token');
my @records = LufiDB::Files->select('WHERE short = ? AND mod_token = ?', ($short, $token));
if (scalar(@records)) {
return $c->render(
json => {
success => true,
counter => $records[0]->counter
}
);
}
return $c->render(
json => {
success => false,
msg => $c->l('Unable to get counter')
}
);
}
sub delete {
my $c = shift;
my $short = $c->param('short');
my $token = $c->param('token');
my @records = LufiDB::Files->select('WHERE short = ? AND mod_token = ?', ($short, $token));
if (scalar(@records)) {
my $f = Lufi::File->new(record => $records[0]);
my $msg;
if ($f->deleted) {
$msg = $c->l('The file has already been deleted');
} else {
$f->delete;
$msg = $c->l('File deleted');
}
return $c->respond_to(
json => {
success => true,
msg => $msg
},
any => sub {
$c->render(
template => 'msg',
f => $f,
msg => $msg
);
}
);
} else {
my $msg = $c->l('Could not find the file. Are you sure of the URL and the token?');
return $c->respond_to(
json => {
success => false,
msg => $msg
},
any => sub {
$c->render(
template => 'msg',
f => undef,
msg => $msg
);
}
);
}
}
1;

107
lib/Lufi/File.pm Normal file
View File

@ -0,0 +1,107 @@
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
package Lufi::File;
use Mojo::Base -base;
use Mojo::Collection;
use LufiDB;
has 'record';
has 'short';
has 'deleted' => 0;
has 'mediatype';
has 'filename';
has 'filesize';
has 'counter' => 0;
has 'delete_at_first_view' => 0;
has 'delete_at_day';
has 'created_at' => sub {
return time;
};
has 'created_by';
has 'last_access_at';
has 'mod_token';
has 'nbslices';
has 'complete' => 0;
has 'slices' => sub {
return Mojo::Collection->new();
};
sub new {
my $c = shift;
$c = $c->SUPER::new(@_);
$c = $c->_slurp if defined $c->record;
return $c;
}
sub write {
my $c = shift;
$c->record->update(
deleted => $c->deleted,
mediatype => $c->mediatype,
filename => $c->filename,
filesize => $c->filesize,
counter => $c->counter,
delete_at_first_view => $c->delete_at_first_view,
delete_at_day => $c->delete_at_day,
created_at => $c->created_at,
created_by => $c->created_by,
last_access_at => $c->last_access_at,
mod_token => $c->mod_token,
nbslices => $c->nbslices,
complete => $c->complete,
);
$c->slices->each(
sub {
my ($e, $num) = @_;
$e->write;
}
);
return $c;
}
sub delete {
my $c = shift;
$c->slices->each(sub {
my ($e, $num) = @_;
unlink $e->path;
});
$c->deleted(1);
$c->write;
return $c;
}
sub _slurp {
my $c = shift;
$c->short($c->record->short);
$c->deleted($c->record->deleted) if defined $c->record->deleted;
$c->mediatype($c->record->mediatype) if defined $c->record->mediatype;
$c->filename($c->record->filename) if defined $c->record->filename;
$c->filesize($c->record->filesize) if defined $c->record->filesize;
$c->counter($c->record->counter) if defined $c->record->counter;
$c->delete_at_first_view($c->record->delete_at_first_view) if defined $c->record->delete_at_first_view;
$c->delete_at_day($c->record->delete_at_day) if defined $c->record->delete_at_day;
$c->created_at($c->record->created_at) if defined $c->record->created_at;
$c->created_by($c->record->created_by) if defined $c->record->created_by;
$c->last_access_at($c->record->last_access_at) if defined $c->record->last_access_at;
$c->mod_token($c->record->mod_token) if defined $c->record->mod_token;
$c->nbslices($c->record->nbslices) if defined $c->record->nbslices;
$c->complete($c->record->complete) if defined $c->record->complete;
my @slices = LufiDB::Slices->select('WHERE short = ? ORDER BY j ASC', $c->short);
for my $s (@slices) {
push @{$c->slices}, Lufi::Slice->new(record => $s);
}
return $c;
}
1;

12
lib/Lufi/I18N.pm Normal file
View File

@ -0,0 +1,12 @@
package Lufi::I18N;
use base 'Locale::Maketext';
use File::Basename qw/dirname/;
use Locale::Maketext::Lexicon {
_auto => 1,
_decode => 1,
_style => 'gettext',
'*' => [Gettext => dirname(__FILE__) . '/I18N/*.po']
};
1;

110
lib/Lufi/I18N/en.po Normal file
View File

@ -0,0 +1,110 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf8\n"
"Content-Transfer-Encoding: 8bit\n"
#. ($delay)
#. (config('max_delay')
#: templates/index.html.ep:12 templates/index.html.ep:21 templates/index.html.ep:22
msgid "%1 days"
msgstr "%1 days"
#: templates/index.html.ep:5
msgid "1 year"
msgstr ""
#: templates/index.html.ep:21 templates/index.html.ep:4
msgid "24 hours"
msgstr ""
#: templates/layouts/default.html.ep:35
msgid "About"
msgstr ""
#: templates/index.html.ep:44
msgid "Click to open the file browser"
msgstr ""
#: templates/index.html.ep:59
msgid "Copy to clipboard"
msgstr ""
#: lib/Lufi/Controller/Files.pm:188
msgid "Could not find the file. Are you sure of the URL and the token?"
msgstr ""
#: lib/Lufi/Controller/Files.pm:132
msgid "Could not find the file. Are you sure of the URL?"
msgstr ""
#: templates/index.html.ep:35
msgid "Delete at first view?"
msgstr ""
#: templates/index.html.ep:60
msgid "Deletion link"
msgstr ""
#: templates/index.html.ep:58
msgid "Download link"
msgstr ""
#: templates/index.html.ep:41
msgid "Drop files here"
msgstr ""
#: lib/Lufi/Controller/Files.pm:172
msgid "File deleted"
msgstr ""
#: templates/index.html.ep:57
msgid "Hit Enter, then Ctrl+C to copy all the download links"
msgstr ""
#: templates/index.html.ep:56
msgid "Hit Enter, then Ctrl+C to copy the download link"
msgstr ""
#: templates/layouts/default.html.ep:34
msgid "My files"
msgstr ""
#: lib/Lufi/Controller/Files.pm:169
msgid "The file has already been deleted"
msgstr ""
#: lib/Lufi/Controller/Files.pm:122
msgid "The file has been deleted and is no more available."
msgstr ""
#: lib/Lufi/Controller/Files.pm:154
msgid "Unable to get counter"
msgstr ""
#: templates/layouts/default.html.ep:33
msgid "Upload files"
msgstr ""
#: templates/index.html.ep:49
msgid "Uploaded files"
msgstr ""
#: templates/index.html.ep:3
msgid "no time limit"
msgstr ""
#: templates/index.html.ep:42
msgid "or"
msgstr ""

52
lib/Lufi/Slice.pm Normal file
View File

@ -0,0 +1,52 @@
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
package Lufi::Slice;
use Mojo::Base -base;
use LufiDB;
has 'record';
has 'short';
has 'j';
has 'path';
sub new {
my $c = shift;
$c = $c->SUPER::new(@_);
$c = $c->_slurp if defined $c->record;
return $c;
}
sub write {
my $c = shift;
if (defined $c->record) {
$c->record->update(
short => $c->short,
j => $c->j,
path => $c->path
);
} else {
my $record = LufiDB::Slices->create(
short => $c->short,
j => $c->j,
path => $c->path
);
$c->record($record);
}
return $c;
}
sub _slurp {
my $c = shift;
$c->short($c->record->short) if defined $c->record->short;
$c->j($c->record->j) if defined $c->record->j;
$c->path($c->record->path) if defined $c->record->path;
return $c;
}
1;

56
lib/LufiDB.pm Normal file
View File

@ -0,0 +1,56 @@
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
package LufiDB;
use Mojolicious;
use FindBin qw($Bin);
use File::Spec::Functions;
BEGIN {
my $m = Mojolicious->new;
our $config = $m->plugin('Config' =>
{
file => catfile($Bin, '..' ,'lufi.conf'),
default => {
db_path => 'lufi.db'
}
}
);
}
# Create database
use ORLite {
file => $config->{db_path},
unicode => 1,
create => sub {
my $dbh = shift;
$dbh->do(
'CREATE TABLE files (
short TEXT PRIMARY KEY,
deleted INTEGER,
mediatype TEXT,
filename TEXT,
filesize INTEGER,
counter INTEGER,
delete_at_first_view INTEGER,
delete_at_day INTEGER,
created_at INTEGER,
created_by TEXT,
last_access_at INTEGER,
mod_token TEXT,
nbslices INTEGER,
complete INTEGER)'
);
$dbh->do(
'CREATE TABLE slices (
short TEXT,
j INTEGER,
path TEXT,
FOREIGN KEY (short) REFERENCES files(short))'
);
$dbh->do(
'CREATE INDEX slices_idx ON slices(short)'
);
return 1;
}
};
1;

22
lib/Mounter.pm Normal file
View File

@ -0,0 +1,22 @@
package Mounter;
use Mojo::Base 'Mojolicious';
use FindBin qw($Bin);
use File::Spec qw(catfile);
# This method will run once at server start
sub startup {
my $self = shift;
my $config = $self->plugin('Config' =>
{
file => File::Spec->catfile($Bin, '..' ,'lufi.conf'),
default => {
prefix => '/'
}
}
);
$self->plugin('Mount' => {$config->{prefix} => File::Spec->catfile($Bin, '..', 'script', 'application')});
}
1;

1
log/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*log

60
lufi.conf.template Normal file
View File

@ -0,0 +1,60 @@
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
{
####################
# Hypnotoad settings
####################
# see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings
hypnotoad => {
# array of IP addresses and ports you want to listen to
listen => ['http://127.0.0.1:8080'],
},
# put a way to contact you here and uncomment it
# MANDATORY
#contact => 'admin[at]example.com',
# array of random strings used to encrypt cookies
# optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT
#secrets => ['fdjsofjoihrei'],
# length of the random URL
# optional, default is 8
#length => 8,
# how many URLs will be provisioned in a batch ?
# optional, default is 5
#provis_step => 5,
# max number of URLs to be provisioned
# optional, default is 100
#provisioning => 100,
# length of the modify/delete token
# optional, default is 32
#token_length => 32,
# default time limit for files
# valid values are 0, 1, 7, 30 and 365
# optional, default is 0 (no limit)
#default_delay => 0,
# number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay)
# a warning message will be displayed on homepage
# optional, default is 0 (no limit)
#max_delay => 0,
# URL sub-directory in which you want Lstu to be accessible
# example: you want to have Lstu under https://example.org/lufi/
# => set prefix to '/lufi' or to '/lufi/', it doesn't matter
# optional, defaut is /
#prefix => '/',
# array of authorized domains for API calls.
# if you want to authorize everyone to use the API: ['*']
# optional, no domains allowed by default
#allowed_domains => ['http://1.example.com', 'http://2.example.com'],
# if set, the shortened URLs will use this domain
# optional
#fixed_domain => 'example.org',
};

View File

@ -0,0 +1,400 @@
{
"vars": {
"@gray-base": "#000",
"@gray-darker": "lighten(@gray-base, 13.5%)",
"@gray-dark": "lighten(@gray-base, 20%)",
"@gray": "lighten(@gray-base, 33.5%)",
"@gray-light": "lighten(@gray-base, 46.7%)",
"@gray-lighter": "lighten(@gray-base, 93.5%)",
"@brand-primary": "darken(#428bca, 6.5%)",
"@brand-success": "#5cb85c",
"@brand-info": "#5bc0de",
"@brand-warning": "#f0ad4e",
"@brand-danger": "#d9534f",
"@body-bg": "#fff",
"@text-color": "@gray-dark",
"@link-color": "@brand-primary",
"@link-hover-color": "darken(@link-color, 15%)",
"@link-hover-decoration": "underline",
"@font-family-sans-serif": "\"Helvetica Neue\", Helvetica, Arial, sans-serif",
"@font-family-serif": "Georgia, \"Times New Roman\", Times, serif",
"@font-family-monospace": "Menlo, Monaco, Consolas, \"Courier New\", monospace",
"@font-family-base": "@font-family-sans-serif",
"@font-size-base": "14px",
"@font-size-large": "ceil((@font-size-base * 1.25))",
"@font-size-small": "ceil((@font-size-base * 0.85))",
"@font-size-h1": "floor((@font-size-base * 2.6))",
"@font-size-h2": "floor((@font-size-base * 2.15))",
"@font-size-h3": "ceil((@font-size-base * 1.7))",
"@font-size-h4": "ceil((@font-size-base * 1.25))",
"@font-size-h5": "@font-size-base",
"@font-size-h6": "ceil((@font-size-base * 0.85))",
"@line-height-base": "1.428571429",
"@line-height-computed": "floor((@font-size-base * @line-height-base))",
"@headings-font-family": "inherit",
"@headings-font-weight": "500",
"@headings-line-height": "1.1",
"@headings-color": "inherit",
"@icon-font-path": "\"../fonts/\"",
"@icon-font-name": "\"glyphicons-halflings-regular\"",
"@icon-font-svg-id": "\"glyphicons_halflingsregular\"",
"@padding-base-vertical": "6px",
"@padding-base-horizontal": "12px",
"@padding-large-vertical": "10px",
"@padding-large-horizontal": "16px",
"@padding-small-vertical": "5px",
"@padding-small-horizontal": "10px",
"@padding-xs-vertical": "1px",
"@padding-xs-horizontal": "5px",
"@line-height-large": "1.3333333",
"@line-height-small": "1.5",
"@border-radius-base": "4px",
"@border-radius-large": "6px",
"@border-radius-small": "3px",
"@component-active-color": "#fff",
"@component-active-bg": "@brand-primary",
"@caret-width-base": "4px",
"@caret-width-large": "5px",
"@table-cell-padding": "8px",
"@table-condensed-cell-padding": "5px",
"@table-bg": "transparent",
"@table-bg-accent": "#f9f9f9",
"@table-bg-hover": "#f5f5f5",
"@table-bg-active": "@table-bg-hover",
"@table-border-color": "#ddd",
"@btn-font-weight": "normal",
"@btn-default-color": "#333",
"@btn-default-bg": "#fff",
"@btn-default-border": "#ccc",
"@btn-primary-color": "#fff",
"@btn-primary-bg": "@brand-primary",
"@btn-primary-border": "darken(@btn-primary-bg, 5%)",
"@btn-success-color": "#fff",
"@btn-success-bg": "@brand-success",
"@btn-success-border": "darken(@btn-success-bg, 5%)",
"@btn-info-color": "#fff",
"@btn-info-bg": "@brand-info",
"@btn-info-border": "darken(@btn-info-bg, 5%)",
"@btn-warning-color": "#fff",
"@btn-warning-bg": "@brand-warning",
"@btn-warning-border": "darken(@btn-warning-bg, 5%)",
"@btn-danger-color": "#fff",
"@btn-danger-bg": "@brand-danger",
"@btn-danger-border": "darken(@btn-danger-bg, 5%)",
"@btn-link-disabled-color": "@gray-light",
"@btn-border-radius-base": "@border-radius-base",
"@btn-border-radius-large": "@border-radius-large",
"@btn-border-radius-small": "@border-radius-small",
"@input-bg": "#fff",
"@input-bg-disabled": "@gray-lighter",
"@input-color": "@gray",
"@input-border": "#ccc",
"@input-border-radius": "@border-radius-base",
"@input-border-radius-large": "@border-radius-large",
"@input-border-radius-small": "@border-radius-small",
"@input-border-focus": "#66afe9",
"@input-color-placeholder": "#999",
"@input-height-base": "(@line-height-computed + (@padding-base-vertical * 2) + 2)",
"@input-height-large": "(ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2)",
"@input-height-small": "(floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2)",
"@form-group-margin-bottom": "15px",
"@legend-color": "@gray-dark",
"@legend-border-color": "#e5e5e5",
"@input-group-addon-bg": "@gray-lighter",
"@input-group-addon-border-color": "@input-border",
"@cursor-disabled": "not-allowed",
"@dropdown-bg": "#fff",
"@dropdown-border": "rgba(0,0,0,.15)",
"@dropdown-fallback-border": "#ccc",
"@dropdown-divider-bg": "#e5e5e5",
"@dropdown-link-color": "@gray-dark",
"@dropdown-link-hover-color": "darken(@gray-dark, 5%)",
"@dropdown-link-hover-bg": "#f5f5f5",
"@dropdown-link-active-color": "@component-active-color",
"@dropdown-link-active-bg": "@component-active-bg",
"@dropdown-link-disabled-color": "@gray-light",
"@dropdown-header-color": "@gray-light",
"@dropdown-caret-color": "#000",
"@screen-xs": "480px",
"@screen-xs-min": "@screen-xs",
"@screen-phone": "@screen-xs-min",
"@screen-sm": "768px",
"@screen-sm-min": "@screen-sm",
"@screen-tablet": "@screen-sm-min",
"@screen-md": "992px",
"@screen-md-min": "@screen-md",
"@screen-desktop": "@screen-md-min",
"@screen-lg": "1200px",
"@screen-lg-min": "@screen-lg",
"@screen-lg-desktop": "@screen-lg-min",
"@screen-xs-max": "(@screen-sm-min - 1)",
"@screen-sm-max": "(@screen-md-min - 1)",
"@screen-md-max": "(@screen-lg-min - 1)",
"@grid-columns": "12",
"@grid-gutter-width": "30px",
"@grid-float-breakpoint": "@screen-sm-min",
"@grid-float-breakpoint-max": "(@grid-float-breakpoint - 1)",
"@container-tablet": "(720px + @grid-gutter-width)",
"@container-sm": "@container-tablet",
"@container-desktop": "(940px + @grid-gutter-width)",
"@container-md": "@container-desktop",
"@container-large-desktop": "(1140px + @grid-gutter-width)",
"@container-lg": "@container-large-desktop",
"@navbar-height": "50px",
"@navbar-margin-bottom": "@line-height-computed",
"@navbar-border-radius": "@border-radius-base",
"@navbar-padding-horizontal": "floor((@grid-gutter-width / 2))",
"@navbar-padding-vertical": "((@navbar-height - @line-height-computed) / 2)",
"@navbar-collapse-max-height": "340px",
"@navbar-default-color": "#777",
"@navbar-default-bg": "#f8f8f8",
"@navbar-default-border": "darken(@navbar-default-bg, 6.5%)",
"@navbar-default-link-color": "#777",
"@navbar-default-link-hover-color": "#333",
"@navbar-default-link-hover-bg": "transparent",
"@navbar-default-link-active-color": "#555",
"@navbar-default-link-active-bg": "darken(@navbar-default-bg, 6.5%)",
"@navbar-default-link-disabled-color": "#ccc",
"@navbar-default-link-disabled-bg": "transparent",
"@navbar-default-brand-color": "@navbar-default-link-color",
"@navbar-default-brand-hover-color": "darken(@navbar-default-brand-color, 10%)",
"@navbar-default-brand-hover-bg": "transparent",
"@navbar-default-toggle-hover-bg": "#ddd",
"@navbar-default-toggle-icon-bar-bg": "#888",
"@navbar-default-toggle-border-color": "#ddd",
"@navbar-inverse-color": "lighten(@gray-light, 15%)",
"@navbar-inverse-bg": "#222",
"@navbar-inverse-border": "darken(@navbar-inverse-bg, 10%)",
"@navbar-inverse-link-color": "lighten(@gray-light, 15%)",
"@navbar-inverse-link-hover-color": "#fff",
"@navbar-inverse-link-hover-bg": "transparent",
"@navbar-inverse-link-active-color": "@navbar-inverse-link-hover-color",
"@navbar-inverse-link-active-bg": "darken(@navbar-inverse-bg, 10%)",
"@navbar-inverse-link-disabled-color": "#444",
"@navbar-inverse-link-disabled-bg": "transparent",
"@navbar-inverse-brand-color": "@navbar-inverse-link-color",
"@navbar-inverse-brand-hover-color": "#fff",
"@navbar-inverse-brand-hover-bg": "transparent",
"@navbar-inverse-toggle-hover-bg": "#333",
"@navbar-inverse-toggle-icon-bar-bg": "#fff",
"@navbar-inverse-toggle-border-color": "#333",
"@nav-link-padding": "10px 15px",
"@nav-link-hover-bg": "@gray-lighter",
"@nav-disabled-link-color": "@gray-light",
"@nav-disabled-link-hover-color": "@gray-light",
"@nav-tabs-border-color": "#ddd",
"@nav-tabs-link-hover-border-color": "@gray-lighter",
"@nav-tabs-active-link-hover-bg": "@body-bg",
"@nav-tabs-active-link-hover-color": "@gray",
"@nav-tabs-active-link-hover-border-color": "#ddd",
"@nav-tabs-justified-link-border-color": "#ddd",
"@nav-tabs-justified-active-link-border-color": "@body-bg",
"@nav-pills-border-radius": "@border-radius-base",
"@nav-pills-active-link-hover-bg": "@component-active-bg",
"@nav-pills-active-link-hover-color": "@component-active-color",
"@pagination-color": "@link-color",
"@pagination-bg": "#fff",
"@pagination-border": "#ddd",
"@pagination-hover-color": "@link-hover-color",
"@pagination-hover-bg": "@gray-lighter",
"@pagination-hover-border": "#ddd",
"@pagination-active-color": "#fff",
"@pagination-active-bg": "@brand-primary",
"@pagination-active-border": "@brand-primary",
"@pagination-disabled-color": "@gray-light",
"@pagination-disabled-bg": "#fff",
"@pagination-disabled-border": "#ddd",
"@pager-bg": "@pagination-bg",
"@pager-border": "@pagination-border",
"@pager-border-radius": "15px",
"@pager-hover-bg": "@pagination-hover-bg",
"@pager-active-bg": "@pagination-active-bg",
"@pager-active-color": "@pagination-active-color",
"@pager-disabled-color": "@pagination-disabled-color",
"@jumbotron-padding": "30px",
"@jumbotron-color": "inherit",
"@jumbotron-bg": "@gray-lighter",
"@jumbotron-heading-color": "inherit",
"@jumbotron-font-size": "ceil((@font-size-base * 1.5))",
"@jumbotron-heading-font-size": "ceil((@font-size-base * 4.5))",
"@state-success-text": "#3c763d",
"@state-success-bg": "#dff0d8",
"@state-success-border": "darken(spin(@state-success-bg, -10), 5%)",
"@state-info-text": "#31708f",
"@state-info-bg": "#d9edf7",
"@state-info-border": "darken(spin(@state-info-bg, -10), 7%)",
"@state-warning-text": "#8a6d3b",
"@state-warning-bg": "#fcf8e3",
"@state-warning-border": "darken(spin(@state-warning-bg, -10), 5%)",
"@state-danger-text": "#a94442",
"@state-danger-bg": "#f2dede",
"@state-danger-border": "darken(spin(@state-danger-bg, -10), 5%)",
"@tooltip-max-width": "200px",
"@tooltip-color": "#fff",
"@tooltip-bg": "#000",
"@tooltip-opacity": ".9",
"@tooltip-arrow-width": "5px",
"@tooltip-arrow-color": "@tooltip-bg",
"@popover-bg": "#fff",
"@popover-max-width": "276px",
"@popover-border-color": "rgba(0,0,0,.2)",
"@popover-fallback-border-color": "#ccc",
"@popover-title-bg": "darken(@popover-bg, 3%)",
"@popover-arrow-width": "10px",
"@popover-arrow-color": "@popover-bg",
"@popover-arrow-outer-width": "(@popover-arrow-width + 1)",
"@popover-arrow-outer-color": "fadein(@popover-border-color, 5%)",
"@popover-arrow-outer-fallback-color": "darken(@popover-fallback-border-color, 20%)",
"@label-default-bg": "@gray-light",
"@label-primary-bg": "@brand-primary",
"@label-success-bg": "@brand-success",
"@label-info-bg": "@brand-info",
"@label-warning-bg": "@brand-warning",
"@label-danger-bg": "@brand-danger",
"@label-color": "#fff",
"@label-link-hover-color": "#fff",
"@modal-inner-padding": "15px",
"@modal-title-padding": "15px",
"@modal-title-line-height": "@line-height-base",
"@modal-content-bg": "#fff",
"@modal-content-border-color": "rgba(0,0,0,.2)",
"@modal-content-fallback-border-color": "#999",
"@modal-backdrop-bg": "#000",
"@modal-backdrop-opacity": ".5",
"@modal-header-border-color": "#e5e5e5",
"@modal-footer-border-color": "@modal-header-border-color",
"@modal-lg": "900px",
"@modal-md": "600px",
"@modal-sm": "300px",
"@alert-padding": "15px",
"@alert-border-radius": "@border-radius-base",
"@alert-link-font-weight": "bold",
"@alert-success-bg": "@state-success-bg",
"@alert-success-text": "@state-success-text",
"@alert-success-border": "@state-success-border",
"@alert-info-bg": "@state-info-bg",
"@alert-info-text": "@state-info-text",
"@alert-info-border": "@state-info-border",
"@alert-warning-bg": "@state-warning-bg",
"@alert-warning-text": "@state-warning-text",
"@alert-warning-border": "@state-warning-border",
"@alert-danger-bg": "@state-danger-bg",
"@alert-danger-text": "@state-danger-text",
"@alert-danger-border": "@state-danger-border",
"@progress-bg": "#f5f5f5",
"@progress-bar-color": "#fff",
"@progress-border-radius": "@border-radius-base",
"@progress-bar-bg": "@brand-primary",
"@progress-bar-success-bg": "@brand-success",
"@progress-bar-warning-bg": "@brand-warning",
"@progress-bar-danger-bg": "@brand-danger",
"@progress-bar-info-bg": "@brand-info",
"@list-group-bg": "#fff",
"@list-group-border": "#ddd",
"@list-group-border-radius": "@border-radius-base",
"@list-group-hover-bg": "#f5f5f5",
"@list-group-active-color": "@component-active-color",
"@list-group-active-bg": "@component-active-bg",
"@list-group-active-border": "@list-group-active-bg",
"@list-group-active-text-color": "lighten(@list-group-active-bg, 40%)",
"@list-group-disabled-color": "@gray-light",
"@list-group-disabled-bg": "@gray-lighter",
"@list-group-disabled-text-color": "@list-group-disabled-color",
"@list-group-link-color": "#555",
"@list-group-link-hover-color": "@list-group-link-color",
"@list-group-link-heading-color": "#333",
"@panel-bg": "#fff",
"@panel-body-padding": "15px",
"@panel-heading-padding": "10px 15px",
"@panel-footer-padding": "@panel-heading-padding",
"@panel-border-radius": "@border-radius-base",
"@panel-inner-border": "#ddd",
"@panel-footer-bg": "#f5f5f5",
"@panel-default-text": "@gray-dark",
"@panel-default-border": "#ddd",
"@panel-default-heading-bg": "#f5f5f5",
"@panel-primary-text": "#fff",
"@panel-primary-border": "@brand-primary",
"@panel-primary-heading-bg": "@brand-primary",
"@panel-success-text": "@state-success-text",
"@panel-success-border": "@state-success-border",
"@panel-success-heading-bg": "@state-success-bg",
"@panel-info-text": "@state-info-text",
"@panel-info-border": "@state-info-border",
"@panel-info-heading-bg": "@state-info-bg",
"@panel-warning-text": "@state-warning-text",
"@panel-warning-border": "@state-warning-border",
"@panel-warning-heading-bg": "@state-warning-bg",
"@panel-danger-text": "@state-danger-text",
"@panel-danger-border": "@state-danger-border",
"@panel-danger-heading-bg": "@state-danger-bg",
"@thumbnail-padding": "4px",
"@thumbnail-bg": "@body-bg",
"@thumbnail-border": "#ddd",
"@thumbnail-border-radius": "@border-radius-base",
"@thumbnail-caption-color": "@text-color",
"@thumbnail-caption-padding": "9px",
"@well-bg": "#f5f5f5",
"@well-border": "darken(@well-bg, 7%)",
"@badge-color": "#fff",
"@badge-link-hover-color": "#fff",
"@badge-bg": "@gray-light",
"@badge-active-color": "@link-color",
"@badge-active-bg": "#fff",
"@badge-font-weight": "bold",
"@badge-line-height": "1",
"@badge-border-radius": "10px",
"@breadcrumb-padding-vertical": "8px",
"@breadcrumb-padding-horizontal": "15px",
"@breadcrumb-bg": "#f5f5f5",
"@breadcrumb-color": "#ccc",
"@breadcrumb-active-color": "@gray-light",
"@breadcrumb-separator": "\"/\"",
"@carousel-text-shadow": "0 1px 2px rgba(0,0,0,.6)",
"@carousel-control-color": "#fff",
"@carousel-control-width": "15%",
"@carousel-control-opacity": ".5",
"@carousel-control-font-size": "20px",
"@carousel-indicator-active-bg": "#fff",
"@carousel-indicator-border-color": "#fff",
"@carousel-caption-color": "#fff",
"@close-font-weight": "bold",
"@close-color": "#000",
"@close-text-shadow": "0 1px 0 #fff",
"@code-color": "#c7254e",
"@code-bg": "#f9f2f4",
"@kbd-color": "#fff",
"@kbd-bg": "#333",
"@pre-bg": "#f5f5f5",
"@pre-color": "@gray-dark",
"@pre-border-color": "#ccc",
"@pre-scrollable-max-height": "340px",
"@component-offset-horizontal": "180px",
"@text-muted": "@gray-light",
"@abbr-border-color": "@gray-light",
"@headings-small-color": "@gray-light",
"@blockquote-small-color": "@gray-light",
"@blockquote-font-size": "(@font-size-base * 1.25)",
"@blockquote-border-color": "@gray-lighter",
"@page-header-border-color": "@gray-lighter",
"@dl-horizontal-offset": "@component-offset-horizontal",
"@hr-border": "@gray-lighter"
},
"css": [
"type.less",
"grid.less",
"tables.less",
"forms.less",
"buttons.less",
"responsive-utilities.less",
"button-groups.less",
"input-groups.less",
"navs.less",
"labels.less",
"alerts.less",
"progress-bars.less",
"list-group.less"
],
"js": [],
"customizerUrl": "http://getbootstrap.com/customize/?id=524bae2807d3b239cf66"
}

85
public/css/animation.css Normal file
View File

@ -0,0 +1,85 @@
/*
Animation example, for spinners
*/
.animate-spin {
-moz-animation: spin 2s infinite linear;
-o-animation: spin 2s infinite linear;
-webkit-animation: spin 2s infinite linear;
animation: spin 2s infinite linear;
display: inline-block;
}
@-moz-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@-webkit-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@-o-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@-ms-keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes spin {
0% {
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
-o-transform: rotate(359deg);
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}

596
public/css/bootstrap-theme.css vendored Normal file
View File

@ -0,0 +1,596 @@
/*!
* Bootstrap v3.3.5 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
/*!
* Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=524bae2807d3b239cf66)
* Config saved to config.json and https://gist.github.com/524bae2807d3b239cf66
*/
/*!
* Bootstrap v3.3.5 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
.btn-default,
.btn-primary,
.btn-success,
.btn-info,
.btn-warning,
.btn-danger {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
}
.btn-default:active,
.btn-primary:active,
.btn-success:active,
.btn-info:active,
.btn-warning:active,
.btn-danger:active,
.btn-default.active,
.btn-primary.active,
.btn-success.active,
.btn-info.active,
.btn-warning.active,
.btn-danger.active {
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
.btn-default.disabled,
.btn-primary.disabled,
.btn-success.disabled,
.btn-info.disabled,
.btn-warning.disabled,
.btn-danger.disabled,
.btn-default[disabled],
.btn-primary[disabled],
.btn-success[disabled],
.btn-info[disabled],
.btn-warning[disabled],
.btn-danger[disabled],
fieldset[disabled] .btn-default,
fieldset[disabled] .btn-primary,
fieldset[disabled] .btn-success,
fieldset[disabled] .btn-info,
fieldset[disabled] .btn-warning,
fieldset[disabled] .btn-danger {
-webkit-box-shadow: none;
box-shadow: none;
}
.btn-default .badge,
.btn-primary .badge,
.btn-success .badge,
.btn-info .badge,
.btn-warning .badge,
.btn-danger .badge {
text-shadow: none;
}
.btn:active,
.btn.active {
background-image: none;
}
.btn-default {
background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);
background-image: -o-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#e0e0e0));
background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #dbdbdb;
text-shadow: 0 1px 0 #fff;
border-color: #ccc;
}
.btn-default:hover,
.btn-default:focus {
background-color: #e0e0e0;
background-position: 0 -15px;
}
.btn-default:active,
.btn-default.active {
background-color: #e0e0e0;
border-color: #dbdbdb;
}
.btn-default.disabled,
.btn-default[disabled],
fieldset[disabled] .btn-default,
.btn-default.disabled:hover,
.btn-default[disabled]:hover,
fieldset[disabled] .btn-default:hover,
.btn-default.disabled:focus,
.btn-default[disabled]:focus,
fieldset[disabled] .btn-default:focus,
.btn-default.disabled.focus,
.btn-default[disabled].focus,
fieldset[disabled] .btn-default.focus,
.btn-default.disabled:active,
.btn-default[disabled]:active,
fieldset[disabled] .btn-default:active,
.btn-default.disabled.active,
.btn-default[disabled].active,
fieldset[disabled] .btn-default.active {
background-color: #e0e0e0;
background-image: none;
}
.btn-primary {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #245580;
}
.btn-primary:hover,
.btn-primary:focus {
background-color: #265a88;
background-position: 0 -15px;
}
.btn-primary:active,
.btn-primary.active {
background-color: #265a88;
border-color: #245580;
}
.btn-primary.disabled,
.btn-primary[disabled],
fieldset[disabled] .btn-primary,
.btn-primary.disabled:hover,
.btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover,
.btn-primary.disabled:focus,
.btn-primary[disabled]:focus,
fieldset[disabled] .btn-primary:focus,
.btn-primary.disabled.focus,
.btn-primary[disabled].focus,
fieldset[disabled] .btn-primary.focus,
.btn-primary.disabled:active,
.btn-primary[disabled]:active,
fieldset[disabled] .btn-primary:active,
.btn-primary.disabled.active,
.btn-primary[disabled].active,
fieldset[disabled] .btn-primary.active {
background-color: #265a88;
background-image: none;
}
.btn-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #3e8f3e;
}
.btn-success:hover,
.btn-success:focus {
background-color: #419641;
background-position: 0 -15px;
}
.btn-success:active,
.btn-success.active {
background-color: #419641;
border-color: #3e8f3e;
}
.btn-success.disabled,
.btn-success[disabled],
fieldset[disabled] .btn-success,
.btn-success.disabled:hover,
.btn-success[disabled]:hover,
fieldset[disabled] .btn-success:hover,
.btn-success.disabled:focus,
.btn-success[disabled]:focus,
fieldset[disabled] .btn-success:focus,
.btn-success.disabled.focus,
.btn-success[disabled].focus,
fieldset[disabled] .btn-success.focus,
.btn-success.disabled:active,
.btn-success[disabled]:active,
fieldset[disabled] .btn-success:active,
.btn-success.disabled.active,
.btn-success[disabled].active,
fieldset[disabled] .btn-success.active {
background-color: #419641;
background-image: none;
}
.btn-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #28a4c9;
}
.btn-info:hover,
.btn-info:focus {
background-color: #2aabd2;
background-position: 0 -15px;
}
.btn-info:active,
.btn-info.active {
background-color: #2aabd2;
border-color: #28a4c9;
}
.btn-info.disabled,
.btn-info[disabled],
fieldset[disabled] .btn-info,
.btn-info.disabled:hover,
.btn-info[disabled]:hover,
fieldset[disabled] .btn-info:hover,
.btn-info.disabled:focus,
.btn-info[disabled]:focus,
fieldset[disabled] .btn-info:focus,
.btn-info.disabled.focus,
.btn-info[disabled].focus,
fieldset[disabled] .btn-info.focus,
.btn-info.disabled:active,
.btn-info[disabled]:active,
fieldset[disabled] .btn-info:active,
.btn-info.disabled.active,
.btn-info[disabled].active,
fieldset[disabled] .btn-info.active {
background-color: #2aabd2;
background-image: none;
}
.btn-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #e38d13;
}
.btn-warning:hover,
.btn-warning:focus {
background-color: #eb9316;
background-position: 0 -15px;
}
.btn-warning:active,
.btn-warning.active {
background-color: #eb9316;
border-color: #e38d13;
}
.btn-warning.disabled,
.btn-warning[disabled],
fieldset[disabled] .btn-warning,
.btn-warning.disabled:hover,
.btn-warning[disabled]:hover,
fieldset[disabled] .btn-warning:hover,
.btn-warning.disabled:focus,
.btn-warning[disabled]:focus,
fieldset[disabled] .btn-warning:focus,
.btn-warning.disabled.focus,
.btn-warning[disabled].focus,
fieldset[disabled] .btn-warning.focus,
.btn-warning.disabled:active,
.btn-warning[disabled]:active,
fieldset[disabled] .btn-warning:active,
.btn-warning.disabled.active,
.btn-warning[disabled].active,
fieldset[disabled] .btn-warning.active {
background-color: #eb9316;
background-image: none;
}
.btn-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #b92c28;
}
.btn-danger:hover,
.btn-danger:focus {
background-color: #c12e2a;
background-position: 0 -15px;
}
.btn-danger:active,
.btn-danger.active {
background-color: #c12e2a;
border-color: #b92c28;
}
.btn-danger.disabled,
.btn-danger[disabled],
fieldset[disabled] .btn-danger,
.btn-danger.disabled:hover,
.btn-danger[disabled]:hover,
fieldset[disabled] .btn-danger:hover,
.btn-danger.disabled:focus,
.btn-danger[disabled]:focus,
fieldset[disabled] .btn-danger:focus,
.btn-danger.disabled.focus,
.btn-danger[disabled].focus,
fieldset[disabled] .btn-danger.focus,
.btn-danger.disabled:active,
.btn-danger[disabled]:active,
fieldset[disabled] .btn-danger:active,
.btn-danger.disabled.active,
.btn-danger[disabled].active,
fieldset[disabled] .btn-danger.active {
background-color: #c12e2a;
background-image: none;
}
.thumbnail,
.img-thumbnail {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
}
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-color: #e8e8e8;
}
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-color: #2e6da4;
}
.navbar-default {
background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#f8f8f8));
background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
}
.navbar-default .navbar-nav > .open > a,
.navbar-default .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
}
.navbar-brand,
.navbar-nav > li > a {
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
}
.navbar-inverse {
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%);
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222222 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222222));
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
border-radius: 4px;
}
.navbar-inverse .navbar-nav > .open > a,
.navbar-inverse .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
}
.navbar-inverse .navbar-brand,
.navbar-inverse .navbar-nav > li > a {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.navbar-static-top,
.navbar-fixed-top,
.navbar-fixed-bottom {
border-radius: 0;
}
@media (max-width: 767px) {
.navbar .navbar-nav .open .dropdown-menu > .active > a,
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
color: #fff;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
}
}
.alert {
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.alert-success {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
border-color: #b2dba1;
}
.alert-info {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
border-color: #9acfea;
}
.alert-warning {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
border-color: #f5e79e;
}
.alert-danger {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
border-color: #dca7a7;
}
.progress {
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
}
.progress-bar {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
}
.progress-bar-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
}
.progress-bar-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
}
.progress-bar-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
}
.progress-bar-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
}
.progress-bar-striped {
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
.list-group {
border-radius: 4px;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
}
.list-group-item.active,
.list-group-item.active:hover,
.list-group-item.active:focus {
text-shadow: 0 -1px 0 #286090;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
border-color: #2b669a;
}
.list-group-item.active .badge,
.list-group-item.active:hover .badge,
.list-group-item.active:focus .badge {
text-shadow: none;
}
.panel {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.panel-default > .panel-heading {
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
}
.panel-primary > .panel-heading {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
}
.panel-success > .panel-heading {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
}
.panel-info > .panel-heading {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
}
.panel-warning > .panel-heading {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
}
.panel-danger > .panel-heading {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
}
.well {
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
border-color: #dcdcdc;
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
}

14
public/css/bootstrap-theme.min.css vendored Normal file

File diff suppressed because one or more lines are too long

3825
public/css/bootstrap.css vendored Normal file

File diff suppressed because it is too large Load Diff

14
public/css/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

169
public/css/cover.css Normal file
View File

@ -0,0 +1,169 @@
/*
* Globals
*/
/* Links */
a,
a:focus,
a:hover {
color: #888;
}
/* Custom default button */
.btn-default,
.btn-default:hover,
.btn-default:focus {
color: #333;
text-shadow: none; /* Prevent inheritence from `body` */
background-color: #fff;
border: 1px solid #fff;
}
/*
* Base structure
*/
html,
body {
height: 100%;
}
body {
color: #888;
text-align: center;
text-shadow: 0 1px 3px rgba(0,0,0,.3);
}
/* Extra markup and styles for table-esque vertical and horizontal centering */
.site-wrapper {
display: table;
width: 100%;
height: 100%; /* For at least Firefox */
min-height: 100%;
/*-webkit-box-shadow: inset 0 0 100px rgba(0,0,0,.5);
box-shadow: inset 0 0 100px rgba(0,0,0,.5);*/
}
.site-wrapper-inner {
display: table-cell;
vertical-align: top;
}
.cover-container {
margin-right: auto;
margin-left: auto;
}
/* Padding for spacing */
.inner {
padding: 30px;
}
/*
* Header
*/
.masthead-brand {
margin-top: 10px;
margin-bottom: 10px;
}
.masthead-nav > li {
display: inline-block;
}
.masthead-nav > li + li {
margin-left: 20px;
}
.masthead-nav > li > a {
padding-right: 0;
padding-left: 0;
font-size: 16px;
font-weight: bold;
color: #888; /* IE8 proofing */
color: rgba(136,136,136,.75);
border-bottom: 2px solid transparent;
text-shadow: 0 1px 3px rgba(0,0,0,.2);
}
.masthead-nav > li > a:hover,
.masthead-nav > li > a:focus {
background-color: transparent;
border-bottom-color: #a9a9a9;
border-bottom-color: rgba(136,136,136,.25);
}
.masthead-nav > .active > a,
.masthead-nav > .active > a:hover,
.masthead-nav > .active > a:focus {
color: #888;
border-bottom-color: #888;
}
@media (min-width: 768px) {
.masthead-brand {
float: left;
}
.masthead-nav {
float: right;
}
}
/*
* Cover
*/
.cover {
padding: 0 20px;
}
.cover .btn-lg {
padding: 10px 20px;
font-weight: bold;
}
/*
* Footer
*/
.mastfoot {
color: #888; /* IE8 proofing */
color: rgba(136,136,136,.5);
}
/*
* Affix and center
*/
@media (min-width: 768px) {
.cover {
margin-top: 90px;
}
.cover.render {
margin-top: 0px;
}
/* Pull out the header and footer */
.masthead {
position: fixed;
top: 0;
}
.mastfoot {
position: fixed;
bottom: 0;
}
/* Start the vertical centering */
.site-wrapper-inner {
vertical-align: middle;
}
/* Handle the widths */
.masthead,
.mastfoot,
.cover-container {
width: 100%; /* Must be percentage or pixels for horizontal alignment */
}
}
@media (min-width: 992px) {
.masthead,
.mastfoot,
.cover-container {
width: 90%;
}
}

4
public/css/fontello-codes.css vendored Normal file
View File

@ -0,0 +1,4 @@
.icon-download:before { content: '\e800'; } /* '' */
.icon-clipboard:before { content: '\e801'; } /* '' */
.icon-trash:before { content: '\e802'; } /* '' */

57
public/css/fontello-embedded.css vendored Normal file

File diff suppressed because one or more lines are too long

4
public/css/fontello-ie7-codes.css vendored Normal file
View File

@ -0,0 +1,4 @@
.icon-download { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe800;&nbsp;'); }
.icon-clipboard { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe801;&nbsp;'); }
.icon-trash { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }

15
public/css/fontello-ie7.css vendored Normal file
View File

@ -0,0 +1,15 @@
[class^="icon-"], [class*=" icon-"] {
font-family: 'fontello';
font-style: normal;
font-weight: normal;
/* fix buttons height */
line-height: 1em;
/* you can be more comfortable with increased icons size */
/* font-size: 120%; */
}
.icon-download { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe800;&nbsp;'); }
.icon-clipboard { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe801;&nbsp;'); }
.icon-trash { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }

59
public/css/fontello.css vendored Normal file
View File

@ -0,0 +1,59 @@
@font-face {
font-family: 'fontello';
src: url('../font/fontello.eot?93658459');
src: url('../font/fontello.eot?93658459#iefix') format('embedded-opentype'),
url('../font/fontello.woff?93658459') format('woff'),
url('../font/fontello.ttf?93658459') format('truetype'),
url('../font/fontello.svg?93658459#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
/*
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'fontello';
src: url('../font/fontello.svg?93658459#fontello') format('svg');
}
}
*/
[class^="icon-"]:before, [class*=" icon-"]:before {
font-family: "fontello";
font-style: normal;
font-weight: normal;
speak: none;
display: inline-block;
text-decoration: inherit;
width: 1em;
margin-right: .2em;
text-align: center;
/* opacity: .8; */
/* For safety - reset parent styles, that can break glyph codes*/
font-variant: normal;
text-transform: none;
/* fix buttons height, for twitter bootstrap */
line-height: 1em;
/* Animation center compensation - margins should be symmetric */
/* remove if not needed */
margin-left: .2em;
/* you can be more comfortable with increased icons size */
/* font-size: 120%; */
/* Font smoothing. That was taken from TWBS */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* Uncomment for 3D effect */
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
}
.icon-download:before { content: '\e800'; } /* '' */
.icon-clipboard:before { content: '\e801'; } /* '' */
.icon-trash:before { content: '\e802'; } /* '' */

28
public/css/lufi.css Normal file
View File

@ -0,0 +1,28 @@
#files {
border: 2px dashed #BBB;
border-radius: 5px;
padding: 25px 25px 41px 25px;
text-align: center;
color: #888;
}
#files label {
background-color: #5A7BC2;
padding: 6px 0;
color: #FFF;
font-weight: bold;
margin: 20px auto;
width: 100%;
}
#files label span {
cursor: pointer;
}
#files input {
display:none;
}
.progress-info {
text-shadow: initial;
font-weight: bold;
}
#results {
display: none;
}

30
public/font/LICENSE.txt Normal file
View File

@ -0,0 +1,30 @@
Font license info
## Font Awesome
Copyright (C) 2012 by Dave Gandy
Author: Dave Gandy
License: SIL ()
Homepage: http://fortawesome.github.com/Font-Awesome/
## Elusive
Copyright (C) 2013 by Aristeides Stathopoulos
Author: Aristeides Stathopoulos
License: SIL (http://scripts.sil.org/OFL)
Homepage: http://aristeides.com/
## Linecons
Copyright (C) 2013 by Designmodo
Author: Designmodo for Smashing Magazine
License: CC BY ()
Homepage: http://designmodo.com/linecons-free/

BIN
public/font/fontello.eot Normal file

Binary file not shown.

14
public/font/fontello.svg Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Copyright (C) 2015 by original authors @ fontello.com</metadata>
<defs>
<font id="fontello" horiz-adv-x="1000" >
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
<missing-glyph horiz-adv-x="1000" />
<glyph glyph-name="download" unicode="&#xe800;" d="m714 100q0 15-10 25t-25 11-26-11-10-25 10-25 26-11 25 11 10 25z m143 0q0 15-10 25t-26 11-25-11-10-25 10-25 25-11 26 11 10 25z m72 125v-179q0-22-16-37t-38-16h-821q-23 0-38 16t-16 37v179q0 22 16 38t38 16h259l75-76q33-32 76-32t76 32l76 76h259q22 0 38-16t16-38z m-182 318q10-23-8-40l-250-250q-10-10-25-10t-25 10l-250 250q-17 17-8 40 10 21 33 21h143v250q0 15 11 25t25 11h143q14 0 25-11t10-25v-250h143q24 0 33-21z" horiz-adv-x="928.6" />
<glyph glyph-name="clipboard" unicode="&#xe801;" d="m0-150l0 904 225 0 0-64-161 0 0-774 579 0 0 774-161 0 0 64 225 0 0-904-707 0z m129 129l0 31 31 0 0-31-31 0z m0 121l0 31 31 0 0-31-31 0z m0 121l0 31 31 0 0-31-31 0z m0 121l0 32 31 0 0-32-31 0z m0 121l0 32 31 0 0-32-31 0z m0 96l0 94 129 0 0 97q0 41 27 71t69 29 69-30 28-70q0-56-2-97l129 0 0-94-449 0z m96-582l0 33 353 0 0-33-353 0z m0 121l0 33 353 0 0-33-353 0z m0 121l0 33 353 0 0-33-353 0z m0 121l0 34 353 0 0-34-353 0z m0 121l0 34 353 0 0-34-353 0z m97 260q0-14 9-22t23-9 22 9 9 22-9 24-22 9-23-9-9-24z" horiz-adv-x="707" />
<glyph glyph-name="trash" unicode="&#xe802;" d="m0 569l0 68q2 37 29 63t65 25l94 0 0 31q0 39 27 67t66 27l313 0q39 0 66-27t28-67l0-31 93 0q37 0 65-25t29-63l0-68q0-26-19-44t-44-19l0-531q0-53-36-89t-88-36l-500 0q-53 0-89 36t-36 89l0 531q-26 0-44 19t-19 44z m63 0l749 0 0 62q0 14-8 23t-23 8l-687 0q-14 0-23-8t-8-23l0-62z m62-594q0-25 19-44t44-19l500 0q25 0 43 19t19 44l0 531-625 0 0-531z m63 31l0 407q0 13 8 22t23 9l62 0q14 0 23-9t9-22l0-407q0-13-9-22t-23-9l-62 0q-14 0-23 9t-8 22z m31 0l62 0 0 407-62 0 0-407z m31 719l375 0 0 31q0 14-9 23t-22 8l-313 0q-13 0-22-8t-9-23l0-31z m125-719l0 407q0 13 9 22t22 9l63 0q13 0 22-9t9-22l0-407q0-13-9-22t-22-9l-63 0q-13 0-22 9t-9 22z m31 0l63 0 0 407-63 0 0-407z m157 0l0 407q0 13 8 22t23 9l62 0q14 0 23-9t9-22l0-407q0-13-9-22t-23-9l-62 0q-14 0-23 9t-8 22z m31 0l62 0 0 407-62 0 0-407z" horiz-adv-x="875" />
</font>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
public/font/fontello.ttf Normal file

Binary file not shown.

BIN
public/font/fontello.woff Normal file

Binary file not shown.

View File

@ -0,0 +1,28 @@
{
"name": "fontello",
"css_prefix_text": "icon-",
"css_use_suffix": false,
"hinting": true,
"units_per_em": 1000,
"ascent": 850,
"glyphs": [
{
"uid": "9a76bc135eac17d2c8b8ad4a5774fc87",
"css": "download",
"code": 59392,
"src": "fontawesome"
},
{
"uid": "c9bef3dc67fea47e94c4a5030ea64dad",
"css": "clipboard",
"code": 59393,
"src": "elusive"
},
{
"uid": "2cc73cc335f054682c04fe63439c4dcb",
"css": "trash",
"code": 59394,
"src": "linecons"
}
]
}

BIN
public/img/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
public/img/lufi-1-5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
public/img/lufi-2-5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
public/img/lufi-3-5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
public/img/lufi-4-5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
public/img/lufi-5-5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
public/img/lufi-min.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 B

BIN
public/img/lufi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

93
public/img/lufi.svg Normal file
View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="282.70126"
height="282.53867"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="lufi.svg"
inkscape:export-filename="/home/luc/PERSONNEL/fiat-tux_designs/lufi.png"
inkscape:export-xdpi="271"
inkscape:export-ydpi="271">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="26.011995"
inkscape:cy="84.402272"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1039"
inkscape:window-x="0"
inkscape:window-y="41"
inkscape:window-maximized="0"
fit-margin-top="10"
fit-margin-left="10"
fit-margin-right="10"
fit-margin-bottom="10" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-221.44765,-362.7203)">
<g
id="g2994"
transform="matrix(1.0020558,-1.0020558,1.0020558,1.0020558,-490.60541,247.60842)"
inkscape:export-filename="/home/luc/tmp/lufi.png"
inkscape:export-xdpi="271.87875"
inkscape:export-ydpi="271.87875">
<path
inkscape:connector-curvature="0"
id="path3875"
d="m 214.90995,548.04352 -38.63833,1.01016 -2.27284,6.56599 3.03045,3.03046 41.92133,0.75761 157.07873,1.01015 40.15356,0 5.3033,-5.3033 -2.77792,-6.81853 -19.69797,-1.01015 -20.20306,0.25254 -2.77791,-15.40483 -6.81853,-16.92005 -14.39468,-17.93021 -13.63706,-12.62691 -13.13198,-6.56599 -18.18275,-5.3033 -22.72843,1.26269 -21.2132,4.79822 -13.38452,5.55584 -13.13199,10.85914 -10.6066,13.38452 -7.3236,9.34392 -3.78808,10.35406 -3.28299,12.87944 z"
style="fill:#ffd119;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<rect
ry="7.3214264"
y="522.78973"
x="216.17265"
height="24.243732"
width="159.85664"
id="rect3873"
style="fill:#ff000a;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<g
transform="translate(-17.800222,72.844826)"
id="g3074">
<path
sodipodi:nodetypes="ssssssssccccssssssssscsscccssccsssscssssssssssssssssssssssssss"
inkscape:connector-curvature="0"
id="path3076"
d="m 191.54148,487.67039 c -5.11077,-4.02013 -3.76609,-12.77744 2.28591,-14.88718 1.90113,-0.66273 9.86788,-1.13532 19.13896,-1.13532 11.85861,0 15.96787,-0.31667 16.22051,-1.25 0.18609,-0.6875 0.81278,-3.78781 1.39265,-6.88958 3.71296,-19.86087 19.21745,-41.97257 37.20623,-53.06158 24.32865,-14.99715 56.89702,-16.92672 82.60964,-4.89433 23.5152,11.00409 42.6905,34.14334 48.04833,57.98096 l 1.78265,7.93123 18.36719,0.34165 17.1045,0.34165 c 9.45192,1.08409 8.2901,17.0871 0.24154,17.5 l -120.94207,0 c -118.96911,0 -120.98307,-0.0323 -123.45604,-1.9775 z m 243.67283,-3.59393 c 2.51368,-0.82861 3.43825,-3.94122 0.37143,-6.22857 -2.52906,-1.88627 -29.03663,-1.2 -120.46482,-1.2 -116.89017,0 -119.28542,0.0385 -120.3,1.93426 -1.4064,2.62788 -1.30648,3.65158 0.53625,5.49431 1.41087,1.41087 13.6645,1.57143 119.92857,1.57143 106.26407,0 116.73199,-0.5177 119.92857,-1.57143 z m -42.52164,-13.34989 c 1.1562,-0.73196 1.21003,-1.8711 0.30207,-6.39217 -0.60767,-3.02582 -1.78032,-7.11812 -2.6059,-9.094 l -1.50105,-3.59251 -74.5376,0 -74.53761,0 -1.4229,3.40548 c -1.94511,4.65531 -3.87987,14.33903 -3.11283,15.58013 0.79688,1.28938 155.38177,1.38078 157.41582,0.0931 z m -25.90693,-24.39153 c 0,-1.24672 -5.7033,-11.73239 -7.62234,-14.01388 -2.88837,-3.43389 -3.05342,-5.67327 -0.41813,-5.67327 4.38551,0 11.74148,11.74497 10.68001,17.05232 l -0.58954,2.94768 7.975,0 c 5.74935,0 7.975,-0.35183 7.975,-1.26068 0,-2.26743 -8.58166,-14.10933 -14.6056,-20.15437 -15.05404,-15.10678 -33.86286,-22.60595 -56.60224,-22.56759 -22.08745,0.0373 -39.40142,6.87913 -54.36384,21.48264 -6.94616,6.77954 -15.42832,18.41013 -15.42832,21.15504 0,1.11203 10.65076,1.34496 61.5,1.34496 33.825,0 61.5,-0.14078 61.5,-0.31285 z m -93.59158,-3.06521 c -0.66235,-1.72604 3.9076,-10.69297 6.76967,-13.28311 1.63365,-1.47843 2.06875,-1.51459 3.27243,-0.27199 1.18957,1.22804 0.82283,2.36345 -2.56242,7.93316 -4.00121,6.58312 -6.41472,8.39719 -7.47968,5.62194 z m 74.8173,0.12806 c -2.02408,-2.4825 -3.63934,-9.73638 -2.43601,-10.93971 1.64641,-1.64641 3.95916,0.61949 5.34149,5.23327 1.63547,5.45873 -0.18778,9.03966 -2.90548,5.70644 z m -87.8736,-3.27477 c -1.08443,-2.82599 9.43089,-16.47523 12.69246,-16.47523 3.51737,0 3.02283,2.00661 -1.97445,8.01145 -2.76577,3.32341 -5.50383,6.93041 -6.08458,8.01556 -1.25494,2.34487 -3.81074,2.59211 -4.63343,0.44822 z m 27.06887,0.17947 c -0.66129,-1.72328 2.00067,-6.21879 4.17686,-7.05387 2.19279,-0.84146 2.95868,2.31694 1.28494,5.29892 -1.67568,2.98543 -4.62586,3.93336 -5.4618,1.75495 z"
style="fill:#000000;fill-opacity:1" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
public/img/lufi.xcf Normal file

Binary file not shown.

BIN
public/img/lufi120.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
public/img/lufi128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
public/img/lufi152.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
public/img/lufi196.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
public/img/lufi60.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
public/img/lufi76.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
public/img/lufi_favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

93
public/js/lufi-down.js Normal file
View File

@ -0,0 +1,93 @@
// vim:set sw=4 ts=4 sts=4 ft=javascript expandtab:
/*
* Return the deciphering key stored in anchor part of the URL
* Stolen from https://github.com/sebsauvage/ZeroBin/blob/master/js/zerobin.js
*/
function pageKey() {
var key = window.location.hash.substring(1); // Get key
// Some stupid web 2.0 services and redirectors add data AFTER the anchor
// (such as &utm_source=...).
// We will strip any additional data.
// First, strip everything after the equal sign (=) which signals end of base64 string.
i = key.indexOf('='); if (i>-1) { key = key.substring(0,i+1); }
// If the equal sign was not present, some parameters may remain:
i = key.indexOf('&'); if (i>-1) { key = key.substring(0,i); }
// Then add trailing equal sign if it's missing
if (key.charAt(key.length-1)!=='=') key+='=';
return key;
}
function base64ToArrayBuffer(base64) {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array( len );
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}
// Spawn WebSocket
function spawnWebsocket() {
var ws = new WebSocket(ws_url);
ws.onopen = function() {
console.log('Connection is established!');
ws.send('{"part":0}');
};
ws.onclose = function() {
console.log('Connection is closed');
}
ws.onmessage = function(e) {
var res = e.data.split('XXMOJOXX');
var json = res.shift();
var data = JSON.parse(json);
if (data.msg !== undefined) {
document.getElementById('please-wait').remove();
var pbd = document.getElementById('pbd');
pbd.setAttribute('class', 'alert alert-danger');
pbd.setAttribute('role', 'alert');
pbd.innerHTML = '<p>'+data.msg+'</p>';
} else {
var slice = JSON.parse(res.shift());
var percent = Math.round(100 * (data.part + 1)/data.total);
var pb = document.getElementById('pb');
pb.style.width = percent+'%';
pb.setAttribute('aria-valuenow', percent);
document.getElementById('pbt').innerHTML = percent+'%';
window.a.push(base64ToArrayBuffer(sjcl.decrypt(window.key, slice)));
if (data.part + 1 === data.total) {
var blob = new File(a, data.name, {type: data.type});
document.getElementById('please-wait').remove();
var pbd = document.getElementById('pbd');
pbd.setAttribute('class', '');
pbd.innerHTML = '<a href="'+URL.createObjectURL(blob)+'" class="btn btn-primary" download="'+data.name+'">Download</a>';
ws.send('{"ended":true}');
} else {
if (ws.readyState === 3) {
ws = spawnWebsocket();
}
ws.send('{"part":'+(data.part + 1)+'}');
}
}
}
ws.onerror = function() {
console.log('error');
}
}
// When it's ready
document.addEventListener('DOMContentLoaded', function() {
window.a = new Array();
window.key = pageKey();
// Set websocket
ws = spawnWebsocket();
});

279
public/js/lufi-up.js Normal file
View File

@ -0,0 +1,279 @@
// vim:set sw=4 ts=4 sts=4 ft=javascript expandtab:
function copyToClipboard(el) {
el = el.previousSibling;
var textArea = document.createElement('textarea');
textArea.style.position = 'fixed';
textArea.style.top = 0;
textArea.style.left = 0;
textArea.style.width = '2em';
textArea.style.height = '2em';
textArea.style.padding = 0;
textArea.style.border = 'none';
textArea.style.outline = 'none';
textArea.style.boxShadow = 'none';
textArea.style.background = 'transparent';
textArea.value = el.value;
document.body.appendChild(textArea);
textArea.select();
try {
var successful = document.execCommand('copy');
var msg = successful ? 'successful' : 'unsuccessful';
console.log('Copying text command was ' + msg);
} catch (err) {
el.focus();
var len = el.value.length * 2;
el.setSelectionRange(0, len);
alert(hit);
}
document.body.removeChild(textArea);
}
function copyAllToClipboard() {
var text = new Array();
document.getElementByClassName('link-input').forEach(function(e, index, array) {
text.push(e.value);
});
var textArea = document.createElement('textarea');
textArea.style.position = 'fixed';
textArea.style.top = 0;
textArea.style.left = 0;
textArea.style.width = '2em';
textArea.style.height = '2em';
textArea.style.padding = 0;
textArea.style.border = 'none';
textArea.style.outline = 'none';
textArea.style.boxShadow = 'none';
textArea.style.background = 'transparent';
textArea.value = text.join("\n");
document.body.appendChild(textArea);
textArea.select();
try {
var successful = document.execCommand('copy');
var msg = successful ? 'successful' : 'unsuccessful';
console.log('Copying text command was ' + msg);
} catch (err) {
textArea.style.width = '';
textArea.style.height = '';
textArea.style.background = '#FFFFFF';
alert(hits);
}
document.body.removeChild(textArea);
}
function addItem(name, url, size, del_at_first_view, created_at, delay, short, token) {
var files = localStorage.getItem('files');
if (files === null) {
files = new Array();
} else {
files = JSON.parse(files);
}
files.push({ name: name, short: short, url: url, size: size, del_at_first_view: del_at_first_view, created_at: created_at, delay: delay, token: token });
localStorage.setItem('files', JSON.stringify(files));
}
// Start uploading the files (called from <input> and from drop zone)
function handleFiles(f) {
window.files = f;
var r = document.getElementById('results');
r.style.display = 'block';
var delay = document.getElementById('delete-day');
var del_at_first_view = document.getElementById('first-view');
delay.setAttribute('disabled', 'disabled');
del_at_first_view.setAttribute('disabled', 'disabled');
uploadFile(0, delay.value, del_at_first_view.checked);
}
// Create progress bar and call slicing and uploading function
function uploadFile(i, delay, del_at_first_view) {
// Create a random key, different for all files
var randomkey = sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0);
// Get the file and properties
var file = window.files[i];
var name = file.name;
var parts = Math.ceil(file.size/window.sliceLength);
// Create a progress bar for the file
var r = document.getElementById('ul-results');
var w = document.createElement('li');
w.setAttribute('class', 'list-group-item');
w.innerHTML='<div><p>'+file.name+'</p></div><div class="progress"><div id="progress-'+i+'" style="width: 0%;" data-key="'+randomkey+'" data-name="'+file.name+'" aria-valuemax="100" aria-valuemin="0" aria-valuenow="0" role="progressbar" class="progress-bar"><span class="sr-only">'+file.name+'0%</span></div></div>';
r.appendChild(w);
sliceAndUpload(randomkey, i, parts, 0, delay, del_at_first_view, null);
}
// Get a slice of file and send it
function sliceAndUpload(randomkey, i, parts, j, delay, del_at_first_view, short) {
var file = window.files[i];
var slice = file.slice(j * window.sliceLength, (j + 1) * window.sliceLength, file.type);
var fr = new FileReader();
fr.onloadend = function() {
// Get the binary result
var bin = fr.result;
// Transform it in base64
var b = window.btoa(bin);
// Encrypt it
var encrypted = sjcl.encrypt(randomkey, b);
// Prepare json
var data = {
total: parts,
part: j,
size: file.size,
name: file.name,
type: file.type,
delay: delay,
del_at_first_view: del_at_first_view,
id: short,
i: i
};
data = JSON.stringify(data);
if (window.ws.readyState === 3) {
window.ws = spawnWebsocket(function() {
window.ws.send(data+'XXMOJOXX'+JSON.stringify(encrypted));
});
} else {
window.ws.send(data+'XXMOJOXX'+JSON.stringify(encrypted));
}
}
fr.readAsBinaryString(slice);
}
// Update the progress bar
function updateProgressBar(data) {
var i = data.i;
var j = data.j;
var parts = data.parts;
var short = data.short;
var del_at_first_view = data.del_at_first_view;
var created_at = data.created_at;
var delay = data.delay;
var dp = document.getElementById('progress-'+i);
var key = dp.getAttribute('data-key');
if (j + 1 === parts) {
var d = document.createElement('div');
var url = document.location.href.replace(/#$/, '')+'r/'+short+'#'+key;
var del_url = document.location.href.replace(/#$/, '')+'d/'+short+'/'+data.token;
d.innerHTML = '<div class="form-group"><label class="sr-only" for="'
+short
+'">'
+dltext
+'</label><div class="input-group"><div class="input-group-addon"><a href="'
+url
+'" target="_blank"><span class="icon icon-download" title="'
+dltext
+'"></span></a></div><input id="'
+short
+'" class="form-control link-input" value="'
+url
+'" readonly="" type="text" style="background-color: #FFF;"><a href="#" onclick="copyToClipboard(this);" class="input-group-addon" title="'
+cptext
+'"><span class="icon icon-clipboard"></span></a></div></div>'
+'<div class="form-group"><label class="sr-only" for="delete-'
+short
+'">'
+del_text
+'</label><div class="input-group"><div class="input-group-addon"><a href="'
+del_url
+'" target="_blank"><span class="icon icon-trash" title="'
+del_text
+'"></span></a></div><input id="delete-'
+short
+'" class="form-control" value="'
+del_url
+'" readonly="" type="text" style="background-color: #FFF;">';
var p2 = dp.parentNode;
var p1 = p2.parentNode;
p2.remove();
p1.appendChild(d);
addItem(data.name, url, data.size, del_at_first_view, created_at, delay, data.short, data.token);
i++;
if (i < window.files.length) {
uploadFile(i, delay, del_at_first_view);
} else {
document.getElementById('delete-day').removeAttribute('disabled');
document.getElementById('first-view').removeAttribute('disabled');
}
} else {
j++;
// Update progress bar
var percent = Math.round(100 * j/parts);
dp.style.width = percent+'%';
dp.setAttribute('aria-valuenow', percent);
// Encrypt and upload next slice
sliceAndUpload(key, i, parts, j, delay, del_at_first_view, short);
}
}
// Dropzone events functions
function handleDrop(evt) {
evt.stopPropagation();
evt.preventDefault();
var f = evt.dataTransfer.files; // FileList object
handleFiles(f);
}
function handleDragOver(evt) {
evt.stopPropagation();
evt.preventDefault();
evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
}
// Spawn websocket
function spawnWebsocket(callback) {
var ws = new WebSocket(ws_url);
ws.onopen = function() {
console.log('Connection is established!');
if (callback !== undefined) {
callback();
}
};
ws.onclose = function() {
console.log('Connection is closed.');
}
ws.onmessage = function(e) {
var data = JSON.parse(e.data);
if (data.success) {
updateProgressBar(data);
} else {
alert(data.msg);
}
}
ws.onerror = function() {
console.log('error');
}
return ws;
}
// When it's ready
document.addEventListener('DOMContentLoaded', function() {
// Dropzone events binding
var dropZone = document.getElementById('files');
dropZone.addEventListener('dragover', handleDragOver, false);
dropZone.addEventListener('drop', handleDrop, false);
// Set websocket
window.ws = spawnWebsocket();
// Use slice of 10MB
window.sliceLength = 10000000;
});

80
public/js/moment-with-locales.min.js vendored Normal file

File diff suppressed because one or more lines are too long

58
public/js/sjcl.js Normal file
View File

@ -0,0 +1,58 @@
"use strict";function q(a){throw a;}var s=void 0,u=!1;var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
"undefined"!==typeof module&&module.exports&&(module.exports=sjcl);"function"===typeof define&&define([],function(){return sjcl});
sjcl.cipher.aes=function(a){this.l[0][0][0]||this.G();var b,c,d,e,g=this.l[0][4],f=this.l[1];b=a.length;var h=1;4!==b&&(6!==b&&8!==b)&&q(new sjcl.exception.invalid("invalid aes key size"));this.b=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=g[c>>>24]<<24^g[c>>16&255]<<16^g[c>>8&255]<<8^g[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:f[0][g[c>>>24]]^f[1][g[c>>16&255]]^f[2][g[c>>8&255]]^f[3][g[c&
255]]};
sjcl.cipher.aes.prototype={encrypt:function(a){return w(this,a,0)},decrypt:function(a){return w(this,a,1)},l:[[[],[],[],[],[]],[[],[],[],[],[]]],G:function(){var a=this.l[0],b=this.l[1],c=a[4],d=b[4],e,g,f,h=[],l=[],k,m,n,p;for(e=0;0x100>e;e++)l[(h[e]=e<<1^283*(e>>7))^e]=e;for(g=f=0;!c[g];g^=k||1,f=l[f]||1){n=f^f<<1^f<<2^f<<3^f<<4;n=n>>8^n&255^99;c[g]=n;d[n]=g;m=h[e=h[k=h[g]]];p=0x1010101*m^0x10001*e^0x101*k^0x1010100*g;m=0x101*h[n]^0x1010100*n;for(e=0;4>e;e++)a[e][g]=m=m<<24^m>>>8,b[e][n]=p=p<<24^p>>>8}for(e=
0;5>e;e++)a[e]=a[e].slice(0),b[e]=b[e].slice(0)}};
function w(a,b,c){4!==b.length&&q(new sjcl.exception.invalid("invalid aes block size"));var d=a.b[c],e=b[0]^d[0],g=b[c?3:1]^d[1],f=b[2]^d[2];b=b[c?1:3]^d[3];var h,l,k,m=d.length/4-2,n,p=4,t=[0,0,0,0];h=a.l[c];a=h[0];var r=h[1],v=h[2],y=h[3],z=h[4];for(n=0;n<m;n++)h=a[e>>>24]^r[g>>16&255]^v[f>>8&255]^y[b&255]^d[p],l=a[g>>>24]^r[f>>16&255]^v[b>>8&255]^y[e&255]^d[p+1],k=a[f>>>24]^r[b>>16&255]^v[e>>8&255]^y[g&255]^d[p+2],b=a[b>>>24]^r[e>>16&255]^v[g>>8&255]^y[f&255]^d[p+3],p+=4,e=h,g=l,f=k;for(n=0;4>
n;n++)t[c?3&-n:n]=z[e>>>24]<<24^z[g>>16&255]<<16^z[f>>8&255]<<8^z[b&255]^d[p++],h=e,e=g,g=f,f=b,b=h;return t}
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.R(a.slice(b/32),32-(b&31)).slice(1);return c===s?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<<c)-1},concat:function(a,b){if(0===a.length||0===b.length)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return 32===d?a.concat(b):sjcl.bitArray.R(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;return 0===
b?0:32*(b-1)+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(32*a.length<b)return a;a=a.slice(0,Math.ceil(b/32));var c=a.length;b&=31;0<c&&b&&(a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return u;var c=0,d;for(d=0;d<a.length;d++)c|=a[d]^b[d];return 0===
c},R:function(a,b,c,d){var e;e=0;for(d===s&&(d=[]);32<=b;b-=32)d.push(c),c=0;if(0===b)return d.concat(a);for(e=0;e<a.length;e++)d.push(c|a[e]>>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32<b+a?c:d.pop(),1));return d},g:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]},byteswapM:function(a){var b,c;for(b=0;b<a.length;++b)c=a[b],a[b]=c>>>24|c>>>8&0xff00|(c&0xff00)<<8|c<<24;return a}};
sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d<c/8;d++)0===(d&3)&&(e=a[d/4]),b+=String.fromCharCode(e>>>24),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c<a.length;c++)d=d<<8|a.charCodeAt(c),3===(c&3)&&(b.push(d),d=0);c&3&&b.push(sjcl.bitArray.partial(8*(c&3),d));return b}};
sjcl.codec.hex={fromBits:function(a){var b="",c;for(c=0;c<a.length;c++)b+=((a[c]|0)+0xf00000000000).toString(16).substr(4);return b.substr(0,sjcl.bitArray.bitLength(a)/4)},toBits:function(a){var b,c=[],d;a=a.replace(/\s|0x/g,"");d=a.length;a+="00000000";for(b=0;b<a.length;b+=8)c.push(parseInt(a.substr(b,8),16)^0);return sjcl.bitArray.clamp(c,4*d)}};
sjcl.codec.base32={p:"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",O:"0123456789ABCDEFGHIJKLMNOPQRSTUV",BITS:32,BASE:5,REMAINING:27,fromBits:function(a,b,c){var d=sjcl.codec.base32.BASE,e=sjcl.codec.base32.REMAINING,g="",f=0,h=sjcl.codec.base32.p,l=0,k=sjcl.bitArray.bitLength(a);c&&(h=sjcl.codec.base32.O);for(c=0;g.length*d<k;)g+=h.charAt((l^a[c]>>>f)>>>e),f<d?(l=a[c]<<d-f,f+=e,c++):(l<<=d,f-=d);for(;g.length&7&&!b;)g+="=";return g},toBits:function(a,b){a=a.replace(/\s|=/g,"").toUpperCase();var c=sjcl.codec.base32.BITS,
d=sjcl.codec.base32.BASE,e=sjcl.codec.base32.REMAINING,g=[],f,h=0,l=sjcl.codec.base32.p,k=0,m,n="base32";b&&(l=sjcl.codec.base32.O,n="base32hex");for(f=0;f<a.length;f++){m=l.indexOf(a.charAt(f));if(0>m){if(!b)try{return sjcl.codec.base32hex.toBits(a)}catch(p){}q(new sjcl.exception.invalid("this isn't "+n+"!"))}h>e?(h-=e,g.push(k^m>>>h),k=m<<c-h):(h+=d,k^=m<<c-h)}h&56&&g.push(sjcl.bitArray.partial(h&56,k,1));return g}};
sjcl.codec.base32hex={fromBits:function(a,b){return sjcl.codec.base32.fromBits(a,b,1)},toBits:function(a){return sjcl.codec.base32.toBits(a,1)}};
sjcl.codec.base64={p:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,g=sjcl.codec.base64.p,f=0,h=sjcl.bitArray.bitLength(a);c&&(g=g.substr(0,62)+"-_");for(c=0;6*d.length<h;)d+=g.charAt((f^a[c]>>>e)>>>26),6>e?(f=a[c]<<6-e,e+=26,c++):(f<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,g=sjcl.codec.base64.p,f=0,h;b&&(g=g.substr(0,62)+"-_");for(d=0;d<a.length;d++)h=g.indexOf(a.charAt(d)),
0>h&&q(new sjcl.exception.invalid("this isn't base64!")),26<e?(e-=26,c.push(f^h>>>e),f=h<<32-e):(e+=6,f^=h<<32-e);e&56&&c.push(sjcl.bitArray.partial(e&56,f,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.b[0]||this.G();a?(this.s=a.s.slice(0),this.o=a.o.slice(0),this.i=a.i):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.s=this.P.slice(0);this.o=[];this.i=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.o=sjcl.bitArray.concat(this.o,a);b=this.i;a=this.i=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)x(this,c.splice(0,16));return this},finalize:function(){var a,b=this.o,c=this.s,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.i/
4294967296));for(b.push(this.i|0);b.length;)x(this,b.splice(0,16));this.reset();return c},P:[],b:[],G:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}var b=0,c=2,d;a:for(;64>b;c++){for(d=2;d*d<=c;d++)if(0===c%d)continue a;8>b&&(this.P[b]=a(Math.pow(c,0.5)));this.b[b]=a(Math.pow(c,1/3));b++}}};
function x(a,b){var c,d,e,g=b.slice(0),f=a.s,h=a.b,l=f[0],k=f[1],m=f[2],n=f[3],p=f[4],t=f[5],r=f[6],v=f[7];for(c=0;64>c;c++)16>c?d=g[c]:(d=g[c+1&15],e=g[c+14&15],d=g[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+g[c&15]+g[c+9&15]|0),d=d+v+(p>>>6^p>>>11^p>>>25^p<<26^p<<21^p<<7)+(r^p&(t^r))+h[c],v=r,r=t,t=p,p=n+d|0,n=m,m=k,k=l,l=d+(k&m^n&(k^m))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;f[0]=f[0]+l|0;f[1]=f[1]+k|0;f[2]=f[2]+m|0;f[3]=f[3]+n|0;f[4]=f[4]+p|0;f[5]=f[5]+t|0;f[6]=
f[6]+r|0;f[7]=f[7]+v|0}
sjcl.mode.ccm={name:"ccm",t:[],listenProgress:function(a){sjcl.mode.ccm.t.push(a)},unListenProgress:function(a){a=sjcl.mode.ccm.t.indexOf(a);-1<a&&sjcl.mode.ccm.t.splice(a,1)},X:function(a){var b=sjcl.mode.ccm.t.slice(),c;for(c=0;c<b.length;c+=1)b[c](a)},encrypt:function(a,b,c,d,e){var g,f=b.slice(0),h=sjcl.bitArray,l=h.bitLength(c)/8,k=h.bitLength(f)/8;e=e||64;d=d||[];7>l&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(g=2;4>g&&k>>>8*g;g++);g<15-l&&(g=15-l);c=h.clamp(c,8*(15-
g));b=sjcl.mode.ccm.M(a,b,c,d,e,g);f=sjcl.mode.ccm.q(a,f,c,b,e,g);return h.concat(f.data,f.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var g=sjcl.bitArray,f=g.bitLength(c)/8,h=g.bitLength(b),l=g.clamp(b,h-e),k=g.bitSlice(b,h-e),h=(h-e)/8;7>f&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(b=2;4>b&&h>>>8*b;b++);b<15-f&&(b=15-f);c=g.clamp(c,8*(15-b));l=sjcl.mode.ccm.q(a,l,c,k,e,b);a=sjcl.mode.ccm.M(a,l.data,c,d,e,b);g.equal(l.tag,a)||q(new sjcl.exception.corrupt("ccm: tag doesn't match"));
return l.data},ea:function(a,b,c,d,e,g){var f=[],h=sjcl.bitArray,l=h.g;d=[h.partial(8,(b.length?64:0)|d-2<<2|g-1)];d=h.concat(d,c);d[3]|=e;d=a.encrypt(d);if(b.length){c=h.bitLength(b)/8;65279>=c?f=[h.partial(16,c)]:0xffffffff>=c&&(f=h.concat([h.partial(16,65534)],[c]));f=h.concat(f,b);for(b=0;b<f.length;b+=4)d=a.encrypt(l(d,f.slice(b,b+4).concat([0,0,0])))}return d},M:function(a,b,c,d,e,g){var f=sjcl.bitArray,h=f.g;e/=8;(e%2||4>e||16<e)&&q(new sjcl.exception.invalid("ccm: invalid tag length"));(0xffffffff<
d.length||0xffffffff<b.length)&&q(new sjcl.exception.bug("ccm: can't deal with 4GiB or more data"));c=sjcl.mode.ccm.ea(a,d,c,e,f.bitLength(b)/8,g);for(d=0;d<b.length;d+=4)c=a.encrypt(h(c,b.slice(d,d+4).concat([0,0,0])));return f.clamp(c,8*e)},q:function(a,b,c,d,e,g){var f,h=sjcl.bitArray;f=h.g;var l=b.length,k=h.bitLength(b),m=l/50,n=m;c=h.concat([h.partial(8,g-1)],c).concat([0,0,0]).slice(0,4);d=h.bitSlice(f(d,a.encrypt(c)),0,e);if(!l)return{tag:d,data:[]};for(f=0;f<l;f+=4)f>m&&(sjcl.mode.ccm.X(f/
l),m+=n),c[3]++,e=a.encrypt(c),b[f]^=e[0],b[f+1]^=e[1],b[f+2]^=e[2],b[f+3]^=e[3];return{tag:d,data:h.clamp(b,k)}}};
sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,g){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));var f,h=sjcl.mode.ocb2.J,l=sjcl.bitArray,k=l.g,m=[0,0,0,0];c=h(a.encrypt(c));var n,p=[];d=d||[];e=e||64;for(f=0;f+4<b.length;f+=4)n=b.slice(f,f+4),m=k(m,n),p=p.concat(k(c,a.encrypt(k(c,n)))),c=h(c);n=b.slice(f);b=l.bitLength(n);f=a.encrypt(k(c,[0,0,0,b]));n=l.clamp(k(n.concat([0,0,0]),f),b);m=k(m,k(n.concat([0,0,0]),f));m=a.encrypt(k(m,k(c,h(c))));d.length&&
(m=k(m,g?d:sjcl.mode.ocb2.pmac(a,d)));return p.concat(l.concat(n,l.clamp(m,e)))},decrypt:function(a,b,c,d,e,g){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));e=e||64;var f=sjcl.mode.ocb2.J,h=sjcl.bitArray,l=h.g,k=[0,0,0,0],m=f(a.encrypt(c)),n,p,t=sjcl.bitArray.bitLength(b)-e,r=[];d=d||[];for(c=0;c+4<t/32;c+=4)n=l(m,a.decrypt(l(m,b.slice(c,c+4)))),k=l(k,n),r=r.concat(n),m=f(m);p=t-32*c;n=a.encrypt(l(m,[0,0,0,p]));n=l(n,h.clamp(b.slice(c),p).concat([0,0,0]));
k=l(k,n);k=a.encrypt(l(k,l(m,f(m))));d.length&&(k=l(k,g?d:sjcl.mode.ocb2.pmac(a,d)));h.equal(h.clamp(k,e),h.bitSlice(b,t))||q(new sjcl.exception.corrupt("ocb: tag doesn't match"));return r.concat(h.clamp(n,p))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.J,e=sjcl.bitArray,g=e.g,f=[0,0,0,0],h=a.encrypt([0,0,0,0]),h=g(h,d(d(h)));for(c=0;c+4<b.length;c+=4)h=d(h),f=g(f,a.encrypt(g(h,b.slice(c,c+4))));c=b.slice(c);128>e.bitLength(c)&&(h=g(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));f=g(f,c);return a.encrypt(g(d(g(h,
d(h))),f))},J:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}};
sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var g=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.q(!0,a,g,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var g=b.slice(0),f=sjcl.bitArray,h=f.bitLength(g);e=e||128;d=d||[];e<=h?(b=f.bitSlice(g,h-e),g=f.bitSlice(g,0,h-e)):(b=g,g=[]);a=sjcl.mode.gcm.q(u,a,g,d,c,e);f.equal(a.tag,b)||q(new sjcl.exception.corrupt("gcm: tag doesn't match"));return a.data},ba:function(a,b){var c,d,e,g,f,h=sjcl.bitArray.g;e=[0,0,0,0];g=
b.slice(0);for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,g));f=0!==(g[3]&1);for(d=3;0<d;d--)g[d]=g[d]>>>1|(g[d-1]&1)<<31;g[0]>>>=1;f&&(g[0]^=-0x1f000000)}return e},h:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;d<e;d+=4)b[0]^=0xffffffff&c[d],b[1]^=0xffffffff&c[d+1],b[2]^=0xffffffff&c[d+2],b[3]^=0xffffffff&c[d+3],b=sjcl.mode.gcm.ba(b,a);return b},q:function(a,b,c,d,e,g){var f,h,l,k,m,n,p,t,r=sjcl.bitArray;n=c.length;p=r.bitLength(c);t=r.bitLength(d);h=r.bitLength(e);f=
b.encrypt([0,0,0,0]);96===h?(e=e.slice(0),e=r.concat(e,[1])):(e=sjcl.mode.gcm.h(f,[0,0,0,0],e),e=sjcl.mode.gcm.h(f,e,[0,0,Math.floor(h/0x100000000),h&0xffffffff]));h=sjcl.mode.gcm.h(f,[0,0,0,0],d);m=e.slice(0);d=h.slice(0);a||(d=sjcl.mode.gcm.h(f,h,c));for(k=0;k<n;k+=4)m[3]++,l=b.encrypt(m),c[k]^=l[0],c[k+1]^=l[1],c[k+2]^=l[2],c[k+3]^=l[3];c=r.clamp(c,p);a&&(d=sjcl.mode.gcm.h(f,h,c));a=[Math.floor(t/0x100000000),t&0xffffffff,Math.floor(p/0x100000000),p&0xffffffff];d=sjcl.mode.gcm.h(f,d,a);l=b.encrypt(e);
d[0]^=l[0];d[1]^=l[1];d[2]^=l[2];d[3]^=l[3];return{tag:r.bitSlice(d,0,g),data:c}}};sjcl.misc.hmac=function(a,b){this.N=b=b||sjcl.hash.sha256;var c=[[],[]],d,e=b.prototype.blockSize/32;this.n=[new b,new b];a.length>e&&(a=b.hash(a));for(d=0;d<e;d++)c[0][d]=a[d]^909522486,c[1][d]=a[d]^1549556828;this.n[0].update(c[0]);this.n[1].update(c[1]);this.I=new b(this.n[0])};
sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){this.S&&q(new sjcl.exception.invalid("encrypt on already updated hmac called!"));this.update(a);return this.digest(a)};sjcl.misc.hmac.prototype.reset=function(){this.I=new this.N(this.n[0]);this.S=u};sjcl.misc.hmac.prototype.update=function(a){this.S=!0;this.I.update(a)};sjcl.misc.hmac.prototype.digest=function(){var a=this.I.finalize(),a=(new this.N(this.n[1])).update(a).finalize();this.reset();return a};
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E3;(0>d||0>c)&&q(sjcl.exception.invalid("invalid params to pbkdf2"));"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));e=e||sjcl.misc.hmac;a=new e(a);var g,f,h,l,k=[],m=sjcl.bitArray;for(l=1;32*k.length<(d||1);l++){e=g=a.encrypt(m.concat(b,[l]));for(f=1;f<c;f++){g=a.encrypt(g);for(h=0;h<g.length;h++)e[h]^=g[h]}k=k.concat(e)}d&&(k=m.clamp(k,d));return k};
sjcl.prng=function(a){this.c=[new sjcl.hash.sha256];this.j=[0];this.H=0;this.u={};this.F=0;this.L={};this.Q=this.d=this.k=this.Z=0;this.b=[0,0,0,0,0,0,0,0];this.f=[0,0,0,0];this.C=s;this.D=a;this.r=u;this.B={progress:{},seeded:{}};this.m=this.Y=0;this.w=1;this.A=2;this.U=0x10000;this.K=[0,48,64,96,128,192,0x100,384,512,768,1024];this.V=3E4;this.T=80};
sjcl.prng.prototype={randomWords:function(a,b){var c=[],d;d=this.isReady(b);var e;d===this.m&&q(new sjcl.exception.notReady("generator isn't seeded"));if(d&this.A){d=!(d&this.w);e=[];var g=0,f;this.Q=e[0]=(new Date).valueOf()+this.V;for(f=0;16>f;f++)e.push(0x100000000*Math.random()|0);for(f=0;f<this.c.length&&!(e=e.concat(this.c[f].finalize()),g+=this.j[f],this.j[f]=0,!d&&this.H&1<<f);f++);this.H>=1<<this.c.length&&(this.c.push(new sjcl.hash.sha256),this.j.push(0));this.d-=g;g>this.k&&(this.k=g);this.H++;
this.b=sjcl.hash.sha256.hash(this.b.concat(e));this.C=new sjcl.cipher.aes(this.b);for(d=0;4>d&&!(this.f[d]=this.f[d]+1|0,this.f[d]);d++);}for(d=0;d<a;d+=4)0===(d+1)%this.U&&A(this),e=B(this),c.push(e[0],e[1],e[2],e[3]);A(this);return c.slice(0,a)},setDefaultParanoia:function(a,b){0===a&&"Setting paranoia=0 will ruin your security; use it only for testing"!==b&&q("Setting paranoia=0 will ruin your security; use it only for testing");this.D=a},addEntropy:function(a,b,c){c=c||"user";var d,e,g=(new Date).valueOf(),
f=this.u[c],h=this.isReady(),l=0;d=this.L[c];d===s&&(d=this.L[c]=this.Z++);f===s&&(f=this.u[c]=0);this.u[c]=(this.u[c]+1)%this.c.length;switch(typeof a){case "number":b===s&&(b=1);this.c[f].update([d,this.F++,1,b,g,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if("[object Uint32Array]"===c){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else{"[object Array]"!==c&&(l=1);for(c=0;c<a.length&&!l;c++)"number"!==typeof a[c]&&(l=1)}if(!l){if(b===s)for(c=b=0;c<a.length;c++)for(e=a[c];0<e;)b++,
e>>>=1;this.c[f].update([d,this.F++,2,b,g,a.length].concat(a))}break;case "string":b===s&&(b=a.length);this.c[f].update([d,this.F++,3,b,g,a.length]);this.c[f].update(a);break;default:l=1}l&&q(new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string"));this.j[f]+=b;this.d+=b;h===this.m&&(this.isReady()!==this.m&&C("seeded",Math.max(this.k,this.d)),C("progress",this.getProgress()))},isReady:function(a){a=this.K[a!==s?a:this.D];return this.k&&this.k>=a?this.j[0]>this.T&&
(new Date).valueOf()>this.Q?this.A|this.w:this.w:this.d>=a?this.A|this.m:this.m},getProgress:function(a){a=this.K[a?a:this.D];return this.k>=a?1:this.d>a?1:this.d/a},startCollectors:function(){this.r||(this.a={loadTimeCollector:D(this,this.da),mouseCollector:D(this,this.fa),keyboardCollector:D(this,this.ca),accelerometerCollector:D(this,this.W),touchCollector:D(this,this.ha)},window.addEventListener?(window.addEventListener("load",this.a.loadTimeCollector,u),window.addEventListener("mousemove",this.a.mouseCollector,
u),window.addEventListener("keypress",this.a.keyboardCollector,u),window.addEventListener("devicemotion",this.a.accelerometerCollector,u),window.addEventListener("touchmove",this.a.touchCollector,u)):document.attachEvent?(document.attachEvent("onload",this.a.loadTimeCollector),document.attachEvent("onmousemove",this.a.mouseCollector),document.attachEvent("keypress",this.a.keyboardCollector)):q(new sjcl.exception.bug("can't attach event")),this.r=!0)},stopCollectors:function(){this.r&&(window.removeEventListener?
(window.removeEventListener("load",this.a.loadTimeCollector,u),window.removeEventListener("mousemove",this.a.mouseCollector,u),window.removeEventListener("keypress",this.a.keyboardCollector,u),window.removeEventListener("devicemotion",this.a.accelerometerCollector,u),window.removeEventListener("touchmove",this.a.touchCollector,u)):document.detachEvent&&(document.detachEvent("onload",this.a.loadTimeCollector),document.detachEvent("onmousemove",this.a.mouseCollector),document.detachEvent("keypress",
this.a.keyboardCollector)),this.r=u)},addEventListener:function(a,b){this.B[a][this.Y++]=b},removeEventListener:function(a,b){var c,d,e=this.B[a],g=[];for(d in e)e.hasOwnProperty(d)&&e[d]===b&&g.push(d);for(c=0;c<g.length;c++)d=g[c],delete e[d]},ca:function(){E(1)},fa:function(a){var b,c;try{b=a.x||a.clientX||a.offsetX||0,c=a.y||a.clientY||a.offsetY||0}catch(d){c=b=0}0!=b&&0!=c&&sjcl.random.addEntropy([b,c],2,"mouse");E(0)},ha:function(a){a=a.touches[0]||a.changedTouches[0];sjcl.random.addEntropy([a.pageX||
a.clientX,a.pageY||a.clientY],1,"touch");E(0)},da:function(){E(2)},W:function(a){a=a.accelerationIncludingGravity.x||a.accelerationIncludingGravity.y||a.accelerationIncludingGravity.z;if(window.orientation){var b=window.orientation;"number"===typeof b&&sjcl.random.addEntropy(b,1,"accelerometer")}a&&sjcl.random.addEntropy(a,2,"accelerometer");E(0)}};function C(a,b){var c,d=sjcl.random.B[a],e=[];for(c in d)d.hasOwnProperty(c)&&e.push(d[c]);for(c=0;c<e.length;c++)e[c](b)}
function E(a){"undefined"!==typeof window&&window.performance&&"function"===typeof window.performance.now?sjcl.random.addEntropy(window.performance.now(),a,"loadtime"):sjcl.random.addEntropy((new Date).valueOf(),a,"loadtime")}function A(a){a.b=B(a).concat(B(a));a.C=new sjcl.cipher.aes(a.b)}function B(a){for(var b=0;4>b&&!(a.f[b]=a.f[b]+1|0,a.f[b]);b++);return a.C.encrypt(a.f)}function D(a,b){return function(){b.apply(a,arguments)}}sjcl.random=new sjcl.prng(6);
a:try{var F,G,H,I;if(I="undefined"!==typeof module){var J;if(J=module.exports){var K;try{K=require("crypto")}catch(L){K=null}J=(G=K)&&G.randomBytes}I=J}if(I)F=G.randomBytes(128),F=new Uint32Array((new Uint8Array(F)).buffer),sjcl.random.addEntropy(F,1024,"crypto['randomBytes']");else if("undefined"!==typeof window&&"undefined"!==typeof Uint32Array){H=new Uint32Array(32);if(window.crypto&&window.crypto.getRandomValues)window.crypto.getRandomValues(H);else if(window.msCrypto&&window.msCrypto.getRandomValues)window.msCrypto.getRandomValues(H);
else break a;sjcl.random.addEntropy(H,1024,"crypto['getRandomValues']")}}catch(M){"undefined"!==typeof window&&window.console&&(console.log("There was an error collecting entropy from the browser:"),console.log(M))}
sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},aa:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,g=e.e({iv:sjcl.random.randomWords(4,0)},e.defaults),f;e.e(g,c);c=g.adata;"string"===typeof g.salt&&(g.salt=sjcl.codec.base64.toBits(g.salt));"string"===typeof g.iv&&(g.iv=sjcl.codec.base64.toBits(g.iv));(!sjcl.mode[g.mode]||!sjcl.cipher[g.cipher]||"string"===typeof a&&100>=g.iter||64!==g.ts&&96!==g.ts&&128!==g.ts||128!==g.ks&&192!==g.ks&&0x100!==g.ks||2>g.iv.length||
4<g.iv.length)&&q(new sjcl.exception.invalid("json encrypt: invalid parameters"));"string"===typeof a?(f=sjcl.misc.cachedPbkdf2(a,g),a=f.key.slice(0,g.ks/32),g.salt=f.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.publicKey&&(f=a.kem(),g.kemtag=f.tag,a=f.key.slice(0,g.ks/32));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));"string"===typeof c&&(g.adata=c=sjcl.codec.utf8String.toBits(c));f=new sjcl.cipher[g.cipher](a);e.e(d,g);d.key=a;g.ct="ccm"===g.mode&&sjcl.arrayBuffer&&sjcl.arrayBuffer.ccm&&
b instanceof ArrayBuffer?sjcl.arrayBuffer.ccm.encrypt(f,b,g.iv,c,g.ts):sjcl.mode[g.mode].encrypt(f,b,g.iv,c,g.ts);return g},encrypt:function(a,b,c,d){var e=sjcl.json,g=e.aa.apply(e,arguments);return e.encode(g)},$:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.e(e.e(e.e({},e.defaults),b),c,!0);var g,f;g=b.adata;"string"===typeof b.salt&&(b.salt=sjcl.codec.base64.toBits(b.salt));"string"===typeof b.iv&&(b.iv=sjcl.codec.base64.toBits(b.iv));(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||"string"===
typeof a&&100>=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4<b.iv.length)&&q(new sjcl.exception.invalid("json decrypt: invalid parameters"));"string"===typeof a?(f=sjcl.misc.cachedPbkdf2(a,b),a=f.key.slice(0,b.ks/32),b.salt=f.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.secretKey&&(a=a.unkem(sjcl.codec.base64.toBits(b.kemtag)).slice(0,b.ks/32));"string"===typeof g&&(g=sjcl.codec.utf8String.toBits(g));f=new sjcl.cipher[b.cipher](a);g="ccm"===
b.mode&&sjcl.arrayBuffer&&sjcl.arrayBuffer.ccm&&b.ct instanceof ArrayBuffer?sjcl.arrayBuffer.ccm.decrypt(f,b.ct,b.iv,b.tag,g,b.ts):sjcl.mode[b.mode].decrypt(f,b.ct,b.iv,g,b.ts);e.e(d,b);d.key=a;return 1===c.raw?g:sjcl.codec.utf8String.fromBits(g)},decrypt:function(a,b,c,d){var e=sjcl.json;return e.$(a,e.decode(b),c,d)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b))switch(b.match(/^[a-z0-9]+$/i)||q(new sjcl.exception.invalid("json encode: invalid property name")),c+=d+'"'+b+
'":',d=",",typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],0)+'"';break;default:q(new sjcl.exception.bug("json encode: unsupported type"))}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");a.match(/^\{.*\}$/)||q(new sjcl.exception.invalid("json decode: this isn't json!"));a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c<a.length;c++)(d=a[c].match(/^\s*(?:(["']?)([a-z][a-z0-9]*)\1)\s*:\s*(?:(-?\d+)|"([a-z0-9+\/%*_.@=\-]*)"|(true|false))$/i))||
q(new sjcl.exception.invalid("json decode: this isn't json!")),null!=d[3]?b[d[2]]=parseInt(d[3],10):null!=d[4]?b[d[2]]=d[2].match(/^(ct|adata|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4]):null!=d[5]&&(b[d[2]]="true"===d[5]);return b},e:function(a,b,c){a===s&&(a={});if(b===s)return a;for(var d in b)b.hasOwnProperty(d)&&(c&&(a[d]!==s&&a[d]!==b[d])&&q(new sjcl.exception.invalid("required parameter overridden")),a[d]=b[d]);return a},ja:function(a,b){var c={},d;for(d in a)a.hasOwnProperty(d)&&
a[d]!==b[d]&&(c[d]=a[d]);return c},ia:function(a,b){var c={},d;for(d=0;d<b.length;d++)a[b[d]]!==s&&(c[b[d]]=a[b[d]]);return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;sjcl.misc.ga={};sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.ga,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=b.salt===s?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};

10
script/application Executable file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env perl
use strict;
use warnings;
use lib 'lib';
# Start command line interface for application
require Mojolicious::Commands;
Mojolicious::Commands->start_app('Lufi');

11
script/lufi Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
# Start command line interface for application
require Mojolicious::Commands;
Mojolicious::Commands->start_app('Mounter');

9
t/basic.t Normal file
View File

@ -0,0 +1,9 @@
use Mojo::Base -strict;
use Test::More;
use Test::Mojo;
my $t = Test::Mojo->new('Lufi');
$t->get_ok('/')->status_is(200)->content_like(qr/Lufi/i);
done_testing();

93
templates/files.html.ep Normal file
View File

@ -0,0 +1,93 @@
% # vim:set sts=4 sw=4 ts=4 ft=html.epl expandtab:
<h2><%= l('My files') %></h2>
<hr>
<p>
<%= l('Only the files sent with this browser will be listed here. The informations are stored in localStorage: if you delete your localStorage data, you\'ll loose this informations.') %>
</p>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th class="text-center"><%= l('File name') %></th>
<th class="text-center"><%= l('Download link') %></th>
<th class="text-center"><%= l('Counter') %></th>
<th class="text-center"><%= l('Delete at first view?') %></th>
<th class="text-center"><%= l('Uploaded at') %></th>
<th class="text-center"><%= l('Expires at') %></th>
<th class="text-center"><%= l('Deletion link') %></th>
</tr>
</thead>
<tbody id="myfiles">
</tbody>
</table>
</div>
%= javascript begin
function populateFilesTable() {
var files = JSON.parse(localStorage.getItem('files'));
files.reverse();
files.forEach(function(element, index, array) {
var del_view = (element.del_at_first_view) ? '<span class="glyphicon glyphicon-ok"></span>' : '<span class="glyphicon glyphicon-remove"></span>';
var dlink = '<%== url_for('/')->to_abs() %>d/'+element.short+'/'+element.token;
var limit = (element.limit === 0) ? '<%= l('No limit') %>' : moment.unix(element.delay * 86400 + element.created_at).locale(window.navigator.language).format('LLLL');
var created_at = moment.unix(element.created_at).locale(window.navigator.language).format('LLLL');
var tr = document.createElement('tr');
tr.innerHTML = '<td class="text-left">'
+element.name
+'</td><td class="text-left">'
+'<a href="'+element.url+'">'+element.url+'</a>'
+'</td><td id="count-'+element.short+'" class="text-center">'
+'</td><td class="text-center">'
+del_view
+'</td><td>'
+created_at
+'</td><td>'
+limit
+'</td><td class="text-left">'
+'<a href="'+dlink+'">'+dlink+'</a>'
+'</td>';
document.getElementById('myfiles').appendChild(tr);
var xhr = new XMLHttpRequest();
xhr.open('POST', '<%== url_for('counter') %>');
xhr.onreadystatechange = function() {
if (xhr.readyState>3 && xhr.status==200) {
var data = JSON.parse(xhr.responseText);
if (data.success) {
document.getElementById('count-'+element.short).innerHTML = data.counter;
} else {
alert(data.msg);
}
}
};
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('short='+element.short+'&token='+element.token);
/*$.ajax({
url : '<%== url_for('counter') %>',
type : 'POST',
data : {
'short': element.short,
'token': element.token
},
success: function(data) {
if (data.success) {
$('#count-'+element.id).text(data.counter);
} else {
alert(data.msg);
}
},
error: function() {
alert(element.filename+'<%= l(': Error while trying to get the counter.') %>');
}
});*/
});
}
document.addEventListener('DOMContentLoaded', function() {
populateFilesTable();
});
% end
%= javascript '/js/moment-with-locales.min.js'

63
templates/index.html.ep Normal file
View File

@ -0,0 +1,63 @@
% # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab:
% my %d = (
% delay_0 => l('no time limit'),
% delay_1 => l('24 hours'),
% delay_365 => l('1 year')
% );
<div class="inner cover">
<div class="form-group row">
<div class="col-sm-6 col-xs-12">
<select id="delete-day" class="form-control">
% for my $delay (qw/0 1 7 30 365/) {
% my $text = ($delay == 7 || $delay == 30) ? l('%1 days', $delay) : $d{'delay_'.$delay};
% if (config('max_delay')) {
% if ($delay) {
% if ($delay < config('max_delay')) {
<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>
% } elsif ($delay == config('max_delay')) {
<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>
% last;
% } else {
% my $text = ($delay == 1) ? l('24 hours') : l('%1 days', $delay);
<option value="<%= config('max_delay') %>" <%== is_selected(config('max_delay')) %>><%= l('%1 days', config('max_delay')) %></option>
% last;
% }
% }
% } else {
<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>
% }
% }
</select>
</div>
<div class="col-sm-6 col-xs-12 text-left">
<div class="checkbox">
<label>
<input type="checkbox" id="first-view"> <%= l('Delete at first view?') %>
</label>
</div>
</div>
</div>
<div id="files">
<h2><%= l('Drop files here') %></h2>
<p><small><%= l('or') %></small></p>
<label>
<span><%= l('Click to open the file browser') %></span>
<input type="file" onchange="handleFiles(this.files)" multiple>
</label>
</div>
<div id="results">
<h2><%= l('Uploaded files') %></h2>
<ul class="list-group" id="ul-results">
</ul>
</div>
</div>
%= javascript begin
var ws_url = '<%= url_for('upload')->to_abs() %>';
var hit = '<%= l('Hit Enter, then Ctrl+C to copy the download link') %>';
var hits = '<%= l('Hit Enter, then Ctrl+C to copy all the download links') %>';
var dltext = '<%= l('Download link') %>';
var cptext = '<%= l('Copy to clipboard') %>';
var del_text = '<%= l('Deletion link') %>';
% end
%= javascript '/js/sjcl.js'
%= javascript '/js/lufi-up.js'

View File

@ -0,0 +1,50 @@
% # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab:
<!DOCTYPE html>
<html>
<head>
<title>Let's Upload that FIle</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<link rel="icon" type="image/png" href="<%= url_for('/img/favicon.png') %>">
<link rel="icon" sizes="128x128" href="<%= url_for('/img/lufi128.png') %>">
<link rel="icon" sizes="196x196" href="<%= url_for('/img/lufi196.png') %>">
<link rel="apple-touch-icon" href="<%= url_for('/img/lufi60.png') %>">
<link rel="apple-touch-icon" sizes="76x76" href="<%= url_for('/img/lufi76.png') %>">
<link rel="apple-touch-icon" sizes="120x120" href="<%= url_for('/img/lufi120.png') %>">
<link rel="apple-touch-icon" sizes="152x152" href="<%= url_for('/img/lufi152.png') %>">
<link rel="apple-touch-icon-precomposed" sizes="128x128" href="<%= url_for('/img/lufi128.png') %>">
%= stylesheet '/css/bootstrap.min.css'
%= stylesheet '/css/cover.css'
%= stylesheet '/css/lufi.css'
%= stylesheet '/css/fontello.css'
</head>
<body>
<div class="site-wrapper">
<div class="site-wrapper-inner">
<div class="cover-container">
<div class="masthead clearfix">
<div class="inner">
<h3 class="masthead-brand"><a href="<%= url_for('/') %>"><img src="<%= url_for('/img/lufi-min.png') %>" alt="logo"> Lufi</a></h3>
<nav>
<ul class="nav masthead-nav">
<li<%== ' class="active"' if (current_route eq 'index') %>><a href="<%= url_for('/') %>"><%= l('Upload files') %></a></li>
<li<%== ' class="active"' if (current_route eq 'files') %>><a href="<%= url_for('/files') %>"><%= l('My files') %></a></li>
<li<%== ' class="active"' if (current_route eq 'about') %>><a href="<%= url_for('/about') %>"><%= l('About') %></a></li>
</ul>
</nav>
</div>
</div>
<%= content %>
<!-- <div class="mastfoot">
<div class="inner">
<p>Lufi, &copy; 2015 <a href="https://fiat-tux.fr">Luc Didry</a>, <a href="https://gnu.org/licenses/agpl.html">GNU Affero Public License</a></p>
</div>
</div> -->
</div>
</div>
</div>
</body>
</html>

9
templates/msg.html.ep Normal file
View File

@ -0,0 +1,9 @@
% # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab:
<div class="inner cover render">
% if (!defined($f) && defined($msg)) {
<div class="alert alert-warning"><%= $msg %></div>
% } else {
<h2><%= stash('f')->filename %></h2>
<div class="alert alert-info"><%= $msg %></div>
% }
</div>

23
templates/render.html.ep Normal file
View File

@ -0,0 +1,23 @@
% # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab:
<div class="inner cover render">
% if (!defined($f) && defined($msg)) {
<div class="alert alert-danger"><%= $msg %></div>
% } else {
<h2><%= stash('f')->filename %></h2>
% if (defined($msg)) {
<div class="alert alert-danger"><%= $msg %></div>
% } else {
<p id="please-wait"><%= l('Please wait while we are getting your file') %></p>
<div class="progress" id="pbd">
<div id="pb" class="progress-bar progress-bar-info progress-bar-striped active" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0;">
<span id="pbt" class="sr-only">0%</span>
</div>
</div>
%= javascript begin
var ws_url = '<%= url_for('download')->to_abs() %>';
% end
%= javascript '/js/sjcl.js'
%= javascript '/js/lufi-down.js'
% }
% }
</div>

View File

@ -0,0 +1,3 @@
templates/layouts/default.html.ep
templates/index.html.ep
lib/Lufi/Controller/Files.pm