Switch Repo to notatio/notatio

This commit is contained in:
James Musselman 2023-11-20 22:10:40 -06:00
commit 9da51a1e6b
26 changed files with 4117 additions and 0 deletions

181
templates/edit.html Normal file
View file

@ -0,0 +1,181 @@
<!DOCTYPE html>
<html lang="en_US">
<head>
<title>Edit File</title>
<link rel="stylesheet" type="text/css"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css">
<link rel="stylesheet" type="text/css"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
</script>
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
<script src="/static/build/editor.js" type="text/javascript"></script>
<style>
.editor:empty:not(:focus):before {
content: attr(data-placeholder);
opacity: 45%;
}
[contenteditable]:focus {
outline: 0px solid transparent;
}
</style>
</head>
<body class="vh-100">
<div class="sticky-top bg-light-grey">
<div class="row">
<div class="col-12">
<div class="mx-auto d-flex justify-content-center">
<h1>Editing File: </h1>
<h1 id="filename" onclick="editFileName()" contenteditable="false">{{.Filename}}</h1>
</div>
</div>
</div>
<div class="row sticky-top bg-light-grey">
<div class="col-12">
<div class="d-flex justify-content-between bg-light-grey border-bottom p-2">
<div>
<div class="dropdown d-inline-block">
<button class="btn dropdown-toggle" type="button" id="headingDropdown"
data-bs-toggle="dropdown" aria-expanded="false">
<i class="fas fa-heading"></i>
</button>
<ul class="dropdown-menu" aria-labelledby="headingDropdown">
<li><button class="dropdown-item" type="button"
onclick="formatText('heading', 'h1')">Heading 1</button></li>
<li><button class="dropdown-item" type="button"
onclick="formatText('heading', 'h2')">Heading 2</button></li>
<li><button class="dropdown-item" type="button"
onclick="formatText('heading', 'h3')">Heading 3</button></li>
<li><button class="dropdown-item" type="button"
onclick="formatText('heading', 'h4')">Heading 4</button></li>
<li><button class="dropdown-item" type="button"
onclick="formatText('heading', 'h5')">Heading 5</button></li>
<li><button class="dropdown-item" type="button"
onclick="formatText('heading', 'h6')">Heading 6</button></li>
</ul>
</div>
<button type="button" class="btn btn-light" onclick="formatText('bold')" aria-label="Bold"><i
class="fas fa-bold"></i></button>
<button type="button" class="btn btn-light" onclick="formatText('italic')"><i
class="fas fa-italic"></i></button>
<button type="button" class="btn btn-light" onclick="formatText('underline')"><i
class="fas fa-underline"></i></button>
<button type="button" class="btn btn-light" onclick="formatText('strikeThrough')"><i
class="fas fa-strikethrough"></i></button>
<button type="button" class="btn btn-light" onclick="formatText('removeFormat')"><i
class="fas fa-eraser"></i></button>
<button class="btn btn-light" onclick="addTable()"><i class="fas fa-table"
disabled></i></button>
<button class="btn btn-light" onclick="addVideo()"><i class="fas fa-video"></i></button>
<button type="button" class="btn btn-light" onclick="undo()"><i
class="fas fa-undo"></i></button>
<button type="button" class="btn btn-light" onclick="redo()"><i
class="fas fa-redo"></i></button>
<button type="button" class="btn btn-light" onclick="formatText('insertUnorderedList')"><i
class="fas fa-list-ul"></i></button>
<button type="button" class="btn btn-light" onclick="formatText('insertOrderedList')"><i
class="fas fa-list-ol"></i></button>
<button type="button" class="btn btn-light" onclick="insertImage()"><i
class="fas fa-image"></i></button>
<button type="button" class="btn btn-light" onclick="formatText('insertHorizontalRule')"><i
class="fas fa-grip-lines"></i></button>
<button type="button" class="btn btn-light" onclick="formatText('formatBlock', '<blockquote>')"
disabled><i class="fas fa-quote-right"></i></button>
<button type="button" class="btn btn-light" onclick="formatText('formatBlock', '<code>')"
disabled><i class="fas fa-code"></i></button>
<button type="button" class="btn btn-light" onclick="formatText('insertHorizontalRule')"><i
class="fas fa-grip-lines"></i></button>
<button class="btn btn-light" onclick="insertLink()"><i class="fas fa-link"></i></button>
</div>
<div>
<button class="btn btn-light" onclick="confirmOverwrite()"> <i class="fa fa-notes-medical"></i>
Templates</i></button>
<button class="btn btn-light" onclick="saveForm()"><i class="fas fa-save"></i></button>
<button class="btn btn-light" onclick="confirmDeletion()"><i class="fas fa-trash"></i></button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div id="template-modal" class="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">Templates</h2>
</div>
<div class="modal-body">
<!-- Generate buttons for each template -->
{{ range .Templates }}
<button type="button" class="btn btn-primary"
onclick="redirectToTemplate('/edit?filename={{.Filename}}&template={{.TemplateName}}')">
{{.TemplateName}}
</button><br>
{{ end }}
</div>
</div>
</div>
</div>
</div>
</div>
<div>
<div>
<form id="save-form" method="post" action="/save?filename={{.Filename}}">
<textarea hidden="true" id="editor-form-content" name="editor"></textarea>
<input type="hidden" id="filename-form-content" name="filename" value="{{.Filename}}">
<div class="editor shadow-none" id="editor" role="textbox" contenteditable="true"
data-placeholder="The page lays unwritten....">
{{.FileContent}}</div>
</form>
</div>
</div>
<div id="tableModal" class="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add Table</h5>
</div>
<div class="modal-body">
<div class="form-row">
<div class="form-group col-sm-6">
<label for="tableRows">Rows</label>
<input type="number" class="form-control" id="tableRows" value="3" min="2">
</div>
<div class="form-group col-sm-6">
<label for="tableColumns">Columns</label>
<input type="number" class="form-control" id="tableColumns" value="3" min="2">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" onclick="document.getElementById('tableModal').style.display = 'none'">Cancel</button>
<button type="button" class="btn btn-primary" onclick="createTable()">Create Table</button>
</div>
</div>
</div>
</div>
</body>
</html>

357
templates/index.html Normal file
View file

@ -0,0 +1,357 @@
<!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>

146
templates/kanban.html Normal file
View file

@ -0,0 +1,146 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<title>Kanban Board</title>
</head>
<body>
<div class="container mt-4">
<h1>Kanban Board</h1>
<form action="/update-task-status" method="POST" class="my-4">
<div class="mb-3">
<label for="title" class="form-label">Task Title</label>
<input type="text" class="form-control" id="title" name="title" required>
</div>
<div class="mb-3">
<label for="description" class="form-label">Task Description</label>
<textarea class="form-control" id="description" name="description" required></textarea>
</div>
<div class="mb-3">
<label for="status" class="form-label">Status</label>
<select class="form-select" id="status" name="status" required>
<option value="todo">To Do</option>
<option value="in_progress">In Progress</option>
<option value="done">Done</option>
</select>
</div>
<input type="hidden" name="task_id" id="task_id">
<button type="submit" class="btn btn-primary" onclick="updateTaskStatus(event)">Add Task</button>
</form>
<h2>Tasks:</h2>
<div class="row">
<div class="col" id="todo">
<h3>To Do</h3>
<div class="sortable" data-status="todo">
{{ range .Tasks }}
{{ if eq .Status "todo" }}
<div class="card my-2" data-id="{{ .ID }}">
<div class="card-body">
<h5 class="card-title">{{ .Title }}</h5>
<p class="card-text">{{ .Description }}</p>
<p class="card-text"><strong>Status:</strong> {{ .Status }}</p>
</div>
</div>
{{ end }}
{{ end }}
</div>
</div>
<div class="col" id="in-progress">
<h3>In Progress</h3>
<div class="sortable" data-status="in_progress">
{{ range .Tasks }}
{{ if eq .Status "in_progress" }}
<div class="card my-2" data-id="{{ .ID }}">
<div class="card-body">
<h5 class="card-title">{{ .Title }}</h5>
<p class="card-text">{{ .Description }}</p>
<p class="card-text"><strong>Status:</strong> {{ .Status }}</p>
</div>
</div>
{{ end }}
{{ end }}
</div>
</div>
<div class="col" id="done">
<h3>Done</h3>
<div class="sortable" data-status="done">
{{ range .Tasks }}
{{ if eq .Status "done" }}
<div class="card my-2" data-id="{{ .ID }}">
<div class="card-body">
<h5 class="card-title">{{ .Title }}</h5>
<p class="card-text">{{ .Description }}</p>
<p class="card-text"><strong>Status:</strong> {{ .Status }}</p>
</div>
</div>
{{ end }}
{{ end }}
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.13.0/Sortable.min.js"></script>
<script>
function updateTaskStatus(event) {
event.preventDefault();
var form = event.target.closest('form');
var taskId = form.querySelector('#task_id').value;
var newStatus = form.querySelector('#status').value;
fetch(form.action, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ id: taskId, status: newStatus })
}).then(function () {
// Update the task status in the card
var card = form.closest('.card');
card.querySelector('.card-text:last-child').innerHTML = '<strong>Status:</strong> ' + newStatus;
}).catch(function (error) {
console.error('Error:', error);
});
}
document.addEventListener('DOMContentLoaded', function () {
var sortableContainers = [].slice.call(document.querySelectorAll('.sortable'));
sortableContainers.forEach(function (container) {
new Sortable(container, {
group: 'kanban',
draggable: '.card',
animation: 150,
handle: '.card',
onEnd: function (evt) {
var taskId = evt.item.getAttribute('data-id');
var newStatus = evt.to.getAttribute('data-status');
// Update the task status in the backend
fetch('/update-task-status', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ id: taskId, status: newStatus })
}).then(function (response) {
if (response.ok) {
// Update the task status in the card
evt.item.querySelector('.card-text:last-child').innerHTML = '<strong>Status:</strong> ' + newStatus;
} else {
console.error('Failed to update task status:', response.status);
}
}).catch(function (error) {
console.error('Error:', error);
});
}
});
});
});
</script>
</body>
</html>

