Fake C++ Compiler with Node.js - Frontend - Part 3
Tags: javascript, cpp, node, programming
Published: 2023-03-26 13:22:46


Finally the front end...

## index.html
```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
```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
```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
{% youtube KfwWq7iYgXQ %}