1/** 2 * Main JavaScript File 3 * 4 * This file contains the main JavaScript code for the Codehills Kickstarter theme. 5 * It includes jQuery scripts that run when the document is ready and after the page is fully loaded. 6 * 7 * @since 2.0.0 8 * @package WordPress 9 * @subpackage Codehills Kickstarter Theme 10 */ 11 12// Theme scripts 13jQuery( document ).ready( function($) { 14 15 // Filter posts by category 16 $(document).on('change', '.post-filter', function(e) { 17 e.preventDefault(); 18 // Get case studie category value 19 var term = document.getElementById('post-filter').value; 20 var type = document.getElementById('post-filter').getAttribute('type'); 21 var taxonomy = document.getElementById('post-filter').getAttribute('taxonomy'); 22 23 // Load more posts 24 $.ajax({ 25 // async: true, 26 url: theme_ajax_params.ajax_url, 27 type: 'post', 28 data: { 29 action : 'filter_posts', 30 term : term, 31 type : type, 32 taxonomy : taxonomy 33 }, 34 success: function(response) { 35 // Convert response to JSON object 36 response = JSON.parse(response); 37 38 // Empty grid 39 $('#posts-grid').empty(); 40 41 // Append posts 42 if(response.posts != null) { 43 $('#posts-grid').append(response.posts); 44 $('.js-loadmore').remove(); 45 } 46 } 47 }); 48 }); 49 50 var canBeLoaded = true; // this param allows to initiate the AJAX call only if necessary 51 var bottomOffset = 1000; // the distance (in px) from the page bottom when you want to load more posts 52 53 $(window).scroll(function() { 54 var paged = document.getElementById('paged') ? document.getElementById('paged').value : null; 55 var max_pages = document.getElementById('max_pages') ? document.getElementById('max_pages').value : null; 56 var type = document.getElementById('type') ? document.getElementById('type').value : null; 57 var tax = document.getElementById('tax') ? document.getElementById('tax').value : null; 58 59 // Only proceed if required elements exist 60 if (paged === null || max_pages === null || type === null || tax === null) return; 61 62 // Load more posts 63 if ($(window).scrollTop() > ($(document).height() - $(window).height() - bottomOffset) && canBeLoaded === true) { 64 $.ajax({
65 url: theme_ajax_params.ajax_url, 66 type: 'post', 67 data: { 68 action: 'loadmore', 69 paged: paged, 70 max_pages: max_pages, 71 type: type, 72 tax: tax 73 }, 74 beforeSend: function(xhr) { 75 $('.js-loadmore').text('Loading...'); // change the button text, you can also add a preloader image 76 canBeLoaded = false; 77 }, 78 success: function(response) { 79 // Convert response to JSON object 80 try { 81 response = JSON.parse(response); 82 } catch (e) { 83 canBeLoaded = true; 84 $('.js-loadmore').text('More Post'); 85 return; 86 } 87 88 // Append posts 89 if (response.posts != null) { 90 $('#posts-grid').append(response.posts); 91 $('.js-loadmore').text('More Post'); 92 canBeLoaded = true; // the ajax is completed, now we can run it again 93 $('#paged').val(response.page); // update the paged value 94 } 95 if (response.no_more_posts === true) { 96 $('.js-loadmore').remove(); 97 } 98 } 99 }); 100 } 101 }); 102 103}); 104 105// Theme scripts after the page is loaded 106jQuery( window ).on( 'load', function() { 107 // Remove 'body' style attribute when the page is loaded 108 $( 'body' ).removeAttr( 'style' ); 109 110 // Initialize Splide Logo slider 111 var splide_logos = document.getElementsByClassName('splide-logos'); 112 113 for ( var i = 0; i < splide_logos.length; i++ ) { 114 var splide = new Splide( splide_logos[ i ], { 115 type : 'loop', 116 drag : 'free', 117 perPage : 7, 118 pagination : false, 119 arrows : false, 120 gap : '3rem', 121 autoWidth : true, 122 // autoplay : true, 123 autoScroll : { 124 speed : 0.5, 125 pauseOnHover : false 126 }, 127 breakpoints: { 128 979 : { 129 perPage: 5, 130 }, 131 639 : { 132 perPage: 3, 133 gap : '1.5rem', 134 autoWidth : false, 135 }, 136 }, 137 }); 138 139 splide.mount(window.splide.Extensions); 140 } 141 142 // Initialize Splide Journey slider 143 var splide_journey = document.getElementsByClassName('splide-journey').length; 144 145 if(splide_journey) { 146 var splide = new Splide( '.splide-journey', { 147 type : 'loop', 148 drag : 'free', 149 perPage : 3, 150 gap : '120px', 151 pagination : false, 152 speed : 1600, 153 breakpoints: { 154 1399 : { 155 gap : '3rem', 156 perPage: 3, 157 padding: '5rem', 158 }, 159 1119 : { 160 perPage: 2, 161 padding: '5rem', 162 }, 163 639 : { 164 perPage: 1, 165 padding: '2rem',
166 }, 167 439 : { 168 padding: '0rem', 169 }, 170 }, 171 }); 172 173 splide.mount(); 174 } 175 176 // Initialize Splide Journey slider 177 var splide_case_studies = document.getElementsByClassName('splide-case-studies').length; 178 179 if(splide_case_studies) { 180 var splide = new Splide( '.splide-case-studies', { 181 type : 'loop', 182 drag : 'free', 183 perPage : 3, 184 gap : '24px', 185 pagination : false, 186 speed : 1000, 187 perMove: 1, 188 padding: { left: '16rem' }, 189 autoScroll : { 190 speed : 0.5 191 }, 192 breakpoints: { 193 1399 : { 194 perPage: 2, 195 padding: '6rem', 196 }, 197 979 : { 198 padding: '2rem', 199 }, 200 639 : { 201 perPage: 1, 202 padding: '1.5rem', 203 }, 204 }, 205 }); 206 207 splide.mount(window.splide.Extensions); 208 } 209 210 gsap.registerPlugin(ScrollTrigger); 211 // Animated content with gsap (opacity to full color) 212 document.querySelectorAll('.animated-content').forEach((splitElement) => { 213 let text = new SplitType(splitElement, { 214 types: 'words, chars' 215 }); 216 217 gsap.fromTo(text.chars, 218 { opacity: 0.1 }, 219 { 220 opacity: 1, 221 stagger: 0.05, 222 scrollTrigger: { 223 trigger: splitElement, 224 start: 'top center', 225 end: 'bottom center', 226 scrub: 1, 227 pinType: "transform", // iOS fix 228 } 229 } 230 ) 231 }); 232 setTimeout(() => { 233 ScrollTrigger.refresh(); // re-calculates trigger positions; 234 }, 500); 235}); 236 237// Theme scripts after the DOM is fully loaded 238document.addEventListener('DOMContentLoaded', () => { 239 240 // Header active class on scroll 241 var header = document.querySelector('header'); 242 243 window.addEventListener('scroll', () => { 244 245 // Sticky cards function 246 // Call stickyCards function to adjust card positions 247 stickyCards(); 248 249 if (!header) return; 250 251 // Toggle header classes 252 if (window.scrollY > 0) { 253 header.classList.add('header-active'); 254 } else { 255 header.classList.remove('header-active'); 256 } 257 258 // Change header class based on section data-color 259 // Find all sections with data-color attribute 260 var sections = document.querySelectorAll('section[data-color]'); 261 var found = false; 262 263 // Loop through sections to find the one in viewport 264 sections.forEach(section => { 265 var rect = section.getBoundingClientRect(); 266 // Check if section is in viewport (top <= 80px && bottom > 80px) 267 if (!found && rect.top <= 80 && rect.bottom > 80) { 268 var color = section.getAttribute('data-color'); 269 if (color === 'light') { 270 header.classList.add('header-light'); 271 header.classList.remove('header-dark'); 272 } else { 273 header.classList.add('header-dark'); 274 header.classList.remove('header-light'); 275 } 276 found = true; 277 } 278 }); 279 // If no section is found in viewport, remove both classes 280 if (!found) { 281 header.classList.remove('header-light'); 282 header.classList.remove('header-dark'); 283 } 284 285 lastScrollY = window.scrollY; 286 }); 287 288 // Media block image animation on scroll 289 var elements = document.querySelectorAll('.media-block-image'); 290 291 if (elements.length > 0) { 292 var initial = { 293 translateY: 14.6289, 294 scale: 0.90857, 295 rotateX: 6.09536, 296 opacity: 0.817139 297 }; 298 299 var final = { 300 scale: 1, 301 opacity: 1 302 }; 303 304 // Define scroll range 305 var startScroll = 100; 306 var endScroll = 300; 307 308 function animateAll() { 309 var scrollY = window.scrollY; 310 var progress = Math.min(Math.max((scrollY - startScroll) / (endScroll - startScroll), 0), 1); 311
312 elements.forEach(function(element) { 313 // Interpolate each property 314 var translateY = initial.translateY * (1 - progress); 315 var scale = initial.scale + (final.scale - initial.scale) * progress; 316 var rotateX = initial.rotateX * (1 - progress); 317 318 // Apply styles 319 element.style.transform = `perspective(1200px) translateY(${translateY}px) scale(${scale}) rotateX(${rotateX}deg)`; 320 }); 321 322 requestAnimationFrame(animateAll); 323 } 324 325 requestAnimationFrame(animateAll); 326 } 327 328 // Interactive bubble animation on mouse move 329 var inter_bubble = document.querySelector('.interactive'); 330 331 if (typeof(inter_bubble) != 'undefined' && inter_bubble != null) { 332 var curX = 0; 333 var curY = 0; 334 var tgX = 0; 335 var tgY = 0; 336 337 function move() { 338 curX += (tgX - curX) / 20; 339 curY += (tgY - curY) / 20; 340 inter_bubble.style.transform = `translate(${Math.round(curX)}px, ${Math.round(curY)}px)`; 341 requestAnimationFrame(() => { 342 move(); 343 }); 344 } 345 346 window.addEventListener('mousemove', (event) => { 347 tgX = event.clientX; 348 tgY = event.clientY; 349 }); 350 351 move(); 352 } 353 354 // // Add wrapper around iframes in article content 355 // var iframes = document.querySelectorAll('iframe'); 356 357 // iframes.forEach(iframe => { 358 // if (document.querySelector('.article-content')) { 359 // var wrapper = document.createElement('div'); 360 // wrapper.classList.add('iframe-wrapper'); 361 362 // // Insert the wrapper before the iframe 363 // iframe.parentNode.insertBefore(wrapper, iframe); 364 365 // // Move the iframe into the wrapper 366 // wrapper.appendChild(iframe); 367 // } 368 // }); 369 370 const article = document.querySelector('.article-content'); 371 if (!article) return; 372 373 const iframes = article.querySelectorAll('iframe'); 374 375 iframes.forEach(iframe => { 376 // Skip hidden / tracking iframes 377 if ( 378 iframe.width === '0' || 379 iframe.height === '0' || 380 iframe.style.display === 'none' || 381 iframe.style.visibility === 'hidden' || 382 iframe.closest('.iframe-wrapper') 383 ) { 384 return; 385 } 386 387 const wrapper = document.createElement('div'); 388 wrapper.className = 'iframe-wrapper'; 389 390 iframe.parentNode.insertBefore(wrapper, iframe); 391 wrapper.appendChild(iframe); 392 }); 393 394 395}); 396 397// Lottie Interactivity 398var players = document.querySelectorAll('.lottie'); 399 400players.forEach(player => { 401 player.addEventListener('ready', function() { 402 LottieInteractivity.create({ 403 player: player.getLottie(), 404 mode: "scroll", 405 actions: [ 406 { 407 visibility:[0.2, 1], 408 type: "playOnce" 409 } 410 ] 411 }); 412 }); 413}); 414 415// Sticky cards function 416function stickyCards() { 417 var cards = document.querySelectorAll('.card'); 418 var is_tablet = window.innerWidth <= 959; 419 var is_mobile = window.innerWidth <= 639; 420 var is_tablet_height = window.innerHeight <= 849; 421 422 if (cards.length === 0) return; // Exit if no cards are found 423 424 cards.forEach((card, index) => { 425 var nextCard = cards[index + 1]; 426 if (!nextCard) return; // Skip the last card 427 428 var cardRect = card.getBoundingClientRect(); 429 var nextCardRect = nextCard.getBoundingClientRect(); 430 431 var overlap = Math.max(0, cardRect.bottom - nextCardRect.top); 432 var progress = overlap / cardRect.height; 433 434 // Clamp progress between 0 and 1 435 progress = Math.min(Math.max(progress, 0), 1); 436 437 card.style.transform = `scale(${1 - 0.2 * progress})`; 438 card.style.opacity = `${1 - progress}`; 439 if (is_mobile) { 440 var card_height = card.offsetHeight + 200; 441 442 // Use half the height, even if negative (for tall cards) 443 card.style.position = 'sticky'; 444 card.style.top = `${(-card_height) / 2}px`; 445 } else if (is_tablet) { 446 // Tablet fallback 447 card.style.position = 'sticky'; 448 card.style.top = '-300px'; 449 } else if (is_tablet_height) { 450 // Desktop fallback 451 card.style.position = 'sticky'; 452 card.style.top = '-100px'; 453 } else { 454 // Desktop fallback 455 card.style.position = 'sticky'; 456 card.style.top = '60px'; 457 } 458 });
459} 460 461// Update combined section height for CSS variable 462function updateCombinedHeight() { 463 var sections = document.querySelectorAll('.section-height'); 464 var totalHeight = 0; 465 466 sections.forEach(section => { 467 totalHeight += section.offsetHeight; 468 }); 469 470 // Check last section and add half height only if it has `.add-half` 471 if (sections.length > 0) { 472 var lastSection = sections[sections.length - 1]; 473 474 if (lastSection.classList.contains('section-height-half')) { 475 var halfHeight = lastSection.offsetHeight / 2; 476 totalHeight -= halfHeight; 477 } 478 } // Set CSS variable 479 document.documentElement.style.setProperty('--combined-section-height', `${totalHeight}px`); 480} 481 482// Initial call to set combined height 483window.addEventListener('load', () => { 484 setTimeout(updateCombinedHeight, 200); 485}); 486 487// Run on resize (for mobile rotation) 488window.addEventListener('resize', () => { 489 stickyCards(); 490 updateCombinedHeight(); 491}); 492 493// YouTube video play on click 494document.querySelectorAll('.youtube-video').forEach((video) => { 495 496 const button = video.querySelector('.youtube-trigger'); 497 const iframe = video.querySelector('.youtube-iframe'); 498 499 button.addEventListener('click', () => { 500 501 iframe.src = iframe.dataset.src; 502 503 video.classList.add('is-active'); 504 505 }); 506 507});
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.