File: /home/workzeni/stream-flix.workzenix.com/public/website/assets/js/scripts.js
/*
* StreamFlix - Netflix-style OTT Platform JavaScript
*
* Features:
* - Carousel autoplay and controls
* - Card hover effects and navigation
* - Sample data population (replace with API endpoints)
* - Video player controls
* - Lazy loading implementation
* - Responsive behavior
*
* API Integration Points:
* - Replace sampleMovies array with actual API calls
* - Update video URLs in playMovie() function
* - Implement search functionality
* - Add user authentication
*/
// Global Variables
let currentMovieData = null;
let autoplayTimer = null;
let videoPlayer = null;
let isVideoPlaying = false;
// DOM Content Loaded Event
document.addEventListener('DOMContentLoaded', function() {
initializeApp();
});
// Initialize Application
function initializeApp() {
// Initialize based on current page
const currentPage = getCurrentPage();
switch(currentPage) {
case 'index':
initializeHomePage();
break;
case 'category':
initializeCategoryPage();
break;
case 'watch':
initializeWatchPage();
break;
}
// Initialize common features
initializeNavigation();
initializeLazyLoading();
initializeAccessibility();
}
// Get Current Page
function getCurrentPage() {
const path = window.location.pathname;
if (path.includes('category.html')) return 'category';
if (path.includes('watch.html')) return 'watch';
return 'index';
}
// Initialize Home Page
function initializeHomePage() {
initializeHeroCarousel();
populateMovieGrids();
initializeHeroAnimation();
initializeHorizontalScroll();
}
// Initialize Hero Carousel
function initializeHeroCarousel() {
const carousel = document.getElementById('heroCarousel');
if (!carousel) return;
// Pause carousel on hover
carousel.addEventListener('mouseenter', function() {
const bsCarousel = bootstrap.Carousel.getInstance(carousel);
if (bsCarousel) {
bsCarousel.pause();
}
});
// Resume carousel when mouse leaves
carousel.addEventListener('mouseleave', function() {
const bsCarousel = bootstrap.Carousel.getInstance(carousel);
if (bsCarousel) {
bsCarousel.cycle();
}
});
}
// Initialize Hero Animation
function initializeHeroAnimation() {
// Trigger hero content animation on page load
setTimeout(() => {
const heroContent = document.querySelector('.hero-content');
if (heroContent) {
heroContent.style.transform = 'translateY(0)';
heroContent.style.opacity = '1';
}
}, 300);
}
// Initialize Horizontal Scroll for Mobile
function initializeHorizontalScroll() {
const horizontalScrollContainers = document.querySelectorAll('.horizontal-scroll');
horizontalScrollContainers.forEach(container => {
let isScrolling = false;
let startX = 0;
let scrollLeft = 0;
// Touch events for mobile smooth scrolling
container.addEventListener('touchstart', (e) => {
isScrolling = true;
startX = e.touches[0].pageX - container.offsetLeft;
scrollLeft = container.scrollLeft;
container.style.scrollBehavior = 'auto';
});
container.addEventListener('touchmove', (e) => {
if (!isScrolling) return;
e.preventDefault();
const x = e.touches[0].pageX - container.offsetLeft;
const walk = (x - startX) * 2; // Scroll speed multiplier
container.scrollLeft = scrollLeft - walk;
});
container.addEventListener('touchend', () => {
isScrolling = false;
container.style.scrollBehavior = 'smooth';
});
// Mouse events for desktop
container.addEventListener('mousedown', (e) => {
isScrolling = true;
container.classList.add('grabbing');
startX = e.pageX - container.offsetLeft;
scrollLeft = container.scrollLeft;
container.style.scrollBehavior = 'auto';
});
container.addEventListener('mouseleave', () => {
isScrolling = false;
container.classList.remove('grabbing');
container.style.scrollBehavior = 'smooth';
});
container.addEventListener('mouseup', () => {
isScrolling = false;
container.classList.remove('grabbing');
container.style.scrollBehavior = 'smooth';
});
container.addEventListener('mousemove', (e) => {
if (!isScrolling) return;
e.preventDefault();
const x = e.pageX - container.offsetLeft;
const walk = (x - startX) * 2;
container.scrollLeft = scrollLeft - walk;
});
// Add scroll indicators
addScrollIndicators(container);
});
}
// Add Scroll Indicators
function addScrollIndicators(container) {
const wrapper = container.parentElement;
if (!wrapper || wrapper.querySelector('.scroll-indicator')) return;
// Left indicator
const leftIndicator = document.createElement('div');
leftIndicator.className = 'scroll-indicator scroll-indicator-left';
leftIndicator.innerHTML = '<i class="bi bi-chevron-left"></i>';
leftIndicator.addEventListener('click', () => {
container.scrollBy({ left: -200, behavior: 'smooth' });
});
// Right indicator
const rightIndicator = document.createElement('div');
rightIndicator.className = 'scroll-indicator scroll-indicator-right';
rightIndicator.innerHTML = '<i class="bi bi-chevron-right"></i>';
rightIndicator.addEventListener('click', () => {
container.scrollBy({ left: 200, behavior: 'smooth' });
});
wrapper.style.position = 'relative';
wrapper.appendChild(leftIndicator);
wrapper.appendChild(rightIndicator);
// Update indicator visibility based on scroll position
const updateIndicators = () => {
const isAtStart = container.scrollLeft <= 10;
const isAtEnd = container.scrollLeft >= container.scrollWidth - container.clientWidth - 10;
leftIndicator.style.opacity = isAtStart ? '0' : '1';
rightIndicator.style.opacity = isAtEnd ? '0' : '1';
};
container.addEventListener('scroll', updateIndicators);
updateIndicators(); // Initial check
}
// Populate Movie Grids
function populateMovieGrids() {
// Trending Now (Latest Movies)
populateMovieGrid('latestMoviesGrid', .slice(0, 10));
populateMovieGrid('latestMoviesHorizontal', .slice(0, 10), true);
// Popular Movies
const popularMovies = [...sampleMovies].sort((a, b) => b.rating - a.rating);
populateMovieGrid('popularMoviesGrid', popularMovies.slice(0, 10));
populateMovieGrid('popularMoviesHorizontal', popularMovies.slice(0, 10), true);
// Category-specific grids
const actionMovies = sampleMovies.filter(m => m.category === 'action').slice(0, 8);
populateMovieGrid('actionMoviesGrid', actionMovies);
populateMovieGrid('actionMoviesHorizontal', actionMovies, true);
// New Releases
const newReleases = [...sampleMovies].sort((a, b) => b.year - a.year);
populateMovieGrid('newReleasesGrid', newReleases.slice(0, 8));
populateMovieGrid('newReleasesHorizontal', newReleases.slice(0, 8), true);
}
// Populate Movie Grid
function populateMovieGrid(containerId, movies, isHorizontal = false) {
const container = document.getElementById(containerId);
if (!container) return;
container.innerHTML = '';
movies.forEach(movie => {
const movieCard = createMovieCard(movie, isHorizontal);
container.appendChild(movieCard);
});
}
// Create Movie Card
function createMovieCard(movie, isHorizontal = false) {
const colClass = isHorizontal ? '' : 'col-6 col-md-4 col-lg-2';
// Create language badges
const languageBadges = movie.languages ? movie.languages.slice(0, 3).map(lang =>
`<span class="language-badge">${lang}</span>`
).join('') : '';
// Show "+X more" if there are more than 3 languages
const moreLanguages = movie.languages && movie.languages.length > 3 ?
`<span class="language-badge more-languages">+${movie.languages.length - 3}</span>` : '';
const cardElement = document.createElement('div');
cardElement.className = colClass;
cardElement.innerHTML = `
<div class="movie-card" tabindex="0" role="button" aria-label="Play ${movie.title}"
onclick="playMovie('${movie.id}')" onkeydown="handleCardKeydown(event, '${movie.id}')">
<div class="movie-poster">
<img src="${movie.poster}" alt="${movie.title}" loading="lazy">
<div class="language-badges">
${languageBadges}${moreLanguages}
</div>
<div class="movie-overlay">
<div class="play-icon">
<i class="bi bi-play-fill"></i>
</div>
</div>
</div>
<div class="movie-info">
<h6 class="movie-card-title">${movie.title}</h6>
<div class="movie-meta">
<span class="badge bg-secondary-accent me-1">${movie.year}</span>
<span class="text-muted">${movie.duration}</span>
</div>
</div>
</div>
`;
return cardElement;
}
// Initialize Category Page
function initializeCategoryPage() {
const urlParams = new URLSearchParams(window.location.search);
const category = urlParams.get('category') || 'action';
updateCategoryTitle(category);
populateCategoryMovies(category);
initializeCategoryFilters();
}
// Update Category Title
function updateCategoryTitle(category) {
const titleElement = document.getElementById('categoryTitle');
if (titleElement) {
const categoryNames = {
'action': 'Action Movies',
'drama': 'Drama Movies',
'comedy': 'Comedy Movies',
'thriller': 'Thriller Movies',
'sci-fi': 'Sci-Fi Movies',
'latest': 'Latest Movies',
'popular': 'Popular Movies'
};
titleElement.textContent = categoryNames[category] || 'Movies';
}
}
// Populate Category Movies
function populateCategoryMovies(category) {
let filteredMovies;
if (category === 'latest') {
filteredMovies = [...sampleMovies].sort((a, b) => b.year - a.year);
} else if (category === 'popular') {
filteredMovies = [...sampleMovies].sort((a, b) => b.rating - a.rating);
} else {
filteredMovies = sampleMovies.filter(movie => movie.category === category);
}
populateMovieGrid('categoryMoviesGrid', filteredMovies);
}
// Initialize Category Filters
function initializeCategoryFilters() {
// Desktop filters
const sortRadios = document.querySelectorAll('input[name="sortBy"]');
sortRadios.forEach(radio => {
radio.addEventListener('change', handleSortChange);
});
const yearFilter = document.getElementById('yearFilter');
if (yearFilter) {
yearFilter.addEventListener('change', handleYearChange);
}
// Mobile filters
const mobileSortBy = document.getElementById('mobileSortBy');
if (mobileSortBy) {
mobileSortBy.addEventListener('change', handleSortChange);
}
const mobileYearFilter = document.getElementById('mobileYearFilter');
if (mobileYearFilter) {
mobileYearFilter.addEventListener('change', handleYearChange);
}
}
// Handle Sort Change
function handleSortChange(event) {
const sortBy = event.target.value;
const urlParams = new URLSearchParams(window.location.search);
const category = urlParams.get('category') || 'action';
let sortedMovies = [...sampleMovies];
if (category !== 'latest' && category !== 'popular') {
sortedMovies = sortedMovies.filter(movie => movie.category === category);
}
switch(sortBy) {
case 'latest':
sortedMovies.sort((a, b) => b.year - a.year);
break;
case 'popular':
sortedMovies.sort((a, b) => b.rating - a.rating);
break;
case 'rated':
sortedMovies.sort((a, b) => b.rating - a.rating);
break;
}
populateMovieGrid('categoryMoviesGrid', sortedMovies);
}
// Handle Year Change
function handleYearChange(event) {
const selectedYear = event.target.value;
const urlParams = new URLSearchParams(window.location.search);
const category = urlParams.get('category') || 'action';
let filteredMovies = [...sampleMovies];
if (category !== 'latest' && category !== 'popular') {
filteredMovies = filteredMovies.filter(movie => movie.category === category);
}
if (selectedYear) {
filteredMovies = filteredMovies.filter(movie => movie.year.toString() === selectedYear);
}
populateMovieGrid('categoryMoviesGrid', filteredMovies);
}
// Initialize Watch Page
function initializeWatchPage() {
initializeVideoPlayer();
populateRelatedMovies();
populatePopularMoviesSidebar();
initializeAutoplayNext();
initializeHorizontalScroll(); // Add horizontal scroll for mobile
// Get movie ID from URL params
const urlParams = new URLSearchParams(window.location.search);
const movieId = urlParams.get('id');
if (movieId) {
loadMovieData(movieId);
}
}
// Initialize Video Player
function initializeVideoPlayer() {
videoPlayer = document.getElementById('mainVideoPlayer');
if (!videoPlayer) return;
const playPauseBtn = document.getElementById('playPauseBtn');
const muteBtn = document.getElementById('muteBtn');
const fullscreenBtn = document.getElementById('fullscreenBtn');
const volumeSlider = document.getElementById('volumeSlider');
const progressFilled = document.getElementById('progressFilled');
const progressHandle = document.getElementById('progressHandle');
const timeDisplay = document.getElementById('timeDisplay');
// Play/Pause functionality
if (playPauseBtn) {
playPauseBtn.addEventListener('click', togglePlayPause);
}
// Mute functionality
if (muteBtn) {
muteBtn.addEventListener('click', toggleMute);
}
// Fullscreen functionality
if (fullscreenBtn) {
fullscreenBtn.addEventListener('click', toggleFullscreen);
}
// Volume control
if (volumeSlider) {
volumeSlider.addEventListener('input', function() {
videoPlayer.volume = this.value / 100;
updateVolumeIcon();
});
}
// Video event listeners
videoPlayer.addEventListener('loadstart', showVideoLoading);
videoPlayer.addEventListener('canplay', hideVideoLoading);
videoPlayer.addEventListener('timeupdate', updateProgress);
videoPlayer.addEventListener('ended', handleVideoEnded);
// Progress bar click
const progressBar = document.querySelector('.progress-bar');
if (progressBar) {
progressBar.addEventListener('click', seekVideo);
}
// Keyboard controls
document.addEventListener('keydown', handleVideoKeyboard);
}
// Toggle Play/Pause
function togglePlayPause() {
if (!videoPlayer) return;
if (videoPlayer.paused) {
videoPlayer.play();
isVideoPlaying = true;
document.getElementById('playIcon').className = 'bi bi-pause-fill';
} else {
videoPlayer.pause();
isVideoPlaying = false;
document.getElementById('playIcon').className = 'bi bi-play-fill';
}
}
// Toggle Mute
function toggleMute() {
if (!videoPlayer) return;
videoPlayer.muted = !videoPlayer.muted;
updateVolumeIcon();
}
// Update Volume Icon
function updateVolumeIcon() {
const volumeIcon = document.getElementById('volumeIcon');
if (!volumeIcon || !videoPlayer) return;
if (videoPlayer.muted || videoPlayer.volume === 0) {
volumeIcon.className = 'bi bi-volume-mute-fill';
} else if (videoPlayer.volume < 0.5) {
volumeIcon.className = 'bi bi-volume-down-fill';
} else {
volumeIcon.className = 'bi bi-volume-up-fill';
}
}
// Toggle Fullscreen
function toggleFullscreen() {
const videoContainer = document.querySelector('.video-container');
if (!videoContainer) return;
if (!document.fullscreenElement) {
videoContainer.requestFullscreen();
} else {
document.exitFullscreen();
}
}
// Update Progress
function updateProgress() {
if (!videoPlayer) return;
const progressFilled = document.getElementById('progressFilled');
const progressHandle = document.getElementById('progressHandle');
const timeDisplay = document.getElementById('timeDisplay');
const progress = (videoPlayer.currentTime / videoPlayer.duration) * 100;
if (progressFilled) {
progressFilled.style.width = progress + '%';
}
if (progressHandle) {
progressHandle.style.left = progress + '%';
}
if (timeDisplay) {
const current = formatTime(videoPlayer.currentTime);
const total = formatTime(videoPlayer.duration);
timeDisplay.textContent = `${current} / ${total}`;
}
}
// Seek Video
function seekVideo(event) {
if (!videoPlayer) return;
const progressBar = event.currentTarget;
const rect = progressBar.getBoundingClientRect();
const clickX = event.clientX - rect.left;
const width = rect.width;
const percentage = clickX / width;
videoPlayer.currentTime = percentage * videoPlayer.duration;
}
// Format Time
function formatTime(seconds) {
if (isNaN(seconds)) return '0:00';
const minutes = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${minutes}:${secs.toString().padStart(2, '0')}`;
}
// Show/Hide Video Loading
function showVideoLoading() {
const loading = document.getElementById('videoLoading');
if (loading) loading.style.display = 'block';
}
function hideVideoLoading() {
const loading = document.getElementById('videoLoading');
if (loading) loading.style.display = 'none';
}
// Handle Video Ended
function handleVideoEnded() {
isVideoPlaying = false;
document.getElementById('playIcon').className = 'bi bi-play-fill';
// Auto-play next video logic can be added here
}
// Handle Video Keyboard Controls
function handleVideoKeyboard(event) {
if (!videoPlayer) return;
switch(event.code) {
case 'Space':
event.preventDefault();
togglePlayPause();
break;
case 'ArrowLeft':
event.preventDefault();
videoPlayer.currentTime -= 10;
break;
case 'ArrowRight':
event.preventDefault();
videoPlayer.currentTime += 10;
break;
case 'ArrowUp':
event.preventDefault();
videoPlayer.volume = Math.min(1, videoPlayer.volume + 0.1);
updateVolumeIcon();
break;
case 'ArrowDown':
event.preventDefault();
videoPlayer.volume = Math.max(0, videoPlayer.volume - 0.1);
updateVolumeIcon();
break;
case 'KeyM':
event.preventDefault();
toggleMute();
break;
case 'KeyF':
event.preventDefault();
toggleFullscreen();
break;
}
}
// Load Movie Data
function loadMovieData(movieId) {
const movie = sampleMovies.find(m => m.id.toString() === movieId);
if (!movie) return;
currentMovieData = movie;
// Update movie information
const titleElement = document.getElementById('movieTitle');
if (titleElement) {
titleElement.textContent = movie.title;
}
// Update video player poster
if (videoPlayer) {
videoPlayer.poster = movie.backdrop;
// In a real application, set the video source here
// videoPlayer.src = movie.videoUrl;
}
}
// Populate Related Movies
function populateRelatedMovies() {
const container = document.getElementById('relatedMovies');
const horizontalContainer = document.getElementById('relatedMoviesHorizontal');
// Get movies from the same category as current movie (action movies for demo)
const relatedMovies = sampleMovies.filter(m => m.category === 'action').slice(0, 6);
// Populate desktop vertical list
if (container) {
container.innerHTML = '';
relatedMovies.slice(0, 5).forEach(movie => {
const relatedItem = document.createElement('div');
relatedItem.className = 'related-movie-item';
relatedItem.innerHTML = `
<div class="related-poster">
<img src="${movie.poster}" alt="${movie.title}" loading="lazy">
<div class="related-overlay">
<i class="bi bi-play-fill"></i>
</div>
</div>
<div class="related-info">
<h6 class="related-title">${movie.title}</h6>
<p class="related-meta">${movie.year} • ${movie.duration}</p>
<div class="related-languages">
${movie.languages ? movie.languages.slice(0, 2).map(lang =>
`<span class="language-tag">${lang}</span>`
).join('') : ''}
</div>
</div>
`;
relatedItem.addEventListener('click', () => playMovie(movie.id));
container.appendChild(relatedItem);
});
}
// Populate mobile horizontal scroll
if (horizontalContainer) {
horizontalContainer.innerHTML = '';
relatedMovies.forEach(movie => {
const cardElement = document.createElement('div');
cardElement.className = 'related-movie-card';
cardElement.innerHTML = `
<div class="card-poster">
<img src="${movie.poster}" alt="${movie.title}" loading="lazy">
<div class="card-overlay">
<div class="play-btn">
<i class="bi bi-play-fill"></i>
</div>
</div>
</div>
<div class="card-info">
<h6 class="card-title">${movie.title}</h6>
<p class="card-meta">${movie.year}</p>
</div>
`;
cardElement.addEventListener('click', () => playMovie(movie.id));
horizontalContainer.appendChild(cardElement);
});
}
}
// Initialize Autoplay Next
function initializeAutoplayNext() {
// Start countdown for next video
startAutoplayCountdown();
}
// Start Autoplay Countdown
function startAutoplayCountdown() {
let countdown = 5;
const countdownElement = document.getElementById('countdownTimer');
if (!countdownElement) return;
autoplayTimer = setInterval(() => {
countdownElement.textContent = countdown;
countdown--;
if (countdown < 0) {
clearInterval(autoplayTimer);
// Auto-play next video
playNextVideo();
}
}, 1000);
}
// Cancel Autoplay
function cancelAutoplay() {
if (autoplayTimer) {
clearInterval(autoplayTimer);
autoplayTimer = null;
}
const upNextSection = document.querySelector('.up-next-section');
if (upNextSection) {
upNextSection.style.display = 'none';
}
}
// Play Next Video
function playNextVideo() {
// Logic to play next video
console.log('Playing next video...');
// In a real application, navigate to the next video
}
// Play Movie Function
function playMovie(movieId) {
// Navigate to view page with movie ID
window.location.href = `view.html?id=${movieId}`;
}
// Load More Movies (for category page)
function loadMoreMovies() {
const loadMoreBtn = document.getElementById('loadMoreBtn');
if (loadMoreBtn) {
loadMoreBtn.innerHTML = '<i class="bi bi-arrow-clockwise me-2"></i>Loading...';
loadMoreBtn.disabled = true;
// Simulate loading delay
setTimeout(() => {
// Add more movies to the grid
const container = document.getElementById('categoryMoviesGrid');
if (container) {
const additionalMovies = sampleMovies.slice(0, 8);
additionalMovies.forEach(movie => {
const movieCard = createMovieCard(movie);
container.appendChild(movieCard);
});
}
loadMoreBtn.innerHTML = '<i class="bi bi-arrow-down-circle me-2"></i>Load More';
loadMoreBtn.disabled = true;
}, 1000);
}
}
// Initialize Navigation
function initializeNavigation() {
// Add active states to navigation items
const currentPage = getCurrentPage();
const navItems = document.querySelectorAll('.navbar-nav .nav-link');
navItems.forEach(item => {
const href = item.getAttribute('href');
if (href && href.includes(currentPage)) {
item.classList.add('active');
}
});
// Add scroll effect to navbar
initializeNavbarScroll();
}
// Initialize Navbar Scroll Effect
function initializeNavbarScroll() {
const navbar = document.querySelector('.custom-navbar');
if (!navbar) return;
const handleScroll = throttle(() => {
if (window.scrollY > 50) {
navbar.classList.add('scrolled');
} else {
navbar.classList.remove('scrolled');
}
}, 100);
window.addEventListener('scroll', handleScroll);
}
// Initialize Lazy Loading
function initializeLazyLoading() {
// Enhanced lazy loading for images
const images = document.querySelectorAll('img[loading="lazy"]');
if ('IntersectionObserver' in window) {
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.src; // Trigger loading
img.classList.remove('loading');
observer.unobserve(img);
}
});
});
images.forEach(img => {
img.classList.add('loading');
imageObserver.observe(img);
});
}
}
// Initialize Accessibility
function initializeAccessibility() {
// Add keyboard navigation for movie cards
const movieCards = document.querySelectorAll('.movie-card');
movieCards.forEach(card => {
card.addEventListener('keydown', function(event) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
card.click();
}
});
});
// Add focus management for modals and dropdowns
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
// Close any open dropdowns or modals
const openDropdowns = document.querySelectorAll('.dropdown-menu.show');
openDropdowns.forEach(dropdown => {
const toggle = dropdown.previousElementSibling;
if (toggle) {
bootstrap.Dropdown.getInstance(toggle)?.hide();
}
});
}
});
}
// Handle Card Keydown
function handleCardKeydown(event, movieId) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
playMovie(movieId);
}
}
// Utility Functions
// Debounce function for search and filters
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Throttle function for scroll events
function throttle(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// Local Storage Helpers
function saveToLocalStorage(key, data) {
try {
localStorage.setItem(key, JSON.stringify(data));
} catch (error) {
console.error('Error saving to localStorage:', error);
}
}
function getFromLocalStorage(key) {
try {
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : null;
} catch (error) {
console.error('Error reading from localStorage:', error);
return null;
}
}
// API Integration Helpers (for future backend integration)
async function fetchMovies(category = '', page = 1) {
try {
// Replace with actual API endpoint
const response = await fetch(`/api/movies?category=${category}&page=${page}`);
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching movies:', error);
// Fallback to sample data
return { movies: sampleMovies, totalPages: 1 };
}
}
async function searchMovies(query) {
try {
// Replace with actual API endpoint
const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
const data = await response.json();
return data;
} catch (error) {
console.error('Error searching movies:', error);
// Fallback to local search
return sampleMovies.filter(movie =>
movie.title.toLowerCase().includes(query.toLowerCase())
);
}
}
// Error Handling
window.addEventListener('error', function(event) {
console.error('Global error:', event.error);
// In production, send error to logging service
});
// Performance Monitoring
if ('performance' in window) {
window.addEventListener('load', function() {
setTimeout(() => {
const perfData = performance.getEntriesByType('navigation')[0];
console.log('Page load time:', perfData.loadEventEnd - perfData.loadEventStart);
}, 0);
});
}
// Share Functionality
function shareToWhatsApp() {
const movieTitle = getCurrentMovieTitle();
const movieUrl = getCurrentMovieUrl();
const text = `Check out this amazing movie: ${movieTitle}`;
const whatsappUrl = `https://wa.me/?text=${encodeURIComponent(text + ' ' + movieUrl)}`;
window.open(whatsappUrl, '_blank');
}
function shareToFacebook() {
const movieUrl = getCurrentMovieUrl();
const facebookUrl = `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(movieUrl)}`;
window.open(facebookUrl, '_blank', 'width=600,height=400');
}
function shareToInstagram() {
// Instagram doesn't support direct URL sharing, so we copy the link and show instructions
copyMovieLink();
showShareMessage('Link copied! Open Instagram and paste it in your story or post.');
}
function shareToMessenger() {
const movieUrl = getCurrentMovieUrl();
const messengerUrl = `https://www.messenger.com/t/?link=${encodeURIComponent(movieUrl)}`;
window.open(messengerUrl, '_blank');
}
function shareToTwitter() {
const movieTitle = getCurrentMovieTitle();
const movieUrl = getCurrentMovieUrl();
const text = `Just watched "${movieTitle}" on StreamFlix! 🎬`;
const twitterUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(text)}&url=${encodeURIComponent(movieUrl)}`;
window.open(twitterUrl, '_blank', 'width=600,height=400');
}
function copyMovieLink() {
const movieUrl = getCurrentMovieUrl();
// Try to use the modern Clipboard API
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(movieUrl).then(() => {
showCopySuccess();
}).catch(() => {
fallbackCopyTextToClipboard(movieUrl);
});
} else {
// Fallback for older browsers
fallbackCopyTextToClipboard(movieUrl);
}
}
function fallbackCopyTextToClipboard(text) {
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
textArea.style.top = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
showCopySuccess();
} catch (err) {
console.error('Fallback: Oops, unable to copy', err);
showShareMessage('Unable to copy link. Please copy the URL manually.');
}
document.body.removeChild(textArea);
}
function showCopySuccess() {
const copyIcon = document.querySelector('.share-icon.copy');
if (copyIcon) {
copyIcon.classList.add('copy-success');
setTimeout(() => {
copyIcon.classList.remove('copy-success');
}, 300);
}
showShareMessage('Link copied to clipboard!');
}
function showShareMessage(message) {
// Create a toast notification
const toast = document.createElement('div');
toast.className = 'share-toast';
toast.innerHTML = `
<div class="toast-content">
<i class="bi bi-check-circle-fill text-success me-2"></i>
<span>${message}</span>
</div>
`;
// Add toast styles
toast.style.cssText = `
position: fixed;
top: 100px;
right: 20px;
background: var(--surface-bg);
color: var(--text-primary);
padding: 16px 20px;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
border: 1px solid rgba(255, 255, 255, 0.1);
z-index: 9999;
transform: translateX(100%);
transition: transform 0.3s ease-out;
max-width: 300px;
`;
document.body.appendChild(toast);
// Animate in
setTimeout(() => {
toast.style.transform = 'translateX(0)';
}, 10);
// Remove after 3 seconds
setTimeout(() => {
toast.style.transform = 'translateX(100%)';
setTimeout(() => {
if (document.body.contains(toast)) {
document.body.removeChild(toast);
}
}, 300);
}, 3000);
}
function getCurrentMovieTitle() {
const titleElement = document.getElementById('movieTitle');
return titleElement ? titleElement.textContent : 'Amazing Movie';
}
function getCurrentMovieUrl() {
return window.location.href;
}
// Web Share API (for modern browsers)
function nativeShare() {
if (navigator.share) {
const movieTitle = getCurrentMovieTitle();
const movieUrl = getCurrentMovieUrl();
navigator.share({
title: movieTitle,
text: `Check out "${movieTitle}" on StreamFlix!`,
url: movieUrl
}).then(() => {
console.log('Thanks for sharing!');
}).catch((error) => {
console.log('Error sharing:', error);
});
}
}
// Populate Popular Movies in Sidebar
function populatePopularMoviesSidebar() {
const container = document.getElementById('popularMovies');
const horizontalContainer = document.getElementById('popularMoviesHorizontal');
// Get top rated movies
const popularMovies = [...sampleMovies].sort((a, b) => b.rating - a.rating).slice(0, 6);
// Populate desktop vertical list
if (container) {
container.innerHTML = '';
popularMovies.forEach(movie => {
const popularItem = document.createElement('div');
popularItem.className = 'related-movie-item';
popularItem.innerHTML = `
<div class="related-poster">
<img src="${movie.poster}" alt="${movie.title}" loading="lazy">
<div class="related-overlay">
<i class="bi bi-play-fill"></i>
</div>
<div class="rating-badge">
<i class="bi bi-star-fill"></i>
<span>${movie.rating}</span>
</div>
</div>
<div class="related-info">
<h6 class="related-title">${movie.title}</h6>
<p class="related-meta">${movie.year} • ${movie.duration}</p>
<div class="related-languages">
${movie.languages ? movie.languages.slice(0, 2).map(lang =>
`<span class="language-tag">${lang}</span>`
).join('') : ''}
</div>
</div>
`;
popularItem.addEventListener('click', () => playMovie(movie.id));
container.appendChild(popularItem);
});
}
// Populate mobile horizontal scroll
if (horizontalContainer) {
horizontalContainer.innerHTML = '';
popularMovies.forEach(movie => {
const cardElement = document.createElement('div');
cardElement.className = 'related-movie-card';
cardElement.innerHTML = `
<div class="card-poster">
<img src="${movie.poster}" alt="${movie.title}" loading="lazy">
<div class="card-overlay">
<div class="play-btn">
<i class="bi bi-play-fill"></i>
</div>
</div>
<div class="rating-badge">
<i class="bi bi-star-fill"></i>
<span>${movie.rating}</span>
</div>
</div>
<div class="card-info">
<h6 class="card-title">${movie.title}</h6>
<p class="card-meta">${movie.year}</p>
</div>
`;
cardElement.addEventListener('click', () => playMovie(movie.id));
horizontalContainer.appendChild(cardElement);
});
}
}
// Language Selector Functions
function changeLanguage(langCode, langName) {
// Update the selected language display
const selectedLanguage = document.getElementById('selectedLanguage');
if (selectedLanguage) {
selectedLanguage.textContent = langName;
}
// Update active state in dropdown
const languageItems = document.querySelectorAll('.language-item[data-lang^="' + langCode + '"]');
const allLanguageItems = document.querySelectorAll('.language-item');
// Remove active class from all items
allLanguageItems.forEach(item => {
item.classList.remove('active');
const check = item.querySelector('.language-check');
if (check) {
check.className = 'bi bi-circle me-2 language-check';
}
});
// Add active class to selected language
const selectedItem = document.querySelector('.language-item[data-lang="' + langCode + '"]');
if (selectedItem) {
selectedItem.classList.add('active');
const check = selectedItem.querySelector('.language-check');
if (check) {
check.className = 'bi bi-check-circle-fill me-2 language-check';
}
}
// Show language change message
showVideoMessage(`Audio language changed to ${langName}`);
// In a real application, this would change the audio track
console.log(`Language changed to: ${langName} (${langCode})`);
}
function changeSubtitles(langCode, langName) {
// Toggle subtitles for the selected language
if (videoPlayer && videoPlayer.textTracks) {
// Hide all text tracks
for (let i = 0; i < videoPlayer.textTracks.length; i++) {
videoPlayer.textTracks[i].mode = 'hidden';
}
// Show selected language subtitles (if available)
const track = Array.from(videoPlayer.textTracks).find(track =>
track.language === langCode || track.label.toLowerCase().includes(langCode)
);
if (track) {
track.mode = 'showing';
showVideoMessage(`${langName} enabled`);
} else {
showVideoMessage(`${langName} not available`);
}
} else {
showVideoMessage(`${langName} selected`);
}
console.log(`Subtitles changed to: ${langName} (${langCode})`);
}
// Enhanced Video Control Functions
function toggleCaptions() {
if (!videoPlayer) return;
const tracks = videoPlayer.textTracks;
const captionsBtn = document.getElementById('captionsBtn');
if (tracks.length > 0) {
const track = tracks[0];
if (track.mode === 'showing') {
track.mode = 'hidden';
captionsBtn.classList.remove('active');
showVideoMessage('Captions Off');
} else {
track.mode = 'showing';
captionsBtn.classList.add('active');
showVideoMessage('Captions On');
}
} else {
showVideoMessage('No captions available');
}
}
function toggleSettings() {
const settingsMenu = document.getElementById('settingsMenu');
if (!settingsMenu) {
createSettingsMenu();
} else {
settingsMenu.style.display = settingsMenu.style.display === 'none' ? 'block' : 'none';
}
}
function createSettingsMenu() {
const videoContainer = document.querySelector('.video-container');
if (!videoContainer) return;
const settingsMenu = document.createElement('div');
settingsMenu.id = 'settingsMenu';
settingsMenu.className = 'settings-menu';
settingsMenu.innerHTML = `
<div class="settings-content">
<h6>Video Settings</h6>
<div class="setting-item">
<label>Quality:</label>
<select id="qualitySelect" class="form-select form-select-sm">
<option value="auto">Auto</option>
<option value="1080p">1080p HD</option>
<option value="720p">720p HD</option>
<option value="480p">480p</option>
<option value="360p">360p</option>
</select>
</div>
<div class="setting-item">
<label>Playback Speed:</label>
<select id="speedSelect" class="form-select form-select-sm">
<option value="0.5">0.5x</option>
<option value="0.75">0.75x</option>
<option value="1" selected>Normal</option>
<option value="1.25">1.25x</option>
<option value="1.5">1.5x</option>
<option value="2">2x</option>
</select>
</div>
<div class="setting-item">
<label>Language:</label>
<select id="languageSelect" class="form-select form-select-sm">
<option value="en">English</option>
<option value="hi">Hindi</option>
<option value="ru">Russian</option>
</select>
</div>
</div>
`;
videoContainer.appendChild(settingsMenu);
// Close settings menu when clicking outside
document.addEventListener('click', function(event) {
if (!settingsMenu.contains(event.target) && !document.getElementById('settingsBtn').contains(event.target)) {
settingsMenu.style.display = 'none';
}
});
// Add event listeners for settings
const speedSelect = document.getElementById('speedSelect');
if (speedSelect) {
speedSelect.addEventListener('change', function() {
if (videoPlayer) {
videoPlayer.playbackRate = parseFloat(this.value);
showVideoMessage(`Speed: ${this.value}x`);
}
});
}
const languageSelect = document.getElementById('languageSelect');
if (languageSelect) {
languageSelect.addEventListener('change', function() {
const selectedOption = this.options[this.selectedIndex];
changeLanguage(this.value, selectedOption.text);
});
}
const qualitySelect = document.getElementById('qualitySelect');
if (qualitySelect) {
qualitySelect.addEventListener('change', function() {
showVideoMessage(`Quality: ${this.value}`);
});
}
}
function showVideoMessage(message) {
const videoContainer = document.querySelector('.video-container');
if (!videoContainer) return;
// Remove existing message
const existingMessage = videoContainer.querySelector('.video-message');
if (existingMessage) {
existingMessage.remove();
}
const messageDiv = document.createElement('div');
messageDiv.className = 'video-message';
messageDiv.textContent = message;
messageDiv.style.cssText = `
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 12px 20px;
border-radius: 8px;
font-size: 1rem;
font-weight: 500;
z-index: 1000;
pointer-events: none;
backdrop-filter: blur(10px);
`;
videoContainer.appendChild(messageDiv);
// Remove message after 2 seconds
setTimeout(() => {
if (messageDiv && messageDiv.parentNode) {
messageDiv.remove();
}
}, 2000);
}
// Enhanced Fullscreen Function
function toggleFullscreen() {
const videoContainer = document.querySelector('.video-container');
if (!videoContainer) return;
if (!document.fullscreenElement) {
videoContainer.requestFullscreen().then(() => {
showVideoMessage('Entered fullscreen');
}).catch(err => {
console.error('Error attempting to enable fullscreen:', err);
showVideoMessage('Fullscreen not supported');
});
} else {
document.exitFullscreen().then(() => {
showVideoMessage('Exited fullscreen');
});
}
}
// ===== REQUEST PAGE FUNCTIONALITY =====
// Initialize request page functionality
function initializeRequestPage() {
initializeFormValidation();
initializeFadeInAnimations();
initializeNavigation();
}
// Form validation and submission
function initializeFormValidation() {
const form = document.getElementById('movieRequestForm');
if (!form) return; // Exit if not on request page
const mobileInput = document.getElementById('mobileNumber');
// Mobile number input formatting
mobileInput.addEventListener('input', function(e) {
// Remove non-numeric characters
this.value = this.value.replace(/[^0-9]/g, '');
// Limit to 15 digits
if (this.value.length > 15) {
this.value = this.value.slice(0, 15);
}
});
// Form submission
form.addEventListener('submit', function(e) {
e.preventDefault();
if (validateRequestForm()) {
submitMovieRequest();
}
});
}
// Validate request form fields
function validateRequestForm() {
const form = document.getElementById('movieRequestForm');
const inputs = form.querySelectorAll('input[required], select[required]');
let isValid = true;
inputs.forEach(input => {
if (!input.value.trim()) {
input.classList.add('is-invalid');
isValid = false;
} else {
input.classList.remove('is-invalid');
input.classList.add('is-valid');
}
});
// Validate mobile number
const mobileInput = document.getElementById('mobileNumber');
const mobilePattern = /^[0-9]{10,15}$/;
if (!mobilePattern.test(mobileInput.value)) {
mobileInput.classList.add('is-invalid');
isValid = false;
}
return isValid;
}
// Submit movie request
function submitMovieRequest() {
const submitBtn = document.querySelector('.btn-request');
const btnText = submitBtn.querySelector('.btn-text');
const btnLoading = submitBtn.querySelector('.btn-loading');
const form = document.getElementById('movieRequestForm');
const successMessage = document.getElementById('successMessage');
// Show loading state
btnText.classList.add('d-none');
btnLoading.classList.remove('d-none');
submitBtn.disabled = true;
// Get form data
const formData = new FormData(form);
const requestData = {
fullName: formData.get('fullName'),
mobileNumber: formData.get('mobileNumber'),
movieName: formData.get('movieName'),
preferredLanguage: formData.get('preferredLanguage'),
additionalNotes: formData.get('additionalNotes'),
timestamp: new Date().toISOString()
};
// Simulate API call (replace with actual API endpoint)
setTimeout(() => {
// In a real application, you would send this data to your backend
console.log('Movie request submitted:', requestData);
// Hide form and show success message
form.parentElement.classList.add('d-none');
successMessage.classList.remove('d-none');
// Scroll to success message
successMessage.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
// Show success toast
showSuccessToast('Your movie request has been sent successfully!');
// Reset button state
btnText.classList.remove('d-none');
btnLoading.classList.add('d-none');
submitBtn.disabled = false;
// Store request in localStorage for demo purposes
storeMovieRequest(requestData);
}, 2000); // 2 second delay to simulate network request
}
// Store movie request in localStorage (for demo purposes)
function storeMovieRequest(requestData) {
try {
let requests = JSON.parse(localStorage.getItem('movieRequests') || '[]');
requests.push(requestData);
localStorage.setItem('movieRequests', JSON.stringify(requests));
} catch (error) {
console.error('Error storing movie request:', error);
}
}
// Reset request form
function resetRequestForm() {
const form = document.getElementById('movieRequestForm');
const successMessage = document.getElementById('successMessage');
// Reset form
form.reset();
form.querySelectorAll('.is-valid, .is-invalid').forEach(input => {
input.classList.remove('is-valid', 'is-invalid');
});
// Show form and hide success message
form.parentElement.classList.remove('d-none');
successMessage.classList.add('d-none');
// Scroll to form
form.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
// Show success toast notification
function showSuccessToast(message) {
const toast = document.createElement('div');
toast.className = 'success-toast';
toast.innerHTML = `
<div class="toast-content">
<i class="bi bi-check-circle-fill text-success me-2"></i>
<span>${message}</span>
</div>
`;
// Add toast styles
toast.style.cssText = `
position: fixed;
top: 100px;
right: 20px;
background: var(--surface-bg);
color: var(--text-primary);
padding: 16px 20px;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
border: 1px solid rgba(255, 255, 255, 0.1);
z-index: 9999;
transform: translateX(100%);
transition: transform 0.3s ease-out;
max-width: 300px;
backdrop-filter: blur(10px);
`;
document.body.appendChild(toast);
// Animate in
setTimeout(() => {
toast.style.transform = 'translateX(0)';
}, 10);
// Remove after 4 seconds
setTimeout(() => {
toast.style.transform = 'translateX(100%)';
setTimeout(() => {
if (document.body.contains(toast)) {
document.body.removeChild(toast);
}
}, 300);
}, 4000);
}
// Initialize fade-in animations for request page
function initializeFadeInAnimations() {
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('fade-in-visible');
observer.unobserve(entry.target);
}
});
}, observerOptions);
// Observe all fade-in elements
document.querySelectorAll('.fade-in-up').forEach(el => {
observer.observe(el);
});
}
// Export functions for testing (if needed)
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
playMovie,
loadMoreMovies,
togglePlayPause,
formatTime,
debounce,
throttle,
shareToWhatsApp,
shareToFacebook,
shareToInstagram,
shareToMessenger,
shareToTwitter,
copyMovieLink,
changeLanguage,
changeSubtitles,
toggleCaptions,
toggleSettings,
toggleFullscreen,
initializeRequestPage,
validateRequestForm,
submitMovieRequest,
resetRequestForm,
showSuccessToast
};
}