Fake C++ Compiler with Node.js - Frontend - Part 3
Gurigraphics
Posted on March 26, 2023
Finally the front end...
index.html
<!DOCTYPE html>
<html lang='en'>
<head>
<meta name='viewport' content='width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no'>
<meta charset='utf-8'>
<title>Fake Compiler C++</title>
<link rel='stylesheet' href='./style.css'>
</head>
<body >
<div id='root'>
<div id="loader" class="center">
<div class="loader"></div>
</div>
<div class="forms center">
<h1>
Fake Compiler C++
</h1>
<div class="area area_download hide">
<div class="folder_icon transparent">
<svg class="transparent" stroke="currentColor" fill="currentColor" stroke-width="0" version="1.1" viewBox="0 0 16 16" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M9 4l-2-2h-7v13h16v-11h-7zM8 13.5l-3.5-3.5h2.5v-4h2v4h2.5l-3.5 3.5z"></path></svg></div>
<label class="form-label transparent" for="customFile"><span class="transparent">Download</span></label>
</div>
<div class="area txt_area">
<div class="folder_icon folder_icon_txt transparent">
<svg class="transparent" stroke="currentColor" stroke-width="0" version="1.1" viewBox="0 0 16 16" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M9 4l-2-2h-7v13h16v-11h-7zM8 7.5l3.5 3.5h-2.5v4h-2v-4h-2.5l3.5-3.5z"></path></svg>
</div>
<div class="folder_icon confirm_icon_txt transparent hide">
<svg class="transparent" stroke="currentColor" stroke-width="0" viewBox="0 0 16 16" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"></path></svg>
</div>
<label class="form-label transparent txt_browse" for="customFile">Drag & Drop or <span class="transparent">Browse</span></label>
<label class="form-label transparent txt_remove hide" for="customFile">Click to <span class="transparent">Remove</span></label>
<form class="form transparent" id="txt" method="post" enctype="multipart/form-data" action="/uploads">
<input type="hidden" name="msgtype" value="2"/>
<input onchange="txt_change();" type="file" name="uploadFile" accept=".txt" class="form-control hide input_file_txt"/>
<label class="form-label suports transparent" for="customFile">Suports: <span class="span transparent">TXT</span></label>
<button type="submit" value="Upload" class="btn btn-dark hide">Browse File</button>
</form>
</div>
<div class="area exe_area">
<div class="folder_icon folder_icon_exe transparent">
<svg class="transparent" stroke="currentColor" stroke-width="0" version="1.1" viewBox="0 0 16 16" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M9 4l-2-2h-7v13h16v-11h-7zM8 7.5l3.5 3.5h-2.5v4h-2v-4h-2.5l3.5-3.5z"></path></svg>
</div>
<div class="folder_icon confirm_icon_exe transparent hide">
<svg class="transparent" stroke="currentColor" stroke-width="0" viewBox="0 0 16 16" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"></path></svg>
</div>
<label class="form-label transparent exe_browse" for="customFile">Drag & Drop or <span class="transparent">Browse</span></label>
<label class="form-label transparent exe_remove hide" for="customFile">Click to <span class="transparent">Remove</span></label>
<form class="form transparent" id="exe" method="post" enctype="multipart/form-data" action="/uploads">
<input type="hidden" name="msgtype" value="2"/>
<input onchange="exe_change();" type="file" name="uploadFile" accept=".exe" class="form-control hide input_file_exe"/>
<label class="form-label suports transparent" for="customFile">Suports: <span class="span transparent">EXE</span></label>
<button type="submit" value="Upload" class="btn btn-dark hide btn_exe">Browse File</button>
</form>
</div>
<div class="btn_compile noSelect hide">Send</div>
</div>
</div>
<script src='./main.js'></script>
</body>
</html>
style.css
*{
padding: 0px;
margin: 0px;
outline: none;
box-sizing: border-box;
font-family: Arial;
background: #191818;
color: black;
}
h1{
color: #b9b9bd;
margin-top: 50px;
margin-bottom: 50px;
}
#loader{
min-width: 100%;
min-height: 90vh;
position: absolute;
background: #191818;
}
.loader{
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite; /* Safari */
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.center-buttons{
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
text-align: center;
}
.center{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
.transparent{
background-color: rgba(255,255,255,0);
pointer-events: none;
}
.noSelect{
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Old versions of Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome, Edge, Opera and Firefox */
}
.hide{
display: none;
}
.btn{
margin-left: 6px;
}
.forms{
margin-top: 50px;
}
.form{
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
.area{
border: 2px dashed #000000;
font-size: 24px;
background: #2d2d2d;
width: 320px;
height: 197px;
padding-top: 36px;
}
.txt_area{
}
.exe_area{
margin-top: 50px;
}
.area_hover{
border: 2px dashed #272629;
background: #3F51B5;
cursor: grabbing;
}
.area_active{
border: 2px dashed #272629;
background: #4CAF50;
cursor: grabbing;
}
.area_download{
padding-top: 46px;
border: 2px solid #000000;
cursor: pointer;
border-radius: 4px
}
.area_download:hover{
background: #4CAF50;
}
.area_download:active{
background: #3F51B5;
}
.folder_icon{
font-size: 50px;
}
span{
font-weight: bold;
font-size: 24px;
}
.suports{
font-size: 18px;
}
.span{
font-size: 16px;
}
.btn_compile{
background: #FFC107;
width: 160px;
height: 52px;
line-height: 52px;
margin-top: 50px;
align-items: center;
font-weight: bold;
font-size: 20px;
border-radius: 4px;
border: 1px solid #000000;
cursor: pointer;
}
.btn_compile:hover{
background: #3F51B5;
}
.btn_compile:active{
background: #4CAF50;
}
main.js
const log = val => console.log( val )
const get = val => document.querySelector( val )
var txt_area = get('.txt_area')
var exe_area = get('.exe_area')
var txt_active = false
var exe_active = false
// audio
var AUDIO = {}
var audio = new Audio('ok.ogg')
AUDIO.play = () => {
audio.play()
}
// loader
var LOADER = {}
LOADER.hide = () => {
get("#loader").classList.add("hide")
}
LOADER.show = () => {
get("#loader").classList.remove("hide")
}
// active and remove
var txt_active_fn = () => {
txt_area.classList.remove("area_hover")
txt_area.classList.add("area_active")
// Confirm
get(".folder_icon_txt").classList.add("hide")
get(".confirm_icon_txt").classList.remove("hide")
get(".txt_browse").classList.add("hide")
get(".txt_remove").classList.remove("hide")
txt_active = true
update_compile_button()
AUDIO.play()
post_txt()
}
var exe_active_fn = () => {
exe_area.classList.remove("area_hover")
exe_area.classList.add("area_active")
// Confirm
get(".folder_icon_exe").classList.add("hide")
get(".confirm_icon_exe").classList.remove("hide")
get(".exe_browse").classList.add("hide")
get(".exe_remove").classList.remove("hide")
exe_active = true
update_compile_button()
AUDIO.play()
}
var txt_remove = () => {
get('.input_file_txt').value = ""
txt_area.classList.remove("area_active")
get(".folder_icon_txt").classList.remove("hide")
get(".confirm_icon_txt").classList.add("hide")
get(".txt_browse").classList.remove("hide")
get(".txt_remove").classList.add("hide")
txt_active = false
update_compile_button()
}
var exe_remove = () => {
get('.input_file_exe').value = ""
exe_area.classList.remove("area_active")
get(".folder_icon_exe").classList.remove("hide")
get(".confirm_icon_exe").classList.add("hide")
get(".exe_browse").classList.remove("hide")
get(".exe_remove").classList.add("hide")
exe_active = false
update_compile_button()
}
// txt_area
txt_area.onclick = function(){
if( !txt_area.classList.contains("area_active") ){
get('.input_file_txt').click()
}else{
txt_remove()
}
}
txt_area.ondragenter = function(){
console.log("start")
if( !txt_area.classList.contains("area_active") ){
txt_area.classList.add("area_hover")
}
}
txt_area.ondragleave = function(){
console.log("end")
if( !txt_area.classList.contains("area_active") ){
txt_area.classList.remove("area_hover")
}
}
txt_area.ondragover = function (e) {
e.preventDefault()
}
txt_area.ondrop = function(e){
e.preventDefault();
console.log("drop")
txt_area.classList.remove("area_hover")
txt_area.classList.add("area_active")
get('.input_file_txt').files = e.dataTransfer.files
const dT = new DataTransfer()
dT.items.add(e.dataTransfer.files[0])
get('.input_file_txt').files = dT.files
// Correct type
var type = get(".input_file_txt").files[0].type
var correct_type = "text/plain"
if(type != correct_type){
txt_remove()
alert("Correct type is TXT")
return 0
}
// Max size
var size = get('.input_file_txt').files[0].size
var max_size = 3525120
if(size > max_size){
txt_remove()
alert("Max size is 3525120 kb")
return 0
}
txt_active_fn()
}
// exe_area
exe_area.onclick = function(){
if( !exe_area.classList.contains("area_active") ){
get('.input_file_exe').click()
}else{
exe_remove()
}
}
exe_area.ondragenter = function(){
console.log("start")
if( !exe_area.classList.contains("area_active") ){
exe_area.classList.add("area_hover")
}
}
exe_area.ondragleave = function(){
console.log("end")
if( !exe_area.classList.contains("area_active") ){
exe_area.classList.remove("area_hover")
}
}
exe_area.ondragover = function (e) {
e.preventDefault()
}
exe_area.ondrop = function(e){
e.preventDefault();
console.log("drop")
exe_area.classList.remove("area_hover")
exe_area.classList.add("area_active")
get('.input_file_exe').files = e.dataTransfer.files
const dT = new DataTransfer()
dT.items.add(e.dataTransfer.files[0])
get('.input_file_exe').files = dT.files
// Correct type
var type = get(".input_file_exe").files[0].type
var correct_type = "application/x-msdownload"
if(type != correct_type){
get('.input_file_exe').files.clear()
exe_area.classList.remove("area_active")
alert("Correct type is EXE")
return 0
}
// Max size
var size = get('.input_file_exe').files[0].size
var max_size = 3525120
if(size > max_size){
get('.input_file_exe').files.clear()
exe_area.classList.remove("area_active")
alert("Max size is 3525120 kb")
return 0
}
exe_active_fn()
}
// compile
var btn_compile = get(".btn_compile")
var update_compile_button = () => {
if(txt_active && exe_active){
btn_compile.classList.remove("hide")
}else{
btn_compile.classList.add("hide")
}
}
btn_compile.onclick = () => {
LOADER.show()
post_exe()
}
// post
const txt = get("#txt");
const exe = get("#exe");
var post_txt = async() => {
const formData = new FormData(txt);
try {
const response = await fetch('/uploads', {
method: 'POST',
body: formData
});
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
var txt_change = () => {
log("txt_change")
if( get(".input_file_txt").files[0].type == "text/plain" ){
txt_active_fn()
}else{
txt_remove()
}
}
var link = ""
var post_exe = async() => {
const formData = new FormData(exe);
try {
const response = await fetch('/uploads', {
method: 'POST',
body: formData
});
const data = await response.json();
link = data.download_link
console.log(data);
txt_remove()
exe_remove()
LOADER.hide()
get(".txt_area").classList.add("hide")
get(".exe_area").classList.add("hide")
get(".area_download").classList.remove("hide")
} catch (error) {
console.error(error);
}
}
var exe_change = () => {
log("exe_change")
if( get(".input_file_exe").files[0].type == "application/x-msdownload" ){
exe_active_fn()
}else{
exe_remove()
}
}
function saveFile(blob, filename) {
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, filename);
} else {
const a = document.createElement('a');
document.body.appendChild(a);
const url = window.URL.createObjectURL(blob);
a.href = url;
a.download = filename;
a.click();
setTimeout(() => {
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
}, 0)
}
}
get(".area_download").onclick = async() => {
log("download")
try {
var result = await fetch(link).then(async res => ({
filename: res.headers.get('content-disposition').split('filename=')[1].split(';')[0].slice(1, -1),
blob: await res.blob()
}))
saveFile(result.blob, result.filename)
location.reload()
} catch (error) {
console.error(error);
}
}
Demo
💖 💪 🙅 🚩
Gurigraphics
Posted on March 26, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.