This commit is contained in:
Tracker-Friendly 2024-03-12 18:34:05 +00:00
parent 211fa7623d
commit 7fc62fff79
70 changed files with 8867 additions and 0 deletions

82
app/index.html Normal file
View File

@ -0,0 +1,82 @@
<!DOCTYPE html>
<html>
<head>
<title>Burgernotes</title>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="../static/css/style.css" />
<script type="text/javascript" src="../static/js/crypto-js.js"></script>
</head>
<body>
<div class="topBar">
<p tabindex="0" class="logo" id="burgerButton">Burgernotes</p>
<p tabindex="0" id="usernameBox" class="usernameBox"></p>
</div>
<div class="bottomBar">
<button id="removeBox" class="removeButton"></button>
<button id="wordCountBox">0 words</button>
<div class="textManipulator">
<button id="textMinusBox">-</button>
<button id="textSizeBox">16px</button>
<button id="textPlusBox">+</button>
</div>
</div>
<div id="notesBar" class="notesBar">
<button id="newNote" class="newNote"><img id="newNoteImage" draggable="false" alt=""
src="../static/svg/add.svg">New note</button>
<div id="notesDiv" class="notesDiv">
<button class="loadingStuff" id="loadingStuff"></button>
</div>
</div>
<div id="optionsCoverDiv" class="optionsCoverDiv hidden">
<div id="optionsDiv" class="optionsDiv hidden">
<button class="exit" id="exitThing">X</button>
<h3 class="w300">Your account</h3>
<p id="usernameThing"></p>
<p id="passwordThing"></p>
<div class="section"></div>
<p>Storage</p>
<progress id="storageProgressThing" value="0" max="100"></progress><br>
<p id="storageThing"></p>
<div class="section"></div>
<p>Account managment</p>
<button id="deleteMyAccountButton"><img src="../static/svg/delete_forever.svg">Delete my account</button>
<button id="exportNotesButton"><img src="../static/svg/download.svg">Export notes</button>
<button id="sessionManagerButton"><img src="../static/svg/list.svg">Session manager</button>
<button class="lastButton" id="logOutButton"><img src="../static/svg/logout.svg">Log out</button>
</div>
<div id="sessionManagerDiv" class="optionsDiv hidden">
<button class="exit" id="exitSessionsThing">X</button>
<h3 class="w300">Session manager</h3>
<p>Manage your sessions</p>
<div class="section"></div>
<div class="sessionDiv" id="sessionDiv">
</div>
</div>
</div>
<div id="errorDiv" class="optionsDiv hidden">
<p id="errorMessageThing"></p>
<input class="hidden" id="errorInput" type="text" placeholder=""><br></input>
<button class="normalButton" id="closeErrorButton">Ok</button>
<button class="normalButton hidden" id="cancelErrorButton">Cancel</button>
</div>
</div>
<textarea id="noteBox" class="noteBox"></textarea>
<script type="text/javascript" src="../static/js/main.js"></script>
<script>
for (let i = 0; i < 40; i++) {
notesDiv.appendChild(loadingStuff.cloneNode())
}
loadingStuff.remove()
</script>
</body>
</html>

23
error/index.html Normal file
View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<title>Burgernotes</title>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="../static/css/style.css" />
</head>
<body>
<h2 class="w300">{{ errorMessage }}</h2>
{{ errorCode }} | {{ errorMessage }}
</body>
<style>
body {
margin-left: 15px;
}
</style>
</html>

47
index.html Normal file
View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<title>Burgernotes</title>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="./static/css/style.css" />
<meta content="Burgernotes" property="og:title" />
<meta content="A simple note-taking app!" property="og:description" />
</head>
<body>
<a href="https://notes.old.hectabit.org" class="hiddenButton">Click here</a>
<div class="mainDiv">
<div class="startDiv">
<h1 class="w300">Burgernotes</h1>
<p>A simple note-taking service!</p>
<br>
<a href="./app/index.html">Open in your browser</a>
<a href="./static/burgernotes.mobileconfig" style="margin-top: 5px;">Download for iOS</a>
<a style="padding: 0; padding-bottom: 0; margin-top: 5px; background-color: rgba(0, 0, 0, 0);" href="https://flathub.org/apps/org.hectabit.burgernotes">
<img class="flathubLogo" style="height: 55px;"src="./static/svg/flathublight.svg">
</a>
</div>
<br>
<div class="feature green">
<h7 class="w300">Secure</h7>
<p2>All your notes are fully end-to-end encrypted. Only you can read your notes, not anyone else.</p2>
</div>
<div class="feature yellow">
<h7 class="w300">Always up-to-date</h7>
<p2>Your notes seamlessly sync across your devices.</p2>
</div>
</div>
<div class="links">
<a href="https://centrifuge.hectabit.org/hectabit/burgernotes"><img src="/static/svg/code.svg">Source Code</a>
<a href="../privacy/index.html"><img src="/static/svg/info.svg">Privacy &amp; Terms</a>
<a href="https://discord.gg/8EbKTjmH2d"><img src="/static/svg/forum.svg">Discord</a>
</div>
<br>
</body>
</html>

41
login/index.html Normal file
View File

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<title>Login - Burgernotes</title>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="../static/css/style.css" />
<script src="../static/js/hash-wasm.js"></script>
<style>
body {
background-color: #d9d9d9;
background-image: url("/static/svg/grid.svg");
background-repeat: repeat;
background-size: 312px;
}
.inoutdiv {
border-radius: 8px;
}
</style>
</head>
<body>
<div class="inoutdiv">
<h2 class="w300">Login</h2>
<p id="statusBox"></p>
<span id="inputNameBox" style="margin-right: 10px;color: var(--text-color);"></span>
<input id="usernameBox" class="hidden" type="text" placeholder="Enter your username">
<input id="passwordBox" class="hidden" type="password" placeholder="Enter your password">
<button id="signupButton">Next</button>
<button id="backButton" class="hidden">Back</button>
<br>
<br>
<p>Don't have an account? If so, <a href="../signup/index.html">Create one here!</a></p>
<div style="display: flex;"><p id="homeserver">Your homeserver is loading... </p><div style="display: flex;flex-direction: column;justify-content: center;"><a href="/homeserver">Change</a></div></div>
<a href="../privacy/index.html">Privacy &amp; Terms</a>
</div>
<script type="text/javascript" src="../static/js/login.js"></script>
</body>

15
logout/index.html Normal file
View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title>Burgernotes</title>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<head>
Logging out..
<script>
localStorage.removeItem("DONOTSHARE-secretkey")
localStorage.removeItem("DONOTSHARE-password")
localStorage.removeItem("CACHE-username")
window.location.replace("../login/index.html")
</script>

82
privacy/index.html Normal file
View File

