Список пользователей

1
Админ
Постов: 101
2
Элита
Постов: 34
3
Элита
Постов: 28
4
VIP
Постов: 26
5
Дизайнер
Постов: 25
6
Пользователи
Постов: 25
7
Пользователи
Постов: 24
8
Проверенные
Постов: 21

  • Страница 1 из 1
  • 1
Парсер горячих тем - на главную планшета со стилиусом UCOZ
Дата: Пятница, 24.10.2025, 23:04 | Сообщение # 1 | | Написал: Начинающий
Автор темы
Мурчанн не в сети
        Сообщений:101
         Регистрация:20.10.2016

Легкая имитация планшета со стилусом. Это вторая версия модификации парсера. По сути, парсер скрыт за стильным планшетом, а сам скрипт остаётся лёгким и простым: он работает автономно и создаёт визуальные эффекты без какой-либо серверной или сторонней поддержки. Если кого-то раздражает частое обновление, интервал можно легко изменить , например, установить обновление раз в 6 часов.

В принципе, можно реализовать скрытое обновление парсера, при котором пользователь ничего не заметит визуально.
Это называется «скрытая подгрузка с плавной подменой контента» или иногда «lazy update».

Идея проста:

Сначала отображается старый контент или заглушка.

Скрипт в фоне загружает новые данные.

Когда всё готово — старый контент мгновенно заменяется новым, без мигания и визуальной перегрузки.

Такое поведение легко реализуется с помощью CSS и JavaScript.



Код
<!-- <body> -->
<section class="sect flex-grow-1">
<div class="sect__header d-flex">
<h2 class="sect__title flex-grow-1">Горячая тема</h2>

<style>
/* Неоновая сиреневая обёртка */
.nf-dark-border {
position: relative;
border: 1px solid #9D4EDD;
padding: 30px;
background-color: #0a0710;
color: #fff;
width: 850px;
margin: 40px auto;
border-radius: 12px;
box-shadow:
0 0 25px rgba(157, 78, 221, 0.7),
0 0 50px rgba(157, 78, 221, 0.5),
inset 0 0 20px rgba(157, 78, 221, 0.25);
animation: nf-neonGlow 3s ease-in-out infinite alternate;
overflow: visible;
}

/* Анимация сияния */
@keyframes nf-neonGlow {
from {
box-shadow:
0 0 15px rgba(157, 78, 221, 0.5),
0 0 30px rgba(157, 78, 221, 0.3),
inset 0 0 12px rgba(157, 78, 221, 0.15);
}
to {
box-shadow:
0 0 35px rgba(157, 78, 221, 0.9),
0 0 70px rgba(157, 78, 221, 0.7),
inset 0 0 25px rgba(157, 78, 221, 0.3);
}
}

/* Контейнер карусели */
.nf-carousel-container {
position: relative;
width: 100%;
overflow: hidden;
}

/* Трек карточек */
.nf-carousel-track {
display: flex;
transition: transform 0.5s ease;
}

/* Карточки */
.nf-card {
flex: 0 0 100%;
background: linear-gradient(145deg, #1c1122, #2a1735);
border-radius: 14px;
overflow: hidden;
display: flex;
text-decoration: none;
color: #fff;
cursor: pointer;
position: relative;
transition: box-shadow 0.3s ease, filter 0.3s ease;
min-height: 250px;
aspect-ratio: 16 / 9;
}

/* Карточка при наведении */
.nf-card:hover {
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.4);
filter: brightness(1.45); /* мягче подсветка */
}

/* Изображение */
.nf-card-img {
width: 55%;
height: 100%;
overflow: hidden;
}

.nf-card-img img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}

.nf-card:hover .nf-card-img img {
transform: scale(1.03);
}

/* Контент карточки */
.nf-card-content {
padding: 25px 30px;
display: flex;
flex-direction: column;
justify-content: flex-start; /* заголовок сверху */
width: 45%;
}

.nf-card-title {
font-weight: bold;
font-size: 26px;
margin-bottom: 20px; /* немного больше пространства */
color: #e0b3ff;
text-shadow: 0 0 10px rgba(157, 78, 221, 0.6);
}

.nf-card-desc {
font-size: 16px;
color: #d8c3f0;
line-height: 1.7;
text-align: justify;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 12; /* длиннее описание */
-webkit-box-orient: vertical;
flex-grow: 1; /* описание заполняет пространство */
}

/* Автор */
.nf-card-meta {
position: absolute;
bottom: 15px;
right: 18px;
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
z-index: 5;
}

