1gsap.registerPlugin(Flip); 2gsap.registerPlugin(ScrollTrigger); 3 4// GET BREAKPOINT TOKENS 5let breakpoints = ['xs', 'sm', 'sm-ext', 'md', 'lg', 'xl', '2xl']; 6let rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize); 7 8var breakpointValues = {}; 9 10breakpoints.forEach(breakpoint => { 11 let cssVar = `--lp-breakpoint-${breakpoint}`; 12 let remValue = parseFloat(getComputedStyle(document.documentElement).getPropertyValue(cssVar).trim()); 13 let pixelValue = remValue * rootFontSize; 14 breakpointValues[breakpoint] = pixelValue; 15}); 16 17document.addEventListener("DOMContentLoaded", () => { 18 document.querySelectorAll(".background-page-head_middle").forEach(element => { 19 const target = element.getAttribute("data-background-position-target"); 20 21 const targetElementProp = document.querySelector(target).getBoundingClientRect(); 22 const elementProp = element.getBoundingClientRect(); 23 24 element.style.height = (targetElementProp.bottom - (targetElementProp.height / 2)) - elementProp.top + "px"; 25 }); 26 27 //GLIDE CAROUSEL 28 if(document.querySelector('.glide.glide_card-carousel')) { 29 const glideCardCarousel = new Glide('.glide.glide_card-carousel', { 30 type: 'carousel', 31 perView: 1, 32 gap: 24, 33 breakpoints: { 34 [breakpointValues['2xl']]: { 35 perView: 5, 36 gap: 24, 37 }, 38 [breakpointValues['md']]: { 39 perView: 4, 40 focusAt: 'center', 41 gap: 16, 42 }, 43 [breakpointValues['sm']]: { 44 perView: 3, 45 focusAt: 'center', 46 gap: 12, 47 }, 48 [breakpointValues['xs']]: { 49 perView: 2, 50 focusAt: 'center', 51 gap: 12, 52 }, 53 } 54 }); 55 56 glideCardCarousel.on(["run.after", "mount.after"], function() { 57 let slides = Array.from(document.querySelectorAll('.glide__slide')); 58 let currentSlide = document.querySelector('.glide__slide--active'); 59 let current = slides.indexOf(currentSlide); 60 let perView = glideCardCarousel.settings.perView; 61 62 document.querySelectorAll('.glide__slide').forEach(function(slide) { 63 slide.classList.remove('glide__slide--active-custom'); 64 }); 65 66 if (glideCardCarousel.settings.focusAt !== 'center') { 67 for (let i = current; i < current + perView; i++) { 68 let slide = document.querySelector(`.glide__slide:nth-child(${i + 1})`); 69 slide.classList.add('glide__slide--active-custom'); 70 } 71 } 72 }); 73 74 glideCardCarousel.mount(); 75 } 76 77 if(document.querySelector('.glide.glide_card-filled-carousel')) { 78 const glideCardFilledCarousel = new Glide('.glide.glide_card-filled-carousel', { 79 type: 'carousel', 80 perView: 1, 81 gap: 16, 82 focusAt: 'center' 83 }); 84 85 glideCardFilledCarousel.mount(); 86 } 87}); 88 89document.addEventListener('DOMContentLoaded', function() { 90 const elements = document.querySelectorAll('[data-responsive-class]'); 91 const width = window.innerWidth; 92 const modifierFormat = "_"; 93 94 elements.forEach(element => { 95 let defaultClass = element.getAttribute('data-responsive-class-default'); 96 let responsiveClass = ''; 97 98 for (let breakpoint in breakpointValues) { 99 if (width > breakpointValues[breakpoint]) { 100 let breakpointClass = element.getAttribute(`data-responsive-class-${breakpoint}`); 101 if (breakpointClass) { 102 responsiveClass = breakpointClass; 103 } 104 }; 105 } 106 107 if (responsiveClass) { 108 defaultClass = responsiveClass; 109 } 110 111 element.classList.add(element.getAttribute('data-responsive-class') + modifierFormat + defaultClass); 112 }); 113 114 let bodyStyles = window.getComputedStyle(document.body); 115 let header_height_origin = parseFloat(bodyStyles.getPropertyValue('--header-height-origin').trim()); 116 let header_height_highlight = parseFloat(bodyStyles.getPropertyValue('--header-highlight-line-height').trim()); 117 let header_height_origin_val = header_height_origin * rootFontSize; 118 let header_height_highlight_val = header_height_highlight * rootFontSize; 119 120 let header_highlight = document.querySelector('.header__highlight-line'); 121 122 if (header_highlight !== null) { 123 document.body.style.setProperty('--header-height', `${(header_height_origin_val + header_height_highlight_val) / rootFontSize}rem`); 124 125 let lastScrollTop = 0; 126 const header = document.querySelector('.header_lp'); 127 128 window.addEventListener('scroll', function() { 129 // Cek jika layar memanjang secara vertikal 130 if (!window.matchMedia("(orientation: portrait)").matches) { 131 let scrollTop = window.scrollY; 132 if (scrollTop > lastScrollTop){ 133 // Downscroll code 134 header.style.top = `-${header_height_highlight_val}px`; 135 } else { 136 // Upscroll code 137 header.style.top = `0px`; 138 } 139 lastScrollTop = scrollTop <= 0 ? 0 : scrollTop; // For Mobile or negative scrolling 140 } 141 }, false); 142 } 143 144 let runningConfig = new ResizeObserver(entries => {
145 for (let entry of entries) { 146 if(isChildOverflowing(entry.target, ".running__item")){ 147 entry.target.classList.add('running_anim'); 148 149 let runningItem = entry.target.querySelector('.running__item'); 150 let clonedNode = runningItem.cloneNode(true); 151 152 entry.target.style.width = `${runningItem.offsetWidth + parseInt(window.getComputedStyle(entry.target).getPropertyValue('gap'))}px`; 153 entry.target.appendChild(clonedNode); 154 } 155 } 156 }); 157 158 // Start observing the element 159 let runnings = document.querySelectorAll('.running'); 160 161 runnings.forEach(function(running) { 162 runningConfig.observe(running); 163 }); 164 165}); 166 167 168//SET BACKGROUND IMAGE FOR YOUTUBE EMBEDS 169const ytEmbeds = embed => { 170 let videoUrl = embed.getAttribute('data-src'); 171 if(videoUrl === null) return; 172 let videoIdMatch = videoUrl.match(/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/ ]{11})/i); 173 let videoId = videoIdMatch ? videoIdMatch[1] : undefined; 174 if (videoId) { 175 let thumbnailUrl = `https://img.youtube.com/vi/${videoId}/0.jpg`; 176 embed.style.backgroundImage = `url('${thumbnailUrl}')`; 177 } 178} 179 180const isChildOverflowing = (element, childClass) => { 181 if (element && element.querySelector) { 182 const childElement = element.querySelector(childClass); 183 if (childElement) { 184 return element.offsetWidth < childElement.scrollWidth; 185 } 186 } 187 return false; 188}; 189 190document.querySelectorAll('.yt-embed[data-src]').forEach(function(embed) { 191 ytEmbeds(embed); 192}); 193 194function vh(percent) { 195 var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); 196 return (percent * h) / 100; 197} 198 199function vw(percent) { 200 var w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); 201 return (percent * w) / 100; 202} 203 204// Scroll Effect in Navbar 205const navbar = document.querySelector(".header.header_lp"); 206const handleScroll = () => { 207 if (window.scrollY > 0) { 208 navbar.classList.add("on-scroll"); 209 } else { 210 navbar.classList.remove("on-scroll"); 211 } 212}; 213window.addEventListener("scroll", handleScroll); 214 215// Accordion 216const accordionHandleClick = event => { 217 const accordionItemTarget = event.currentTarget.closest(".lp-accordion__item"); 218 const accordion = accordionItemTarget.closest(".lp-accordion"); 219 const accordionItemsOpen = [... accordion.querySelectorAll(".lp-accordion__item.open")]; 220 const accordionItemsOpenOther = accordionItemsOpen.filter(function(element){ 221 return element !== accordionItemTarget; 222 }) 223 224 let state = Flip.getState(accordionItemTarget, accordionItemsOpenOther); 225 226 accordionItemTarget.classList.toggle('open'); 227 228 Flip.from(state, { 229 duration: 0.4, 230 nested: true, 231 toggleClass: "animation", 232 ease: "power4.inOut" 233 }); 234 235 accordionItemsOpenOther.forEach(element => { 236 element.classList.remove("open"); 237 }); 238}; 239 240const accordionToggle = document.querySelectorAll('.lp-accordion__toggle'); 241 242accordionToggle.forEach(element => { 243 element.addEventListener('click', accordionHandleClick); 244}); 245 246 247function scrollTabs(button, direction) { 248 const tabsContainer = button.closest('.nav-tab_scrollable'); 249 const tabsWrapper = tabsContainer.querySelector('.nav-tab__wrapper'); 250 const scrollAmount = 100; 251 252 if (direction === 'left') { 253 tabsWrapper.scrollLeft -= scrollAmount; 254 } else if (direction === 'right') { 255 tabsWrapper.scrollLeft += scrollAmount; 256 } 257 updateScrollOverlays(tabsContainer); 258} 259 260function updateScrollOverlays(tabsContainer) { 261 const tabsWrapper = tabsContainer.querySelector('.nav-tab__wrapper'); 262 const scrollLeftOverlay = tabsContainer.querySelector('.nav-tab__scroll-overlay.left'); 263 const scrollRightOverlay = tabsContainer.querySelector('.nav-tab__scroll-overlay.right'); 264 const maxScrollLeft = tabsWrapper.scrollWidth - tabsWrapper.clientWidth; 265 266 if (tabsWrapper.scrollLeft <= 0) { 267 scrollLeftOverlay.style.display = 'none'; 268 } else { 269 scrollLeftOverlay.style.display = 'flex'; 270 } 271 272 if (tabsWrapper.scrollLeft >= maxScrollLeft - 1) { 273 scrollRightOverlay.style.display = 'none'; 274 } else { 275 scrollRightOverlay.style.display = 'flex'; 276 } 277} 278 279document.querySelectorAll('.nav-tab__wrapper').forEach(wrapper => { 280 wrapper.addEventListener('scroll', () => { 281 const tabsContainer = wrapper.closest('.nav-tab_scrollable'); 282 updateScrollOverlays(tabsContainer); 283 }); 284}); 285 286window.addEventListener('load', () => { 287 document.querySelectorAll('.nav-tab_scrollable').forEach(container => { 288 updateScrollOverlays(container); 289 }); 290}); 291 292window.addEventListener('resize', () => { 293 document.querySelectorAll('.nav-tab_scrollable').forEach(container => { 294 updateScrollOverlays(container); 295 }); 296}); 297
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.