const TITLE_PREFIX = "Wiki :: "; const TITLE_DOWNLOAD = "Télécharger"; const TITLE_EDIT = "Modifier"; const TITLE_SAVE = "Sauvegarder"; const TITLE_LOGIN = "Connexion"; const TITLE_LOGOUT = "Déconnexion"; const PLACEHOLDER_USERNAME = "Nom d'utilisateur"; const PLACEHOLDER_PASSWORD = "Mot de passe"; var markdown_content = ""; let mode = "display"; function format_markdown() { const md = window.markdownit(); const src = document.getElementById("markdown").value; // Markdown formatted_content = md.render(src); // Prepare mermaid mermaid.initialize(); formatted_content = formatted_content.replace( /
([\s\S]*?)<\/code><\/pre>/g,
(_, code) => `${code}`
);
// Output
document.getElementById("formatted").innerHTML = formatted_content;
// Format mermaid
mermaid.run();
}
function toggle( part ){
let menu = document.getElementById("menu");
let main = document.getElementById("main");
let formatted = document.getElementById("formatted");
let editor = document.getElementById("editor");
let button_menu = document.getElementById("button_menu");
let button_preview = document.getElementById("button_preview");
if( part === "menu"){
if( menu.style.display === "block" ){
menu.style.display = "none";
main.style.gridTemplateColumns = "100vw";
button_menu.style.left = "-1em";
if( button_preview !== null ){
button_preview.style.display = "grid";
}
} else {
menu.style.display = "block";
main.style.gridTemplateColumns = "14em 1fr";
button_menu.style.left = "6em";
if( button_preview !== null ){
button_preview.style.display = "none";
}
}
} else if( part === "preview") {
menu.style.display = "none"
if( editor.style.display !== "none" ){
formatted.style.display = "block"
editor.style.display = "none"
} else {
formatted.style.display = "none"
editor.style.display = "block"
}
}
}
async function save_file(){
try {
const response = await fetch(window.location.pathname,{
method: "PUT",
headers: {
"Content-Type": "text/plain",
"X-SSID": getCookie("SSID"),
},
body: document.getElementById("markdown").value
})
if( response.ok ) {
window.location.assign( window.location.pathname );
} else {
throw new Error(`Failed to update content: ${response.statusText}`);
}
} catch( err ) {
console.error("Error during content update", err)
}
}
function generateMenu(data, parentPath = "") {
const ul = document.createElement("ul");
// separate files and folders
const dirs = [];
const files = [];
data.forEach(item => {
if (typeof item === "string") {
files.push(item);
} else if (typeof item === "object") {
dirs.push(item);
}
});
// sort alphabetically
dirs.sort((a, b) => Object.keys(a)[0].localeCompare(Object.keys(b)[0]));
files.sort((a, b) => a.localeCompare(b));
// directories first
dirs.forEach(item => {
for (const key in item) {
const li = document.createElement("li");
li.className = "dir";
const span = document.createElement("span");
span.textContent = key;
li.appendChild(span);
if (item[key].length > 0) {
li.appendChild(generateMenu(item[key], parentPath + "/" + key));
}
ul.appendChild(li);
}
});
// files at the bottom
files.forEach(file => {
const li = document.createElement("li");
li.className = "file";
const a = document.createElement("a")
a.addEventListener("click", function(){ fetch_markdown(parentPath + "/" + file)})
a.textContent = file.replace(/\.md$/i,"");
a.title = parentPath + "/" + file;
li.appendChild(a)
ul.appendChild(li);
});
return ul;
}
/*
function buttons(){
let match = document.cookie.match(/(?:^|;\s*)user=([^;]+)/);
let user = match ? match[1] : null
let buttons = document.getElementById("buttons")
let download_icon = document.createElement("span");
download_icon.className = "icon icon-download";
let download = document.createElement("a");
download.href = "?download";
download.title = "Télécharger";
download.appendChild(download_icon);
buttons.appendChild(download)
if( user != null ){
// Logged
let logout_icon = document.createElement("span");
logout_icon.className = "icon icon-logout";
let logout = document.createElement("a");
logout.title = "Déconnexion ["+user+"]";
logout.href = "?logout";
logout.appendChild(logout_icon);
buttons.appendChild(logout);
const params = new URLSearchParams(window.location.search);
if( params.has("edit") ){
let save_icon = document.createElement("span");
save_icon.className = "icon icon-save";
let save = document.createElement("a");
save.href = "#";
save.addEventListener("click", function(){ save_file() });
save.title = "enregistrer";
save.appendChild(save_icon);
buttons.appendChild(save);
} else {
let modify_icon = document.createElement("span");
modify_icon.className = "icon icon-edit";
let modify = document.createElement("a");
modify.href = "?edit";
modify.title = "Modifier";
modify.appendChild(modify_icon);
buttons.appendChild(modify);
}
} else {
// Not logged
let login_icon = document.createElement("span");
login_icon.className = "icon icon-login";
let login = document.createElement("a");
login.title = "Connexion";
login.href = "?login";
login.appendChild(login_icon);
buttons.appendChild(login);
}
}
*/
function download_markdown( path ) {
fetch( path, {
headers: {
"X-Download": "true",
"X-SSID": getCookie("SSID"),
}
}).then(response => {
return response.blob();
}).then(blob => {
const link = document.createElement("a");
const objectURL = URL.createObjectURL(blob);
link.href = objectURL;
link.download = markdown_content.filename.replace(/^\//,"").replace(/\//g," - ");
link.click()
link.remove();
URL.revokeObjectURL(objectURL);
})
}
function fetch_markdown(path) {
let button_preview = document.getElementById("button_preview");
if( button_preview != null ){
button_preview.remove();
}
fetch("/md" + path, {
headers: {
"X-SSID": getCookie("SSID"),
}
})
.then(response => {
return response.json()
})
.then(content => {
// update address bar
history.replaceState({},"",path);
// store markdown in page
markdown_content = content
// update content
mode = "display";
update_content();
// create link to page
let page_link = document.createElement("a");
page_link.href=path;
page_link.textContent = TITLE_PREFIX + content.filename;
title = document.getElementById("title");
title.innerHTML = ""
title.appendChild(page_link);
// update window title
document.title = TITLE_PREFIX + content.filename;
})
.catch(err => console.error(err))
;
}
function refresh_menu(){
fetch("/ws/menu", {
headers: {
"X-SSID": getCookie("SSID"),
}
})
.then(response => {
//console.log("response = ",response,"===")
return response.json()
})
.then(data => {
document.getElementById("menu").innerHTML = "";
document.getElementById("menu").appendChild( generateMenu(data, "") );
})
.catch(err => console.error(err));
}
function call_logout(){
fetch("/ws/logout",{
method: "POST",
headers: {
"X-SSID": getCookie("SSID"),
}
})
.then(response => {
return response.text()
})
.then( _ => {
document.cookie = "SSID=; expires=Thu 01 Jan 1970 00:00:00; path=/";
refresh_menu();
fetch_markdown("/")
})
}
function login_page(){
let mask = document.createElement("div")
mask.className = "mask"
mask.id = "mask"
let username = document.createElement("input")
username.className = "username"
username.id = "username";
username.placeholder = PLACEHOLDER_USERNAME;
username.addEventListener("keydown", function(event){
if( event.key === "Enter" ){
event.preventDefault();
document.getElementById("submit").click();
} else if( event.key === "Escape" ){
event.preventDefault();
document.getElementById("cancel").click();
}
})
let password = document.createElement("input")
password.className = "password"
password.type = "password"
password.id = "password";
password.placeholder = PLACEHOLDER_PASSWORD;
password.addEventListener("keydown", function(event){
if( event.key === "Enter" ){
event.preventDefault();
document.getElementById("submit").click();
} else if( event.key === "Escape" ){
event.preventDefault();
document.getElementById("cancel").click();
}
})
let submit = document.createElement("button")
submit.innerText = "Envoyer"
submit.className = "submit"
submit.id = "submit"
submit.addEventListener("click", function(){
fetch("/ws/login",{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
username: document.getElementById("username").value,
password: document.getElementById("password").value,
})
})
.then(response => {
//console.log("response = ",response,"===")
return response.json()
})
.then( data => {
document.cookie = "SSID="+data.ssid+"; path=/";
document.getElementById("mask").remove()
refresh_menu();
fetch_markdown(window.location.pathname)
})
})
let cancel = document.createElement("button")
cancel.innerText = "Annuler"
cancel.className = "cancel"
cancel.id = "cancel"
cancel.addEventListener("click", function(){ document.getElementById("mask").remove()})
let login = document.createElement("div")
login.className = "login";
login.appendChild(username)
login.appendChild(password)
login.appendChild(submit)
login.appendChild(cancel)
mask.appendChild(login);
document.body.appendChild(mask);
}
function session_button(){
let buttons = document.getElementById("buttons")
ssid = getCookie("SSID");
if( ssid === null ){
let login_icon = document.createElement("span")
login_icon.className = "icon icon-login"
let login = document.createElement("a")
login.id = "login";
login.href="#"
login.addEventListener("click", function(){ login_page()})
login.title = TITLE_LOGIN;
login.appendChild(login_icon)
buttons.appendChild(login)
} else {
let logout_icon = document.createElement("span")
logout_icon.className = "icon icon-logout"
let logout = document.createElement("a")
logout.id = "login";
logout.href="#"
logout.addEventListener("click", function(){ call_logout()})
logout.title = TITLE_LOGOUT;
logout.appendChild(logout_icon)
buttons.appendChild(logout)
}
}
function update_content(){
// buttons
let buttons = document.getElementById("buttons")
buttons.innerHTML = "";
if( document.getElementById("button_menu") === null){
button_menu = document.createElement("div");
button_menu.className = "circle";
button_menu.id = "button_menu";
button_menu.addEventListener("click", function(){ toggle("menu") });
menu_icon = document.createElement("span")
menu_icon.className = "icon icon-menu";
button_menu.appendChild(menu_icon);
document.body.appendChild(button_menu);
}
// prepare content
let content = document.getElementById("content");
content.innerHTML = "";
if( mode == "edit" ) {
let edit = document.createElement("div");
edit.className = "content-edit";
let editor = document.createElement("div");
editor.className = "editor";
editor.id = "editor";
let markdown = document.createElement("textarea");
markdown.id = "markdown";
markdown.addEventListener("keyup", function(){ format_markdown() });
let preview = document.createElement("div");
preview.className = "preview";
preview.id = "formatted";
editor.appendChild(markdown);
edit.appendChild(editor);
edit.appendChild(preview);
content.appendChild(edit);
if( markdown_content.write ){
let save_icon = document.createElement("span")
save_icon.className = "icon icon-save"
let save = document.createElement("a")
save.href="#";
save.addEventListener("click", function(){ save_content()})
save.title = TITLE_SAVE;
save.appendChild(save_icon)
buttons.appendChild(save)
}
if( document.getElementById("button_preview") === null ){
button_preview = document.createElement("div");
button_preview.className = "circle";
button_preview.id = "button_preview";
button_preview.addEventListener("click", function(){ toggle("preview") });
preview_icon = document.createElement("span")
preview_icon.className = "icon icon-display";
button_preview.appendChild(preview_icon);
document.body.appendChild(button_preview);
}
} else {
let markdown = document.createElement("textarea");
markdown.id = "markdown";
markdown.style="display:none";
let formatted = document.createElement("div");
formatted.className = "content-main";
formatted.id = "formatted";
content.append(markdown);
content.append(formatted);
if( markdown_content.write ){
let edit_icon = document.createElement("span")
edit_icon.className = "icon icon-edit"
let edit = document.createElement("a")
edit.href="#";
edit.addEventListener("click", function(){ mode = "edit" ; update_content()})
edit.title = TITLE_EDIT;
edit.appendChild(edit_icon)
buttons.appendChild(edit)
}
}
document.getElementById("markdown").value = markdown_content.data;
let download_icon = document.createElement("span")
download_icon.className = "icon icon-download"
let download = document.createElement("a")
download.href="#"
download.addEventListener("click", function(){ download_markdown(window.location.pathname)})
download.title = TITLE_DOWNLOAD;
download.appendChild(download_icon)
buttons.appendChild(download)
session_button()
// format markdown
format_markdown()
}
function save_content(){
fetch("/md"+window.location.pathname,{
method: "PUT",
headers: {
"Content-Type": "text/plain",
"X-SSID": getCookie("SSID"),
},
body: document.getElementById("markdown").value
})
.then(response => {
//console.log("response = ",response,"===")
return response.text()
})
.then(content => {
// refresh
fetch_markdown(window.location.pathname);
})
.catch(err => console.error(err))
;
}
window.addEventListener("load", () => {
refresh_menu()
if( path = "/" || path.match(`\.md$`)){
// fetch content
fetch_markdown(window.location.pathname)
// update footer
footer_url = document.createElement("a");
footer_url.href = window.location.origin;
footer_url.text = window.location.host;
document.getElementById("footer").appendChild(footer_url);
}
/*
document.getElementById("menu").appendChild(generateMenu(menu));
buttons();
format_markdown();
*/
})
function getCookie(name) {
const cookies = document.cookie.split("; ");
for (const cookie of cookies) {
const [key, value] = cookie.split("=");
if (key === name) return value;
}
return null;
}