summaryrefslogtreecommitdiff
path: root/public
diff options
context:
space:
mode:
Diffstat (limited to 'public')
-rw-r--r--public/404.html18
-rw-r--r--public/css/404.css20
-rw-r--r--public/css/console.css60
-rw-r--r--public/css/header.css55
-rw-r--r--public/css/home.css182
-rw-r--r--public/css/login.css47
-rw-r--r--public/css/main.css307
-rw-r--r--public/css/people.css44
-rw-r--r--public/css/profile.css121
-rwxr-xr-xpublic/fonts/facebook.otfbin0 -> 25740 bytes
-rw-r--r--public/fonts/sfpro.otfbin0 -> 298944 bytes
-rw-r--r--public/fonts/sfprobold.otfbin0 -> 334728 bytes
-rw-r--r--public/home.html17
-rw-r--r--public/js/api.js63
-rw-r--r--public/js/header.js25
-rw-r--r--public/js/home.js233
-rw-r--r--public/js/login.js29
-rw-r--r--public/js/main.js22
-rw-r--r--public/js/people.js65
-rw-r--r--public/js/profile.js88
-rw-r--r--public/login.html170
-rw-r--r--public/people.html15
-rw-r--r--public/profile.html17
23 files changed, 1598 insertions, 0 deletions
diff --git a/public/404.html b/public/404.html
new file mode 100644
index 0000000..04ddadc
--- /dev/null
+++ b/public/404.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <link rel="stylesheet" href="/css/main.css">
+ <link rel="stylesheet" href="/css/404.css">
+ <link rel="stylesheet" href="/css/header.css">
+ <title>XSSBook - Not Found</title>
+</head>
+<body>
+ <div id="header">
+ <span class="logo"><a href="/">xssbook</a></span>
+ </div>
+ <div class="error">
+ <span class="logo">404</span>
+ <span class="gtext desc">Page not found.</span>
+ </div>
+</body> \ No newline at end of file
diff --git a/public/css/404.css b/public/css/404.css
new file mode 100644
index 0000000..38035a4
--- /dev/null
+++ b/public/css/404.css
@@ -0,0 +1,20 @@
+body {
+ background-color: #f0f2f5;
+}
+
+.error {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100vw;
+ height: 100vh;
+ flex-direction: column;
+}
+
+.error .logo {
+ font-size: 100px;
+}
+
+.desc {
+ font-size: 40px;
+} \ No newline at end of file
diff --git a/public/css/console.css b/public/css/console.css
new file mode 100644
index 0000000..bc07969
--- /dev/null
+++ b/public/css/console.css
@@ -0,0 +1,60 @@
+body {
+ margin: 0;
+ padding: 0;
+ background-color: #181818;
+ display: flex;
+ flex-direction: column-reverse;
+}
+
+@font-face {
+ font-family: sfpro;
+ src: url("../fonts/sfpro.otf") format("opentype");
+}
+
+div {
+ background-color: #282828;
+ font-family: sfpro;
+ margin: 15px;
+ margin-bottom: 0px;
+ border-radius: 5px;
+ padding: 10px;
+ width: calc(100% - 50px)
+}
+
+span {
+ display: inline-block;
+ padding: 0;
+ margin: 0;
+ color: #ffffff;
+ font-family: sfpro;
+ margin-right: 10px;
+}
+
+.json span {
+ display: inline;
+ margin: 0;
+}
+
+.key {
+ color: white;
+}
+
+.value {
+ color: white;
+}
+
+.boolean {
+ color: aqua;
+}
+
+.null {
+ color: blue;
+}
+
+.number {
+ color: yellow;
+}
+
+.string {
+ color: #4ae04a
+} \ No newline at end of file
diff --git a/public/css/header.css b/public/css/header.css
new file mode 100644
index 0000000..a491f33
--- /dev/null
+++ b/public/css/header.css
@@ -0,0 +1,55 @@
+#header {
+ height: 3.5em;
+ background-color: white;
+ position: fixed;
+ width: 100vw;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, .05), 0 8px 16px rgba(0, 0, 0, .05);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ z-index: 5;
+}
+
+.spacer {
+ margin-bottom: 5em;
+}
+
+#header .logo {
+ position: absolute;
+ font-size: 2.5em;
+ padding-left: .5em;
+ padding-top: .2em;
+}
+
+#header .buttons {
+ flex: 1;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+#header .buttons a {
+ padding: 0px 50px;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #606770;
+}
+
+#header .buttons a:hover {
+ background-color: #dddfe2;
+}
+
+.selected {
+ color: #1778f2 !important;
+ border-bottom: 3px solid #1778f2;
+}
+
+#header .pfp, #header .pfp img {
+ position: absolute;
+ right: 1em;
+ top: .5em;
+} \ No newline at end of file
diff --git a/public/css/home.css b/public/css/home.css
new file mode 100644
index 0000000..33d72c0
--- /dev/null
+++ b/public/css/home.css
@@ -0,0 +1,182 @@
+body {
+ background-color: #f0f2f5;
+}
+
+#posts {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.post, .create {
+ width: 40em;
+ height: fit-content;
+ background-color: white;
+ border-radius: 10px;
+ padding: 15px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, .05);
+ margin-bottom: 1.5em;
+}
+
+.post {
+ padding-bottom: 0;
+}
+
+.create {
+ display: flex;
+ flex-direction: row;
+}
+
+.create button {
+ all: unset;
+ background-color: #f0f2f5;
+ border-radius: 3em;
+ margin-left: 1em;
+ flex: 1;
+}
+
+.create button:hover {
+ background-color: #e4e6e8;
+ cursor: pointer;
+}
+
+.create button p {
+ margin-left: 1em;
+ font-size: 18px;
+}
+
+.postheader {
+ display: flex;
+ flex-direction: row;
+ margin-bottom: .5em;
+}
+
+.postbuttons {
+ display: flex;
+ flex-direction: row;
+}
+
+.postbuttons>span {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ padding: 7px;
+ border-radius: 5px;
+ color: #606770;
+ margin: 3px
+}
+
+.postbuttons>span:hover {
+ background-color: #e4e6e8;
+}
+
+.postname {
+ margin-left: 1em;
+ display: flex;
+ flex-direction: column;
+}
+
+.icons {
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAMaCAMAAABESx1iAAAC/VBMVEVHcEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcHiEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD3uSgcHiEAAAAcHiEAAAAcHiEAAAAcHiEAAAAAAAAcHiEcHiH3uSgcHiEcHiEcHiH3uSgcHiEcHiEcHiEcHiEcHiEcHiEAAAAAAAAAAAD3uSj3uSj3uSgcHiH3uSgAyIL3uSgAyIIAyIIcHiEAyIIAyIL3uSj3uSgAyIL3uSj3uSgcHiEcHiEcHiH3uSgcHiH3uSgAyIIcHiEAyIIAyIIAyIIAyIIAyIL3uSgAyIIcHiEAyIIAyIIAyIIAyIL3uSgAyIIAyIIcHiEAyIIAyIIAyIIAyIIAyIIAyIIAyIIAyIL3uSgAyIIAyIIAyIIAyIIAyIL3uSgAyIL3uSgAyIL3uSgAyIIcHiH3uSgcHiEcHiH3uSgcHiEcHiEcHiH3uSj3uSj3uSj3uSj3uSj3uSgcHiH3uSgcHiEcHiEcHiEcHiEcHiEcHiEAyIIcHiEAyIIAyIIAyIIcHiEAyIL3uSgcHiEcHiEAyIL3uSj3uSgcHiEAyIIcHiH3uSj3uSj3uSj3uSgAyIIAyIIAAAAcHiEAyIL3uSi0ITmYAAAA+3RSTlMA6yr7BIHV8wL9Bo+l6b0cDJPhEL/dFPnx7e/1wefJLNkmTrcYiZUKhc1qfjDlFnB4GqlAi7mr17t2jdsIpyTPSB6vZPd0KLVMOONaVDpQIpFEbhIgSp9ol3yHDplcy3qhNN8uPgLFnaM2rTxYm8ezYoOx0cNWQmz1BNPzUvtm+WBGi2rxUN1U+dWlcCrZDF4ych529/U+/f2911xa9wavPky9venbapEqdEIIHPNYUocq906JegwCFiynQredQMs8Dpn7Ejjlk7MK61Ji7S56qWjt8+d+bqPvsTBOSGQ8oZvX6wYKm++f20y/8aeNDjYWz7Hn8cXXEMPJx5v4g7kAABPcSURBVHja3ZsFcBtZtoZ/WbLIFliyLLIsy5KZHVPMHGPMFCcOTphxJslMJjOzw7g0O7NMb98yMzM+ZmZmhuO4Xndb0r233V0VbyaVevuVSyXpq3v6gPt2dcdBEldFvNBiKYxXuCBQf8pGSWyn6sEospCxeSzmdsfGmo1kKUqLGRv1dSFJVx/ZrqRW2GzbwbHdZttYVW8hSQiKLMqxTlEfYH2pEow+OiWnazN2AXuJbjLTZbS5gApqBjBKdB6MZqoA4jQGIEwUAmOM4kAhxQDkEvWDEaNCwEJuOXOiHjDcZAFC5IVEA0XmVGaV9ivvL1D7EHBiV306WgONbRRspoGybQbyZ6UyqJVeFLxxUjCksl4JUwwbXG+XTV2qUpTQUaQ4sVy2P90d9NMZaHf0OC0CWlOwW6j34Ezb3ObJTZMtKI8/frLKK047i4g8+xYGNv+GtO57WI7knTneHWK/VXcB6/ZyPwn4y7dbZZFLGuRa5VZosh0o1zblgF/b+AHaYH7CoIxtYp42SJsJZBgkkYEJ0cjfSUp5YYZTimCGU7LYilFH089AP2v9SvW7o99R/SnoT05/2ncF03i2vCNmj5sgstNHjoa2tgYH+XaCw9pI3T1W5V1PNzVyaTVSjikdNocaWSjKAUcOpQKafN1WJHbfTK/q9iUjjFMP0ExmpOihcShkO6xAkGgknZAjGwqFDQDayZFAioZCKFjalL0hjjRtFiiEpgDYy2lmk1ndAQlXNe0yreSXzXLRsiNKknPt5GkhcigZiEm624xE1MRljcJVbJCoPVJbylWKvlHodMeyxPed6+g0DVY0O3eahCmwvcpDkfNTbHLcXmWd7+wOsWnfI2YPh/q1zQxRSNtMEx2GjpqZxZaobQ/15WmJvKxOoiloUkQU1zaniRb0ltCwptlHdBqaxImKtM0UUXaWdtp9ofZa3DseeOLp565de+7pJx6AyOseX0vy+OvAcd8ja2tPPfPs888/+8xTa2uP3MfMI2s3Hk1+vO/RG2uPsFBrNz4kffelf7/2r5L/0I21VMAHHl97FMCXpIN86h8APLr2eDKNJ9aekkOtyfylHPCptSeg8PTaM5B4UDYPQuKZtaeh8Nzas5D4dWURJJ5dew4K19aeh8Sv3Fhbu/GbkHh+7Zpg8EvXfvU3wAyLxpCjsQwe+MD7H5QSeP8H/kzJgGX9j2ufWlN405/+uZw1q/RvlZyVvP+EVSp158FrKSGtelDqDuuowCPcFP6YF390Hzj+4E3pYG9VT/ut733fjRvve+9bH8AdUtUaoBSB1ipO+D1ZmSmyPH6mWj1eMLyeVqQIZIEnK4AUlCmYTLr3Rj9r/UqF7iy7gJFK1h2uo8v2RhvrKAcdN9NSKTQgqr4JTQqPevFTMH50Wlt0+MkyrmncfqJ2TWO1EflNWiYaJgrXaJkpI5FRa40pQAq24IKb1S+BItko+EtEY6Y0uaIhhlPX+PWMcVXPWIr0TPigjhnIhGhajKklGSqT6AwrIvgQVOanw7S3tbhG05RRwYDHqzmgAF70t2mZXX6g1adlToWBNr+GyBs4AjSHM2t71CPPtMWAFpIwzyLJeZI4E+yDgr3IVpwylJ+ZmUt0DEkKm/kzuJL2IMmKY4douFM4pGOGCrt1zDSNaRt7I2VomyJagGha9mXK7Cq0eCHWE4+QjLF8GyD04Dxeawrb9AzRbj3joX1WbZO5lxpMUFFTdzFEy6il83bVPfIAXciudAGL1NTBi2xy7scGD9tyS5nZTYNIs8Q9F5mgXUwIa1Y9HYBrvr9OfZwuqsB4q41oU26DlNFM7Xv312+q50yk3Vir2YNusoxo981AO3V63bIXry2XBmiDSxB4KIuKoFAUFsdfPkhIQlPC7uIGgRRAg6uFzJgPgq1BKTHTMMatQbSFmeVd/JqxBmaKdiTXKGbXMjOTHn7NjiIwwiuc8UyCUTDBzIpQ65GHmJko4M1MSTjVHVo8xZubnv7UZn2pZAY8DdRe4D9wvuxSRp7nJtSYYtczj7dTA26LmI0ULOasDIicoxS2HJN4iAOUximoHOLIAWPIxhsbd6wsEsjipi0aMzMW0VgEU5M++SUlRBvmb7aEDJqwslRdvduLJsFk2IjO9XY6HJ29SjdUlebPJxLz+aQyJifxgFc5Nt4IZGSZLaHuxmMhIt+dTu5qHiTcVZWBTZO7gg3mclWT62N7Sq4wOYcXaeYC/OQywVHJT66KN1X85Ny8cVsE09/ks9iMlvb4WcAmRPOdnSu1Wt2T/T6An1wl2rInGyKRhsnsRsFkBObsrbamgwebbK12wSAnF9axfIMhf0yuWJxc7hwUZicA1eQClVXuvBNllnbgDicHU21B0EgSxmBBrUlIgApKyjIlykoKhFOrllKDg31uhmqZKWAbygLNcZ8QLAHg6oxEOl3zC/aSIDPGMkkEA7t3B4IuoMzIX0aBzsAkMBnoBDJJNJHdkNgd2YIRoulnoJk1AJY1q5R90ugOwLqj01H9KegjTlvjnIO9xFa46+zZXYW2ErsonP66jWegdX6noEr8+5Fkv78E/L5Th20NpVCoE3bLQivM1A8FayF/zu0CAtSGDXbx59xZYIGi2OCshZnQQ0Be9aobVrvKrDYCqAoW9iwZBsVoWQfkYmKniWhUzGDSdgkywwtHo2LWdksFtCu9TEOH5qHVnSwaIDpQ3M86yj+djLV1h7SmoM+rt9K8Kppb/FvRcOiu0TLvepee+d73NM0nP/PKrVuvfOaTm83HXpaP/vLHNKJ9XDYfh4b5iGw+omXe9tEPfvCjb9tsPowkH367aP7qr/8JUGq69RcQedu/fes7wHe+9Xd/AzXf/P6PfvKTH33/m9DgF//3f34RW8NIEkta5moVkDDctiEVnIHA7RkV4BR+ug+VJFGpNvf+w1XiuIo7J09+5mBsyYNIzfRwoXxhto4VDk/zsodOty5CYbE1lzKYqX4RHA9zadsg4WpyOJrcGO+GTazNTcsu1xS5XOPqaTdNQWK5CVAbh0sJ6diKaVqGxJRGNBdNuVzLtKIyJjk5OWs5pMnPmXwT0pjy85GmNN9PHKXQ586vc6Sw+TpHaVTXOZLp7iYZ8TpHEgOlpX7tuwIjYNS5K/B69e4KYjFSsGwyhw4xI0a7cIFFEzKwHayysQxY1rI5aGNZC5XabKxSne7od1QkqkxhKgP/z8g7fDhPW5iJzHnawmxmShR5eVrqsPSd4g+rzY6No9cc3oGfBbZFjKRgNA8LYphGrFCwnqBtvDGfQJqRCG+MVqSxGu/FtUQ/g+oJpDlhFlpAJ+RVCcA6QaommI3A9TCRsXobREyEylF5jQlqqPl0DbSwBjrtd75bsjt+nWcozi08Q2H7TnY2kfKq3i2zgezUq7hbMqPaLVk09W7Jo94t9w2QRHjf5t2yf6ihsLBhqF9jt1xyAa4ltlvylUYirFLt7uh2VHcKWyCa4wuHfTlRqDCdMtQNud1DdYYsiLSsuqHgphYhhSMRpFnlV0UNbqRxG6JcC+qAHTuAjdc6rgm+Id4M+ZgJi89QwvpGjLazAxIdO+VoYgZF5qL6eulFyUDMek/cYIjvUWWNI6tIQ6eEtrVQqjvZLRA5kuroEZPuFO4ZVfEBsg3E90NF71GLpT/W1U+W7Dkx5eoFmoXErOFIpAaMFdqTO48JI41gPreMJplpeQGGBIjIiBoDaguZ8Ywj0gu/pNAbwc1RLi+67tyJYzRwDDud85QAw2XICiRQWooEZTkmwNPVF6LFoa7FcKhvCCqOSZXSQNMINLDKP/eMnqPlPoXy7Gno7olTYJwkgZPMNJFnfmV/AR074nPD5IxzvbbU580crKPSwXw8XGbwMGNpxCoRKSZf2PmMlzKIKCCZHbhUTEbOjEeJGjrCpYNL6HAFOGMpxgUqgWFzNE8gkaidk9cohssgTr7LHVWnqWp3+2w7UVy/Uv0rBk9PdrnHYLEYPOVHe3AXGeszV0ckqs3n9whiH52MJuolEtFxOgrGXksp0pRSBdI45wErqyCX61uvtZlaZkFQThR+cu6sw3nHFKN6rmw0RRIAksZq5A3VA63JaFYbH62T8iZJI1qg15oTHN0PQiUgZODsEfruRJorxFdq2QvG0dFL0bwVk2klL3qSKsGzp8/ssBiNFoe5bwyvBfZih6PYrmWKSaJYyzhk49A3W4s2TRYLTWuZVY/d7lnVEJfoBeAFugQ1pvYL8l51od2kNhXUo0yAKtTlGz125Z3HyBrBEmbvxCLDlRJhVi4zDId+NPUAuHe3yTgF2g8fX8qsGI+CcXAJpTThjZ0Yr8hcOk67mMk3YM6DFDXETLAdIwUos5RtGAMz1eUoOoNwcpOKmpkxO9F/JL3mci4zh/uwN3nYndWHTjYwE1/AVC1kXrJR8FwOM51ZaDjZ65xBptK2vczszkRuXZBsRT2yCfVzHa1FIUlYZoxEZChipu4sApQmOMJMxXWrjZnAJDNjJ2uIYSxlZuQcWjllAqO5HiNjdUt9p9sNfrqMu8nZnA5tYadmT5G2ImxraRrSNjDVBtvcmgbIOxLZo22A/dWkY46RtqlZ0I5mmgk2ujWzNrfGdCrduZXu3Hu8xGEs8DJT0Mzf7TaTeBvMKRJ6RmmAO3hKffeiaRixHiNnjgs9KOD6VmAkDi/uFt2Uu03bEJGOO0B6boFIx+0k0nNEeq6qVv7rzNzcXNnpG/1oehnoZa1XqV53dDv6GpOgc3qKDizqmbnqPToGMcd1qLEXHXcQcNBwEyL15RfGEsoGFzwkiJXR9LG3GfYLJ20lNysCY3CVi+t7CQzfi6zc9kEw9rdL4acOBGxATft2cDQuI7twb5eXUFO4FzxN8/vIDoDyWpZVIz3TboVsPJUQIVrZ2LIaoTYvQMGErRItOB2FBh2DltFRy2CHegR7jodpRyKxg8LH99h5k0ueqQm8+CImpjzk5A1lAb2NRmNjLzBFgqlEASkUIFNtKInKGMuYKTPyxrOAyIaoxoJHyK0c2RvmKMpzeTNDsa4BWQx0xWiGNzX+ZhxyEDkOodlfA55BqkDHnj0dqKBBCFjPUM4sMJtDZ6xQqUYaOHp0gBoVITJU4vGUxLAV/n7953XMW95y/88BeMe7f+t3IfLk+vr97/n9d77xsfXfhsj96+vrTz4mvTz2exB543qSN75DFH+4nuJ3wPP5T6SXPPZuXvzXf96fXvLke3iz/pZ1hpQ847/lpJjiS/7hj9+5/u2kkAIw8fo3SMVLpUr82v1Prv8yM294veTeKQd6M1S8+RfejB8/tn7/JzTE69/ww/Vv/8vnocBHk3/+Y/2fsTWWKEPH2HYGFwHYhxeqIBIu7So/05MdyL8QgkhoFqbi/MUa5BZBxJKQYwE1AbsoTtAsFB7qBI810xFNPcUd5oW3uzV9AhSf4Q1xEUzli2A4w02L6UVdQb7k0uvZ1AKJ2cRs6U5ionc7YA8ngCgZQmFaYmZ7r+SyF1ETOQQVh4KHcL0VrZkaond7KWUetkKBjyb/5FMNtoq96oXGpupQfAg8ebUNLTbaILICxsEgcfTz7SGJ9DP8En6g8pfp5/61YIxufLnxUu4GI75h5IW2xo5BMBpTh6DQ5ZodATD2EPnisjBeHnFQARhVRA2mq5JZnjQQ7eLLtxEdyrhKjtImIlsMHC1Eo9tM8bZjm/7eo4EkOtuv7yVqsgvmJVKoP0PODgjUBJVF3gN1dqg4eHHAV2eq6dj65NqaIqSaeayuocV/tbNuvt6d4+gFo96QMxY1YYN9FWAIF9/FI7xJjZ5Iqi0ERrULIEqavWF+csNJI7FMRn5yddyftp9w8JPrTAlzU8cJYXJXge1eAMU5wO5Bvny/CYM+L/IMvagP1guTiwKVjbh0HrhQDJ7mk5DJGrTuEP5wPfWvDaW+ivw+EwRihuIVVxtFPBVQU080Wn18mwn3Fhf5tUWUiDRFLKRjJoM2HTNqoy0bMZp+BiLRgJ5BTDI/MxDFdE0gqmcoFNMztuCkjiHbqK7x6EfbcgY6WfvJhXuM11zs1RblV66UezVFG9DGlCgk1V2qNi2KALLIpFpCpDxiMRKp4rk74IsBXT5o7P/xeaCnCRrk1AIzJVrmSoliNehpBeJFWqbLAVCVlrGShBVqKgHYAbf89nYf3LRQmhbcRS5f1jMWi+KrH4IaouR/1q9Qm8jGr27FZtVJcR3lClKaaZXqjDCjhkXbipjWFKzSe8FwusrqYdFIIq1EQwr59bATaZkoAG1jhYO0DUCaBjJuXdO1hWi6ppoZVaXDigJYd26L/gKLpaAfm0g4Lw4nEsMXnQm1cZZZIWEtc6pDXbRfXVbURVXAguGM5OlZVKC++zIF2zYOaBFNyIvxwDFM2jeZ09NAxcCpUPWcOtpDZwC8uCPkLFVnMH0a0M4662Jn87RXq9LWhYcXT4c0u6PPD24l+QFum1tptMwrr+iZ735X23z6c8DnPq1lXv0C8IVXNaN9GfiyRrS337r1ja9+9Ru3br1dbT778kbOL38War74FVl85YvQ4Gtf//rXsBUymn0OCV9zDCJdFkpi6RJNNqXJ1ngQPDpKEj7RhGdN2/us1r4udIVFY4OjI3ruXJRANrXB/o6LFzs2mzCC9uQaVTTfrPWKfBzqogOqrFlu2bdbKWLNvpDRGPA1ZOA1YmREzzideiY/f2tmxJmfHwrl5ztH9M0dH4dlrV/pa0h3OTToLfPmEeV5y3rVpowMTiKngcrUxmsgBYNXbfKcFHe54uTMUx+ciFyAi4jK9Y1uNP0M9LPWr1SnO1vg/wDZSSIGDqRwtgAAAABJRU5ErkJggg==');
+ display: inline-block;
+ width: 18px;
+ height: 18px;
+ background-size: auto;
+ background-repeat: no-repeat;
+ margin-right: .5em;
+ filter: invert(39%) sepia(21%) saturate(200%) saturate(109.5%) hue-rotate(174deg) brightness(94%) contrast(86%);
+}
+
+.blue {
+ filter: invert(39%) sepia(57%) saturate(200%) saturate(200%) saturate(200%) saturate(200%) saturate(200%) saturate(147.75%) hue-rotate(202deg) brightness(97%) contrast(96%);
+}
+
+.like {
+ background-position: 0px -132px;
+}
+
+.comm {
+ background-position: 0px -113px;
+}
+
+.createpost {
+ position: relative;
+ background-color: white;
+ width: 450px;
+ padding: 20px;
+ border-radius: .5em;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, .1), 0 8px 16px rgba(0, 0, 0, .1);
+}
+
+.createpost .postheader {
+ margin-top: 20px;
+}
+
+.createpost textarea {
+ border: none;
+ resize: none;
+ outline: none;
+ font-family: sfpro;
+ font-size: 24px;
+ margin-top: 10px;
+ width: 100%;
+ height: 120px;
+ margin-bottom: 20px;
+}
+
+.createpost>span {
+ margin-top: -10px;
+}
+
+.primary {
+ margin: 0;
+ width: calc(100% - 20px);
+ font-family: sfprobold;
+ height: 15px;
+}
+
+.close {
+ top: 1em;
+ right: 1em;
+}
+
+.comment {
+ display: flex;
+ flex-direction: row;
+ margin-bottom: 10px;
+}
+
+.comment p, .post p {
+ word-break: break-all;
+ white-space: normal;
+}
+
+.comment>span {
+ display: flex;
+ flex-direction: column;
+ margin-left: 10px;
+ background-color: #f0f2f5;
+ border-radius: 10px;
+ padding: 10px;
+ padding-right: 10px;
+}
+
+.larger {
+ font-size: 18px;
+}
+
+.newcomment {
+ display: flex;
+ flex-direction: row;
+}
+
+#comments input {
+ all: unset;
+ padding: 10px;
+ border-radius: 10px;
+ width: calc(100% - 20px);
+ background-color: #f0f2f5;
+ font-family: sfpro;
+} \ No newline at end of file
diff --git a/public/css/login.css b/public/css/login.css
new file mode 100644
index 0000000..7e5cde7
--- /dev/null
+++ b/public/css/login.css
@@ -0,0 +1,47 @@
+.login {
+ background-color: #f0f2f5;
+ display: flex;
+ justify-content: center;
+ align-content: center;
+ flex-direction: row;
+ padding: 8em 0em;
+}
+
+.prompt {
+ display: flex;
+ position: relative;
+ flex-direction: column;
+ background-color: white;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, .1), 0 8px 16px rgba(0, 0, 0, .1);
+ border-radius: 8px;
+ width: 396px;
+ padding: 10px
+}
+
+.show {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+
+.login-button {
+ margin-bottom: 10px;
+ font-size: 20px;
+}
+
+.newacc {
+ margin: 10px 70px;
+ margin-bottom: 20px;
+}
+
+.signacc {
+ margin: 10px 70px;
+ margin-bottom: 0;
+}
+
+@media (max-aspect-ratio: 2/3) {
+ .login {
+ flex-direction: column;
+ align-items: center;
+ }
+} \ No newline at end of file
diff --git a/public/css/main.css b/public/css/main.css
new file mode 100644
index 0000000..c1b4fa2
--- /dev/null
+++ b/public/css/main.css
@@ -0,0 +1,307 @@
+body {
+ background-color: white;
+ width: 100vw;
+ height: 100vh;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ flex-direction: column;
+}
+
+@font-face {
+ font-family: facebook;
+ src: url("../fonts/facebook.otf") format("opentype");
+}
+
+@font-face {
+ font-family: sfpro;
+ src: url("../fonts/sfpro.otf") format("opentype");
+}
+
+@font-face {
+ font-family: sfprobold;
+ src: url("../fonts/sfprobold.otf") format("opentype");
+}
+
+.logo {
+ color: #1778f2;
+ font-size: 3.5em;
+ font-family: facebook;
+}
+
+.text {
+ font-family: sfpro;
+ font-size: 28px;
+ font-weight: normal;
+ line-height: 32px;
+ width: 500px;
+}
+
+.btext {
+ font-family: sfpro;
+ color: #1778f2
+}
+
+.error {
+ font-family: sfpro;
+ color: #f02849;
+ padding-top: 10px;
+ margin-bottom: -10px;
+ font-size: 15px;
+}
+
+.gtext {
+ font-family: sfpro;
+ color: #606770
+}
+
+.label {
+ font-family: sfpro;
+ color: #606770;
+ font-size: 15px;
+ padding-top: 10px;
+ padding-left: 10px;
+}
+
+.stext {
+ font-family: sfpro;
+ font-size: 10px;
+}
+
+.mtext {
+ font-family: sfpro;
+ font-size: 15px;
+}
+
+.ltext {
+ font-family: sfpro;
+ font-size: 22px;
+}
+
+.ctext {
+ display: block;
+ font-family: sfpro;
+ text-align: center;
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+p {
+ padding: 0;
+ margin: 0;
+}
+
+span {
+ padding: 0;
+ margin: 0;
+}
+
+footer {
+ bottom: 0;
+ height: 400px;
+ background-color: white;
+}
+
+input {
+ flex: 1;
+ font-family: sfpro;
+ background-color: white;
+ padding: 10px;
+ margin: 10px;
+ margin-bottom: 0;
+ border-radius: 5px;
+ border: 1px solid #dddfe2;
+ color: #1d2129;
+ font-size: 18px;
+}
+
+.radiomenu {
+ display: flex;
+ flex-wrap: wrap;
+}
+
+.radiomenu span {
+ display: inline-block;
+ position: relative;
+ font-family: sfpro;
+ background-color: white;
+ margin: 10px;
+ margin-bottom: 0;
+ border-radius: 5px;
+ border: 1px solid #dddfe2;
+ color: #1d2129;
+ font-size: 15px;
+ flex: 1 0 auto;
+}
+
+.radiomenu span label {
+ padding: 10px;
+ display: block;
+ box-sizing: border-box;
+ width: auto;
+ color: #1d2129;
+}
+
+[type="radio"] {
+ height: 40px;
+ margin: 0;
+ position: absolute;
+ right: 10px;
+ top: 0;
+ text-align: left;
+}
+
+select {
+ all: unset;
+ flex: 1;
+ font-family: sfpro;
+ background-color: white;
+ padding: 10px;
+ margin: 10px;
+ margin-bottom: 0;
+ border-radius: 5px;
+ border: 1px solid #dddfe2;
+ color: #1d2129;
+ font-size: 15px;
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYAgMAAACdGdVrAAAADFBMVEVMaXEFBQUFBQUFBQXG+MOgAAAAA3RSTlMAn3BcqiM3AAAAOUlEQVQIW53BsQ2AMAwAMJelV6C80qPYw4k9JmskbqA2px5uNIlcoxF7FmbFxuhckA2iwNzgev3wAR4FDUQbc/qhAAAAAElFTkSuQmCC");
+ background-position: right 10px center;
+ background-repeat: no-repeat;
+ background-size: 15px;
+}
+
+input:focus {
+ border: 1px solid #1778f2;
+}
+
+.primary {
+ all: unset;
+ font-family: sfpro;
+ background-color: #1778f2;
+ color: white;
+ padding: 10px;
+ margin: 20px;
+ border-radius: 5px;
+ padding-bottom: 15px;
+ text-align: center;
+ cursor: pointer;
+}
+
+.success {
+ all: unset;
+ font-family: sfpro;
+ background-color: #42b72a;
+ color: white;
+ padding: 10px;
+ margin-left: 10px;
+ margin-right: 10px;
+ border-radius: 5px;
+ text-align: center;
+ cursor: pointer;
+}
+
+.bold {
+ font-family: sfprobold;
+}
+
+.line {
+ width: calc(100% - 40px);
+ margin-left: 20px;
+ margin-right: 20px;
+ border-bottom: 1px solid #dadde1;
+ margin-bottom: 10px;
+ margin-top: 10px;
+}
+
+.fullline {
+ width: calc(100%);
+ border-bottom: 1px solid #dadde1;
+ margin-bottom: 10px;
+ margin-top: 10px;
+}
+
+footer {
+ text-align: center;
+ font-family: sfpro;
+ padding-top: 30px;
+ padding-bottom: 30px;
+ font-size: 13px;
+ color: #737373;
+}
+
+#popup {
+ position: absolute;
+ width: 100vw;
+ height: 100vh;
+ background-color: rgba(255, 255, 255, .8);
+ margin: 0;
+ padding: 0;
+ top: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.row {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+}
+
+.row input {
+ width: 50%
+}
+
+.close {
+ position: absolute;
+ z-index: 2;
+ width: 20px;
+ height: 20px;
+ right: 12px;
+ top: 12px;
+ cursor: pointer;
+ background-size: 20px;
+ background-position: right;
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYAgMAAACdGdVrAAAADFBMVEVgZ3FHcExgZ3FgZ3Fd28LEAAAAA3RSTlOfAHBcPEovAAAASklEQVQIW2MIBQMG7FRmaGgmUG5PaKgdkNovGvIPSPkfDf4IpII/+h8FUiF/7EVB2u3/gE3x/4hMQQShSqAaoNqhhkGNhlqE2y0A1E85Y0JErBoAAAAASUVORK5CYII=');
+}
+
+.hidden {
+ visibility: hidden;
+ pointer-events: none;
+ display: none !important;
+}
+
+.pfp, .pfp img {
+ display: block;
+ width: 2.5em;
+ height: 2.5em;
+ border-radius: 3em;
+ background-color: #e4e6e8;
+ flex-shrink: 0;
+}
+
+.nb {
+ margin-bottom: 0;
+}
+
+form {
+ all: unset;
+ border-radius: 10px;
+ margin-left: 10px;
+ width: 100%;
+}
+
+#load {
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ padding-bottom: 20px;
+}
+
+#load a:hover {
+ border-bottom: #606770 1px solid;
+} \ No newline at end of file
diff --git a/public/css/people.css b/public/css/people.css
new file mode 100644
index 0000000..b8cf025
--- /dev/null
+++ b/public/css/people.css
@@ -0,0 +1,44 @@
+body {
+ background-color: #f0f2f5;
+}
+
+#users {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.person {
+ width: 30em;
+ height: fit-content;
+ background-color: white;
+ border-radius: 10px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, .05);
+ margin-bottom: 1.5em;
+ display: flex;
+ flex-direction: row;
+}
+
+.profile, .profile img {
+ border-radius: 10px 0px 0px 10px;
+ width: 10em;
+ height: 10em;
+ padding: 0;
+ display: block;
+ background-color: #e4e6e8;
+ flex-shrink: 0;
+}
+
+.info {
+ margin: 20px;
+ display: flex;
+ flex-direction: column;
+}
+
+.info span {
+ width: 280px;
+ margin-bottom: 5px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+} \ No newline at end of file
diff --git a/public/css/profile.css b/public/css/profile.css
new file mode 100644
index 0000000..4c5ae10
--- /dev/null
+++ b/public/css/profile.css
@@ -0,0 +1,121 @@
+body {
+ background-color: #f0f2f5;
+}
+
+.spacer {
+ margin-bottom: 3.5em;
+}
+
+#top {
+ background-color: white;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 100%;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, .05);
+}
+
+#banner {
+ background-image: linear-gradient(#949494, white, white);
+ height: 30em;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+}
+
+#banner div, #banner img {
+ width: 80em;
+ height: inherit;
+ background-color: #e4e6e8;
+ border-radius: 0px 0px 20px 20px;
+}
+
+#info {
+ width: 80em;
+ display: flex;
+ flex-direction: row;
+}
+
+.face {
+ background-color: #e4e6e8;
+ height: 12em;
+ width: 12em;
+ border-radius: 7em;
+ border: solid 5px white;
+ margin-top: -2em;
+ margin-left: 2em;
+ margin-right: 2em;
+}
+
+.infodata {
+ margin-top: 2em;
+ display: flex;
+ flex-direction: column;
+}
+
+.infodata span {
+ margin-bottom: .5em;
+}
+
+.profilebuttons {
+ width: 80em;
+ height: 3em;
+ display: flex;
+ align-items: center;
+}
+
+.profilebuttons button {
+ all: unset;
+ font-family: sfprobold;
+ padding: 0px 50px;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #606770;
+ cursor: pointer;
+}
+
+.profilebuttons button:hover {
+ background-color: #dddfe2;
+}
+
+.selected {
+ color: #1778f2 !important;
+ border-bottom: 3px solid #1778f2 !important;
+}
+
+#about {
+ margin-top: 2em;
+ align-self: center;
+ padding: 0;
+ display: flex;
+ flex-direction: row;
+}
+
+#posts {
+ margin-top: 2em;
+}
+
+#about .ltext {
+ border-right: 2px solid #dadde1;
+ padding: 10px;
+ padding-right: 3em;
+}
+
+#about .data {
+ display: flex;
+ flex-direction: column;
+ padding: 10px;
+ padding-left: 20px;
+ padding-top: 15px;
+}
+
+#about .data span {
+ margin-bottom: 10px;
+ width: 28em;
+ margin-bottom: 5px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+} \ No newline at end of file
diff --git a/public/fonts/facebook.otf b/public/fonts/facebook.otf
new file mode 100755
index 0000000..97d5c6f
--- /dev/null
+++ b/public/fonts/facebook.otf
Binary files differ
diff --git a/public/fonts/sfpro.otf b/public/fonts/sfpro.otf
new file mode 100644
index 0000000..09aaca9
--- /dev/null
+++ b/public/fonts/sfpro.otf
Binary files differ
diff --git a/public/fonts/sfprobold.otf b/public/fonts/sfprobold.otf
new file mode 100644
index 0000000..025b25c
--- /dev/null
+++ b/public/fonts/sfprobold.otf
Binary files differ
diff --git a/public/home.html b/public/home.html
new file mode 100644
index 0000000..865e53a
--- /dev/null
+++ b/public/home.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <link rel="stylesheet" href="/css/header.css">
+ <link rel="stylesheet" href="/css/main.css">
+ <link rel="stylesheet" href="/css/home.css">
+ <title>XSSBook - Home</title>
+</head>
+<body>
+ <script src="/js/main.js"></script>
+ <script src="/js/header.js"></script>
+ <script src="/js/api.js"></script>
+ <script src="/js/home.js"></script>
+ <script>init()</script>
+</body>
+</html> \ No newline at end of file
diff --git a/public/js/api.js b/public/js/api.js
new file mode 100644
index 0000000..07769f6
--- /dev/null
+++ b/public/js/api.js
@@ -0,0 +1,63 @@
+const endpoint = '/api'
+
+const request = async (url, body, method) => {
+ if (method === undefined) method = 'POST'
+ const response = await fetch(endpoint + url, {
+ method,
+ body: JSON.stringify(body),
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ });
+ if (response.status == 401) {
+ location.href = 'login'
+ }
+ const contentType = response.headers.get("content-type");
+ if (contentType && contentType.indexOf("application/json") !== -1) {
+ const json = await response.json()
+ return { status: response.status, msg: json.msg, json }
+ } else {
+ const msg = await response.text();
+ return { status: response.status, msg }
+ }
+}
+
+const login = async (email, password) => {
+ return await request('/auth/login', {email, password})
+}
+
+const register = async (firstname, lastname, email, password, gender, day, month, year) => {
+ return await request('/auth/register', {firstname, lastname, email, password, gender, day, month, year})
+}
+
+const loadpostspage = async (page) => {
+ return await request('/posts/page', {page})
+}
+
+const loadusersposts = async (user_id) => {
+ return await request('/posts/user', {user_id})
+}
+
+const loadusers = async (ids) => {
+ return await request('/users/load', {ids})
+}
+
+const loaduserspage = async (page) => {
+ return await request('/users/page', {page})
+}
+
+const loadself = async () => {
+ return await request("/users/self", {})
+}
+
+const postcomment = async (post_id, content) => {
+ return await request('/posts/comment', {post_id, content}, 'PATCH')
+}
+
+const postlike = async (post_id, state) => {
+ return await request('/posts/like', {post_id, state}, 'PATCH')
+}
+
+const createpost = async (content) => {
+ return await request('/posts/create', {content})
+} \ No newline at end of file
diff --git a/public/js/header.js b/public/js/header.js
new file mode 100644
index 0000000..8fe03e5
--- /dev/null
+++ b/public/js/header.js
@@ -0,0 +1,25 @@
+function header(home, people) {
+ const html = `
+ <div id="header">
+ <span class="logo"><a href="/">xssbook</a></span>
+ <div class="buttons">
+ <a id="home" ${home ? 'class="selected"' : ''} href="home">
+ <svg viewBox="0 0 28 28" fill="currentColor" height="28" width="28">
+ <path d="M25.825 12.29C25.824 12.289 25.823 12.288 25.821 12.286L15.027 2.937C14.752 2.675 14.392 2.527 13.989 2.521 13.608 2.527 13.248 2.675 13.001 2.912L2.175 12.29C1.756 12.658 1.629 13.245 1.868 13.759 2.079 14.215 2.567 14.479 3.069 14.479L5 14.479 5 23.729C5 24.695 5.784 25.479 6.75 25.479L11 25.479C11.552 25.479 12 25.031 12 24.479L12 18.309C12 18.126 12.148 17.979 12.33 17.979L15.67 17.979C15.852 17.979 16 18.126 16 18.309L16 24.479C16 25.031 16.448 25.479 17 25.479L21.25 25.479C22.217 25.479 23 24.695 23 23.729L23 14.479 24.931 14.479C25.433 14.479 25.921 14.215 26.132 13.759 26.371 13.245 26.244 12.658 25.825 12.29"></path>
+ </svg>
+ </a>
+ <a id="people" ${people ? 'class="selected"' : ''} href="people">
+ <svg viewBox="0 0 28 28" fill="currentColor" height="28" width="28">
+ <path d="M10.5 4.5c-2.272 0-2.75 1.768-2.75 3.25C7.75 9.542 8.983 11 10.5 11s2.75-1.458 2.75-3.25c0-1.482-.478-3.25-2.75-3.25zm0 8c-2.344 0-4.25-2.131-4.25-4.75C6.25 4.776 7.839 3 10.5 3s4.25 1.776 4.25 4.75c0 2.619-1.906 4.75-4.25 4.75zm9.5-6c-1.41 0-2.125.841-2.125 2.5 0 1.378.953 2.5 2.125 2.5 1.172 0 2.125-1.122 2.125-2.5 0-1.659-.715-2.5-2.125-2.5zm0 6.5c-1.999 0-3.625-1.794-3.625-4 0-2.467 1.389-4 3.625-4 2.236 0 3.625 1.533 3.625 4 0 2.206-1.626 4-3.625 4zm4.622 8a.887.887 0 00.878-.894c0-2.54-2.043-4.606-4.555-4.606h-1.86c-.643 0-1.265.148-1.844.413a6.226 6.226 0 011.76 4.336V21h5.621zm-7.122.562v-1.313a4.755 4.755 0 00-4.749-4.749H8.25A4.755 4.755 0 003.5 20.249v1.313c0 .518.421.938.937.938h12.125c.517 0 .938-.42.938-.938zM20.945 14C24.285 14 27 16.739 27 20.106a2.388 2.388 0 01-2.378 2.394h-5.81a2.44 2.44 0 01-2.25 1.5H4.437A2.44 2.44 0 012 21.562v-1.313A6.256 6.256 0 018.25 14h4.501a6.2 6.2 0 013.218.902A5.932 5.932 0 0119.084 14h1.861z"></path>
+ </svg>
+ </a>
+ </div>
+ <a class="pfp" id="profile" hreF="profile">
+
+ </a>
+ </div>
+ <div class="spacer"></div>
+ `
+
+ add(html, 'header')
+} \ No newline at end of file
diff --git a/public/js/home.js b/public/js/home.js
new file mode 100644
index 0000000..fd40ebf
--- /dev/null
+++ b/public/js/home.js
@@ -0,0 +1,233 @@
+const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+
+function parseDate(date) {
+ return months[date.getUTCMonth()] + ' ' + date.getUTCDate() + ', ' + date.getUTCFullYear() + ' ' + date.toLocaleTimeString();
+}
+
+function parseComment(comment) {
+ const author = data.users[comment[0]]
+ if (author === undefined) {
+ author = {}
+ }
+ const html = `
+ <div class="comment">
+ <a class="pfp">
+
+ </a>
+ <span>
+ <span class="bold mtext">${author.firstname + ' ' + author.lastname}</span>
+ <p class="mtext">${comment[1]}</p>
+ </span>
+ </div>
+ `
+ return html
+}
+
+function parsePost(post) {
+ console.log(post.likes)
+ const author = data.users[post.user_id]
+ if (author === undefined) {
+ author = {}
+ }
+ const html = `
+ <div class="post" postid=${post.post_id}>
+ <div class="postheader">
+ <a class="pfp">
+
+ </a>
+ <div class="postname">
+ <span class="bold">${author.firstname + ' ' + author.lastname}</span>
+ <span class="gtext mtext">${parseDate(new Date(post.date))}</span>
+ </div>
+ </div>
+ <p class="mtext">
+ ${post.content.replace(/\n/g,'<br>')}
+ </p>
+ <span class="gtext mtext">
+ ${Object.keys(post.likes).map(k => post.likes[k]).filter(v => v !== false).length} Likes
+ </span>
+ <div class="fullline nb"></div>
+ <div class="postbuttons">
+ <span onclick="like(this)">
+ <i class="icons like ${post.likes.includes(data.user.user_id) ? 'blue' : ''}"></i>
+ <span class="bold ${post.likes.includes(data.user.user_id) ? 'blue' : ''}">Like</span>
+ </span>
+ <span onclick="this.parentElement.parentElement.getElementsByClassName('newcomment')[0].focus()">
+ <i class="icons comm"></i>
+ <span class="bold">Comment</span>
+ </span>
+ </div>
+ <div id="comments">
+ <div class="fullline" style="margin-top: 0"></div>
+ ${post.comments.map(parseComment).join('')}
+ <div class="comment">
+ <a class="pfp" href="profile">
+
+ </a>
+ <form onsubmit="comment(event)">
+ <input type="text" name="text" placeholder="Write a comment..." id="newcomment" class="newcomment">
+ </form>
+ </div>
+ </div>
+ </div>
+ `
+
+ return html
+}
+
+function getPost(post_id) {
+ for (let i = 0; i < data.posts.length; i++) {
+ if (data.posts[i].post_id === post_id) {
+ return i
+ }
+ }
+ return -1
+}
+
+async function like(span) {
+ const id = parseInt(span.parentElement.parentElement.getAttribute('postid'))
+ const post = data.posts[getPost(id)]
+ const index = post.likes.indexOf(data.user.user_id)
+ const current = index !== -1
+ const response = await postlike(id, !current)
+ if (response.status != 200) return;
+ if (current) {
+ post.likes.splice(index, 1)
+ } else {
+ post.likes.push(data.user.user_id)
+ }
+ render()
+}
+
+async function comment(event) {
+ event.preventDefault();
+ const text = event.target.elements.text.value.trim();
+ if (text.length < 1) return;
+ const id = parseInt(event.target.parentElement.parentElement.parentElement.getAttribute('postid'))
+ var index = getPost(id);
+ if (index === -1) return;
+ const response = await postcomment(id, text)
+ if (response.status != 200) return;
+ event.target.elements.text.value = '';
+ data.posts[index].comments.push([data.user.user_id, text])
+ render()
+}
+
+async function post() {
+ const text = document.getElementById("text").value.trim()
+ const error = document.getElementsByClassName('error')[0]
+ if (text.length < 1) return;
+ const response = await createpost(text);
+ if (response.status != 201) {
+ error.innerHTML = response.msg
+ return;
+ }
+ error.innerHTML = '';
+ data.posts.unshift({
+ post_id: response.msg,
+ user_id: data.user.user_id,
+ date: Date.now(),
+ content: text,
+ likes: [],
+ comments: []
+ })
+ render()
+}
+
+function render() {
+ const html = `
+ <div id="posts">
+ <div class="create">
+ <a class="pfp" href="profile">
+
+ </a>
+ <button class="pfp">
+ <p class="gtext" onclick="document.getElementById('popup').classList.remove('hidden')">
+ What's on your mind, ${data.user.firstname}?
+ </p>
+ </button>
+ </div>
+ ${data.posts.map(p => parsePost(p)).join('')}
+ </div>
+ `
+
+ add(html, 'posts')
+
+ const popup = `
+ <div id="popup" class="hidden">
+ <div class="createpost">
+ <div class="close" onclick="document.getElementById('popup').classList.add('hidden')"></div>
+ <span class="ltext ctext bold">Create post</span>
+ <div class="fullline"></div>
+ <div class="postheader">
+ <a class="pfp" style="cursor: auto">
+
+ </a>
+ <div class="postname">
+ <span class="bold">${data.user.firstname + ' ' + data.user.lastname}</span>
+ <span class="gtext mtext">Now</span>
+ </div>
+ </div>
+ <textarea type="text" name="text" id="text" placeholder="What's on your mind, ${data.user.firstname}?"></textarea>
+ <span class="error ctext" style="padding-bottom: 15px; margin-top: -30px;"></span>
+ <button class="primary" onclick="post(this)">Post</button>
+ </div>
+ </div>
+ `
+
+ add(popup, 'popup')
+
+ const load = `
+ <div id="load">
+ <a class="bold gtext" onclick="load()">Load more posts</a>
+ </div>
+ `
+
+ if (page !== -1) {
+ add(load, 'load')
+ } else {
+ remove('load')
+ }
+}
+
+var page = 0
+const data = {
+ user: {},
+ users: {},
+ posts: []
+}
+
+async function load() {
+ const posts = (await loadpostspage(page)).json
+ if (posts.length === 0) {
+ page = -1
+ } else {
+ page++
+ }
+ data.posts.push(... posts)
+ const batch = []
+ for (const post of posts) {
+ for(const comment of post.comments) {
+ if (data.users[comment[0]] !== undefined) continue
+ if (batch.includes(comment[0])) continue
+ batch.push(comment[0])
+ }
+ if (data.users[post.user_id] !== undefined) continue
+ if (batch.includes(post.user_id)) continue
+ batch.push(post.user_id)
+ }
+ const users = (await loadusers(batch)).json
+ for (const id in users) {
+ data.users[id] = users[id]
+ }
+ render()
+}
+
+
+async function init() {
+ header(true, false)
+ data.user = (await loadself()).json
+ data.users[data.user.user_id] = data.user
+ load()
+} \ No newline at end of file
diff --git a/public/js/login.js b/public/js/login.js
new file mode 100644
index 0000000..f65808b
--- /dev/null
+++ b/public/js/login.js
@@ -0,0 +1,29 @@
+async function onlogin() {
+ const email = document.getElementById('email').value
+ const password = document.getElementById('pass').value
+ const response = await login(email, password)
+ if (response.status !== 200) {
+ const error = document.getElementsByClassName('error')[0]
+ error.innerHTML = response.msg
+ } else {
+ location.href = '/home'
+ }
+}
+
+async function onregister() {
+ const first = document.getElementById('firstname').value
+ const last = document.getElementById('lastname').value
+ const email = document.getElementById('newemail').value
+ const pass = document.getElementById('newpass').value
+ const month = document.getElementById('month').value
+ const day = document.getElementById('day').value
+ const year = document.getElementById('year').value
+ const gender = document.querySelector('input[name="gender"]:checked').value
+ const response = await register(first, last, email, pass, gender, parseInt(day), parseInt(month), parseInt(year))
+ if (response.status !== 200) {
+ const error = document.getElementsByClassName('error')[1]
+ error.innerHTML = response.msg
+ } else {
+ location.href = '/home'
+ }
+} \ No newline at end of file
diff --git a/public/js/main.js b/public/js/main.js
new file mode 100644
index 0000000..0003c0d
--- /dev/null
+++ b/public/js/main.js
@@ -0,0 +1,22 @@
+var range;
+
+function add(html, id) {
+ const old = document.getElementById(id)
+ if (old !== null) {
+ old.remove()
+ }
+ if (range === undefined) {
+ var range = document.createRange()
+ range.setStart(document.body, 0)
+ }
+ document.body.appendChild(
+ range.createContextualFragment(html)
+ )
+}
+
+function remove(id) {
+ const old = document.getElementById(id)
+ if (old !== null) {
+ old.remove()
+ }
+} \ No newline at end of file
diff --git a/public/js/people.js b/public/js/people.js
new file mode 100644
index 0000000..ddd1875
--- /dev/null
+++ b/public/js/people.js
@@ -0,0 +1,65 @@
+const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+
+function parseDate(date) {
+ return months[date.getUTCMonth()] + ' ' + date.getUTCDate() + ', ' + date.getUTCFullYear() + ' ' + date.toLocaleTimeString();
+}
+
+function parseUser(user) {
+ const html = `
+ <a class="person" href="/profile?id=${user.id}">
+ <div class="profile">
+
+ </div>
+ <div class="info">
+ <span class="bold ltext">${user.firstname + ' ' + user.lastname}</span>
+ <span class="gtext">Joined ${parseDate(new Date(user.date))}</span>
+ <span class="gtext">Gender: ${user.gender}</span>
+ <span class="gtext">Birthday: ${months[user.month] + ' ' + user.day + ', ' + user.year}</span>
+ <span class="gtext" style="margin-bottom: -100px;">User ID: ${user.user_id}</span>
+ </div>
+ </a>
+ `
+ return html
+}
+
+function render() {
+ const html = `
+ <div id="users">
+ ${data.users.map(u => parseUser(u)).join('')}
+ </div>
+ `
+
+ add(html, 'users')
+
+ const load = `
+ <div id="load">
+ <a class="bold gtext" onclick="load()">Load more users</a>
+ </div>
+ `
+
+ if (page !== -1) {
+ add(load, 'load')
+ } else {
+ remove('load')
+ }
+}
+
+var page = 0
+var data = {
+ users: []
+}
+
+async function load() {
+ const users = (await loaduserspage(page)).json
+ if (users.length === 0) {
+ page = -1
+ } else {
+ page++
+ }
+ data.users.push(... users)
+ render()
+}
+
+header(false, true)
+load() \ No newline at end of file
diff --git a/public/js/profile.js b/public/js/profile.js
new file mode 100644
index 0000000..79dbe2f
--- /dev/null
+++ b/public/js/profile.js
@@ -0,0 +1,88 @@
+function render() {
+ const html = `
+ <div id="top">
+ <div id="banner">
+ <div>
+
+ </div>
+ </div>
+ <div id="info">
+ <div class="face">
+
+ </div>
+ <div class="infodata">
+ <span class="bold ltext">${data.user.firstname + ' ' + data.user.lastname}</span>
+ <span class="gtext">Joined ${parseDate(new Date(data.user.date))}</span>
+ </div>
+ </div>
+ <div class="fullline" style="width: 80em; margin-bottom: 0;"></div>
+ <div class="profilebuttons">
+ <button class="${posts ? 'selected' : ''}" onclick="posts = true; render()">
+ Posts
+ </button>
+ <button class="${posts ? '' : 'selected'}" onclick="posts = false; render()">
+ About
+ </button>
+ </div>
+ </div>
+ `
+
+ add(html, 'top')
+
+ const postsh = `
+ <div id="posts" class="${posts ? '' : 'hidden'}">
+ ${data.posts.map(p => parsePost(p)).join('')}
+ </div>
+ `
+
+ add(postsh, 'posts')
+
+ const about = `
+ <div id="about" class="post ${posts ? 'hidden' : ''}">
+ <span class="bold ltext">About</span>
+ <div class="data">
+ <span class="gtext bold">Name: ${data.user.firstname + ' ' + data.user.lastname}</span>
+ <span class="gtext bold">Email: ${data.user.email}</span>
+ <span class="gtext bold">Gender: ${data.user.gender}</span>
+ <span class="gtext bold">Birthday: ${months[data.user.month] + ' ' + data.user.day + ', ' + data.user.year}</span>
+ <span class="gtext bold">User ID: ${data.user.user_id}</span>
+ </div>
+ </div>
+ `
+
+ add(about, 'about')
+}
+
+var posts = true
+
+async function load() {
+ header(false, false)
+
+ var params = {};
+ for (const [key, value] of new URLSearchParams(location.search)) {
+ params[key] = value
+ }
+
+ const id = params.id !== undefined && !isNaN(params.id) ? parseInt(params.id) : (await loadself()).json.user_id
+ const posts = (await loadusersposts(id)).json
+ data.posts.push(... posts)
+ const batch = [id]
+ for (const post of posts) {
+ for(const comment of post.comments) {
+ if (data.users[comment[0]] !== undefined) continue
+ if (batch.includes(comment[0])) continue
+ batch.push(comment[0])
+ }
+ if (data.users[post.user_id] !== undefined) continue
+ if (batch.includes(post.user_id)) continue
+ batch.push(post.user_id)
+ }
+ const users = (await loadusers(batch)).json
+ for (const user of users) {
+ data.users[user.user_id] = user
+ }
+ data.user = data.users[id]
+ render()
+}
+
+load() \ No newline at end of file
diff --git a/public/login.html b/public/login.html
new file mode 100644
index 0000000..97398f9
--- /dev/null
+++ b/public/login.html
@@ -0,0 +1,170 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <link rel="stylesheet" href="/css/main.css">
+ <link rel="stylesheet" href="/css/login.css">
+ <script src="/js/api.js"></script>
+ <script src="/js/login.js"></script>
+ <title>XSSBook - Login</title>
+</head>
+<body>
+ <div class="login">
+ <div class="show">
+ <span class="logo">xssbook</span>
+ <p class="text">Connect with javascript and the world around you on XSSBook.</p>
+ </div>
+ <div class="prompt">
+ <input type="text" name="email" id="email" placeholder="Email" autofocus="1">
+ <input type="password" name="pass" id="pass" placeholder="Password">
+ <span class="error ctext"></span>
+ <button class="primary login-button bold" value="1" name="login" type="submit" id="login" onclick="onlogin()">Log In</button>
+ <a class="btext ctext">Forgot Password?</a>
+ <div class="line"></div>
+ <button class="success newacc" onclick="document.getElementById('popup').classList.remove('hidden')">Create new account</button>
+ </div>
+ <div id="popup" class="hidden">
+ <div class="prompt">
+ <div class="close" onclick="document.getElementById('popup').classList.add('hidden')"></div>
+ <span class="text bold">Sign Up</span>
+ <span class="gtext">It's quick and easy.</span>
+ <div class="fullline"></div>
+ <div class="row">
+ <input type="text" name="firstname" id="firstname" placeholder="First Name" autofocus="1">
+ <input type="text" name="lastname" id="lastname" placeholder="Last Name">
+ </div>
+ <input type="text" name="newemail" id="newemail" placeholder="Email" autofocus="1">
+ <input type="password" name="newpass" id="newpass" placeholder="Password">
+ <span class="label">Birthday</span>
+ <div class="row">
+ <select name="month" id="month" title="Month">
+ <option value="1" selected="1">Jan</option>
+ <option value="2">Feb</option>
+ <option value="3">Mar</option>
+ <option value="4">Apr</option>
+ <option value="5">May</option>
+ <option value="6">Jun</option>
+ <option value="7">Jul</option>
+ <option value="8">Aug</option>
+ <option value="9">Sep</option>
+ <option value="10">Oct</option>
+ <option value="11">Nov</option>
+ <option value="12">Dec</option>
+ </select>
+ <select name="day" id="day" title="Day">
+ <option value="1" selected="1">1</option>
+ <option value="2">2</option>
+ <option value="3">3</option>
+ <option value="4">4</option>
+ <option value="5">5</option>
+ <option value="6">6</option>
+ <option value="7">7</option>
+ <option value="8">8</option>
+ <option value="9">9</option>
+ <option value="10">10</option>
+ <option value="11">11</option>
+ <option value="12">12</option>
+ <option value="13">13</option>
+ <option value="14">14</option>
+ <option value="15">15</option>
+ <option value="16">16</option>
+ <option value="17">17</option>
+ <option value="18">18</option>
+ <option value="19">19</option>
+ <option value="20">20</option>
+ <option value="21">21</option>
+ <option value="22">22</option>
+ <option value="23">23</option>
+ <option value="24">24</option>
+ <option value="25">25</option>
+ <option value="26">26</option>
+ <option value="27">27</option>
+ <option value="28">28</option>
+ <option value="29">29</option>
+ <option value="30">30</option>
+ <option value="31">31</option>
+ </select>
+ <select name="year" id="year" title="Year">
+ <option value="2023" selected="1">2023</option>
+ <option value="2022">2022</option>
+ <option value="2021">2021</option>
+ <option value="2020">2020</option>
+ <option value="2019">2019</option>
+ <option value="2018">2018</option>
+ <option value="2017">2017</option>
+ <option value="2016">2016</option>
+ <option value="2015">2015</option>
+ <option value="2014">2014</option>
+ <option value="2013">2013</option>
+ <option value="2012">2012</option>
+ <option value="2011">2011</option>
+ <option value="2010">2010</option>
+ <option value="2009">2009</option>
+ <option value="2008">2008</option>
+ <option value="2007">2007</option>
+ <option value="2006">2006</option>
+ <option value="2005">2005</option>
+ <option value="2004">2004</option>
+ <option value="2003">2003</option>
+ <option value="2002">2002</option>
+ <option value="2001">2001</option>
+ <option value="2000">2000</option>
+ <option value="1999">1999</option>
+ <option value="1998">1998</option>
+ <option value="1997">1997</option>
+ <option value="1996">1996</option>
+ <option value="1995">1995</option>
+ <option value="1994">1994</option>
+ <option value="1993">1993</option>
+ <option value="1992">1992</option>
+ <option value="1991">1991</option>
+ <option value="1990">1990</option>
+ <option value="1989">1989</option>
+ <option value="1988">1988</option>
+ <option value="1987">1987</option>
+ <option value="1986">1986</option>
+ <option value="1985">1985</option>
+ <option value="1984">1984</option>
+ <option value="1983">1983</option>
+ <option value="1982">1982</option>
+ <option value="1981">1981</option>
+ <option value="1980">1980</option>
+ <option value="1979">1979</option>
+ <option value="1978">1978</option>
+ <option value="1977">1977</option>
+ <option value="1976">1976</option>
+ <option value="1975">1975</option>
+ <option value="1974">1974</option>
+ <option value="1973">1973</option>
+ <option value="1972">1972</option>
+ <option value="1971">1971</option>
+ <option value="1970">1970</option>
+ </select>
+ </div>
+ <span class="label">Gender</span>
+ <div class="radiomenu" data-type="radio" data-name="gender_wrapper">
+ <span>
+ <label class="gtext" for="female">Female</label>
+ <input id="female" type="radio" name="gender" value="Female" checked="true">
+ </span>
+ <span>
+ <label class="gtext" for="male">Male</label>
+ <input id="male" type="radio" name="gender" value="Male">
+ </span>
+ <span>
+ <label class="gtext" for="lettuce">Lettuce</label>
+ <input id="lettuce" type="radio" name="gender" value="Lettuce">
+ </span>
+ </div>
+ <span class="error ctext" style="padding-bottom: 10px;"></span>
+ <span class="label stext">By clicking Sign Up, you agree to have your password stored in plain text and have any javascript run on your pc at any time.</span>
+ <span class="label stext">XSSBook is not responsible for any ones loss of finances, mental state, relationships, or life when using this site.</span>
+ <button class="success signacc" onclick="onregister()">Sign Up</button>
+ </div>
+ </div>
+ </div>
+ <footer>
+ Metashit © 2023 | This website does not care about you
+ </footer>
+</body>
+</html> \ No newline at end of file
diff --git a/public/people.html b/public/people.html
new file mode 100644
index 0000000..399751a
--- /dev/null
+++ b/public/people.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <link rel="stylesheet" href="/css/main.css">
+ <link rel="stylesheet" href="/css/people.css">
+ <link rel="stylesheet" href="/css/header.css">
+ <title>XSSBook - People</title>
+</head>
+<body>
+ <script src="/js/main.js"></script>
+ <script src="/js/header.js"></script>
+ <script src="/js/api.js"></script>
+ <script src="/js/people.js"></script>
+</body> \ No newline at end of file
diff --git a/public/profile.html b/public/profile.html
new file mode 100644
index 0000000..d17ab09
--- /dev/null
+++ b/public/profile.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <link rel="stylesheet" href="/css/main.css">
+ <link rel="stylesheet" href="/css/header.css">
+ <link rel="stylesheet" href="/css/profile.css">
+ <link rel="stylesheet" href="/css/home.css">
+ <title>XSSBook - Profile</title>
+</head>
+<body>
+ <script src="/js/main.js"></script>
+ <script src="/js/header.js"></script>
+ <script src="/js/api.js"></script>
+ <script src="/js/home.js"></script>
+ <script src="/js/profile.js"></script>
+</body> \ No newline at end of file