PageSourceSearch

https://www.prameyanews.com/assets/js/app.js?v=179083741340961790960522

js prameyanews.com collected 2026-10-02 17:02:11 UTC 54,822 bytes, 1,653 lines download raw bytes

1// Pagination Variable Start
2let paginationStates = {
3  "currentPage": 0,
4  "perPage": 0,
5  "total": 0,
6  "lastPage": 0
7};
8
9// ---------------------- GLOBAL DATA FOR CATEGORY ------------------------
10let allCategoryPosts = [], filteredCategoryPosts = [];
11let globalImageUrl = "", globalBaseUrl = "", categoryId = "";
12
13
14
15// *********************************************************************
16// *********************************************************************
17// **********************Start Header Page****************************
18// *********************************************************************
19// *********************************************************************
20
21
22// function loadNavbarCategories(baseUrl) {
23//   $.ajax({
24//     url: "/api/getPublishedCategories",
25//     method: "GET",
26//     dataType: "json",
27
28//     success: function (response) {
29//       if (response.status === 1) {
30//         let categories = response.categories || [];
31//         // let ticker = response.ticker || [];
32
33//         // Group categories by parent_category_id
34//         let categoryMap = {};
35//         categories.forEach(cat => {
36//           let parentId = cat.parent_category_id || 0;
37//           if (!categoryMap[parentId]) categoryMap[parentId] = [];
38//           categoryMap[parentId].push(cat);
39//         });
40
41//         // Start with Home icon
42//         let navHtml = `
43//           <li class="nav-item">
44//             <a href="/" class="nav-link ">
45//               <i class="fas fa-home" ></i>
46//             </a>
47//           </li>`;
48
49//         let parentCategories = categoryMap[0] || [];
50//         let visibleParents = parentCategories.slice(0, 10);
51//         let extraParents = parentCategories.slice(10);
52
53//         // Render first 10 top-level categories
54//         visibleParents.forEach(parentCat => {
55//           let children = categoryMap[parentCat.id] || [];
56
57//           if (children.length > 0) {
58//             navHtml += `
59//               <li class="nav-item dropdown">
60//                 <a href="${baseUrl}/${parentCat.slug}" 
61//                    class="nav-link dropdown-toggle" 
62//                    role="button" 
63//                    data-bs-toggle="dropdown" 
64//                    aria-expanded="false">
65//                   ${parentCat.name}
66//                 </a>
67//                 <ul class="dropdown-menu">
68//                   ${children.map(child => `
69//                     <li>
70//                       <a class="dropdown-item" href="${baseUrl}/${child.slug}">
71//                         ${child.name}
72//                       </a>
73//                     </li>
74//                   `).join("")}
75//                 </ul>
76//               </li>`;
77//           } else {
78//             navHtml += `
79//               <li class="nav-item">
80//                 <a class="nav-link" href="${baseUrl}/${parentCat.slug}">
81//                   ${parentCat.name}
82//                 </a>
83//               </li>`;
84//           }
85//         });
86
87//         // Add remaining categories under "MORE"
88//         if (extraParents.length > 0) {
89//           navHtml += `
90//             <li class="nav-item dropdown">
91//               <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
92//                 MORE
93//               </a>
94//               <ul class="dropdown-menu">
95//                 ${extraParents.map(parentCat => {
96//             let children = categoryMap[parentCat.id] || [];
97
98//             if (children.length > 0) {
99//               return `
100//                       <li class="dropdown-submenu">
101//                         <a class="dropdown-item dropdown-toggle" href="${baseUrl}/${parentCat.slug}">
102//                           ${parentCat.name}
103//                         </a>
104//                         <ul class="dropdown-menu">
105//                           ${children.map(child => `
106//                             <li>
107//                               <a class="dropdown-item" href="${baseUrl}/${child.slug}">
108//                                 ${child.name}
109//                               </a>
110//                             </li>
111//                           `).join("")}
112//                         </ul>
113//                       </li>`;
114//             } else {
115//               return `
116//                       <li>
117//                         <a class="dropdown-item" href="${baseUrl}/${parentCat.slug}">
118//                           ${parentCat.name}
119//                         </a>
120//                       </li>`;
121//             }
122//           }).join("")}
123//               </ul>
124//             </li>`;
125//         }
126
127//         // Inject the generated HTML into the correct <ul> by ID
128//         $("#navbarCategories").html(navHtml);
129
130//         // Optional: render news ticker if applicable
131//         // renderTicker(ticker);
132//       }
133//     },
134
135//     error: function (xhr, status, error) {
136//       console.error("API Error:", error);
137//     }
138//   });
139// }
140
141
142// *********************************************************************
143// *********************************************************************
144// **********************Start Index Page****************************
145// *********************************************************************
146// *********************************************************************
147
148// Format Date helper
149// function formatDate(dateStr) {
150//   const date = new Date(dateStr);
151//   return date.toLocaleDateString("en-US", {
152//     year: "numeric",
153//     month: "long",
154//     day: "numeric"
155//   });
156// }
157
158// Loader Functions
159// function showLoader() {
160//   $(".page-loader").show().removeClass("fade-out");
161// }
162
163// function hideLoader() {
164//   $(".page-loader").addClass("fade-out");
165//   setTimeout(() => {
166//     $(".page-loader").hide();
167//     $("#content-div").fadeIn(400).css("display", "block"); // show content
168//   }, 500); // match fade-out animation
169// }
170
171// Load Categories & Ads
172// function loadCategoriesAndAds(baseUrl2, imageUrl, baseUrl) {
173//   $.ajax({
174//     url: "/api/getPublishedCategoriesWithPosts",
175//     method: "GET",
176//     dataType: "json",
177//     beforeSend: function () {
178//       showLoader();
179//     },
180//     success: function (response) {
181//       if (response.status === 1) {
182//         let ticker = response.ticker || [];
183//         let categories = response.categories || [];
184//         let ads = response.ads || [];
185//         let Videos = response.shortVideos || [];
186
187//         let galary = response.galary || [];
188//         // Render UI
189//         renderSlider(baseUrl2, imageUrl, categories);
190//         renderNewsGrid(categories, baseUrl2, imageUrl, baseUrl, ads);
191//         renderMostReadNews(categories, baseUrl2, imageUrl);
192//         renderAds(ads, imageUrl);
193
194//         renderTicker(ticker)
195//         hideLoader();
196//       }
197//     },
198//     error: function (xhr, status, error) {
199//       console.error("API Error:", error);
200//     },
201//     complete: function () {
202//       hideLoader();
203//     }
204//   });
205// }
206// ======================
207// Function: render slider
208// ======================
209
210// function renderSlider(baseUrl2, imageUrl, topCategories) {
211//   console.log(baseUrl2);
212//   console.log(topCategories);
213
214//   if (!topCategories || !topCategories.length) return;
215
216//   let slideIndex = 0;
217
218//   const slidesHtml = topCategories
219//     .map((cat) => {
220//       if (!cat.posts || !Array.isArray(cat.posts) || cat.posts.length === 0) {
221//         // console.warn(`Category "${cat.name || cat.category_name}" has no posts.`);
222//         return '';
223//       }
224
225//       // Get latest post by publish_datetime
226//       const post = cat.posts
227//         .sort((a, b) => new Date(b.publish_datetime) - new Date(a.publish_datetime)).slice(0, 4);
228
229//       if (!post) {
230//         // console.warn(`No valid post found for category "${cat.name || cat.category_name}"`);
231//         return '';
232//       }
233
234//       const activeClass = slideIndex === 0 ? 'active' : '';
235//       slideIndex++; // only increment for valid slides
236
237//       const imgPath = post.image_path ? `${imageUrl}${post.image_path}` : 'default.jpg';
238
239//       return `
240//         <a href="${baseUrl2}/${post.category_slug}/${post.slug}" class="text-decoration-none">
241//          <div class="carousel-item ${activeClass} position-relative my-clickable">
242//           <img src="${imgPath}" 
243//                class="d-block mx-auto carousel-image" 
244//                alt="${post.title}" 
245//                style="">
246//           <a href="${baseUrl2}/${post.category_slug}/${post.slug}" class="slide-link"></a>
247//           <div class="carousel-caption">
248//             <h6>${post.title}</h6>
249//           </div>
250//         </div>
251//          </a>      `;
252//     })
253//     .join('');
254
255//   const sliderContainer = document.querySelector("#slides");
256//   if (sliderContainer) {
257//     sliderContainer.innerHTML = slidesHtml;
258//   } else {
259//     console.error(`Element with id "slides" not found in DOM.`);
260//   }
261// }
262
263
264
265// function renderSlider(imageUrl, topCategories) {
266//   // Guard: valid input
267//   if (!topCategories || !Array.isArray(topCategories) || topCategories.length === 0) return;
268
269//   // 1) Collect all posts from all categories
270//   let allPosts = [];
271//   for (const cat of topCategories) {
272//     if (cat && Array.isArray(cat.posts)) {
273//       allPosts = allPosts.concat(cat.posts);
274//     }
275//   }
276
277//   // 2) Sort by latest date (desc)
278//   allPosts.sort((a, b) => new Date(b.publish_datetime) - new Date(a.publish_datetime));
279
280//   // 3) Take only the latest 4 posts
281//   const latestPosts = allPosts.slice(0, 4);
282//   console.log(latestPosts);
283
284//   // 4) Build slides
285//   let slideIndex = 0;
286//   const slidesHtml = latestPosts
287//     .map((post) => {
288//       if (!post) return '';
289
290//       const activeClass = slideIndex === 0 ? 'active' : '';
291//       slideIndex++;
292
293//       const imgPath = post.image_path ? `${imageUrl}${post.image_path}` : 'default.jpg';
294//       const postUrl = `${post.slug}`;
295//       const safeTitle = (post.title || '').replace(/</g, '&lt;').replace(/>/g, '&gt;');
296
297//       return `
298//         <div class="carousel-item ${activeClass} position-relative my-clickable">
299//           <a href="${postUrl}" class="stretched-link" aria-label="${safeTitle}"></a>
300//           <img 
301//             src="${imgPath}" 
302//             class="carousel-image" 
303//             alt="${safeTitle}" 
304            
305//           >
306//           <div class="carousel-caption d-none d-md-block">
307//             <h6>${safeTitle}</h6>
308//           </div>
309//         </div>
310//       `;
311//     })
312//     .join('');
313
314//   // 5) Inject into DOM
315//   const sliderContainer = document.querySelector("#slides");
316//   if (sliderContainer) {
317//     sliderContainer.innerHTML = slidesHtml;
318//   } else {
319//     console.error('Element with id "slides" not found in DOM.');
320//   }
321// }
322
323
324function renderSlider(imageUrl, sliderPosts) {
325
326    if (!sliderPosts || sliderPosts.length === 0) return;
327
328    let slideIndex = 0;
329
330    const slidesHtml = sliderPosts.map((post) => {
331
332        const activeClass = slideIndex === 0 ? 'active' : '';
333        slideIndex++;
334
335        const imgPath = post.image_path
336            ? `${imageUrl}${post.image_path}`
337            : 'default.jpg';
338
339        const postUrl = `${post.slug}`;
340        const safeTitle = (post.title || '')
341            .replace(/</g, '&lt;')
342            .replace(/>/g, '&gt;');
343
344        return `
345           <div class="carousel-item ${activeClass} position-relative my-clickable">
346          <a href="${postUrl}" class="stretched-link" aria-label="${safeTitle}"></a>
347           <img 
348             src="${imgPath}" 
349             class="carousel-image" 
350            alt="${safeTitle}" 
351            
352         >
353         <div class="carousel-caption d-none d-md-block">
354             <h6>${safeTitle}</h6>
355          </div>
356        </div>
357        `;
358    }).join('');
359
360    document.querySelector('#slides').innerHTML = slidesHtml;
361}
362
363
364
365
366// ======================
367// Function: Render Most Read News from Category JSON
368// ======================
369//   <img src="${ads[0].image_url ?? "https://via.placeholder.com/300x600?text=Advertisement"}" alt="Ad">
370function renderMostReadNews(categories, baseUrl, imageUrl) {
371  if (!categories || !categories.length) {
372    $("#most-read-news").html("<li class='text-muted'>No news available</li>");
373    return;
374  }
375
376  // Flatten all posts across categories
377  let allPosts = [];
378  categories.forEach(cat => {
379    if (cat.posts && cat.posts.length) {
380      allPosts = allPosts.concat(cat.posts);
381    }
382  });
383
384  if (!allPosts.length) {
385    $("#most-read-news").html("<li class='text-muted'>No posts found</li>");
386    return;
387  }
388
389  // Sort by publish_datetime (latest first)
390  allPosts.sort((a, b) => new Date(b.publish_datetime) - new Date(a.publish_datetime));
391
392  let sidebarHtml = ``;
393  allPosts.forEach(news => {
394    sidebarHtml += `
395         <div class="col-md-6">
396                <a href="${baseUrl}${news.slug}" class="text-decoration-none">
397                  <article class="ar-news-box">
398                    <div class="col-4 news-img-container">
399                      <img src="${imageUrl}${news.image_path ?? "img/no-image.jpg"}" alt="${news.alt_text || "news"}">
400                    </div>
401                    <div class="col-8 news-text-container">
402                      <h3> ${news.title.substring(0, 50)}...</h3>
403                      <time class="news-date"> ${new Date(news.publish_datetime).toLocaleDateString("en-US", {
404      year: 'numeric', month: 'long', day: 'numeric'
405    })}</time>
406                    </div>
407                  </article>
408                </a>
409              </div>
410        `;
411  });
412
413  $("#most-read-news").html(sidebarHtml);
414}
415// ======================
416// Function: Render Column Category (News Card style)
417// Function: Render News Grid (distributes categories to sidebars)
418// ======================
419function renderNewsGrid(categories, baseUrl2, imageUrl, baseUrl, ads) {
420  // Define sidebars (easy to extend later)
421  const sidebars = ["first", "second", "third"];
422
423  // Clear all sidebars before rendering (including rest)
424  ["odisha", ...sidebars, "rest"].forEach(slug => {
425    $(`#${slug}-sidebar`).html('');
426  });
427
428  // Collect categories in one pass
429  let odishaCat = null;
430  const otherCats = [];
431
432  categories.forEach(cat => {
433    if (!cat.posts || !cat.posts.length) return;
434    if (cat.slug === "odisha") {
435      odishaCat = cat;
436    } else {
437      otherCats.push(cat);
438    }
439  });
440
441  // Render Odisha sidebar
442  if (odishaCat) {
443    renderCatSidebar(odishaCat, baseUrl2, imageUrl, baseUrl, "#odisha-sidebar");
444  }
445
446  // Assign first 3 categories to first/second/third
447  const usedCats = otherCats.slice(0, sidebars.length);
448  usedCats.forEach((cat, idx) => {
449    renderCatSidebar(cat, baseUrl2, imageUrl, baseUrl, `#${sidebars[idx]}-sidebar`);
450  });
451
452  // Remaining categories → append into "rest-sidebar"
453  const restCats = otherCats.slice(sidebars.length);
454  console.log(restCats);
455  restCats.forEach(cat => {
456    renderCatrestSidebar(cat, baseUrl2, imageUrl, baseUrl, "#rest-sidebar");
457  });
458
459  // --- NEW: ensure odisha sidebar expands to fit content (no visible scrollbar) ---
460  // Call after rendering is complete so DOM nodes exist
461  adjustSidebarToContent("#odisha-sidebar .odisha-scrollable");
462}
463
464/**
465 * Measure the rendered content height and set the container height to that
466 * so no internal scrollbar appears. If the content is taller than maxAllowedHeight,
467 * we clamp to that value — change maxAllowedHeight as needed or set Infinity.
468 *
469 * @param {string} selector - selector for the scrollable container inside the sidebar
470 * @param {number} [maxAllowedHeight=600] - optional clamp in pixels (set to Infinity to disable)
471 */
472function adjustSidebarToContent(selector, maxAllowedHeight = 400) {
473  const $container = $(selector);
474  if (!$container.length) return;
475
476  // Allow layout to settle (images might still be loading). Use requestAnimationFrame for minimal delay.
477  requestAnimationFrame(() => {
478    // get the full content height (including overflow)
479    const contentHeight = $container.prop('scrollHeight') || $container[0].scrollHeight || 0;
480
481    // Decide final height (clamped)
482    const finalHeight = Math.min(contentHeight, maxAllowedHeight);
483
484    // Apply styles to remove scrollbar and expand to fit content (or clamp)
485    $container.css({
486      'height': finalHeight + 'px',
487      'max-height': 'none',
488      'overflow': 'visible'  // visible prevents scrollbars from showing
489    });
490
491    // If content was clamped and actually higher than finalHeight, we may choose to
492    // show a "More" button or fade — but per your request we don't show a scrollbar.
493    // If you prefer to allow hidden overflow but still hide the scrollbar, set:
494    // 'overflow': 'hidden' and maybe add a "Show more" control.
495  });
496}
497
498// ======================
499// Function: Render a Sidebar Category (News Card style)
500// ======================
501function renderCatSidebar(cat, baseUrl2, imageUrl, baseUrl, containerSelector) {
502  let html = `
503    <div class="card mb-3">
504      <div class="card-header">${cat.name}</div>
505      <div class="card-body p-2">`;
506
507  cat.posts
508    .sort((a, b) => new Date(b.publish_datetime) - new Date(a.publish_datetime))
509    .slice(0, 4) // show first 4 posts
510    .forEach(post => {
511      html += `
512        <a href="${baseUrl2}${post.slug}" class="text-decoration-none">
513          <article class="row news-box mb-2">
514            <div class="col-4">
515              <img class="img-fluid rounded" src="${imageUrl}${post.image_path ?? 'img/no-image.jpg'}" alt="${post.alt_text ?? 'news'}">
516            </div>
517            <div class="col-8 news-text-container">
518              <h3 class="h6 mb-1">${post.title.substring(0, 50)}...</h3>
519              <time class="news-date small text-muted">
520                ${post.publish_datetime
521          ? new Date(post.publish_datetime).toLocaleDateString("en-US", {
522            year: 'numeric', month: 'long', day: 'numeric'
523          })
524          : ''}
525              </time>
526            </div>
527          </article>
528        </a>`;
529    });
530
531  // "More" link
532  html += `
533        <a href="${baseUrl}/${cat.slug}" class="more-link">
534          More ${cat.name} News &rarr;
535        </a>
536      </div>
537    </div>`;
538
539  // Append to container (important for #rest-sidebar to stack multiple categories)
540  $(containerSelector).append(html);
541}
542// ======================
543// Function: Render a Sidebar Category (News Card style)
544// ======================
545function renderCatrestSidebar(cat, baseUrl2, imageUrl, baseUrl, containerSelector) {
546  console.log(`cate-${cat}`);
547
548  let html = `<div class="col-md-4">
549    <div class="card mb-3">
550      <div class="card-header">${cat.name}</div>
551      <div class="card-body p-2">`;
552
553  cat.posts
554    .sort((a, b) => new Date(b.publish_datetime) - new Date(a.publish_datetime))
555    .slice(0, 4) // show first 4 posts
556    .forEach(post => {
557      html += `
558        <a href="${baseUrl2}${post.slug}" class="text-decoration-none">
559          <article class="row news-box mb-2">
560            <div class="col-4">
561              <img class="img-fluid rounded" src="${imageUrl}${post.image_path ?? 'img/no-image.jpg'}" alt="${post.alt_text ?? 'news'}">
562            </div>
563            <div class="col-8 news-text-container">
564              <h3 class="h6 mb-1">${post.title.substring(0, 50)}...</h3>
565              <time class="news-date small text-muted">
566                ${post.publish_datetime
567          ? new Date(post.publish_datetime).toLocaleDateString("en-US", {
568            year: 'numeric', month: 'long', day: 'numeric'
569          })
570          : ''}
571              </time>
572            </div>
573          </article>
574        </a>`;
575    });
576
577  // "More" link
578  html += `
579        <a href="${baseUrl}/${cat.slug}" class="more-link">
580          More ${cat.name} News &rarr;
581        </a>
582      </div>
583    </div></div>`;
584
585  // Append to container (important for #rest-sidebar to stack multiple categories)
586  $(containerSelector).append(html);
587}
588// ======================
589// Function: Render Ticker Section (News Card style)
590// ======================
591function renderTicker(tickerData) {
592  const ticker = document.getElementById("ticker");
593  if (!ticker || !tickerData.length) return;
594
595  // join all names with separator
596  const items = tickerData.map(item => item.name).join(" &nbsp; | &nbsp; ");
597  ticker.innerHTML = items;
598}
599
600
601function renderAds(ads, imageUrl) {
602  // Ensure ads is always an array
603  if (!Array.isArray(ads)) {
604    ads = ads ? [ads] : [];
605  }
606  console.log(ads);
607
608  // Example: three containers
609  const adsSlidetop = "adsSlidetop";
610  const adsSlideleft = "adsSlideleft";
611  const adsSlideright = "adsSlideright";
612
613  $.each(ads, function (i, ad) {
614    if (!ad) return;
614 // skip nulls
615
616    if (ad.ad_type === 3 || ad.ad_type === "3") {
617      renderAdsContainer([ad], imageUrl, adsSlideleft);
618    }
619
620    if (ad.ad_type === 2 || ad.ad_type === "2") {
621      renderAdsContainer([ad], imageUrl, adsSlideright);
622    }
623    if (ad.ad_type === 4 || ad.ad_type === "4") {
624      renderAdsContainer([ad], imageUrl, adsSlidetop);
625    }
626  });
627}
628
629
630function renderAdsContainer(ads, imageUrl, containerId) {
631  const $carouselInner = $("#" + containerId);
632  $carouselInner.empty();
633
634  if (!ads || ads.length === 0) {
635    $carouselInner.append(`
636      <div class="carousel-item active">
637        <img src="/assets/images/images-not-found.png" 
638             class="d-block w-100 img-fluid rounded ad-banner-img"
639             alt="Ad Banner Not Found">
640      </div>
641    `);
642    return;
643  }
644
645  $.each(ads, function (index, ad) {
646    const activeClass = index === 0 ? "active" : "";
647    const adUrl = ad.link ? ad.link : "#";
648    const imgPath = ad.image_path
649      ? imageUrl + ad.image_path
650      : "/assets/images/images-not-found.png";
651    const title = ad.image_alt_text || "Advertisement";
652
653    let slide = `
654      <div class="carousel-item ${activeClass}">
655        <img src="${imgPath}" 
656             class="d-block w-100 img-fluid rounded ad-banner-img" 
657             alt="${title}">
658      </div>
659    `;
660
661    if (adUrl && adUrl !== "#") {
662      slide = `
663        <a href="${adUrl}" target="_blank" rel="noopener" class="text-decoration-none">
664          ${slide}
665        </a>
666      `;
667    }
668
669    $carouselInner.append(slide);
670  });
671}
672
673
674// *********************************************************************
675// *********************************************************************
676// **********************Start Category Page****************************
677// *********************************************************************
678// *********************************************************************
679
680
681// ======================
682// Function: Load Category Posts
683// ======================
684function loadCategoryPosts(categoryId1, baseUrl1, imageUrl) {
685  globalBaseUrl = baseUrl1;
686  globalImageUrl = imageUrl;
687  categoryId = categoryId1;
688  loadCategoryPosts1(1);
689}
690
691// Load posts for a specific page
692function loadCategoryPosts1(page = 1) {
693  $.ajax({
694    url: "/api/getPostData",
695    type: "POST",
696    data: { categoryId: categoryId, page: page },
697    headers: { "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content") },
698    beforeSend: showLoader,
699    success: function (res) {
700      if (res.status === 1) {
701        allCategoryPosts = res.data || [];
702        paginationStates = res.pagination; // contains currentPage, total, perPage
703        renderCategoryPosts();
704        renderSidebar(res.category || [], globalBaseUrl, globalImageUrl);
705        renderlatestSidebar(res.category || [], globalBaseUrl, globalImageUrl);
706        // CALL UPDATED: use the exact function name (case-sensitive)
707        renderLatestImage(res.category || [], globalBaseUrl, globalImageUrl);
708        hideLoader()
709      } else {
710        $("#category-posts").html(`<p class="text-center text-muted">No posts found.</p>`);
711        $("#categoryPagination").html("");
712      }
713    },
714    error: function (xhr, status, error) {
715      console.error("Category API Error:", error);
716    },
717    complete: hideLoader
718  });
719}
720
721// Render posts
722function renderCategoryPosts() {
723  const pagePosts = allCategoryPosts;
724  if (!pagePosts || pagePosts.length === 0) {
725    $("#category-posts").html(`<div class="alert alert-info text-center">No posts to display.</div>`);
726    return;
727  }
728
729  let postsHtml = '';
730  pagePosts.forEach((post, index) => {
731    postsHtml += `
732
733<a href="${globalBaseUrl}/${post.category_slug}/${post.slug}">
734          <div class="row g-0">
735            <div class="col-sm-4">
736              <img src="${globalImageUrl}/${post.image_path}" class="img-fluid" alt="Article">
737            </div>
738            <div class="col-sm-8">
739              <div class="card-body">
740                <p class="card-title">${(post.title ?? "")}</p>
741              </div>
742              <small class="text-muted d-block mb-1 ps-3">
743                ${new Date(post.publish_datetime).toLocaleDateString("en-US", { month: "long", day: "
743numeric", year: "numeric" })}
744              </small>
745            </div>
746          </div>
747          </a>
748        <hr>
749      `;
750  });
751
752  $("#category-posts").html(postsHtml);
753  paginationStates.lastPage = Math.ceil(paginationStates.total / paginationStates.perPage);
754  renderCategoryPagination();
755}
756
757
758
759function renderCategoryPagination() {
760  const { currentPage, lastPage } = paginationStates;
761  const container = document.getElementById("categoryPagination");
762  container.innerHTML = "";
763
764  if (!container || lastPage <= 1) return;
765
766  // Config: max number of visible page *buttons* (not counting Prev/Next/First/Last)
767  // Must be at least 5 to allow first, last and ellipses logic to make sense.
768  const maxVisible = 7;
769
770  // Helper to create an li element string
771  const pageItem = (label, page, opts = {}) => {
772    const disabled = opts.disabled ? "disabled" : "";
773    const active = opts.active ? "active" : "";
774    const ariaCurrent = opts.active ? ' aria-current="page"' : "";
775    // when disabled we still render a link but prevent navigation via href="#" and do nothing on click
776    if (disabled) {
777      return `<li class="page-item ${disabled} ${active}"><a class="page-link" href="#" tabindex="-1"${ariaCurrent}>${label}</a></li>`;
778    }
779    return `<li class="page-item ${active}"><a class="page-link" href="#" onclick="changeCategoryPage(${page}); return false;"${ariaCurrent}>${label}</a></li>`;
780  };
781
782  // FIRST
783  container.innerHTML += pageItem("First", 1, { disabled: currentPage === 1 });
784
785  // PREV
786  container.innerHTML += pageItem("Prev", Math.max(1, currentPage - 1), { disabled: currentPage === 1 });
787
788  // If number of pages is small -> show all
789  if (lastPage <= maxVisible) {
790    for (let i = 1; i <= lastPage; i++) {
791      container.innerHTML += pageItem(i, i, { active: currentPage === i });
792    }
793  } else {
794    // We will always show: 1, (maybe ...), a window, (maybe ...), lastPage
795    const leftSiblingCount = Math.floor((maxVisible - 3) / 2); // reserve spots for first,last and at least one possible ellipsis
796    const rightSiblingCount = leftSiblingCount;
797
798    let start = currentPage - leftSiblingCount;
799    let end = currentPage + rightSiblingCount;
800
801    // clamp to valid range (2 .. lastPage-1) because we always show 1 and lastPage separately
802    if (start < 2) {
803      end += (2 - start);
804      start = 2;
805    }
806    if (end > lastPage - 1) {
807      start -= (end - (lastPage - 1));
808      end = lastPage - 1;
809    }
810    start = Math.max(2, start);
811    end = Math.min(lastPage - 1, end);
812
813    // page 1 (already shown? we included First/Prev but still show numeric)
814    container.innerHTML += pageItem(1, 1, { active: currentPage === 1 });
815
816    // left ellipsis if needed
817    if (start > 2) {
818      container.innerHTML += `<li class="page-item disabled"><a class="page-link" href="#" tabindex="-1">…</a></li>`;
819    }
820
821    // middle window
822    for (let i = start; i <= end; i++) {
823      container.innerHTML += pageItem(i, i, { active: currentPage === i });
824    }
825
826    // right ellipsis if needed
827    if (end < lastPage - 1) {
828      container.innerHTML += `<li class="page-item disabled"><a class="page-link" href="#" tabindex="-1">…</a></li>`;
829    }
830
831    // last page
832    container.innerHTML += pageItem(lastPage, lastPage, { active: currentPage === lastPage });
833  }
834
835  // NEXT
836  container.innerHTML += pageItem("Next", Math.min(lastPage, currentPage + 1), { disabled: currentPage === lastPage });
837
838  // LAST
839  container.innerHTML += pageItem("Last", lastPage, { disabled: currentPage === lastPage });
840}
841
842
843function changeCategoryPage(page) {
844  if (page < 1 || page > paginationStates.lastPage) return;
845  loadCategoryPosts1(page);
846}
847
848// Sidebar renderer
849function renderSidebar(categoryData, baseUrl1, imageUrl) {
850  let sidebarHtml = "";
851  categoryData.forEach((cat, index) => {
852    sidebarHtml += `<div class="card mb-3" >
853    <div class="card-header">${cat.name}</div>
854          <div class="card-body p-2">
855      
856          ${cat.posts && cat.posts.length > 0 ? cat.posts.map(sidePost =>
857
858      `
859            <a href="${baseUrl1}${sidePost.slug}" class="text-decoration-none">
860              <article class="row news-box mb-2">
861                <div class="col-4">
862                  <img class="img-fluid rounded" src="${imageUrl}${sidePost.image_path}" alt="">
863                </div>
864                <div class="col-8 news-text-container">
865                  <h3 class="h6 mb-1">${sidePost.title}</h3>
866                  <time class="news-date small text-muted">
867                   ${new Date(sidePost.publish_datetime).toLocaleDateString("en-US", { month: "long", day: "numeric", year: "numeric" })}
868                  </time>
869                </div>
870              </article>
871            </a>
872         
873          `).join("") : `<p class="text-muted">No related posts.</p>`}
874        <a href="/category/${cat.slug}" class="more-link">
875          More ${cat.name} News &rarr;
876        </a>
877      </div></div>`;
878  });
879  $("#sidebar-container").html(sidebarHtml);
880}
881
882
883function renderlatestSidebar(categories, baseUrl, imageUrl) {
884  if (!categories || !categories.length) {
885    $("#most-read-news").html("<li class='text-muted'>No news available</li>");
886    return;
887  }
888
889  // Flatten all posts across categories
890  let allPosts = [];
891  categories.forEach(cat => {
892    if (cat.posts && cat.posts.length) {
893      allPosts = allPosts.concat(cat.posts);
894    }
895  });
896
897  if (!allPosts.length) {
898    $("#most-read-news").html("<li class='text-muted'>No posts found</li>");
899    return;
900  }
901
902  // Sort by publish_datetime (latest first)
903  allPosts.sort((a, b) => new Date(b.publish_datetime) - new Date(a.publish_datetime));
904
905  let sidebarHtml = ` <h5>Latest News</h5>`;
906  allPosts.forEach(news => {
907    sidebarHtml += `
908         
909                <a href="${baseUrl}/${news.category_slug}/${news.slug}" class="text-decoration-none">
910                  <article class="ar-news-box">
911                    <div class="col-4 news-img-container">
912                      <img src="${imageUrl}${news.image_path ?? "img/no-image.jpg"}" alt="${news.alt_text || "news"}">
913                    </div>
914                    <div class="col-8 news-text-container">
915                      <h3> ${news.title}</h3>
916                      <time class="news-date"> ${new Date(news.publish_datetime).toLocaleDateString("en-US", {
917      year: 'numeric', month: 'long', day: 'numeric'
918    })}</time>
919                    </div>
920                  </article>
921                </a>
922             
923        `;
924  });
925
926  $("#mostcat-read-news").html(sidebarHtml);
927}
928
929
930
931
932
933function renderLatestImage(categories, baseUrl, imageUrl) {
934  const allPosts = (categories || []).flatMap(cat => {
935    return Array.isArray(cat.posts) ? cat.posts.map(p => ({ ...p, category_slug: cat.slug })) : [];
936  });
937
938  if (!allPosts.length) return;
939
940  // Sort by datetime desc
941  allPosts.sort((a, b) => {
942    const da = new Date(a.publish_datetime || 0).getTime();
943    const db = new Date(b.publish_datetime || 0).getTime();
944    return db - da;
945  });
946
947  // Pick first 5 posts
948  const latest = allPosts.slice(0, 5);
949
950  latest.forEach((news, index) => {
951    const link = `${baseUrl}/${news.category_slug}/${news.slug}`;
952    const img = `${imageUrl}${news.image_path ?? "img/no-image.jpg"}`;
953    const date = new Date(news.publish_datetime).toLocaleDateString("en-US", {
954      year: "numeric",
955      month: "long",
956      day: "numeric"
957    });
958    const tags = (news.categories || []).map(c => `<span class="badge">${c}</span>`).join("");
959    const cardContent = `  <a href="${link}" class="text-decoration-none image-card">
960      <div class="tags">${tags}</div>
961      <img src="${img}" class="w-100 h-100" alt="News Image">
962      <div class="overlay">
963        <h6>${news.title}</h6>
964        ${index === 2 ? "" : `<div class="date">${date}</div>`}
965      </div>
966   </a>  `;
967
968    // Inject into static divs
969    if (index === 0) document.getElementById("large-card").innerHTML = cardContent;
970    else if (index === 1) document.getElementById("medium-card").innerHTML = cardContent;
971    else if (index === 2) document.getElementById("small-card1").innerHTML = cardContent;
972    else if (index === 3) document.getElementById("small-card2").innerHTML = cardContent;
973    else if (index === 4) document.getElementById("small-card3").innerHTML = cardContent;
974  });
975}
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994// *********************************************************************
995// *********************************************************************
996// **********************Start Post Page****************************
997// *********************************************************************
998// *********************************************************************
999
1000// ======================
1001// Function: Load Single Posts
1002// ======================
1003function loadPosts(postdata, catslug, imageUrl, baseUrl3) {
1004  $.ajax({
1005    url: "/api/getSinglePostData",
1006    type: "POST",
1007    data: {
1008      postslug: postdata,
1009      catslug: catslug,
1010    },
1011    headers: {
1012      "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"),
1013    },
1014
1015    beforeSend: function () {
1016      showLoader();
1017    },
1018
1019    success: function (res) {
1020      let post_data = res.data || [];
1021      let post = post_data; // might need [0] depending on API response
1022      let categoryData = res.category || [];
1023      let postsHtml = "";
1024
1025      if (res.status === 1 && post) {
1026        postsHtml += `
1027
1028
1029
1030            <!-- Breadcrumb Card -->
1031            <div class="card mb-3 shadow-sm breadcrumb-card">
1032                <div class="card-body py-2">
1033                    <nav aria-label="breadcrumb" class="small">
1034                        <ol class="breadcrumb mb-0">
1035                            <li class="breadcrumb-item"><a href="/">Home</a></li>
1036                            <li class="breadcrumb-item"><a href="/category/${catslug}">${post.category_name}</a></li>
1037                            <li class="breadcrumb-item active" aria-current="page">
1038                               ${post.title}
1039                            </li>
1040                        </ol>
1041                    </nav>
1042                </div>
1043            </div>
1044
1045            <!-- Post Card -->
1046            <div class="card mb-4 shadow-sm">
1047                <div class="card-body">
1048
1049                    <!-- Article Title -->
1050                    <h3 class="card-title fw-bold mb-3">
1051                        ${post.title}
1052                    </h3>
1053
1054                    <!-- Author + Publish Info -->
1055                    <div class="mb-3 text-muted small d-flex align-items-center flex-wrap">
1056                        <b><span>
1057                                Published By : <strong></strong>
1057 |${new Date(post.publish_datetime).toLocaleString("en-US", {
1058          month: "long",
1059          day: "numeric",
1060          year: "numeric",
1061          hour: "numeric",
1062          minute: "2-digit",
1063          hour12: true
1064        })}
1065                            </span></b>
1066                        <span class="ms-auto">
1067                          
1068                            <i class="bi bi-facebook me-2 text-primary cursor-pointer fs-3" onclick="shareOn('facebook', window.location.href, '${post.title.replace(/'/g, "\\'")}')"></i>
1069                            <i class="bi bi-twitter-x me-2 text-info cursor-pointer fs-3" onclick="shareOn('twitter', window.location.href, '${post.title.replace(/'/g, "\\'")}')"></i>
1070                            <i class="bi bi-whatsapp text-success cursor-pointer fs-3" onclick="shareOn('whatsapp', window.location.href, '${post.title.replace(/'/g, "\\'")}')"></i>
1071                        </span>
1072                    </div>
1073
1074                            
1075                            <div class="row align-items-stretch">
1076                                    <div class="col-md-12">
1077                                        <div class="h-100">
1078                                        <img src="${imageUrl}${post.image_path}" alt="${post.alt_text}" class="w-100 h-100 object-fit-cover rounded">
1079                                        </div>
1080                                    </div>
1081                                    <div class="col-md-12">
1082                                        <div class="p-1 h-100 d-flex flex-column justify-content-between">
1083                                        <p class="mb-0">
1084                                     
1085                                            ${post.description}
1086                                            
1087                                        </p>
1088                                    
1089                                        </div>
1090                                    </div>
1091                </div>
1092            </div>
1093        
1094        `;
1095
1096        renderSidebar(categoryData, baseUrl3, imageUrl);
1097        hideLoader();
1098        // ⚠️ This looks suspicious: sidebarHtml isn’t defined in this function.
1099        // Maybe you meant postsHtml?
1100        $("#single-post").html(postsHtml);
1101      } else {
1102        $("#single-post").html(
1103          '<p class="text-center text-muted">No posts found in this category.</p>'
1104        );
1105      }
1106    },
1107
1108    error: function (xhr, status, error) {
1109      console.error("Category API Error:", error);
1110      $("#single-post").html(
1111        '<p class="text-center text-danger">Something went wrong. Please try again.</p>'
1112      );
1113    },
1114
1115    complete: function () {
1116      hideLoader();
1117    },
1118  });
1119}
1120function loadSeoPosts(postdata, catslug, imageUrl, baseUrl3) {
1121  $.ajax({
1122    url: "/api/getSingleSeoPostData",
1123    type: "POST",
1124    data: {
1125      postslug: postdata,
1126      catslug: catslug,
1127    },
1128    headers: {
1129      "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"),
1130    },
1131
1132    beforeSend: function () {
1133      showLoader();
1134    },
1135
1136    success: function (res) {
1137      let post_data = res.data || [];
1138      let post = post_data; // might need [0] depending on API response
1139      let categoryData = res.category || [];
1140      let postsHtml = "";
1141
1142      if (res.status === 1 && post != "nodata") {
1143        postsHtml += `
1144
1145
1146
1147            <!-- Breadcrumb Card -->
1148            <div class="card mb-3 shadow-sm breadcrumb-card">
1149                <div class="card-body py-2">
1150                    <nav aria-label="breadcrumb" class="small">
1151                        <ol class="breadcrumb mb-0">
1152                            <li class="breadcrumb-item"><a href="/">Home</a></li>
1153                            <li class="breadcrumb-item"><a href="/category/${catslug}">${post.category_name}</a></li>
1154                            <li class="breadcrumb-item active" aria-current="page">
1155                               ${post.title}
1156                            </li>
1157                        </ol>
1158                    </nav>
1159                </div>
1160            </div>
1161
1162            <!-- Post Card -->
1163            <div class="card mb-4 shadow-sm">
1164                <div class="card-body">
1165
1166                    <!-- Article Title -->
1167                    <h3 class="card-title fw-bold mb-3">
1168                        ${post.title}
1169                    </h3>
1170
1171                    <!-- Author + Publish Info -->
1172                    <div class="mb-3 text-muted small d-flex align-items-center flex-wrap">
1173                        <b><span>
1174                                Published By : <strong></strong>
1174 |${new Date(post.publish_datetime).toLocaleString("en-US", {
1175          month: "long",
1176          day: "numeric",
1177          year: "numeric",
1178          hour: "numeric",
1179          minute: "2-digit",
1180          hour12: true
1181        })}
1182                            </span></b>
1183                        <span class="ms-auto">
1184                          
1185                            <i class="bi bi-facebook me-2 text-primary cursor-pointer fs-3" onclick="shareOn('facebook', window.location.href, '${post.title.replace(/'/g, "\\'")}')"></i>
1186                            <i class="bi bi-twitter-x me-2 text-info cursor-pointer fs-3" onclick="shareOn('twitter', window.location.href, '${post.title.replace(/'/g, "\\'")}')"></i>
1187                            <i class="bi bi-whatsapp text-success cursor-pointer fs-3" onclick="shareOn('whatsapp', window.location.href, '${post.title.replace(/'/g, "\\'")}')"></i>
1188                        </span>
1189                    </div>
1190
1191                            <div class="row align-items-stretch">
1192                                    <div class="col-md-12">
1193                                        <div class="h-100">
1194                                        <img src="${imageUrl}${post.image_path}" alt="${post.alt_text}" class="w-100 h-100 object-fit-cover rounded">
1195                                        </div>
1196                                    </div>
1197                                    <div class="col-md-12">
1198                                        <div class="p-3 h-100 d-flex flex-column justify-content-between">
1199                                        <p class="mb-0">
1200                                     
1201                                            ${post.description}
1202                                            
1203                                        </p>
1204                                    
1205                                        </div>
1206                                    </div>
1207                </div>
1208            </div>
1209        
1210        `;
1211
1212        renderSidebar(categoryData, baseUrl3, imageUrl);
1213        hideLoader();
1214        // ⚠️ This looks suspicious: sidebarHtml isn’t defined in this function.
1215        // Maybe you meant postsHtml?
1216        $("#single-post").html(postsHtml);
1217      } else {
1218        postsHtml += `
1219
1220
1221
1222            <!-- Breadcrumb Card -->
1223            <div class="card mb-3 shadow-sm breadcrumb-card">
1224                <div class="card-body py-2">
1225                    <nav aria-label="breadcrumb" class="small">
1226                        <ol class="breadcrumb mb-0">
1227                            <li class="breadcrumb-item"><a href="/">Home</a></li>
1228                            <li class="breadcrumb-item"><a href="/category/${catslug}">${catslug}</a></li>
1229                            <li class="breadcrumb-item active" aria-current="page">
1230                                ${postdata}
1231                            </li>
1232                        </ol>
1233                    </nav>
1234                </div>
1235            </div>
1236
1237            <!-- Post Card -->
1238            <div class="card mb-4 shadow-sm">
1239                <div class="card-body">
1240
1241                    <!-- Article Title -->
1242                    <h3 class="card-title fw-bold mb-3">
1243                        ${postdata.replace(/-/g, " ")}
1244                    </h3>
1245
1246                
1247
1248                              <div class="row align-items-stretch">
1249                                    <div class="col-md-12">
1250                                        <div class="h-100">
1251                                        <img src="assets/images/404.png" alt="${post.alt_text}" class="w-100 h-100 object-fit-cover rounded">
1252                                        </div>
1253                                    </div>
1254                                
1255                </div>
1256            </div>
1257        
1258        `;
1259
1260        renderSidebar(categoryData, baseUrl3, imageUrl);
1261        hideLoader();
1262        // ⚠️ This looks suspicious: sidebarHtml isn’t defined in this function.
1263        // Maybe you meant postsHtml?
1264        $("#single-post").html(postsHtml);
1265      }
1266    },
1267
1268    error: function (xhr, status, error) {
1269      console.error("Category API Error:", error);
1270      $("#single-post").html(
1271        '<p class="text-center text-danger">Something went wrong. Please try again.</p>'
1272      );
1273    },
1274
1275    complete: function () {
1276      hideLoader();
1277    },
1278  });
1279}
1280
1281function shareOn(platform, url, title) {
1282  let shareUrl = "";
1283  let encodedUrl = encodeURIComponent(url);
1284  let encodedTitle = encodeURIComponent(title);
1285
1286  if (platform === "facebook") {
1287    shareUrl = `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}&quote=${encodedTitle}`;
1288  } else if (platform === "twitter") {
1289    shareUrl = `https://twitter.com/intent/tweet?url=${encodedUrl}&text=${encodedTitle}`;
1290  } else if (platform === "whatsapp") {
1291    // WhatsApp expects one encoded string
1292    shareUrl = `https://api.whatsapp.com/send?text=${encodedTitle}%20${encodedUrl}`;
1293  }
1294
1295  window.open(shareUrl, "_blank", "width=600,height=400");
1296}
1297
1298// new js for title length adjustment
1299
1300// new s
1301
1302
1303
1304function loadBudgetPosts(baseUrl, imageUrl, page = 1) {
1305
1306    $.ajax({
1307        url: baseUrl + "/api/budget-posts?page=" + page,
1308        method: "GET",
1309        success: function (response) {
1310
1311            let html = "";
1312
1313            response.data.forEach(post => {
1314
1315                html += `
1316                  <div class="card mb-3 shadow-sm">
1317                    <div class="row g-0">
1318
1319                      <div class="col-md-4">
1320                        <img src="${imageUrl + post.image_path}"
1321                             class="img-fluid rounded-start">
1322                      </div>
1323
1324                      <div class="col-md-8">
1325                        <div class="card-body">
1326                          <a href="/${post.slug}" class="text-decoration-none">
1327                            <h5>${post.title}</h5>
1328                          </a>
1329                        </div>
1330                      </div>
1331
1332                    </div>
1333                  </div>
1334                `;
1335            });
1336
1337            $("#budget-posts").html(html);
1338
1339            // Pagination Build
1340            buildPagination(response, "#budgetPagination", loadBudgetPosts, baseUrl, imageUrl);
1341
1342        }
1343    });
1344}
1345
1346
1347
1348
1349
1350// -----------------------------------------------------
1351// GLOBALS FOR SHORTS + EMBEDDED VIDEO PLAYLISTS
1352// -----------------------------------------------------
1353let shortsPlaylist = [];    // type = 2 (shorts)
1354let embeddedPlaylist = [];  // type = 1 (normal embeds)
1355
1356// -----------------------------------------------------
1357// HELPERS TO PARSE YOUTUBE LINKS
1358// -----------------------------------------------------
1359function extractShortsYouTubeId(permalink) {
1360  if (!permalink) return null;
1361
1362  // Extract iframe src if needed
1363  const srcMatch = permalink.match(/src="([^"]+)"/);
1364  let url = srcMatch ? srcMatch[1] : permalink;
1365
1366  // Support:
1367  // shorts/
1368  // embed/
1369  // watch?v=
1370  // youtu.be/
1371  const match = url.match(
1372    /(?:youtube\.com\/(?:shorts\/|embed\/|watch\?v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/
1373  );
1374
1375  return match ? match[1] : null;
1376}
1377function extractEmbedSrc(permalink) {
1378
1379    if (!permalink) return null;
1380
1381    const srcMatch = permalink.match(/src="([^"]+)"/);
1382
1383    let url = srcMatch ? srcMatch[1] : permalink;
1384
1385    const id = extractYouTubeId(url);
1386
1387    if (!id) return null;
1388
1389    return `https://www.youtube.com/embed/${id}`;
1390}
1391function extractYouTubeId(permalink) {
1392  if (!permalink) return null;
1393
1394  // If iframe code pasted
1395  const srcMatch = permalink.match(/src="([^"]+)"/);
1396  let url = srcMatch ? srcMatch[1] : permalink;
1397
1398  // Support:
1399  // embed/
1400  // shorts/
1401  // watch?v=
1402  // youtu.be/
1403  const match = url.match(
1404    /(?:youtube\.com\/(?:embed\/|shorts\/|watch\?v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/
1405  );
1406
1407  return match ? match[1] : null;
1408}
1409
1410// -----------------------------------------------------
1411// RENDER SHORTS + EMBEDDED LISTS
1412// -----------------------------------------------------
1413window.renderShortVideos = function (shortVideosInput) {
1414  let shortVideos;
1415
1416  // normalise input
1417  if (typeof shortVideosInput === 'string') {
1418    const trimmed = shortVideosInput.trim()
1419      .replace(/&quot;/g, '"')
1420      .replace(/&#039;/g, "'");
1421    const cleaned =
1422      (trimmed.startsWith('"') && trimmed.endsWith('"')) ||
1423        (trimmed.startsWith("'") && trimmed.endsWith("'"))
1424        ? trimmed.slice(1, -1)
1425        : trimmed;
1426    shortVideos = JSON.parse(cleaned);
1427  } else if (Array.isArray(shortVideosInput)) {
1428    shortVideos = shortVideosInput;
1429  } else if (shortVideosInput && typeof shortVideosInput === 'object') {
1430    shortVideos = Array.isArray(shortVideosInput.data)
1431      ? shortVideosInput.data
1432      : [shortVideosInput];
1433  } else {
1434    shortVideos = [];
1435  }
1436
1437  shortsPlaylist = [];
1438  embeddedPlaylist = [];
1439
1440  let htmlShorts = '<div class="carousel rath-shorts">';
1441  let htmlEmbedded = '<div class="video-track rath-videos">';
1442
1443  shortVideos.forEach(video => {
1444    // type 2 → shorts
1445    if (video.type === 2) {
1446      const videoId = extractShortsYouTubeId(video.permalink);
1447      if (!videoId) return;
1448
1449      shortsPlaylist.push(videoId);
1450      const idx = shortsPlaylist.length - 1;
1451
1452      htmlShorts += `
1453               <div class="item short-card">
1454                    <div class="youtube-thumb"
1455                         data-index="${idx}"
1456                         data-type="shorts"
1457                         data-bs-toggle="modal"
1458                         data-bs-target="#videoModal">
1459                        <img src="https://img.youtube.com/vi/${videoId}/hqdefault.jpg"
1460                             alt="${video.title || 'Video Thumbnail'}" />
1461                        <div class="play-button"></div>
1462                    </div>
1463                </div>`;
1464    }
1465    // type 1 → normal embedded
1466    else if (video.type === 1) {
1467      const embedSrc = extractEmbedSrc(video.permalink);
1468      if (!embedSrc) return;
1469
1470      embeddedPlaylist.push(embedSrc);
1471      const idx = embeddedPlaylist.length - 1;
1472      const thumbId = extractYouTubeId(embedSrc);
1473      if (!thumbId) return;
1474      htmlEmbedded += `
1475                <div class="video-card shadow-sm">
1476                    <div class="youtube-thumb"
1477                         data-index="${idx}"
1478                         data-type="embedded"
1479                         data-bs-toggle="modal"
1480                         data-bs-target="#videoModal">
1481                        <img src="https://img.youtube.com/vi/${thumbId}/hqdefault.jpg"
1482                             alt="${video.title || 'Video Thumbnail'}" />
1483                        <div class="play-button"></div>
1484                    </div>
1485                </div>`;
1486    }
1487  });
1488
1489  htmlShorts += '</div>';
1490  htmlEmbedded += '</div>';
1491
1492  $("#shortVideosSectiona").html(htmlShorts);
1493  $("#videosSectiona").html(htmlEmbedded);
1494
1495  bindModalEvents();
1496  initVideoSlider();   // your horizontal slider for .video-card
1497}
1498
1499// -----------------------------------------------------
1500// BIND CLICK HANDLERS FOR MODAL
1501// -----------------------------------------------------
1502function bindModalEvents() {
1503
1504    $(".youtube-thumb").off("click").on("click", function () {
1505
1506        const index = Number($(this).data("index"));
1507        const type = $(this).data("type");
1508
1509        if (type === "shorts") {
1510
1511            playShort(index);
1512
1513        } else {
1514
1515            playEmbedded(index);
1516
1517        }
1518
1519        bootstrap.Modal.getOrCreateInstance(
1520            document.getElementById("videoModal")
1521        ).show();
1522
1523    });
1524
1525    $("#videoModal")
1526        .off("hidden.bs.modal")
1527        .on("hidden.bs.modal", function () {
1528
1529            $("#videoContainer").html("");
1530
1531        });
1532
1533}
1534
1535// -----------------------------------------------------
1536// HORIZONTAL SLIDER FOR .video-card (unchanged logic)
1537// -----------------------------------------------------
1538function initVideoSlider() {
1539
1540    const track = document.querySelector(".video-track");
1541
1542    if (!track) return;
1543
1544    let index = 0;
1545
1546    function getCards() {
1547        return document.querySelectorAll(".video-card");
1548    }
1549
1550    function visibleCards() {
1551        return window.innerWidth <= 768 ? 2 : 4;
1552    }
1553
1554    function updateSlider() {
1555
1556        const cards = getCards();
1557
1558        if (!cards.length) return;
1559
1560        const gap = 16;
1561        const cardWidth = cards[0].offsetWidth + gap;
1562
1563        track.style.transform = `translateX(${-index * cardWidth}px)`;
1564
1565    }
1566
1567    window.nextSlide = function () {
1568
1569        const cards = getCards();
1570
1571        const visible = visibleCards();
1572
1573        if (!cards.length) return;
1574
1575        index = index < cards.length - visible ? index + 1 : 0;
1576
1577        updateSlider();
1578
1579    };
1580
1581    window.prevSlide = function () {
1582
1583        const cards = getCards();
1584
1585        const visible = visibleCards();
1586
1587        if (!cards.length) return;
1588
1589        index = index > 0 ? index - 1 : Math.max(cards.length - visible, 0);
1590
1591        updateSlider();
1592
1593    };
1594
1595    window.addEventListener("resize", updateSlider);
1596
1597    updateSlider();
1598
1599}
1600// -----------------------------------------------------
1601// PLAYERS – THIS IS WHERE ERROR 153 IS FIXED
1602// -----------------------------------------------------
1603
1604function playShort(index) {
1605  const videoContainer = document.getElementById('videoContainer');
1606  if (!videoContainer) return;
1607
1608  const videoId = shortsPlaylist[index];
1609  if (!videoId) return;
1610
1611  const src = `https://www.youtube.com/embed/${videoId}?autoplay=1&mute=1`;
1612
1613  videoContainer.innerHTML = `
1614        <iframe width="100%" height="100%"
1615            src="${src}"
1616            frameborder="0"
1617            allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
1618            referrerpolicy="strict-origin-when-cross-origin"
1619            allowfullscreen>
1620        </iframe>`;
1621}
1622
1623function playEmbedded(index) {
1624
1625    const container = document.getElementById("videoContainer");
1626
1627    if (!container) return;
1628
1629    let url = embeddedPlaylist[index];
1630
1631    if (!url) return;
1632
1633    url = url
1634        .replace("watch?v=", "embed/")
1635        .replace("youtu.be/", "youtube.com/embed/")
1636        .replace("/shorts/", "/embed/");
1637
1638    if (!url.includes("autoplay=1")) {
1639
1640        url += (url.includes("?") ? "&" : "?") + "autoplay=1";
1641
1642    }
1643
1644    container.innerHTML = `
1645        <iframe
1646            width="100%"
1647            height="100%"
1648            src="${url}"
1649            frameborder="0"
1650            allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
1651            allowfullscreen>
1652        </iframe>`;
1653}

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.