diff --git a/public/app.js b/public/app.js index 53fbb0c..0305be3 100644 --- a/public/app.js +++ b/public/app.js @@ -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 `${text}`; + } 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"); diff --git a/public/settings.js b/public/settings.js index 2af5e6a..dc44ede 100644 --- a/public/settings.js +++ b/public/settings.js @@ -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 `${text}`; + } 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) => { diff --git a/public/style.css b/public/style.css index 425b1c9..5338167 100644 --- a/public/style.css +++ b/public/style.css @@ -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; } diff --git a/public/test-external-links.html b/public/test-external-links.html new file mode 100644 index 0000000..4d6461e --- /dev/null +++ b/public/test-external-links.html @@ -0,0 +1,160 @@ + + +
+ + ++ Google +
++ GitHub +
++ Wikipedia +
+ +Внутренняя ссылка: [Заметки](/notes)
+Внешняя ссылка: [Google](https://www.google.com)
+Внешняя ссылка: [GitHub](https://github.com)
+