diff --git a/burgernotes-app b/burgernotes-app deleted file mode 100755 index 93ddc15..0000000 Binary files a/burgernotes-app and /dev/null differ diff --git a/go.mod b/go.mod index b5af947..6609a5e 100644 --- a/go.mod +++ b/go.mod @@ -2,5 +2,9 @@ module hectabit.org/burgernotes-app go 1.22.2 -require github.com/arzumify/webview_go-4.1 v0.0.0-20240425153857-cdb51de8ba32 // indirect -replace github.com/arzumify/webview_go-4.1 v0.0.0-20240425153857-cdb51de8ba32 => "./webkit-4.1" +require ( + github.com/arzumify/webview_go-4.1 v0.0.0-20240425153857-cdb51de8ba32 + github.com/gotk3/gotk3 v0.6.3 +) + +replace github.com/arzumify/webview_go-4.1 v0.0.0-20240425153857-cdb51de8ba32 => ./webkit-4.1 diff --git a/go.sum b/go.sum index 0796e40..ef31949 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,2 @@ -github.com/arzumify/webview_go-4.1 v0.0.0-20240425153334-e12795daefc2 h1:yDtRFTJw5KVTCADMjM10fze4K+3G0uZ7gFFLkoVa8UE= -github.com/arzumify/webview_go-4.1 v0.0.0-20240425153334-e12795daefc2/go.mod h1:5oDdOhCdYkLdjNnr/+tZO65M+oUlEa/ddnevtaIb7LM= -github.com/arzumify/webview_go-4.1 v0.0.0-20240425153857-cdb51de8ba32 h1:BmQ/UMgzmODmCqX3wKDGfwlrPWYoL7ZLNY0bYHvhjno= -github.com/arzumify/webview_go-4.1 v0.0.0-20240425153857-cdb51de8ba32/go.mod h1:5oDdOhCdYkLdjNnr/+tZO65M+oUlEa/ddnevtaIb7LM= +github.com/gotk3/gotk3 v0.6.3 h1:+Ke4WkM1TQUNOlM2TZH6szqknqo+zNbX3BZWVXjSHYw= +github.com/gotk3/gotk3 v0.6.3/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q= diff --git a/main.go b/main.go index 5b807ee..1ab9143 100644 --- a/main.go +++ b/main.go @@ -1,20 +1,155 @@ package main import ( + "fmt" + "path/filepath" + "archive/zip" "github.com/arzumify/webview_go-4.1" "net/http" "os" - "path/filepath" + "io" + "strconv" ) -func main() { - go func() { - exepath, _ := os.Executable() - path, _ := filepath.EvalSymlinks(exepath) +func upgrade(path string) { + err := os.RemoveAll(filepath.Join(path, "website")) + if err != nil { + fmt.Println("[ERROR] Failed to delete current version:", err) + return + } + err = os.MkdirAll(filepath.Join(path, "website"), os.ModePerm) + if err != nil { + fmt.Println("[ERROR] Failed to create website directory:", err) + return + } + resp, err := http.Get("https://centrifuge.hectabit.org/HectaBit/Burgernotes-client-web/archive/main.zip") + if err != nil { + fmt.Println("[ERROR] Cannot fetch latest version:", err) + return + } + defer resp.Body.Close() + tempFile, err := os.CreateTemp("", "upgrade_*.zip") + if err != nil { + fmt.Println("[ERROR] Failed to create temporary file:", err) + return + } + defer os.Remove(tempFile.Name()) + _, err = io.Copy(tempFile, resp.Body) + if err != nil { + fmt.Println("[ERROR] Failed to copy zip content to temporary file:", err) + return + } + zipReader, err := zip.OpenReader(tempFile.Name()) + if err != nil { + fmt.Println("[ERROR] Failed to open zip file:", err) + return + } + defer zipReader.Close() + for _, file := range zipReader.File { + dstPath := filepath.Join(filepath.Join(path, "website"), file.Name) + if file.FileInfo().IsDir() { + os.MkdirAll(dstPath, os.ModePerm) + continue + } + fileReader, err := file.Open() + if err != nil { + fmt.Println("[ERROR] Failed to open file in zip:", err) + return + } + defer fileReader.Close() + dstFile, err := os.Create(dstPath) + if err != nil { + fmt.Println("[ERROR] Failed to create destination file:", err) + return + } + defer dstFile.Close() + _, err = io.Copy(dstFile, fileReader) + if err != nil { + fmt.Println("[ERROR] Failed to copy file contents:", err) + return + } + } + src, err := os.Open(filepath.Join(filepath.Join(path, "website"), "burgernotes-client-web")) + if err != nil { + fmt.Println("[ERROR] Cannot find created folder:", err) + return + } + files, err := src.Readdir(-1) + if err != nil { + fmt.Println("[ERROR] Failed to read files:", err) + return + } + for _, file := range files { + srcPath := filepath.Join(filepath.Join(filepath.Join(path, "website"), "burgernotes-client-web"), file.Name()) + dstPath := filepath.Join(filepath.Join(path, "website"), file.Name()) + err := os.Rename(srcPath, dstPath) + if err != nil { + fmt.Println("[ERROR] Failed to move files:", err) + return + } + } + err = os.Remove(filepath.Join(filepath.Join(path, "website"), "burgernotes-client-web")) + if err != nil { + fmt.Println("[ERROR] Failed to delete source directory:", err) + return + } + file, err := os.OpenFile(filepath.Join(filepath.Join(path, "website"), "index.html"), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) + if err != nil { + fmt.Println("[ERROR] Failed to open index.html:", err) + return + } + defer file.Close() + filecontent := "BurgernotesRedirecting..." + _, err = file.WriteString(filecontent) + if err != nil { + fmt.Println("[ERROR] Failed to replace index.html:", err) + return + } +} +func vcheck(path string) { + localVersion, err := os.ReadFile(path + "/website/static/version.txt") + if err != nil { + fmt.Println("[ERROR] Cannot get local version:", err) + os.Exit(1) + } + localVersionNum, err := strconv.Atoi(string(localVersion)) + if err != nil { + fmt.Println("[ERROR] Failed to convert local version to integer:", err) + return + } + resp, err := http.Get("https://notes.hectabit.org/static/version.txt") + if err != nil { + fmt.Println("[ERROR] Cannot fetch remote version:", err) + return + } + remoteVersion, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Println("[ERROR] Failed to read remote version:", err) + return + } + defer resp.Body.Close() + remoteVersionNum, err := strconv.Atoi(string(remoteVersion)) + if err != nil { + fmt.Println("[ERROR] Failed to convert remote version to integer:", err) + return + } + if localVersionNum < remoteVersionNum { + fmt.Println("[INFO] Local version is old. Attempting upgrade...") + upgrade(path) + } else { + fmt.Println("[INFO] Up to date") + } +} + +func main() { + exepath, _ := os.Executable() + path, _ := filepath.EvalSymlinks(exepath) + go func() { http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(filepath.Dir(path) + "/website")))) http.ListenAndServe("localhost:52064", nil) }() + vcheck(filepath.Dir(path)) w := webview.New(false) defer w.Destroy() diff --git a/rdir.html b/rdir.html deleted file mode 100644 index 841cf32..0000000 --- a/rdir.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Burgernotes - - - - -Redirecting... - diff --git a/website/.gitignore b/website/.gitignore new file mode 100644 index 0000000..485dee6 --- /dev/null +++ b/website/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/website/app/index.html b/website/app/index.html index 72111a2..e9e8b5f 100644 --- a/website/app/index.html +++ b/website/app/index.html @@ -1,23 +1,15 @@ - + Burgernotes - - - - + + + + @@ -29,6 +21,7 @@
+
@@ -38,7 +31,7 @@
+ src="/static/svg/add.svg">New note
@@ -56,10 +49,10 @@

Account managment

- - - - + + + +
-
- +
+ + +
- + - - - -

{{ errorMessage }}

- {{ errorCode }} | {{ errorMessage }} - - - - - diff --git a/website/homeserver/index.html b/website/homeserver/index.html index 0b1d4ed..66a0a75 100644 --- a/website/homeserver/index.html +++ b/website/homeserver/index.html @@ -1,5 +1,5 @@ - + Signup - Burgernotes @@ -7,13 +7,13 @@ - - + +

Image by perga (@pergagreen on discord)

- +

Homeserver

Change your Burgernotes homeserver

@@ -24,7 +24,7 @@

Please put in the URL in standard format; https://, http://, etc.

- + diff --git a/website/index.html b/website/index.html index 841cf32..602bcd7 100644 --- a/website/index.html +++ b/website/index.html @@ -1,13 +1 @@ - - - - - Burgernotes - - - - -Redirecting... - +BurgernotesRedirecting... \ No newline at end of file diff --git a/website/login/index.html b/website/login/index.html index d8c1b0a..0477053 100644 --- a/website/login/index.html +++ b/website/login/index.html @@ -1,28 +1,19 @@ - + Login - Burgernotes - - - - + + +

Image by perga (@pergagreen on discord)

- +

Login

@@ -33,9 +24,9 @@

-

Don't have an account? If so, Create one here!

+

Don't have an account? If so, Create one here!

Your homeserver is loading...

