Дата: Пятница, 24.10.2025, 19:39 | Сообщение # 1 |
|
Написал: Начинающий
Автор темы
Мурчанн
не в сети
Сообщений: 101
В конструкцию Парсера внесены небольшие изменения , внешний облик получил лёгкие штрихи обновления, а общий дизайн уверенно сместился в сторону планшетного формата.Управляем через глобальный блок. Парсер горячих тем. Код
<!-- <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, чтобы поднять выше */ } </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> <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>
Признаюсь, не знаю почему, но глядя на звезды мне всегда хочется мечтать.