1const cdbId = $('meta[name="cdb-id"]').attr('content'); 2const path = $('meta[name="path"]').attr('content'); 3const isLogin = $('meta[name="is-login"]').attr('content'); 4const isSubscriber = $('meta[name="is-subscriber"]').attr('content'); 5const isPartnerIP = $('meta[name="is-partner-ip"]').attr('content'); 6const csrfToken = $('meta[name="csrf-token"]').attr('content'); 7 8// Function to generate a unique random ID 9const generateRandomId = () => { 10 // Using Math.random() and Date.now() to create a unique ID 11 return 'tjp-' + Math.random().toString(36).substr(2, 16) + '-' + Date.now(); 12}; 13 14// Function to retrieve or generate a unique ID from localStorage 15const getRandomId = () => { 16 // Check if unique_id exists in localStorage 17 let unique_id = localStorage.getItem('unique_id'); 18 19 // If unique_id does not exist, generate one and store it 20 if (!unique_id) { 21 unique_id = generateRandomId(); 22 localStorage.setItem('unique_id', unique_id); 23 } 24 25 // Return the unique ID 26 return unique_id; 27}; 28 29// JS FOR PARTNER IP 30$(document).ready(function() { 31 // Get the unique ID from localStorage or generate it if not found 32 let unique_id = getRandomId(); 33 let path = window.location.pathname + window.location.search; 34 35 function fetchData() { 36 $.ajax({ 37 url: `${baseURL}/partner/analytic/store`, 38 data: { 39 _token: csrfToken, 40 unique_id: unique_id, 41 path: path 42 }, 43 method: 'POST', 44 success: function(data) { 45 console.log(data); // Process the data received 46 }, 47 error: function(error) { 48 console.error('Error fetching data:', error); 49 } 50 }); 51 } 52 53 // Function to check if the page is visible 54 function isPageVisible() { 55 return document.visibilityState === 'visible'; 56 } 57 58 // Check if 'is-partner-ip' value is '1' and if the page is visible 59 if (isPartnerIP == '1') { 60 // Call fetchData when the page first loads 61 fetchData(); 62 63 // Run fetchData every 10 seconds (10,000 ms) if the page is visible 64 setInterval(function() { 65 if (isPageVisible()) { 66 fetchData(); 67 } 68 }, 10000); 69 } 70}); 71// END JS FOR PARTNER IP 72 73const isValidEmail = function(email) { 74 // Add your email validation logic here 75 // For simplicity, assuming a basic check for a valid email format 76 const emailRegex = /\S+@\S+\.\S+/; 77 return emailRegex.test(email); 78} 79 80const isEmptyValue = function(value) { 81 return ( 82 value === null || // check for null 83 value === undefined || // check for undefined 84 value === '' || // check for empty string 85 (Array.isArray(value) && value.length === 0) || // check for empty array 86 (typeof value === 'object' && Object.keys(value).length === 0) // check for empty object 87 ); 88} 89 90const isMobile = function() { 91 // Get the user agent string from the browser 92 var userAgent = navigator.userAgent.toLowerCase(); 93 94 // List of keywords indicating mobile devices 95 var mobileKeywords = ['android', 'iphone', 'ipad', 'windows phone', 'blackberry', 'iemobile', 'opera mini', 'mobile', 'tablet']; 96 97 // Variable to store whether the user is on a mobile device or not 98 var isMobile = false; 99 100 // Check if the user agent contains any of the mobile keywords 101 for (var i = 0; i < mobileKeywords.length; i++) { 102 if (userAgent.indexOf(mobileKeywords[i]) !== -1) { 103 isMobile = true; 104 break; 105 } 106 } 107 108 return isMobile; 109} 110 111const toIDR = function(value) { 112 return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); 113} 114 115const displayErrorMessage = function(element, className, message) { 116 element.addClass(className); 117 element.html(message); 118} 119 120const displayToast = function(type, message) { 121 // Define an object mapping types to their corresponding icons 122 const icons = { 123 error: `<i class="fa-solid fa-exclamation"></i>`, 124 success: `<i class="fa-solid fa-check"></i>`, 125 info: `<i class="fa-solid fa-info"></i>`, 126 warning: `<i class="fa-solid fa-exclamation"></i>` // Assuming the warning icon is the same as error, adjust if needed 127 }; 128 129 // Use the type to determine the icon, defaulting to an empty string if the type is unknown 130 const iconHTML = icons[type] || ''; 131 132 // Apply the type-specific classes and content 133 $(".tjp-toast").addClass(`active`); 134 $(".tjp-toast").addClass(`tjp-toast--${type}`); 135 $(".tjp-toast__content-icon").html(iconHTML); 136 $(".tjp-toast__content-info__message").text(message); 137 138 139 setTimeout(() => { 140 $(".tjp-toast").removeClass(`active`); 141 }, 5000); 142} 143 144const moveTabs = function(elementID) { 145 $(".tjp-tabs__nav-item").removeClass("active"); 146 $(`.tjp-tabs__nav-item#${elementID}`).addClass("active"); 147 148 $(".tjp-tabs__content-item").removeClass("active"); 149 $(`.tjp-tabs__content-item#${elementID}`).addClass("active"); 150} 151 152const moveStep = function(elementID) { 153 $('.tjp-step').each(function(index, element) { 154 var $content = $(this).find(".tjp-step__content"); 155 (index === stepIndex) ? $content.slideDown() : $content.slideUp(); 156 }); 157} 158 159const resetTextSize = function() { 160 if ($(window).width() > 768) { 161 $(".tjp-sticky-popup__content-list-btn.text").removeClass("active"); 162 } else { 163 $(".tjp-text-size-list__item").removeClass("active"); 164 bottomPopupTextSizeWrapper.removeClass("active"); 165 166 setTimeout(function() { 167 bottomPopupTextSize.removeClass("active"); 168 }, 500); // Delay matches the transition duration 169 } 170 $(".tjp-single__content").removeClass("tjp-single__content--small"); 171 $(".tjp-single__content").removeClass("tjp-single__content--medium"); 172 $(".tjp-single__content").removeClass("tjp-single__content--large"); 173} 174 175const setTextSize = function(size) { 176 localStorage.setItem("size", size); 177} 178 179const getTextSize = function() { 180 resetTextSize(); 181 182 var size = localStorage.getItem("size"); 183 if (size) { 184 $(".tjp-single__content").addClass(`tjp-single__content--${size}`); 185 if ($(window).width() > 768) { 186 $(`.tjp-sticky-popup__content-list-btn.text-${size}`).addClass("active"); 187 } else { 188 $(`.tjp-text-size-list__item.text-${size}`).addClass("active"); 189 } 190 } else { 191 $(".tjp-single__content").addClass(`tjp-single__content--small`); 192 if ($(window).width() > 768) { 193 $(`.tjp-sticky-popup__content-list-btn.text-small`).addClass("active"); 194 } else { 195 $(`.tjp-text-size-list__item.text-small`).addClass("active"); 196 bottomPopupTextSizeWrapper.removeClass("active"); 197 198 setTimeout(function() { 199 bottomPopupTextSize.removeClass("active"); 200 }, 500); // Delay matches the transition duration 201 } 202 } 203} 204 205const changeTextSize = function(size) { 206 setTextSize(size); 207 getTextSize(); 208} 209 210const copyLink = (element) => { 211 var url = window.location.href; 212 // Copy the text inside the text field 213 navigator.clipboard.writeText(url); 214 215 if ($(window).width() > 768) { 216 stickyPopupLinkNotes.html("Link Copied"); 217 setTimeout(function() { 218 stickyPopupLinkNotes.html(""); 219 }, 1500); 220 } else { 221 $(element).find(".tjp-social-media-list__item-text").html(`<span style="color: green;"><i class="fa-solid fa-circle-check"></i> Link Copied</span>`); 222 223 setTimeout(function() { 224 $(element).find(".tjp-social-media-list__item-text").html(`<span>Copy Link</span>`) 225 }, 1500); 226 } 227} 228 229const btnPopupShare = $(".tjp-sticky-popup--share .tjp-sticky-popup__action"); 230const btnPopupTextSize = $(".tjp-sticky-popup--text-size .tjp-sticky-popup__action"); 231const btnPopupGift = $(".tjp-sticky-popup--gift .tjp-sticky-popup__action"); 232 233const stickyPopupShare = $(".tjp-sticky-popup--share"); 234const stickyPopupTextSize = $(".tjp-sticky-popup--text-size"); 235const stickyPopupGift = $(".tjp-sticky-popup--gift"); 236 237const stickyPopupActionShare = $(".tjp-sticky-popup__content-action-btn"); 238const stickyPopupLinkBtn = $(".tjp-sticky-popup__content-link button"); 239const stickyPopupLinkNotes = $(".tjp-sticky-popup__content-notes"); 240 241const bottomPopupShare = $(".tjp-bottom-popup--share"); 242const bottomPopupShareClose = $(".tjp-bottom-popup--share .tjp-bottom-popup__header-action-close"); 243const bottomPopupShareWrapper = $(".tjp-bottom-popup--share .tjp-bottom-popup__wrapper"); 244const bottomPopupTextSize = $(".tjp-bottom-popup--text-size"); 245const bottomPopupTextSizeClose = $(".tjp-bottom-popup--text-size .tjp-bottom-popup__header-action-close"); 246const bottomPopupTextSizeWrapper = $(".tjp-bottom-popup--text-size .tjp-bottom-popup__wrapper"); 247const bottomPopupGift = $(".tjp-bottom-popup--gift"); 248const bottomPopupGiftClose = $(".tjp-bottom-popup--gift .tjp-bottom-popup__header-action-close"); 249const bottomPopupGiftWrapper = $(".tjp-bottom-popup--gift .tjp-bottom-popup__wrapper"); 250 251const bottomPopupActionShare = $(".tjp-bottom-popup__footer-action-share"); 252const bottomPopupAuthLogin = $(".tjp-sticky-popup__content-action-login"); 253const bottomPopupAuthSubscribe = $(".tjp-sticky-popup__content-action-subscribe"); 254 255btnPopupShare.click(function() { 256 if ($(window).width() > 768) { 257 stickyPopupShare.toggleClass("active"); 258 stickyPopupGift.removeClass("active"); 259 stickyPopupTextSize.removeClass("active"); 260 } else { 261 bottomPopupShare.addClass("active"); 262 bottomPopupShareWrapper.addClass("active"); 263 } 264}); 265 266btnPopupTextSize.click(function() { 267 if ($(window).width() > 768) { 268 stickyPopupTextSize.toggleClass("active"); 269 stickyPopupShare.removeClass("active"); 270 stickyPopupGift.removeClass("active"); 271 } else { 272 bottomPopupTextSize.addClass("active"); 273 bottomPopupTextSizeWrapper.addClass("active"); 274 } 275}) 276 277btnPopupGift.click(function() { 278 if ($(window).width() > 768) { 279 $(this).parent().toggleClass("active"); 280 stickyPopupShare.removeClass("active") 281 stickyPopupTextSize.removeClass("active"); 282 } else { 283 bottomPopupGift.addClass("active"); 284 bottomPopupGiftWrapper.addClass("active"); 285 } 286}) 287 288bottomPopupShare.click(function(event) { 289 // Check if the click was on the background (not on the wrapper) 290 if (!$(event.target).closest(".tjp-bottom-popup__wrapper").length) { 291 bottomPopupShareWrapper.removeClass("active"); 292 293 setTimeout(function() { 294 bottomPopupShare.removeClass("active"); 295 }, 500); // Delay matches the transition duration 296 } else if ($(event.target).closest(".tjp-social-media-list__item").length) { 297 return; 298 } 299}); 300 301bottomPopupShareClose.click(function() { 302 bottomPopupShareWrapper.removeClass("active"); 303 setTimeout(function() { 304 bottomPopupShare.removeClass("active"); 305 }, 500); // Delay matches the transition duration 306}) 307 308bottomPopupTextSize.click(function(event) { 309 // Check if the click was on the background (not on the wrapper) 310 if (!$(event.target).closest(".tjp-bottom-popup__wrapper").length) { 311 bottomPopupTextSizeWrapper.removeClass("active"); 312 313 setTimeout(function() { 314 bottomPopupTextSize.removeClass("active"); 315 }, 500); // Delay matches the transition duration 316 } 317}); 318 319bottomPopupTextSizeClose.click(function() { 320 bottomPopupTextSizeWrapper.removeClass("active"); 321 322 setTimeout(function() { 323 bottomPopupTextSize.removeClass("active"); 324 }, 500); // Delay matches the transition duration 325}) 326 327bottomPopupGift.click(function(event) { 328 // Check if the click was on the background (not on the wrapper) 329 if (!$(event.target).closest(".tjp-bottom-popup__wrapper").length) { 330 bottomPopupGiftWrapper.removeClass("active"); 331 332 setTimeout(function() { 333 bottomPopupGift.removeClass("active"); 334 }, 500); // Delay matches the transition duration 335 } 336}); 337 338bottomPopupGiftClose.click(function() { 339 bottomPopupGiftWrapper.removeClass("active"); 340 341 setTimeout(function() { 342 bottomPopupGift.removeClass("active"); 343 }, 500); // Delay matches the transition duration 344}) 345 346bottomPopupActionShare.click(function() { 347 pushIntoGTMEvents('gift_article_btn_share_click'); 348 bottomPopupShareWrapper.removeClass("active"); 349 350 setTimeout(function() { 351 bottomPopupShare.removeClass("active"); 352 bottomPopupGiftWrapper.addClass("active"); 353 bottomPopupGift.addClass("active"); 354 }, 500); // Delay matches the transition duration 355}) 356 357$(document).click(function(event) { 358 // Check if the clicked element is not inside the shares or text-size containers 359 if (!stickyPopupShare.is(event.target) && stickyPopupShare.has(event.target).length === 0 && 360 !stickyPopupTextSize.is(event.target) && stickyPopupTextSize.has(event.target).length === 0 && 361 !stickyPopupGift.is(event.target) && stickyPopupGift.has(event.target).length === 0) { 362 // Clicked outside of both elements 363 stickyPopupShare.removeClass("active"); 364 stickyPopupTextSize.removeClass("active"); 365 stickyPopupGift.removeClass("active"); 366 } 367}); 368 369// TJP ANALYTICS TRIGGERS 370const shareIntoSocmed = (platform, data) => { 371 switch (platform) { 372 case 'WhatsApp':
373 window.open(`https://web.whatsapp.com/send?text=${data.gift_title} ${data.gift_path}`, '_blank'); 374 break; 375 case 'Facebook': 376 window.open(`https://www.facebook.com/sharer/sharer.php?u=${data.gift_path}`, '_blank'); 377 break; 378 case 'Twitter': 379 somedShare('https://twitter.com/share?text='+data.gift_title+'&url='+data.gift_path, 'tw'); 380 break; 381 case 'LinkedIn': 382 somedShare('http://www.linkedin.com/shareArticle?mini=true&summary='+data.gift_summary+'&title='+data.gift_title+'&url='+data.gift_path, 'pn'); 383 break; 384 case 'Telegram': 385 window.open(`https://telegram.me/share/url?url=${data.gift_path}&text=${data.gift_title}`); 386 break; 387 case 'Email': 388 window.open(`mailto:?subject=${data.gift_title}&body=Check out this article:%0A%0A${data.gift_path}%0A%0A${data.gift_title}`); 389 break; 390 case 'CopyLink': 391 var url = data.gift_path; 392 // Copy the text inside the text field 393 navigator.clipboard.writeText(url); 394 if ($(window).width() > 768) { 395 stickyPopupLinkNotes.html("Link Copied"); 396 setTimeout(function() { 397 stickyPopupLinkNotes.html(""); 398 }, 1500); 399 } else { 400 const element = ".tjp-bottom-popup--gift .tjp-social-media-list__item:nth-child(1)"; 401 $(element).find(".tjp-social-media-list__item-text").html(`<span style="color: green;"><i class="fa-solid fa-circle-check"></i> Link Copied</span>`); 402 403 setTimeout(function() { 404 $(element).find(".tjp-social-media-list__item-text").html(`<span>Copy Link</span>`) 405 }, 1500); 406 } 407 break; 408 default: 409 break; 410 } 411} 412 413$('.tjp-social-media-list__item').click(function(event) { 414 event.stopPropagation(); // Prevent event from bubbling up 415}); 416 417$(".tjp-paywall-gifted-article .action__btn").click(function() { 418 const source = $(this).data("source"); 419 if (source == "gift_article") { 420 pushIntoGTMEvents('gift_article_btn_subscribe_click'); 421 window.location.href = `${baseURL}/subscribe`; 422 } else { 423 pushIntoGTMEvents('ppa_btn_get_unlimited_click'); 424 window.location.href = `${baseURL}/subscribe`; 425 } 426}); 427 428bottomPopupAuthLogin.click(function() { 429 pushIntoGTMEvents('gift_article_btn_authlogin_click'); 430 window.location.href = `${baseURL}/user/account/login`; 431}) 432 433bottomPopupAuthSubscribe.click(function() { 434 pushIntoGTMEvents('gift_article_btn_authsubscribe_click'); 435 window.location.href = `${baseURL}/subscribe`; 436}) 437 438btnPopupGift.click(function() { 439 const path = $(this).data("path"); 440 sendGiftDetailIntoGTMEvent('gift_article_btn_share_click', path); 441}) 442 443stickyPopupActionShare.click(function() { 444 pushIntoGTMEvents('gift_article_btn_share_click'); 445 btnPopupGift.click(); 446}); 447 448// Track untuk tombol Subscribe (buat semua visitor dan user) 449$(document).on('click', '.tjp-btn-subscribe--new', function () { 450 pushIntoGTMEvents('btn_subscribe_new_click'); 451 window.location.href = `${baseURL}/subscribe`; 452}); 453 454// Track untuk tombol Renew (khusus subscriber, dengan userDetails) 455$(document).on('click', '.tjp-btn-subscribe--renew', function () { 456 pushIntoGTMEvents('btn_subscribe_renew_click'); 457 window.location.href = `${baseURL}/subscribe`; 458}); 459 460const giftArticle = (platform, path = '') => { 461 let event = 'gift_article_btn_platform_click'; 462 let pathAPI = window.location.pathname.substring(1); 463 if (path != '') { 464 pathAPI = path; 465 } 466 $.ajax({ 467 type: "POST", 468 url: `${baseURL}/api/gift-article/create`, 469 data: { 470 path: pathAPI 471 }, 472 success: function(response) { 473 const data = response.data; 474 475 $(".tjp-sticky-popup__content-head-number .current").html(data.gift_remaining); 476 $(".tjp-sticky-popup__content-head-number .limit").html(data.gift_maximum); 477 478 shareIntoSocmed(platform, data); 479 sendShareDetailsIntoGTMEvent(event, platform, pathAPI); 480 481 // if (data.gift_remaining == 0) { 482 // $(".tjp-sticky-popup--gift .tjp-sticky-popup__content-list").remove(); 483 // $(".tjp-sticky-popup--gift .tjp-sticky-popup__content-link").remove(); 484 // $(".tjp-sticky-popup--gift .tjp-sticky-popup__content-text").html("You've Shared All Your Articles This Month. Come back next month for more articles to share with everyone."); 485 // } 486 487 }, 488 error: function(){ 489 console.log('No data available'); // Optional: log for debugging 490 } 491 }); 492} 493 494// BOTTOM POPUP OPEN APP 495$(document).ready(function () { 496 const VISIT_COUNT_KEY = 'visitCount'; 497 const LAST_VISITED_PATH_KEY = 'lastVisitedPath'; 498 const POPUP_DISMISSED_KEY = 'popupDismissedTime'; 499 const ONE_MINUTE_MS = 24 * 60 * 60 * 1000; 500 501 const currentPath = window.location.pathname; 502 503 // Get current visit count and last visited path from localStorage 504 let visitCount = parseInt(localStorage.getItem(VISIT_COUNT_KEY)) || 0; 505 const lastVisitedPath = localStorage.getItem(LAST_VISITED_PATH_KEY); 506 507 // Increment visit count only if the current path is different from the last visited path 508 if (currentPath !== lastVisitedPath) {
509 visitCount++; 510 localStorage.setItem(VISIT_COUNT_KEY, visitCount); 511 localStorage.setItem(LAST_VISITED_PATH_KEY, currentPath); // Update the last visited path 512 } 513 514 // Check if popup should be shown 515 const popupDismissedTime = localStorage.getItem(POPUP_DISMISSED_KEY); 516 const isDismissedRecently = popupDismissedTime && (Date.now() - popupDismissedTime < ONE_MINUTE_MS); 517 518 if (visitCount >= 3 && !isDismissedRecently) { 519 sendOpenAppDetailIntoGTMEvent('open_app_popup_show', currentPath.substring(1)); 520 $('.tjp-bottom-popup--open-app').addClass('active'); 521 $('.tjp-bottom-popup--open-app .tjp-bottom-popup__wrapper').addClass('active'); 522 } 523 524 // Handle Open App button click 525 $('.tjp-cta-btn--open-open-app').on('click', function () { 526 pushIntoGTMEvents('open_app_btn_open_click'); 527 528 const ANDROID_APP_URL = "https://play.google.com/store/apps/details?id=com.thejakartapost.ndroid"; 529 const IOS_APP_URL = "https://apps.apple.com/id/app/the-jakarta-post/id1321863437"; 530 531 // Detect the device and set the appropriate URL 532 const userAgent = navigator.userAgent || navigator.vendor || window.opera; 533 let url; 534 535 if (/android/i.test(userAgent)) { 536 url = ANDROID_APP_URL; // Android URL 537 } else if (/iPhone|iPad|iPod/i.test(userAgent)) { 538 url = IOS_APP_URL; // iOS URL 539 } else { 540 url = "https://www.thejakartapost.com/apps/download"; // Default fallback URL 541 } 542 window.location.href = url; 543 }); 544 545 // Handle popup close button click 546 $('.tjp-cta-btn--close-open-app').on('click', function () { 547 $('.tjp-bottom-popup--open-app').removeClass('active'); 548 $('.tjp-bottom-popup--open-app .tjp-bottom-popup__wrapper').removeClass('active'); 549 pushIntoGTMEvents('open_app_btn_close_click'); 550 // Store the dismissal timestamp in localStorage 551 localStorage.setItem(POPUP_DISMISSED_KEY, Date.now().toString()); 552 }); 553 554 $(".tjp-preferred-source-btn").click(function() { 555 pushIntoGTMEvents('set_as_preferred_source_click'); 556 }); 557}); 558 559$(".tjp-account-action").click(function() { 560 561 if ($(".channelHeader").hasClass("fix-head")) { 562 $(".channelHeader .tjp-account-action").toggleClass("active"); 563 $(".channelHeader .tjp-account-nav").slideToggle("slow"); 564 } else { 565 $(".tjp-account-action").toggleClass("active"); 566 $(".tjp-account-nav").slideToggle("slow"); 567 } 568}); 569 570function getSearchInput() { 571 return $(window).width() < 1024 572 ? $(".tjp-Head .tjp-search-form__input input") 573 : $(".channelHeader .tjp-search-form__input input"); 574} 575 576function getSearchPopularSection() { 577 return $(window).width() < 1024 578 ? $(".tjp-Head .tjp-search-result--popular") 579 : $(".channelHeader .tjp-search-result--popular"); 580} 581 582function getSearchTopSection() { 583 return $(window).width() < 1024 584 ? $(".tjp-Head .tjp-search-result--top") 585 : $(".channelHeader .tjp-search-result--top"); 586} 587 588function getSearchResultList() { 589 return $(window).width() < 1024 590 ? $(".tjp-Head .tjp-search-result--top .tjp-search-result__list") 591 : $(".channelHeader .tjp-search-result--top .tjp-search-result__list"); 592} 593 594function toggleSearchBox() { 595 const $searchBox = $(window).width() < 1024 596 ? $(".tjp-Head .tjp-search") 597 : $(".channelHeader .tjp-search"); 598 599 $searchBox.toggleClass("active"); 600} 601 602function clearSearch() { 603 const $searchBox = $(window).width() < 1024 604 ? $(".tjp-Head .tjp-search") 605 : $(".channelHeader .tjp-search"); 606 607 const $input = getSearchInput(); 608 const $resultList = getSearchResultList(); 609 const $sectionTop = getSearchTopSection(); 610 const $sectionPopular = getSearchPopularSection(); 611 612 $searchBox.removeClass("active"); 613 $input.val(""); 614 $resultList.empty(); 615 $sectionTop.hide(); 616 $sectionPopular.show(); // tampilkan popular kembali 617} 618 619function submitSearch() { 620 const query = getSearchInput().val().trim(); 621 622 if (!query) return; 623 624 pushIntoGTMEvents('btn_search_submit_click', { query }); 625 626 const encodedQuery = encodeURIComponent(query); 627 const url = `${baseURL}
627/search?q=${encodedQuery}#gsc.tab=0&gsc.q=${encodedQuery}&gsc.page=1`; 628 629 // Uncomment untuk redirect: 630 window.location.href = url; 631} 632 633function renderSearchResults(data) { 634 const $sectionTop = getSearchTopSection(); 635 const $sectionPopular = getSearchPopularSection(); 636 const $resultList = getSearchResultList(); 637 $resultList.empty(); 638 $sectionPopular.hide(); 639 $sectionTop.show(); 640 641 if (data.length > 0) { 642 data.forEach(item => { 643 $resultList.append(` 644 <div class="tjp-search-result__list-item"> 645 <a href="${baseURL}${item.path}">${item.title}</a> 646 </div> 647 `); 648 }); 649 } else { 650 $resultList.append(` 651 <div class="tjp-search-result__list-item"> 652 <em>No results found. Please check your search term and try again</em> 653 </div> 654 `); 655 } 656} 657 658function handleSearchInput(query) { 659 const $resultList = getSearchResultList(); 660 const $sectionTop = getSearchTopSection(); 661 const $sectionPopular = getSearchPopularSection(); 662 663 if (!query) { 664 console.log(query); 665 $sectionPopular.show(); 666 $sectionTop.hide(); 667 $resultList.empty(); 668 return; 669 } 670 671 $.ajax({ 672 url: `${baseURL}/api/search/top-result`, 673 method: "GET", 674 data: { query }, 675 success: (response) => renderSearchResults(response.data), 676 error: (xhr) => { 677 console.error("Search error:", xhr.responseText); 678 $resultList.empty().append(` 679 <div class="tjp-search-result__list-item"> 680 <em>Error loading results</em> 681 </div> 682 `); 683 } 684 }); 685} 686 687// === Event Bindings === 688$(".btn-search").on("click", toggleSearchBox); 689 690$(".tjp-search-btn--close").on("click", clearSearch); 691 692$(".tjp-search-form__input input").on("keypress", function (e) { 693 if (e.key === "Enter") { 694 const $submitBtn = $(window).width() < 1024 695 ? $(".tjp-Head .tjp-search-btn--submit") 696 : $(".channelHeader .tjp-search-btn--submit"); 697 698 $submitBtn.click(); 699 } 700}); 701 702$(".tjp-search-btn--submit").on("click", submitSearch); 703$(".tjp-search-result__action-link").on("click", function (e) { 704 e.preventDefault(); // Hindari reload default 705 submitSearch(); // Panggil fungsi submit pencarian 706}); 707 708 709let debounceTimeout; 710$(".tjp-search-form__input input").on("input", function () { 711 const query = $(this).val().trim(); 712 713 clearTimeout(debounceTimeout); 714 debounceTimeout = setTimeout(() => handleSearchInput(query), 300); 715}); 716 717$(document).on("click", function (e) { 718 const $searchBox = $(window).width() < 1024 719 ? $(".tjp-Head .tjp-search") 720 : $(".channelHeader .tjp-search"); 721 722 const $btnSearch = $(".btn-search"); 723 724 // Kalau klik di luar search box dan bukan tombol .btn-search 725 if ( 726 $searchBox.hasClass("active") && 727 !$searchBox.is(e.target) && $searchBox.has(e.target).length === 0 && 728 !$btnSearch.is(e.target) && $btnSearch.has(e.target).length === 0 729 ) { 730 clearSearch(); 731 } 732}); 733 734$(function () { 735 const $subheaders = $('.jpx-subheader'); 736 if (!$subheaders.length) return; 737 738 function initSubheader($subheader) {
739 const $scrollContainer = $subheader.find('.jpx-subheader__scroll'); 740 const $wrapper = $subheader.find('.jpx-subheader__scroll-wrapper'); 741 const $prevBtn = $subheader.find('.jpx-subheader__nav--prev'); 742 const $nextBtn = $subheader.find('.jpx-subheader__nav--next'); 743 const scrollAmount = 150; 744 745 if (!$scrollContainer.length) return; 746 747 function updateArrowsAndGradient() { 748 const scrollLeft = $scrollContainer.scrollLeft(); 749 const scrollWidth = $scrollContainer[0].scrollWidth; 750 const clientWidth = $scrollContainer[0].clientWidth; 751 const maxScroll = scrollWidth - clientWidth; 752 const atStart = scrollLeft <= 1; 753 const atEnd = scrollLeft >= maxScroll - 1; 754 755 if ($prevBtn.length) $prevBtn.toggleClass('is-hidden', atStart); 756 if ($nextBtn.length) $nextBtn.toggleClass('is-hidden', atEnd); 757 758 if ($wrapper.length) { 759 $wrapper.removeClass('is-start is-end is-middle'); 760 if (atStart) $wrapper.addClass('is-start'); 761 else if (atEnd) $wrapper.addClass('is-end'); 762 else $wrapper.addClass('is-middle'); 763 } 764 } 765 766 function scrollToActiveItem() { 767 const $activeItem = $scrollContainer.find('.jpx-subheader__item.active'); 768 if (!$activeItem.length) { 769 updateArrowsAndGradient(); 770 return; 771 } 772 773 const containerWidth = $scrollContainer[0].clientWidth; 774 const itemEl = $activeItem[0]; 775 776 // hitung offsetLeft relatif ke scroll container langsung, 777 // bukan ke offsetParent, biar akurat walau ada nested wrapper 778 const itemLeft = itemEl.offsetLeft - $scrollContainer[0].offsetLeft; 779 const itemWidth = itemEl.offsetWidth; 780 const targetScrollLeft = itemLeft - (containerWidth / 2) + (itemWidth / 2); 781 782 $scrollContainer.scrollLeft(Math.max(0, targetScrollLeft)); 783 updateArrowsAndGradient(); 784 } 785 786 if ($prevBtn.length) { 787 $prevBtn.on('click', function () { 788 $scrollContainer.stop().animate({ scrollLeft: '-=' + scrollAmount }, 300); 789 }); 790 } 791 if ($nextBtn.length) { 792 $nextBtn.on('click', function () { 793 $scrollContainer.stop().animate({ scrollLeft: '+=' + scrollAmount }, 300); 794 }); 795 } 796 797 $scrollContainer.on('scroll', updateArrowsAndGradient); 798 $(window).on('resize', updateArrowsAndGradient); 799 800 // pakai requestAnimationFrame 2x supaya browser benar2 selesai 801 // layout & paint dulu sebelum kita baca offsetLeft/clientWidth, 802 // dan pakai setTimeout kecil untuk 'menang' dari scroll restoration browser 803 requestAnimationFrame(() => { 804 requestAnimationFrame(() => { 805 scrollToActiveItem(); 806 // extra safety: beberapa mobile browser reset scroll 807 // beberapa saat setelah load, jadi kita paksa lagi 808 setTimeout(scrollToActiveItem, 150); 809 }); 810 }); 811 } 812 813 // guard: kalau document sudah 'complete' (load event sudah lewat), 814 // langsung jalankan; kalau belum, baru nunggu event 'load' 815 if (document.readyState === 'complete') { 816 $subheaders.each(function () { 817 initSubheader($(this)); 818 }); 819 } else { 820 $(window).on('load', function () { 821 $subheaders.each(function () { 822 initSubheader($(this)); 823 }); 824 }); 825 } 826}); 827 828$(function () { 829 // buat toast container sekali aja, reuse terus 830 let $toast = $('.jpx-toast'); 831 if (!$toast.length) { 832 $toast = $('<div class="jpx-toast"><i class="fa-solid fa-check"></i><span>Link copied!</span></div>'); 833 $('body').append($toast); 834 } 835 836 let toastTimeout; 837 838 function showToast(message) { 839 clearTimeout(toastTimeout); 840 $toast.find('span').text(message); 841 $toast.addClass('is-visible'); 842 843 toastTimeout = setTimeout(function () { 844 $toast.removeClass('is-visible'); 845 }, 1800); 846 } 847 848 $(document).on('click', '.jpx-copy-link', function (e) { 849 e.preventDefault(); 850 851 const $btn = $(this); 852 const url = $btn.data('url'); 853 854 if (!url) return; 855 856 function onCopySuccess() { 857 const $icon = $btn.find('i'); 858 const originalClass = $icon.attr('class'); 859 860 $icon.attr('class', 'fa-solid fa-check'); 861 $btn.addClass('is-copied'); 862 863 setTimeout(function () { 864 $icon.attr('class', originalClass); 865 $btn.removeClass('is-copied'); 866 }, 1500); 867 868 showToast('Link copied!'); 869 } 870 871 if (navigator.clipboard && navigator.clipboard.writeText) { 872 navigator.clipboard.writeText(url).then(onCopySuccess).catch(function () { 873 fallbackCopy(url, onCopySuccess); 874 }); 875 } else { 876 fallbackCopy(url, onCopySuccess); 877 } 878 }); 879 880 function fallbackCopy(text, callback) { 881 const $temp = $('<textarea>'); 882 $('body').append($temp); 883 $temp.val(text).select(); 884 885 try { 886 document.execCommand('copy'); 887 callback(); 888 } catch (err) { 889 console.error('Copy failed:', err); 890 } 891 892 $temp.remove(); 893 } 894});
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.