.nf-card-meta img {
width: 55px;
height: 55px;
border-radius: 50%;
border: 2px solid #b367ff;
object-fit: cover;
background: #1a0e25;
box-shadow: 0 0 10px rgba(157, 78, 221, 0.6);
}

.nf-card-meta .author-nick {
font-size: 14px;
font-weight: bold;
color: #d7a7ff;
background: rgba(0, 0, 0, 0.45);
padding: 3px 10px;
border-radius: 8px;
backdrop-filter: blur(3px);
text-shadow: 0 0 6px rgba(157, 78, 221, 0.8);
}

/* Бейдж */
.nf-card-badge {
position: absolute;
top: 10px;
left: 10px;
background: linear-gradient(135deg, #b367ff, #7a3dbd);
padding: 5px 9px;
border-radius: 8px;
font-size: 11px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 0.6px;
box-shadow: 0 0 12px rgba(157, 78, 221, 0.6);
}

/* Стрелки */
.nf-carousel-arrow {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 52px;
height: 52px;
background: radial-gradient(circle at center, rgba(157, 78, 221, 0.85), rgba(80, 30, 140, 0.8));
border-radius: 50%;
color: #fff;
font-size: 30px;
text-align: center;
line-height: 52px;
cursor: pointer;
user-select: none;
z-index: 20;
box-shadow: 0 0 20px rgba(157, 78, 221, 0.8), 0 0 35px rgba(157, 78, 221, 0.6);
transition: all 0.3s ease;
}

.nf-carousel-arrow:hover {
background: radial-gradient(circle at center, rgba(180, 100, 255, 0.9), rgba(100, 40, 160, 0.85));
transform: translateY(-50%) scale(1.1);
}

.nf-carousel-arrow:active {
background: rgba(157, 78, 221, 0.7);
}

.nf-carousel-arrow-left {
left: -25px;
}

.nf-carousel-arrow-right {
right: -25px;
}

/* Нижняя панель телевизора */
.nf-tv-bottom {
position: relative;
width: 100%;
height: 40px; /* высота панели */
background-color: #000; /* черная панель */
display: flex;
justify-content: center;
align-items: center;
font-family: 'Arial', sans-serif;
font-weight: bold;
font-size: 18px;
color: #fff;
letter-spacing: 2px;
text-shadow: 0 0 6px rgba(255,255,255,0.5);
border-top: 0px solid rgba(157,78,221,0.5); /* тонкая светящаяся линия сверху */
margin-top: 10px;
border-radius: 0 0 12px 12px; /* скругление как у рамки */
margin-top: 1px; /* или отрицательное значение, например -5px, чтобы поднять выше */
}

.tablet-stylus {
  position: absolute;
  
  /* Горизонтальное положение: left или right */
  left: 280px; ;        /* если хочешь слева */
  right: 100px;      /* если хочешь справа, регулируй значение */

  /* Вертикальное положение */
  top: 30%;          /* центр по высоте */
  transform: translateY(-50%) rotate(0deg) scale(1); /* центровка + угол + масштаб */

  /* Размеры */
  width: 14px;       /* толщина стилуса */
  height: 400px;     /* длина стилуса */

  /* Дизайн */
  background: linear-gradient(180deg, #ffffff, #e6e6e6);
  border-radius: 10px;
  box-shadow:
    inset 0 0 4px rgba(255,255,255,0.7),
    0 0 8px rgba(255,255,255,0.8),
    0 0 12px rgba(255,255,255,0.5);
  z-index: 10;

  /* Дополнительно для регулировки */
  /* margin-left или margin-right можно использовать для точной подстройки */
}

/* Кончик стилуса */
.tablet-stylus::after {
  content: '';
  position: absolute;
  bottom: -6px;
  left: 50%;
  transform: translateX(-50%);
  width: 8px;
  height: 8px;
  background: radial-gradient(circle at center, #ccc 20%, #999 80%);
  border-radius: 50%;
  box-shadow: 0 0 3px rgba(0,0,0,0.4);
}

</style>

<div class="nf-dark-border">
<div class="nf-carousel-container">

<div class="nf-carousel-track" id="nf-forum-cards"></div>

<!-- Нижняя панель телевизора -->
<div class="nf-tv-bottom">
<span>SAMSUNG</span>
</div>

</div>

<div class="nf-carousel-arrow nf-carousel-arrow-left">❮</div>
<div class="nf-carousel-arrow nf-carousel-arrow-right">❯</div>
</div>

<!-- Белый стилус сбоку планшета -->
<div class="tablet-stylus"></div>

<script>
(async function nfForumCarouselHot3_carousel() {
const track = document.querySelector('.nf-carousel-track');
const fallbackContainer = document.querySelector('#nf-forum-cards');
const cardsHost = track || fallbackContainer;
if (!cardsHost) return;

const arrowLeft = document.querySelector('.nf-carousel-arrow-left');
const arrowRight = document.querySelector('.nf-carousel-arrow-right');

const BASE_FORUM_URL = '/forum/0-0-1-34';
const CACHE_KEY = 'nf_forum_hot_3_clean';
const CACHE_TIME = 30 * 60 * 1000;
const MAX_CARDS = 6;

function toAbs(url){
if(!url) return '';
if(url.startsWith('http') || url.startsWith('//')) return url.startsWith('//') ? window.location.protocol + url : url;
return window.location.origin + (url.startsWith('/') ? url : '/' + url);
}

const extra = Array.from(document.querySelectorAll('#nf-forum-cards')).slice(1);
extra.forEach(e => e.remove());

function findHotInDoc(doc) {
const rows = Array.from(doc.querySelectorAll('td.threadNametd, .threadNametd, tr'));
const result = [];

for (let td of rows) {
const badge = td.querySelector('.ipsBadge');
const pages = td.querySelector('.postpSwithces, .postPSwithcesLink');
if (!((badge && /горяч/i.test(badge.textContent || '')) || pages)) continue;

const linkEl = td.querySelector('a.threadLink');
if (!linkEl) continue;

const href = toAbs(linkEl.getAttribute('href') || linkEl.href || '');
const titleEl = linkEl.querySelector('.ipsType_pagetitle') || linkEl;
const title = (titleEl && titleEl.textContent || '').trim() || '(без названия)';

const row = td.closest('tr');
let icon = row ? (row.querySelector('.threadIcoTd img') || row.querySelector('td img')) : null;
icon = icon ? icon.getAttribute('src') || icon.src : '';
icon = toAbs(icon) || '/forumimages/Newsletter-3.png';

let author = '';
const authTd = row ? row.querySelector('.threadAuthTd') : null;
if (authTd) author = authTd.textContent.trim();
if (!author) {
const authorLink = td.querySelector('a[href*="/index/"], .lastPostUser, .postUser, .author');
if (authorLink) author = authorLink.textContent.trim();
}
if (!author) author = 'Аноним';

const viewsEl = row ? row.querySelector('.threadViewTd, .views, .thread-views') : null;
let views = 0;
if (viewsEl) {
const m = (viewsEl.textContent||'').replace(/\s/g,'').match(/(\d{1,})/);
if (m) views = parseInt(m[1],10);
}

result.push({ href, title, author, icon, views });
}

const unique = [];
const seen = new Set();
for (let t of result) {
if (seen.has(t.href)) continue;
seen.add(t.href);
unique.push(t);
if (unique.length >= MAX_CARDS) break;
}
return unique;
}

async function fetchHTML(url) {
const res = await fetch(url, { credentials: 'same-origin' });
return await res.text();
}

async function enrichThread(t) {
try {
const txt = await fetchHTML(t.href);
const parser = new DOMParser();
const doc = parser.parseFromString(txt, 'text/html');

const postEl = doc.querySelector('.post_content, .post_body, .post, .ipsType_richText, .message');
const text = postEl ? (postEl.textContent || '').trim().replace(/\s+/g,' ').slice(0,200) : '';

let img = '';
if (postEl) {
const imgs = Array.from(postEl.querySelectorAll('img'));
for (let im of imgs) {
const src = im.getAttribute('ilo-full-src') || im.getAttribute('data-src') || im.src || '';
if (!src) continue;
if (/avatar|\/forumimages\/|\/template\//i.test(src)) continue;
img = toAbs(src);
break;
}
}

if (!img) img = t.icon;

let author = t.author;
const firstAuthor = doc.querySelector('.postUser, .author, .post-author-name');
if (firstAuthor && firstAuthor.textContent.trim()) author = firstAuthor.textContent.trim();

const avatarEl = doc.querySelector('.ipsUserPhoto, .avatar, .user-avatar img');
let avatar = avatarEl ? (avatarEl.src || '') : '/forumimages/default-avatar.png';
if (avatar && !avatar.startsWith('http')) avatar = window.location.origin + (avatar.startsWith('/') ? avatar : '/' + avatar);

return { ...t, text, img, author, avatar };
} catch (e) {
return { ...t, text: '', img: t.icon, avatar: '/forumimages/default-avatar.png' };
}
}

try {
const cached = localStorage.getItem(CACHE_KEY);
if (cached) {
const data = JSON.parse(cached);
if (Date.now() - data.time < CACHE_TIME) {
renderCarousel(data.cards);
return;
}
}
} catch(e){}

let threads = findHotInDoc(document);
if (!threads.length) {
try {
const html = await fetchHTML(BASE_FORUM_URL);
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
threads = findHotInDoc(doc);
} catch(e){ }
}

if (!threads.length) {
cardsHost.innerHTML = '<div style="color:#fff; padding:12px;">🔥 Нет горячих тем</div>';
return;
}

const cards = await Promise.all(threads.slice(0, MAX_CARDS).map(enrichThread));
try { localStorage.setItem(CACHE_KEY, JSON.stringify({ time: Date.now(), cards })); } catch(e){}

renderCarousel(cards);

function renderCarousel(cards) {
cardsHost.innerHTML = '';
const hostIsTrack = !!track;
const trackEl = hostIsTrack ? cardsHost : document.createElement('div');
if (!hostIsTrack) {
trackEl.className = 'nf-carousel-track';
cardsHost.appendChild(trackEl);
}

cards.forEach(c => {
const a = document.createElement('a');
a.className = 'nf-card';
a.href = c.href;
a.target = '_self';
a.style.flex = '0 0 100%';
a.style.boxSizing = 'border-box';

a.innerHTML = `
<div class="nf-card-img">
<img src="${c.img}" alt="${escapeHtml(c.title)}">
<div class="nf-card-badge">🔥 Горячая тема</div>
</div>
<div class="nf-card-content">
<div class="nf-card-title">${escapeHtml(c.title)}</div>
<div class="nf-card-desc">${escapeHtml(c.text || '')}</div>
<div class="nf-card-meta" style="display:flex; align-items:center; gap:8px;">
<img src="${c.avatar}" alt="${escapeHtml(c.author)}" style="width:48px; height:48px; border-radius:50%;">
<span class="author-nick">${escapeHtml(c.author)}</span>
<span class="views-count"> 🔥 ${c.views} просмотров</span>
</div>
</div>
`;
trackEl.appendChild(a);
});

if (!hostIsTrack) {
const wrap = document.querySelector('.nf-carousel-container') || cardsHost;
wrap.innerHTML = '';
wrap.appendChild(trackEl);
}

let index = 0;
const slides = Array.from(trackEl.children);
const total = slides.length;
const visibleWrap = trackEl.closest('.nf-carousel-container') || trackEl.parentElement;

function update() {
const width = visibleWrap.clientWidth || visibleWrap.getBoundingClientRect().width;
const x = -index * width;
trackEl.style.transform = `translateX(${x}px)`;
if (arrowLeft) arrowLeft.style.opacity = index === 0 ? '0.4' : '1';
if (arrowRight) arrowRight.style.opacity = index >= total - 1 ? '0.4' : '1';
}

function setSlideSizes() {
const width = visibleWrap.clientWidth || visibleWrap.getBoundingClientRect().width;
slides.forEach(s => s.style.width = width + 'px');
trackEl.style.display = 'flex';
trackEl.style.transition = 'transform 0.45s ease';
update();
}

window.addEventListener('resize', setSlideSizes);
setSlideSizes();

if (arrowLeft) arrowLeft.onclick = () => { index = Math.max(0, index - 1); update(); };
if (arrowRight) arrowRight.onclick = () => { index = Math.min(total - 1, index + 1); update(); };

let startX = null;
trackEl.addEventListener('touchstart', e => { startX = e.touches[0].clientX; }, {passive:true});
trackEl.addEventListener('touchend', e => {
if (startX === null) return;
const endX = (e.changedTouches && e.changedTouches[0].clientX) || 0;
const dx = endX - startX;
if (Math.abs(dx) > 50) {
index = dx < 0 ? Math.min(total - 1, index + 1) : Math.max(0, index - 1);
update();
}
startX = null;
}, {passive:true});

window.addEventListener('keydown', e => {
if (e.key === 'ArrowLeft') { index = Math.max(0, index - 1); update(); }
if (e.key === 'ArrowRight') { index = Math.min(total - 1, index + 1); update(); }
});
}

function escapeHtml(s){ return String(s || '').replace(/[&<>"']/g, m=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m])); }
})();
</script>

</div>


Используем для удобства глобальные блоки 9

Код

$GLOBAL_FORUMERST$

Мурчанн

Признаюсь, не знаю почему, но глядя на звезды мне всегда хочется мечтать.
  • Страница 1 из 1
  • 1
Поиск: