13 Commits

Author SHA1 Message Date
f2f98d9229 add: Счетчик метрики
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m26s
2026-02-22 13:01:16 +03:00
a33b517a3e add: Планы (памятка) 2026-02-22 13:00:49 +03:00
d4624e7761 mod: Улучшения для мобильных устройств. 2026-02-22 12:12:49 +03:00
a608dea61f mod: Страницы ошибок (в новом дизайне и оптимизированы). 2026-02-22 02:55:21 +03:00
5bfd50efd5 mod: Переработаны дизайн и компоновка. Минималистичный код.
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m27s
2026-02-22 01:23:46 +03:00
c1bcb2895d mod: CSRF_TRUSTED_ORIGINS по порту 8010 для localhost 2026-02-21 02:36:53 +03:00
9ea2b15043 fix: Исключено широковещательная трансляция gunicorn по порту 8010. 2026-02-21 02:31:59 +03:00
5a80cf6406 fix: Добавлена CSRF_TRUSTED_ORIGINS для правильной верификации CSRF при работе с формами (и админки) 2026-02-21 02:23:09 +03:00
915c286e81 fix: ошибки 2026-02-21 02:03:37 +03:00
c46b6c1061 del: ненужный шаблон 2026-02-21 01:10:42 +03:00
b2a26a9dcc mod: url админки из .env 2026-02-21 00:58:57 +03:00
bd5cdcd870 mod: описание проекта и развёртывание. 2026-02-21 00:43:47 +03:00
db6cbb7bdf mod: # DOCKER_API_VERSION и WATCHTOWER_SCOPE 2026-02-20 23:06:06 +03:00
21 changed files with 881 additions and 563 deletions

View File

@@ -9,6 +9,11 @@ SECRET_KEY='change_me_in_production'
# Разрешённые хосты. В ПРОДАКШЕНЕ: Установите реальные домены, с которых будет доступно ваше приложение. # Разрешённые хосты. В ПРОДАКШЕНЕ: Установите реальные домены, с которых будет доступно ваше приложение.
ALLOWED_HOSTS=127.0.0.1,localhost ALLOWED_HOSTS=127.0.0.1,localhost
# CSRF Trusted Origins (ВАЖНО для Docker/Nginx/SSL).
# Перечислите здесь URL, по которым вы заходите на сайт, включая схему (http/https).
# Если этого не сделать, при попытке залогиниться в админку вы получите ошибку CSRF.
CSRF_TRUSTED_ORIGINS=https://dq.cube2.ru,http://127.0.0.1:8010,http://localhost:8010
# Email администратора (для получения уведомлений о критических ошибках и других важных сообщений от Django). # Email администратора (для получения уведомлений о критических ошибках и других важных сообщений от Django).
ADMIN_EMAIL=admin@example.com ADMIN_EMAIL=admin@example.com

45
PLANS.md Normal file
View File

@@ -0,0 +1,45 @@
# Планы по развитию проекта (DicQuo)
## 1. Список Авторов (Feature: Authors List)
**Цель:** Улучшить SEO (плоская структура) и навигацию, сохранив "Дзен" (минимализм).
**Концепция:**
- Добавить иконку "Люди/Авторы" в шапку сайта (рядом с бургером).
- По клику открывается **полноэкранный оверлей** (как статистика).
- Внутри список авторов карточками/строками.
**Элементы списка:**
1. **Имя Автора** (крупно) -> Ссылка на ротацию цитат автора (`/?tag=author-slug`).
2. **Счетчик цитат** (мелко, например `(25)`) -> Клик раскрывает "гармошку" (аккордеон).
3. **Список цитат** (внутри гармошки) -> Прямые ссылки на цитаты (например: `/123_nachalo-frazy...`). Текст ссылок — начало фразы.
**Техническая реализация:**
- **Backend:** `Context Processor` или логика в `IndexView` (или отдельный AJAX endpoint) для сбора данных:
```json
[
{
"name": "Имя",
"slug": "slug",
"count": 25,
"quotes": [{"id": 1, "url": "...", "text": "Текст..."}, ...]
}, ...
]
```
- **Frontend:** HTML/CSS для модального окна и JS для раскрытия списков.
## 2. Админка
- Починить мелкие баги в управлении тегами.
- Улучшить управление настройками типографа (etpgrf) через виртуальные поля.
- Поля в админке для настройки (кавычки, неразрывные пробелы и т.д.).
- При сохранении применять типограф к полям `szContent` -> `szContentHTML`.
- `szContentHTML` сделать редактитруемым чекрез CodeMirror (для ручной типографики тяжёлых случаев).
## 3. SEO и Оптимизация
- Проверить индексацию новых страниц `static_404`/`static_500`.
- Убедиться, что `canonical` ссылки работают корректно.
## 4. Дальние планы
- Форма для добавления цитат пользователями (с модерацией).
- API для интеграции с внешними сервисами (магазинами грампластинок и музыкальными сервисами).
- Сбор цитат из открытых источников (например, с помощью парсинга сайтов с цитатами или API).

132
README.md
View File

