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, '<').replace(/>/g, '>'); 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, '<') 342 .replace(/>/g, '>'); 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 → 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 → 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(" | "); 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 → 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}"e=${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(/"/g, '"') 1420 .replace(/'/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.