..
This commit is contained in:
parent
00bba0bc39
commit
5ac3b05660
@ -171,50 +171,43 @@ class BlogController(
|
||||
* [수정됨] 이미지 URL을 새로운 API 경로인 /api/images/ 로 생성합니다.
|
||||
*/
|
||||
private fun processPostForView(post: Post): Post {
|
||||
// 1. URL 디코딩: 인코딩되어 저장된 모든 텍스트 필드를 디코딩합니다.
|
||||
// ?.let {} 구문을 사용하여 null-safe하게 처리합니다.
|
||||
post.title = post.title?.let { URLDecoder.decode(it, "UTF-8") } ?: ""
|
||||
post.content = post.content?.let { URLDecoder.decode(it, "UTF-8") } ?: ""
|
||||
post.tags = post.tags?.let { URLDecoder.decode(it, "UTF-8") } ?: ""
|
||||
post.category = post.category?.let { URLDecoder.decode(it, "UTF-8") } ?: "none"
|
||||
post.firstAddress = post.firstAddress?.let { URLDecoder.decode(it, "UTF-8") } ?: ""
|
||||
post.modifyAddress = post.modifyAddress?.let { URLDecoder.decode(it, "UTF-8") } ?: ""
|
||||
// [수정] 모든 URLDecoder 호출을 제거합니다. 데이터는 이미 순수 텍스트입니다.
|
||||
post.title = post.title ?: ""
|
||||
post.content = post.content ?: ""
|
||||
post.tags = post.tags ?: ""
|
||||
post.category = if (post.category.isNullOrBlank()) "none" else post.category
|
||||
post.firstAddress = post.firstAddress ?: ""
|
||||
post.modifyAddress = post.modifyAddress ?: ""
|
||||
|
||||
// 2. 기본값 설정: 제목이 비어있을 경우, 작성 시간을 기반으로 기본 제목을 생성합니다.
|
||||
if (post.title.isNullOrBlank()) {
|
||||
// 제목이 비어있을 경우 기본값 설정
|
||||
if (post.title!!.isBlank()) {
|
||||
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm")
|
||||
post.title = "무제(無題) [${sdf.format(Date(post.writeTime))}]"
|
||||
}
|
||||
|
||||
var firstImgSrc: String? = null
|
||||
val defaultThumb = "/images/pic01.jpg" // 기본 썸네IL 경로를 상수로 정의
|
||||
val defaultThumb = "/images/pic01.jpg"
|
||||
|
||||
try {
|
||||
// 3. 콘텐츠 처리 (Delta or HTML)
|
||||
// Delta(JSON) 형식인지 먼저 시도
|
||||
// Delta 형식인지 먼저 시도
|
||||
JsonParser.parseString(post.content)
|
||||
val (text, firstImg) = extractFromDelta(post.content!!)
|
||||
post.html = text // Jsoup.parse() 대신 Delta에서 추출한 순수 텍스트를 사용
|
||||
post.html = text
|
||||
firstImgSrc = firstImg
|
||||
} catch (e: Exception) {
|
||||
// JSON 파싱 실패 시 일반 HTML로 간주
|
||||
val doc = Jsoup.parse(post.content)
|
||||
post.html = doc.text() // HTML 태그를 제외한 순수 텍스트만 요약으로 저장
|
||||
post.html = doc.text()
|
||||
firstImgSrc = doc.select("img").first()?.attr("src")
|
||||
}
|
||||
|
||||
// 4. 이미지 및 썸네일 경로 설정 (로직 중앙화)
|
||||
if (!firstImgSrc.isNullOrBlank()) {
|
||||
val filename = firstImgSrc.substringAfterLast("/")
|
||||
// 원본 이미지 URL 경로 설정
|
||||
post.image = "/api/images/$filename"
|
||||
|
||||
// 썸네일 생성 및 경로 설정
|
||||
generateThumbnail(filename, 200) // 너비 200px 썸네일 생성
|
||||
generateThumbnail(filename, 200)
|
||||
val thumbFilename = filename.substringBeforeLast(".") + "_thumbnail." + filename.substringAfterLast(".")
|
||||
post.thumb = "/api/images/$thumbFilename?type=thumbnail"
|
||||
} else {
|
||||
// 게시물에 이미지가 없는 경우, 기본 썸네일을 지정합니다.
|
||||
post.image = null
|
||||
post.thumb = defaultThumb
|
||||
}
|
||||
@ -580,13 +573,22 @@ class BlogController(
|
||||
logService.log("User ${userDetails.username} not authorized to edit post $postId")
|
||||
return ResultMV("redirect:/blog/posts")
|
||||
}
|
||||
val decodedContent = URLDecoder.decode(rawPost.content, "UTF-8")
|
||||
logService.log("$postId ${decodedContent}")
|
||||
if (decodedContent.contains("/blog/post/images/")) {
|
||||
rawPost.content = decodedContent.replace("/blog/post/images/", "/api/images/")
|
||||
// content가 변경되었으므로 다시 인코딩해서 저장
|
||||
rawPost.content = URLEncoder.encode(rawPost.content, "UTF-8")
|
||||
// ======================= ▼▼▼ 수정된 로직 시작 ▼▼▼ =======================
|
||||
var processedContent: String
|
||||
try {
|
||||
// 1. URL 디코딩을 시도합니다.
|
||||
processedContent = URLDecoder.decode(rawPost.content, "UTF-8")
|
||||
} catch (e: Exception) {
|
||||
// 2. 디코딩에 실패하면 (예: 이미 디코딩된 상태이거나 인코딩되지 않은 데이터), 원본 내용을 그대로 사용합니다.
|
||||
processedContent = rawPost.content ?: ""
|
||||
logService.log("URL decoding failed for post $postId, using raw content. Error: ${e.message}")
|
||||
}
|
||||
|
||||
// 3. Post 객체의 content 필드를 안전하게 처리된 내용으로 업데이트하여 뷰에 전달합니다.
|
||||
// (이후 processPostForView에서 추가 처리됩니다.)
|
||||
rawPost.content = processedContent
|
||||
// ======================= ▲▲▲ 수정된 로직 종료 ▲▲▲ =======================
|
||||
|
||||
// ====================================================================
|
||||
|
||||
|
||||
|
||||
BIN
src/main/resources/static/css/images/bum.png
Normal file
BIN
src/main/resources/static/css/images/bum.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
@ -4182,3 +4182,32 @@ a.btn_layerClose:hover {
|
||||
.image-preview-item.is-hidden .toggle-visibility-btn {
|
||||
background: #28a745; /* 숨겨진 아이템을 다시 보이게 하는 버튼은 초록색으로 표시 */
|
||||
}
|
||||
|
||||
.ql-toolbar.ql-snow.sticky {
|
||||
position: fixed; /* 화면을 기준으로 위치 고정 */
|
||||
top: 44px; /* 화면 상단에서 44px 떨어진 곳에 위치 (모바일 상단바 높이 고려) */
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%; /* 화면 너비 전체를 차지 */
|
||||
z-index: 999; /* 다른 요소들보다 위에 표시되도록 z-index 설정 */
|
||||
background: #f7f7f7; /* 배경색을 지정해야 아래 내용이 비치지 않음 */
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1); /* 떠 있는 효과를 위한 그림자 */
|
||||
|
||||
/* [중요] 툴바가 화면 너비를 꽉 채우므로, container 너비에 맞게 내부 컨텐츠를 제한합니다. */
|
||||
/* 기존 .container 스타일과 유사하게 설정 */
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
box-sizing: border-box; /* 패딩이 너비에 포함되도록 설정 */
|
||||
}
|
||||
|
||||
/* 2. 툴바가 고정되었을 때, 에디터 본문이 툴바 뒤로 숨지 않도록 상단에 여백(padding)을 추가 */
|
||||
.ql-container.ql-snow.has-sticky-toolbar {
|
||||
padding-top: 60px; /* 툴바 높이(약 42px) + 약간의 여유 공간 */
|
||||
}
|
||||
|
||||
/* 모바일 화면에서는 상단바(titleBar)가 없으므로 top: 0 으로 조정 */
|
||||
@media screen and (min-width: 841px) {
|
||||
.ql-toolbar.ql-snow.sticky {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
BIN
src/main/resources/static/images/bum.png
Normal file
BIN
src/main/resources/static/images/bum.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
@ -243,6 +243,7 @@ window.addEventListener('DOMContentLoaded', () => {
|
||||
/* --- Quill 에디터 초기화 관련 함수들 --- */
|
||||
/* ============================================= */
|
||||
|
||||
|
||||
/**
|
||||
* [핵심] Quill 에디터를 초기화하는 메인 함수입니다.
|
||||
* (이 함수는 바닐라 JS와 Quill API로만 작성되어 수정이 필요 없습니다.)
|
||||
@ -256,8 +257,8 @@ function initEditor(useEditor = false) {
|
||||
// serverData (includes.html에 정의됨)에서 baseData (JS 내부 변수)로 모든 값을 복사합니다.
|
||||
if (typeof serverData !== 'undefined') {
|
||||
baseData.id = serverData.id;
|
||||
baseData.title = decodeURIComponent(serverData.title || '');
|
||||
baseData.content = decodeURIComponent(serverData.content || '');
|
||||
baseData.title = serverData.title || '';
|
||||
baseData.content = serverData.content || '';
|
||||
baseData.category = serverData.category;
|
||||
baseData.tags = serverData.tags;
|
||||
baseData.firstPostLat = serverData.firstPostLat;
|
||||
@ -293,15 +294,46 @@ function initEditor(useEditor = false) {
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
const ImageBlot = Quill.import('formats/image');
|
||||
|
||||
class StyledImage extends ImageBlot {
|
||||
static formats(domNode) {
|
||||
// 기존 속성(alt, height, width) 외에 style 속성을 추가로 허용합니다.
|
||||
const formats = super.formats(domNode);
|
||||
if (domNode.hasAttribute('style')) {
|
||||
formats.style = domNode.getAttribute('style');
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
|
||||
format(name, value) {
|
||||
if (name === 'style') {
|
||||
if (value) {
|
||||
this.domNode.setAttribute(name, value);
|
||||
} else {
|
||||
this.domNode.removeAttribute(name);
|
||||
}
|
||||
} else {
|
||||
super.format(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Quill 에디터 옵션 및 모듈 설정 (편집/읽기 모드 전환)
|
||||
var Font = Quill.import('formats/font');
|
||||
Font.whitelist = ['sans-serif', 'serif', 'monospace', 'arial', 'georgia', 'comic-sans-ms', 'courier-new', 'roboto', 'playfair-display'];
|
||||
|
||||
Font.whitelist = ['monospace','sans-serif', 'serif', 'arial', 'georgia', 'comic-sans-ms', 'courier-new', 'roboto', 'playfair-display'];
|
||||
Quill.register(Font, true);
|
||||
Quill.register({ 'modules/table-better': QuillTableBetter }, true);
|
||||
|
||||
// Quill.register({'modules/imageResize': ImageResize}, true);
|
||||
Quill.register('modules/imageResize', QuillResizeModule);
|
||||
Quill.register(StyledImage, true);
|
||||
const quillOptions = {
|
||||
theme: 'snow',
|
||||
modules: useEditor ? {
|
||||
imageResize: {
|
||||
displaySize: true
|
||||
},
|
||||
toolbar: {
|
||||
container: [
|
||||
[{ font: Font.whitelist }], [{ 'size': ['small', false, 'large', 'huge'] }],
|
||||
@ -316,7 +348,9 @@ function initEditor(useEditor = false) {
|
||||
},
|
||||
'table-better': { language: 'en_US', toolbarTable: true },
|
||||
keyboard: { bindings: QuillTableBetter.keyboardBindings }
|
||||
} : {
|
||||
} : {imageResize: {
|
||||
displaySize: true
|
||||
},
|
||||
toolbar: false // 읽기 모드(useEditor: false)일 경우 툴바 숨김
|
||||
},
|
||||
readOnly: !useEditor // 읽기 전용 모드 설정
|
||||
@ -324,10 +358,48 @@ function initEditor(useEditor = false) {
|
||||
|
||||
quill = new Quill(editorContainer, quillOptions); // Quill 인스턴스 생성
|
||||
|
||||
if (useEditor) { // 편집 모드일 때만 스티키 로직을 활성화합니다.
|
||||
const toolbar = document.querySelector('.ql-toolbar');
|
||||
const editorTop = editorContainer.offsetTop;
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
if (window.scrollY > editorTop) {
|
||||
toolbar.classList.add('sticky');
|
||||
editorContainer.classList.add('has-sticky-toolbar');
|
||||
} else {
|
||||
toolbar.classList.remove('sticky');
|
||||
editorContainer.classList.remove('has-sticky-toolbar');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
quill.root.addEventListener('paste', (event) => {
|
||||
// 1. 클립보드에서 텍스트 데이터 가져오기
|
||||
let pasteText = (event.clipboardData || window.clipboardData).getData('text');
|
||||
|
||||
// 간단하게 마크다운인지 확인 (예: #, *, -, > 등의 문자로 시작하는지)
|
||||
// 좀 더 정교한 확인 로직을 추가할 수 있습니다.
|
||||
const isMarkdown = /^(#|\*|-|>|`)/.test(pasteText.trim());
|
||||
|
||||
if (isMarkdown) {
|
||||
// 2. 기본 붙여넣기 동작을 막습니다.
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
// 3. 마크다운을 HTML로 변환합니다.
|
||||
const html = marked.parse(pasteText, { gfm: true , breaks: true});
|
||||
|
||||
// 4. 현재 커서 위치에 변환된 HTML을 삽입합니다.
|
||||
const range = quill.getSelection(true);
|
||||
quill.clipboard.dangerouslyPasteHTML(range.index, html);
|
||||
}
|
||||
// 마크다운이 아니면 Quill의 기본 붙여넣기 로직이 실행됩니다.
|
||||
}, true);
|
||||
if (baseData.content) {
|
||||
loadContent(baseData.content); // DB에서 불러온 콘텐츠를 에디터에 로드
|
||||
}
|
||||
|
||||
if (useEditor) {
|
||||
quill.format('font', Font.whitelist[0], 'silent');
|
||||
}
|
||||
// 읽기 모드/편집 모드에 따라 CSS 클래스 및 제목 필드 처리
|
||||
if (!useEditor) {
|
||||
editorContainer.classList.add('readonly-mode');
|
||||
|
||||
1
src/main/resources/static/js/image-resize.min.js
vendored
Normal file
1
src/main/resources/static/js/image-resize.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -128,11 +128,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.js" defer></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.snow.css" rel="stylesheet" />
|
||||
<!-- <script src="https://unpkg.com/quill-image-resize@3.0.9/image-resize.min.js" defer></script>-->
|
||||
<!-- <script th:src="@{/js/image-resize.min.js}"></script>-->
|
||||
<script src="https://cdn.jsdelivr.net/gh/scrapooo/quill-resize-module@1.0.2/dist/quill-resize-module.js"></script>
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/quill-table-better@1/dist/quill-table-better.css" rel="stylesheet" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/quill-table-better@1/dist/quill-table-better.js"></script>
|
||||
<script>document.addEventListener('DOMContentLoaded', function() {initEditor(true)});</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/quill-table-better@1/dist/quill-table-better.js" defer></script>
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/quill-image-resize-module@3.0.0/image-resize.min.js"></script>-->
|
||||
<script defer>document.addEventListener('DOMContentLoaded', function() {initEditor(true)});</script>
|
||||
<!-- <script defer>initEditor(true);</script>-->
|
||||
</th:block>
|
||||
</body>
|
||||
</html>
|
||||
@ -42,7 +42,9 @@
|
||||
<section th:each="post , iterStat : ${Posts}">
|
||||
<div class="box post" th:id="${post.id}" onclick="goToViewer(this)" style="cursor: pointer;">
|
||||
<span class="image left">
|
||||
<img th:src="${post.thumb != null and not #strings.isEmpty(post.thumb)} ? ${apiBaseUrl + post.thumb} : @{/images/pic01.jpg}" alt="Post Thumbnail" />
|
||||
<img th:src="${post.thumb != null and not #strings.isEmpty(post.thumb)} ? ${apiBaseUrl + post.thumb} : @{/images/pic01.jpg}"
|
||||
alt="Post Thumbnail"
|
||||
th:onerror="|this.onerror=null; this.src='@{/images/bum.png}';|" />
|
||||
</span>
|
||||
|
||||
<div class="inner">
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
<section class="wrapper style1">
|
||||
<div class="container">
|
||||
<div id="license-content-container">
|
||||
<p>#lun ##Dependency License Report <em>2025-09-24 15:26:44 KST</em></p>
|
||||
<p>#lun ##Dependency License Report <em>2025-10-10 15:13:12 KST</em></p>
|
||||
<h2>Apache 2.0</h2>
|
||||
<p><strong>1</strong> <strong>Group:</strong> <code>com.google.android</code> <strong>Name:</strong> <code>annotations</code> <strong>Version:</strong> <code>4.1.1.4</code></p>
|
||||
<blockquote>
|
||||
@ -41,7 +41,7 @@
|
||||
<li><strong>POM License</strong>: Apache 2.0 - <a href="http://www.apache.org/licenses/LICENSE-2.0.txt">http://www.apache.org/licenses/LICENSE-2.0.txt</a></li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<p><strong>5</strong> <strong>Group:</strong> <code>com.google.errorprone</code> <strong>Name:</strong> <code>error_prone_annotations</code> <strong>Version:</strong> <code>2.26.1</code></p>
|
||||
<p><strong>5</strong> <strong>Group:</strong> <code>com.google.errorprone</code> <strong>Name:</strong> <code>error_prone_annotations</code> <strong>Version:</strong> <code>2.27.0</code></p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li><strong>Manifest Project URL</strong>: <a href="https://errorprone.info/error_prone_annotations">https://errorprone.info/error_prone_annotations</a></li>
|
||||
@ -766,10 +766,10 @@
|
||||
<li><strong>POM License</strong>: Apache-2.0 - <a href="https://www.apache.org/licenses/LICENSE-2.0.txt">https://www.apache.org/licenses/LICENSE-2.0.txt</a></li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<p><strong>100</strong> <strong>Group:</strong> <code>com.google.code.gson</code> <strong>Name:</strong> <code>gson</code> <strong>Version:</strong> <code>2.10.1</code></p>
|
||||
<p><strong>100</strong> <strong>Group:</strong> <code>com.google.code.gson</code> <strong>Name:</strong> <code>gson</code> <strong>Version:</strong> <code>2.11.0</code></p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li><strong>Manifest Project URL</strong>: <a href="https://github.com/google/gson/gson">https://github.com/google/gson/gson</a></li>
|
||||
<li><strong>Manifest Project URL</strong>: <a href="https://github.com/google/gson">https://github.com/google/gson</a></li>
|
||||
<li><strong>Manifest License</strong>: "Apache-2.0";link="https://www.apache.org/licenses/LICENSE-2.0.txt" (Not Packaged)</li>
|
||||
<li><strong>POM License</strong>: Apache-2.0 - <a href="https://www.apache.org/licenses/LICENSE-2.0.txt">https://www.apache.org/licenses/LICENSE-2.0.txt</a></li>
|
||||
</ul>
|
||||
@ -1695,10 +1695,11 @@
|
||||
<p><strong>219</strong> <strong>Group:</strong> <code>com.fasterxml.jackson</code> <strong>Name:</strong> <code>jackson-bom</code> <strong>Version:</strong> <code>2.17.2</code></p>
|
||||
<p><strong>220</strong> <strong>Group:</strong> <code>com.squareup.okio</code> <strong>Name:</strong> <code>okio</code> <strong>Version:</strong> <code>3.6.0</code></p>
|
||||
<p><strong>221</strong> <strong>Group:</strong> <code>org.apache.groovy</code> <strong>Name:</strong> <code>groovy-bom</code> <strong>Version:</strong> <code>4.0.23</code></p>
|
||||
<p><strong>222</strong> <strong>Group:</strong> <code>org.jetbrains.kotlin</code> <strong>Name:</strong> <code>kotlin-stdlib-common</code> <strong>Version:</strong> <code>1.9.25</code></p>
|
||||
<p><strong>223</strong> <strong>Group:</strong> <code>org.jetbrains.kotlinx</code> <strong>Name:</strong> <code>kotlinx-coroutines-bom</code> <strong>Version:</strong> <code>1.8.1</code></p>
|
||||
<p><strong>224</strong> <strong>Group:</strong> <code>org.jetbrains.kotlinx</code> <strong>Name:</strong> <code>kotlinx-coroutines-core</code> <strong>Version:</strong> <code>1.8.1</code></p>
|
||||
<p><strong>225</strong> <strong>Group:</strong> <code>org.springframework.ai</code> <strong>Name:</strong> <code>spring-ai-bom</code> <strong>Version:</strong> <code>1.0.0-M6</code></p>
|
||||
<p><strong>222</strong> <strong>Group:</strong> <code>org.jetbrains.kotlin</code> <strong>Name:</strong> <code>kotlin-bom</code> <strong>Version:</strong> <code>1.9.25</code></p>
|
||||
<p><strong>223</strong> <strong>Group:</strong> <code>org.jetbrains.kotlin</code> <strong>Name:</strong> <code>kotlin-stdlib-common</code> <strong>Version:</strong> <code>1.9.25</code></p>
|
||||
<p><strong>224</strong> <strong>Group:</strong> <code>org.jetbrains.kotlinx</code> <strong>Name:</strong> <code>kotlinx-coroutines-bom</code> <strong>Version:</strong> <code>1.8.1</code></p>
|
||||
<p><strong>225</strong> <strong>Group:</strong> <code>org.jetbrains.kotlinx</code> <strong>Name:</strong> <code>kotlinx-coroutines-core</code> <strong>Version:</strong> <code>1.8.1</code></p>
|
||||
<p><strong>226</strong> <strong>Group:</strong> <code>org.springframework.ai</code> <strong>Name:</strong> <code>spring-ai-bom</code> <strong>Version:</strong> <code>1.0.0-M6</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -24,7 +24,9 @@
|
||||
<section th:id="'post-section-' + ${post.id}">
|
||||
<div class="box post" th:id="${post.id}">
|
||||
<a href="javascript:void(0);" th:onclick="'goToViewer(this.parentNode)'" class="image left">
|
||||
<img th:src="${post.thumb != null and not #strings.isEmpty(post.thumb)} ? ${apiBaseUrl + post.thumb} : @{/images/pic01.jpg}" alt="Post Thumbnail" />
|
||||
<img th:src="${post.thumb != null and not #strings.isEmpty(post.thumb)} ? ${apiBaseUrl + post.thumb} : @{/images/pic01.jpg}"
|
||||
alt="Post Thumbnail"
|
||||
th:onerror="|this.onerror=null; this.src='@{/images/bum.png}';|" />
|
||||
</a>
|
||||
<div class="inner">
|
||||
<h3 style="display: flex; justify-content: space-between; align-items: center;">
|
||||
|
||||
@ -101,11 +101,18 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.js" defer></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.snow.css" rel="stylesheet" />
|
||||
<!-- <script src="https://unpkg.com/quill-image-resize@3.0.9/image-resize.min.js" defer></script>-->
|
||||
<!-- <script th:src="@{/js/image-resize.min.js}"></script>-->
|
||||
<script src="https://cdn.jsdelivr.net/gh/scrapooo/quill-resize-module@1.0.2/dist/quill-resize-module.js"></script>
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/quill-table-better@1/dist/quill-table-better.css" rel="stylesheet" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/quill-table-better@1/dist/quill-table-better.js"></script>
|
||||
<script>document.addEventListener('DOMContentLoaded', function() {
|
||||
<script src="https://cdn.jsdelivr.net/npm/quill-table-better@1/dist/quill-table-better.js" defer></script>
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/quill-image-resize-module@3.0.0/image-resize.min.js"></script>-->
|
||||
<!-- <script defer>document.addEventListener('DOMContentLoaded', function() {initEditor(true)});</script>-->
|
||||
<script defer>document.addEventListener('DOMContentLoaded', function() {
|
||||
initEditor(false)
|
||||
fetchComments(serverData.id);
|
||||
});</script>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user