PageSourceSearch

https://thelatest.com/

html thelatest.com collected 2026-10-02 14:07:15 UTC 23,322 bytes, 754 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3<head>
4    <meta charset="UTF-8">
5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6    <title>TheLatest.com</title>
7    <link rel="icon" href="/img/favicon.svg" type="image/svg+xml"> 
8    <link rel="stylesheet" href="css/lateststyles.css">
9</head>
10<body>
11<div id="header"></div>
12<!-- end header-->
13
14
15<!-- =============== LOGO + TITLE SECTION =============== -->
16<section class="logo-section">
17<div class="container">
18    <h1 class="title">
19        <span class="black">THE</span>
20        <span class="red">LATEST</span>
21    </h1>
22
23    <!-- SEARCH BOX -->
24<div class="search-box">
25    <input type="text" id="newsSearchInput" placeholder="Search">
26    <div class="icon-wrapper" onclick="searchNews()">
27        <span class="search-icon"></span>
28    </div>
29</div>
30
31<div class="news-results-wrapper">
32  <div id="newsResults" class="news-grid"></div>
33</div>
34</div>
35</section>
36<div class="container">
37<div class="category-section">
38<div class="top-news-containers category-left">
39 <div class="section-header">
40    <h2>Top Stories</h2>
41</div>
42  
43 <!-- dynamic category slider -->
44  <div class="category-wrapper" id="categoryWrapper">
45    <button class="cat-arrow left" id="leftArrow" aria-label="Scroll left">‹</button>
46    <div class="category-slider" id="categorySlider" role="tablist" aria-label="News categories"></div>
47    <button class="cat-arrow right" id="rightArrow" aria-label="Scroll right">›</button>
48  </div>
49
50  <!-- news card -->
51  <div class="news-card" id="newsCard" aria-live="polite">
52    <img class="news-img" id="newsImg" src="img/banner.png" alt="">
53    <div class="news-content">
54      <h3 class="news-title" id="newsTitle"></h3>
55      <p class="news-desc" id="newsDesc"></p>
56      <div class="news-meta" id="newsMeta"></div>
57      <a class="news-link" id="newsLink" href="#" target="_blank" rel="noopener">Read full story →</a>
58    </div>
59  </div>
60
61</div>
62<script>
63/* ===============================
64       CONFIG
65=============================== */
66const RSS_URL = "https://rss.app/feeds/_4MQIsURyoARvPa0M.xml";
67
68/* DOM */
69const categorySlider = document.getElementById("categorySlider");
70const leftArrow = document.getElementById("leftArrow");
71const rightArrow = document.getElementById("rightArrow");
72
73const newsImg  = document.getElementById("newsImg");
74const newsTitle = document.getElementById("newsTitle");
75const newsDesc = document.getElementById("newsDesc");
76const newsMeta = document.getElementById("newsMeta");
77const newsLink = document.getElementById("newsLink");
78
79/* GLOBALS */
80let rssItems = [];
81let categories = [];
82
83/* ===============================
84   Helpers
85=============================== */
86function getDomainKey(urlStr){
87    try{
88        const url = new URL(urlStr);
89        let host = url.hostname.toLowerCase().replace(/^www\./,'');
90        let parts = host.split(".");
91        if(parts.length>=3 && parts.at(-1).length==2){
92            return parts.at(-3);
93        }
94        return parts.at(-2);
95    }catch(e){return "";}
96}
97
98function timeAgo(d){
99    const date = new Date(d);
100    if(isNaN(date)) return "";
101    const diff = Date.now()-date;
102    const mins = Math.floor(diff/60000);
103    if(mins<1) return "Just now";
104    if(mins<60) return mins+" mins ago";
105    const hrs = Math.floor(mins/60);
106    if(hrs<24) return hrs+" hrs ago";
107    const days = Math.floor(hrs/24);
108    return days+" days ago";
109}
110
111function extractImage(item){
112    let media = item.querySelector("media\\:content, enclosure");
113    if(media) return media.getAttribute("url");
114
115    let d = item.querySelector("description")?.textContent || "";
116    let t = document.createElement("div");
117    t.innerHTML = d;
118    let img = t.querySelector("img");
119    return img ? img.src : null;
120}
121
122function extractText(item){
123    let d = item.querySelector("description")?.textContent || "";
124    let t = document.createElement("div");
125    t.innerHTML = d;
126    return t.textContent.trim().replace(/\s+/g," ");
127}
128
129/* ===============================
130   Parse RSS XML
131=============================== */
132function parseRSS(xmlText){
133    const parser = new DOMParser();
134    const xml = parser.parseFromString(xmlText, "text/xml");
135    const items = [...xml.querySelectorAll("item")];
136
137    return items.map(n => {
138        const title = n.querySelector("title")?.textContent.trim() || "";
139        const link  = n.querySelector("link")?.textContent.trim() || "";
140        return {
141            title,
142            link,
143            pubDate: n.querySelector("pubDate")?.textContent || "",
144            domainKey: getDomainKey(link),
145            image: extractImage(n) || "img/banner.png",
146            description: extractText(n)
147        };
148    });
149}
150
151/* ===============================
152   Build Categories
153=============================== */
154function buildCategories(items){
155    const map = new Map();
156    items.forEach(it=>{
157        if(!map.has(it.domainKey)){
158            map.set(it.domainKey, it.domainKey.toUpperCase());
159        }
160    });
161    return [...map.entries()].map(([key,label])=>({key,label}));
162}
163
164/* ===============================
165   Build category buttons
166=============================== */
167function buildCategoryButtons(){
168    categorySlider.innerHTML = "";
169    categories.forEach(cat=>{
170        const btn = document.createElement("button");
171        btn.className="cat-item";
172        btn.dataset.key = cat.key;
173        btn.textContent = cat.label;
174        btn.onclick = ()=>{
175            setActive(btn);
176            loadCategory(cat.key);
177        };
178        categorySlider.appendChild(btn);
179    });
180
181    const first = categorySlider.querySelector(".cat-item");
182    if(first){
183        setActive(first);
184        loadCategory(first.dataset.key);
185    }
186}
187
188/* ===============================
189   Set Active Button (NO vertical scroll)
190=============================== */
191function setActive(btn){
192    categorySlider.querySelectorAll(".cat-item").forEach(b=>b.classList.remove("active"));
193    btn.classList.add("active");
194
195    // Horizontal-only centering (no page jump)
196    categorySlider.scrollTo({
197        left: btn.offsetLeft - categorySlider.offsetWidth / 2 + btn.offsetWidth / 2,
198        behavior: "smooth"
199    });
200}
201
202/* ===============================
203   Load category
204=============================== */
205function loadCategory(key){
206    const filtered = rssItems.filter(it=>it.domainKey === key);
207    if(filtered.length === 0){
208        renderEmpty(key);
209        return;
210    }
211    renderItem(filtered[0]);
212}
213
214function renderItem(it){
215    newsTitle.textContent = it.title;
216    newsDesc.textContent = it.description;
217    newsImg.src = it.image;
218    newsLink.href = it.link;
219    newsMeta.textContent = `${it.domainKey}.com • ${timeAgo(it.pubDate)}`;
220}
221
222function renderEmpty(key){
223    newsTitle.textContent = `No news for ${key}`;
224    newsDesc.textContent = "";
225    newsImg.src = "img/banner.png";
226    newsLink.href="#";
227    newsMeta.textContent="";
228}
229
230/* ===============================
231   ARROW SCROLL (NO scrollIntoView)
232=============================== */
233function scrollCategories(direction){
234    const items = [...document.querySelectorAll(".cat-item")];
235    const activeIndex = items.findIndex(btn => btn.classList.contains("active"));
236
237    let newIndex = activeIndex + direction;
238
239    if(newIndex < 0) newIndex = items.length - 1;
240    if(newIndex >= items.length) newIndex = 0;
241
242    setActive(items[newIndex]);
243    loadCategory(items[newIndex].dataset.key);
244
245    // horizontal-only scroll
246    categorySlider.scrollTo({
247        left: items[newIndex].offsetLeft - categorySlider.offsetWidth / 2 + items[newIndex].offsetWidth / 2,
248        behavior: "smooth"
249    });
250}
251
252/* ===============================
253   Arrow events
254=============================== */
255leftArrow.addEventListener("click", function(e){
256    e.preventDefault();
257    e.stopPropagation();
258    this.blur();
259    scrollCategories(-1);
260});
261
262rightArrow.addEventListener("click", function(e){
263    e.preventDefault();
264    e.stopPropagation();
265    this.blur();
266    scrollCategories(1);
267});
268
269/* ===============================
270   INIT
271=============================== */
272(async function init(){
273    try{
274        const r = await fetch(RSS_URL);
275        const xml = await r.text();
276        rssItems = parseRSS(xml);
277        categories = buildCategories(rssItems);
278        buildCategoryButtons();
279    } catch(err){
280        console.error(err);
281        renderEmpty("ALL");
282    }
283})();
284</script>
284
285
286    <!-- RIGHT (new sidebar video) -->
287            
288  <div class="category-right">
289       <div class="section-header">
290    <h2>The Latest Video</h2>
291</div>
292    <div class="video-slider">
293
294        <button class="video-arrow left">&#8249;</button>
295
296        <div class="video-track">
297            <!-- Slide 001 LATEST VOCAL-->
298            <div class="video-slide">
299            <iframe src="https://www.youtube.com/embed/SBTRrhQzU4k?si=vbZ3xRH-0nwIDjdk" width="640" height="480"></iframe>
300             <h2 class="video-title"  style="text-align:center;">Vokal: What Should Go Inside that Empty Storefront?</h2>
301            </div>
302              <!-- Slide 2 (MP4) JEFF -->
303            <div class="video-slide">
304                <video controls>
305                    <source src="/img/video2.mp4" type="video/mp4">
306                </video>
307                <h2 class="video-title"  style="text-align:center;"> All About THE LATEST</h2>
308            </div>
309            <!-- Slide 001 LATEST ICE -->
310            <div class="video-slide">
311              <iframe src="https://www.youtube.com/embed/69LkGZtYK_I?si=3vxtKh22N4Gxc1FM" width="640" height="480"></iframe>
312               <h2 class="video-title" style="text-align:center;"> ICE in Minneapolis: The Latest</h2>
313            </div>
314               <!-- Slide 001 LATEST EPSTEIN -->
315            <div class="video-slide">
316            <iframe src="https://www.youtube.com/embed/PZZ9neLj2xM?si=6Gg2GuWZc49Rprfb" width="640" height="480"></iframe>
317             <h2 class="video-title"  style="text-align:center;"> Gen Z's Take on Jeffrey Epstein: The Latest</h2>
318            </div>
319                 <!-- Slide 001 LATEST IRAN-->
320            <div class="video-slide">
321           <iframe src="https://www.youtube.com/embed/BW7cHuIXg7Y?si=HDCYf0HYAmlHIyZ6" width="640" height="480"></iframe>
322             <h2 class="video-title"  style="text-align:center;">  Iran: What's Next?</h2>
323            </div>
324                             <!-- Slide 001 LATESTWOMEN HISTORY -->
325            <div class="video-slide">
326           <iframe src="https://www.youtube.com/embed/9ZCSQtCmW9g?si=h52yLE3K4Sm2TZP3" width="640" height="480"></iframe>
327            <h2 class="video-title"  style="text-align:center;"> Women's History: The Latest </h2>
328            </div>
329     </div>
330
331        <button class="video-arrow right">&#8250;</button>
332
333    </div>
334</div>
335
336</div>
337<style>.video-slider {
338    position: relative;
339    width: 100%;
340    overflow: hidden;
341}
342.video-track {
343    display: flex;
344    transition: transform 0.4s ease;
345}
346
347.video-slide {
348    min-width: 100%;
349    box-sizing: border-box;
350}
351
352/* Video + iframe styling */
353.video-slide iframe,
354.video-slide video {
355    width: 100%;
356   height: 380px;
357    border-radius: 10px;
358    object-fit: cover;
359}
360
361/* Arrows */
362.video-arrow {
363    position: absolute;
364    top: 50%;
365    transform: translateY(-50%);
366    background: #ba1717;
367    color: #fff;
368    border: none;
369    width: 40px;
370    height: 40px;
371    border-radius: 50%;
372    cursor: pointer;
373    z-index: 2;
374}
375
376.video-arrow.left {
377    left: 10px;
378}
379
380.video-arrow.right {
381    right: 10px;
382}</style>
383<script>
384const track = document.querySelector('.video-track');
385const slides = document.querySelectorAll('.video-slide');
386let index = 0;
387
388document.querySelector('.video-arrow.right').onclick = () => {
389    if (index < slides.length - 1) {
390        index++;
391        track.style.transform = `translateX(-${index * 100}%)`;
392    }
393};
394
395document.querySelector('.video-arrow.left').onclick = () => {
396    if (index > 0) {
397        index--;
398        track.style.transform = `translateX(-${index * 100}%)`;
399    }
400};
401</script>
401
402<style>/* MAIN LAYOUT */
403.category-section {
404    display: flex;
405    align-items: flex-start; /* important */
406    gap: 30px;
407}
408
409/* LEFT 60% */
410.category-left {
411    width: 50%;
412}
413
414.category-left {
415    flex: 1;
416}
417
418.category-right {
419    flex: 1.2; /* makes video area wider */
420}
421.category-right video {
422    width: 100%;
423    border-radius: 10px;
424    object-fit: cover;
425}
426
427/* Slight adjustment so slider fits nicely */
428.category-wrapper {
429    width: 100%;
430}
431
432/* RESPONSIVE */
433@media (max-width: 768px) {
434    .category-section {
435        flex-direction: column;
436    }
437
438    .category-left,
439    .category-right {
440        width: 100%;
441    }
442}</style>
443<div class="section-header">
444    <h2>Roundup</h2>
445</div>
446
447<rssapp-wall id="_HFJNL4vGeQQxp8mu"></rssapp-wall>
447<script src="https://widget.rss.app/v1/wall.js" type="text/javascript" async></script>
447
448<div class="section-header">
449    <h2>AI news</h2>
450</div>
451
452<rssapp-wall id="_4HRGgAFWTK8t9eeQ"></rssapp-wall>
452<script src="https://widget.rss.app/v1/wall.js" type="text/javascript" async></script>
452
453   <!-- TOP STORIES BLOCK -->
454
455        <div class="section-header">
456            <h2>Gen Z</h2>
457        </div>
458
459     <rssapp-wall id="_7GVgiiUfDYCERtnD"></rssapp-wall>
459<script src="https://widget.rss.app/v1/wall.js" type="text/javascript" async></script>
459
460  
461
462<div class="podcast-container">
463
464    <div class="section-header">
465        <h2>Podcasts</h2>
466    </div>
467
468    <div id="podcast-grid" class="podcast-grid">
469        <!-- Videos will load here -->
470    </div>
471
472    <div class="podcast-footer">
473        <a href="https://www.youtube.com/@ournationalconversation/videos" 
474           target="_blank" class="view-all-btn">
475            View All Episodes →
476        </a>
477    </div>
478
479</div>
480
481
482</div><!-- end container -->
483<div id="footer"></div>
484<script>async function includeHTML(id, file){
485    const el = document.getElementById(id);
486    const html = await fetch(file).then(r => r.text());
487    el.innerHTML = html;
488}
489
490document.addEventListener("DOMContentLoaded", function(){
491    includeHTML("header", "header.html");
492    includeHTML("footer", "footer.html");
493});
494</script>
vendor: 2 bytes, line 494
494
495<script>
496async function loadPodcasts() {
497    try {
498        const rssUrl = "https://rss.app/feeds/_prQqC2BPRFz8BR6o.xml";
499        const response = await fetch(rssUrl);
500        const text = await response.text();
501
502        const xml = new DOMParser().parseFromString(text, "text/xml");
503        const items = [...xml.querySelectorAll("item")].slice(0, 3);
504
505        const container = document.getElementById("podcast-grid");
506
507        items.forEach(item => {
508            const title = item.querySelector("title").textContent;
509            const link = item.querySelector("link").textContent;
510
511            const videoId = link.split("v=")[1];
512
513            const card = document.createElement("div");
514            card.classList.add("podcast-card");
515            card.style.position = "relative";
516
517            const iframe = document.createElement("iframe");
518            iframe.src = `https://www.youtube.com/embed/${videoId}`;
519            iframe.width = "100%";
520            iframe.height = "250";
521            iframe.
521setAttribute("allowfullscreen", "true");
522            iframe.setAttribute("loading", "lazy");
523
524            // critical: disable iframe controls
525            iframe.style.pointerEvents = "none";
526
527            // overlay
528            const overlay = document.createElement("div");
529            overlay.style.position = "absolute";
530            overlay.style.top = "0";
531            overlay.style.left = "0";
532            overlay.style.width = "100%";
533            overlay.style.height = "250px";
534            overlay.style.cursor = "pointer";
535            
536            overlay.addEventListener("click", () => {
537                window.open(link, "_blank");
538            });
539
540            const h3 = document.createElement("h3");
541            h3.textContent = title;
542
543            card.appendChild(iframe);
544            card.appendChild(overlay);
545            card.appendChild(h3);
546
547            container.appendChild(card);
548        });
549
550    } catch (err) {
551        console.error("Podcast Load Error:", err);
552    }
553}
554
555loadPodcasts();
556</script>
vendor: 2 bytes, line 556
556
557<script>
558/* ========= TIME AGO ========= */
559function timeAgo(dateString) {
560    const now = new Date();
561    const past = new Date(dateString);
562    const diff = now - past;
563
564    const min = Math.floor(diff / 60000);
565    const hr = Math.floor(min / 60);
566    const day = Math.floor(hr / 24);
567
568    if (min < 1) return "Just now";
569    if (min < 60) return min + "m";
570    if (hr < 24) return hr + "h";
571    return day + "d";
572}
573
574/* ========= NORMALIZER – remove HTML, CDATA, entities, accents ========= */
575function normalize(str) {
576    return (str || "")
577        .replace(/<!\[CDATA\[(.*?)\]\]>/gs, '$1')
578        .replace(/<[^>]+>/g, "")
579        .replace(/&amp;/g, "&")
580        .replace(/&nbsp;/g, " ")
581        .replace(/&#39;/g, "'")
582        .replace(/&quot;/g, '"')
583        .normalize("NFD").replace(/[\u0300-\u036f]/g, "") // remove accents
584        .replace(/\s+/g, " ")
585        .toLowerCase()
586        .trim();
587}
588
589/* ========= RSS FEEDS ========= */
590const feeds = [
591    "https://rss.app/feeds/_dvflTF17jBYVShP3.xml",
592    "https://rss.app/feeds/_iIjbt3XTnFFpU0Cv.xml",
593     "https://rss.app/feeds/6iOkEIv8cZkmdYOD.xml",
594     "https://rss.app/feeds/tHgBWweO6AKTNDAN.xml",
595    "https://rss.app/feeds/tTWnpqRL1kY8uxZD.xml"
596];
597
598
599/* ========= MAIN SEARCH FUNCTION ========= */
600async function searchNews(limit = 0) {
601    const input = document.getElementById("newsSearchInput");
602    const keyword = normalize(input.value);
603    const resultsDiv = document.getElementById("newsResults");
604
605    if (!keyword) {
606        resultsDiv.innerHTML = "";
607        return;
608    }
609
610    if (!limit) resultsDiv.innerHTML = "Searching...";
611
612    let allItems = [];
613
614    // FETCH ALL FEEDS
615    for (const url of feeds) {
616        try {
617            const res = await fetch(url);
618            const text = await res.text();
619            const parser = new DOMParser();
620            const xml = parser.parseFromString(text, "text/xml");
621            const items = xml.querySelectorAll("item");
622            items.forEach(item => allItems.push(item));
623        } catch (e) {
624            console.log("Feed failed:", url);
625        }
626    }
627
628    let parsed = [];
629
630    allItems.forEach(item => {
631        const title = item.querySelector("title")?.textContent || "";
632        const link = item.querySelector("link")?.textContent || "";
633
634        const descNode = item.querySelector("description")?.textContent || "";
635        const contentNode = item.querySelector("content\\:encoded")?.textContent || "";
636        const fullDescription = descNode + " " + contentNode;
637
638        const pubDate = item.querySelector("pubDate")?.textContent ||
639                        item.querySelector("published")?.textContent || "";
640
641        let image = "";
642        const mediaNode = item.querySelector("media\\:content");
643        if (mediaNode) image = mediaNode.getAttribute("url");
644        if (!image) {
645            const imgMatch = descNode.match(/<img[^>]+src="([^">]+)"/);
646            if (imgMatch) image = imgMatch[1];
647        }
648
649        // EXTRACT SOURCE NAME
650        const source = item.querySelector("dc\\:creator")?.textContent ||
651                       item.querySelector("author")?.textContent ||
652                       (function(){
653                           try { return new URL(link).hostname.replace("www.", ""); }
654                           catch { return "Unknown"; }
655                       })();
656
657        parsed.push({
658            title,
659            link,
660            fullDescription,
661            pubDate: pubDate ? new Date(pubDate).getTime() : 0,
662            image,
663            source
664        });
665    });
666
667    // REMOVE DUPLICATES
668    parsed = parsed.filter((item, index, self) =>
669        index === self.findIndex(t => t.link === item.link)
670    );
671
672    // MULTI-WORD SEARCH
673    const keywords = keyword.split(" ").filter(k => k.length > 0);
674    parsed = parsed.filter(item => {
675        const normTitle = normalize(item.title);
676        const normDesc = normalize(item.fullDescription);
677        return keywords.some(k => normTitle.includes(k) || normDesc.includes(k));
678    });
679
680    // SORT NEWEST FIRST
681    parsed.sort((a, b) => b.pubDate - a.pubDate);
682
683    if (limit) parsed = parsed.slice(0, limit);
684
685    // RENDER RESULTS
686    let html = "";
687    parsed.forEach(item => {
688        html += `
689            <a class="news-card" href="${item.link}" target="_blank">
690                <div class="news-title">${item.title}</div>
691                ${item.image ? `<img src="${item.image}" />` : ""}
692                <div class="news-meta">${item.source} • ${timeAgo(item.pubDate)}</div>
693            </a>
694        `;
695    });
696
697    resultsDiv.innerHTML = html || `<div class="no-results">No news found.</div>`;
698    resizeAllGridItems();
699    watchImagesAndResize();
700}
701
702/* ========= INPUT EVENTS ========= */
703document.addEventListener("DOMContentLoaded", function () {
704    const input = document.getElementById("newsSearchInput");
705    let debounceTimer = null;
706
707    input.addEventListener("keydown", function (e) {
708        if (e.key === "Enter") {
709            e.preventDefault();
710            searchNews();
711        }
712    });
713
714    input.addEventListener("input", function () {
715        clearTimeout(debounceTimer);
716        if (input.value.trim() === "") {
717            document.getElementById("newsResults").innerHTML = "";
718            return;
719        }
720        debounceTimer = setTimeout(() => {
721            searchNews();
722        }, 400);
723    });
724});
725
726/* ========= GRID RESIZE ========= */
727function resizeGridItem(item) {
728    const grid = document.querySelector('.news-grid');
729    if (!grid) return;
730    const rowHeight = parseInt(window.getComputedStyle(grid).getPropertyValue('grid-auto-rows'));
731    const gap = parseInt(window.getComputedStyle(grid).getPropertyValue('gap')) || 0;
732    const itemHeight = item.getBoundingClientRect().height;
733    const span = Math.ceil((itemHeight + gap) / (rowHeight + gap));
734    item.style.gridRowEnd = 'span ' + span;
735}
736
737function resizeAllGridItems() {
738    document.querySelectorAll('.news-card').forEach(item => resizeGridItem(item));
739}
740
741function watchImagesAndResize() {
742    document.querySelectorAll('.news-card img').forEach(img => {
743        if (img.complete) resizeGridItem(img.closest('.news-card'));
744        else {
745            img.addEventListener('load', () => resizeGridItem(img.closest('.news-card')));
746            img.addEventListener('error', () => resizeGridItem(img.closest('.news-card')));
747        }
748    });
749}
750</script>
750
751
752
753</body>
754</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.