@@ -1,16 +1,130 @@
# DicQuo (цитаты и высказывания) # DicQuo (Цитаты, Афоризмы и Факты)
Пет-проект ротации цитат и высказываний. Развернут на [dq.cube2.ru](https://dq.cube2.ru). **Dicquo** — это коллекция отобранных вручную цитат, оформленных с уважением к типографике. Место для вдумчивого чтения, переосмысления и поиска вдохновения. Проект создан как пространство, где типографика встречается со смыслом, а технологии помогают контенту выглядеть безупречно.
Изначальная цель: Основные цели проекта:
* Испытывать различные типографы (библиотеки и API), разрабатывать и тестировать свои * **Типографика как искусство:** Разработка и тестирование собственных алгоритмов типографирования (висячая пунктуация, неразрывные пробелы, правильные тире) из библиотеки `etpgrf` (доступен в [GitHub](https://github.com/erjemin/etpgrf), [GitVerse](https://gitverse.ru/erjemin/etpgrf) и self-hosted [Cube2](https://git.cube2.ru/erjemin/2025-etpgrf), онлайн версия развёрнута на [typograph.cube2.ru](https://typograph.cube2.ru/)).
типографы и правила типографирования. * **SEO-эксперименты:** Исследование влияния микроразметки, мета-тегов и семантической верстки на индексацию поисковыми системами.
* Испытать, как содержание отдельных атрибуты (meta, keywords, description и т.п.) * **Технологический стек:** Современный Django, Docker, CI/CD и автоматизация деплоя.
влияют на поисковую выдачу.
[Инструкция по развертыванию на хостинге c CGI Passenger](deploy_to_dreamhost.md) Развернут на [dq.cube2.ru](https://dq.cube2.ru).
## ToDo? ---
## Структура файлов на сервере (Production)
После правильного развертывания, папка проекта на сервере (например, `~/docker-apps/dicquo/`) должна выглядеть так:
```text
dicquo/
├── docker-compose.yml # Переименованный docker-compose.prod.yml из этого репозитория (запускает контейнеры)
├── .env # Файл с переменными окружения и секретами (не пушить в git!)
├── database/ # Папка для базы данных (Persistent Volume)
│ ├── db.sqlite3 # Фактический файл базы данных
├── media/ # Папка с медиа-файлами (Persistent Volume)
│ ├── img2/ # Картинки для цитат (доступны через Nginx)
│ └── errors/ # Картинки для страниц ошибок (404, 500)
└── config/ # Папка для конфигураций (Генерируется контейнером)
└── nginx/
├── dq-app--external-nginx.conf # Конфиг для внешнего Nginx (скопируется из контейнера при первом запуске)
└── nginx_dq.conf.example # Образец конфига для внешнего Nginx (не используется в продакшене)
```
---
## Развёртывание (Deployment)
Проект полностью упакован в Docker и разворачивается с помощью `docker compose`. Ниже приведена инструкция для развертывания на чистом Linux-сервере (Ubuntu/Debian, архитектуры AMD/ARM).
### 1. Подготовка структуры
Создайте директорию для проекта (например, в домашней папке пользователя) и необходимые подпапки для persistent-данных:
```bash
mkdir -p ~/docker-apps/dicquo
cd ~/docker-apps/dicquo
mkdir -p database media config
```
### 2. Файлы конфигурации
Вам понадобятся два файла из репозитория (или их содержимое):
1. **`docker-compose.prod.yml`** -> сохраните его на сервере как `docker-compose.yml`.
2. **`.env`** -> создайте на основе `.env.sample`, заполнив секретами для продакшена.
**Важные переменные в `.env`:**
* `HOST_PROJECT_PATH`: Полный путь к папке проекта на хосте (например, `/home/username/docker-apps/dicquo`). Используется для корректной генерации конфига Nginx.
* `DJANGO_ALLOWED_HOSTS`: Список доменов через запятую (например, `dq.cube2.ru,127.0.0.1`).
### 3. Перенос данных (Опционально)
Если вы мигрируете с dev-окружения или другого сервера, можно просто скопировать файлы базы данных и медиа.
Скопируйте файл базы `database/db.sqlite3` и содержимое папки `media/` в соответсвующие папки на сервере.
### 4. Настройка прав доступа (Permissions) ⚠️
Это **критически важный этап**.
1. Docker-контейнер (с нашим бэкендом Django и Gunicorn) должн иметь доступ к примонтированным папкам.
2. Внешний Nginx (на хосте) должен иметь доступ к статике и медиа.
**Права на папки проекта:**
```bash
# Разрешаем запись в базу и медиа для всех (самый простой способ избежать проблем с UID внутри Docker)
sudo chmod 777 database
sudo chmod 666 database/db.sqlite3
sudo chmod -R 755 media
```
**Права на родительские директории (Pass-through):**
Если проект лежит в домашней папке пользователя (`/home/username/...`), то Nginx (пользователь `www-data`) по умолчанию **не сможет** туда попасть. Нужно разрешить "проход" (execute) для всех пользователей по пути к проекту:
```bash
# Разрешаем "проход" через домашнюю папку (чтение файлов при этом остается закрытым, только доступ к известным путям)
chmod o+x /home/username
chmod o+x /home/username/docker-apps
chmod o+x /home/username/docker-apps/dicquo
```
> *Без этого шага Nginx будет выдавать 403 Forbidden на картинки и статику.*
### 5. Запуск
```bash
docker compose up -d
```
При первом запуске контейнер автоматически:
* Применит миграции.
* Соберет статику.
* Сгенерирует конфиг для Nginx в папке `config/nginx/`.
### 6. Настройка Nginx и SSL
1. **Подключение конфига:**
Создайте симлинк на сгенерированный конфиг:
```bash
sudo ln -s /home/username/docker-apps/dicquo/config/nginx/dq-app--external-nginx.conf /etc/nginx/sites-enabled/dq-app.conf
```
2. **Проверка и релоад:**
```bash
sudo nginx -t
sudo systemctl reload nginx
```
3. **Получение SSL сертификата (Certbot):**
```bash
sudo certbot --nginx -d dq.cube2.ru
```
---
## Разработка (Dev)
Для локального запуска используется `docker-compose.yml` (он же dev-версия).
```bash
docker compose up --build
```
Проект будет доступен по адресу: http://127.0.0.1:8008
## ToDo
* В будущем, возможно, сделать API для предоставления цитат вешним потребителям * В будущем, возможно, сделать API для предоставления цитат вешним потребителям
(по темам, авторам и т.п.). (по темам, авторам и т.п.).

View File

@@ -21,15 +21,18 @@ env = environ.Env(
# If BASE_DIR is .../dicquo, then .env is at BASE_DIR.parent/.env # If BASE_DIR is .../dicquo, then .env is at BASE_DIR.parent/.env
environ.Env.read_env(os.path.join(BASE_DIR.parent, '.env')) environ.Env.read_env(os.path.join(BASE_DIR.parent, '.env'))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env('SECRET_KEY') SECRET_KEY = env('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env('DEBUG') DEBUG = env('DEBUG')
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=[]) ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=['127.0.0.1', 'localhost'])
CSRF_TRUSTED_ORIGINS = env.list('CSRF_TRUSTED_ORIGINS', default=['http://127.0.0.1', 'http://localhost'])
# Custom Admin URL from .env
ADMIN_URL = env('ADMIN_URL', default='admin/')
######################################### #########################################
# Настройки сообщений об ошибках когда все упало и т.п. # Настройки сообщений об ошибках когда все упало и т.п.

View File

@@ -19,7 +19,7 @@ from django.urls import path, re_path
from django.conf.urls.static import static from django.conf.urls.static import static
from django.contrib.sitemaps.views import sitemap from django.contrib.sitemaps.views import sitemap
from django.views.generic import TemplateView from django.views.generic import TemplateView
from dicquo import settings from django.conf import settings
from web import views from web import views
from web.sitemaps import DictumSitemap from web.sitemaps import DictumSitemap
@@ -28,7 +28,7 @@ sitemaps = {
} }
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), re_path(f'^{settings.ADMIN_URL}', admin.site.urls),
re_path(r'^$', views.IndexView.as_view()), re_path(r'^$', views.IndexView.as_view()),
re_path(r'^(?P<dq_id>\d{1,12})_\S*$', views.DictumDetailView.as_view()), re_path(r'^(?P<dq_id>\d{1,12})_\S*$', views.DictumDetailView.as_view()),
@@ -42,4 +42,7 @@ if settings.DEBUG:
path('500/', TemplateView.as_view(template_name="500.html")), path('500/', TemplateView.as_view(template_name="500.html")),
path('403/', TemplateView.as_view(template_name="403.html")), path('403/', TemplateView.as_view(template_name="403.html")),
path('400/', TemplateView.as_view(template_name="400.html")), path('400/', TemplateView.as_view(template_name="400.html")),
# Для проверки статических страниц ошибок (Nginx)
path('static_404/', TemplateView.as_view(template_name="static_404.html")),
path('static_500/', TemplateView.as_view(template_name="static_500.html")),
] ]

View File