212
templates/list.html Normal file
View file

@ -0,0 +1,212 @@
<!DOCTYPE html>
<html>
<head>
<title>List Files</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
<script>
// Function to convert Unix timestamps to local time
function convertUnixTimeToLocalTime(unixTime, element) {
const date = new Date(unixTime * 1000);
element.textContent = date.toLocaleString();
}
// Call the function for each timestamp on page load
window.onload = function () {
const creationTimeElements = document.querySelectorAll('.creation-time');
const lastEditedTimeElements = document.querySelectorAll('.last-edited-time');
creationTimeElements.forEach(element => {
const unixTime = parseInt(element.getAttribute('data-unix'));
convertUnixTimeToLocalTime(unixTime, element);
});
lastEditedTimeElements.forEach(element => {
const unixTime = parseInt(element.getAttribute('data-unix'));
convertUnixTimeToLocalTime(unixTime, element);
});
};
</script>
</head>
<body>
<div id="page-container" class="container">
<h1>Welcome, {{.Username}}!</h1>
<!-- Upload Form Modal -->
<div id="uploadModal" class="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Upload File</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form method="post" action="/upload" enctype="multipart/form-data">
<input type="file" name="file" id="file" accept=".md,.html" multiple required /><br /><br />
<button type="submit" class="btn btn-primary">Upload</button>
</form>
</div>
</div>
</div>
</div>
<!-- New File Modal -->
<div id="newFileModal" class="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Create New File</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="newFileForm" method="post" action="/create">
<label for="newFileName">File Name:</label>
<input type="text" name="newFileName" id="newFileName" required><br /><br />
<button type="submit" id="createNewFile" class="btn btn-primary">Create</button>
</form>
</div>
</div>
</div>
</div>
<div id="buttons-container" class="d-flex justify-content-between">
<div>
<button id="openUploadModal" class="btn btn-primary" data-bs-toggle="modal"
data-bs-target="#uploadModal">Upload File</button>
</div>
<div>
<button id="openNewFileModal" class="btn btn-primary" data-bs-toggle="modal"
data-bs-target="#newFileModal">Create New File</button>
</div>
<div>
<button id="openDeleteFileModal" class="btn btn-primary" data-bs-toggle="modal"
data-bs-target="#deleteFileModal">Delete Files</button>
</div>
<div>
<button id="exportFolder" class="btn btn-primary">Export Folder</button>
</div>
<div>
<button onclick="location.href='/logout'" class="btn btn-primary">Logout</button>
</div>
</div>
<!-- Delete File Modal -->
<div id="deleteFileModal" class="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Delete Files</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="deleteForm" action="/delete" method="post">
<div class="checklist">
{{range .Files}}
<label>
<input type="checkbox" name="filesToDelete[]" value="{{.Filename}}">
{{.Filename}}
</label>
<br>
{{end}}
</div>
<button type="button" data-bs-dismiss="modal" class="btn btn-secondary">Cancel</button>
<button type="button" id="checkAll" class="btn btn-primary">Check All</button>
<button type="submit" id="deleteSelected" class="btn btn-danger">Delete Selected</button>
</form>
</div>
</div>
</div>
</div>
<table class="table">
<thead>
<tr>
<th>File Name</th>
<th>Creation Time</th>
<th>Last Edited</th>
</tr>
</thead>
<tbody>
{{range .Files}}
<tr>
<td>
<a href="/edit?filename={{.Filename}}">{{.Filename}}</a>
</td>
<td>
<span class="creation-time" data-unix="{{.CreationTime}}"></span>
</td>
<td>
<span class="last-edited-time" data-unix="{{.LastEdited}}"></span>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
const deleteForm = document.getElementById("deleteForm");
const checkAllButton = document.getElementById("checkAll");
const checkboxes = document.querySelectorAll('input[type="checkbox"]');
checkAllButton.addEventListener("click", function () {
checkboxes.forEach(checkbox => {
checkbox.checked = true;
});
});
deleteForm.addEventListener("submit", function (event) {
event.preventDefault(); // Stop form submission
// Get all checkboxes in the delete form
const checkboxes = deleteForm.querySelectorAll('input[type="checkbox"]');
const filesToDelete = [];
// Get the filenames of the selected files
checkboxes.forEach(checkbox => {
if (checkbox.checked) {
filesToDelete.push(checkbox.value);
}
});
// Call the deleteFiles function to send the request to the server
deleteFiles(filesToDelete);
});
function deleteFiles(filesToDelete) {
// Send a POST request to the server to delete the selected files
fetch("/delete", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ files: filesToDelete }),
})
.then(response => {
if (response.ok) {
// Files deleted successfully
location.reload(); // Refresh the page to reflect the changes
} else {
// Handle the error, e.g., display an error message
console.error("Error deleting files");
}
})
.catch(error => {
console.error("Error deleting files:", error);
});
}
// Handle exportFolder button click
const exportFolderButton = document.getElementById("exportFolder");
exportFolderButton.addEventListener("click", function () {
window.location.href = "/export";
});
</script>
</body>
</html>

24
templates/newuser.html Normal file
View file

@ -0,0 +1,24 @@
<html>
<head>
<link rel="stylesheet" type="text/css" href="/static/style.css">
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
</head>
<body>
<h2>Welcome to Notatio!</h2>
<p>Before you begin creating your first file we want to give you a few tips!</p>
<tip>Although E2EE is planned, It is not implemented yet so <strong>DO NOT</strong> store sensitive information.
(The admins can read it)</tip>
<tip>Use unique and meaningful file names, dates often dont convey what the file holds, nor does temp or document 1.
</tip>
<tip>Back up your files often if you are using a public instance! You never know If the admin will take the service
down.</tip>
<h4>Enter the name of your first file:</h4>
<form id="newFileForm" method="post" action="/create">
<input type="text" name="newFileName" id="newFileName" required><br><br>
<button type="submit" id="createNewFile">Create</button>
</form>
</body>
</html>