Version 3 #40
|
@ -17,6 +17,15 @@ open Microsoft.Extensions.Hosting
|
||||||
open NodaTime
|
open NodaTime
|
||||||
|
|
||||||
|
|
||||||
|
/// Enable buffering on the request body
|
||||||
|
type BufferedBodyMiddleware (next : RequestDelegate) =
|
||||||
|
|
||||||
|
member _.InvokeAsync (ctx : HttpContext) = task {
|
||||||
|
ctx.Request.EnableBuffering ()
|
||||||
|
return! next.Invoke ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[<EntryPoint>]
|
[<EntryPoint>]
|
||||||
let main args =
|
let main args =
|
||||||
|
|
||||||
|
@ -54,6 +63,7 @@ let main args =
|
||||||
let _ = app.UseCookiePolicy (CookiePolicyOptions (MinimumSameSitePolicy = SameSiteMode.Strict))
|
let _ = app.UseCookiePolicy (CookiePolicyOptions (MinimumSameSitePolicy = SameSiteMode.Strict))
|
||||||
let _ = app.UseStaticFiles ()
|
let _ = app.UseStaticFiles ()
|
||||||
let _ = app.UseRouting ()
|
let _ = app.UseRouting ()
|
||||||
|
let _ = app.UseMiddleware<BufferedBodyMiddleware> ()
|
||||||
let _ = app.UseAuthentication ()
|
let _ = app.UseAuthentication ()
|
||||||
let _ = app.UseAuthorization ()
|
let _ = app.UseAuthorization ()
|
||||||
let _ = app.UseSession ()
|
let _ = app.UseSession ()
|
||||||
|
|
|
@ -226,9 +226,10 @@ module Api =
|
||||||
|
|
||||||
// POST: /api/markdown-preview
|
// POST: /api/markdown-preview
|
||||||
let markdownPreview : HttpHandler = requireUser >=> fun next ctx -> task {
|
let markdownPreview : HttpHandler = requireUser >=> fun next ctx -> task {
|
||||||
|
let _ = ctx.Request.Body.Seek(0L, SeekOrigin.Begin)
|
||||||
use reader = new StreamReader (ctx.Request.Body)
|
use reader = new StreamReader (ctx.Request.Body)
|
||||||
let! preview = reader.ReadToEndAsync ()
|
let! preview = reader.ReadToEndAsync ()
|
||||||
return! htmlString (MarkdownString.toHtml (Text (defaultArg (Option.ofObj preview) "--"))) next ctx
|
return! htmlString (MarkdownString.toHtml (Text preview)) next ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -60,11 +60,7 @@ module EditProfileViewModel =
|
||||||
FullTime = false
|
FullTime = false
|
||||||
Biography = ""
|
Biography = ""
|
||||||
Experience = None
|
Experience = None
|
||||||
Skills = [|
|
Skills = [||]
|
||||||
{ Id = "1"; Description = "test 1"; Notes = None }
|
|
||||||
{ Id = "3"; Description = "test 2"; Notes = Some "noted" }
|
|
||||||
{ Id = "4"; Description = "asfasdfa"; Notes = None }
|
|
||||||
|]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an instance of this form from the given profile
|
/// Create an instance of this form from the given profile
|
||||||
|
|
|
@ -37,12 +37,13 @@ let markdownEditor attrs name value editorLabel =
|
||||||
rawText "Markdown"
|
rawText "Markdown"
|
||||||
]
|
]
|
||||||
rawText " "
|
rawText " "
|
||||||
button [ _type "button"; _id $"{name}PreviewButton"; _class "btn btn-outline-secondary btn-sm rounded-pill"
|
button [ _type "button"; _id $"{name}PreviewButton"
|
||||||
_onclick $"jjj.showPreview('{name}')" ] [
|
_class "btn btn-outline-secondary btn-sm rounded-pill" ] [
|
||||||
rawText "Preview"
|
rawText "Preview"
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
section [ _id $"{name}Preview"; _class "jjj-not-shown"; _ariaLabel "Rendered Markdown preview" ] []
|
section [ _id $"{name}Preview"; _class "jjj-not-shown jjj-markdown-preview px-2 pt-2"
|
||||||
|
_ariaLabel "Rendered Markdown preview" ] []
|
||||||
div [ _id $"{name}Edit"; _class "form-floating jjj-shown" ] [
|
div [ _id $"{name}Edit"; _class "form-floating jjj-shown" ] [
|
||||||
textarea (List.append attrs
|
textarea (List.append attrs
|
||||||
[ _id name; _name name; _class "form-control jjj-markdown-editor"; _rows "10" ]) [
|
[ _id name; _name name; _class "form-control jjj-markdown-editor"; _rows "10" ]) [
|
||||||
|
@ -50,4 +51,9 @@ let markdownEditor attrs name value editorLabel =
|
||||||
]
|
]
|
||||||
label [ _for name ] [ rawText editorLabel ]
|
label [ _for name ] [ rawText editorLabel ]
|
||||||
]
|
]
|
||||||
|
script [] [
|
||||||
|
rawText """document.addEventListener("DOMContentLoaded", function () {"""
|
||||||
|
rawText $" jjj.markdownOnLoad('{name}') "
|
||||||
|
rawText "})"
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
|
|
@ -53,6 +53,14 @@ this.jjj = {
|
||||||
} catch (_) { }
|
} catch (_) { }
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up the onClick event for the preview button
|
||||||
|
* @param {string} editorId The ID of the editor to wire up
|
||||||
|
*/
|
||||||
|
markdownOnLoad(editorId) {
|
||||||
|
document.getElementById(`${editorId}PreviewButton`).addEventListener("click", () => { this.showPreview(editorId) })
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a preview of the Markdown in the given editor
|
* Show a preview of the Markdown in the given editor
|
||||||
* @param {string} editorId The ID of the Markdown editor whose preview should be shown
|
* @param {string} editorId The ID of the Markdown editor whose preview should be shown
|
||||||
|
@ -62,6 +70,8 @@ this.jjj = {
|
||||||
const editBtn = document.getElementById(`${editorId}EditButton`)
|
const editBtn = document.getElementById(`${editorId}EditButton`)
|
||||||
/** @type {HTMLDivElement} */
|
/** @type {HTMLDivElement} */
|
||||||
const editDiv = document.getElementById(`${editorId}Edit`)
|
const editDiv = document.getElementById(`${editorId}Edit`)
|
||||||
|
/** @type {HTMLTextAreaElement} */
|
||||||
|
const editor = document.getElementById(editorId)
|
||||||
/** @type {HTMLButtonElement} */
|
/** @type {HTMLButtonElement} */
|
||||||
const previewBtn = document.getElementById(`${editorId}PreviewButton`)
|
const previewBtn = document.getElementById(`${editorId}PreviewButton`)
|
||||||
/** @type {HTMLDivElement} */
|
/** @type {HTMLDivElement} */
|
||||||
|
@ -69,20 +79,12 @@ this.jjj = {
|
||||||
|
|
||||||
editBtn.classList.remove("btn-primary")
|
editBtn.classList.remove("btn-primary")
|
||||||
editBtn.classList.add("btn-outline-secondary")
|
editBtn.classList.add("btn-outline-secondary")
|
||||||
editBtn.addAttribute("onclick", `jjj.showEditor('{editorId}')`)
|
editBtn.addEventListener("click", () => { this.showEditor(editorId) })
|
||||||
previewBtn.classList.remove("btn-outline-secondary")
|
previewBtn.classList.remove("btn-outline-secondary")
|
||||||
previewBtn.classList.add("btn-primary")
|
previewBtn.classList.add("btn-primary")
|
||||||
previewBtn.removeAttribute("onclick")
|
previewBtn.removeEventListener("click", () => { this.showPreview(editorId) })
|
||||||
|
|
||||||
editDiv.classList.remove("jjj-shown")
|
|
||||||
editDiv.classList.add("jjj-not-shown")
|
|
||||||
previewDiv.innerHTML = "<p><strong><em>Loading preview...</em></strong></p>"
|
|
||||||
previewtDiv.classList.remove("jjj-not-shown")
|
|
||||||
previewDiv.classList.add("jjj-shown")
|
|
||||||
|
|
||||||
const preview = await fetch("/api/markdown-preview",
|
|
||||||
{ method: "POST", body: document.getElementById(editorId).textContent })
|
|
||||||
|
|
||||||
|
const preview = await fetch("/api/markdown-preview", { method: "POST", body: editor.value })
|
||||||
let text
|
let text
|
||||||
if (preview.ok) {
|
if (preview.ok) {
|
||||||
text = await preview.text()
|
text = await preview.text()
|
||||||
|
@ -90,6 +92,12 @@ this.jjj = {
|
||||||
text = `<p class="text-danger"><strong> ERROR ${preview.status}</strong> – ${preview.statusText}`
|
text = `<p class="text-danger"><strong> ERROR ${preview.status}</strong> – ${preview.statusText}`
|
||||||
}
|
}
|
||||||
previewDiv.innerHTML = text
|
previewDiv.innerHTML = text
|
||||||
|
|
||||||
|
editDiv.classList.remove("jjj-shown")
|
||||||
|
editDiv.classList.add("jjj-not-shown")
|
||||||
|
previewDiv.classList.remove("jjj-not-shown")
|
||||||
|
previewDiv.classList.add("jjj-shown")
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,17 +109,19 @@ this.jjj = {
|
||||||
const editBtn = document.getElementById(`${editorId}EditButton`)
|
const editBtn = document.getElementById(`${editorId}EditButton`)
|
||||||
/** @type {HTMLDivElement} */
|
/** @type {HTMLDivElement} */
|
||||||
const editDiv = document.getElementById(`${editorId}Edit`)
|
const editDiv = document.getElementById(`${editorId}Edit`)
|
||||||
|
/** @type {HTMLTextAreaElement} */
|
||||||
|
const editor = document.getElementById(editorId)
|
||||||
/** @type {HTMLButtonElement} */
|
/** @type {HTMLButtonElement} */
|
||||||
const previewBtn = document.getElementById(`${editorId}PreviewButton`)
|
const previewBtn = document.getElementById(`${editorId}PreviewButton`)
|
||||||
/** @type {HTMLDivElement} */
|
/** @type {HTMLDivElement} */
|
||||||
const previewDiv = document.getElementById(`${editorId}Preview`)
|
const previewDiv = document.getElementById(`${editorId}Preview`)
|
||||||
|
|
||||||
previewtBtn.classList.remove("btn-primary")
|
previewBtn.classList.remove("btn-primary")
|
||||||
previewBtn.classList.add("btn-outline-secondary")
|
previewBtn.classList.add("btn-outline-secondary")
|
||||||
previewtBtn.addAttribute("onclick", `jjj.showPreview('{editorId}')`)
|
this.markdownOnLoad(editorId)
|
||||||
editBtn.classList.remove("btn-outline-secondary")
|
editBtn.classList.remove("btn-outline-secondary")
|
||||||
editBtn.classList.add("btn-primary")
|
editBtn.classList.add("btn-primary")
|
||||||
editBtn.removeAttribute("onclick")
|
editBtn.removeEventListener("click", () => { this.showEditor(editorId) })
|
||||||
|
|
||||||
previewDiv.classList.remove("jjj-shown")
|
previewDiv.classList.remove("jjj-shown")
|
||||||
previewDiv.classList.add("jjj-not-shown")
|
previewDiv.classList.add("jjj-not-shown")
|
||||||
|
|
|
@ -212,6 +212,10 @@ span.jjj-audio-clip:hover {
|
||||||
would be; this overrides that for this textarea specifically */
|
would be; this overrides that for this textarea specifically */
|
||||||
height: inherit !important;
|
height: inherit !important;
|
||||||
}
|
}
|
||||||
|
.jjj-markdown-preview {
|
||||||
|
border: solid 1px lightgray;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
/* Footer styling */
|
/* Footer styling */
|
||||||
footer {
|
footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user