Change
- Privacy & Terms + Privacy & Terms
diff --git a/website/logout/index.html b/website/logout/index.html index f2c1f70..b77d35f 100644 --- a/website/logout/index.html +++ b/website/logout/index.html @@ -1,25 +1,16 @@ - + Burgernotes - - - -Logging out.. + + +

Logging out...

diff --git a/website/privacy/index.html b/website/privacy/index.html index 616e10c..725608c 100644 --- a/website/privacy/index.html +++ b/website/privacy/index.html @@ -1,22 +1,13 @@ - + Burgernotes Privacy & Terms - - - + + @@ -39,21 +30,21 @@
  • Web browser "User agent"
  • Information we collect while using our services

    -

    When you create an note, we collect and use this information:

    +

    When you create a note, we collect and use this information:

    -

    When you edit an note, we collect and use this information:

    +

    When you edit a note, we collect and use this information:

    How we use your data

    We use your data to make our services work. We don't share your information with third-parties.

    -

    We can't see notes you create's content and title

    +

    We can't see the content and title of the notes you create

    Your notes are encrypted end-to-end using AES (Advanced Encryption Standard) 256-bit encryption.

    We can only see:

    @@ -79,7 +70,7 @@ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.


    - +

    diff --git a/website/signup/index.html b/website/signup/index.html index e2dbceb..f896d63 100644 --- a/website/signup/index.html +++ b/website/signup/index.html @@ -1,28 +1,19 @@ - + Signup - Burgernotes - - - - + + +

    Image by perga (@pergagreen on discord)

    - +

    Signup

    Signup for a Burgernotes account

    @@ -30,10 +21,10 @@


    -

    Already have an account? If so, Login instead!

    +

    Already have an account? If so, Login instead!

    Please note that it's impossible to reset your password, do not forget it!

    Your homeserver is loading...

    Change
    - Privacy & Terms + Privacy & Terms
    - + diff --git a/website/static/css/style.css b/website/static/css/style.css index 1212323..3947ec5 100644 --- a/website/static/css/style.css +++ b/website/static/css/style.css @@ -345,6 +345,21 @@ body { width: calc(100% - 180px - 7px - 6px); height: calc(100% - 50px - 6px - 8px - 30px); font-family: "Inter", sans-serif; + display: flex; +} + +.noteBoxText { + background-color: var(--editor); + color: var(--text-color); + border: none; + width: 100%; + font-family: "Inter", sans-serif; +} + +iframe#markdown { + width: 100%; + border: none; + border-left: solid var(--bar) 1px; } .noteBox:focus { diff --git a/website/static/js/login.js b/website/static/js/login.js index a415070..9703f1b 100644 --- a/website/static/js/login.js +++ b/website/static/js/login.js @@ -1,10 +1,10 @@ if (localStorage.getItem("DONOTSHARE-secretkey") !== null) { - window.location.replace("../app/index.html") + window.location.replace("/app/") document.body.innerHTML = "Redirecting..." throw new Error(); } if (localStorage.getItem("DONOTSHARE-password") !== null) { - window.location.replace("../app/index.html") + window.location.replace("/app/") document.body.innerHTML = "Redirecting..." throw new Error(); } @@ -28,20 +28,20 @@ inputNameBox.innerText = "Username:" let currentInputType = 0 function showInput(inputType) { - if (inputType == 0) { + 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) { + } 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) { + } else if (inputType === 2) { usernameBox.classList.add("hidden") passwordBox.classList.add("hidden") signupButton.classList.add("hidden") @@ -75,9 +75,9 @@ document.addEventListener('DOMContentLoaded', function() { document.getElementById("homeserver").innerText = "Your homeserver is: " + remote + ". " }); -signupButton.addEventListener("click", (event) => { +signupButton.addEventListener("click", () => { if (passwordBox.classList.contains("hidden")) { - if (usernameBox.value == "") { + if (usernameBox.value === "") { statusBox.innerText = "A username is required!" return } else { @@ -89,7 +89,7 @@ signupButton.addEventListener("click", (event) => { let username = usernameBox.value let password = passwordBox.value - if (password == "") { + if (password === "") { statusBox.innerText = "A password is required!" return } @@ -99,7 +99,7 @@ signupButton.addEventListener("click", (event) => { statusBox.innerText = "Signing in..." async function hashpassold(pass) { - const key = await hashwasm.argon2id({ + return await hashwasm.argon2id({ password: pass, salt: await hashwasm.sha512(pass), parallelism: 1, @@ -107,9 +107,8 @@ signupButton.addEventListener("click", (event) => { memorySize: 512, hashLength: 32, outputType: "encoded" - }); - return key - }; + }) + } async function hashpass(pass) { let key = pass @@ -117,7 +116,7 @@ signupButton.addEventListener("click", (event) => { key = await hashwasm.sha3(key) } return key - }; + } fetch(remote + "/api/login", { method: "POST", @@ -135,13 +134,13 @@ signupButton.addEventListener("click", (event) => { .then((response) => { async function doStuff() { let responseData = await response.json() - if (response.status == 200) { + if (response.status === 200) { localStorage.setItem("DONOTSHARE-secretkey", responseData["key"]) localStorage.setItem("DONOTSHARE-password", await hashwasm.sha512(password)) - window.location.href = "../app/index.html" + window.location.href = "/app/" } - else if (response.status == 401) { + else if (response.status === 401) { console.log("Trying oldhash") fetch(remote + "/api/login", { method: "POST", @@ -159,13 +158,13 @@ signupButton.addEventListener("click", (event) => { .then((response) => { async function doStuff2() { let responseData = await response.json() - if (response.status == 200) { + if (response.status === 200) { localStorage.setItem("DONOTSHARE-secretkey", responseData["key"]) localStorage.setItem("DONOTSHARE-password", await hashwasm.sha512(password)) - window.location.href = "../app/index.html" + window.location.href = "/app/" } - else if (response.status == 401) { + else if (response.status === 401) { statusBox.innerText = "Wrong username or password..." showInput(1) showElements(true) @@ -192,7 +191,7 @@ signupButton.addEventListener("click", (event) => { } }); -backButton.addEventListener("click", (event) => { +backButton.addEventListener("click", () => { showInput(0) }); diff --git a/website/static/js/main.js b/website/static/js/main.js index 4b102ca..d8f285d 100644 --- a/website/static/js/main.js +++ b/website/static/js/main.js @@ -1,10 +1,10 @@ if (localStorage.getItem("DONOTSHARE-secretkey") === null) { - window.location.replace("../login/index.html") + window.location.replace("/login") document.body.innerHTML = "Redirecting..." throw new Error(); } if (localStorage.getItem("DONOTSHARE-password") === null) { - window.location.replace("../login/index.html") + window.location.replace("/login") document.body.innerHTML = "Redirecting..." throw new Error(); } @@ -21,16 +21,10 @@ if (remote == null) { 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 currentFontSize = 16 +let markdowntoggle = false let usernameBox = document.getElementById("usernameBox") let optionsCoverDiv = document.getElementById("optionsCoverDiv") @@ -45,7 +39,6 @@ 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") @@ -56,8 +49,13 @@ 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 markdown = document.getElementById('markdown'); +let textSizeBox = document.getElementById('textSizeBox'); +let textPlusBox = document.getElementById('textPlusBox'); +let textMinusBox = document.getElementById('textMinusBox'); +let wordCountBox = document.getElementById('wordCountBox'); +let removeBox = document.getElementById("removeBox") let selectedNote = 0 let timer @@ -70,6 +68,8 @@ if (/Android|iPhone|iPod/i.test(navigator.userAgent)) { noteBox.style.fontSize = "18px" noteBox.classList.add("hidden") + let touchstartX, touchstartY, touchendX, touchendY + notesBar.addEventListener("touchstart", function (event) { touchstartX = event.changedTouches[0].screenX; touchstartY = event.changedTouches[0].screenY; @@ -96,7 +96,7 @@ if (/Android|iPhone|iPod/i.test(navigator.userAgent)) { if (touchendX > touchstartX + 75) { notesBar.style.width = "calc(100% - 10px)"; noteBox.style.width = "10px" - if (selectedNote != 0) { + if (selectedNote !== 0) { noteBox.readOnly = true } notesDiv.classList.remove("hidden") @@ -107,7 +107,7 @@ if (/Android|iPhone|iPod/i.test(navigator.userAgent)) { if (touchendX < touchstartX - 75) { noteBox.style.width = "calc(100% - 30px)"; notesBar.style.width = "10px" - if (selectedNote != 0) { + if (selectedNote !== 0) { noteBox.readOnly = false } notesDiv.classList.add("hidden") @@ -129,50 +129,11 @@ function displayError(message) { errorMessageThing.innerHTML = message } -closeErrorButton.addEventListener("click", (event) => { +closeErrorButton.addEventListener("click", () => { 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) => { +closeErrorButton.addEventListener("click", () => { errorDiv.classList.add("hidden") optionsCoverDiv.classList.add("hidden") errorInput.classList.add("hidden") @@ -180,9 +141,12 @@ closeErrorButton.addEventListener("click", (event) => { }); function updateFont() { - let currentFontSize = localStorage.getItem("SETTING-fontsize") + currentFontSize = localStorage.getItem("SETTING-fontsize") noteBox.style.fontSize = currentFontSize + "px" textSizeBox.innerText = currentFontSize + "px" + if (markdowntoggle) { + markdown.srcdoc = "" + marked.parse(noteBox.value) + ""; + } } async function waitforedit() { @@ -200,13 +164,13 @@ async function waitforedit() { async function doStuff() { const data = await response.json(); // Access the "note" field from the response - const note = data.note; - if (note == selectedNote) { + const note = data["note"]; + if (note === selectedNote) { selectNote(selectedNote) } } - doStuff(); - }) + doStuff() + }); } } @@ -217,11 +181,11 @@ if (localStorage.getItem("SETTING-fontsize") === null) { updateFont() } -textPlusBox.addEventListener("click", (event) => { +textPlusBox.addEventListener("click", () => { localStorage.setItem("SETTING-fontsize", String(Number(localStorage.getItem("SETTING-fontsize")) + Number(1))) updateFont() }); -textMinusBox.addEventListener("click", (event) => { +textMinusBox.addEventListener("click", () => { localStorage.setItem("SETTING-fontsize", String(Number(localStorage.getItem("SETTING-fontsize")) - Number(1))) updateFont() }); @@ -246,19 +210,19 @@ function updateUserInfo() { "Content-Type": "application/json; charset=UTF-8" } }) - .catch((error) => { + .catch(() => { 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) { + 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") + window.location.replace("/logout") }, 2500); } else { let responseData = await response.json() @@ -274,20 +238,20 @@ function updateUserInfo() { doStuff() }); } -usernameBox.addEventListener("click", (event) => { +usernameBox.addEventListener("click", () => { optionsCoverDiv.classList.remove("hidden") optionsDiv.classList.remove("hidden") updateUserInfo() }); -logOutButton.addEventListener("click", (event) => { - window.location.replace("../logout/index.html") +logOutButton.addEventListener("click", () => { + window.location.replace("/logout") }); -exitThing.addEventListener("click", (event) => { +exitThing.addEventListener("click", () => { 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) { +deleteMyAccountButton.addEventListener("click", () => { + 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({ @@ -298,15 +262,15 @@ deleteMyAccountButton.addEventListener("click", (event) => { } }) .then((response) => { - if (response.status == 200) { - window.location.href = "../logout/index.html" + if (response.status === 200) { + window.location.href = "/logout" } else { displayError("Failed to delete account (HTTP error code " + response.status + ")") } }) } }); -sessionManagerButton.addEventListener("click", (event) => { +sessionManagerButton.addEventListener("click", () => { optionsDiv.classList.add("hidden") sessionManagerDiv.classList.remove("hidden") @@ -323,13 +287,14 @@ sessionManagerButton.addEventListener("click", (event) => { async function doStuff() { let responseData = await response.json() document.querySelectorAll(".burgerSession").forEach((el) => el.remove()); + let ua; 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) { + if (responseData[i]["thisSession"] === true) { sessionText.innerText = "(current) " + responseData[i]["device"] } else { sessionText.innerText = responseData[i]["device"] @@ -344,11 +309,11 @@ sessionManagerButton.addEventListener("click", (event) => { if (ua.includes("NT") || ua.includes("Linux")) { sessionImage.src = "/static/svg/device_computer.svg" } - if (ua.includes("iPhone" || ua.includes("Android") || ua.include("iPod"))) { + if (ua.includes("iPhone" || ua.includes("Android") || ua.includes("iPod"))) { sessionImage.src = "/static/svg/device_smartphone.svg" } - sessionRemoveButton.addEventListener("click", (event) => { + sessionRemoveButton.addEventListener("click", () => { fetch(remote + "/api/sessions/remove", { method: "POST", body: JSON.stringify({ @@ -359,9 +324,9 @@ sessionManagerButton.addEventListener("click", (event) => { "Content-Type": "application/json; charset=UTF-8" } }) - .then((response) => { - if (responseData[i]["thisSession"] == true) { - window.location.replace("../logout/index.html") + .then(() => { + if (responseData[i]["thisSession"] === true) { + window.location.replace("/logout") } }); sessionElement.remove() @@ -379,7 +344,7 @@ sessionManagerButton.addEventListener("click", (event) => { doStuff() }); }); -exitSessionsThing.addEventListener("click", (event) => { +exitSessionsThing.addEventListener("click", () => { optionsDiv.classList.remove("hidden") sessionManagerDiv.classList.add("hidden") }); @@ -388,15 +353,21 @@ updateUserInfo() function updateWordCount() { let wordCount = noteBox.value.split(" ").length - if (wordCount == 1) { + if (wordCount === 1) { wordCount = 0 } wordCountBox.innerText = wordCount + " words" } +function renderMarkDown() { + if (markdowntoggle) { + markdown.srcdoc = "" + marked.parse(noteBox.value) + "" + } +} + function selectNote(nameithink) { document.querySelectorAll(".noteButton").forEach((el) => el.classList.remove("selected")); - let thingArray = Array.from(document.querySelectorAll(".noteButton")).find(el => el.id == nameithink); + let thingArray = Array.from(document.querySelectorAll(".noteButton")).find(el => String(nameithink) === String(el.id)); thingArray.classList.add("selected") fetch(remote + "/api/readnote", { @@ -409,7 +380,7 @@ function selectNote(nameithink) { "Content-Type": "application/json; charset=UTF-8" } }) - .catch((error) => { + .catch(() => { noteBox.readOnly = true noteBox.value = "" noteBox.placeholder = "" @@ -424,17 +395,17 @@ function selectNote(nameithink) { let responseData = await response.json() let bytes = CryptoJS.AES.decrypt(responseData["content"], password); - let originalText = bytes.toString(CryptoJS.enc.Utf8); - - noteBox.value = originalText + noteBox.value = bytes.toString(CryptoJS.enc.Utf8) updateWordCount() + renderMarkDown() - noteBox.addEventListener("input", (event) => { + noteBox.addEventListener("input", () => { updateWordCount() + renderMarkDown() clearTimeout(timer); timer = setTimeout(() => { let encryptedTitle = "New note" - if (noteBox.value.substring(0, noteBox.value.indexOf("\n")) != "") { + if (noteBox.value.substring(0, noteBox.value.indexOf("\n")) !== "") { let firstTitle = noteBox.value.substring(0, noteBox.value.indexOf("\n")); document.getElementById(nameithink).innerText = firstTitle @@ -442,7 +413,7 @@ function selectNote(nameithink) { } let encryptedText = CryptoJS.AES.encrypt(noteBox.value, password).toString(); - if (selectedNote == nameithink) { + if (selectedNote === nameithink) { fetch(remote + "/api/editnote", { method: "POST", body: JSON.stringify({ @@ -456,11 +427,11 @@ function selectNote(nameithink) { } }) .then((response) => { - if (response.status == 418) { + if (response.status === 418) { displayError("You've ran out of storage... Changes will not be saved until you free up storage!") } }) - .catch((error) => { + .catch(() => { displayError("Failed to save changes, please try again later...") }) } @@ -490,6 +461,7 @@ function updateNotes() { noteBox.value = "" clearTimeout(timer) updateWordCount() + renderMarkDown() let responseData = await response.json() for (let i in responseData) { @@ -515,10 +487,10 @@ function updateNotes() { "Content-Type": "application/json; charset=UTF-8" } }) - .then((response) => { + .then(() => { updateNotes() }) - .catch((error) => { + .catch(() => { displayError("Something went wrong! Please try again later...") }) } else { @@ -534,9 +506,9 @@ function updateNotes() { updateNotes() -newNote.addEventListener("click", (event) => { +newNote.addEventListener("click", () => { let noteName = "New note" - let encryptedName = CryptoJS.AES.encrypt(noteName, password).toString(); + let encryptedName = CryptoJS.AES.encrypt(noteName, password).toString(CryptoJS.enc.Utf8); fetch(remote + "/api/newnote", { method: "POST", body: JSON.stringify({ @@ -547,7 +519,7 @@ newNote.addEventListener("click", (event) => { "Content-Type": "application/json; charset=UTF-8" } }) - .catch((error) => { + .catch(() => { displayError("Failed to create new note, please try again later...") }) .then((response) => { @@ -560,8 +532,8 @@ newNote.addEventListener("click", (event) => { }); }); function downloadObjectAsJson(exportObj, exportName) { - var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportObj)); - var downloadAnchorNode = document.createElement("a"); + let dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportObj)); + let downloadAnchorNode = document.createElement("a"); downloadAnchorNode.setAttribute("href", dataStr); downloadAnchorNode.setAttribute("download", exportName + ".json"); document.body.appendChild(downloadAnchorNode); @@ -570,7 +542,6 @@ function downloadObjectAsJson(exportObj, exportName) { } function exportNotes() { - let noteExport = [] fetch(remote + "/api/exportnotes", { method: "POST", body: JSON.stringify({ @@ -587,14 +558,10 @@ function exportNotes() { 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 + responseData[i]["title"] = bytes.toString(CryptoJS.enc.Utf8) let bytesd = CryptoJS.AES.decrypt(responseData[i]["content"], password); - let originalContent = bytesd.toString(CryptoJS.enc.Utf8); - - responseData[i]["content"] = originalContent + responseData[i]["content"] = bytesd.toString(CryptoJS.enc.Utf8) } let jsonString = JSON.parse(JSON.stringify(responseData)) @@ -618,7 +585,7 @@ function isFirstTimeVisitor() { } function firstNewVersion() { - if (localStorage.getItem("NEWVERSION") == "1.2") { + if (localStorage.getItem("NEWVERSION") === "1.2") { return false; } else { localStorage.setItem("NEWVERSION", "1.2") @@ -626,13 +593,25 @@ function firstNewVersion() { } } -exportNotesButton.addEventListener("click", (event) => { +function toggleMarkdown() { + if (markdown.style.display === 'none') { + markdown.style.display = 'inherit'; + markdowntoggle = true + renderMarkDown() + } else { + markdown.style.display = 'none'; + markdowntoggle = false + markdown.srcdoc = "" + } +} + +exportNotesButton.addEventListener("click", () => { exportNotesButton.innerText = "Downloading..." exportNotes() }); -removeBox.addEventListener("click", (event) => { - if (selectedNote == 0) { +removeBox.addEventListener("click", () => { + if (selectedNote === 0) { displayError("You need to select a note first!") } else { fetch(remote + "/api/removenote", { @@ -645,15 +624,19 @@ removeBox.addEventListener("click", (event) => { "Content-Type": "application/json; charset=UTF-8" } }) - .then((response) => { + .then(() => { updateNotes() }) - .catch((error) => { + .catch(() => { displayError("Something went wrong! Please try again later...") }) } }); +document.addEventListener("DOMContentLoaded", function() { + markdown.srcdoc = "" + marked.parse(noteBox.value) + "" +}); + 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") } diff --git a/website/static/js/marked.js b/website/static/js/marked.js new file mode 100644 index 0000000..1668d4c --- /dev/null +++ b/website/static/js/marked.js @@ -0,0 +1,1297 @@ +/** + * beautified version of: + * marked v12.0.2 - a markdown parser + * Copyright (c) 2011-2024, Christopher Jeffrey. (MIT Licensed) + * https://github.com/markedjs/marked + */ +!(function (e, t) { + "object" == typeof exports && "undefined" != typeof module ? t(exports) : "function" == typeof define && define.amd ? define(["exports"], t) : t(((e = "undefined" != typeof globalThis ? globalThis : e || self).marked = {})); +})(this, function (e) { + "use strict"; + function t() { + return { async: !1, breaks: !1, extensions: null, gfm: !0, hooks: null, pedantic: !1, renderer: null, silent: !1, tokenizer: null, walkTokens: null }; + } + function n(t) { + e.defaults = t; + } + e.defaults = { async: !1, breaks: !1, extensions: null, gfm: !0, hooks: null, pedantic: !1, renderer: null, silent: !1, tokenizer: null, walkTokens: null }; + const s = /[&<>"']/, + r = new RegExp(s.source, "g"), + i = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/, + l = new RegExp(i.source, "g"), + o = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }, + a = (e) => o[e]; + function c(e, t) { + if (t) { + if (s.test(e)) return e.replace(r, a); + } else if (i.test(e)) return e.replace(l, a); + return e; + } + const h = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi; + function p(e) { + return e.replace(h, (e, t) => ("colon" === (t = t.toLowerCase()) ? ":" : "#" === t.charAt(0) ? ("x" === t.charAt(1) ? String.fromCharCode(parseInt(t.substring(2), 16)) : String.fromCharCode(+t.substring(1))) : "")); + } + const u = /(^|[^\[])\^/g; + function k(e, t) { + let n = "string" == typeof e ? e : e.source; + t = t || ""; + const s = { + replace: (e, t) => { + let r = "string" == typeof t ? t : t.source; + return (r = r.replace(u, "$1")), (n = n.replace(e, r)), s; + }, + getRegex: () => new RegExp(n, t), + }; + return s; + } + function g(e) { + try { + e = encodeURI(e).replace(/%25/g, "%"); + } catch (e) { + return null; + } + return e; + } + const f = { exec: () => null }; + function d(e, t) { + const n = e + .replace(/\|/g, (e, t, n) => { + let s = !1, + r = t; + for (; --r >= 0 && "\\" === n[r]; ) s = !s; + return s ? "|" : " |"; + }) + .split(/ \|/); + let s = 0; + if ((n[0].trim() || n.shift(), n.length > 0 && !n[n.length - 1].trim() && n.pop(), t)) + if (n.length > t) n.splice(t); + else for (; n.length < t; ) n.push(""); + for (; s < n.length; s++) n[s] = n[s].trim().replace(/\\\|/g, "|"); + return n; + } + function x(e, t, n) { + const s = e.length; + if (0 === s) return ""; + let r = 0; + for (; r < s; ) { + const i = e.charAt(s - r - 1); + if (i !== t || n) { + if (i === t || !n) break; + r++; + } else r++; + } + return e.slice(0, s - r); + } + function b(e, t, n, s) { + const r = t.href, + i = t.title ? c(t.title) : null, + l = e[1].replace(/\\([\[\]])/g, "$1"); + if ("!" !== e[0].charAt(0)) { + s.state.inLink = !0; + const e = { type: "link", raw: n, href: r, title: i, text: l, tokens: s.inlineTokens(l) }; + return (s.state.inLink = !1), e; + } + return { type: "image", raw: n, href: r, title: i, text: c(l) }; + } + class w { + options; + rules; + lexer; + constructor(t) { + this.options = t || e.defaults; + } + space(e) { + const t = this.rules.block.newline.exec(e); + if (t && t[0].length > 0) return { type: "space", raw: t[0] }; + } + code(e) { + const t = this.rules.block.code.exec(e); + if (t) { + const e = t[0].replace(/^ {1,4}/gm, ""); + return { type: "code", raw: t[0], codeBlockStyle: "indented", text: this.options.pedantic ? e : x(e, "\n") }; + } + } + fences(e) { + const t = this.rules.block.fences.exec(e); + if (t) { + const e = t[0], + n = (function (e, t) { + const n = e.match(/^(\s+)(?:```)/); + if (null === n) return t; + const s = n[1]; + return t + .split("\n") + .map((e) => { + const t = e.match(/^\s+/); + if (null === t) return e; + const [n] = t; + return n.length >= s.length ? e.slice(s.length) : e; + }) + .join("\n"); + })(e, t[3] || ""); + return { type: "code", raw: e, lang: t[2] ? t[2].trim().replace(this.rules.inline.anyPunctuation, "$1") : t[2], text: n }; + } + } + heading(e) { + const t = this.rules.block.heading.exec(e); + if (t) { + let e = t[2].trim(); + if (/#$/.test(e)) { + const t = x(e, "#"); + this.options.pedantic ? (e = t.trim()) : (t && !/ $/.test(t)) || (e = t.trim()); + } + return { type: "heading", raw: t[0], depth: t[1].length, text: e, tokens: this.lexer.inline(e) }; + } + } + hr(e) { + const t = this.rules.block.hr.exec(e); + if (t) return { type: "hr", raw: t[0] }; + } + blockquote(e) { + const t = this.rules.block.blockquote.exec(e); + if (t) { + let e = t[0].replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g, "\n $1"); + e = x(e.replace(/^ *>[ \t]?/gm, ""), "\n"); + const n = this.lexer.state.top; + this.lexer.state.top = !0; + const s = this.lexer.blockTokens(e); + return (this.lexer.state.top = n), { type: "blockquote", raw: t[0], tokens: s, text: e }; + } + } + list(e) { + let t = this.rules.block.list.exec(e); + if (t) { + let n = t[1].trim(); + const s = n.length > 1, + r = { type: "list", raw: "", ordered: s, start: s ? +n.slice(0, -1) : "", loose: !1, items: [] }; + (n = s ? `\\d{1,9}\\${n.slice(-1)}` : `\\${n}`), this.options.pedantic && (n = s ? n : "[*+-]"); + const i = new RegExp(`^( {0,3}${n})((?:[\t ][^\\n]*)?(?:\\n|$))`); + let l = "", + o = "", + a = !1; + for (; e; ) { + let n = !1; + if (!(t = i.exec(e))) break; + if (this.rules.block.hr.test(e)) break; + (l = t[0]), (e = e.substring(l.length)); + let s = t[2].split("\n", 1)[0].replace(/^\t+/, (e) => " ".repeat(3 * e.length)), + c = e.split("\n", 1)[0], + h = 0; + this.options.pedantic ? ((h = 2), (o = s.trimStart())) : ((h = t[2].search(/[^ ]/)), (h = h > 4 ? 1 : h), (o = s.slice(h)), (h += t[1].length)); + let p = !1; + if ((!s && /^ *$/.test(c) && ((l += c + "\n"), (e = e.substring(c.length + 1)), (n = !0)), !n)) { + const t = new RegExp(`^ {0,${Math.min(3, h - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`), + n = new RegExp(`^ {0,${Math.min(3, h - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`), + r = new RegExp(`^ {0,${Math.min(3, h - 1)}}(?:\`\`\`|~~~)`), + i = new RegExp(`^ {0,${Math.min(3, h - 1)}}#`); + for (; e; ) { + const a = e.split("\n", 1)[0]; + if (((c = a), this.options.pedantic && (c = c.replace(/^ {1,4}(?=( {4})*[^ ])/g, " ")), r.test(c))) break; + if (i.test(c)) break; + if (t.test(c)) break; + if (n.test(e)) break; + if (c.search(/[^ ]/) >= h || !c.trim()) o += "\n" + c.slice(h); + else { + if (p) break; + if (s.search(/[^ ]/) >= 4) break; + if (r.test(s)) break; + if (i.test(s)) break; + if (n.test(s)) break; + o += "\n" + c; + } + p || c.trim() || (p = !0), (l += a + "\n"), (e = e.substring(a.length + 1)), (s = c.slice(h)); + } + } + r.loose || (a ? (r.loose = !0) : /\n *\n *$/.test(l) && (a = !0)); + let u, + k = null; + this.options.gfm && ((k = /^\[[ xX]\] /.exec(o)), k && ((u = "[ ] " !== k[0]), (o = o.replace(/^\[[ xX]\] +/, "")))), + r.items.push({ type: "list_item", raw: l, task: !!k, checked: u, loose: !1, text: o, tokens: [] }), + (r.raw += l); + } + (r.items[r.items.length - 1].raw = l.trimEnd()), (r.items[r.items.length - 1].text = o.trimEnd()), (r.raw = r.raw.trimEnd()); + for (let e = 0; e < r.items.length; e++) + if (((this.lexer.state.top = !1), (r.items[e].tokens = this.lexer.blockTokens(r.items[e].text, [])), !r.loose)) { + const t = r.items[e].tokens.filter((e) => "space" === e.type), + n = t.length > 0 && t.some((e) => /\n.*\n/.test(e.raw)); + r.loose = n; + } + if (r.loose) for (let e = 0; e < r.items.length; e++) r.items[e].loose = !0; + return r; + } + } + html(e) { + const t = this.rules.block.html.exec(e); + if (t) { + return { type: "html", block: !0, raw: t[0], pre: "pre" === t[1] || "script" === t[1] || "style" === t[1], text: t[0] }; + } + } + def(e) { + const t = this.rules.block.def.exec(e); + if (t) { + const e = t[1].toLowerCase().replace(/\s+/g, " "), + n = t[2] ? t[2].replace(/^<(.*)>$/, "$1").replace(this.rules.inline.anyPunctuation, "$1") : "", + s = t[3] ? t[3].substring(1, t[3].length - 1).replace(this.rules.inline.anyPunctuation, "$1") : t[3]; + return { type: "def", tag: e, raw: t[0], href: n, title: s }; + } + } + table(e) { + const t = this.rules.block.table.exec(e); + if (!t) return; + if (!/[:|]/.test(t[2])) return; + const n = d(t[1]), + s = t[2].replace(/^\||\| *$/g, "").split("|"), + r = t[3] && t[3].trim() ? t[3].replace(/\n[ \t]*$/, "").split("\n") : [], + i = { type: "table", raw: t[0], header: [], align: [], rows: [] }; + if (n.length === s.length) { + for (const e of s) /^ *-+: *$/.test(e) ? i.align.push("right") : /^ *:-+: *$/.test(e) ? i.align.push("center") : /^ *:-+ *$/.test(e) ? i.align.push("left") : i.align.push(null); + for (const e of n) i.header.push({ text: e, tokens: this.lexer.inline(e) }); + for (const e of r) i.rows.push(d(e, i.header.length).map((e) => ({ text: e, tokens: this.lexer.inline(e) }))); + return i; + } + } + lheading(e) { + const t = this.rules.block.lheading.exec(e); + if (t) return { type: "heading", raw: t[0], depth: "=" === t[2].charAt(0) ? 1 : 2, text: t[1], tokens: this.lexer.inline(t[1]) }; + } + paragraph(e) { + const t = this.rules.block.paragraph.exec(e); + if (t) { + const e = "\n" === t[1].charAt(t[1].length - 1) ? t[1].slice(0, -1) : t[1]; + return { type: "paragraph", raw: t[0], text: e, tokens: this.lexer.inline(e) }; + } + } + text(e) { + const t = this.rules.block.text.exec(e); + if (t) return { type: "text", raw: t[0], text: t[0], tokens: this.lexer.inline(t[0]) }; + } + escape(e) { + const t = this.rules.inline.escape.exec(e); + if (t) return { type: "escape", raw: t[0], text: c(t[1]) }; + } + tag(e) { + const t = this.rules.inline.tag.exec(e); + if (t) + return ( + !this.lexer.state.inLink && /^/i.test(t[0]) && (this.lexer.state.inLink = !1), + !this.lexer.state.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(t[0]) + ? (this.lexer.state.inRawBlock = !0) + : this.lexer.state.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(t[0]) && (this.lexer.state.inRawBlock = !1), + { type: "html", raw: t[0], inLink: this.lexer.state.inLink, inRawBlock: this.lexer.state.inRawBlock, block: !1, text: t[0] } + ); + } + link(e) { + const t = this.rules.inline.link.exec(e); + if (t) { + const e = t[2].trim(); + if (!this.options.pedantic && /^$/.test(e)) return; + const t = x(e.slice(0, -1), "\\"); + if ((e.length - t.length) % 2 == 0) return; + } else { + const e = (function (e, t) { + if (-1 === e.indexOf(t[1])) return -1; + let n = 0; + for (let s = 0; s < e.length; s++) + if ("\\" === e[s]) s++; + else if (e[s] === t[0]) n++; + else if (e[s] === t[1] && (n--, n < 0)) return s; + return -1; + })(t[2], "()"); + if (e > -1) { + const n = (0 === t[0].indexOf("!") ? 5 : 4) + t[1].length + e; + (t[2] = t[2].substring(0, e)), (t[0] = t[0].substring(0, n).trim()), (t[3] = ""); + } + } + let n = t[2], + s = ""; + if (this.options.pedantic) { + const e = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(n); + e && ((n = e[1]), (s = e[3])); + } else s = t[3] ? t[3].slice(1, -1) : ""; + return ( + (n = n.trim()), + /^$/.test(e) ? n.slice(1) : n.slice(1, -1)), + b(t, { href: n ? n.replace(this.rules.inline.anyPunctuation, "$1") : n, title: s ? s.replace(this.rules.inline.anyPunctuation, "$1") : s }, t[0], this.lexer) + ); + } + } + reflink(e, t) { + let n; + if ((n = this.rules.inline.reflink.exec(e)) || (n = this.rules.inline.nolink.exec(e))) { + const e = t[(n[2] || n[1]).replace(/\s+/g, " ").toLowerCase()]; + if (!e) { + const e = n[0].charAt(0); + return { type: "text", raw: e, text: e }; + } + return b(n, e, n[0], this.lexer); + } + } + emStrong(e, t, n = "") { + let s = this.rules.inline.emStrongLDelim.exec(e); + if (!s) return; + if (s[3] && n.match(/[\p{L}\p{N}]/u)) return; + if (!(s[1] || s[2] || "") || !n || this.rules.inline.punctuation.exec(n)) { + const n = [...s[0]].length - 1; + let r, + i, + l = n, + o = 0; + const a = "*" === s[0][0] ? this.rules.inline.emStrongRDelimAst : this.rules.inline.emStrongRDelimUnd; + for (a.lastIndex = 0, t = t.slice(-1 * e.length + n); null != (s = a.exec(t)); ) { + if (((r = s[1] || s[2] || s[3] || s[4] || s[5] || s[6]), !r)) continue; + if (((i = [...r].length), s[3] || s[4])) { + l += i; + continue; + } + if ((s[5] || s[6]) && n % 3 && !((n + i) % 3)) { + o += i; + continue; + } + if (((l -= i), l > 0)) continue; + i = Math.min(i, i + l + o); + const t = [...s[0]][0].length, + a = e.slice(0, n + s.index + t + i); + if (Math.min(n, i) % 2) { + const e = a.slice(1, -1); + return { type: "em", raw: a, text: e, tokens: this.lexer.inlineTokens(e) }; + } + const c = a.slice(2, -2); + return { type: "strong", raw: a, text: c, tokens: this.lexer.inlineTokens(c) }; + } + } + } + codespan(e) { + const t = this.rules.inline.code.exec(e); + if (t) { + let e = t[2].replace(/\n/g, " "); + const n = /[^ ]/.test(e), + s = /^ /.test(e) && / $/.test(e); + return n && s && (e = e.substring(1, e.length - 1)), (e = c(e, !0)), { type: "codespan", raw: t[0], text: e }; + } + } + br(e) { + const t = this.rules.inline.br.exec(e); + if (t) return { type: "br", raw: t[0] }; + } + del(e) { + const t = this.rules.inline.del.exec(e); + if (t) return { type: "del", raw: t[0], text: t[2], tokens: this.lexer.inlineTokens(t[2]) }; + } + autolink(e) { + const t = this.rules.inline.autolink.exec(e); + if (t) { + let e, n; + return "@" === t[2] ? ((e = c(t[1])), (n = "mailto:" + e)) : ((e = c(t[1])), (n = e)), { type: "link", raw: t[0], text: e, href: n, tokens: [{ type: "text", raw: e, text: e }] }; + } + } + url(e) { + let t; + if ((t = this.rules.inline.url.exec(e))) { + let e, n; + if ("@" === t[2]) (e = c(t[0])), (n = "mailto:" + e); + else { + let s; + do { + (s = t[0]), (t[0] = this.rules.inline._backpedal.exec(t[0])?.[0] ?? ""); + } while (s !== t[0]); + (e = c(t[0])), (n = "www." === t[1] ? "http://" + t[0] : t[0]); + } + return { type: "link", raw: t[0], text: e, href: n, tokens: [{ type: "text", raw: e, text: e }] }; + } + } + inlineText(e) { + const t = this.rules.inline.text.exec(e); + if (t) { + let e; + return (e = this.lexer.state.inRawBlock ? t[0] : c(t[0])), { type: "text", raw: t[0], text: e }; + } + } + } + const m = /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/, + y = /(?:[*+-]|\d{1,9}[.)])/, + $ = k(/^(?!bull |blockCode|fences|blockquote|heading|html)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html))+?)\n {0,3}(=+|-+) *(?:\n+|$)/) + .replace(/bull/g, y) + .replace(/blockCode/g, / {4}/) + .replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/) + .replace(/blockquote/g, / {0,3}>/) + .replace(/heading/g, / {0,3}#{1,6}/) + .replace(/html/g, / {0,3}<[^\n>]+>\n/) + .getRegex(), + z = /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/, + T = /(?!\s*\])(?:\\.|[^\[\]\\])+/, + R = k(/^ {0,3}\[(label)\]: *(?:\n *)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/) + .replace("label", T) + .replace("title", /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/) + .getRegex(), + _ = k(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/) + .replace(/bull/g, y) + .getRegex(), + A = + "address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul", + S = /|$))/, + I = k( + "^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n *)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$))", + "i" + ) + .replace("comment", S) + .replace("tag", A) + .replace("attribute", / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/) + .getRegex(), + E = k(z) + .replace("hr", m) + .replace("heading", " {0,3}#{1,6}(?:\\s|$)") + .replace("|lheading", "") + .replace("|table", "") + .replace("blockquote", " {0,3}>") + .replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n") + .replace("list", " {0,3}(?:[*+-]|1[.)]) ") + .replace("html", ")|<(?:script|pre|style|textarea|!--)") + .replace("tag", A) + .getRegex(), + q = { + blockquote: k(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/) + .replace("paragraph", E) + .getRegex(), + code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/, + def: R, + fences: /^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/, + heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, + hr: m, + html: I, + lheading: $, + list: _, + newline: /^(?: *(?:\n|$))+/, + paragraph: E, + table: f, + text: /^[^\n]+/, + }, + Z = k("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)") + .replace("hr", m) + .replace("heading", " {0,3}#{1,6}(?:\\s|$)") + .replace("blockquote", " {0,3}>") + .replace("code", " {4}[^\\n]") + .replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n") + .replace("list", " {0,3}(?:[*+-]|1[.)]) ") + .replace("html", ")|<(?:script|pre|style|textarea|!--)") + .replace("tag", A) + .getRegex(), + L = { + ...q, + table: Z, + paragraph: k(z) + .replace("hr", m) + .replace("heading", " {0,3}#{1,6}(?:\\s|$)") + .replace("|lheading", "") + .replace("table", Z) + .replace("blockquote", " {0,3}>") + .replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n") + .replace("list", " {0,3}(?:[*+-]|1[.)]) ") + .replace("html", ")|<(?:script|pre|style|textarea|!--)") + .replace("tag", A) + .getRegex(), + }, + P = { + ...q, + html: k("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))") + .replace("comment", S) + .replace(/tag/g, "(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b") + .getRegex(), + def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, + heading: /^(#{1,6})(.*)(?:\n+|$)/, + fences: f, + lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/, + paragraph: k(z) + .replace("hr", m) + .replace("heading", " *#{1,6} *[^\n]") + .replace("lheading", $) + .replace("|table", "") + .replace("blockquote", " {0,3}>") + .replace("|fences", "") + .replace("|list", "") + .replace("|html", "") + .replace("|tag", "") + .getRegex(), + }, + Q = /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, + v = /^( {2,}|\\)\n(?!\s*$)/, + B = "\\p{P}\\p{S}", + C = k(/^((?![*_])[\spunctuation])/, "u") + .replace(/punctuation/g, B) + .getRegex(), + M = k(/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/, "u") + .replace(/punct/g, B) + .getRegex(), + O = k( + "^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)[punct](\\*+)(?=[\\s]|$)|[^punct\\s](\\*+)(?!\\*)(?=[punct\\s]|$)|(?!\\*)[punct\\s](\\*+)(?=[^punct\\s])|[\\s](\\*+)(?!\\*)(?=[punct])|(?!\\*)[punct](\\*+)(?!\\*)(?=[punct])|[^punct\\s](\\*+)(?=[^punct\\s])", + "gu" + ) + .replace(/punct/g, B) + .getRegex(), + D = k("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)[punct](_+)(?=[\\s]|$)|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)|(?!_)[punct\\s](_+)(?=[^punct\\s])|[\\s](_+)(?!_)(?=[punct])|(?!_)[punct](_+)(?!_)(?=[punct])", "gu") + .replace(/punct/g, B) + .getRegex(), + j = k(/\\([punct])/, "gu") + .replace(/punct/g, B) + .getRegex(), + H = k(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/) + .replace("scheme", /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/) + .replace("email", /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/) + .getRegex(), + U = k(S).replace("(?:--\x3e|$)", "--\x3e").getRegex(), + X = k("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^") + .replace("comment", U) + .replace("attribute", /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/) + .getRegex(), + F = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/, + N = k(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/) + .replace("label", F) + .replace("href", /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/) + .replace("title", /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/) + .getRegex(), + G = k(/^!?\[(label)\]\[(ref)\]/) + .replace("label", F) + .replace("ref", T) + .getRegex(), + J = k(/^!?\[(ref)\](?:\[\])?/) + .replace("ref", T) + .getRegex(), + K = { + _backpedal: f, + anyPunctuation: j, + autolink: H, + blockSkip: /\[[^[\]]*?\]\([^\(\)]*?\)|`[^`]*?`|<[^<>]*?>/g, + br: v, + code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, + del: f, + emStrongLDelim: M, + emStrongRDelimAst: O, + emStrongRDelimUnd: D, + escape: Q, + link: N, + nolink: J, + punctuation: C, + reflink: G, + reflinkSearch: k("reflink|nolink(?!\\()", "g").replace("reflink", G).replace("nolink", J).getRegex(), + tag: X, + text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\ t + " ".repeat(n.length)); e; ) + if (!(this.options.extensions && this.options.extensions.block && this.options.extensions.block.some((s) => !!(n = s.call({ lexer: this }, e, t)) && ((e = e.substring(n.raw.length)), t.push(n), !0)))) + if ((n = this.tokenizer.space(e))) (e = e.substring(n.raw.length)), 1 === n.raw.length && t.length > 0 ? (t[t.length - 1].raw += "\n") : t.push(n); + else if ((n = this.tokenizer.code(e))) + (e = e.substring(n.raw.length)), + (s = t[t.length - 1]), + !s || ("paragraph" !== s.type && "text" !== s.type) ? t.push(n) : ((s.raw += "\n" + n.raw), (s.text += "\n" + n.text), (this.inlineQueue[this.inlineQueue.length - 1].src = s.text)); + else if ((n = this.tokenizer.fences(e))) (e = e.substring(n.raw.length)), t.push(n); + else if ((n = this.tokenizer.heading(e))) (e = e.substring(n.raw.length)), t.push(n); + else if ((n = this.tokenizer.hr(e))) (e = e.substring(n.raw.length)), t.push(n); + else if ((n = this.tokenizer.blockquote(e))) (e = e.substring(n.raw.length)), t.push(n); + else if ((n = this.tokenizer.list(e))) (e = e.substring(n.raw.length)), t.push(n); + else if ((n = this.tokenizer.html(e))) (e = e.substring(n.raw.length)), t.push(n); + else if ((n = this.tokenizer.def(e))) + (e = e.substring(n.raw.length)), + (s = t[t.length - 1]), + !s || ("paragraph" !== s.type && "text" !== s.type) + ? this.tokens.links[n.tag] || (this.tokens.links[n.tag] = { href: n.href, title: n.title }) + : ((s.raw += "\n" + n.raw), (s.text += "\n" + n.raw), (this.inlineQueue[this.inlineQueue.length - 1].src = s.text)); + else if ((n = this.tokenizer.table(e))) (e = e.substring(n.raw.length)), t.push(n); + else if ((n = this.tokenizer.lheading(e))) (e = e.substring(n.raw.length)), t.push(n); + else { + if (((r = e), this.options.extensions && this.options.extensions.startBlock)) { + let t = 1 / 0; + const n = e.slice(1); + let s; + this.options.extensions.startBlock.forEach((e) => { + (s = e.call({ lexer: this }, n)), "number" == typeof s && s >= 0 && (t = Math.min(t, s)); + }), + t < 1 / 0 && t >= 0 && (r = e.substring(0, t + 1)); + } + if (this.state.top && (n = this.tokenizer.paragraph(r))) + (s = t[t.length - 1]), + i && "paragraph" === s.type ? ((s.raw += "\n" + n.raw), (s.text += "\n" + n.text), this.inlineQueue.pop(), (this.inlineQueue[this.inlineQueue.length - 1].src = s.text)) : t.push(n), + (i = r.length !== e.length), + (e = e.substring(n.raw.length)); + else if ((n = this.tokenizer.text(e))) + (e = e.substring(n.raw.length)), + (s = t[t.length - 1]), + s && "text" === s.type ? ((s.raw += "\n" + n.raw), (s.text += "\n" + n.text), this.inlineQueue.pop(), (this.inlineQueue[this.inlineQueue.length - 1].src = s.text)) : t.push(n); + else if (e) { + const t = "Infinite loop on byte: " + e.charCodeAt(0); + if (this.options.silent) { + console.error(t); + break; + } + throw new Error(t); + } + } + return (this.state.top = !0), t; + } + inline(e, t = []) { + return this.inlineQueue.push({ src: e, tokens: t }), t; + } + inlineTokens(e, t = []) { + let n, + s, + r, + i, + l, + o, + a = e; + if (this.tokens.links) { + const e = Object.keys(this.tokens.links); + if (e.length > 0) + for (; null != (i = this.tokenizer.rules.inline.reflinkSearch.exec(a)); ) + e.includes(i[0].slice(i[0].lastIndexOf("[") + 1, -1)) && (a = a.slice(0, i.index) + "[" + "a".repeat(i[0].length - 2) + "]" + a.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex)); + } + for (; null != (i = this.tokenizer.rules.inline.blockSkip.exec(a)); ) a = a.slice(0, i.index) + "[" + "a".repeat(i[0].length - 2) + "]" + a.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); + for (; null != (i = this.tokenizer.rules.inline.anyPunctuation.exec(a)); ) a = a.slice(0, i.index) + "++" + a.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex); + for (; e; ) + if ( + (l || (o = ""), + (l = !1), + !(this.options.extensions && this.options.extensions.inline && this.options.extensions.inline.some((s) => !!(n = s.call({ lexer: this }, e, t)) && ((e = e.substring(n.raw.length)), t.push(n), !0)))) + ) + if ((n = this.tokenizer.escape(e))) (e = e.substring(n.raw.length)), t.push(n); + else if ((n = this.tokenizer.tag(e))) (e = e.substring(n.raw.length)), (s = t[t.length - 1]), s && "text" === n.type && "text" === s.type ? ((s.raw += n.raw), (s.text += n.text)) : t.push(n); + else if ((n = this.tokenizer.link(e))) (e = e.substring(n.raw.length)), t.push(n); + else if ((n = this.tokenizer.reflink(e, this.tokens.links))) (e = e.substring(n.raw.length)), (s = t[t.length - 1]), s && "text" === n.type && "text" === s.type ? ((s.raw += n.raw), (s.text += n.text)) : t.push(n); + else if ((n = this.tokenizer.emStrong(e, a, o))) (e = e.substring(n.raw.length)), t.push(n); + else if ((n = this.tokenizer.codespan(e))) (e = e.substring(n.raw.length)), t.push(n); + else if ((n = this.tokenizer.br(e))) (e = e.substring(n.raw.length)), t.push(n); + else if ((n = this.tokenizer.del(e))) (e = e.substring(n.raw.length)), t.push(n); + else if ((n = this.tokenizer.autolink(e))) (e = e.substring(n.raw.length)), t.push(n); + else if (this.state.inLink || !(n = this.tokenizer.url(e))) { + if (((r = e), this.options.extensions && this.options.extensions.startInline)) { + let t = 1 / 0; + const n = e.slice(1); + let s; + this.options.extensions.startInline.forEach((e) => { + (s = e.call({ lexer: this }, n)), "number" == typeof s && s >= 0 && (t = Math.min(t, s)); + }), + t < 1 / 0 && t >= 0 && (r = e.substring(0, t + 1)); + } + if ((n = this.tokenizer.inlineText(r))) + (e = e.substring(n.raw.length)), "_" !== n.raw.slice(-1) && (o = n.raw.slice(-1)), (l = !0), (s = t[t.length - 1]), s && "text" === s.type ? ((s.raw += n.raw), (s.text += n.text)) : t.push(n); + else if (e) { + const t = "Infinite loop on byte: " + e.charCodeAt(0); + if (this.options.silent) { + console.error(t); + break; + } + throw new Error(t); + } + } else (e = e.substring(n.raw.length)), t.push(n); + return t; + } + } + class se { + options; + constructor(t) { + this.options = t || e.defaults; + } + code(e, t, n) { + const s = (t || "").match(/^\S*/)?.[0]; + return (e = e.replace(/\n$/, "") + "\n"), s ? '
    ' + (n ? e : c(e, !0)) + "
    \n" : "
    " + (n ? e : c(e, !0)) + "
    \n"; + } + blockquote(e) { + return `
    \n${e}
    \n`; + } + html(e, t) { + return e; + } + heading(e, t, n) { + return `${e}\n`; + } + hr() { + return "
    \n"; + } + list(e, t, n) { + const s = t ? "ol" : "ul"; + return "<" + s + (t && 1 !== n ? ' start="' + n + '"' : "") + ">\n" + e + "\n"; + } + listitem(e, t, n) { + return `
  • ${e}
  • \n`; + } + checkbox(e) { + return "'; + } + paragraph(e) { + return `

    ${e}

    \n`; + } + table(e, t) { + return t && (t = `${t}`), "\n\n" + e + "\n" + t + "
    \n"; + } + tablerow(e) { + return `\n${e}\n`; + } + tablecell(e, t) { + const n = t.header ? "th" : "td"; + return (t.align ? `<${n} align="${t.align}">` : `<${n}>`) + e + `\n`; + } + strong(e) { + return `${e}`; + } + em(e) { + return `${e}`; + } + codespan(e) { + return `${e}`; + } + br() { + return "
    "; + } + del(e) { + return `${e}`; + } + link(e, t, n) { + const s = g(e); + if (null === s) return n; + let r = '
    "), r; + } + image(e, t, n) { + const s = g(e); + if (null === s) return n; + let r = `${n} 0 && "paragraph" === n.tokens[0].type + ? ((n.tokens[0].text = e + " " + n.tokens[0].text), + n.tokens[0].tokens && n.tokens[0].tokens.length > 0 && "text" === n.tokens[0].tokens[0].type && (n.tokens[0].tokens[0].text = e + " " + n.tokens[0].tokens[0].text)) + : n.tokens.unshift({ type: "text", text: e + " " }) + : (o += e + " "); + } + (o += this.parse(n.tokens, i)), (l += this.renderer.listitem(o, r, !!s)); + } + n += this.renderer.list(l, t, s); + continue; + } + case "html": { + const e = r; + n += this.renderer.html(e.text, e.block); + continue; + } + case "paragraph": { + const e = r; + n += this.renderer.paragraph(this.parseInline(e.tokens)); + continue; + } + case "text": { + let i = r, + l = i.tokens ? this.parseInline(i.tokens) : i.text; + for (; s + 1 < e.length && "text" === e[s + 1].type; ) (i = e[++s]), (l += "\n" + (i.tokens ? this.parseInline(i.tokens) : i.text)); + n += t ? this.renderer.paragraph(l) : l; + continue; + } + default: { + const e = 'Token with "' + r.type + '" type was not found.'; + if (this.options.silent) return console.error(e), ""; + throw new Error(e); + } + } + } + return n; + } + parseInline(e, t) { + t = t || this.renderer; + let n = ""; + for (let s = 0; s < e.length; s++) { + const r = e[s]; + if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[r.type]) { + const e = this.options.extensions.renderers[r.type].call({ parser: this }, r); + if (!1 !== e || !["escape", "html", "link", "image", "strong", "em", "codespan", "br", "del", "text"].includes(r.type)) { + n += e || ""; + continue; + } + } + switch (r.type) { + case "escape": { + const e = r; + n += t.text(e.text); + break; + } + case "html": { + const e = r; + n += t.html(e.text); + break; + } + case "link": { + const e = r; + n += t.link(e.href, e.title, this.parseInline(e.tokens, t)); + break; + } + case "image": { + const e = r; + n += t.image(e.href, e.title, e.text); + break; + } + case "strong": { + const e = r; + n += t.strong(this.parseInline(e.tokens, t)); + break; + } + case "em": { + const e = r; + n += t.em(this.parseInline(e.tokens, t)); + break; + } + case "codespan": { + const e = r; + n += t.codespan(e.text); + break; + } + case "br": + n += t.br(); + break; + case "del": { + const e = r; + n += t.del(this.parseInline(e.tokens, t)); + break; + } + case "text": { + const e = r; + n += t.text(e.text); + break; + } + default: { + const e = 'Token with "' + r.type + '" type was not found.'; + if (this.options.silent) return console.error(e), ""; + throw new Error(e); + } + } + } + return n; + } + } + class le { + options; + constructor(t) { + this.options = t || e.defaults; + } + static passThroughHooks = new Set(["preprocess", "postprocess", "processAllTokens"]); + preprocess(e) { + return e; + } + postprocess(e) { + return e; + } + processAllTokens(e) { + return e; + } + } + class oe { + defaults = { async: !1, breaks: !1, extensions: null, gfm: !0, hooks: null, pedantic: !1, renderer: null, silent: !1, tokenizer: null, walkTokens: null }; + options = this.setOptions; + parse = this.#e(ne.lex, ie.parse); + parseInline = this.#e(ne.lexInline, ie.parseInline); + Parser = ie; + Renderer = se; + TextRenderer = re; + Lexer = ne; + Tokenizer = w; + Hooks = le; + constructor(...e) { + this.use(...e); + } + walkTokens(e, t) { + let n = []; + for (const s of e) + switch (((n = n.concat(t.call(this, s))), s.type)) { + case "table": { + const e = s; + for (const s of e.header) n = n.concat(this.walkTokens(s.tokens, t)); + for (const s of e.rows) for (const e of s) n = n.concat(this.walkTokens(e.tokens, t)); + break; + } + case "list": { + const e = s; + n = n.concat(this.walkTokens(e.items, t)); + break; + } + default: { + const e = s; + this.defaults.extensions?.childTokens?.[e.type] + ? this.defaults.extensions.childTokens[e.type].forEach((s) => { + const r = e[s].flat(1 / 0); + n = n.concat(this.walkTokens(r, t)); + }) + : e.tokens && (n = n.concat(this.walkTokens(e.tokens, t))); + } + } + return n; + } + use(...e) { + const t = this.defaults.extensions || { renderers: {}, childTokens: {} }; + return ( + e.forEach((e) => { + const n = { ...e }; + if ( + ((n.async = this.defaults.async || n.async || !1), + e.extensions && + (e.extensions.forEach((e) => { + if (!e.name) throw new Error("extension name required"); + if ("renderer" in e) { + const n = t.renderers[e.name]; + t.renderers[e.name] = n + ? function (...t) { + let s = e.renderer.apply(this, t); + return !1 === s && (s = n.apply(this, t)), s; + } + : e.renderer; + } + if ("tokenizer" in e) { + if (!e.level || ("block" !== e.level && "inline" !== e.level)) throw new Error("extension level must be 'block' or 'inline'"); + const n = t[e.level]; + n ? n.unshift(e.tokenizer) : (t[e.level] = [e.tokenizer]), + e.start && + ("block" === e.level + ? t.startBlock + ? t.startBlock.push(e.start) + : (t.startBlock = [e.start]) + : "inline" === e.level && (t.startInline ? t.startInline.push(e.start) : (t.startInline = [e.start]))); + } + "childTokens" in e && e.childTokens && (t.childTokens[e.name] = e.childTokens); + }), + (n.extensions = t)), + e.renderer) + ) { + const t = this.defaults.renderer || new se(this.defaults); + for (const n in e.renderer) { + if (!(n in t)) throw new Error(`renderer '${n}' does not exist`); + if ("options" === n) continue; + const s = n, + r = e.renderer[s], + i = t[s]; + t[s] = (...e) => { + let n = r.apply(t, e); + return !1 === n && (n = i.apply(t, e)), n || ""; + }; + } + n.renderer = t; + } + if (e.tokenizer) { + const t = this.defaults.tokenizer || new w(this.defaults); + for (const n in e.tokenizer) { + if (!(n in t)) throw new Error(`tokenizer '${n}' does not exist`); + if (["options", "rules", "lexer"].includes(n)) continue; + const s = n, + r = e.tokenizer[s], + i = t[s]; + t[s] = (...e) => { + let n = r.apply(t, e); + return !1 === n && (n = i.apply(t, e)), n; + }; + } + n.tokenizer = t; + } + if (e.hooks) { + const t = this.defaults.hooks || new le(); + for (const n in e.hooks) { + if (!(n in t)) throw new Error(`hook '${n}' does not exist`); + if ("options" === n) continue; + const s = n, + r = e.hooks[s], + i = t[s]; + le.passThroughHooks.has(n) + ? (t[s] = (e) => { + if (this.defaults.async) return Promise.resolve(r.call(t, e)).then((e) => i.call(t, e)); + const n = r.call(t, e); + return i.call(t, n); + }) + : (t[s] = (...e) => { + let n = r.apply(t, e); + return !1 === n && (n = i.apply(t, e)), n; + }); + } + n.hooks = t; + } + if (e.walkTokens) { + const t = this.defaults.walkTokens, + s = e.walkTokens; + n.walkTokens = function (e) { + let n = []; + return n.push(s.call(this, e)), t && (n = n.concat(t.call(this, e))), n; + }; + } + this.defaults = { ...this.defaults, ...n }; + }), + this + ); + } + setOptions(e) { + return (this.defaults = { ...this.defaults, ...e }), this; + } + lexer(e, t) { + return ne.lex(e, t ?? this.defaults); + } + parser(e, t) { + return ie.parse(e, t ?? this.defaults); + } + #e(e, t) { + return (n, s) => { + const r = { ...s }, + i = { ...this.defaults, ...r }; + !0 === this.defaults.async && !1 === r.async && (i.silent || console.warn("marked(): The async option was set to true by an extension. The async: false option sent to parse will be ignored."), (i.async = !0)); + const l = this.#t(!!i.silent, !!i.async); + if (null == n) return l(new Error("marked(): input parameter is undefined or null")); + if ("string" != typeof n) return l(new Error("marked(): input parameter is of type " + Object.prototype.toString.call(n) + ", string expected")); + if ((i.hooks && (i.hooks.options = i), i.async)) + return Promise.resolve(i.hooks ? i.hooks.preprocess(n) : n) + .then((t) => e(t, i)) + .then((e) => (i.hooks ? i.hooks.processAllTokens(e) : e)) + .then((e) => (i.walkTokens ? Promise.all(this.walkTokens(e, i.walkTokens)).then(() => e) : e)) + .then((e) => t(e, i)) + .then((e) => (i.hooks ? i.hooks.postprocess(e) : e)) + .catch(l); + try { + i.hooks && (n = i.hooks.preprocess(n)); + let s = e(n, i); + i.hooks && (s = i.hooks.processAllTokens(s)), i.walkTokens && this.walkTokens(s, i.walkTokens); + let r = t(s, i); + return i.hooks && (r = i.hooks.postprocess(r)), r; + } catch (e) { + return l(e); + } + }; + } + #t(e, t) { + return (n) => { + if (((n.message += "\nPlease report this to https://github.com/markedjs/marked."), e)) { + const e = "

    An error occurred:

    " + c(n.message + "", !0) + "
    "; + return t ? Promise.resolve(e) : e; + } + if (t) return Promise.reject(n); + throw n; + }; + } + } + const ae = new oe(); + function ce(e, t) { + return ae.parse(e, t); + } + (ce.options = ce.setOptions = function (e) { + return ae.setOptions(e), (ce.defaults = ae.defaults), n(ce.defaults), ce; + }), + (ce.getDefaults = t), + (ce.defaults = e.defaults), + (ce.use = function (...e) { + return ae.use(...e), (ce.defaults = ae.defaults), n(ce.defaults), ce; + }), + (ce.walkTokens = function (e, t) { + return ae.walkTokens(e, t); + }), + (ce.parseInline = ae.parseInline), + (ce.Parser = ie), + (ce.parser = ie.parse), + (ce.Renderer = se), + (ce.TextRenderer = re), + (ce.Lexer = ne), + (ce.lexer = ne.lex), + (ce.Tokenizer = w), + (ce.Hooks = le), + (ce.parse = ce); + const he = ce.options, + pe = ce.setOptions, + ue = ce.use, + ke = ce.walkTokens, + ge = ce.parseInline, + fe = ce, + de = ie.parse, + xe = ne.lex; + (e.Hooks = le), + (e.Lexer = ne), + (e.Marked = oe), + (e.Parser = ie), + (e.Renderer = se), + (e.TextRenderer = re), + (e.Tokenizer = w), + (e.getDefaults = t), + (e.lexer = xe), + (e.marked = ce), + (e.options = he), + (e.parse = fe), + (e.parseInline = ge), + (e.parser = de), + (e.setOptions = pe), + (e.use = ue), + (e.walkTokens = ke); +}); diff --git a/website/static/js/signup.js b/website/static/js/signup.js index e2a0221..2474ffb 100644 --- a/website/static/js/signup.js +++ b/website/static/js/signup.js @@ -1,10 +1,10 @@ if (localStorage.getItem("DONOTSHARE-secretkey") !== null) { - window.location.replace("../app/index.html") + window.location.replace("/app/") document.body.innerHTML = "Redirecting..." throw new Error(); } if (localStorage.getItem("DONOTSHARE-password") !== null) { - window.location.replace("../app/index.html") + window.location.replace("/app/") document.body.innerHTML = "Redirecting..." throw new Error(); } @@ -37,12 +37,12 @@ document.addEventListener('DOMContentLoaded', function() { document.getElementById("homeserver").innerText = "Your homeserver is: " + remote + ". " }); -signupButton.addEventListener("click", (event) => { +signupButton.addEventListener("click", () => { async function doStuff() { let username = usernameBox.value let password = passwordBox.value - if (username == "") { + if (username === "") { statusBox.innerText = "A username is required!" return } @@ -50,7 +50,7 @@ signupButton.addEventListener("click", (event) => { statusBox.innerText = "Username cannot be more than 20 characters!" return } - if (password == "") { + if (password === "") { statusBox.innerText = "A password is required!" return } @@ -68,7 +68,7 @@ signupButton.addEventListener("click", (event) => { key = await hashwasm.sha3(key) } return key - }; + } fetch(remote + "/api/signup", { @@ -86,14 +86,14 @@ signupButton.addEventListener("click", (event) => { async function doStuff() { let responseData = await response.json() - if (response.status == 200) { - statusBox.innerText == "redirecting.." + 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" + window.location.href = "/app/" } - else if (response.status == 409) { + else if (response.status === 409) { statusBox.innerText = "Username already taken!" showElements(true) } diff --git a/website/static/version.txt b/website/static/version.txt new file mode 100644 index 0000000..5a396e2 --- /dev/null +++ b/website/static/version.txt @@ -0,0 +1 @@ +121 \ No newline at end of file