Compare commits

..

No commits in common. "d1462470b17712ad2da1cbe075f1209e20980559" and "a228f94dd0d6daa10b5b31d1ff8cde443a254ceb" have entirely different histories.

14 changed files with 329 additions and 404 deletions

2
.gitignore vendored
View File

@ -1,2 +1,2 @@
/files
*.log
*.log

View File

@ -1,22 +1,16 @@
# HuffPress
## What's up?
Hey! This is a simple web-service called __HuffPress__. What it does? Well, actually it _huffpresses_ files... I mean, compresses files using the [Huffman compression](https://en.wikipedia.org/wiki/Huffman_coding 'Read about it on Wikipedia').
Also it uses a bit modified [SHICHIRO coding](https://github.com/H1K0/SHICHIRO 'View on GitHub') for compressing and embedding Huffman table.
Hey! This is a simple web-service called ![HuffPress](https://img.shields.io/badge/Huff-Press-orange.svg). What it does? Well, actually it *huffpresses* files... I mean, compresses files using the [Huffman compression](https://en.wikipedia.org/wiki/Huffman_coding "Read about it on Wikipedia").
## What do I need?
- Any web browser
- ![PHP 7+](https://img.shields.io/badge/PHP-7+-blueviolet.svg)
- ![Python 3.7+](https://img.shields.io/badge/Python-3+-blue.svg)
- `click` python library
- ![Python 3+](https://img.shields.io/badge/Python-3+-blue.svg)
- `click` library
## So, how do I use it?
I think the usage is too easy to explain it, but anyway here's [a video guide](https://yadi.sk/i/aiy78bDaoKqoJQ 'Watch!').
---
<p align=center><i>&copy; Masahiko AMANO a.k.a. H1K0, 2020-2021</i></p>
I think it's too easy to explain the usage of this service, but anyway here's [a video guide](https://yadi.sk/i/aiy78bDaoKqoJQ "Watch!").

View File

Before

Width:  |  Height:  |  Size: 190 KiB

After

Width:  |  Height:  |  Size: 190 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,204 +1,190 @@
from os.path import dirname, basename, join, abspath as path
from os.path import dirname,basename,join,abspath as path
from time import time
from datetime import datetime as dt
import click
from urllib.parse import quote as url
class Log:
def __init__(self, path, name):
self.path = path
self.name = name
def __init__(self,path,name):
self.path=path
self.name=name
def log(self, msg, name=None):
if name is None:
name = self.name
now = dt.today()
with open(self.path, 'a', encoding='utf-8') as file:
file.write(f'{now.year}-{str(now.month).rjust(2, "0")}-{str(now.day).rjust(2, "0")} '
f'{str(now.hour).rjust(2, "0")}:{str(now.minute).rjust(2, "0")}:{str(now.second).rjust(2, "0")},{str(now.microsecond)[:3]}'
' | '
f'{name}'
' | '
f'{msg}\n')
def log(self,msg,name=None):
if name is None:
name=self.name
now=dt.today()
with open(self.path,'a',encoding='utf-8') as file:
file.write(f'{now.year}-{str(now.month).rjust(2,"0")}-{str(now.day).rjust(2,"0")} '
f'{str(now.hour).rjust(2,"0")}:{str(now.minute).rjust(2,"0")}:{str(now.second).rjust(2,"0")},{str(now.microsecond)[:3]}'
' | '
f'{name}'
' | '
f'{msg}\n')
log = Log(join(dirname(__file__), 'hfm.log'), 'hfm-py')
output = ''
log=Log(join(dirname(__file__),'hfm.log'),'hfm-py')
def huffman(data):
units = {} # getting element-wise info
for c in data:
if c in units:
units[c] += 1
else:
units[c] = 1
codes = dict.fromkeys(units.keys(), '')
units = sorted([([u], units[u]) for u in units], key=lambda u: u[1])
units={} #getting element-wise info
for c in data:
if c in units:
units[c]+=1
else:
units[c]=1
units,codes=sorted([([u],units[u]) for u in units],key=lambda u:u[1]),dict.fromkeys(units.keys(),'')
while units: # creating Haffman table
if len(units) > 2:
b = int(units[0][1] + units[1][1] > units[1][1] + units[2][1])
else:
b = 0
for c in units[b][0]:
codes[c] = '0' + codes[c]
for c in units[1 + b][0]:
codes[c] = '1' + codes[c]
units[2 * b] = units[b][0] + units[1 + b][0], units[b][1] + units[1 + b][1]
if len(units) > 2:
del units[1]
units.sort(key=lambda u: u[1])
else:
del units
break
return codes
while units: #creating Haffman table
if len(units)>2:
b=int(units[0][1]+units[1][1]>units[1][1]+units[2][1])
else:
b=0
for c in units[b][0]:
codes[c]='0'+codes[c]
for c in units[1+b][0]:
codes[c]='1'+codes[c]
units[2*b]=units[b][0]+units[1+b][0],units[b][1]+units[1+b][1]
if len(units)>2:
del units[1]
units.sort(key=lambda u:u[1])
else:
del units
break
return codes
def shichiro_encode(table):
table = ';'.join([f'{k};{table[k]}' for k in table]).split(';')
bits = ''
for i in range(len(table)):
if i % 2:
code = table[i]
bits += bin(len(code))[2:].rjust(8,'0')
bits += code
else:
bits += bin(int(table[i]))[2:].rjust(8, '0')
continue
return bits
def tbl(table):
table=';'.join([f'{k};{table[k]}' for k in table]).split(';')
byts=[]
for i in range(len(table)):
if i%2:
num=table[i]
else:
num=bin(int(table[i]))[2:]
while len(num)>7:
byts.append(int('1'+num[:7],2))
num=num[7:]
byts.append(int(num,2))
byts.append(8-len(num))
return byts
def shichiro_decode(byts):
bits=''.join(byts)
dec = []
table = {}
stack = ''
i = 0
c = 0
while i < len(bits) and len(bits) - i >= 8:
if c % 2 == 0:
dec.append(bits[i:i+8])
i += 8
c += 1
continue
bitlen = int(bits[i:i+8], 2)
i += 8
dec.append(bits[i:i+bitlen])
i += bitlen
c += 1
for i in range(0, len(dec), 2):
table[dec[i + 1]] = int(dec[i], 2)
return table
def detbl(byts):
dec=[]
table={}
stack=''
i=0
while i<len(byts):
if byts[i][0]=='1':
stack+=byts[i][1:]
else:
stack+=byts[i][int(byts[i+1],2):]
dec.append(stack[:])
stack=''
i+=1
i+=1
for i in range(0,len(dec),2):
table[dec[i+1]]=int(dec[i],2)
return table
def compress_file(filename):
global log, output
log.log(f"Loading '{filename}'...")
with open(filename, 'rb') as file: # get data
data = list(map(int, file.read()))
log.log(f'Original size: {len(data)} bytes.')
log.log('Creating Huffman table...')
hf = huffman(data)
table = shichiro_encode(hf)
log.log('Embedding Huffman table...')
tablen = bin(len(table))[2:] # embed the table
bits = ''
bitlen = bin(len(tablen))[2:]
while len(bitlen) > 7:
bits += '1' + bitlen[:7]
bitlen = bitlen[7:]
bits += bitlen.rjust(8, '0') + bin(len(bitlen))[2:].rjust(8, '0') + tablen + table
log.log(f'Huffman table size: {len(bits)} bits.')
log.log('Compressing...')
for i in range(len(data)): # encode to Haffman
bits += hf[data[i]]
out = []
for i in range(0, len(bits), 8):
out.append(int(bits[i:i+8].ljust(8, '0'), 2))
out.append(len(bits) % 8)
log.log(f'Compressed size: {len(out)} bytes.')
log.log(f"Saving to '{filename}.hfm'...")
with open(f'{filename}.hfm', 'wb') as file: # save Haffman code
file.write(bytes(out))
log.log('SUCCESSFULLY COMPRESSED')
output += f'"origSize":{len(data)},'
output += f'"compSize":{len(out)},'
log.log(f"Loading '{filename}'...")
with open(filename,'rb') as file: #get data
data=list(map(int,file.read()))
log.log(f'Original size: {len(data)} bytes.')
log.log('Creating Huffman table...')
hf=huffman(data)
table=tbl(hf)
log.log('Embedding Huffman table...')
out=[]
ln=bin(len(table))[2:] #embed the table
while len(ln)>7:
out.append(int('1'+ln[:7],2))
ln=ln[7:]
out+=[int(ln,2),8-len(ln)]+table
log.log(f'Huffman table size: {len(out)} bytes.')
log.log('Compressing...')
stack=''
for i in range(len(data)): #encode to Haffman
stack+=hf[data[i]]
while len(stack)>=8:
out.append(int(stack[:8],2))
stack=stack[8:]
out+=[int(stack.ljust(8,'0'),2),len(stack)]
log.log(f'Compressed size: {len(out)} bytes.')
log.log(f"Saving to '{filename}.hfm'...")
with open(f'{filename}.hfm','wb') as file: #save Haffman code
file.write(bytes(out))
log.log('SUCCESSFULLY COMPRESSED')
print(f'"origSize":{len(data)},')
print(f'"compSize":{len(out)},')
def decompress_file(filename):
global log, output
log.log(f"Loading '{filename}'...")
with open(filename, 'rb') as file: # get data
data = [bin(byte)[2:].rjust(8, '0') for byte in file.read()]
os = len(data)
data[-2] = data[-2][:int(data[-1], 2)]
del data[-1]
log.log('Extracting Huffman table...')
bitlen = '' # extract the table
i = 0
while 1:
if data[i][0] == '1':
bitlen += data[i][1:]
else:
bitlen += data[i][int(data[i + 1], 2):]
break
i += 1
del data[:i + 2]
data = ''.join(data)
bitlen = int(bitlen, 2)
tablen = int(data[:bitlen], 2)
table = shichiro_decode(data[bitlen:tablen+bitlen])
data = data[bitlen+tablen:]
stack = ''
out = []
log.log('Decompressing...')
for c in data: # decode Haffman
stack += c
if stack in table:
out.append(int(table[stack]))
stack = ''
if filename[-4:] == '.hfm':
filename = filename[:-4]
log.log(f"Saving to '{filename}'...")
with open(f'{filename}', 'wb') as file: # save decoded data
file.write(bytes(out))
log.log(f'SUCCESSFULLY DECOMPRESSED')
output += f'"compSize":{os},'
output += f'"origSize":{len(out)},'
log.log(f"Loading '{filename}'...")
with open(filename,'rb') as file: #get data
data=[bin(byte)[2:].rjust(8,'0')for byte in file.read()]
os=len(data)
data[-2]=data[-2][:int(data[-1],2)]
del data[-1]
log.log('Extracting Huffman table...')
ln='' #extract the table
i=0
while 1:
if data[i][0]=='1':
ln+=data[i][1:]
else:
ln+=data[i][int(data[i+1],2):]
break
i+=1
del data[:i+2]
table=detbl(data[:int(ln,2)])
del data[:int(ln,2)]
data=''.join(data)
stack=''
out=[]
log.log('Decompressing...')
for c in data: #decode Haffman
stack+=c
if stack in table:
out.append(int(table[stack]))
stack=''
filename=filename[:-4]
log.log(f"Saving to '{filename}'...")
with open(f'{filename}','wb') as file: #save decoded data
file.write(bytes(out))
log.log(f'SUCCESSFULLY DECOMPRESSED')
print(f'"compSize":{os},')
print(f'"origSize":{len(out)},')
@click.command(options_metavar='[-c / -d]')
@click.argument('files', nargs=-1, metavar='<file [file [...]]>')
@click.option('-c/-d', 'comp', default=True, help='Compress/decompress mode selectors.')
def CLI(files, comp):
global log, output
log.log(f'hfm {"-c" * comp}{"-d" * (not comp)} "' + "\" \"".join(files) + '"')
for file in files:
output += '{'
stime = time()
if comp:
try:
compress_file(path(file))
wtime = time() - stime
output += '"status":true,'
output += f'"time":{round(wtime, 3)},'
output += f'"dlink":"./files/{url(basename(file)) + ".hfm"}"'
except Exception as e:
output += f'"status":false'
else:
try:
decompress_file(path(file))
wtime = time() - stime
output += '"status":true,'
output += f'"time":{round(wtime, 3)},'
output += f'"dlink":"./files/{url(basename(file)[:-4])}"'
except Exception as e:
output += f'"status":false'
output += '}'
print(output)
@click.argument('files',nargs=-1,metavar='<file [file [...]]>')
@click.option('-c/-d','comp',default=True,help='Compress/decompress mode selectors.')
def CLI(files,comp):
log.log(f'hfm {"-c"*comp}{"-d"*(not comp)} {" ".join(files)}')
for file in files:
print('{')
stime=time()
if comp:
compress_file(path(file))
wtime=time()-stime
print('"status":true,')
print(f'"time":{round(wtime,3)},')
print(f'"dlink":"./files/{basename(file)+".hfm"}"')
else:
try:
decompress_file(path(file))
wtime=time()-stime
print('"status":true,')
print(f'"time":{round(wtime,3)},')
print(f'"dlink":"./files/{basename(file)[:-4]}"')
except Exception as e:
print(f'"status":false')
print('}')
if __name__ == '__main__':
CLI()
if __name__=='__main__':
CLI()

View File

@ -1,17 +1,12 @@
<?php
$mode = $_POST['mode'];
$file = $_FILES['file'];
if (!is_dir('./files')) {
mkdir('./files');
$mode=$_POST['mode'];
$file=$_FILES['file'];
if (!is_dir(dirname(__file__).'/files')){
mkdir(dirname(__file__).'/files');
}
if (!file_exists('./files/counter.txt')){
file_put_contents('./files/counter.txt', '0');
}
$id = (int)file_get_contents('./files/counter.txt');
file_put_contents('./files/counter.txt', (string)($id+1));
$path = "./files/{$id}__".basename($file['name']);
move_uploaded_file($file['tmp_name'], $path);
$result = json_decode((string)shell_exec(dirname(__file__)."/huffman.py -".($mode == 'compress' ? 'c' : 'd')." \"{$path}\""));
$path=dirname(__file__).'/files/'.basename($file['name']);
move_uploaded_file($file['tmp_name'],$path);
$result=json_decode((string)shell_exec('python '.dirname(__file__).'/huffman.py -'.($mode=='compress'?'c':'d').' "'.$path.'"'));
header('Content-Type: application/json');
echo json_encode($result);
?>

File diff suppressed because one or more lines are too long

View File

@ -1,62 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HuffPress</title>
<link rel="shortcut icon" href="./favicon.ico" />
<link rel="stylesheet" href="./css/bootstrap.min.css" />
<link rel="stylesheet" href="./css/style.css" />
<script src="./js/jquery-3.6.0.min.js"></script>
<script src="./js/console.image.min.js"></script>
</head>
<body>
<header>
<h1>HuffPress</h1>
</header>
<main>
<h2>Compress your files with the Huffman compression!</h2>
<form id="form" class="was-validated" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="mode">Select mode</label>
<select id="mode" class="form-control" id="mode">
<option value="compress">Compress</option>
<option value="decompress">Decompress</option>
</select>
</div>
<div class="form-group">
<div class="custom-file">
<label class="custom-file-label" for="file">Choose file...</label>
<input id="file" type="file" class="custom-file-input" required />
<div class="invalid-feedback">Let's choose your file!</div>
<div class="valid-feedback">Ready to start!</div>
</div>
</div>
<div class="form-group">
<input id="submit" type="submit" class="btn btn-primary" value="HuffPress!" />
</div>
</form>
</main>
<div class="wrap">
<div class="process">
<img src="./images/processing.gif" alt="Processing..." />
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HuffPress</title>
<link rel="shortcut icon" href="favicon.ico">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link rel="stylesheet" href="style.css">
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
</head>
<body>
<header>
<h1>HuffPress</h1>
</header>
<main>
<h2>Compress your files with the Huffman compression!</h2>
<form id="form" class="was-validated" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="mode">Select mode</label>
<select id="mode" class="form-control" id="mode">
<option value="compress">Compress</option>
<option value="decompress">Decompress</option>
</select>
</div>
<div class="complete">
<h3>Completed!</h3>
<p class="status"></p>
<div class="btncont">
<a id="dlink" class="btn btn-primary" href="" download>Download</a>
<button class="btn closebtn">Close</button>
<div class="form-group">
<div class="custom-file">
<label class="custom-file-label" for="file">Choose file...</label>
<input id="file" type="file" class="custom-file-input" required>
<div class="invalid-feedback">Let's choose your file!</div>
<div class="valid-feedback">Ready to start!</div>
</div>
</div>
<div class="error">
<h3>Error!</h3>
<p class="status">Something went wrong!</p>
<div class="btncont">
<button class="btn closebtn">Close</button>
</div>
<div class="form-group">
<input id="submit" type="submit" class="btn btn-primary" value="HuffPress!">
</div>
</form>
</main>
<div class="wrap">
<div class="process"><img src="processing.gif" alt="Processing..."></div>
<div class="complete">
<h3>Completed!</h3>
<p class="status"></p>
<div class="btncont">
<a id="dlink" class="btn btn-primary" href="" download>Download</a>
<button class="btn closebtn">Close</button>
</div>
</div>
<script src="./js/script.js"></script>
</body>
</html>
<div class="error">
<h3>Error!</h3>
<p class="status">Unable to decompress this file!</p>
<div class="btncont">
<button class="btn closebtn">Close</button>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,106 +0,0 @@
function winsize() {
if (
(!toggled && $(window).width() / $(window).height() > 90/31) ||
(toggled && $(window).width() / ($(window).height()-219) > 18/7)
) {
$('body').css('justify-content', 'flex-start');
} else {
$('body').css('justify-content', 'center');
}
};
$(window).on('load', function () {
setTimeout(() => {
$('main').slideDown(500);
}, 100);
winsize();
});
$(window).on('resize', function () {
winsize();
});
var toggled = false;
$('h2').click(function () {
var time = 300;
if (toggled) {
$('form').slideUp(time);
$('main').css('min-height', '9vw');
setTimeout(() => {
$('h2').css('border-bottom', '0');
}, time);
toggled = false;
winsize();
} else {
$('main').css('min-height', 'calc(9vw + 260px)');
$('form').slideDown(time);
$('form').css('display', 'flex');
$('h2').css('border-bottom', '1px solid black');
toggled = true;
setTimeout(() => {
winsize();
}, time);
};
});
$('form').on('submit', function submit(e) {
e.preventDefault();
$('.wrap').css('display', 'flex');
$('.process').css('display', 'block');
var form = new FormData();
form.append('mode', $('#mode').val());
$.each($('#file')[0].files, function (i, file) {
form.append('file', file);
});
$.ajax({
url: 'huffpress.php',
type: 'POST',
processData: false,
contentType: false,
dataType: 'json',
data: form,
success: function (resp) {
if (resp === null) {
$('.process').css('display', 'none');
$('.error').css('display', 'block');
} else if (resp.status) {
$('.process').css('display', 'none');
$('.complete').css('display', 'block');
if ($('#mode').val() == 'compress') {
$('.complete .status').html(
`Original size:&nbsp;&nbsp;&nbsp;${resp.origSize} B<br>Compressed size: ${resp.compSize} B<br>Time:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${resp.time} s`
);
} else {
$('.complete .status').html(
`Compressed size: ${resp.compSize} B<br>Original size:&nbsp;&nbsp;&nbsp;${resp.origSize} B<br>Time:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${resp.time} s`
);
}
$('#dlink').attr('href', resp.dlink);
} else {
$('.process').css('display', 'none');
$('.error').css('display', 'block');
};
},
});
});
$('.closebtn').click(function () {
$('.wrap').css('display', 'none');
$('.process').css('display', 'none');
$('.error').css('display', 'none');
$('.complete').css('display', 'none');
});
$(window).on('load', function() {
let msg = 'И что это мы тут забыли?\nА ну кыш отседова!\nНашлись тут кулхацкеры понимаешь!';
let style = [
'padding: 1rem;',
'font-family: monospace;',
'font-size: 18pt;',
].join('');
console.log('%c%s', style, msg);
$.ajax({
url: './images/angry_huffman',
success: function(data) {
console.image(data);
},
});
});

View File

Before

Width:  |  Height:  |  Size: 972 KiB

After

Width:  |  Height:  |  Size: 972 KiB

76
script.js Normal file
View File

@ -0,0 +1,76 @@
function winsize(){
if (!toggled && $(window).width()/$(window).height()>(90/31) ||
toggled && $(window).width()/($(window).height()-219)>(18/7)){
$('body').css('justify-content','flex-start');
} else {
$('body').css('justify-content','center');
}
};
window.onload=function(){
setTimeout(()=>{$('main').slideDown(500)},100);
winsize();
};
$(window).on('resize',function(){
winsize();
});
var toggled=false;
$('h2').click(function(){
var time=300;
if (toggled) {
$('form').slideUp(time);
$('main').css('min-height','9vw');
setTimeout(()=>{$('h2').css('border-bottom','0')},time);
toggled=false;
winsize();
} else {
$('main').css('min-height','calc(9vw + 260px)');
$('form').slideDown(time);
$('form').css('display','flex');
$('h2').css('border-bottom','1px solid black');
toggled=true;
setTimeout(()=>{winsize()},time);
}
});
$('form').on('submit',function submit(e){
e.preventDefault();
$('.wrap').css('display','flex');
$('.process').css('display','block');
var form=new FormData();
form.append('mode',$('#mode').val());
$.each($('#file')[0].files, function(i, file) {
form.append('file', file);
});
$.ajax({
url: 'huffpress.php',
type: 'POST',
processData: false,
contentType: false,
dataType: 'json',
data: form,
success: function(resp){
console.log(resp);
if (resp.status){
$('.process').css('display','none');
$('.complete').css('display','block');
if ($('#mode').val()=='compress'){
$('.complete .status').html(`Original size:&nbsp;&nbsp;&nbsp;${resp.origSize} B<br>Compressed size: ${resp.compSize} B<br>Time:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${resp.time} s`);
} else {
$('.complete .status').html(`Compressed size: ${resp.compSize} B<br>Original size:&nbsp;&nbsp;&nbsp;${resp.origSize} B<br>Time:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${resp.time} s`);
}
$('#dlink').attr('href',resp.dlink);
} else {
$('.process').css('display','none');
$('.error').css('display','block');
}
}
});
});
$('.closebtn').click(function(){
$('.wrap').css('display','none');
$('.process').css('display','none');
$('.error').css('display','none');
$('.complete').css('display','none');
});

View File

@ -1,12 +1,11 @@
@import url('https://fonts.googleapis.com/css2?family=Epilogue&family=Secular+One&display=swap');
html,
body {
html,body{
width: 100%;
min-height: 100vh;
margin: 0;
padding: 0;
}
body {
body{
position: absolute;
left: 0;
right: 0;
@ -16,16 +15,16 @@ body {
flex-direction: column;
justify-content: center;
align-items: center;
background-image: url(../images/bg.webp);
background-image: url(bg.webp);
background-size: cover;
}
header {
header{
margin: 0;
margin-top: 2vw;
padding: 0;
text-align: center;
}
h1 {
h1{
margin: 0;
padding: 0;
color: white;
@ -34,21 +33,21 @@ h1 {
text-shadow: 0 0 1vw black;
cursor: default;
}
main {
main{
display: none;
margin-bottom: 2vw;
background-color: #fff8;
box-shadow: 0 0 0.5vw black;
box-shadow: 0 0 .5vw black;
border-radius: 1.5vw;
min-height: 9vw;
transition: 0.3s;
transition: .3s;
overflow: hidden;
}
main:hover {
main:hover{
background-color: #fff;
box-shadow: 0 0 1vw black;
}
h2 {
h2{
margin: 0;
padding: 0 2vw;
height: 9vw;
@ -56,14 +55,14 @@ h2 {
flex-direction: column;
justify-content: center;
align-items: center;
background-color: hsla(270, 80%, 80%, 0.6);
background-color: hsla(270,80%,80%,.6);
border-bottom: 0;
font-family: Secular One;
font-size: 3vw;
text-shadow: 2.5px 2px 0.5px white;
text-shadow: 2.5px 2px .5px white;
cursor: pointer;
}
form {
form{
display: flex;
flex-direction: column;
justify-content: center;
@ -72,7 +71,7 @@ form {
padding: 2vw 2vw;
box-sizing: border-box;
}
.wrap {
.wrap{
display: none;
position: absolute;
left: 0;
@ -84,9 +83,7 @@ form {
align-items: center;
z-index: 10;
}
.process,
.complete,
.error {
.process,.complete,.error{
display: none;
padding: 2vw 5vw;
background-color: white;
@ -95,28 +92,28 @@ form {
font-size: 5vw;
cursor: default;
}
.process img {
.process img{
width: 15vw;
}
h3 {
h3{
margin: 0;
margin-top: 0.5vw;
margin-top: .5vw;
font-family: Secular One;
font-size: 3vw;
}
.status {
.status{
margin: 0;
margin-top: 0.5vw;
margin-top: .5vw;
font-family: monospace;
font-size: 1.5vw;
}
.btncont {
.btncont{
margin: 0;
padding: 0;
}
.btncont * {
.btncont *{
font-size: 2vw;
}
#dlink {
#dlink{
margin-right: 1vw;
}
}