1/** 2 * Custom JavaScript for GSM Growth Agency 3 */ 4;(($) => { 5 // Custom cursor 6 const cursor = document.querySelector(".custom-cursor") 7 8 if (cursor) { 9 document.addEventListener("mousemove", (e) => { 10 cursor.style.left = e.clientX + "px" 11 cursor.style.top = e.clientY + "px" 12 }) 13 14 document.addEventListener("mousedown", () => { 15 cursor.classList.add("active") 16 }) 17 18 document.addEventListener("mouseup", () => { 19 cursor.classList.remove("active") 20 }) 21 22 // Add hover effect to links and buttons 23 const interactiveElements = document.querySelectorAll('a, button, input[type="submit"], .interactive') 24 interactiveElements.forEach((element) => { 25 element.addEventListener("mouseenter", () => { 26 cursor.classList.add("active") 27 }) 28 29 element.addEventListener("mouseleave", () => { 30 cursor.classList.remove("active") 31 }) 32 }) 33 } 34 35 // Header scroll effect 36 const header = document.querySelector(".header") 37 38 if (header) { 39 window.addEventListener("scroll", () => { 40 if (window.scrollY > 50) { 41 header.classList.add("scrolled") 42 } else { 43 header.classList.remove("scrolled") 44 } 45 }) 46 } 47 48 // Mobile menu toggle 49 const menuToggle = document.querySelector(".mobile-menu-toggle") 50 const mobileMenu = document.querySelector(".mobile-menu") 51 const bars = document.querySelectorAll(".bar") 52 53 if (menuToggle && mobileMenu) { 54 menuToggle.addEventListener("click", () => { 55 mobileMenu.classList.toggle("active") 56 bars.forEach((bar) => bar.classList.toggle("active")) 57 document.body.classList.toggle("menu-open") 58 }) 59 60 // Close mobile menu when clicking a link 61 const mobileLinks = mobileMenu.querySelectorAll("a") 62 mobileLinks.forEach((link) => { 63 link.addEventListener("click", () => { 64 mobileMenu.classList.remove("active") 65 bars.forEach((bar) => bar.classList.remove("active")) 66 document.body.classList.remove("menu-open") 67 }) 68 }) 69 } 70 71 // Animate elements on scroll 72 function animateOnScroll() { 73 const animatedElements = document.querySelectorAll(".animate-on-scroll") 74 75 animatedElements.forEach((element) => { 76 const elementPosition = element.getBoundingClientRect().top 77 const windowHeight = window.innerHeight 78 79 if (elementPosition < windowHeight - 100) { 80 element.classList.add("animated") 81 } 82 }) 83 } 84 85 // Add animate-on-scroll class to elements that should animate 86 function setupAnimations() { 87 // Add to section titles 88 const sectionTitles = document.querySelectorAll(".section-title, .seo-section-title, .tech-section-title")
89 sectionTitles.forEach((title) => { 90 title.classList.add("animate-on-scroll") 91 }) 92 93 // Add to cards and grid items 94 const cards = document.querySelectorAll( 95 ".benefit-card, .service-card, .tech-card, .seo-service-card, .tech-benefit", 96 ) 97 cards.forEach((card, index) => { 98 card.classList.add("animate-on-scroll") 99 card.style.transitionDelay = `${index * 0.1}s` 100 }) 101 102 // Add to process steps 103 const processSteps = document.querySelectorAll(".seo-process-step") 104 processSteps.forEach((step, index) => { 105 step.classList.add("animate-on-scroll") 106 step.style.transitionDelay = `${index * 0.15}s` 107 }) 108 } 109 110 // Initialize animations 111 document.addEventListener("DOMContentLoaded", () => { 112 setupAnimations() 113 animateOnScroll() // Check on page load 114 115 // Check on scroll 116 window.addEventListener("scroll", animateOnScroll) 117 }) 118 119 // SEO Page specific scripts 120 if (document.querySelector(".seo-page")) { 121 // FAQ accordion 122 const faqItems = document.querySelectorAll(".seo-faq-item") 123 124 faqItems.forEach((item) => { 125 const question = item.querySelector(".seo-faq-question") 126 127 if (question) { 128 question.addEventListener("click", () => { 129 // Toggle active class on the clicked item 130 item.classList.toggle("active") 131 132 // Optional: Close other items when one is opened 133 faqItems.forEach((otherItem) => { 134 if (otherItem !== item) { 135 otherItem.classList.remove("active") 136 } 137 }) 138 }) 139 } 140 }) 141 142 // Testimonial slider 143 const testimonials = document.querySelectorAll(".seo-testimonial") 144 const prevButton = document.querySelector(".seo-testimonial-prev") 145 const nextButton = document.querySelector(".seo-testimonial-next") 146 let currentTestimonial = 0 147 148 function showTestimonial(index) { 149 testimonials.forEach((testimonial, i) => { 150 testimonial.style.display = i === index ? "block" : "none" 151 }) 152 } 153 154 if (testimonials.length > 0 && prevButton && nextButton) { 155 showTestimonial(currentTestimonial) 156 157 prevButton.addEventListener("click", () => { 158 currentTestimonial = (currentTestimonial - 1 + testimonials.length) % testimonials.length 159 showTestimonial(currentTestimonial) 160 }) 161 162 nextButton.addEventListener("click", () => { 163 currentTestimonial = (currentTestimonial + 1) % testimonials.length 164 showTestimonial(currentTestimonial) 165 }) 166 } 167 168 // Counter animation for stats 169 const statNumbers = document.querySelectorAll(".seo-stat-number") 170 let statsAnimated = false 171 172 function animateStats() { 173 if (statsAnimated) return 174 175 statsAnimated = true 176 177 statNumbers.forEach((stat) => { 178 const target = Number.parseInt(stat.getAttribute("data-count")) 179 const duration = 2000 // 2 seconds 180 const step = target / (duration / 16) // 60fps 181 let current = 0 182 183 const timer = setInterval(() => { 184 current += step 185 if (current >= target) { 186 clearInterval(timer) 187 stat.textContent = target 188 } else { 189 stat.textContent = Math.floor(current) 190 } 191 }, 16) 192 }) 193 } 194 195 // Check if stats are in viewport 196 function checkStats() { 197 if (statNumbers.length > 0) { 198 const statsSection = document.querySelector(".seo-hero-stats") 199 if (statsSection) { 200 const rect = statsSection.getBoundingClientRect() 201 if (rect.top < window.innerHeight && rect.bottom > 0) { 202 animateStats() 203 window.removeEventListener("scroll", checkStats) 204 } 205 } 206 } 207 } 208 209 window.addEventListener("scroll", checkStats) 210 checkStats() // Check on page load 211 } 212 213 // Tech We Use page specific scripts 214 if (document.querySelector(".tech-page")) { 215 // Category filtering 216 const tabs = document.querySelectorAll(".tech-tab") 217 const cards = document.querySelectorAll(".tech-card") 218
219 tabs.forEach((tab) => { 220 tab.addEventListener("click", () => { 221 // Update active tab 222 tabs.forEach((t) => t.classList.remove("active")) 223 tab.classList.add("active") 224 225 const category = tab.getAttribute("data-category") 226 227 // Filter cards 228 cards.forEach((card) => { 229 if (category === "all") { 230 card.style.display = "block" 231 } else { 232 const cardCategories = card.getAttribute("data-categories") 233 if (cardCategories && cardCategories.includes(category)) { 234 card.style.display = "block" 235 } else { 236 card.style.display = "none" 237 } 238 } 239 }) 240 }) 241 }) 242 } 243 244 // Community page specific scripts 245 if (document.querySelector(".value-item")) { 246 // Reveal animations for value items 247 const valueItems = document.querySelectorAll(".value-item") 248 249 function checkValueItems() { 250 valueItems.forEach((item, index) => { 251 const itemTop = item.getBoundingClientRect().top 252 const windowHeight = window.innerHeight 253 254 if (itemTop < windowHeight - 100) { 255 // Add staggered delay based on index 256 setTimeout(() => { 257 item.classList.add("visible") 258 }, index * 150) 259 } 260 }) 261 } 262 263 // Check on scroll 264 window.addEventListener("scroll", checkValueItems) 265 266 // Check on page load 267 document.addEventListener("DOMContentLoaded", checkValueItems) 268 269 // Add animation to hero content when page loads 270 setTimeout(() => { 271 document.body.classList.add("loaded") 272 }, 500) 273 274 // Reveal animation for pricing card 275 const pricingCard = document.querySelector(".pricing-card") 276 277 if (pricingCard) { 278 function checkPricingCard() { 279 const cardTop = pricingCard.getBoundingClientRect().top 280 const windowHeight = window.innerHeight 281 282 if (cardTop < windowHeight - 100) { 283 pricingCard.classList.add("visible") 284 } 285 } 286 287 // Check on scroll 288 window.addEventListener("scroll", checkPricingCard) 289 290 // Check on page load 291 document.addEventListener("DOMContentLoaded", checkPricingCard) 292 } 293 } 294 295 // Careers page specific scripts 296 if (document.querySelector(".careers-page")) { 297 const form = document.getElementById("application-form") 298 const fileInput = document.getElementById("cvFile") 299 const fileName = document.getElementById("file-name") 300 const successMessage = document.getElementById("success-message") 301 302 // Show success message if URL parameter is present 303 if (window.location.search.includes("submission=success") && successMessage && form) { 304 successMessage.style.display = "block" 305 form.style.display = "none" 306 } 307 308 // Display file name when file is selected 309 if (fileInput && fileName) { 310 fileInput.addEventListener("change", function () { 311 if (this.files.length > 0) { 312 fileName.textContent = this.files[0].name 313 } else { 314 fileName.textContent = "No file selected" 315 } 316 }) 317 } 318 319 // Form validation 320 if (form) { 321 form.addEventListener("submit", (e) => { 322 let isValid = true 323 324 // Reset error messages 325 document.querySelectorAll(".error-message").forEach((el) => { 326 el.style.display = "none" 327 }) 328 329 // Validate required fields 330 const requiredFields = ["firstName", "lastName", "email", "phone", "position", "additionalInfo", "referral"] 331 requiredFields.forEach((field) => { 332 const input = document.getElementById(field) 333 if (input && !input.value.trim()) { 334 const errorEl = document.getElementById(field + "-error") 335 if (errorEl) { 336 errorEl.style.display = "block" 337 } 338 isValid = false 339 } 340 }) 341 342 // Validate email format 343 const email = document.getElementById("email") 344 if (email && email.value.trim()) { 345 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ 346 if (!emailRegex.test(email.value.trim())) { 347 const errorEl = document.getElementById("email-error") 348 if (errorEl) { 349 errorEl.style.display = "block" 350 } 351 isValid = false 352 } 353 } 354 355 // Validate CV (either link or file) 356 const cvLink = document.getElementById("cvLink") 357 if (cvLink && fileInput && !cvLink.value.trim() && !fileInput.files.length) { 358 const errorEl = document.getElementById("cv-error") 359 if (errorEl) { 360 errorEl.style.display = "block" 361 } 362 isValid = false 363 } 364 365 if (!isValid) { 366 e.preventDefault() 367 } 368 }) 369 } 370 } 371})(jQuery)( 372 /** 373 * Custom JavaScript for BadMarketing theme 374 */ 375 ($) => { 376 $(document).ready(() => { 377 // Custom cursor animation 378 const cursor = document.querySelector(".custom-cursor") 379 380 if (cursor) { 381 document.addEventListener("mousemove", (e) => { 382 cursor.style.left = e.clientX + "px" 383 cursor.style.top = e.clientY + "px" 384 }) 385 386 document.addEventListener("mousedown", () => { 387 cursor.classList.add("active") 388 }) 389 390 document.addEventListener("mouseup", () => { 391 cursor.classList.remove("active") 392 }) 393 394 // Add hover effect to links 395 const links = document.querySelectorAll("a") 396 links.forEach((link) => { 397 link.addEventListener("mouseenter", () => { 398 cursor.classList.add("active") 399 }) 400 401 link.addEventListener("mouseleave", () => { 402 cursor.classList.remove("active") 403 }) 404 }) 405 } 406 407 // Fix for blog post links 408 if ($("body").hasClass("single-post") || $("body").hasClass("page-template-page-blog")) { 409 // Ensure social sharing buttons work correctly 410 $(".social-button").on("click", function (e) { 411 e.preventDefault() 412 const url = $(this).attr("href") 413 window.open(url, "share-dialog", "width=626,height=436") 414 }) 415 } 416 417 // Smooth scroll for anchor links 418 $('a[href^="#"]').on("click", function (e) { 419 if (this.hash !== "") { 420 e.preventDefault() 421 const hash = this.hash 422 $("html, body").animate( 423 {
424 scrollTop: $(hash).offset().top - 100, 425 }, 426 800, 427 ) 428 } 429 }) 430 431 // Header scroll effect 432 $(window).on("scroll", () => { 433 if ($(window).scrollTop() > 50) { 434 $(".header").addClass("scrolled") 435 } else { 436 $(".header").removeClass("scrolled") 437 } 438 }) 439 440 // Initialize header state on page load 441 if ($(window).scrollTop() > 50) { 442 $(".header").addClass("scrolled") 443 } 444 }) 445 }, 446)(jQuery)
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.