✨ Добавлены функции для улучшения обработки тегов и редактирования заметок
- Реализована проверка на наличие символа # внутри HTML-атрибутов при извлечении тегов. - Добавлена кнопка отмены редактирования с соответствующей логикой для возврата к исходному состоянию заметки. - Обновлены обработчики событий для поддержки новой кнопки отмены и улучшения пользовательского интерфейса.
This commit is contained in:
parent
083ac11ab1
commit
05706a7e35
105
public/app.js
105
public/app.js
@ -108,6 +108,45 @@ function extractTags(content) {
|
|||||||
let match;
|
let match;
|
||||||
|
|
||||||
while ((match = tagRegex.exec(content)) !== null) {
|
while ((match = tagRegex.exec(content)) !== null) {
|
||||||
|
const matchIndex = match.index;
|
||||||
|
|
||||||
|
// Проверяем, не находится ли # внутри HTML-атрибута
|
||||||
|
const beforeContext = content.substring(
|
||||||
|
Math.max(0, matchIndex - 100),
|
||||||
|
matchIndex
|
||||||
|
);
|
||||||
|
const afterContext = content.substring(
|
||||||
|
matchIndex + match[0].length,
|
||||||
|
Math.min(content.length, matchIndex + match[0].length + 100)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Проверяем, есть ли признаки HTML-атрибута (например, style="color: #...)
|
||||||
|
const lastOpenTag = beforeContext.lastIndexOf("<");
|
||||||
|
const lastCloseTag = beforeContext.lastIndexOf(">");
|
||||||
|
|
||||||
|
// Если внутри HTML-тега, пропускаем
|
||||||
|
if (lastOpenTag > lastCloseTag) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем наличие = и кавычки перед #
|
||||||
|
const lastQuote = Math.max(
|
||||||
|
beforeContext.lastIndexOf('"'),
|
||||||
|
beforeContext.lastIndexOf("'")
|
||||||
|
);
|
||||||
|
const lastEquals = beforeContext.lastIndexOf("=");
|
||||||
|
|
||||||
|
// Если перед # есть = и кавычка, и после есть закрывающая кавычка
|
||||||
|
if (lastEquals > -1 && lastQuote > lastEquals) {
|
||||||
|
const nextQuote = Math.min(
|
||||||
|
afterContext.indexOf('"') !== -1 ? afterContext.indexOf('"') : Infinity,
|
||||||
|
afterContext.indexOf("'") !== -1 ? afterContext.indexOf("'") : Infinity
|
||||||
|
);
|
||||||
|
if (nextQuote !== Infinity) {
|
||||||
|
continue; // Пропускаем, это часть HTML-атрибута
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const tag = match[1].toLowerCase();
|
const tag = match[1].toLowerCase();
|
||||||
if (!tags.includes(tag)) {
|
if (!tags.includes(tag)) {
|
||||||
tags.push(tag);
|
tags.push(tag);
|
||||||
@ -149,6 +188,31 @@ function makeTagsClickable(content) {
|
|||||||
continue; // Пропускаем этот тег
|
continue; // Пропускаем этот тег
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Дополнительная проверка: не находится ли # внутри HTML-атрибута (например, style="color: #ff0000")
|
||||||
|
// Ищем последний символ кавычки перед текущей позицией
|
||||||
|
const beforeContext = beforeTag.substring(Math.max(0, match.index - 100));
|
||||||
|
const lastQuote = Math.max(
|
||||||
|
beforeContext.lastIndexOf('"'),
|
||||||
|
beforeContext.lastIndexOf("'")
|
||||||
|
);
|
||||||
|
const lastEquals = beforeContext.lastIndexOf("=");
|
||||||
|
|
||||||
|
// Если перед # есть = и кавычка, и нет закрывающей кавычки после =, то # находится в атрибуте
|
||||||
|
if (lastEquals > -1 && lastQuote > lastEquals) {
|
||||||
|
const afterContext = afterTag.substring(
|
||||||
|
0,
|
||||||
|
Math.min(100, afterTag.length)
|
||||||
|
);
|
||||||
|
const nextQuote = Math.min(
|
||||||
|
afterContext.indexOf('"') !== -1 ? afterContext.indexOf('"') : Infinity,
|
||||||
|
afterContext.indexOf("'") !== -1 ? afterContext.indexOf("'") : Infinity
|
||||||
|
);
|
||||||
|
// Если нашли закрывающую кавычку, значит # внутри атрибута
|
||||||
|
if (nextQuote !== Infinity) {
|
||||||
|
continue; // Пропускаем этот тег
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Заменяем тег на кликабельный элемент
|
// Заменяем тег на кликабельный элемент
|
||||||
const replacement = `<span class="tag-in-note" data-tag="${match.tag}">${match.fullMatch}</span>`;
|
const replacement = `<span class="tag-in-note" data-tag="${match.tag}">${match.fullMatch}</span>`;
|
||||||
result = beforeTag + replacement + afterTag;
|
result = beforeTag + replacement + afterTag;
|
||||||
@ -1489,12 +1553,19 @@ function addNoteEventListeners() {
|
|||||||
saveEditBtn.textContent = "Сохранить";
|
saveEditBtn.textContent = "Сохранить";
|
||||||
saveEditBtn.classList.add("btnSave");
|
saveEditBtn.classList.add("btnSave");
|
||||||
|
|
||||||
|
// Кнопка отмены
|
||||||
|
const cancelEditBtn = document.createElement("button");
|
||||||
|
cancelEditBtn.textContent = "Отмена";
|
||||||
|
cancelEditBtn.classList.add("btnSave");
|
||||||
|
cancelEditBtn.style.marginLeft = "8px";
|
||||||
|
|
||||||
// Подсказка о горячей клавише
|
// Подсказка о горячей клавише
|
||||||
const saveHint = document.createElement("span");
|
const saveHint = document.createElement("span");
|
||||||
saveHint.classList.add("save-hint");
|
saveHint.classList.add("save-hint");
|
||||||
saveHint.textContent = "или нажмите Alt + Enter";
|
saveHint.textContent = "или нажмите Alt + Enter";
|
||||||
|
|
||||||
saveButtonContainer.appendChild(saveEditBtn);
|
saveButtonContainer.appendChild(saveEditBtn);
|
||||||
|
saveButtonContainer.appendChild(cancelEditBtn);
|
||||||
saveButtonContainer.appendChild(saveHint);
|
saveButtonContainer.appendChild(saveHint);
|
||||||
|
|
||||||
// Функция обновления превью изображений для режима редактирования
|
// Функция обновления превью изображений для режима редактирования
|
||||||
@ -1593,11 +1664,42 @@ function addNoteEventListeners() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Функция отмены редактирования
|
||||||
|
const cancelEditNote = async function () {
|
||||||
|
const originalMarkdown = noteContent.dataset.originalContent || "";
|
||||||
|
const hasTextChanges = textarea.value !== originalMarkdown;
|
||||||
|
const hasNewImages = editSelectedImages.length > 0;
|
||||||
|
|
||||||
|
if (hasTextChanges || hasNewImages) {
|
||||||
|
const ok = confirm("Отменить изменения?");
|
||||||
|
if (!ok) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Очистить выбранные новые изображения и превью
|
||||||
|
editSelectedImages.length = 0;
|
||||||
|
if (editImagePreviewContainer) {
|
||||||
|
editImagePreviewContainer.style.display = "none";
|
||||||
|
}
|
||||||
|
if (editImagePreviewList) {
|
||||||
|
editImagePreviewList.innerHTML = "";
|
||||||
|
}
|
||||||
|
if (editImageInput) {
|
||||||
|
editImageInput.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Перерисовать заметки, вернув исходное состояние
|
||||||
|
await loadNotes(true);
|
||||||
|
};
|
||||||
|
|
||||||
// Обработчик горячей клавиши Alt+Enter для сохранения редактирования
|
// Обработчик горячей клавиши Alt+Enter для сохранения редактирования
|
||||||
textarea.addEventListener("keydown", function (event) {
|
textarea.addEventListener("keydown", function (event) {
|
||||||
if (event.altKey && event.key === "Enter") {
|
if (event.altKey && event.key === "Enter") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
saveEditNote();
|
saveEditNote();
|
||||||
|
} else if (event.key === "Escape") {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
cancelEditNote();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1654,6 +1756,9 @@ function addNoteEventListeners() {
|
|||||||
|
|
||||||
// Обработчик сохранения редактирования
|
// Обработчик сохранения редактирования
|
||||||
saveEditBtn.addEventListener("click", saveEditNote);
|
saveEditBtn.addEventListener("click", saveEditNote);
|
||||||
|
|
||||||
|
// Обработчик отмены редактирования
|
||||||
|
cancelEditBtn.addEventListener("click", cancelEditNote);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user