Meme app

by PhilQ

HTML

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css">
<div class="bg">
	<form action="?" method="post" enctype="multipart/form-data">
		<input type="file" id="picture" name="picture" accept="image/*" />
	</form>
</div>

<!-- TODO: set to "?" -->
<a href="#" class="head">Meme generator</a>

<div id="loading">
	<div class="lds-dual-ring"></div>
</div>

<div class="bottom">
	<div class="modes show">
		<a href="#" id="mode_image" class="on"><i class="fas fa-image"></i></a>
		<a href="#" id="mode_adjust" class="disabled"><i class="fas fa-crop-alt"></i></a>
		<a href="#" id="mode_text" class="disabled"><i class="fas fa-font"></i></a>
		<a href="#" id="mode_emoji" class="disabled"><i class="fas fa-grin-alt"></i></a>
		<a href="#" id="mode_draw" class="disabled"><i class="fas fa-signature"></i></a>
		<a href="#" id="mode_done" class="disabled"><i class="fas fa-share-square_ fa-share_ fa-download_ fa-check-square_ fa-flag-checkered"></i></a>
	</div>
	<div class="confirm with-text">
		<a href="#" id="confirm_cancel"><i class="fas fa-times"></i>Cancel</a>
		<a href="#" id="confirm_ok"><i class="fas fa-check"></i>Ok</a>
	</div>
</div>

<div class="sidebar">
	<div class="image show">
		<a href="#" id="upload_image"><i class="fas fa-camera"></i></a>
	</div>
	<div class="adjust">
	</div>
	<div class="text">
		<a href="#" id="add_text"><i class="fas fa-plus"></i></a>
		<a href="#" id="edit_text"><i class="fas fa-pen"></i></a>
		<a href="#" id="delete_text"><i class="fas fa-times_ fa-trash-alt_ fa-trash"></i></a>
	</div>
</div>

<div class="outerlist">
	<div class="innerlist">
		<div id="imagelist">
			<a href="#" id="upload_button"><i class="fas fa-camera"></i></a>
		</div>
	</div>
</div>

<div class="uploadpreview">
	<img id="previewimage" />
</div>

SCSS

*, *:before, *:after {
	margin: 0px;
	padding: 0px;
	box-sizing: border-box;
}

@import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;0,700;0,800;1,400&display=swap');

@mixin full {
	position: fixed;
	top: 0px;
	bottom: 0px;
	left: 0px;
	right: 0px;
}

$background: #1a1d21;
$button: #1f2227;
$border: #2c2f34;

$txt-hover: #fbfbfb;
$txt-active: #c8c9cb;
$txt-normal: #616367;
$txt-inactive: #36393d;

html, body {
	width: 100%;
	height: 100%;
	background: $background;
}
body {
	font-family: 'Open Sans', sans-serif;
}

a {
	text-decoration: none;
	color: $txt-normal;
	
	&.inactive {
		color: $txt-inactive !important;
	}
	
	&.on {
		color: $txt-active;
	}

	&:hover, &:focus {
		color: $txt-hover;
	}
}

.bg {
	@include full;
	z-index: -1;
	background: $background;
	opacity: 0;
	overflow: hidden;
}

#loading {
	@include full;
	z-index: -1;
	background: rgba($background, 0.8);
	opacity: 0;
	overflow: hidden;
	
	&.show {
		opacity: 1;
		z-index: 110;
	}
}
.lds-dual-ring {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	display: inline-block;
	width: 64px;
	height: 64px;
}
.lds-dual-ring:after {
	content: " ";
	display: block;
	width: 46px;
	height: 46px;
	margin: 1px;
	border-radius: 50%;
	border: 5px solid $txt-active;
	border-color: $txt-active transparent $txt-active transparent;
	animation: lds-dual-ring 1.2s linear infinite;
}
@keyframes lds-dual-ring {
	0% {
		transform: rotate(0deg);
	}
	100% {
		transform: rotate(360deg);
	}
}

.head {
	@include full;
	z-index: 150;
	bottom: auto;
	height: 4rem;
	background: $button;
	box-shadow: 0px -10px 15px 10px rgba(0,0,0, 0.3);
}

.bottom {
	@include full;
	z-index: 100;
	top: auto;
	height: 4rem;
	background: $button;
	box-shadow: 0px 10px 15px 10px rgba(0,0,0, 0.3);

	a {
		position: relative;
		flex-grow: 1;
		flex-basis: 0;
		height: 4rem;
		box-sizing: content-box;
		border-right: 1px solid transparent;
		text-shadow: 0px 1px 2px rgba(0,0,0, 1);
		font-size:...

JavaScript

function getOrientation(file, callback) {
	var reader = new FileReader();
	reader.onload = function(e) {

		var view = new DataView(e.target.result);
		if (view.getUint16(0, false) != 0xFFD8)
		{
			return callback(-2);
		}
		var length = view.byteLength, offset = 2;
		while (offset < length) 
		{
			if (view.getUint16(offset+2, false) <= 8) return callback(-1);
			var marker = view.getUint16(offset, false);
			offset += 2;
			if (marker == 0xFFE1) 
			{
				if (view.getUint32(offset += 2, false) != 0x45786966) 
				{
					return callback(-1);
				}

				var little = view.getUint16(offset += 6, false) == 0x4949;
				offset += view.getUint32(offset + 4, little);
				var tags = view.getUint16(offset, little);
				offset += 2;
				for (var i = 0; i < tags; i++)
				{
					if (view.getUint16(offset + (i * 12), little) == 0x0112)
					{
						return callback(view.getUint16(offset + (i * 12) + 8, little));
					}
				}
			}
			else if ((marker & 0xFF00) != 0xFF00)
			{
				break;
			}
			else
			{ 
				offset += view.getUint16(offset, false);
			}
		}
		return callback(-1);
	};
	reader.readAsArrayBuffer(file);
}

function readFile(file) {
	var reader = new FileReader();

	reader.onloadend = function () {
		getOrientation(file, function(orientation) {
			processFile(reader.result, file.type, orientation);
		});
	}

	reader.onerror = function () {
		alert('There was an error reading the file!');
	}

	reader.readAsDataURL(file);
}

function processFile(dataURL, fileType, orientation) {
	var maxWidth = 1024;
	var maxHeight = 1024;

	var image = new Image();
	image.src = dataURL;

	image.onload = function () {
		var width = image.width;
		var height = image.height;
		var shouldResize = (width > maxWidth) || (height > maxHeight);

		if (!shouldResize && 1 == orientation) {
			previewFile(dataURL);
			return;
		}

		var newWidth;
		var newHeight;

		if (shouldResize) {
			if (width > height) {
				newHeight = height * (maxWidth / width);
				newWidth = maxWidth;
			} else...