notatio/templates/index.html

357 lines
17 KiB
HTML
Raw Normal View History

2023-11-20 22:10:40 -06:00
<!DOCTYPE html>
<html lang="en_US">
<head>
<title>Notatio</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
<!-- Font Awesome CSS for spinner -->
<link rel="stylesheet" type="text/css"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<link rel="stylesheet" type="text/css"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
<script>
document.addEventListener("DOMContentLoaded", function () {
const passwordInput = document.getElementById("signup-password");
const passwordRequirements = document.querySelectorAll(".password-requirement");
passwordInput.addEventListener("input", function () {
const password = passwordInput.value;
passwordRequirements.forEach(function (requirement) {
const requirementRegex = new RegExp(requirement.dataset.regex);
if (requirementRegex.test(password)) {
requirement.classList.add("text-success");
} else {
requirement.classList.remove("text-success");
}
});
});
});
</script>
</head>
<body class="d-flex flex-column min-vh-100 ">
<nav class="navbar navbar-expand-lg navbar-light bg-light border-bottom p-2">
<a class="navbar-brand" href="/">
<img src="/static/favicon.ico" alt="" width="24" height="24" class="d-inline-block align-text-top">
Notatio
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="#" data-bs-toggle="modal" data-bs-target="#loginModal">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" data-bs-toggle="modal" data-bs-target="#signupModal">Sign up</a>
</li>
</ul>
</div>
</nav>
<main class="container">
<br>
<p>
Welcome to Notatio, an open source, web-based text editor written in the powerful Go programming language.
</p>
<p>
Notatio provides a user-friendly interface combined with robust features, making it the perfect choice for
developers, writers, and anyone who interacts with text on a daily basis.
</p>
<p><strong>Notatio is alpha software! Do no use it as your daily driver!</strong></p>
<h1 class="mt-4">Text Editor Comparison</h1>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Features</th>
<th scope="col">Notion.so</th>
<th scope="col">ObsidianMD</th>
<th scope="col">Notatio</th>
</tr>
</thead>
<tbody>
<tr>
<td>Web-Based</td>
<td class="fs-4 text-success fw-bold">&#10003;</td>
<td class="fs-4 text-danger">&#10007;</td>
<td class="fs-4 text-success fw-bold">&#10003;</td>
</tr>
<tr>
<td>Open Source</td>
<td class="fs-4 text-danger">&#10007;</td>
<td class="fs-4 text-danger">&#10007;</td>
<td class="fs-4 text-success fw-bold">&#10003;</td>
</tr>
<tr>
<td> <b>Does not</b> push paid plans</td>
<td class="fs-4 text-danger">&#10007;</td>
<td class="fs-4 text-danger">&#10007;</td>
<td class="fs-4 text-success fw-bold">&#10003;</td>
</tr>
<tr>
<td>Uses Open File Formats</td>
<td class="fs-4 text-danger">&#10007;</td>
<td class="fs-4 text-success fw-bold">&#10003;</td>
<td class="fs-4 text-success fw-bold">&#10003;</td>
</tr>
<tr>
<td>Sleek and Intuitive Interface</td>
<td class="fs-4 text-success fw-bold">&#10003;</td>
<td class="fs-4 text-success fw-bold">&#10003;</td>
<td class="fs-4 text-success fw-bold">&#10003;</td>
</tr>
<tr>
<td>Collaborative Editing</td>
<td class="fs-4 text-success fw-bold">&#10003;</td>
<td class="fs-4 text-danger">&#10007;</td>
<td class="planned">Planned</td>
</tr>
<tr>
<td>Syntax Highlighting</td>
<td class="fs-4 text-danger">&#10007;</td>
<td class="fs-4 fw-bold text-success ">&#10003;</td>
<td class=" planned">Planned</td>
</tr>
</tbody>
</table>
</div>
<div class="modal fade" id="loginModal" tabindex="-1" aria-labelledby="loginModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title" id="loginModalLabel">Log In</h3>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form name="loginForm">
<div class="modal-body">
<div id="login-error" class="red"></div>
<label for="username">Username:</label>
<input type="text" id="username" name="username" required class="form-control"><br>
<label for="login_password">Password:</label>
<input type="password" id="login_password" name="login_password" required
class="form-control"><br>
</div>
<div class="modal-footer justify-content-between">
<p>
Don't have an account?
<a href="#" data-bs-toggle="modal" data-bs-target="#signupModal"
data-bs-dismiss="modal">Sign up</a>
</p>
<button type="submit" id="loginButton" class="btn btn-primary">Login</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal fade" id="signupModal" tabindex="-1" aria-labelledby="signupModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title" id="signupModalLabel">Sign Up</h3>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="post" action="/signup" name="signupForm" class="was-validated">
<div class="modal-body">
<label for="name" class="form-label">Name:</label>
<input type="text" id="name" name="name" pattern="[a-zA-Z]{2,100}" required
class="form-control">
<div class="invalid-feedback">Please provide the name you would like to be addressed by.
</div><br>
<label for="email" class="form-label">Email:</label>
<input type="email" name="email" id="email" required class="form-control" >
<div class="invalid-feedback">Please provide a valid email for password recovery.</div>
<br>
<label for="username" class="form-label">Username:</label>
<div class="input-group has-validation">
<span class="input-group-text">@</span>
<input type="text" onblur="checkUsernameAvailability();" id="signup-username"
name="username" pattern="[a-zA-Z0-9_\-]{5,20}" required class="form-control"
data-bs-toggle="popover"
data-bs-content="Username must be 5-20 characters and can contain letters, numbers, underscores, or hyphens."
data-bs-trigger="focus">
<div class="invalid-feedback">Please select a username that consists of 5 to 20
alphanumeric characters (a-z and 0-9).</div>
</div>
<div id="usernameAvailability"></div>
<br>
<label for="password" class="form-label">Password:</label>
<input type="password" id="signup-password" name="password"
pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=!]).{10,}" required
class="form-control">
<div class="invalid-feedback">
Password must meet the following requirements:
<ul>
<li class="password-requirement" data-regex="\d">At least one digit (0-9)</li>
<li class="password-requirement" data-regex="[a-z]">At least one lowercase letter
(a-z)</li>
<li class="password-requirement" data-regex="[A-Z]">At least one uppercase letter
(A-Z)</li>
<li class="password-requirement" data-regex="[@#$%^&+=!?]">At least one special
symbol (@#$%^&+=!?)</li>
<li class="password-requirement" data-regex=".{10,}">At least 10 characters long
</li>
</ul>
</div>
</div>
<div class="modal-footer justify-content-between">
<p>
Already have an account?
<a href="#" data-bs-toggle="modal" data-bs-target="#loginModal"
data-bs-dismiss="modal">Log
in</a>
</p>
<div class="mb-3">
<button class="btn btn-primary" type="submit" id="submitBtn" disabled>Submit
form</button>
</div>
</div>
</form>
</div>
</div>
</div>
</main>
<footer class="footer mt-auto">
<div class="container d-flex justify-content-center py-3 bg-light ">
<a href="https://codeberg.org/musselman/notatio#download" class="mx-2 text-muted">Download</a>
<a href="https://codeberg.org/Musselman/Notatio/wiki" class="mx-2 text-muted">Documentation</a>
<a href="https://codeberg.org/Musselman/Notatio/issues" class="mx-2 text-muted">Report Issues</a>
<a href="https://codeberg.org/Musselman/Notatio/sponsors" class="mx-2 text-muted">Sponsor</a>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
const signupForm = document.forms.signupForm;
const loginForm = document.forms.loginForm;
const submitBtn = document.getElementById("submitBtn");
signupForm.addEventListener("input", function () {
const formIsValid = signupForm.checkValidity();
submitBtn.disabled = !formIsValid;
});
loginForm.addEventListener("submit", function (event) {
event.preventDefault(); // Prevent the form from being submitted normally
submitLogin();
});
var urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('login')) {
console.log("login parameter")
$('#loginModal').modal('show');
}
else if (urlParams.has('signup')) {
$('#signupModal').modal('show');
console.log("signup parameter")
}
});
const usernameInput = document.getElementById("signup-username");
usernameInput.addEventListener("blur", function () {
checkUsernameAvailability();
});
function checkUsernameAvailability() {
const usernameInput = document.getElementById("signup-username");
const availabilityDiv = document.getElementById("usernameAvailability");
// Get the value of the username input
const username = usernameInput.value;
// Check if the input value meets the required pattern
const usernameRegex = new RegExp(usernameInput.pattern);
if (!usernameRegex.test(username)) {
availabilityDiv.innerHTML = "";
return; // Exit the function if the username is not valid
}
// Send an AJAX request to the server
const xhr = new XMLHttpRequest();
xhr.open("GET", "/checkusername?username=" + username, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
const isAvailable = response.available;
if (!isAvailable) {
availabilityDiv.innerHTML = "<span style='color:green;'>Username is available.</span>";
} else {
availabilityDiv.innerHTML = "<span style='color:red;'>Username is not available.</span>";
}
}
};
xhr.send();
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function submitLogin() {
// Get the values of the username and password fields
const username = loginForm.elements.username.value;
const password = loginForm.elements.login_password.value;
// Create a new FormData object and append the values
const formData = new FormData();
formData.append("username", username);
formData.append("password", password);
// Send the login request as form data
const xhr = new XMLHttpRequest();
xhr.open("POST", "/login", true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// Login successful, redirect to home page
console.log("Waiting for redirect...")
window.location.href = "/home";
} else {
// Login failed, display the error message
const response = JSON.parse(xhr.responseText);
const loginErrorDiv = document.getElementById("login-error");
loginErrorDiv.textContent = response.error;
}
}
};
xhr.send(formData);
// Disable the button to prevent multiple clicks
loginButton.disabled = true;
// Show the spinner
const spinner = document.createElement('i');
spinner.classList.add('fas', 'fa-spinner', 'fa-spin');
loginButton.innerHTML = '';
loginButton.appendChild(spinner);
// Enable the button and remove the spinner after 5 seconds
setTimeout(function () {
loginButton.disabled = false;
loginButton.innerHTML = 'Login';
}, 5000);
sleep(5000).then(() => { loginButton.disabled = false; });
};
</script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</body>
</html>