feat(cafe24): HTML 편집기 Ctrl+/ 주석 토글
- 선택이 있으면 선택이 걸친 줄 전체가 대상. 반쯤 걸친 줄이 잘려 태그가
깨지지 않게 하기 위함. 선택이 없으면 커서가 있는 줄 하나.
- 대상 안에 주석 기호가 하나라도 있으면 제거, 없으면 블록 전체를 <!-- -->
로 감싼다. HTML 주석은 중첩이 안 되므로 이미 주석인 부분을 또 감싸지
않는다.
- 선택이 개행에서 끝나면 다음 줄은 제외(줄 끝까지 드래그했을 때 아래 줄이
딸려오지 않게).
- execCommand("insertText") 로 넣어 Ctrl+Z 이력에 남긴다. 미지원 브라우저는
value 를 직접 바꾼다.
- 되돌린 뒤 커서/선택 위치를 복원한다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -78,7 +78,7 @@
|
|||||||
<input type="hidden" name="list_query" value="{{ list_query }}" />
|
<input type="hidden" name="list_query" value="{{ list_query }}" />
|
||||||
|
|
||||||
<div class="cf24-editor-bar">
|
<div class="cf24-editor-bar">
|
||||||
<span class="cf24-muted">상세설명 HTML · {{ desc.description | length }}자 · PC/모바일 공통</span>
|
<span class="cf24-muted">상세설명 HTML · {{ desc.description | length }}자 · PC/모바일 공통 · Ctrl+/ 주석</span>
|
||||||
<span class="cf24-editor-bar-right">
|
<span class="cf24-editor-bar-right">
|
||||||
<input class="cf24-memo" type="text" name="memo" maxlength="200"
|
<input class="cf24-memo" type="text" name="memo" maxlength="200"
|
||||||
placeholder="변경 메모 (버전 이력에 남습니다)" />
|
placeholder="변경 메모 (버전 이력에 남습니다)" />
|
||||||
@@ -99,7 +99,8 @@
|
|||||||
<div class="cf24-code-body">
|
<div class="cf24-code-body">
|
||||||
<pre class="cf24-code-hl" id="cf24-hl-pc" aria-hidden="true"></pre>
|
<pre class="cf24-code-hl" id="cf24-hl-pc" aria-hidden="true"></pre>
|
||||||
<textarea id="cf24-html-pc" class="cf24-code-input" name="html" wrap="off"
|
<textarea id="cf24-html-pc" class="cf24-code-input" name="html" wrap="off"
|
||||||
spellcheck="false" autocapitalize="off" autocorrect="off">{{ html_pc }}</textarea>
|
spellcheck="false" autocapitalize="off" autocorrect="off"
|
||||||
|
title="Tab 들여쓰기 · Ctrl+/ 주석 토글(선택한 줄 전체, 선택이 없으면 커서 줄)">{{ html_pc }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -186,10 +186,75 @@
|
|||||||
timer = setTimeout(repaint, 80);
|
timer = setTimeout(repaint, 80);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Ctrl+/ (Mac ⌘+/) 로 HTML 주석 토글.
|
||||||
|
- 선택이 있으면 그 선택이 걸친 **줄 전체**가 대상이다(반쯤 걸친 줄이
|
||||||
|
잘려 태그가 깨지지 않게).
|
||||||
|
- 선택이 없으면 커서가 있는 줄 하나.
|
||||||
|
- 대상 안에 주석 기호가 하나라도 있으면 **제거**, 없으면 블록 전체를
|
||||||
|
<!-- --> 로 감싼다. HTML 주석은 중첩이 안 되므로 "이미 주석인 부분을
|
||||||
|
또 감싸기"를 피하는 것이 이 규칙의 이유다. */
|
||||||
|
var COMMENT_MARK = /<!--[ \t]?|[ \t]?-->/g;
|
||||||
|
|
||||||
|
function toggleComment() {
|
||||||
|
var value = ta.value;
|
||||||
|
var start = ta.selectionStart, end = ta.selectionEnd;
|
||||||
|
// 선택이 개행에서 끝나면 그 다음 줄은 대상이 아니다(드래그로 줄 끝까지
|
||||||
|
// 끌었을 때 아래 줄까지 주석되는 것을 막는다).
|
||||||
|
if (end > start && value.charAt(end - 1) === "\n") end -= 1;
|
||||||
|
|
||||||
|
var lineStart = value.lastIndexOf("\n", start - 1) + 1;
|
||||||
|
var lineEnd = value.indexOf("\n", end);
|
||||||
|
if (lineEnd === -1) lineEnd = value.length;
|
||||||
|
|
||||||
|
var block = value.slice(lineStart, lineEnd);
|
||||||
|
if (!block.trim()) return; // 빈 줄에서는 아무 것도 하지 않는다
|
||||||
|
|
||||||
|
var caretIn = start - lineStart; // 블록 안에서의 커서 위치
|
||||||
|
var shift = 0, out;
|
||||||
|
|
||||||
|
COMMENT_MARK.lastIndex = 0;
|
||||||
|
if (COMMENT_MARK.test(block)) {
|
||||||
|
COMMENT_MARK.lastIndex = 0;
|
||||||
|
out = block.replace(COMMENT_MARK, function (mark, offset) {
|
||||||
|
if (offset < caretIn) shift -= mark.length; // 커서 앞이 줄어든 만큼
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var indent = block.match(/^[ \t]*/)[0];
|
||||||
|
out = indent + "<!-- " + block.slice(indent.length) + " -->";
|
||||||
|
shift = caretIn >= indent.length ? 5 : 0; // "<!-- " 길이
|
||||||
|
}
|
||||||
|
|
||||||
|
// execCommand 로 넣어야 브라우저의 실행취소(Ctrl+Z) 이력에 남는다.
|
||||||
|
// 지원하지 않으면 value 를 직접 바꾼다(그때는 되돌리기가 안 될 뿐 동작은 같다).
|
||||||
|
ta.selectionStart = lineStart;
|
||||||
|
ta.selectionEnd = lineEnd;
|
||||||
|
var inserted = false;
|
||||||
|
try { inserted = document.execCommand("insertText", false, out); } catch (err) { inserted = false; }
|
||||||
|
if (!inserted) ta.value = value.slice(0, lineStart) + out + value.slice(lineEnd);
|
||||||
|
|
||||||
|
if (start === end) {
|
||||||
|
var pos = lineStart + Math.min(out.length, Math.max(0, caretIn + shift));
|
||||||
|
ta.selectionStart = ta.selectionEnd = pos;
|
||||||
|
} else {
|
||||||
|
ta.selectionStart = lineStart; // 바꾼 범위를 계속 선택해 둔다
|
||||||
|
ta.selectionEnd = lineStart + out.length;
|
||||||
|
}
|
||||||
|
dirty = true;
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
ta.addEventListener("scroll", sync);
|
ta.addEventListener("scroll", sync);
|
||||||
ta.addEventListener("input", function () { dirty = true; refresh(); });
|
ta.addEventListener("input", function () { dirty = true; refresh(); });
|
||||||
// Tab 은 포커스 이동이 아니라 들여쓰기로 쓴다.
|
|
||||||
ta.addEventListener("keydown", function (e) {
|
ta.addEventListener("keydown", function (e) {
|
||||||
|
// Ctrl+/ · ⌘+/ — 자판에 따라 key 가 "/" 가 아닐 수 있어 code 도 함께 본다.
|
||||||
|
if ((e.ctrlKey || e.metaKey) && !e.altKey &&
|
||||||
|
(e.key === "/" || e.code === "Slash" || e.code === "NumpadDivide")) {
|
||||||
|
e.preventDefault();
|
||||||
|
toggleComment();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Tab 은 포커스 이동이 아니라 들여쓰기로 쓴다.
|
||||||
if (e.key !== "Tab") return;
|
if (e.key !== "Tab") return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var start = ta.selectionStart, end = ta.selectionEnd;
|
var start = ta.selectionStart, end = ta.selectionEnd;
|
||||||
|
|||||||
Reference in New Issue
Block a user