@@ -1,31 +1,44 @@
{% extends "base.html" %} <!DOCTYPE html>
<html lang="ru">
{% block Title %}400: Плохой запрос{% endblock %} <head>
<meta charset="utf-8">
{% block CONTENT %}{% include "blocks/header_nav.html" %} <meta name="viewport" content="width=device-width, initial-scale=1">
<div class="container main-content"> <title>400: Плохой запрос | DicQuo</title>
<!-- Осно<D0BD><D0BE>ной контент: Текст + Картинка --> <style>
<div class="content-row"> body { margin: 0; min-height: 100vh; background-color: #211; color: silver; font-family: serif; opacity: 1; transition: opacity 0.9s ease-in-out; }
header { display: flex; justify-content: space-between; align-items: center; padding: 1vh 4vw; }
<!-- Текстовая колонка --> header > #logo { margin-top: 1vh; float: left; }
<div class="text-col"> header > #logo a { border: none; text-decoration: none; color: silver;}
<!-- Цитата --> main { padding: 1vh 8vw; display: flex; flex-direction: column; justify-content: center; min-height: 60vh; }
<blockquote id="bb" style="border:none; margin:0; padding:0;"> main > article { display: flex; align-items: center; justify-content: center; gap: 2vw; }
<span style="margin-left:-0.44em;">&laquo;</span>Вы спрашиваете меня о&nbsp;чем-то странном. Я&nbsp;не&nbsp;понимаю ваш запрос.» 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: 8vmin; font-style: italic; display: block; }
.tags { text-align: center; color: silver; font-size: 1.5vmin; line-height: 1.9vmin; padding: 1vh 8vw; float: left;}
.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: 2vmin; }
.tags a:hover { color: white; background-position: 0 100%; border-bottom: solid 1px white; }
</style>
</head>
<body>
<header>
<div id="logo">
<a href="/" style="font-size: 3em; font-weight: bold; color: orange; font-style: italic">DQ</a>
</div>
</header>
<main>
<article>
<figure>
<p>Загадочно:</p>
<blockquote id="bb">
<span style="margin-left:-0.44em;">&laquo;</span>Вы спрашиваете меня о&nbsp;чем-то странном. Я&nbsp;не&nbsp;понимаю
ваш запрос.»
</blockquote> </blockquote>
<!-- Автор -->
<div id="author">
<cite>Озадаченный Сервер (400)</cite> <cite>Озадаченный Сервер (400)</cite>
</div> </figure>
</div> </article>
</main>
</div> <div class="tags">
<!-- Блок тегов и навигации -->
<div class="tags">
<a href="/">Сформулировать иначе (на главную)</a> <a href="/">Сформулировать иначе (на главную)</a>
</div>
</div> </div>
</body>
{% endblock %} </html>

View File

@@ -1,31 +1,43 @@
{% extends "base.html" %} <!DOCTYPE html>
<html lang="ru">
{% block Title %}403: Доступ запрещен{% endblock %} <head>
<meta charset="utf-8">
{% block CONTENT %}{% include "blocks/header_nav.html" %} <meta name="viewport" content="width=device-width, initial-scale=1">
<div class="container main-content"> <title>403: Доступ запрещен | DicQuo</title>
<!-- Основной контент: Текст + Картинка --> <style>
<div class="content-row"> body { margin: 0; min-height: 100vh; background-color: #211; color: silver; font-family: serif; opacity: 1; transition: opacity 0.9s ease-in-out; }
header { display: flex; justify-content: space-between; align-items: center; padding: 1vh 4vw; }
<!-- Текстовая колонка --> header > #logo { margin-top: 1vh; float: left; }
<div class="text-col"> header > #logo a { border: none; text-decoration: none; color: silver;}
<!-- Цитата --> main { padding: 1vh 8vw; display: flex; flex-direction: column; justify-content: center; min-height: 60vh; }
<blockquote id="bb" style="border:none; margin:0; padding:0;"> main > article { display: flex; align-items: center; justify-content: center; gap: 2vw; }
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: 8vmin; font-style: italic; display: block; }
.tags { text-align: center; color: silver; font-size: 1.5vmin; line-height: 1.9vmin; padding: 1vh 8vw; float: left;}
.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: 2vmin; }
.tags a:hover { color: white; background-position: 0 100%; border-bottom: solid 1px white; }
</style>
</head>
<body>
<header>
<div id="logo">
<a href="/" style="font-size: 3em; font-weight: bold; color: orange; font-style: italic">DQ</a>
</div>
</header>
<main>
<article>
<figure>
<p>Категорически:</p>
<blockquote id="bb">
<span style="margin-left:-0.44em;">&laquo;</span>Вам сюда нельзя. Даже если очень хочется. Уходите!» <span style="margin-left:-0.44em;">&laquo;</span>Вам сюда нельзя. Даже если очень хочется. Уходите!»
</blockquote> </blockquote>
<!-- Автор -->
<div id="author">
<cite>Строгий Вахтёр (403)</cite> <cite>Строгий Вахтёр (403)</cite>
</div> </figure>
</div> </article>
</main>
</div> <div class="tags">
<!-- Блок тегов и навигации -->
<div class="tags">
<a href="/">Уйти по-добру по-здорову</a> <a href="/">Уйти по-добру по-здорову</a>
</div>
</div> </div>
</body>
{% endblock %} </html>

View File

@@ -1,31 +1,43 @@
{% extends "base.html" %} <!DOCTYPE html>
<html lang="ru">
{% block Title %}404: Страница не найдена{% endblock %} <head>
<meta charset="utf-8">
{% block CONTENT %}{% include "blocks/header_nav.html" %} <meta name="viewport" content="width=device-width, initial-scale=1">
<div class="container main-content"> <title>404: Страница не найдена | DicQuo</title>
<!-- Основной контент: Текст + Картинка --> <style>
<div class="content-row"> body { margin: 0; min-height: 100vh; background-color: #211; color: silver; font-family: serif; opacity: 1; transition: opacity 0.9s ease-in-out; }
header { display: flex; justify-content: space-between; align-items: center; padding: 1vh 4vw; }
<!-- Текстовая колонка --> header > #logo { margin-top: 1vh; float: left; }
<div class="text-col"> header > #logo a { border: none; text-decoration: none; color: silver;}
<!-- Цитата --> main { padding: 1vh 8vw; display: flex; flex-direction: column; justify-content: center; min-height: 60vh; }
<blockquote id="bb" style="border:none; margin:0; padding:0;"> main > article { display: flex; align-items: center; justify-content: center; gap: 2vw; }
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: 8vmin; font-style: italic; display: block; }
.tags { text-align: center; color: silver; font-size: 1.5vmin; line-height: 1.9vmin; padding: 1vh 8vw; float: left;}
.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: 2vmin; }
.tags a:hover { color: white; background-position: 0 100%; border-bottom: solid 1px white; }
</style>
</head>
<body>
<header>
<div id="logo">
<a href="/" style="font-size: 3em; font-weight: bold; color: orange; font-style: italic">DQ</a>
</div>
</header>
<main>
<article>
<figure>
<p>Вздыхая:</p>
<blockquote id="bb">
<span style="margin-left:-0.44em;">&laquo;</span>Я искал везде. Даже под&nbsp;диваном. Этой страницы здесь&nbsp;нет.» <span style="margin-left:-0.44em;">&laquo;</span>Я искал везде. Даже под&nbsp;диваном. Этой страницы здесь&nbsp;нет.»
</blockquote> </blockquote>
<!-- Автор -->
<div id="author">
<cite>Системный Администратор (404)</cite> <cite>Системный Администратор (404)</cite>
</div> </figure>
</div> </article>
</main>
</div> <div class="tags">
<!-- Блок тегов и навигации -->
<div class="tags">
<a href="/">Вернуться на главную</a> <a href="/">Вернуться на главную</a>
</div>
</div> </div>
</body>
{% endblock %} </html>

