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; }