- -
- - -
- - {% if DQ.szIntroHTML %} -
{{ DQ.szIntroHTML|safe }}
- {% endif %} - - -
- {{ DQ.szContentHTML|safe }} -
- - -
- - {% if AUTHOR %} - {{ AUTHOR.szAuthorHTML|default:AUTHOR.szAuthor|safe }} - {% endif %} - +{% block CONTENT %}
{# Основной контент: Текст + Картинка #} +
{# Текстовая ряб. Задает высоту двум колонкам: текст и картина #} +
{# КОЛОНКА С ТЕКСТОМ #}{% if DQ.szIntroHTML %} + {# Интро/Вступление (например "Вася Пупкин как-то сказа". Может отсутствовать #}

{{ DQ.szIntroHTML|safe }}

{% endif %} +
{{ DQ.szContentHTML|safe }}
{% if AUTHOR %} + {# Автор #}{{ AUTHOR.szAuthorHTML|default:AUTHOR.szAuthor|safe }}{% endif %} +
{% if IMAGE %} +
{# КОЛОНКА С КАРТИНКОЙ #} +
+
+ {% if AUTHOR %}{{ AUTHOR.szAuthor }}{% else %}Dictum & Quotes{% endif %} +
-
- - - {% if IMAGE %} -
-
-
-
{% if AUTHOR %}{{ AUTHOR.szAuthor }}{% else %}Dictum & Quotes{% endif %}
-
-
-
- {% endif %} -
- - -
- {% for i in TAGS %}{{ i.name|safe }} {% endfor %} +
{% endif %} + + +
- - - - - -{% if not cookie_accept %}{% include "blocks/cookie_warning.html" %}{% endif %} -{% endblock %} +
+ + {% endblock %} \ No newline at end of file diff --git a/public/static/css/dicquo.css b/public/static/css/dicquo.css index ccd2bde..6ef89ba 100644 --- a/public/static/css/dicquo.css +++ b/public/static/css/dicquo.css @@ -1,143 +1,11 @@ @charset "utf-8"; - -.tags{ - color: silver; - font-size:1.5vh; - line-height:1.9vh; - padding-top: 7vh; -} - -/***************************************************************** - * Настройки для анимирования цвета ссылок: - * рецепт взят из: https://habr.com/ru/company/ruvds/blog/491702/ - *****************************************************************/ -.tags a { - text-decoration: none; - position: relative; - padding: 0 0.5ex; - display: inline-block; - color: white; - border-bottom: dotted 1px silver; - /* градиент для цвета ссылки */ - background: linear-gradient(to right, rgba(255,255,255,0.9) 40%, slategray, silver, lightyellow 50%, rgba(255,255,255,0.4)); - /* обрезка градиента */ - background-clip: initial; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-size: 250% 100%; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; - background-position: 100%; - /* плавное позиионирование градиента */ - transition: background-position 0.65s ease; - margin-right: 2vh; -} - -.tags a:hover { - color: white; - background-position: 0 100%; - border-bottom: solid 1px white; -} - -div[name="cookies_accept"] { - font-family:'Roboto', 'Lucida Grande', Verdana, Arial, sans-serif; - position:fixed; - bottom: 0; left: 0; - width: 100%; - padding: 2vh 2vw; - color: black; - background-color: gray; - text-align: center; -} - -div[name="cookies_accept"] button { - padding:0.5vh 0.5vw; - background: silver; - color: black; - margin-left: 2vw; - cursor: pointer; -} - -#logo { - margin-top:1vh; - filter: alpha(Opacity=75); /* Полупрозрачность для IE */ - opacity: 0.75; - float: left; -} - -#logo a { - border: none; - text-decoration: none; -} - -table { width: 80%; } - -#menu { - display: none; - color: silver; -} - -#mm { - text-decoration: none; - color: silver; -} - -#image { - width: 30vw; - text-align: center; - vertical-align: center; -} - -#image > center > div { - width: 22vw; - height: 22vw; - padding:0.5vw; - border-radius: 50%; -} - -#image > center > div > div { - border-radius:50%; - overflow: hidden; - display: flex; - justify-content: center; - align-items: center; -} - -#image > center > div > div > img { - width: auto; - height: 22vw; -} - -#author { - color: silver; - font-size: 3.5vh; - line-height: 4vh; - text-align: right; - padding-top: 4vh; - font-style: italic; -} - -#info { - color: silver; - font-size: 2.5vh; - line-height: 3vh; - padding-bottom: 2vh; -} - -#bb { - color: whitesmoke; - font-size: 4.5vh; - line-height: 5vh -} - -#next { float: right; } - -#next a { border-bottom: none; } - -/* --- NEW STYLES for FLEXBOX LAYOUT --- */ -.container { - width: 90%; - max-width: 1200px; - margin: 0 auto; +body { + margin: 0; + min-height: 100vh; + min-width: 100vw; + background-color: #111; /* Изначально темный фон */ + opacity: 0; /* Скрываем контент до расчета цвета */ + transition: opacity 0.9s ease-in-out; /* Очень плавное появление */ } /* Header */ @@ -145,46 +13,101 @@ header { display: flex; justify-content: space-between; align-items: center; - padding: 1vh 0; + padding: 1vh 4vw; } -/* Main Content Area */ -.main-content { - display: flex; - flex-direction: column; - justify-content: center; - min-height: 80vh; +header > #logo { + margin-top: 1vh; + float: left; } -.content-row { - display: flex; - align-items: center; - justify-content: center; - gap: 2vw; +header > #logo a { + border: none; + text-decoration: none; } -.text-col { - flex: 1; +header > #logo a > img { + width:50px; + height:46px; } -.image-col { - flex: 0 0 30vw; - display: flex; - justify-content: center; +header > nav { + border: #555555; + min-height: 50px; } -/* --- Icons for Header Stats (SVG in Base64) --- */ -.stats-icon { +header > nav > a { /* бургер */ + color: silver; + text-decoration: none; + font-size: 1.2em; + padding: 0 0.5em; + margin-right: -0.5em; + border: solid 1px transparent; + transition: border-color 0.8s ease, color 0.8s ease; + vertical-align: top; +} + +header > nav > a:hover { + color: white; + border: solid 1px silver; + transition: border-color 0.8s ease, color 0.8s ease; +} + +header > nav > #stats-menu { + display: none; + color: silver; + font-size: 0.9em; + margin-right: 15px; + text-align: right; + vertical-align: top; +} + +header > nav > #stats-menu > b { + font-weight: normal; + margin: 0 1ex; +} + +header > nav > #stats-menu > p { + font-style: italic; display: inline-block; + margin: 0 1vw; + padding-right: 1vw; + border-right: 1px dotted silver; +} + +header > nav > #stats-menu > i.stats-icon { display: inline-block; width: 0.9em; height: 0.9em; vertical-align: middle; background-size: contain; background-repeat: no-repeat; - margin-right: 0.2em; + margin-right: .2em; opacity: 0.7; /* Slight transparency for subtle look */ } +header > nav > #stats-menu > i.stats-icon.icon-views { + margin-left: .2em; +} + + +header > nav > #stats-menu > a { + color: silver; + text-decoration: none; + border: solid 1px gray; + border-radius: 2em; + padding: 1.5px 0.2em 0 0.2em; + margin-left: 1em; + transition: background-color 0.3s ease, color 0.3s ease; +} + +header > nav > #stats-menu > a:hover { + background-color: tan; + color: black; + border: solid 1px white; + transition: background-color 0.3s ease, color 0.3s ease; +} + +/* --- Icons for Header Stats (SVG in Base64) --- */ /* Clock Icon (Time) */ .icon-time { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' stroke='silver' fill='none' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E"); @@ -195,30 +118,194 @@ header { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' stroke='silver' fill='none' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z'%3E%3C/path%3E%3Ccircle cx='12' cy='12' r='3'%3E%3C/circle%3E%3C/svg%3E"); } -/* Responsive: on mobile stack columns */ +/* MAIN ARTICLE CONTENT */ +main { + /*justify-content: space-between;*/ + /*align-items: center;*/ + padding: 1vh 8vw; + display: flex; + flex-direction: column; + justify-content: center; + min-height: 60vh; + /*width: 90%;*/ + /*max-width: 1200px;*/ + /*margin: 0 auto;*/ +} + +main > article { + display: flex; + align-items: center; + justify-content: center; + gap: 2vw; +} + +main > article > figure { + flex: 1; +} + +main > article > figure > p { /* Интро/Вступление */ + color: silver; + font-size: 3vmin; + line-height: 3.5vmin; + padding-bottom: 2vmin; + font-style: italic; +} + +main > article > figure > blockquote { /* Цитата */ + color: whitesmoke; + font-size: 4.5vmin; + line-height: 5vmin; + border:none; + margin:0; + padding:0; +} + +main > article > figure > cite { /* Автор цитаты */ + color: silver; + font-size: 3.5vmin; + line-height: 4vmin; + text-align: right; + padding-top: 4vmin; + font-style: italic; +} + +main > article > div { + flex: 0 0 30vw; + display: flex; + justify-content: center; + width: 30vw; + text-align: right; + margin-bottom: 10vh; +} + +main > article > div > div { + width: 26vmax; + height: 26vmax; + padding: 0.5vw; + border-radius: 50%; +} + +main > article > div > div > div { + border-radius: 50%; + overflow: hidden; + display: flex; + justify-content: center; + align-items: center; +} + +main > article > div > div > div > img { + width: auto; + height: 26vmax; +} + +/* НАВИГАЦИЯ (ТЕГИ И ДАЛЕЕ) В КОНЦЕ */ +nav { + padding: 1vh 4vw; +} +nav > div { + color: silver; + font-size: 1.5vmin; + line-height: 1.9vmin; + padding-top: 7vh; +} +nav > div a { + text-decoration: none; + position: relative; + padding: 0 0.5ex; + display: inline-block; + color: white; + border-bottom: dotted 1px silver; + /* градиент для цвета ссылки */ + background: linear-gradient(to right, rgba(255, 255, 255, 0.9) 40%, slategray, silver, lightyellow 50%, rgba(255, 255, 255, 0.4)); + /* обрезка градиента */ + background-clip: initial; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-size: 250% 100%; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; + background-position: 100%; + /* плавное позиионирование градиента */ + transition: background-position 0.65s ease; + margin-right: 2vmin; +} + +nav > div a:hover { + color: white; + background-position: 0 100%; + border-bottom: solid 1px white; +} + +nav > div > div { + float: right; +} + +nav > div > div a { + border-bottom: none; +} + +/* --- ПОДВАЛ-КУКИ (ДЗЕН-СТИЛЬ) --- */ +footer { + font-family: 'Roboto', sans-serif; + position: fixed; + bottom: 0; + left: 0; + width: 100%; + padding: 2vh 4vw; + color: silver; /* Мягкий серый цвет текста */ + background-color: rgba(30, 30, 30, 0.8); /* Темный полупрозрачный фон */ + backdrop-filter: blur(5px); /* Эффект матового стекла (современно и медитативно) */ + text-align: center; + border-top: 1px solid #444; /* Тонкая грань */ + font-size: 0.9em; + z-index: 1000; /* Чтобы точно было поверх всего */ +} + +footer small { + display: inline-block; + margin-right: 2vw; + letter-spacing: 0.05em; /* Немного воздуха в тексте */ +} + +footer button { + padding: 0.5vh 1.5vw; + background: transparent; + color: silver; + border: 1px solid silver; + border-radius: 2em; /* Округлые, мягкие формы */ + cursor: pointer; + font-family: inherit; + font-size: 0.9em; + transition: all 0.4s ease; +} + +footer button:hover { + background: silver; + color: #111; + box-shadow: 0 0 10px rgba(255, 255, 255, 0.2); /* Легкое свечение при наведении */ +} + +/* Отзывчивость: для мобильных устройств колонки свапаются */ @media (max-width: 768px) { - .content-row { + main > article { flex-direction: column-reverse; } - .image-col { + + main > article > div { flex: 0 0 auto; margin-bottom: 2vh; } } /* --- ВЫРАВНИВАНИЕ СИМВОЛОВ ВИСЯЧЕЙ ПУНКТУАЦИИ (Hanging Punctuation) ТИПОГРАФА ETPGRF --- */ -/* --- ЛЕВЫЕ ВИСЯЧИЕ СИМВОЛЫ (выравнивание по левому краю) --- */ -.etp-laquo { margin-left: -0.44em; } /* « */ -.etp-ldquo, .etp-bdquo { margin-left: -0.4em; } /* “ „ */ -.etp-lsquo { margin-left: -0.22em; } /* ‘ */ -.etp-lpar, .etp-lsqb, .etp-lcub { margin-left: -0.25em; } /* ( [ { */ +/* --- В ПРОЕКТЕ ТОЛЬКО ЛЕВЫЕ ВИСЯЧИЕ СИМВОЛЫ (выравнивание по левому краю) --- */ +.etp-laquo {margin-left: -0.44em;} /* « */ +.etp-ldquo, .etp-bdquo { margin-left: -0.4em;} /* “ „ */ +.etp-lsquo {margin-left: -0.22em;} /* ‘ */ +.etp-lpar, .etp-lsqb, .etp-lcub {margin-left: -0.25em;}/* ( [ { */ -/* --- ПРАВЫЕ ВИСЯЧИЕ СИМВОЛЫ (выравнивание по правому краю) --- */ -/* Общая механика: "вырываем" символ из потока для идеального выравнивания текста */ -[class^="etp-r"], [class*=" etp-r"] { position: absolute; } -/* Точечная настройка смещения для каждого символа */ -.etp-raquo { right: -0.44em; } /* » */ -.etp-rdquo { right: -0.4em; } /* ” */ -.etp-rsquo { right: -0.22em; } /* ’ */ -.etp-rpar, .etp-rsqb, .etp-rcub { right: -0.25em; } /* ) ] } */ -.etp-r-dot, .etp-r-comma, .etp-r-colon { right: -0.15em; } /* . , : */ +/* --- СЧЕТЧИКИ (СКРЫТЫЙ ПИКСЕЛЬ) --- */ +.counter-pixel { + border: 0; + position: absolute; + left: -9999px; +} diff --git a/public/static/js/bg-generator.js b/public/static/js/bg-generator.js index 4c31786..94017ad 100644 --- a/public/static/js/bg-generator.js +++ b/public/static/js/bg-generator.js @@ -1,25 +1,29 @@ -// bg-generator.js: Generates a unique gradient background based on text content +// bg-generator.js: +// - Генерирует уникальный градиентный фон на основе текстового содержимого +// - Реализует плавное появление и исчезновение при навигации +// - Авто-редирект через 15 секунд для создания "медитативного" слайд-шоу эффекта +// - Обрабатывает принятие Cookie document.addEventListener("DOMContentLoaded", function() { - // 1. Get the text to hash (from hidden span in base.html) + // 1. Получаем текст для хеширования (из скрытого span в base.html) const rawSpan = document.getElementById('dq-content-raw'); let text = rawSpan ? rawSpan.innerText.trim() : ""; if (!text) { - text = "DictumAndQuotesDefault" + Math.random(); // Fallback random if no text + text = "DictumAndQuotesDefault" + Math.random(); // Случайный вариант, если текста нет } - // 2. Hash function (DJB2) + // 2. Хеш-функция (DJB2) let hash = 5381; for (let i = 0; i < text.length; i++) { - // Force 32-bit integer arithmetic + // Принудительная 32-битная целочисленная арифметика hash = ((hash << 5) + hash) + text.charCodeAt(i); - hash = hash & hash; // Convert to 32bit integer + hash = hash & hash; // Преобразование в 32-битное целое } - // 3. Generate 6 color components deterministically from the hash - // We need 6 numbers between 0 and 255. - // Let's use pseudo-random generator seeded by hash + // 3. Детерминированная генерация 6 цветовых компонентов из хеша + // Нам нужно 6 чисел от 0 до 255. + // Используем генератор псевдослучайных чисел с затравкой из хеша function Mulberry32(a) { return function() { @@ -30,53 +34,53 @@ document.addEventListener("DOMContentLoaded", function() { } } - const rand = Mulberry32(hash); // Seeded random generator + const rand = Mulberry32(hash); // Генератор случайных чисел с seed - // Generate 6 color components with darker range for "meditative" feel + // Генерация 6 цветовых компонентов в темном диапазоне для "медитативного" ощущения let colors = []; for(let i=0; i<6; i++) { - // Generate number between 10 and 80 (dark colors) + // Генерируем число от 10 до 80 (темные цвета) colors.push(Math.floor(rand() * 70) + 10); } - // Shuffle slightly based on random to allow variation on refresh (optional) - // colors.sort(() => Math.random() - 0.5); + // Немного перетасовываем на основе случайности, чтобы позволить вариации при обновлении (опционально) + colors.sort(() => Math.random() - 0.5); const rgb1 = `rgb(${colors[0]}, ${colors[1]}, ${colors[2]})`; const rgb2 = `rgb(${colors[3]}, ${colors[4]}, ${colors[5]})`; console.log("DQ BG Generator:", text.substring(0, 20) + "...", hash, rgb1, rgb2); - // 4. Apply to body - // Using linear-gradient to right with standard syntax + // 4. Применяем к body. + // Используем линейный градиент вправо со стандартным синтаксисом const bgString = `linear-gradient(90deg, ${rgb1} 0%, ${rgb2} 100%)`; document.body.style.background = bgString; - // 5. Apply to image background container (if exists on index page) + // 5. Применяем к контейнеру фона изображения (если он есть на главной странице) const imgBgContainer = document.querySelector('.image-col center > div'); if (imgBgContainer) { - // Use the first color of the gradient with opacity 0.7 + // Используем первый цвет градиента с прозрачностью 0.7 imgBgContainer.style.background = `rgba(${colors[0]}, ${colors[1]}, ${colors[2]}, 0.7)`; } - // 6. Reveal content (Fade In effect) + // 6. Показываем контент (эффект плавного появления - Fade In) setTimeout(() => { document.body.style.opacity = 1; }, 50); - // 7. Handle Fade Out on link clicks + // 7. Обработка плавного исчезновения (Fade Out) при клике по ссылкам document.body.addEventListener('click', function(e) { - // Find if a link was clicked (bubble up) + // Ищем, была ли нажата ссылка (всплытие) const link = e.target.closest('a'); if (link && link.href && link.target !== '_blank') { const hrefAttr = link.getAttribute('href'); if (hrefAttr && !hrefAttr.startsWith('#') && !link.href.includes('javascript:')) { - // Check if it is an internal link (same domain) + // Проверяем, является ли ссылка внутренней (тот же домен) if (new URL(link.href).origin === window.location.origin) { - e.preventDefault(); // Stop immediate navigation - document.body.style.opacity = 0; // Start Fade Out + e.preventDefault(); // Останавливаем немедленный переход + document.body.style.opacity = 0; // Запускаем Fade Out - // Wait for transition (matches CSS transition time 1.5s) + // Ждем завершения перехода (соответствует времени CSS transition 0.9s (было 1.5s)) setTimeout(() => { window.location.href = link.href; }, 900); @@ -85,14 +89,28 @@ document.addEventListener("DOMContentLoaded", function() { } }); - // 8. Auto-redirect ("meditative" slideshow) - // Find the NEXT link and simulate a click on it after 15 seconds + // 8. Авто-редирект ("медитативное" слайд-шоу) + // Ищем ссылку "ДАЛЕЕ" и симулируем клик по ней через 15 секунд const nextLink = document.querySelector('#next a'); if (nextLink) { setTimeout(() => { - // Trigger the click event on the link so our handler above (step 7) catches it - // and performs the smooth fade out animation. + // Вызываем событие клика по ссылке, чтобы наш обработчик выше (шаг 7) поймал его + // и выполнил анимацию плавного исчезновения. nextLink.click(); }, 15000); } + + // 9. Логика принятия Cookie + const cookieBanner = document.querySelector('footer'); + if (cookieBanner) { + const acceptButton = cookieBanner.querySelector('button'); + if (acceptButton) { + acceptButton.addEventListener('click', function() { + const date = new Date(); + date.setTime(date.getTime() + (92 * 24 * 60 * 60 * 1000)); // ~3 месяца (7948800000ms) + document.cookie = "cookie_accept=1; expires=" + date.toUTCString() + "; path=/; SameSite=Lax"; + cookieBanner.remove(); + }); + } + } }); diff --git a/public/static/js/counters.js b/public/static/js/counters.js new file mode 100644 index 0000000..16db91f --- /dev/null +++ b/public/static/js/counters.js @@ -0,0 +1,22 @@ +// Rating Mail.ru counter +var _tmr = window._tmr || (window._tmr = []); +_tmr.push({id: "3744288", type: "pageView", start: (new Date()).getTime()}); +(function (d, w, id) { + if (d.getElementById(id)) return; + var ts = d.createElement("script"); + ts.type = "text/javascript"; + ts.async = true; + ts.id = id; + ts.src = "https://top-fwz1.mail.ru/js/code.js"; + var f = function () { + var s = d.getElementsByTagName("script")[0]; + s.parentNode.insertBefore(ts, s); + }; + if (w.opera == "[object Opera]") { + d.addEventListener("DOMContentLoaded", f, false); + } else { + f(); + } +})(document, window, "tmr-code"); +// //Rating Mail.ru counter +