@ -0,0 +1,82 @@
<!DOCTYPE html>
<html>
<head>
<title>Burgernotes Privacy &amp; Terms</title>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="../static/css/style.css" />
</head>
<body>
<h1 class="w300">Burgernotes Privacy Policy &amp; Terms & Conditions</h1>
<h2 class="w300">Preamble</h2>
<p><i>Please note that I am not a lawyer, please don't expect too much of this policy :3</i></p>
<p>Welcome to the Burgernotes privacy policy! Burgernotes is <a
href="https://centrifuge.hectabit.org/hectabit/burgernotes">free & open source</a> software licensed under <a
href="https://www.gnu.org/licenses/agpl-3.0.en.html">GNU AGPL-3.0</a>.</p>
<h2 class="w300">Information collected when signing up</h2>
<p>When signing up for an account, we collect and store the following information:</p>
<ul>
<li>Username, and your password hashed</li>
<li>Date of creating account</li>
<li>Web browser "User Agent"</li>
</ul>
<h2 class="w300">Information collected when logging in</h2>
<p>When logging back in to your account, we collect and store the following information:</p>
<ul>
<li>Web browser "User agent"</li>
</ul>
<h2 class="w300">Information we collect while using our services</h2>
<p>When you create an note, we collect and use this information:</p>
<ul>
<li>Encrypted note content and title</li>
<li>Note creator</li>
<li>Note creation date</li>
<li>Note last edited date</li>
</ul>
<p>When you edit an note, we collect and use this information:</p>
<ul>
<li>Encrypted note content and title</li>
<li>Note last edited date</li>
</ul>
<h2 class="w300">How we use your data</h2>
<p>We use your data to make our services work. We don't share your information with third-parties.</p>
<h2 class="w300">We can't see notes you create's content and title</h2>
<p>Your notes are <a href="https://en.wikipedia.org/wiki/End-to-end_encryption">encrypted end-to-end</a> using AES
(Advanced Encryption Standard) 256-bit encryption.</p>
<p>We can only see:</p>
<ul>
<li>Note creation date</li>
<li>Note last edited date</li>
<li>Note creator</li>
</ul>
<p>Not note content or title.</p>
<h2 class="w300">We don't sell your data</h2>
<p>We don't sell or share your data to advertisers or third-parties.</p>
<h2 class="w300">Liability</h2>
<p>We take no responsibility for the use of burgernotes, or any external instances provided by third-parties. We
refuse liability for any inappropriate or illegal use of burgernotes.</p>
<p>You may view the AGPL-3.0 license which this software is provided to you with. A copy of the section is below.</p>
<p>IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.</p>
<br>
<button onclick="history.back()" style="cursor: pointer; padding: 15px 20px;margin-right: auto;color: white;text-decoration: none;background-color: var(--theme-color);border-radius: 8px;border: medium;font-size: 15px;">Take me back where I was!</button>
<br><br>
</body>
<style>
body {
margin-left: 15px;
}
</style>
</html>

38
signup/index.html Normal file
View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<title>Signup - Burgernotes</title>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="../static/css/style.css" />
<script src="../static/js/hash-wasm.js"></script>
<style>
body {
background-color: #d9d9d9;
background-image: url("/static/svg/grid.svg");
background-repeat: repeat;
background-size: 312px;
}
.inoutdiv {
border-radius: 8px;
}
</style>
</head>
<body>
<div class="inoutdiv">
<h2 class="w300">Signup</h2>
<p>Signup for a Burgernotes account</p>
<p id="statusBox"></p>
<input id="usernameBox" type="text" placeholder="Username">
<input id="passwordBox" type="password" placeholder="Password"><br>
<button id="signupButton">Signup</button><br><br>
<p>Already have an account? If so, <a href="../login/index.html">Login</a> instead!</p>
<p>Please note that it's impossible to reset your password, do not forget it!</p>
<div style="display: flex;"><p id="homeserver">Your homeserver is loading... </p><div style="display: flex;flex-direction: column;justify-content: center;"><a href="/homeserver">Change</a></div></div>
<a href="../privacy/index.html">Privacy &amp; Terms</a>
</div>
<script type="text/javascript" src="../static/js/signup.js"></script>
</body>

File diff suppressed because one or more lines are too long

696
static/css/style.css Normal file
View File