View File

@@ -1,31 +1,44 @@
{% extends "base.html" %} <!DOCTYPE html>
<html lang="ru">
{% block Title %}500: Ошибка сервера{% endblock %} <head>
<meta charset="utf-8">
{% block CONTENT %}{% include "blocks/header_nav.html" %} <meta name="viewport" content="width=device-width, initial-scale=1">
<div class="container main-content"> <title>500: Ошибка сервера DicQuo</title>
<!-- Основной контент: Текст + Картинка --> <style>
<div class="content-row"> body { margin: 0; min-height: 100vh; background-color: #211; color: silver; font-family: serif; opacity: 1; transition: opacity 0.9s ease-in-out; }
header { display: flex; justify-content: space-between; align-items: center; padding: 1vh 4vw; }
<!-- Текстовая колонка --> header > #logo { margin-top: 1vh; float: left; }
<div class="text-col"> header > #logo a { border: none; text-decoration: none; color: silver;}
<!-- Цитата --> main { padding: 1vh 8vw; display: flex; flex-direction: column; justify-content: center; min-height: 60vh; }
<blockquote id="bb" style="border:none; margin:0; padding:0;"> main > article { display: flex; align-items: center; justify-content: center; gap: 2vw; }
<span style="margin-left:-0.44em;">&laquo;</span>Что-то пошло не&nbsp;так. Кажется, я&nbsp;уронил сервер. Подождите, пока я&nbsp;его подниму.» 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: 8vmin; font-style: italic; display: block; }
.tags { text-align: center; color: silver; font-size: 1.5vmin; line-height: 1.9vmin; padding: 1vh 8vw; float: left;}
.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: 2vmin; }
.tags a:hover { color: white; background-position: 0 100%; border-bottom: solid 1px white; }
</style>
</head>
<body>
<header>
<div id="logo">
<a href="/" style="font-size: 3em; font-weight: bold; color: orange; font-style: italic">DQ</a>
</div>
</header>
<main>
<article>
<figure>
<p>Неожиданно:</p>
<blockquote id="bb">
<span style="margin-left:-0.44em;">&laquo;</span>Что-то пошло не&nbsp;так. Кажется, я&nbsp;уронил сервер.
Подождите, пока я&nbsp;его подниму.»
</blockquote> </blockquote>
<!-- Автор -->
<div id="author">
<cite>Системный Администратор (500)</cite> <cite>Системный Администратор (500)</cite>
</div> </figure>
</div> </article>
</main>
</div> <div class="tags">
<!-- Блок тегов и навигации -->
<div class="tags">
<a href="/">Попробовать обновить страницу</a> <a href="/">Попробовать обновить страницу</a>
</div>
</div> </div>
</body>
{% endblock %} </html>

View File

