1import { fetch2 } from './httpService.mjs?v=1500' 2import { formatUtcTime, parseMetaContent } from './utils.module.mjs'; 3import { resizeImages, applyImageZooming } from './post.imageutils.mjs'; 4import { renderCodeHighlighter, renderLaTeX } from './post.highlight.mjs'; 5import { renderMermaid } from './post.mermaid.mjs'; 6import { calculateReadingTime } from './post.readingtime.mjs'; 7import { recordPostView } from './postview.mjs'; 8import { error } from './toastService.mjs'; 9 10const commentEditorStylesheetId = 'comment-markdown-editor-stylesheet'; 11const commentEditorStylesheetPath = '/_content/Moonglade.Editor.StaticAssets/moonglade-editor/moonglade-editor.css'; 12const commentEditorModulePath = '/_content/Moonglade.Editor.StaticAssets/moonglade-editor/moonglade-editor.code.js'; 13let commentEditorModulePromise = null; 14let commentMarkdownEditor = null; 15let commentMarkdownEditorEnabled = false; 16 17function ensureCommentEditorStylesheet() { 18 if (document.getElementById(commentEditorStylesheetId)) { 19 return; 20 } 21 22 const link = document.createElement('link'); 23 link.id = commentEditorStylesheetId; 24 link.rel = 'stylesheet'; 25 link.href = commentEditorStylesheetPath; 26 document.head.append(link); 27} 28 29async function ensureCommentEditorModule() { 30 commentEditorModulePromise ??= import(commentEditorModulePath); 31 return await commentEditorModulePromise; 32} 33 34async function enableCommentMarkdownEditor() { 35 if (commentMarkdownEditorEnabled) { 36 return; 37 } 38 39 const editorElement = document.querySelector('#comment-markdown-editor'); 40 const textarea = document.querySelector('#input-comment-content'); 41 if (!editorElement || !textarea) { 42 return; 43 } 44 45 ensureCommentEditorStylesheet(); 46 const { createMoongladeCodeEditor } = await ensureCommentEditorModule(); 47 48 textarea.removeAttribute('required'); 49 textarea.classList.add('d-none'); 50 editorElement.classList.remove('d-none'); 51 commentMarkdownEditor = createMoongladeCodeEditor({ 52 element: editorElement, 53 textarea, 54 content: textarea.value || '', 55 language: 'markdown', 56 height: '180px', 57 lineWrapping: true 58 }); 59 commentMarkdownEditorEnabled = true; 60 commentMarkdownEditor.focus(); 61} 62 63function disableCommentMarkdownEditor() { 64 const editorElement = document.querySelector('#comment-markdown-editor'); 65 const textarea = document.querySelector('#input-comment-content'); 66 67 if (commentMarkdownEditor) { 68 commentMarkdownEditor.syncToTextarea(); 69 commentMarkdownEditor.destroy(); 70 commentMarkdownEditor = null; 71 } 72 73 editorElement?.replaceChildren(); 74 editorElement?.classList.add('d-none'); 75 76 if (textarea) { 77 textarea.classList.remove('d-none'); 78 textarea.setAttribute('required', 'required'); 79 } 80 81 commentMarkdownEditorEnabled = false; 82} 83 84function resetCommentMarkdownEditor() { 85 const toggle = document.querySelector('#input-comment-use-markdown-editor'); 86 if (toggle) { 87 toggle.checked = false; 88 } 89 90 disableCommentMarkdownEditor(); 91} 92 93function syncCommentMarkdownEditor() { 94 commentMarkdownEditor?.syncToTextarea(); 95} 96 97function isCommentMarkdownEditorEnabled() { 98 return commentMarkdownEditorEnabled && !!commentMarkdownEditor; 99} 100 101async function toggleCommentMarkdownEditor(event) { 102 const checkbox = event.currentTarget; 103 checkbox.disabled = true; 104 105 try { 106 if (checkbox.checked) { 107 await enableCommentMarkdownEditor(); 108 } 109 else { 110 disableCommentMarkdownEditor(); 111 } 112 } 113 catch (err) { 114 checkbox.checked = false; 115 disableCommentMarkdownEditor(); 116 error(err); 117 } 118 finally { 119 checkbox.disabled = false; 120 } 121} 122 123async function submitComment(pid) { 124 const thxForComment = document.querySelector('#thx-for-comment'); 125 const thxForCommentNonReview = document.querySelector('#thx-for-comment-non-review'); 126 const loadingIndicator = document.querySelector('#loadingIndicator'); 127 const btnSubmitComment = document.querySelector('#btn-submit-comment'); 128 const commentForm = document.querySelector('#comment-form'); 129 const commentContentInput = document.querySelector('#input-comment-content'); 130 131 syncCommentMarkdownEditor(); 132 133 const username = document.querySelector('#input-comment-name').value; 134 const content = commentContentInput.value; 135 const email = document.querySelector('#input-comment-email').value; 136 const source = document.querySelector('#input-comment-source')?.value ?? ''; 137 const formRenderedUtc = Number(document.querySelector('#input-comment-form-rendered-utc')?.value ?? 0); 138 139 if (isCommentMarkdownEditorEnabled() && !content.trim()) { 140 commentMarkdownEditor.focus(); 141 error(commentContentInput.dataset.commentRequiredMessage || commentContentInput.placeholder); 142 return; 143 } 144 145 thxForComment.style.display = 'none'; 146 thxForCommentNonReview.style.display = 'none'; 147 loadingIndicator.style.display = 'block'; 148 btnSubmitComment.classList.add('disabled'); 149 btnSubmitComment.setAttribute('disabled', 'disabled'); 150 151 try { 152 const data = await fetch2(`/api/comment/${pid}`, 'POST', { username, content, email, source, formRenderedUtc }); 153 resetCommentMarkdownEditor(); 154 commentForm.reset(); 155 resetCommentFormRenderedAt(data.formRenderedUtc); 156 157 if (data.requireCommentReview) { 158 thxForComment.style.display = 'block'; 159 } 160 else { 161 thxForCommentNonReview.style.display = 'block'; 162 } 163 } catch (err) { 164 error(err); 165 } finally { 166 loadingIndicator.style.display = 'none'; 167 btnSubmitComment.classList.remove('disabled'); 168 btnSubmitComment.removeAttribute('disabled'); 169 } 170} 171 172function resetCommentFormRenderedAt(formRenderedUtc) { 173 const input = document.querySelector('#input-comment-form-rendered-utc'); 174 if (input && Number.isFinite(formRenderedUtc)) { 175 input.value = formRenderedUtc.toString(); 176 } 177} 178 179document.addEventListener('DOMContentLoaded', () => { 180 resizeImages('.post-content img'); 181 if (window.innerWidth >= 768) { 182 applyImageZooming('.post-content img'); 183 } 184 185 renderMermaid().catch(err => console.error(err)); 186 renderCodeHighlighter(); 187 renderLaTeX('pre.language-latex code'); 188 189 if (parseMetaContent('post-is-published')) { 190 191 let pid = document.querySelector('article').dataset.postid; 192 193 const blogContent = document.querySelector('.post-content').innerText; 194 let roundedReadingTime = calculateReadingTime(blogContent); 195 196 const elem = document.getElementById('reading-time'); 197 if (elem) { 198 elem.innerText = `Estimated Reading Time: ${roundedReadingTime} minute(s)`; 199 } 200 201 document.getElementById('comment-form')?.addEventListener('submit', function (e) { 202 e.preventDefault(); 203 submitComment(pid); 204 }); 205 document.getElementById('input-comment-use-markdown-editor')?.addEventListener('change', toggleCommentMarkdownEditor); 206 document.getElementById('btn-submit-comment')?.addEventListener('click', syncCommentMarkdownEditor); 207 208 formatUtcTime(); 209 210 recordPostView(pid); 211 } 212});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.