|
|
| |
| window.addEventListener('load', () => { |
| setTimeout(() => { |
| const loadingScreen = document.getElementById('loadingScreen'); |
| if (loadingScreen) { |
| loadingScreen.style.opacity = '0'; |
| loadingScreen.style.transition = 'opacity 0.5s ease-out'; |
| setTimeout(() => { |
| loadingScreen.style.display = 'none'; |
| }, 500); |
| } |
| }, 1500); |
| }); |
|
|
| |
| class CinematicBackground { |
| constructor() { |
| this.particlesLayer = document.getElementById('particlesLayer'); |
| this.particles = []; |
| this.neonLines = []; |
| this.init(); |
| } |
|
|
| init() { |
| this.createParticles(); |
| this.createNeonLines(); |
| this.animateParticles(); |
| } |
|
|
| createParticles() { |
| const particleCount = 30; |
| |
| for (let i = 0; i < particleCount; i++) { |
| const particle = document.createElement('div'); |
| particle.className = 'tech-particle'; |
| |
| const size = Math.random() * 4 + 2; |
| const x = Math.random() * window.innerWidth; |
| const y = Math.random() * window.innerHeight; |
| const duration = Math.random() * 15 + 10; |
| const delay = Math.random() * 5; |
| |
| particle.style.width = `${size}px`; |
| particle.style.height = `${size}px`; |
| particle.style.left = `${x}px`; |
| particle.style.top = `${y}px`; |
| particle.style.animation = `gentle-float ${duration}s ease-in-out ${delay}s infinite`; |
| |
| this.particlesLayer.appendChild(particle); |
| this.particles.push(particle); |
| } |
| } |
|
|
| createNeonLines() { |
| const lineCount = 5; |
| |
| for (let i = 0; i < lineCount; i++) { |
| const line = document.createElement('div'); |
| line.className = 'neon-line'; |
| |
| const width = Math.random() * 200 + 100; |
| const x = Math.random() * window.innerWidth; |
| const y = Math.random() * window.innerHeight; |
| const rotation = Math.random() * 360; |
| const delay = Math.random() * 3; |
| |
| line.style.width = `${width}px`; |
| line.style.left = `${x}px`; |
| line.style.top = `${y}px`; |
| line.style.transform = `rotate(${rotation}deg)`; |
| line.style.animationDelay = `${delay}s`; |
| |
| this.particlesLayer.appendChild(line); |
| this.neonLines.push(line); |
| } |
| } |
|
|
| animateParticles() { |
| |
| setInterval(() => { |
| this.particles.forEach((particle, index) => { |
| const currentX = parseFloat(particle.style.left); |
| const currentY = parseFloat(particle.style.top); |
| |
| const deltaX = (Math.random() - 0.5) * 2; |
| const deltaY = (Math.random() - 0.5) * 2; |
| |
| const newX = Math.max(0, Math.min(window.innerWidth - 10, currentX + deltaX)); |
| const newY = Math.max(0, Math.min(window.innerHeight - 10, currentY + deltaY)); |
| |
| particle.style.left = `${newX}px`; |
| particle.style.top = `${newY}px`; |
| }); |
| }, 3000); |
| } |
| } |
|
|
| |
| document.addEventListener('DOMContentLoaded', () => { |
| const cinematicBg = new CinematicBackground(); |
| |
| |
| document.addEventListener('mousemove', (e) => { |
| const mouseX = e.clientX / window.innerWidth - 0.5; |
| const mouseY = e.clientY / window.innerHeight - 0.5; |
| |
| const floatingElements = document.querySelectorAll('.floating-review-card, .floating-text'); |
| |
| floatingElements.forEach((element, index) => { |
| const depth = (index % 3) + 1; |
| const moveX = mouseX * depth * 10; |
| const moveY = mouseY * depth * 10; |
| |
| element.style.transform = `translate(${moveX}px, ${moveY}px)`; |
| }); |
| }); |
| |
| |
| if (window.innerWidth < 768) { |
| document.querySelector('.floating-reviews').style.display = 'none'; |
| document.getElementById('particlesLayer')?.style.setProperty('opacity', '0.3'); |
| } |
| }); |
| |
| const services = [ |
| 'Telegram Promotion', |
| 'Telegram Ads', |
| 'Telegram Bot Development', |
| 'Vibe Coding / App Development', |
| 'App Promotion', |
| 'Music Marketing', |
| 'Crypto Marketing' |
| ]; |
|
|
| let currentServiceIndex = 0; |
| const animatedServiceElement = document.getElementById('animatedService'); |
|
|
| function typeService(service, callback) { |
| let index = 0; |
| animatedServiceElement.textContent = ''; |
| |
| const typingInterval = setInterval(() => { |
| if (index < service.length) { |
| animatedServiceElement.textContent += service[index]; |
| index++; |
| } else { |
| clearInterval(typingInterval); |
| setTimeout(callback, 2000); |
| } |
| }, 50); |
| } |
|
|
| function deleteService(callback) { |
| const currentText = animatedServiceElement.textContent; |
| let index = currentText.length; |
| |
| const deletingInterval = setInterval(() => { |
| if (index > 0) { |
| animatedServiceElement.textContent = currentText.substring(0, index - 1); |
| index--; |
| } else { |
| clearInterval(deletingInterval); |
| callback(); |
| } |
| }, 30); |
| } |
|
|
| function animateServices() { |
| typeService(services[currentServiceIndex], () => { |
| deleteService(() => { |
| currentServiceIndex = (currentServiceIndex + 1) % services.length; |
| animateServices(); |
| }); |
| }); |
| } |
|
|
| if (animatedServiceElement) { |
| animateServices(); |
| } |
|
|
| |
| document.querySelectorAll('a[href^="#"]').forEach(anchor => { |
| anchor.addEventListener('click', function (e) { |
| e.preventDefault(); |
| const target = document.querySelector(this.getAttribute('href')); |
| if (target) { |
| target.scrollIntoView({ |
| behavior: 'smooth', |
| block: 'start' |
| }); |
| } |
| }); |
| }); |
|
|
| |
| const observerOptions = { |
| threshold: 0.1, |
| rootMargin: '0px 0px -50px 0px' |
| }; |
|
|
| const observer = new IntersectionObserver((entries) => { |
| entries.forEach(entry => { |
| if (entry.isIntersecting) { |
| entry.target.style.opacity = '1'; |
| entry.target.style.transform = 'translateY(0)'; |
| } |
| }); |
| }, observerOptions); |
|
|
| |
| document.querySelectorAll('.service-card').forEach(card => { |
| card.style.opacity = '0'; |
| card.style.transform = 'translateY(20px)'; |
| card.style.transition = 'all 0.6s ease-out'; |
| observer.observe(card); |
| }); |
| |
| let reviewsData = [ |
| { |
| service: 'Telegram Promotion', |
| rating: 5, |
| name: 'Alex Johnson', |
| amount: 599, |
| date: '2025-01-15', |
| comment: 'Amazing service! Our Telegram channel grew by 500% in just one month.' |
| }, |
| { |
| service: 'App Development', |
| rating: 5, |
| name: 'Sarah Chen', |
| amount: 999, |
| date: '2025-01-12', |
| comment: 'Professional team and excellent communication. Delivered beyond expectations.' |
| }, |
| { |
| service: 'Crypto Marketing', |
| rating: 4, |
| name: 'Michael Ross', |
| amount: 799, |
| date: '2025-01-10', |
| comment: 'Great results! Our token sale was a huge success thanks to their marketing.' |
| }, |
| { |
| service: 'Music Marketing', |
| rating: 5, |
| name: 'Emily Davis', |
| amount: 399, |
| date: '2025-01-08', |
| comment: 'Helped me reach thousands of new listeners. Highly recommend!' |
| } |
| ]; |
|
|
| |
| function loadReviewsFromStorage() { |
| const storedReviews = localStorage.getItem('nomarddesk_reviews'); |
| if (storedReviews) { |
| reviewsData = JSON.parse(storedReviews); |
| } |
| } |
|
|
| |
| function saveReviewsToStorage() { |
| localStorage.setItem('nomarddesk_reviews', JSON.stringify(reviewsData)); |
| } |
| |
| function loadReviews() { |
| const reviewsContainer = document.getElementById('reviewsContainer'); |
| if (!reviewsContainer) return; |
|
|
| reviewsContainer.innerHTML = ''; |
| |
| reviewsData.forEach((review, index) => { |
| const reviewCard = document.createElement('div'); |
| reviewCard.className = 'glass-morphism p-6 rounded-2xl hover:scale-105 transition-all duration-300'; |
| reviewCard.style.animationDelay = `${index * 0.1}s`; |
| reviewCard.classList.add('animate-fade-in-up'); |
| reviewCard.innerHTML = ` |
| <div class="flex items-center mb-3"> |
| <div class="w-12 h-12 bg-gradient-to-br from-purple-500 to-blue-500 rounded-full flex items-center justify-center text-white font-bold mr-3"> |
| ${review.name.charAt(0)} |
| </div> |
| <div> |
| <h4 class="font-semibold">${review.name}</h4> |
| <p class="text-sm text-gray-400">${review.service}</p> |
| </div> |
| </div> |
| <div class="flex mb-3"> |
| ${generateStars(review.rating)} |
| </div> |
| ${review.amount ? `<div class="text-sm text-purple-400 font-semibold mb-2">💰 Spent: ${review.amount.toFixed(2)}</div>` : ''} |
| <p class="text-gray-300">${review.comment}</p> |
| <p class="text-sm text-gray-500 mt-3">${formatDate(review.date)}</p> |
| `; |
| reviewsContainer.appendChild(reviewCard); |
| }); |
| } |
|
|
| function generateStars(rating) { |
| let stars = ''; |
| for (let i = 1; i <= 5; i++) { |
| stars += `<span class="text-yellow-400">${i <= rating ? '★' : '☆'}</span>`; |
| } |
| return stars; |
| } |
|
|
| function formatDate(dateString) { |
| const options = { year: 'numeric', month: 'long', day: 'numeric' }; |
| return new Date(dateString).toLocaleDateString(undefined, options); |
| } |
|
|
| |
| if (document.getElementById('reviewsContainer')) { |
| loadReviews(); |
| } |
|
|
| |
| function validateForm(formData) { |
| const errors = {}; |
| |
| if (!formData.name || formData.name.trim().length < 2) { |
| errors.name = 'Name must be at least 2 characters'; |
| } |
| |
| if (!formData.email || !isValidEmail(formData.email)) { |
| errors.email = 'Please enter a valid email address'; |
| } |
| |
| if (!formData.service) { |
| errors.service = 'Please select a service'; |
| } |
| |
| if (!formData.message || formData.message.trim().length < 10) { |
| errors.message = 'Message must be at least 10 characters'; |
| } |
| |
| return errors; |
| } |
|
|
| function isValidEmail(email) { |
| const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; |
| return emailRegex.test(email); |
| } |
|
|
| |
| function showNotification(message, type = 'success') { |
| const notification = document.createElement('div'); |
| notification.className = `notification ${type}`; |
| notification.textContent = message; |
| document.body.appendChild(notification); |
| |
| setTimeout(() => { |
| notification.classList.add('show'); |
| }, 100); |
| |
| setTimeout(() => { |
| notification.classList.remove('show'); |
| setTimeout(() => { |
| document.body.removeChild(notification); |
| }, 300); |
| }, 3000); |
| } |
|
|
| |
| function handleFormSubmit(formId) { |
| const form = document.getElementById(formId); |
| if (!form) return; |
| |
| form.addEventListener('submit', (e) => { |
| e.preventDefault(); |
| |
| const formData = { |
| name: form.querySelector('#name').value, |
| email: form.querySelector('#email').value, |
| service: form.querySelector('#service').value, |
| message: form.querySelector('#message').value |
| }; |
| |
| const errors = validateForm(formData); |
| |
| if (Object.keys(errors).length > 0) { |
| showNotification('Please fill in all required fields correctly', 'error'); |
| return; |
| } |
| |
| |
| const submitButton = form.querySelector('button[type="submit"]'); |
| const originalText = submitButton.textContent; |
| submitButton.disabled = true; |
| submitButton.innerHTML = '<span class="loading-dots"><span></span><span></span><span></span></span>'; |
| |
| |
| setTimeout(() => { |
| showNotification('Message sent successfully! We\'ll get back to you soon.'); |
| form.reset(); |
| submitButton.disabled = false; |
| submitButton.textContent = originalText; |
| }, 2000); |
| |
| |
| const handleReviewFormSubmit = (e) => { |
| e.preventDefault(); |
| |
| const form = e.target; |
| const service = form.querySelector('#reviewService').value; |
| const name = form.querySelector('#reviewName').value; |
| const amount = form.querySelector('#reviewAmount').value; |
| const feedback = form.querySelector('#reviewFeedback').value; |
| |
| |
| const ratings = window.ratings || {}; |
| const ratingCategories = ['community', 'support', 'pricing', 'quality', 'recommend']; |
| const allRatingsSelected = ratingCategories.every(cat => ratings[cat] > 0); |
| |
| if (!service || !name || !amount || !allRatingsSelected) { |
| showNotification('Please fill in all required fields and rate all categories', 'error'); |
| return; |
| } |
| |
| |
| const submitBtn = form.querySelector('button[type="submit"]'); |
| const originalText = submitBtn.textContent; |
| submitBtn.disabled = true; |
| submitBtn.innerHTML = '<span class="loading-dots"><span></span><span></span><span></span></span>'; |
| |
| |
| const averageRating = Object.values(ratings).reduce((a, b) => a + b, 0) / Object.keys(ratings).length; |
| |
| |
| const newReview = { |
| service: service.replace('-', ' ').replace(/\b\w/g, l => l.toUpperCase()), |
| rating: averageRating, |
| name: name, |
| amount: parseFloat(amount), |
| date: new Date('2025-01-14').toISOString().split('T')[0], |
| comment: feedback || 'Great service!' |
| }; |
| |
| reviewsData.unshift(newReview); |
| saveReviewsToStorage(); |
| |
| |
| if (typeof displayReviews === 'function') { |
| displayReviews(); |
| } |
| |
| |
| const homeReviewsContainer = window.parent.document?.getElementById('reviewsContainer'); |
| if (homeReviewsContainer) { |
| |
| window.parent.location.reload(); |
| } |
| |
| setTimeout(() => { |
| showNotification('Review submitted successfully! Thank you for your feedback.'); |
| form.reset(); |
| Object.keys(ratings).forEach(key => ratings[key] = 0); |
| document.querySelectorAll('.star').forEach(star => { |
| star.classList.remove('active'); |
| star.style.color = ''; |
| }); |
| submitBtn.disabled = false; |
| submitBtn.textContent = originalText; |
| }, 1500); |
| }; |
|
|
| |
| document.getElementById('reviewForm')?.addEventListener('submit', handleReviewFormSubmit); |
| }); |
| } |
| |
| document.addEventListener('DOMContentLoaded', () => { |
| loadReviewsFromStorage(); |
| handleFormSubmit('contactForm'); |
| handleFormSubmit('reviewForm'); |
| }); |
| |
| window.addEventListener('scroll', () => { |
| const scrolled = window.pageYOffset; |
| const parallaxElements = document.querySelectorAll('.parallax'); |
| |
| parallaxElements.forEach(element => { |
| const speed = element.dataset.speed || 0.5; |
| element.style.transform = `translateY(${scrolled * speed}px)`; |
| }); |
| }); |
| |
| document.addEventListener('DOMContentLoaded', () => { |
| const yearElements = document.querySelectorAll('.current-year'); |
| const currentYear = 2025; |
| yearElements.forEach(element => { |
| element.textContent = currentYear; |
| }); |
| }); |
| |
| function toggleMobileMenu() { |
| const mobileMenu = document.querySelector('.mobile-menu'); |
| if (mobileMenu) { |
| mobileMenu.classList.toggle('active'); |
| } |
| } |
|
|
| |
| function initTooltips() { |
| const tooltipElements = document.querySelectorAll('[data-tooltip]'); |
| |
| tooltipElements.forEach(element => { |
| element.addEventListener('mouseenter', (e) => { |
| const tooltip = document.createElement('div'); |
| tooltip.className = 'absolute bg-gray-800 text-white text-sm px-2 py-1 rounded shadow-lg z-50'; |
| tooltip.textContent = e.target.dataset.tooltip; |
| tooltip.style.bottom = '100%'; |
| tooltip.style.left = '50%'; |
| tooltip.style.transform = 'translateX(-50%) translateY(-5px)'; |
| e.target.style.position = 'relative'; |
| e.target.appendChild(tooltip); |
| }); |
| |
| element.addEventListener('mouseleave', (e) => { |
| const tooltip = e.target.querySelector('.absolute'); |
| if (tooltip) { |
| tooltip.remove(); |
| } |
| }); |
| }); |
| } |
|
|
| document.addEventListener('DOMContentLoaded', initTooltips); |