✨ Добавлена поддержка внешних ссылок в рендерере marked.js
- Реализована функция для определения внешних ссылок и переопределен рендеринг ссылок, чтобы открывать их в новом окне. - Добавлены обработчики для внешних ссылок, которые учитывают режим PWA. - Обновлены стили для внешних ссылок, добавлен значок для визуального обозначения.
This commit is contained in:
parent
e249638e7f
commit
ce803b4c20
@ -1585,6 +1585,33 @@ function highlightSearchText(content, query) {
|
||||
// Настройка marked.js для поддержки чекбоксов и strikethrough
|
||||
const renderer = new marked.Renderer();
|
||||
|
||||
// Функция для определения внешних ссылок
|
||||
function isExternalLink(href) {
|
||||
try {
|
||||
const url = new URL(href);
|
||||
return url.origin !== window.location.origin;
|
||||
} catch (e) {
|
||||
// Если URL невалидный, считаем его внутренним
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Переопределяем рендеринг ссылок для открытия внешних ссылок в браузере
|
||||
const originalLink = renderer.link.bind(renderer);
|
||||
renderer.link = function (href, title, text) {
|
||||
const isExternal = isExternalLink(href);
|
||||
|
||||
if (isExternal) {
|
||||
// Внешние ссылки открываем в браузере
|
||||
return `<a href="${href}" title="${
|
||||
title || ""
|
||||
}" target="_blank" rel="noopener noreferrer" class="external-link">${text}</a>`;
|
||||
} else {
|
||||
// Внутренние ссылки обрабатываем как обычно
|
||||
return originalLink(href, title, text);
|
||||
}
|
||||
};
|
||||
|
||||
// Переопределяем рендеринг списков, чтобы чекбоксы были кликабельными (без disabled)
|
||||
const originalListItem = renderer.listitem.bind(renderer);
|
||||
renderer.listitem = function (text, task, checked) {
|
||||
@ -1743,6 +1770,9 @@ async function renderNotes(notes) {
|
||||
// Добавляем обработчики для изображений в заметках
|
||||
addImageEventListeners();
|
||||
|
||||
// Добавляем обработчики для внешних ссылок
|
||||
addExternalLinkListeners();
|
||||
|
||||
// Добавляем обработчики для чекбоксов в заметках
|
||||
addCheckboxEventListeners();
|
||||
|
||||
@ -2676,6 +2706,32 @@ function addImageEventListeners() {
|
||||
// Обработчики для кнопок удаления изображений удалены - кнопки удаления только в режиме редактирования
|
||||
}
|
||||
|
||||
// Функция для добавления обработчиков внешних ссылок
|
||||
function addExternalLinkListeners() {
|
||||
// Обработчики для внешних ссылок
|
||||
document.querySelectorAll(".external-link").forEach((linkElement) => {
|
||||
// Проверяем, не добавлен ли уже обработчик
|
||||
if (linkElement._externalClickHandler) {
|
||||
return; // Пропускаем, если обработчик уже добавлен
|
||||
}
|
||||
|
||||
// Создаем новый обработчик
|
||||
linkElement._externalClickHandler = function (event) {
|
||||
// Для PWA приложений открываем ссылку в браузере
|
||||
if (
|
||||
window.matchMedia("(display-mode: standalone)").matches ||
|
||||
window.navigator.standalone === true
|
||||
) {
|
||||
event.preventDefault();
|
||||
window.open(this.href, "_blank", "noopener,noreferrer");
|
||||
}
|
||||
// В обычном браузере оставляем стандартное поведение (target="_blank")
|
||||
};
|
||||
|
||||
linkElement.addEventListener("click", linkElement._externalClickHandler);
|
||||
});
|
||||
}
|
||||
|
||||
// Функция для применения визуальных стилей к чекбоксу
|
||||
function applyCheckboxStyles(checkbox, checked) {
|
||||
const parentLi = checkbox.closest("li");
|
||||
|
||||
@ -1,3 +1,70 @@
|
||||
// Настройка marked.js для поддержки внешних ссылок
|
||||
function setupMarkedRenderer() {
|
||||
// Функция для определения внешних ссылок
|
||||
function isExternalLink(href) {
|
||||
try {
|
||||
const url = new URL(href);
|
||||
return url.origin !== window.location.origin;
|
||||
} catch (e) {
|
||||
// Если URL невалидный, считаем его внутренним
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Создаем renderer для marked
|
||||
const renderer = new marked.Renderer();
|
||||
|
||||
// Переопределяем рендеринг ссылок для открытия внешних ссылок в браузере
|
||||
const originalLink = renderer.link.bind(renderer);
|
||||
renderer.link = function (href, title, text) {
|
||||
const isExternal = isExternalLink(href);
|
||||
|
||||
if (isExternal) {
|
||||
// Внешние ссылки открываем в браузере
|
||||
return `<a href="${href}" title="${
|
||||
title || ""
|
||||
}" target="_blank" rel="noopener noreferrer" class="external-link">${text}</a>`;
|
||||
} else {
|
||||
// Внутренние ссылки обрабатываем как обычно
|
||||
return originalLink(href, title, text);
|
||||
}
|
||||
};
|
||||
|
||||
// Настраиваем marked
|
||||
marked.setOptions({
|
||||
gfm: true,
|
||||
breaks: true,
|
||||
renderer: renderer,
|
||||
html: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Функция для добавления обработчиков внешних ссылок
|
||||
function addExternalLinkListeners() {
|
||||
// Обработчики для внешних ссылок
|
||||
document.querySelectorAll(".external-link").forEach((linkElement) => {
|
||||
// Проверяем, не добавлен ли уже обработчик
|
||||
if (linkElement._externalClickHandler) {
|
||||
return; // Пропускаем, если обработчик уже добавлен
|
||||
}
|
||||
|
||||
// Создаем новый обработчик
|
||||
linkElement._externalClickHandler = function (event) {
|
||||
// Для PWA приложений открываем ссылку в браузере
|
||||
if (
|
||||
window.matchMedia("(display-mode: standalone)").matches ||
|
||||
window.navigator.standalone === true
|
||||
) {
|
||||
event.preventDefault();
|
||||
window.open(this.href, "_blank", "noopener,noreferrer");
|
||||
}
|
||||
// В обычном браузере оставляем стандартное поведение (target="_blank")
|
||||
};
|
||||
|
||||
linkElement.addEventListener("click", linkElement._externalClickHandler);
|
||||
});
|
||||
}
|
||||
|
||||
// Логика переключения темы
|
||||
function initThemeToggle() {
|
||||
const themeToggleBtn = document.getElementById("theme-toggle-btn");
|
||||
@ -58,7 +125,10 @@ function applyTheme(theme) {
|
||||
}
|
||||
|
||||
// Инициализируем переключатель темы при загрузке страницы
|
||||
document.addEventListener("DOMContentLoaded", initThemeToggle);
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
initThemeToggle();
|
||||
setupMarkedRenderer();
|
||||
});
|
||||
|
||||
// Переменные для пагинации логов
|
||||
let logsOffset = 0;
|
||||
@ -439,6 +509,9 @@ async function loadArchivedNotes() {
|
||||
|
||||
// Добавление обработчиков для архивных заметок
|
||||
function addArchivedNotesEventListeners() {
|
||||
// Добавляем обработчики для внешних ссылок
|
||||
addExternalLinkListeners();
|
||||
|
||||
// Восстановление
|
||||
document.querySelectorAll(".btn-restore").forEach((btn) => {
|
||||
btn.addEventListener("click", async (e) => {
|
||||
|
||||
@ -800,6 +800,26 @@ textarea:focus {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/* Стили для внешних ссылок */
|
||||
.textNote a.external-link {
|
||||
position: relative;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.textNote a.external-link::after {
|
||||
content: "↗";
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
font-size: 0.8em;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.textNote a.external-link:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.textNote a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
160
public/test-external-links.html
Normal file
160
public/test-external-links.html
Normal file
@ -0,0 +1,160 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Тест внешних ссылок - NoteJS</title>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<meta name="theme-color" content="#007bff" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Тест внешних ссылок в PWA</h1>
|
||||
|
||||
<div class="textNote">
|
||||
<h2>Примеры ссылок:</h2>
|
||||
|
||||
<h3>Внутренние ссылки (открываются в PWA):</h3>
|
||||
<p><a href="/notes">Перейти к заметкам</a></p>
|
||||
<p><a href="/profile">Перейти к профилю</a></p>
|
||||
|
||||
<h3>Внешние ссылки (открываются в браузере):</h3>
|
||||
<p>
|
||||
<a
|
||||
href="https://www.google.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="external-link"
|
||||
>Google</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a
|
||||
href="https://github.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="external-link"
|
||||
>GitHub</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a
|
||||
href="https://www.wikipedia.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="external-link"
|
||||
>Wikipedia</a
|
||||
>
|
||||
</p>
|
||||
|
||||
<h3>Markdown ссылки:</h3>
|
||||
<p>Внутренняя ссылка: [Заметки](/notes)</p>
|
||||
<p>Внешняя ссылка: [Google](https://www.google.com)</p>
|
||||
<p>Внешняя ссылка: [GitHub](https://github.com)</p>
|
||||
</div>
|
||||
|
||||
<div class="test-results">
|
||||
<h3>Ожидаемое поведение:</h3>
|
||||
<ul>
|
||||
<li>Внутренние ссылки должны открываться в том же PWA окне</li>
|
||||
<li>Внешние ссылки должны открываться в браузере (новой вкладке)</li>
|
||||
<li>Внешние ссылки должны иметь иконку ↗</li>
|
||||
<li>
|
||||
В PWA режиме внешние ссылки должны принудительно открываться в
|
||||
браузере
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script>
|
||||
// Настройка marked.js для поддержки внешних ссылок
|
||||
function setupMarkedRenderer() {
|
||||
// Функция для определения внешних ссылок
|
||||
function isExternalLink(href) {
|
||||
try {
|
||||
const url = new URL(href);
|
||||
return url.origin !== window.location.origin;
|
||||
} catch (e) {
|
||||
// Если URL невалидный, считаем его внутренним
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Создаем renderer для marked
|
||||
const renderer = new marked.Renderer();
|
||||
|
||||
// Переопределяем рендеринг ссылок для открытия внешних ссылок в браузере
|
||||
const originalLink = renderer.link.bind(renderer);
|
||||
renderer.link = function (href, title, text) {
|
||||
const isExternal = isExternalLink(href);
|
||||
|
||||
if (isExternal) {
|
||||
// Внешние ссылки открываем в браузере
|
||||
return `<a href="${href}" title="${
|
||||
title || ""
|
||||
}" target="_blank" rel="noopener noreferrer" class="external-link">${text}</a>`;
|
||||
} else {
|
||||
// Внутренние ссылки обрабатываем как обычно
|
||||
return originalLink(href, title, text);
|
||||
}
|
||||
};
|
||||
|
||||
// Настраиваем marked
|
||||
marked.setOptions({
|
||||
gfm: true,
|
||||
breaks: true,
|
||||
renderer: renderer,
|
||||
html: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Функция для добавления обработчиков внешних ссылок
|
||||
function addExternalLinkListeners() {
|
||||
// Обработчики для внешних ссылок
|
||||
document.querySelectorAll(".external-link").forEach((linkElement) => {
|
||||
// Проверяем, не добавлен ли уже обработчик
|
||||
if (linkElement._externalClickHandler) {
|
||||
return; // Пропускаем, если обработчик уже добавлен
|
||||
}
|
||||
|
||||
// Создаем новый обработчик
|
||||
linkElement._externalClickHandler = function (event) {
|
||||
// Для PWA приложений открываем ссылку в браузере
|
||||
if (
|
||||
window.matchMedia("(display-mode: standalone)").matches ||
|
||||
window.navigator.standalone === true
|
||||
) {
|
||||
event.preventDefault();
|
||||
window.open(this.href, "_blank", "noopener,noreferrer");
|
||||
}
|
||||
// В обычном браузере оставляем стандартное поведение (target="_blank")
|
||||
};
|
||||
|
||||
linkElement.addEventListener(
|
||||
"click",
|
||||
linkElement._externalClickHandler
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
setupMarkedRenderer();
|
||||
|
||||
// Обрабатываем markdown ссылки
|
||||
const markdownLinks = document.querySelectorAll(".textNote p");
|
||||
markdownLinks.forEach((p) => {
|
||||
if (p.textContent.includes("[") && p.textContent.includes("](")) {
|
||||
const htmlContent = marked.parse(p.textContent);
|
||||
p.innerHTML = htmlContent;
|
||||
}
|
||||
});
|
||||
|
||||
addExternalLinkListeners();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user