@ -0,0 +1,696 @@
@import url("../fonts/inter.css");
:root {
--contrast: #eee;
--contrast2: #fff;
--invertdm: 0%;
--bar: #f4f4f4;
--editor: #ffffff;
--text-color: #000000;
--border-color: #dadada;
--theme-color: #157efb;
--theme-text-color: #ffffff;
--exit-color: #e9e9e9;
--session-color: #f4f4f4;
--note-button: #ffffff;
--note-button-text-color: #ffffff;
--unselected-note-button-text-color: #000000;
--option-background: #ffffff;
--invert: 100%;
--bottomBarHover: #e4e4e4;
}
/* dark mode */
@media (prefers-color-scheme: dark) {
:root {
--invertdm: 100%;
--contrast: #2d2f21;
--contrast2: #2d2f21;
--bar: #2d2f31;
--editor: #202124;
--text-color: #ffffff;
--border-color: #393b3d;
--theme-color: #157efb;
--theme-text-color: #ffffff;
--exit-color: #454649;
--session-color: #2d2f31;
--note-button: #202124;
--note-button-text-color: #ffffff;
--unselected-note-button-text-color: #ffffff;
--option-background: #202124;
--invert: 100%;
--bottomBarHover: #e4e4e4;
}
.startDiv p {
color: white !important;
}
.topBar p {
color: white !important;
}
.newNote {
color: white !important;
}
.newNote img {
filter: invert(100%);
}
#errorDiv p {
color: white !important;
}
.optionsCoverDiv p {
color: white !important;
}
.burgerSession img {
filter: invert(100%) !important;
}
.links a {
color: white !important;
}
.inoutdiv p {
color: white !important;
}
.inoutdiv a {
color: #969696 !important;
}
.inoutdiv input {
color: white;
background-color: var(--editor);
}
.flathubLogo {
filter: invert(100%);
}
.feature {
background-color: rgba(0, 0, 0, 0) !important;
color: var(--text-color);
}
.mainDiv .yellow {
border-color: #e9e98d !important;
}
.mainDiv .green {
border-color: #a9f9a9 !important;
}
}
p,
li,
h1,
h2,
h3,
h4,
h5,
h6 {
color: var(--text-color);
white-space: break-spaces;
}
h7 {
display: block;
font-size: 20px;
margin-top: 0.67em;
margin-bottom: 0.67em;
margin-left: 0;
margin-right: 0;
}
body {
margin: 0;
background-color: var(--editor);
font-family: "Inter", sans-serif;
}
.hiddenButton {
right: 0px;
position: fixed;
background-color: var(--editor);
color: var(--editor);
padding: 20px;
}
/* Web app */
.topBar {
position: fixed;
width: 100%;
height: 50px;
background-color: var(--bar);
border: solid;
border-color: var(--border-color);
border-width: 0px;
border-bottom-width: 1px;
}
.bottomBar {
position: fixed;
width: 100%;
height: 29px;
bottom: 0;
background-color: var(--bar);
border: solid;
border-color: var(--border-color);
border-width: 0px;
border-top-width: 1px;
display: flex;
}
.bottomBar button {
background-color: rgba(0, 0, 0, 0);
color: var(--text-color);
height: 100%;
border: none;
font-size: 14px;
padding-left: 7.5px;
padding-right: 7.5px;
}
.bottomBar .removeButton {
filter: invert(var(--invertdm));
padding-left: 17.5px;
padding-right: 17.5px;
background-image: url("../../static/svg/delete.svg");
background-position: center;
background-repeat: no-repeat;
background-size: 55%;
}
.bottomBar .textManipulator {
margin-left: auto;
}
.bottomBar button:hover {
background-color: var(--bottomBarHover);
}
.bottomBar .right {
float: right;
}
.burgerDropdown {
position: fixed;
z-index: 2;
left: 7px;
top: 30px;
width: 160px;
height: 90px;
background-color: var(--bar);
border: solid;
border-color: var(--border-color);
border-width: 1px;
border-radius: 8px;
}
.burgerDropdown a {
position: absolute;
width: calc(100% - 14px - 4px - 7px);
color: var(--text-color);
background-color: #ffffff;
height: 35px;
line-height: 35px;
margin: 7px;
padding-left: 7px;
border: solid;
border-color: var(--border-color);
border-width: 1px;
border-radius: 8px;
font-size: 15px;
text-decoration: none;
}
.topBar p {
display: inline-block;
}
.topBar .logo {
padding-left: 12px;
}
.topBar .usernameBox {
text-align: right;
position: absolute;
cursor: pointer;
right: 12px;
}
.notesBar {
position: fixed;
width: 180px;
top: 50px;
height: calc(100% - 50px - 30px - 1px);
background-color: var(--bar);
border: solid;
border-color: var(--border-color);
border-width: 0px;
border-right-width: 1px;
border-top-width: 1px;
}
.notesDiv {
height: calc(100% - 50px);
overflow-y: scroll;
}
.notesBar .noteButton, .notesBar .loadingStuff {
width: calc(100% - 7px - 7px - 3.5px);
height: 35px;
line-height: 0px;
padding: 10px;
margin: 5px;
margin-bottom: 0;
background-color: var(--note-button);
color: var(--unselected-note-button-text-color);
border: none;
border-radius: 8px;
border: solid;
border-color: var(--border-color);
border-width: 1px;
font-size: 15px;
text-align: left;
cursor: pointer;
overflow-x: hidden;
}
.notesBar .loadingStuff {
border: none;
background:
linear-gradient(0.25turn, transparent, var(--contrast2), transparent),
linear-gradient(var(--contrast), var(--contrast)),
radial-gradient(38px circle at 19px 19px, #eee 50%, transparent 51%),
linear-gradient(var(--contrast), var(--contrast));
background-repeat: no-repeat;
background-size: 315px 250px, 315px 180px, 100px 100px, 225px 30px;
background-position: -315px 0, 0 0, 0px 190px, 50px 195px;
animation: loading 1.5s infinite;
}
@keyframes loading {
to {
background-position: 315px 0, 0 0, 0 190px, 50px 195px;
}
}
.notesBar .selected {
background-color: var(--theme-color) !important;
border: none;
color: var(--note-button-text-color);
}
.notesBar .newNote {
height: 41px;
line-height: 41px;
width: 100%;
text-align: left;
background-color: rgba(0, 0, 0, 0);
border: none;
font-size: 16px;
margin-bottom: 5px;
cursor: pointer;
}
.notesBar .newNote img {
height: 32px;
padding-right: 5px;
transform: translateY(30%);
}
.noteBox {
resize: none;
position: fixed;
right: 0;
top: 55px;
padding: 4px;
border: none;
font-size: 16px;
color: var(--text-color);
background-color: var(--editor);
width: calc(100% - 180px - 7px - 6px);
height: calc(100% - 50px - 6px - 8px - 30px);
font-family: "Inter", sans-serif;
}
.noteBox:focus {
outline: none;
}
.optionsCoverDiv {
position: fixed;
width: 100%;
height: 100%;
z-index: 2;
background-color: rgba(0, 0, 0, 0.7);
transition: opacity 1s;
}
.optionsDiv {
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
position: fixed;
background-color: var(--option-background);
padding: 10px;
color: var(--text-color);
border-radius: 8px;
min-width: 300px;
z-index: 3;
}
.optionsDiv button {
width: 100%;
padding: 10px;
padding-bottom: 13px;
margin-right: 5px;
margin-bottom: 7px;
padding-left: 15px;
padding-right: 15px;
color: var(--theme-text-color);
border: none;
text-decoration: none;
background-color: var(--theme-color);
border-radius: 8px;
cursor: pointer;
}
.optionsDiv .normalButton {
width: auto;
margin-bottom: 0px;
}
.optionsDiv .lastButton {
margin-bottom: 0px;
}
.optionsDiv input {
width: calc(100% - 12px);
height: 25px;
background-color: #ffffff;
padding-left: 5px;
padding-right: 5px;
margin-bottom: 7px;
border: solid;
border-color: var(--border-color);
border-width: 1px;
border-radius: 8px;
}
.optionsDiv .mfacheckbox {
display: inline;
margin: 0;
margin-left: 5px;
margin-bottom: 12px;
padding: 0;
height: 17px;
width: 17px;
}
.optionsDiv input:focus {
outline: 0;
border-color: var(--theme-color);
}
.optionsDiv code {
padding: 7px;
font-size: 14px;
border-radius: 8px;
background-color: var(--session-color);
}
.optionsDiv progress {
width: 100%;
background-color: var(--session-color);
border: none;
border-radius: 99px;
height: 7px;
}
.optionsDiv progress::-moz-progress-bar {
background: var(--theme-color);
}
.optionsDiv progresss::-webkit-progress-value {
background: var(--theme-color);
}
.optionsDiv .exit {
position: absolute;
right: 5px;
width: 40px;
height: 40px;
font-size: 16px;
background-color: var(--exit-color);
color: var(--text-color);
border-radius: 100%;
}
.optionsDiv img {
height: 18px;
padding-right: 5px;
filter: invert(var(--invert));
transform: translateY(3.25px);
}
.optionsDiv .section {
width: 100%;
height: 1px;
background-color: var(--border-color);
margin-top: 2px;
margin-bottom: 10px;
}
.sessionDiv {
max-height: 255px;
overflow-y: auto;
}
.sessionDiv div {
position: relative;
background-color: var(--session-color);
border-radius: 8px;
margin-bottom: 5px;
padding: 10px;
height: 35px;
}
.sessionDiv div p {
display: inline;
position: absolute;
transform: translateY(-7.5px);
}
.sessionDiv div button {
position: absolute;
border-radius: 99px;
right: 15px;
width: 40px;
height: 40px;
font-size: 16px;
transform: translateY(-2px);
}
.sessionDiv img {
display: inline;
filter: none;
height: 100%;
}
/* Sign up/log in div */
.inoutdiv {
margin: 10%;
padding: 30px;
border-radius: 25px;
border: solid 1px var(--border-color);
background-color: var(--bar);
}
.inoutdiv input {
width: calc(100% - 120px);
height: 30px;
margin-bottom: 10px;
padding-left: 10px;
padding-right: 10px;
border: solid;
border-color: var(--border-color);
border-width: 1px;
border-radius: 8px;
}
.inoutdiv button {
background-color: var(--theme-color);
color: white;
padding: 10px;
margin-right: 5px;
padding-left: 20px;
padding-right: 20px;
border: none;
border-radius: 8px;
font-size: 14px;
}
.inoutdiv a {
color: grey;
text-align: center;
}
.hidden {
display: none !important;
}
.w100 {
font-weight: 300;
}
.w200 {
font-weight: 300;
}
.w300 {
font-weight: 300;
}
.w400 {
font-weight: 400;
}
.w500 {
font-weight: 500;
}
.w600 {
font-weight: 600;
}
.w700 {
font-weight: 700;
}
.w800 {
font-weight: 800;
}
.w900 {
font-weight: 900;
}
.alertDiv {
position: fixed;
margin: 0;
width: 100%;
background-color: #ffffeb;
height: 25px;
z-index: 9999;
top: 0;
}
/* main */
.mainDiv {
text-align: center;
}
.startDiv {
text-align: left;
margin-top: 8vh;
margin-left: 7vh;
display: flex;
flex-direction: column;
}
.startDiv h1 {
margin: 0;
}
.mainDiv a {
padding: 15px;
padding-left: 20px;
padding-right: 20px;
margin-right: auto;
color: white;
text-decoration: none;
background-color: var(--theme-color);
border-radius: 8px;
}
.mainDiv .feature {
width: calc(100% - 7vh - 7vh - 3.5vh);
margin-bottom: 10px;
margin-left: 7vh;
margin-right: 7vh;
padding-left: 10px;
padding-right: 10px;
border: solid;
border-radius: 8px;
border-width: 1px;
border-color: var(--border-color);
padding-top: 15px;
padding-bottom: 15px;
font-size: 17px;
padding-bottom: 30px;
}
.mainDiv .green {
background-color: #ebffeb;
}
.mainDiv .yellow {
background-color: #ffffeb;
}
.links {
text-align: center;
padding: 10px;
}
.links a {
margin-left: 5px;
text-decoration: none;
background-color: var(--bar);
color: var(--text-color);
padding: 10px;
padding-top: 2.5px;
margin-bottom: 10px;
border-radius: 10px;
transition: background-color .2s;
display: inline-block;
}
.links a:hover {
background-color: var(--editor);
}
.links a img {
transform: translateY(5px);
padding-right: 10px;
filter: invert(var(--invertdm));
}
.links a:hover {
text-decoration: dashed;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

57
static/fonts/inter.css Normal file
View File

@ -0,0 +1,57 @@
/* Variable fonts usage:
:root { font-family: "Inter", sans-serif; }
@supports (font-variation-settings: normal) {
:root { font-family: "InterVariable", sans-serif; font-optical-sizing: auto; }
} */
@font-face {
font-family: InterVariable;
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url("InterVariable.woff2") format("woff2");
}
@font-face {
font-family: InterVariable;
font-style: italic;
font-weight: 100 900;
font-display: swap;
src: url("InterVariable-Italic.woff2") format("woff2");
}
/* static fonts */
@font-face { font-family: "Inter"; font-style: normal; font-weight: 100; font-display: swap; src: url("Inter-Thin.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: italic; font-weight: 100; font-display: swap; src: url("Inter-ThinItalic.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 200; font-display: swap; src: url("Inter-ExtraLight.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: italic; font-weight: 200; font-display: swap; src: url("Inter-ExtraLightItalic.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 300; font-display: swap; src: url("Inter-Light.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: italic; font-weight: 300; font-display: swap; src: url("Inter-LightItalic.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 400; font-display: swap; src: url("Inter-Regular.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: italic; font-weight: 400; font-display: swap; src: url("Inter-Italic.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 500; font-display: swap; src: url("Inter-Medium.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: italic; font-weight: 500; font-display: swap; src: url("Inter-MediumItalic.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 600; font-display: swap; src: url("Inter-SemiBold.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: italic; font-weight: 600; font-display: swap; src: url("Inter-SemiBoldItalic.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 700; font-display: swap; src: url("Inter-Bold.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: italic; font-weight: 700; font-display: swap; src: url("Inter-BoldItalic.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 800; font-display: swap; src: url("Inter-ExtraBold.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: italic; font-weight: 800; font-display: swap; src: url("Inter-ExtraBoldItalic.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 900; font-display: swap; src: url("Inter-Black.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: italic; font-weight: 900; font-display: swap; src: url("Inter-BlackItalic.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: normal; font-weight: 100; font-display: swap; src: url("InterDisplay-Thin.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: italic; font-weight: 100; font-display: swap; src: url("InterDisplay-ThinItalic.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: normal; font-weight: 200; font-display: swap; src: url("InterDisplay-ExtraLight.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: italic; font-weight: 200; font-display: swap; src: url("InterDisplay-ExtraLightItalic.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: normal; font-weight: 300; font-display: swap; src: url("InterDisplay-Light.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: italic; font-weight: 300; font-display: swap; src: url("InterDisplay-LightItalic.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: normal; font-weight: 400; font-display: swap; src: url("InterDisplay-Regular.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: italic; font-weight: 400; font-display: swap; src: url("InterDisplay-Italic.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: normal; font-weight: 500; font-display: swap; src: url("InterDisplay-Medium.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: italic; font-weight: 500; font-display: swap; src: url("InterDisplay-MediumItalic.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: normal; font-weight: 600; font-display: swap; src: url("InterDisplay-SemiBold.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: italic; font-weight: 600; font-display: swap; src: url("InterDisplay-SemiBoldItalic.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: normal; font-weight: 700; font-display: swap; src: url("InterDisplay-Bold.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: italic; font-weight: 700; font-display: swap; src: url("InterDisplay-BoldItalic.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: normal; font-weight: 800; font-display: swap; src: url("InterDisplay-ExtraBold.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: italic; font-weight: 800; font-display: swap; src: url("InterDisplay-ExtraBoldItalic.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: normal; font-weight: 900; font-display: swap; src: url("InterDisplay-Black.woff2") format("woff2"); }
@font-face { font-family: "InterDisplay"; font-style: italic; font-weight: 900; font-display: swap; src: url("InterDisplay-BlackItalic.woff2") format("woff2"); }

1826
static/js/crypto-js.js Normal file

File diff suppressed because it is too large Load Diff

1353
static/js/hash-wasm.js Normal file

File diff suppressed because one or more lines are too long

49
static/js/homeserver.js Normal file
View File

@ -0,0 +1,49 @@
let homeserverBox = document.getElementById("homeserverBox")
let statusBox = document.getElementById("statusBox")
let changeButton = document.getElementById("changeButton")
function showElements(yesorno) {
if (!yesorno) {
homeserverBox.classList.add("hidden")
changeButton.classList.add("hidden")
}
else {
homeserverBox.classList.remove("hidden")
changeButton.classList.remove("hidden")
}
}
changeButton.addEventListener("click", (event) => {
async function doStuff() {
let remote = homeserverBox.value
if (remote == "") {
statusBox.innerText = "A homeserver is required!"
return
}
showElements(false)
statusBox.innerText = "Connecting to homeserver..."
fetch(remote + "/api/version")
.then((response) => response)
.then((response) => {
async function doStuff() {
if (response.status == 200) {
localStorage.setItem("homeserverURL", remote)
window.location.href = document.referrer;
}
else if (response.status == 404) {
statusBox.innerText = "Not a valid homeserver!"
}
else {
statusBox.innerText = "Something went wrong!"
showElements(true)
}
}
doStuff()
});
}
doStuff()
});

199
static/js/login.js Normal file
View File

@ -0,0 +1,199 @@
if (localStorage.getItem("DONOTSHARE-secretkey") !== null) {
window.location.replace("../app/index.html")
document.body.innerHTML = "Redirecting..."
throw new Error();
}
if (localStorage.getItem("DONOTSHARE-password") !== null) {
window.location.replace("../app/index.html")
document.body.innerHTML = "Redirecting..."
throw new Error();
}
let remote = localStorage.getItem("homeserverURL")
if (remote == null) {
localStorage.setItem("homeserverURL", "https://notes.hectabit.org")
remote = "https://notes.hectabit.org"
}
let usernameBox = document.getElementById("usernameBox")
let passwordBox = document.getElementById("passwordBox")
let statusBox = document.getElementById("statusBox")
let signupButton = document.getElementById("signupButton")
let inputNameBox = document.getElementById("inputNameBox")
let backButton = document.getElementById("backButton")
usernameBox.classList.remove("hidden")
inputNameBox.innerText = "Username:"
let currentInputType = 0
function showInput(inputType) {
if (inputType == 0) {
usernameBox.classList.remove("hidden")
passwordBox.classList.add("hidden")
backButton.classList.add("hidden")
inputNameBox.innerText = "Username:"
statusBox.innerText = "Login to your Burgernotes account!"
currentInputType = 0
} else if (inputType == 1) {
usernameBox.classList.add("hidden")
passwordBox.classList.remove("hidden")
backButton.classList.remove("hidden")
inputNameBox.innerText = "Password:"
currentInputType = 1
} else if (inputType == 2) {
usernameBox.classList.add("hidden")
passwordBox.classList.add("hidden")
signupButton.classList.add("hidden")
backButton.classList.add("hidden")
inputNameBox.classList.add("hidden")
inputNameBox.innerText = "Password:"
currentInputType = 2
}
}
function showElements(yesorno) {
if (!yesorno) {
usernameBox.classList.add("hidden")
passwordBox.classList.add("hidden")
signupButton.classList.add("hidden")
backButton.classList.add("hidden")
inputNameBox.classList.add("hidden")
showInput(currentInputType)
}
else {
usernameBox.classList.remove("hidden")
passwordBox.classList.remove("hidden")
signupButton.classList.remove("hidden")
backButton.classList.remove("hidden")
inputNameBox.classList.remove("hidden")
showInput(currentInputType)
}
}
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("homeserver").innerText = "Your homeserver is: " + remote + ". "
});
signupButton.addEventListener("click", (event) => {
if (passwordBox.classList.contains("hidden")) {
if (usernameBox.value == "") {
statusBox.innerText = "A username is required!"
return
} else {
statusBox.innerText = "Welcome back, " + usernameBox.value + "!"
}
showInput(1)
} else {
async function doStuff() {
let username = usernameBox.value
let password = passwordBox.value
if (password == "") {
statusBox.innerText = "A password is required!"
return
}
showInput(2)
showElements(true)
statusBox.innerText = "Signing in..."
async function hashpassold(pass) {
const key = await hashwasm.argon2id({
password: pass,
salt: await hashwasm.sha512(pass),
parallelism: 1,
iterations: 256,
memorySize: 512,
hashLength: 32,
outputType: "encoded"
});
return key
};
async function hashpass(pass) {
let key = pass
for (let i = 0; i < 128; i++) {
key = await hashwasm.sha3(key)
}
return key
};
fetch(remote + "/api/login", {
method: "POST",
body: JSON.stringify({
username: username,
password: await hashpass(password),
passwordchange: "no",
newpass: "null"
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
.then((response) => response)
.then((response) => {
async function doStuff() {
let responseData = await response.json()
if (response.status == 200) {
localStorage.setItem("DONOTSHARE-secretkey", responseData["key"])
localStorage.setItem("DONOTSHARE-password", await hashwasm.sha512(password))
window.location.href = "../app/index.html"
}
else if (response.status == 401) {
console.log("Trying oldhash")
fetch(remote + "/api/login", {
method: "POST",
body: JSON.stringify({
username: username,
password: await hashpassold(password),
passwordchange: "yes",
newpass: await hashpass(password)
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
.then((response) => response)
.then((response) => {
async function doStuff2() {
let responseData = await response.json()
if (response.status == 200) {
localStorage.setItem("DONOTSHARE-secretkey", responseData["key"])
localStorage.setItem("DONOTSHARE-password", await hashwasm.sha512(password))
window.location.href = "../app/index.html"
}
else if (response.status == 401) {
statusBox.innerText = "Wrong username or password..."
showInput(1)
showElements(true)
}
else {
statusBox.innerText = "Something went wrong! (error code: " + response.status + ")"
showInput(1)
showElements(true)
}
}
doStuff2()
});
}
else {
statusBox.innerText = "Something went wrong! (error code: " + response.status + ")"
showInput(1)
showElements(true)
}
}
doStuff()
});
}
doStuff()
}
});
backButton.addEventListener("click", (event) => {
showInput(0)
});
showInput(0)

642
static/js/main.js Normal file
View File

@ -0,0 +1,642 @@
if (localStorage.getItem("DONOTSHARE-secretkey") === null) {
window.location.replace("../login/index.html")
document.body.innerHTML = "Redirecting..."
throw new Error();
}
if (localStorage.getItem("DONOTSHARE-password") === null) {
window.location.replace("../login/index.html")
document.body.innerHTML = "Redirecting..."
throw new Error();
}
if (localStorage.getItem("CACHE-username") !== null) {
document.getElementById("usernameBox").innerText = localStorage.getItem("CACHE-username")
}
let remote = localStorage.getItem("homeserverURL")
if (remote == null) {
localStorage.setItem("homeserverURL", "https://notes.hectabit.org")
remote = "https://notes.hectabit.org"
}
function formatBytes(a, b = 2) { if (!+a) return "0 Bytes"; const c = 0 > b ? 0 : b, d = Math.floor(Math.log(a) / Math.log(1000)); return `${parseFloat((a / Math.pow(1000, d)).toFixed(c))} ${["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"][d]}` }
function truncateString(str, num) {
if (str.length > num) {
return str.slice(0, num) + "...";
} else {
return str;
}
}
let secretkey = localStorage.getItem("DONOTSHARE-secretkey")
let password = localStorage.getItem("DONOTSHARE-password")
let usernameBox = document.getElementById("usernameBox")
let optionsCoverDiv = document.getElementById("optionsCoverDiv")
let optionsDiv = document.getElementById("optionsDiv")
let errorDiv = document.getElementById("errorDiv")
let errorMessageThing = document.getElementById("errorMessageThing")
let closeErrorButton = document.getElementById("closeErrorButton")
let cancelErrorButton = document.getElementById("cancelErrorButton")
let errorInput = document.getElementById("errorInput")
let exitThing = document.getElementById("exitThing")
let exitSessionsThing = document.getElementById("exitSessionsThing")
let sessionManagerButton = document.getElementById("sessionManagerButton")
let sessionManagerDiv = document.getElementById("sessionManagerDiv")
let sessionDiv = document.getElementById("sessionDiv")
let mfaDiv = document.getElementById("mfaDiv")
let deleteMyAccountButton = document.getElementById("deleteMyAccountButton")
let storageThing = document.getElementById("storageThing")
let storageProgressThing = document.getElementById("storageProgressThing")
let usernameThing = document.getElementById("usernameThing")
let logOutButton = document.getElementById("logOutButton")
let notesBar = document.getElementById("notesBar")
let notesDiv = document.getElementById("notesDiv")
let newNote = document.getElementById("newNote")
let noteBox = document.getElementById("noteBox")
let loadingStuff = document.getElementById("loadingStuff")
let burgerButton = document.getElementById("burgerButton")
let exportNotesButton = document.getElementById("exportNotesButton")
let selectedNote = 0
let timer
let waitTime = 400
if (/Android|iPhone|iPod/i.test(navigator.userAgent)) {
noteBox.style.width = "10px";
notesBar.style.width = "calc(100% - 10px)"
noteBox.readOnly = true
noteBox.style.fontSize = "18px"
noteBox.classList.add("hidden")
notesBar.addEventListener("touchstart", function (event) {
touchstartX = event.changedTouches[0].screenX;
touchstartY = event.changedTouches[0].screenY;
}, false);
notesBar.addEventListener("touchend", function (event) {
touchendX = event.changedTouches[0].screenX;
touchendY = event.changedTouches[0].screenY;
handleGesture();
}, false);
noteBox.addEventListener("touchstart", function (event) {
touchstartX = event.changedTouches[0].screenX;
touchstartY = event.changedTouches[0].screenY;
}, false);
noteBox.addEventListener("touchend", function (event) {
touchendX = event.changedTouches[0].screenX;
touchendY = event.changedTouches[0].screenY;
handleGesture();
}, false);
function handleGesture() {
if (touchendX > touchstartX + 75) {
notesBar.style.width = "calc(100% - 10px)";
noteBox.style.width = "10px"
if (selectedNote != 0) {
noteBox.readOnly = true
}
notesDiv.classList.remove("hidden")
noteBox.classList.add("hidden")
newNote.classList.remove("hidden")
}
if (touchendX < touchstartX - 75) {
noteBox.style.width = "calc(100% - 30px)";
notesBar.style.width = "10px"
if (selectedNote != 0) {
noteBox.readOnly = false
}
notesDiv.classList.add("hidden")
noteBox.classList.remove("hidden")
newNote.classList.add("hidden")
}
}
}
noteBox.value = ""
noteBox.readOnly = true
let noteCount = 0
function displayError(message) {
errorDiv.classList.remove("hidden")
optionsCoverDiv.classList.remove("hidden")
errorMessageThing.innerHTML = message
}
closeErrorButton.addEventListener("click", (event) => {
errorDiv.classList.add("hidden")
optionsCoverDiv.classList.add("hidden")
});
function displayPrompt(message, placeholdertext, callback) {
errorMessageThing.innerText = message
errorInput.value = ""
errorInput.placeholder = placeholdertext
closeErrorButton.addEventListener("click", (event) => {
if (callback) {
callback(errorInput.value)
callback = undefined
}
});
errorInput.addEventListener("keyup", (event) => {
if (event.key == "Enter") {
callback(errorInput.value)
callback = undefined
errorDiv.classList.add("hidden")
optionsCoverDiv.classList.add("hidden")
errorInput.classList.add("hidden")
cancelErrorButton.classList.add("hidden")
}
});
cancelErrorButton.addEventListener("click", (event) => {
callback = undefined
errorDiv.classList.add("hidden")
optionsCoverDiv.classList.add("hidden")
errorInput.classList.add("hidden")
cancelErrorButton.classList.add("hidden")
});
errorDiv.classList.remove("hidden")
optionsCoverDiv.classList.remove("hidden")
errorInput.classList.remove("hidden")
cancelErrorButton.classList.remove("hidden")
errorInput.focus()
}
closeErrorButton.addEventListener("click", (event) => {
errorDiv.classList.add("hidden")
optionsCoverDiv.classList.add("hidden")
errorInput.classList.add("hidden")
cancelErrorButton.classList.add("hidden")
});
function updateFont() {
let currentFontSize = localStorage.getItem("SETTING-fontsize")
noteBox.style.fontSize = currentFontSize + "px"
textSizeBox.innerText = currentFontSize + "px"
}
if (localStorage.getItem("SETTING-fontsize") === null) {
localStorage.setItem("SETTING-fontsize", "16")
updateFont()
} else {
updateFont()
}
textPlusBox.addEventListener("click", (event) => {
localStorage.setItem("SETTING-fontsize", String(Number(localStorage.getItem("SETTING-fontsize")) + Number(1)))
updateFont()
});
textMinusBox.addEventListener("click", (event) => {
localStorage.setItem("SETTING-fontsize", String(Number(localStorage.getItem("SETTING-fontsize")) - Number(1)))
updateFont()
});
function truncateString(str, num) {
if (str.length > num) {
return str.slice(0, num) + "..";
} else {
return str;
}
}
function updateUserInfo() {
fetch(remote + "/api/userinfo", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
.catch((error) => {
noteBox.readOnly = true
noteBox.value = ""
noteBox.placeholder = "Failed to connect to the server.\nPlease check your internet connection."
})
.then((response) => {
async function doStuff() {
if (response.status == 500) {
displayError("Something went wrong! Signing you out..")
closeErrorButton.classList.add("hidden")
usernameBox.innerText = ""
setTimeout(function () {
window.location.replace("../logout/index.html")
}, 2500);
} else {
let responseData = await response.json()
usernameBox.innerText = responseData["username"]
usernameThing.innerText = "Username: " + responseData["username"]
storageThing.innerText = "You've used " + formatBytes(responseData["storageused"]) + " out of " + formatBytes(responseData["storagemax"])
storageProgressThing.value = responseData["storageused"]
storageProgressThing.max = responseData["storagemax"]
noteCount = responseData["notecount"]
localStorage.setItem("CACHE-username", responseData["username"])
}
}
doStuff()
});
}
usernameBox.addEventListener("click", (event) => {
optionsCoverDiv.classList.remove("hidden")
optionsDiv.classList.remove("hidden")
updateUserInfo()
});
logOutButton.addEventListener("click", (event) => {
window.location.replace("../logout/index.html")
});
exitThing.addEventListener("click", (event) => {
optionsDiv.classList.add("hidden")
optionsCoverDiv.classList.add("hidden")
});
deleteMyAccountButton.addEventListener("click", (event) => {
if (confirm("Are you REALLY sure that you want to delete your account? There's no going back!") == true) {
fetch(remote + "/api/deleteaccount", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
.then((response) => {
if (response.status == 200) {
window.location.href = "../logout/index.html"
} else {
displayError("Failed to delete account (HTTP error code " + response.status + ")")
}
})
}
});
sessionManagerButton.addEventListener("click", (event) => {
optionsDiv.classList.add("hidden")
sessionManagerDiv.classList.remove("hidden")
fetch(remote + "/api/sessions/list", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
.then((response) => {
async function doStuff() {
let responseData = await response.json()
document.querySelectorAll(".burgerSession").forEach((el) => el.remove());
for (let i in responseData) {
let sessionElement = document.createElement("div")
let sessionText = document.createElement("p")
let sessionImage = document.createElement("img")
let sessionRemoveButton = document.createElement("button")
sessionText.classList.add("w300")
if (responseData[i]["thisSession"] == true) {
sessionText.innerText = "(current) " + responseData[i]["device"]
} else {
sessionText.innerText = responseData[i]["device"]
}
sessionText.title = responseData[i]["device"]
sessionRemoveButton.innerText = "x"
sessionImage.src = "/static/svg/device_other.svg"
ua = responseData[i]["device"]
if (ua.includes("NT") || ua.includes("Linux")) {
sessionImage.src = "/static/svg/device_computer.svg"
}
if (ua.includes("iPhone" || ua.includes("Android") || ua.include("iPod"))) {
sessionImage.src = "/static/svg/device_smartphone.svg"
}
sessionRemoveButton.addEventListener("click", (event) => {
fetch(remote + "/api/sessions/remove", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey,
sessionId: responseData[i]["id"]
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
.then((response) => {
if (responseData[i]["thisSession"] == true) {
window.location.replace("../logout/index.html")
}
});
sessionElement.remove()
});
sessionElement.append(sessionImage)
sessionElement.append(sessionText)
sessionElement.append(sessionRemoveButton)
sessionElement.classList.add("burgerSession")
sessionDiv.append(sessionElement)
}
}
doStuff()
});
});
exitSessionsThing.addEventListener("click", (event) => {
optionsDiv.classList.remove("hidden")
sessionManagerDiv.classList.add("hidden")
});
updateUserInfo()
function updateWordCount() {
let wordCount = noteBox.value.split(" ").length
if (wordCount == 1) {
wordCount = 0
}
wordCountBox.innerText = wordCount + " words"
}
function selectNote(nameithink) {
document.querySelectorAll(".noteButton").forEach((el) => el.classList.remove("selected"));
let thingArray = Array.from(document.querySelectorAll(".noteButton")).find(el => el.id == nameithink);
thingArray.classList.add("selected")
fetch(remote + "/api/readnote", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey,
noteId: nameithink,
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
.catch((error) => {
noteBox.readOnly = true
noteBox.value = ""
noteBox.placeholder = ""
displayError("Something went wrong... Please try again later!")
})
.then((response) => {
selectedNote = nameithink
noteBox.readOnly = false
noteBox.placeholder = "Type something!"
async function doStuff() {
let responseData = await response.json()
let bytes = CryptoJS.AES.decrypt(responseData["content"], password);
let originalText = bytes.toString(CryptoJS.enc.Utf8);
noteBox.value = originalText
updateWordCount()
noteBox.addEventListener("input", (event) => {
updateWordCount()
clearTimeout(timer);
timer = setTimeout(() => {
let encryptedTitle = "New note"
if (noteBox.value.substring(0, noteBox.value.indexOf("\n")) != "") {
let firstTitle = noteBox.value.substring(0, noteBox.value.indexOf("\n"));
document.getElementById(nameithink).innerText = firstTitle
encryptedTitle = CryptoJS.AES.encrypt(firstTitle, password).toString();
}
let encryptedText = CryptoJS.AES.encrypt(noteBox.value, password).toString();
if (selectedNote == nameithink) {
fetch(remote + "/api/editnote", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey,
noteId: nameithink,
content: encryptedText,
title: encryptedTitle
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
.then((response) => {
if (response.status == 418) {
displayError("You've ran out of storage... Changes will not be saved until you free up storage!")
}
})
.catch((error) => {
displayError("Failed to save changes, please try again later...")
})
}
}, waitTime);
});
}
doStuff()
});
}
function updateNotes() {
fetch(remote + "/api/listnotes", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
.then((response) => {
async function doStuff() {
document.querySelectorAll(".noteButton").forEach((el) => el.remove());
noteBox.readOnly = true
selectedNote = 0
noteBox.placeholder = ""
noteBox.value = ""
clearTimeout(timer)
updateWordCount()
let responseData = await response.json()
for (let i in responseData) {
let noteButton = document.createElement("button");
noteButton.classList.add("noteButton")
notesDiv.append(noteButton)
let bytes = CryptoJS.AES.decrypt(responseData[i]["title"], password);
let originalTitle = bytes.toString(CryptoJS.enc.Utf8);
noteButton.id = responseData[i]["id"]
noteButton.innerText = truncateString(originalTitle, 15)
noteButton.addEventListener("click", (event) => {
if (event.ctrlKey) {
fetch(remote + "/api/removenote", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey,
noteId: responseData[i]["id"]
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
.then((response) => {
updateNotes()
})
.catch((error) => {
displayError("Something went wrong! Please try again later...")
})
} else {
selectNote(responseData[i]["id"])
}
});
}
document.querySelectorAll(".loadingStuff").forEach((el) => el.remove());
}
doStuff()
});
}
updateNotes()
newNote.addEventListener("click", (event) => {
let noteName = "New note"
let encryptedName = CryptoJS.AES.encrypt(noteName, password).toString();
fetch(remote + "/api/newnote", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey,
noteName: encryptedName,
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
.catch((error) => {
displayError("Failed to create new note, please try again later...")
})
.then((response) => {
if (response.status !== 200) {
updateNotes()
displayError("Failed to create new note (HTTP error code " + response.status + ")")
} else {
updateNotes()
}
});
});
function downloadObjectAsJson(exportObj, exportName) {
var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportObj));
var downloadAnchorNode = document.createElement("a");
downloadAnchorNode.setAttribute("href", dataStr);
downloadAnchorNode.setAttribute("download", exportName + ".json");
document.body.appendChild(downloadAnchorNode);
downloadAnchorNode.click();
downloadAnchorNode.remove();
}
function exportNotes() {
let noteExport = []
fetch(remote + "/api/exportnotes", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
.then((response) => {
async function doStuff() {
let responseData = await response.json()
for (let i in responseData) {
exportNotes.innerText = "Decrypting " + i + "/" + noteCount
let bytes = CryptoJS.AES.decrypt(responseData[i]["title"], password);
let originalTitle = bytes.toString(CryptoJS.enc.Utf8);
responseData[i]["title"] = originalTitle
let bytesd = CryptoJS.AES.decrypt(responseData[i]["content"], password);
let originalContent = bytesd.toString(CryptoJS.enc.Utf8);
responseData[i]["content"] = originalContent
}
let jsonString = JSON.parse(JSON.stringify(responseData))
exportNotesButton.innerText = "Export notes"
downloadObjectAsJson(jsonString, "data")
optionsDiv.classList.add("hidden")
displayError("Exported notes!")
}
doStuff()
})
}
function isFirstTimeVisitor() {
if (document.cookie.indexOf("visited=true") !== -1) {
return false;
} else {
var expirationDate = new Date();
expirationDate.setFullYear(expirationDate.getFullYear() + 1);
document.cookie = "visited=true; expires=" + expirationDate.toUTCString() + "; path=/; SameSite=strict";
return true;
}
}
function firstNewVersion() {
if (document.cookie.indexOf("version=1.1") !== -1) {
return false;
} else {
var expirationDate = new Date();
expirationDate.setFullYear(expirationDate.getFullYear() + 1);
document.cookie = "version=1.1; expires=" + expirationDate.toUTCString() + "; path=/; SameSite=strict";
return true;
}
}
exportNotesButton.addEventListener("click", (event) => {
exportNotesButton.innerText = "Downloading..."
exportNotes()
});
removeBox.addEventListener("click", (event) => {
if (selectedNote == 0) {
displayError("You need to select a note first!")
} else {
fetch(remote + "/api/removenote", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey,
noteId: selectedNote
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
.then((response) => {
updateNotes()
})
.catch((error) => {
displayError("Something went wrong! Please try again later...")
})
}
});
if (isFirstTimeVisitor() && /Android|iPhone|iPod/i.test(navigator.userAgent)) {
displayError("To use Burgernotes:\n Swipe Right on a note to open it\n Swipe left in the text boxes to return to notes\n Click on a note to highlight it")
}
if (firstNewVersion()) {
displayError("What's new in Burgernotes 1.1?\n\nNote titles are now the first line of a note \(will not break compatibility with older notes\)\nIntroduced improved login screen\nNote titles now scroll correctly")
}

109
static/js/signup.js Normal file
View File

@ -0,0 +1,109 @@
if (localStorage.getItem("DONOTSHARE-secretkey") !== null) {
window.location.replace("../app/index.html")
document.body.innerHTML = "Redirecting..."
throw new Error();
}
if (localStorage.getItem("DONOTSHARE-password") !== null) {
window.location.replace("../app/index.html")
document.body.innerHTML = "Redirecting..."
throw new Error();
}
let remote = localStorage.getItem("homeserverURL")
if (remote == null) {
localStorage.setItem("homeserverURL", "https://notes.hectabit.org")
remote = "https://notes.hectabit.org"
}
let usernameBox = document.getElementById("usernameBox")
let passwordBox = document.getElementById("passwordBox")
let statusBox = document.getElementById("statusBox")
let signupButton = document.getElementById("signupButton")
function showElements(yesorno) {
if (!yesorno) {
usernameBox.classList.add("hidden")
passwordBox.classList.add("hidden")
signupButton.classList.add("hidden")
}
else {
usernameBox.classList.remove("hidden")
passwordBox.classList.remove("hidden")
signupButton.classList.remove("hidden")
}
}
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("homeserver").innerText = "Your homeserver is: " + remote + ". "
});
signupButton.addEventListener("click", (event) => {
async function doStuff() {
let username = usernameBox.value
let password = passwordBox.value
if (username == "") {
statusBox.innerText = "A username is required!"
return
}
if ((username).length > 20) {
statusBox.innerText = "Username cannot be more than 20 characters!"
return
}
if (password == "") {
statusBox.innerText = "A password is required!"
return
}
if ((password).length < 8) {
statusBox.innerText = "8 or more characters are required!"
return
}
showElements(false)
statusBox.innerText = "Creating account, please hold on..."
async function hashpass(pass) {
let key = pass
for (let i = 0; i < 128; i++) {
key = await hashwasm.sha3(key)
}
return key
};
fetch(remote + "/api/signup", {
method: "POST",
body: JSON.stringify({
username: username,
password: await hashpass(password)
}),
headers: {
"Content-Type": "application/json; charset=UTF-8"
}
})
.then((response) => response)
.then((response) => {
async function doStuff() {
let responseData = await response.json()
if (response.status == 200) {
statusBox.innerText == "redirecting.."
localStorage.setItem("DONOTSHARE-secretkey", responseData["key"])
localStorage.setItem("DONOTSHARE-password", await hashwasm.sha512(password))
window.location.href = "../app/index.html"
}
else if (response.status == 409) {
statusBox.innerText = "Username already taken!"
showElements(true)
}
else {
statusBox.innerText = "Something went wrong!"
showElements(true)
}
}
doStuff()
});
}
doStuff()
});

1
static/svg/add.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M450-450H200v-60h250v-250h60v250h250v60H510v250h-60v-250Z"/></svg>

After

Width:  |  Height:  |  Size: 163 B

1
static/svg/code.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M320-240 80-480l240-240 57 57-184 184 183 183-56 56Zm320 0-57-57 184-184-183-183 56-56 240 240-240 240Z"/></svg>

After

Width:  |  Height:  |  Size: 209 B

1
static/svg/delete.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"/></svg>

After

Width:  |  Height:  |  Size: 300 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="m361-299 119-121 120 121 47-48-119-121 119-121-47-48-120 121-119-121-48 48 120 121-120 121 48 48ZM261-120q-24 0-42-18t-18-42v-570h-41v-60h188v-30h264v30h188v60h-41v570q0 24-18 42t-42 18H261Zm438-630H261v570h438v-570Zm-438 0v570-570Z"/></svg>

After

Width:  |  Height:  |  Size: 338 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M70-120q-12.75 0-21.375-8.675Q40-137.351 40-150.175 40-163 48.625-171.5T70-180h820q12.75 0 21.375 8.675 8.625 8.676 8.625 21.5 0 12.825-8.625 21.325T890-120H70Zm70-120q-24 0-42-18t-18-42v-480q0-24 18-42t42-18h680q24 0 42 18t18 42v480q0 24-18 42t-42 18H140Zm0-60h680v-480H140v480Zm0 0v-480 480Z"/></svg>

After

Width:  |  Height:  |  Size: 399 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M140-160q-24 0-42-18t-18-42v-520q0-24 18-42t42-18h680q24 0 42 18t18 42v520q0 24-18 42t-42 18H140Zm0-60h680v-436H140v436Zm160-72-42-42 103-104-104-104 43-42 146 146-146 146Zm190 4v-60h220v60H490Z"/></svg>

After

Width:  |  Height:  |  Size: 300 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M260-40q-24 0-42-18t-18-42v-760q0-24 18-42t42-18h440q24 0 42 18t18 42v760q0 24-18 42t-42 18H260Zm0-90v30h440v-30H260Zm0-60h440v-580H260v580Zm0-640h440v-30H260v30Zm0 0v-30 30Zm0 700v30-30Z"/></svg>

After

Width:  |  Height:  |  Size: 293 B

1
static/svg/download.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M480-313 287-506l43-43 120 120v-371h60v371l120-120 43 43-193 193ZM220-160q-24 0-42-18t-18-42v-143h60v143h520v-143h60v143q0 24-18 42t-42 18H220Z"/></svg>

After

Width:  |  Height:  |  Size: 249 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.1 KiB

1
static/svg/forum.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M280-240q-17 0-28.5-11.5T240-280v-80h520v-360h80q17 0 28.5 11.5T880-680v600L720-240H280ZM80-280v-560q0-17 11.5-28.5T120-880h520q17 0 28.5 11.5T680-840v360q0 17-11.5 28.5T640-440H240L80-280Zm520-240v-280H160v280h440Zm-440 0v-280 280Z"/></svg>

After

Width:  |  Height:  |  Size: 338 B

3535
static/svg/grid.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 244 KiB

1
static/svg/info.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>

After

Width:  |  Height:  |  Size: 516 B

1
static/svg/list.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M290-620v-60h550v60H290Zm0 170v-60h550v60H290Zm0 170v-60h550v60H290ZM150-620q-12 0-21-9t-9-21.5q0-12.5 9-21t21.5-8.5q12.5 0 21 8.625T180-650q0 12-8.625 21T150-620Zm0 170q-12 0-21-9t-9-21.5q0-12.5 9-21t21.5-8.5q12.5 0 21 8.625T180-480q0 12-8.625 21T150-450Zm0 170q-12 0-21-9t-9-21.5q0-12.5 9-21t21.5-8.5q12.5 0 21 8.625T180-310q0 12-8.625 21T150-280Z"/></svg>

After

Width:  |  Height:  |  Size: 455 B

1
static/svg/logout.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M180-120q-24 0-42-18t-18-42v-600q0-24 18-42t42-18h299v60H180v600h299v60H180Zm486-185-43-43 102-102H360v-60h363L621-612l43-43 176 176-174 174Z"/></svg>

After

Width:  |  Height:  |  Size: 247 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M634-320q-14 0-24-10t-10-24v-132q0-14 10-24t24-10h6v-40q0-33 23.5-56.5T720-640q33 0 56.5 23.5T800-560v40h6q14 0 24 10t10 24v132q0 14-10 24t-24 10H634Zm46-200h80v-40q0-17-11.5-28.5T720-600q-17 0-28.5 11.5T680-560v40ZM280-40q-33 0-56.5-23.5T200-120v-720q0-33 23.5-56.5T280-920h400q33 0 56.5 23.5T760-840v160h-80v-40H280v480h400v-40h80v160q0 33-23.5 56.5T680-40H280Zm0-120v40h400v-40H280Zm0-640h400v-40H280v40Zm0 0v-40 40Zm0 640v40-40Z"/></svg>

After

Width:  |  Height:  |  Size: 538 B