@@ -1,58 +1,34 @@
<!DOCTYPE html> <!DOCTYPE html>{% load static %}
{% load static %}<html lang="ru"> <html lang="ru">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1"/>
{# SEO & Meta #}<title>{% block Title %}{% endblock %}</title>
<!-- SEO & Meta --> <meta name="description" content="{% block Description %}{% endblock %}"/>
<title>{% block Title %}{% endblock %}</title> <meta name="keywords" content="{% block Keywords %}{% endblock %}"/>
<meta name="description" content="{% block Description %}{% endblock %}" /> <meta name="copyright" content="Sergei Erjemin (дизайн){% block CopyrightAuthor4Meta %}{% endblock %}."/>
<meta name="keywords" content="{% block Keywords %}{% endblock %}" /> <meta name="robots" content="index,follow"/>
<meta name="copyright" content="Sergei Erjemin (дизайн){% block CopyrightAuthor4Meta %}{% endblock %}." /> {# Open Graph / Social Media #}<meta property="og:type" content="article"/>
<meta name="robots" content="index,follow" /> <meta property="og:title" content="{% block OgTitle %}{{ DQ.szContent|truncatechars:85 }}{% endblock %}"/>
<meta property="og:description" content="{% block OgDescription %}{{ DQ.szIntro|default:'' }} {{ DQ.szContent }} {{ AUTHOR.szAuthor|default:'' }}{% endblock %}"/>
<!-- Open Graph / Social Media --> <meta property="og:url" content="{{ request.build_absolute_uri }}"/>
<meta property="og:type" content="article" /> <meta property="og:site_name" content="DicQuo"/>
<meta property="og:title" content="{% block OgTitle %}{{ DQ.szContent|truncatechars:60 }}{% endblock %}" /> {% if IMAGE %}<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{{ IMAGE.url }}"/>{% endif %}
<meta property="og:description" content="{% block OgDescription %}{{ DQ.szIntro|default:'' }} {{ DQ.szContent }} {{ AUTHOR.szAuthor|default:'' }}{% endblock %}" /> {# Шутка #}<meta name="generator" content="Microsoft FrontPage 1.0"/>
<meta property="og:url" content="{{ request.build_absolute_uri }}" /> {# Canonical #}<link rel="canonical" href="{{ request.build_absolute_uri }}"/>
<meta property="og:site_name" content="DicQuo" /> {# Favicons #}<link rel="shortcut icon" type="image/x-icon" href="{% static 'img/favicon.ico' %}"/>
{% if IMAGE %}<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{{ IMAGE.url }}" />{% endif %} <link rel="icon" type="image/png" href="{% static 'img/favicon.png' %}"/>
{# Technical Meta #}<meta http-equiv="Last-Modified" content="{% block Last4Meta %}{% endblock %}"/>
<!-- Technical Meta --> {# CSS #}<link rel="stylesheet" href="{% static 'css/dicquo.css' %}"/>
<meta http-equiv="Last-Modified" content="{% block Last4Meta %}{% endblock %}" /> <noscript><style>body { opacity: 1; }</style></noscript>{# Показать все если JS не поддерживатся #}
<meta name="generator" content="Django" /> {% block ExtraHead %}{# Если нужно что=то добавить в `<head>` #}{% endblock %}
<!-- Favicons -->
<link rel="shortcut icon" type="image/x-icon" href="{% static 'img/favicon.ico' %}" />
<link rel="icon" type="image/png" href="{% static 'img/favicon.png' %}" />
<!-- Styles -->
<link rel="stylesheet" href="{% static 'css/dicquo.css' %}" />
<style>
body {
margin: 0;
min-height: 100vh;
background-color: #111; /* Изначально темный фон */
opacity: 0; /* Скрываем контент до расчета цвета */
transition: opacity 0.9s ease-in-out; /* Очень плавное появление */
}
</style>
<noscript>
<style>body { opacity: 1; }</style>
</noscript>
{% block ExtraHead %}{% endblock %}
</head> </head>
<body> <body>{% if DQ %}
{% if DQ %} {# Этот блок для передачи JavaScript-скрипту `bg-generator.js` текст цитаты ({{ DQ.szContent }}) и скрипт на основе него делает уникальный, но постоянный для этой цитаты фон (градиент) #}<span id="dq-content-raw" style="display:none;">{{ DQ.szContent }} ({% if AUTHOR %}{{ AUTHOR.szAuthorHTML|default:AUTHOR.szAuthor }}{% endif %})</span>{% endif %}
<span id="dq-content-raw" style="display:none;">{{ DQ.szContent }}</span> {% include "blocks/header_nav.html" %}
{% endif %} {% block CONTENT %}{% endblock %}{% if not cookie_accept %}
{% include "blocks/cookie_warning.html" %}{% endif %}
{% block CONTENT %}{% endblock %}
{% include "blocks/counters.html" %}
<script src="{% static 'js/bg-generator.js' %}"></script> <script src="{% static 'js/bg-generator.js' %}"></script>
{% include "blocks/counters.html" %}
</body> </body>
</html> </html>

View File

@@ -1,11 +1,6 @@
<!-- ПОДВАЛ: НАЧАЛО -- соглашение о сборе технической информации --> <footer data-nosnippet>
<div name="cookies_accept"> <!--noindex-->
<small>Тут используют cookie и&nbsp;ведут сбор технических данных о&nbsp;посещениях, потому как без этого <nobr>интернет-сайты</nobr> вообще почти <nobr>не&nbsp;работают&hellip;</small> <small>Следы на песке смывает волна, но цифровые следы (cookie) помогают нам помнить вас, пока вы здесь.</small>
<button onclick="CookieAcceptDate = new Date(); <button>Осознаю</button>
CookieAcceptDate.setTime(CookieAcceptDate.getTime() + 7948800000); <!--/noindex-->
document.cookie = 'cookie_accept=yes;expires=' + CookieAcceptDate; </footer>
document.getElementsByName('cookies_accept')[0].remove();">
Я согласен!
</button></nobr>
</div>
<!-- ПОДВАЛ: КОНЕЦ -->

View File

@@ -1,4 +1,2 @@
<!-- Rating Mail.ru counter --> {% load static %}<script src="{% static 'js/counters.js' %}"></script>
<script type="text/javascript">var _tmr = window._tmr || (window._tmr = []);_tmr.push({id:"1603042",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,"topmailru-code");</script><noscript><div><img src="https://top-fwz1.mail.ru/counter?id=1603042;js=na" style="border:0;position:absolute;left:-9999px;" alt="Top.Mail.Ru" /></div></noscript> <noscript><div><img src="https://top-fwz1.mail.ru/counter?id=3744288;js=na" class="counter-pixel" alt=""/></div><div><img src="https://mc.yandex.ru/watch/106953063" class="counter-pixel" alt="" /></div></noscript>
<!-- //Rating Mail.ru counter -->

View File

@@ -1,25 +1,15 @@
{% load static %} {% load static %}{# ШАПКА #}<header>
<!-- ШАПКА: НАЧАЛО -->
<div class="container">
<header>
<a href="/" id="logo"> <a href="/" id="logo">
<img src='{% static "svgs/dq-logo.svg" %}' alt="Dictum & Quotes" title="Dictum & Quotes" <img src='{% static "svgs/dq-logo.svg" %}' alt="Dictum & Quotes" title="Dictum & Quotes"/>
width="50" height="46"/>
</a> </a>
<div> <nav>
<span id="stats-menu" style="display: none; color: silver; font-size: 0.9em; margin-right: 15px; text-align: right;"> <span id="stats-menu">
<!-- Манифест проекта --> {# Манифест проекта #}<p>
<div style="margin-bottom: 5px; color: #aaa; font-style: italic; max-width: 300px; display: inline-block;"> DicQuo&nbsp;— это коллекция отобранных вручную цитат, оформленных с&nbsp;уважением к&nbsp;типографике.<br/>
Dicquo&nbsp;— это коллекция отобранных вручную цитат, оформленных с&nbsp;уважением к&nbsp;типографике. Место для&nbsp;вдумчивого чтения. Место для&nbsp;вдумчивого&nbsp;чтения.
</div> </p>
<br/> {# МЕНЮ #}{% if ticks %}<i class="stats-icon icon-time" title="Время генерации"></i>{{ ticks|floatformat:3|default:"1,023" }} ms{% endif %}{% if DQ %} <b>|</b> <i class="stats-icon icon-views" title="Просмотры"></i> {{ DQ.iViewCounter }}{% endif %} <a href="/add_quote/" title="Добавить цитату"></a> &nbsp;
<!-- Статистика -->
{% if ticks %}<i class="stats-icon icon-time" title="Время генерации"></i>{{ ticks|floatformat:1 }}ms{% endif %}
{% if DQ %} &nbsp;|&nbsp; <i class="stats-icon icon-views" title="Просмотры"></i>{{ DQ.iViewCounter }}{% endif %}
&nbsp;|&nbsp; <a href="/add_quote/" style="color: silver; text-decoration: none;" title="Добавить цитату"><i class="stats-icon icon-add"></i></a> &nbsp;|&nbsp;
</span> </span>
<a href="#" onclick="var m=document.getElementById('stats-menu'); m.style.display = (m.style.display === 'none' ? 'inline-block' : 'none'); return false;" style="color: silver; text-decoration: none; font-size: 1.2em;"></a> {# БУРГЕР #}<a href="#" onclick="var m=document.getElementById('stats-menu'); m.style.display = (m.style.display === 'none' ? 'inline-block' : 'none'); return false;" title="О проекте"></a>
</div> </nav>
</header> </header>
</div>
<!-- ШАПКА: КОНЕЦ -->

View File

@@ -1,6 +0,0 @@
<!-- ТЕХНИЧЕСКАЯ ИНФОРМАЦИЯ: НАЧАЛО -->
<div style="bottom:0;" class="position-sticky float-right fixed-bottom">
<small style="background:#674376;color: white;font-size: xx-small;"
class="x">&ensp;🕗&nbsp;{{ ticks|stringformat:".6f" }}&thinsp;s&thinsp;<nobr>({% now 'c' %})</nobr>&ensp;</small>
</div>
<!-- ТЕХНИЧЕСКАЯ ИНФОРМАЦИЯ: КОНЕЦ -->

View File

@@ -13,9 +13,8 @@
<!--- ТИТУЛ ---> <!--- ТИТУЛ --->
{% block Title %}{% if AUTHOR %}{{ AUTHOR.szAuthor }} — {% endif %}{{ DQ.szContent|truncatewords:7 }} | Dicquo{% endblock %} {% block Title %}{% if AUTHOR %}{{ AUTHOR.szAuthor }} — {% endif %}{{ DQ.szContent|truncatewords:7 }} | Dicquo{% endblock %}
{% block ExtraHead %} {% block ExtraHead %}<script type="application/ld+json">
<script type="application/ld+json"> {
{
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "Quotation", "@type": "Quotation",
"name": "Цитата #{{ DQ.id }}", "name": "Цитата #{{ DQ.id }}",
@@ -35,62 +34,32 @@
}, },
"dateCreated": "{{ DQ.dtCreated|date:'Y-m-d' }}", "dateCreated": "{{ DQ.dtCreated|date:'Y-m-d' }}",
"dateModified": "{{ DQ.dtEdited|date:'Y-m-d' }}" "dateModified": "{{ DQ.dtEdited|date:'Y-m-d' }}"
} }
</script> </script>{% endblock %}
{% endblock %}
{% block CONTENT %}{% include "blocks/header_nav.html" %} {% block CONTENT %}<main>{# Основной контент: Текст + Картинка #}
<div class="container main-content"> <article>{# Текстовая ряб. Задает высоту двум колонкам: текст и картина #}
<!-- Основной контент: Текст + Картинка --> <figure>{# КОЛОНКА С ТЕКСТОМ #}{% if DQ.szIntroHTML %}
<div class="content-row"> {# Интро/Вступление (например "Вася Пупкин как-то сказа". Может отсутствовать #}<p>{{ DQ.szIntroHTML|safe }}</p>{% endif %}
<blockquote>{{ DQ.szContentHTML|safe }}</blockquote>{% if AUTHOR %}
<!-- Текстовая колонка --> <cite>{# Автор #}{{ AUTHOR.szAuthorHTML|default:AUTHOR.szAuthor|safe }}</cite>{% endif %}
<div class="text-col"> </figure>{% if IMAGE %}
<!-- Интро/Вступление --> <div>{# КОЛОНКА С КАРТИНКОЙ #}
{% if DQ.szIntroHTML %}
<div id="info">{{ DQ.szIntroHTML|safe }}</div>
{% endif %}
<!-- Цитата: Семантический blockquote -->
<blockquote id="bb" style="border:none; margin:0; padding:0;">
{{ DQ.szContentHTML|safe }}
</blockquote>
<!-- Автор: Семантический cite -->
<div id="author">
<cite>
{% if AUTHOR %}
{{ AUTHOR.szAuthorHTML|default:AUTHOR.szAuthor|safe }}
{% endif %}
</cite>
</div>
</div>
<!-- Колонка с картинкой (если есть) -->
{% if IMAGE %}
<div class="image-col" id="image">
<center>
<div style="background:rgba(87,0,0,0.7);"> <div style="background:rgba(87,0,0,0.7);">
<div><img src="{{IMAGE.url}}" alt="{% if AUTHOR %}{{ AUTHOR.szAuthor }}{% else %}Dictum & Quotes{% endif %}" title="{% if AUTHOR %}{{ AUTHOR.szAuthor }}{% else %}Dictum & Quotes{% endif %}" /></div> <div>
<img src="{{IMAGE.url}}" alt="{% if AUTHOR %}{{ AUTHOR.szAuthor }}{% else %}Dictum & Quotes{% endif %}" title="{% if AUTHOR %}{{ AUTHOR.szAuthor }}{% else %}Dictum & Quotes{% endif %}" />
</div> </div>
</center>
</div> </div>
{% endif %} </div>{% endif %}
</div> </article>
</main>
<!-- Блок тегов и навигации --> <nav>
<div class="tags"> <div>{# ТЕГИ #}{% for i in TAGS %}
{% for i in TAGS %}<a href="/?tag={{ i.slug }}">{{ i.name|safe }}</a> {% endfor %} <a href="/?tag={{ i.slug }}">{{ i.name|safe }}</a> {% endfor %}
<div id="next"><a href="/{{ NEXT }}_{{ NEXT_TXT }}{% if CURRENT_TAG %}?tag={{ CURRENT_TAG }}{% endif %}">&rightarrow;</a></div> <div id="next"><a href="/{{ NEXT }}_{{ NEXT_TXT }}{% if CURRENT_TAG %}?tag={{ CURRENT_TAG }}{% endif %}">&rightarrow;</a></div>
</div> </div>
</div> </nav>
<noscript>
<noscript>
<meta http-equiv="refresh" content="15; url=/{{ NEXT}}_{{ NEXT_TXT }}{% if CURRENT_TAG %}?tag={{ CURRENT_TAG }}{% endif %}"> <meta http-equiv="refresh" content="15; url=/{{ NEXT}}_{{ NEXT_TXT }}{% if CURRENT_TAG %}?tag={{ CURRENT_TAG }}{% endif %}">
</noscript> </noscript>{% endblock %}
{% if not cookie_accept %}{% include "blocks/cookie_warning.html" %}{% endif %}
{% endblock %}

View File

@@ -1,27 +1,43 @@
<!DOCTYPE html> <!DOCTYPE html>
</html>
</body>
</div>
<a href="/">Вернуться на главную</a>
<cite>Системный Администратор (404)</cite>
</blockquote>
<span>«</span>Я искал везде. Даже под&nbsp;диваном. Этой страницы здесь&nbsp;нет.»
<blockquote>
<div class="container">
<body>
</head>
</style>
a:hover { color: #999; border-bottom: 1px solid #999; }
a { color: #555; text-decoration: none; border-bottom: 1px dotted #555; transition: color 0.3s; font-size: 0.8em; margin-top: 30px; display: inline-block;}
cite { display: block; font-size: 0.9em; color: #777; margin-top: 15px; font-style: normal;}
blockquote span { margin-left: -0.44em; }
blockquote { font-size: 2em; margin: 0 0 20px 0; font-style: italic; line-height: 1.4; }
.container { max-width: 600px; padding: 20px; }
body { background-color: #111; color: #ccc; font-family: Georgia, serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; text-align: center; }
<style>
<title>404: Страница не найдена</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<head>
<html lang="ru"> <html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>404: Страница не найдена | DicQuo</title>
<style>
body { margin: 0; min-height: 100vh; background-color: #211; color: silver; font-family: serif; opacity: 1; transition: opacity 0.9s ease-in-out; }
header { display: flex; justify-content: space-between; align-items: center; padding: 1vh 4vw; }
header > #logo { margin-top: 1vh; float: left; }
header > #logo a { border: none; text-decoration: none; color: silver;}
main { padding: 1vh 8vw; display: flex; flex-direction: column; justify-content: center; min-height: 60vh; }
main > article { display: flex; align-items: center; justify-content: center; gap: 2vw; }
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: 8vmin; font-style: italic; display: block; }
.tags { text-align: center; color: silver; font-size: 1.5vmin; line-height: 1.9vmin; padding: 1vh 8vw; float: left;}
.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: 2vmin; }
.tags a:hover { color: white; background-position: 0 100%; border-bottom: solid 1px white; }
</style>
</head>
<body>
<header>
<div id="logo">
<a href="/" style="font-size: 3em; font-weight: bold; color: orange; font-style: italic">DQ</a>
</div>
</header>
<main>
<article>
<figure>
<p>Озадачено:</p>
<blockquote id="bb">
<span style="margin-left:-0.44em;">&laquo;</span>Я искал везде. Даже под&nbsp;диваном. Этой страницы здесь&nbsp;нет.»
</blockquote>
<cite>Системный Администратор (404)</cite>
</figure>
</article>
</main>
<div class="tags">
<a href="/">Вернуться на главную</a>
</div>
</body>
</html>

View File

@@ -3,25 +3,43 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>500: Ошибка сервера</title> <title>500: Ошибка сервера | DicQuo</title>
<style> <style>
body { background-color: #111; color: #ccc; font-family: Georgia, serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; text-align: center; } body { margin: 0; min-height: 100vh; background-color: #211; color: silver; font-family: serif; opacity: 1; transition: opacity 0.9s ease-in-out; }
.container { max-width: 600px; padding: 20px; } header { display: flex; justify-content: space-between; align-items: center; padding: 1vh 4vw; }
blockquote { font-size: 2em; margin: 0 0 20px 0; font-style: italic; line-height: 1.4; } header > #logo { margin-top: 1vh; float: left; }
blockquote span { margin-left: -0.44em; } header > #logo a { border: none; text-decoration: none; color: silver;}
cite { display: block; font-size: 0.9em; color: #777; margin-top: 15px; font-style: normal; } main { padding: 1vh 8vw; display: flex; flex-direction: column; justify-content: center; min-height: 60vh; }
a { color: #555; text-decoration: none; border-bottom: 1px dotted #555; transition: color 0.3s; font-size: 0.8em; margin-top: 30px; display: inline-block;} main > article { display: flex; align-items: center; justify-content: center; gap: 2vw; }
a:hover { color: #999; border-bottom: 1px solid #999; } 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: 8vmin; font-style: italic; display: block; }
.tags { text-align: center; color: silver; font-size: 1.5vmin; line-height: 1.9vmin; padding: 1vh 8vw; float: left;}
.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: 2vmin; }
.tags a:hover { color: white; background-position: 0 100%; border-bottom: solid 1px white; }
</style> </style>
</head> </head>
<body> <body>
<div class="container"> <header>
<blockquote> <div id="logo">
<span>«</span>Что-то пошло не&nbsp;так. Кажется, я&nbsp;уронил сервер, контейнер или &nbsp;случилось что-то ещё. Подождите, пока я&nbsp;его подниму.» <a href="/" style="font-size: 3em; font-weight: bold; color: orange; font-style: italic">DQ</a>
</blockquote>
<cite>Системный Администратор (5xx)</cite>
<a href="#" onclick="window.location.reload(); return false;">Попробовать обновить через 5&nbsp;минут.</a>
</div> </div>
</header>
<main>
<article>
<figure>
<p>Неожиданно:</p>
<blockquote id="bb">
<span style="margin-left:-0.44em;">&laquo;</span>Что-то пошло не&nbsp;так. Кажется, я&nbsp;уронил сервер.
Подождите, пока я&nbsp;его подниму.»
</blockquote>
<cite>Системный Администратор (500)</cite>
</figure>
</article>
</main>
<div class="tags">
<a href="#" onclick="window.location.reload(); return false;">Попробовать обновить страницу</a>
</div>
</body> </body>
</html> </html>

View File

@@ -15,9 +15,9 @@ services:
# 1. ОБРАЗ # 1. ОБРАЗ
# В продакшене мы используем готовый, собранный образ из реестра (Gitea) # В продакшене мы используем готовый, собранный образ из реестра (Gitea)
# image: git.cube2.ru/e-serg/dicquo:latest image: git.cube2.ru/erjemin/2020-dq:latest
# Но пока, для первого деплоя или если реестра нет, можно собирать локально: # Если образа в gitae нет, то перенести весь код в прод и можно собирать локально:
build: . # build: .
restart: always restart: always
@@ -50,7 +50,8 @@ services:
# 4. Проброс портов (Внешний Nginx -> localhost:8010) # 4. Проброс портов (Внешний Nginx -> localhost:8010)
ports: ports:
- "8010:8000" # Слушаем только на localhost хоста, чтобы закрыть прямой доступ из интернета к Gunicorn
- "127.0.0.1:8010:8000"
# 5. Тома (Volumes) # 5. Тома (Volumes)
volumes: volumes:
@@ -106,7 +107,7 @@ services:
- WATCHTOWER_SCOPE=dq-scope - WATCHTOWER_SCOPE=dq-scope
- WATCHTOWER_CLEANUP=true # Удалять старые образы после обновления - WATCHTOWER_CLEANUP=true # Удалять старые образы после обновления
- WATCHTOWER_POLL_INTERVAL=1800 # Проверять каждые 30 минут - WATCHTOWER_POLL_INTERVAL=1800 # Проверять каждые 30 минут
command: --scope dq-scope - DOCKER_API_VERSION=1.44
logging: logging:
driver: "json-file" driver: "json-file"
options: options:

View File

@@ -1,143 +1,11 @@
@charset "utf-8"; @charset "utf-8";
body {
.tags{ margin: 0;
color: silver; min-height: 100vmin;
font-size:1.5vh; min-width: 100vmin;
line-height:1.9vh; background-color: #111; /* Изначально темный фон */
padding-top: 7vh; opacity: 0; /* Скрываем контент до расчета цвета */
} transition: opacity 0.9s ease-in-out; /* Очень плавное появление */
/*****************************************************************
* Настройки для анимирования цвета ссылок:
* рецепт взят из: 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;
} }
/* Header */ /* Header */
@@ -145,46 +13,101 @@ header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 1vh 0; padding: 1vmin 4vmin;
} }
/* Main Content Area */ header > #logo {
.main-content { margin-top: 1vmin;
display: flex; float: left;
flex-direction: column;
justify-content: center;
min-height: 80vh;
} }
.content-row { header > #logo a {
display: flex; border: none;
align-items: center; text-decoration: none;
justify-content: center;
gap: 2vw;
} }
.text-col { header > #logo a > img {
flex: 1; width:50px;
height:46px;
} }
.image-col { header > nav {
flex: 0 0 30vw; border: #555555;
display: flex; min-height: 50px;
justify-content: center;
} }
/* --- Icons for Header Stats (SVG in Base64) --- */ header > nav > a { /* бургер */
.stats-icon { 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 1vmin;
padding-right: 1vmin;
border-right: 1px dotted silver;
}
header > nav > #stats-menu > i.stats-icon {
display: inline-block; display: inline-block;
width: 0.9em; width: 0.9em;
height: 0.9em; height: 0.9em;
vertical-align: middle; vertical-align: middle;
background-size: contain; background-size: contain;
background-repeat: no-repeat; background-repeat: no-repeat;
margin-right: 0.2em; margin-right: .2em;
opacity: 0.7; /* Slight transparency for subtle look */ 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) */ /* Clock Icon (Time) */
.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"); 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,202 @@ 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"); 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 {
padding: 1vmin 8vmin;
display: flex;
flex-direction: column;
justify-content: center;
min-height: 60vmin;
}
main > article {
display: flex;
align-items: center;
justify-content: center;
gap: 2vmin;
}
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 30vmax;
display: flex;
justify-content: center;
width: 30vmax;
text-align: right;
margin-bottom: 10vmin;
}
main > article > div > div {
width: 26vmax;
height: 26vmax;
padding: 0.5vmin;
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: 1vmin 4vmin;
}
nav > div {
color: silver;
font-size: 1.5vmin;
line-height: 1.9vmin;
padding: 7vmin 0 4vmin 0;
}
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: 2vmin 4vmin;
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: 2vmin;
letter-spacing: 0.05em; /* Немного воздуха в тексте */
}
footer button {
padding: 0.5vmin 1.5vmin;
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) { @media (max-width: 768px) {
.content-row { main > article {
flex-direction: column-reverse; flex-direction: column-reverse;
} }
.image-col {
main > article > div {
flex: 0 0 auto; flex: 0 0 auto;
margin-bottom: 2vh; margin: 8vmin 0 2vmin 0;
}
main > article > div {
width: 80vmin;
}
main > article > div > div {
width: 36vmax;
height: 36vmax;
}
main > article > div > div > div > img {
height: 36vmax;
} }
} }
/* --- ВЫРАВНИВАНИЕ СИМВОЛОВ ВИСЯЧЕЙ ПУНКТУАЦИИ (Hanging Punctuation) ТИПОГРАФА ETPGRF --- */ /* --- ВЫРАВНИВАНИЕ СИМВОЛОВ ВИСЯЧЕЙ ПУНКТУАЦИИ (Hanging Punctuation) ТИПОГРАФА ETPGRF --- */
/* --- ЛЕВЫЕ ВИСЯЧИЕ СИМВОЛЫ (выравнивание по левому краю) --- */ /* --- В ПРОЕКТЕ ТОЛЬКО ЛЕВЫЕ ВИСЯЧИЕ СИМВОЛЫ (выравнивание по левому краю) --- */
.etp-laquo { margin-left: -0.44em; } /* « */ .etp-laquo {margin-left: -0.44em;} /* « */
.etp-ldquo, .etp-bdquo { margin-left: -0.4em; } /* “ „ */ .etp-ldquo, .etp-bdquo { margin-left: -0.4em;} /* “ „ */
.etp-lsquo { margin-left: -0.22em; } /* */ .etp-lsquo {margin-left: -0.22em;} /* */
.etp-lpar, .etp-lsqb, .etp-lcub { margin-left: -0.25em; } /* ( [ { */ .etp-lpar, .etp-lsqb, .etp-lcub {margin-left: -0.25em;}/* ( [ { */
/* --- ПРАВЫЕ ВИСЯЧИЕ СИМВОЛЫ (выравнивание по правому краю) --- */ /* --- СЧЕТЧИКИ (СКРЫТЫЙ ПИКСЕЛЬ) --- */
/* Общая механика: "вырываем" символ из потока для идеального выравнивания текста */ .counter-pixel {
[class^="etp-r"], [class*=" etp-r"] { position: absolute; } border: 0;
/* Точечная настройка смещения для каждого символа */ position: absolute;
.etp-raquo { right: -0.44em; } /* » */ left: -9999px;
.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; } /* . , : */

View File

@@ -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() { 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'); const rawSpan = document.getElementById('dq-content-raw');
let text = rawSpan ? rawSpan.innerText.trim() : ""; let text = rawSpan ? rawSpan.innerText.trim() : "";
if (!text) { if (!text) {
text = "DictumAndQuotesDefault" + Math.random(); // Fallback random if no text text = "DictumAndQuotesDefault" + Math.random(); // Случайный вариант, если текста нет
} }
// 2. Hash function (DJB2) // 2. Хеш-функция (DJB2)
let hash = 5381; let hash = 5381;
for (let i = 0; i < text.length; i++) { for (let i = 0; i < text.length; i++) {
// Force 32-bit integer arithmetic // Принудительная 32-битная целочисленная арифметика
hash = ((hash << 5) + hash) + text.charCodeAt(i); 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 // 3. Детерминированная генерация 6 цветовых компонентов из хеша
// We need 6 numbers between 0 and 255. // Нам нужно 6 чисел от 0 до 255.
// Let's use pseudo-random generator seeded by hash // Используем генератор псевдослучайных чисел с затравкой из хеша
function Mulberry32(a) { function Mulberry32(a) {
return function() { 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 = []; let colors = [];
for(let i=0; i<6; i++) { for(let i=0; i<6; i++) {
// Generate number between 10 and 80 (dark colors) // Генерируем число от 10 до 80 (темные цвета)
colors.push(Math.floor(rand() * 70) + 10); 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 rgb1 = `rgb(${colors[0]}, ${colors[1]}, ${colors[2]})`;
const rgb2 = `rgb(${colors[3]}, ${colors[4]}, ${colors[5]})`; const rgb2 = `rgb(${colors[3]}, ${colors[4]}, ${colors[5]})`;
console.log("DQ BG Generator:", text.substring(0, 20) + "...", hash, rgb1, rgb2); console.log("DQ BG Generator:", text.substring(0, 20) + "...", hash, rgb1, rgb2);
// 4. Apply to body // 4. Применяем к body.
// Using linear-gradient to right with standard syntax // Используем линейный градиент вправо со стандартным синтаксисом
const bgString = `linear-gradient(90deg, ${rgb1} 0%, ${rgb2} 100%)`; const bgString = `linear-gradient(90deg, ${rgb1} 0%, ${rgb2} 100%)`;
document.body.style.background = bgString; 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'); const imgBgContainer = document.querySelector('.image-col center > div');
if (imgBgContainer) { 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)`; imgBgContainer.style.background = `rgba(${colors[0]}, ${colors[1]}, ${colors[2]}, 0.7)`;
} }
// 6. Reveal content (Fade In effect) // 6. Показываем контент (эффект плавного появления - Fade In)
setTimeout(() => { setTimeout(() => {
document.body.style.opacity = 1; document.body.style.opacity = 1;
}, 50); }, 50);
// 7. Handle Fade Out on link clicks // 7. Обработка плавного исчезновения (Fade Out) при клике по ссылкам
document.body.addEventListener('click', function(e) { document.body.addEventListener('click', function(e) {
// Find if a link was clicked (bubble up) // Ищем, была ли нажата ссылка (всплытие)
const link = e.target.closest('a'); const link = e.target.closest('a');
if (link && link.href && link.target !== '_blank') { if (link && link.href && link.target !== '_blank') {
const hrefAttr = link.getAttribute('href'); const hrefAttr = link.getAttribute('href');
if (hrefAttr && !hrefAttr.startsWith('#') && !link.href.includes('javascript:')) { 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) { if (new URL(link.href).origin === window.location.origin) {
e.preventDefault(); // Stop immediate navigation e.preventDefault(); // Останавливаем немедленный переход
document.body.style.opacity = 0; // Start Fade Out document.body.style.opacity = 0; // Запускаем Fade Out
// Wait for transition (matches CSS transition time 1.5s) // Ждем завершения перехода (соответствует времени CSS transition 0.9s (было 1.5s))
setTimeout(() => { setTimeout(() => {
window.location.href = link.href; window.location.href = link.href;
}, 900); }, 900);
@@ -85,14 +89,28 @@ document.addEventListener("DOMContentLoaded", function() {
} }
}); });
// 8. Auto-redirect ("meditative" slideshow) // 8. Авто-редирект ("медитативное" слайд-шоу)
// Find the NEXT link and simulate a click on it after 15 seconds // Ищем ссылку "ДАЛЕЕ" и симулируем клик по ней через 15 секунд
const nextLink = document.querySelector('#next a'); const nextLink = document.querySelector('#next a');
if (nextLink) { if (nextLink) {
setTimeout(() => { setTimeout(() => {
// Trigger the click event on the link so our handler above (step 7) catches it // Вызываем событие клика по ссылке, чтобы наш обработчик выше (шаг 7) поймал его
// and performs the smooth fade out animation. // и выполнил анимацию плавного исчезновения.
nextLink.click(); nextLink.click();
}, 15000); }, 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();
});
}
}
}); });

View File

@@ -0,0 +1,28 @@
// 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");
// Yandex.Metrika counter
(function(m,e,t,r,i,k,a){
m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
m[i].l=1*new Date();
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)
})(window, document,'script','https://mc.yandex.ru/metrika/tag.js?id=106953063', 'ym');
ym(106953063, 'init', {ssr:true, webvisor:true, clickmap:true, ecommerce:"dataLayer", referrer: document.referrer, url: location.href, accurateTrackBounce:true, trackLinks:true});