/* ===================================
VELORA - Main Application JS
=================================== */
(function($) {
'use strict';
const Velora = {
init: function() {
this.preloader();
this.backToTop();
this.search();
this.tooltips();
this.quantity();
this.tabs();
this.zoom();
this.gallery();
this.countdown();
this.lazyLoad();
this.newsletter();
this.imagePreviews();
},
// Image previews for file inputs
imagePreviews: function() {
$(document).on('change', 'input[type="file"][data-preview]', function() {
const input = this;
const previewId = $(this).data('preview');
const preview = document.getElementById(previewId);
if (!preview) return;
if (input.files && input.files[0]) {
const reader = new FileReader();
reader.onload = function(e) {
preview.src = e.target.result;
preview.style.display = 'block';
};
reader.readAsDataURL(input.files[0]);
} else {
preview.src = '';
preview.style.display = 'none';
}
});
// Multi-image preview
$(document).on('change', '#images-input', function() {
const container = document.getElementById('images-preview');
if (!container) return;
container.innerHTML = '';
const files = this.files;
for (let i = 0; i < Math.min(files.length, 8); i++) {
const reader = new FileReader();
const wrap = document.createElement('div');
wrap.style.cssText = 'width:80px;height:80px;border-radius:8px;overflow:hidden;border:1px solid #eee;';
const img = document.createElement('img');
img.style.cssText = 'width:100%;height:100%;object-fit:cover;';
reader.onload = (function(w, im) {
return function(e) { im.src = e.target.result; w.appendChild(im); };
})(wrap, img);
reader.readAsDataURL(files[i]);
}
});
},
// Preloader
preloader: function() {
$(window).on('load', function() {
setTimeout(function() {
$('#preloader').addClass('hidden');
$('body').css('overflow', 'auto');
}, 800);
});
},
// Back to Top
backToTop: function() {
$(window).on('scroll', function() {
if ($(this).scrollTop() > 300) {
$('#back-to-top').addClass('show');
} else {
$('#back-to-top').removeClass('show');
}
});
$('#back-to-top').on('click', function(e) {
e.preventDefault();
$('html, body').animate({ scrollTop: 0 }, 600);
});
},
// Live Search
search: function() {
let searchTimer;
const runSearch = function(input, resultsContainer) {
const query = input.val();
clearTimeout(searchTimer);
if (query.length < 2) {
resultsContainer.removeClass('active').empty();
return;
}
searchTimer = setTimeout(function() {
$.ajax({
url: SITE_URL + 'ajax/search.php',
type: 'GET',
data: { q: query },
dataType: 'json',
success: function(response) {
if (response.success && response.data.length > 0) {
let html = '';
response.data.forEach(function(item) {
html += '';
html += '
';
html += '';
html += '
' + item.name + '
';
html += '
' + item.price + '
';
html += '
';
});
resultsContainer.html(html).addClass('active');
} else {
resultsContainer.html('
No products found
').addClass('active');
}
}
});
}, 300);
};
// Desktop search
$('#search-input').on('input', function() {
runSearch($(this), $('#search-results'));
});
// Mobile search
$('#mobile-search-input').on('input', function() {
runSearch($(this), $('#mobile-search-results'));
});
$(document).on('click', function(e) {
if (!$(e.target).closest('.search-wrapper').length) {
$('#search-results, #mobile-search-results').removeClass('active');
}
});
// Mobile search toggle
$('#mobile-search-toggle').on('click', function(e) {
e.preventDefault();
const bar = $('#mobile-search-bar');
bar.toggleClass('active');
if (bar.hasClass('active')) {
setTimeout(function() { $('#mobile-search-input').focus(); }, 200);
}
});
$('#mobile-search-close').on('click', function(e) {
e.preventDefault();
$('#mobile-search-bar').removeClass('active');
$('#mobile-search-input').val('');
$('#mobile-search-results').removeClass('active').empty();
});
},
// Bootstrap Tooltips
tooltips: function() {
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function (el) {
return new bootstrap.Tooltip(el);
});
},
// Quantity Controls
quantity: function() {
$(document).on('click', '.qty-btn', function() {
const $input = $(this).siblings('.qty-input');
const currentVal = parseInt($input.val());
const min = parseInt($input.attr('min')) || 1;
const max = parseInt($input.attr('max')) || 999;
if ($(this).hasClass('qty-plus')) {
if (currentVal < max) {
$input.val(currentVal + 1).trigger('change');
}
} else if ($(this).hasClass('qty-minus')) {
if (currentVal > min) {
$input.val(currentVal - 1).trigger('change');
}
}
});
},
// Product Tabs
tabs: function() {
$('.tab-btn').on('click', function() {
const target = $(this).data('target');
$('.tab-btn').removeClass('active');
$(this).addClass('active');
$('.tab-content').removeClass('active');
$(target).addClass('active');
});
},
// Product Image Zoom
zoom: function() {
if ($('.product-zoom').length) {
$('.product-zoom').on('mousemove', function(e) {
const offset = $(this).offset();
const x = (e.pageX - offset.left) / $(this).width() * 100;
const y = (e.pageY - offset.top) / $(this).height() * 100;
$(this).find('img').css('transform-origin', x + '% ' + y + '%');
});
$('.product-zoom').on('mouseenter', function() {
$(this).find('img').css('transform', 'scale(1.5)');
}).on('mouseleave', function() {
$(this).find('img').css('transform', 'scale(1)');
});
}
},
// Product Gallery
gallery: function() {
$(document).on('click', '.gallery-thumb img', function() {
const mainImg = $(this).closest('.product-gallery').find('.main-image img');
const newSrc = $(this).data('large') || $(this).attr('src');
mainImg.attr('src', newSrc);
$('.gallery-thumb').removeClass('active');
$(this).closest('.gallery-thumb').addClass('active');
});
},
// Countdown Timer
countdown: function() {
$('.countdown-timer').each(function() {
const $this = $(this);
const endTime = new Date($this.data('end-time')).getTime();
const timer = setInterval(function() {
const now = new Date().getTime();
const distance = endTime - now;
if (distance < 0) {
clearInterval(timer);
$this.html('Deal Expired
');
return;
}
const days = Math.floor(distance / (1000 * 60 * 60 * 24));
const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
$this.find('.days').text(String(days).padStart(2, '0'));
$this.find('.hours').text(String(hours).padStart(2, '0'));
$this.find('.minutes').text(String(minutes).padStart(2, '0'));
$this.find('.seconds').text(String(seconds).padStart(2, '0'));
}, 1000);
});
},
// Lazy Load Images
lazyLoad: function() {
if ('IntersectionObserver' in window) {
const imageObserver = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.add('loaded');
imageObserver.unobserve(img);
}
});
});
document.querySelectorAll('img[data-src]').forEach(function(img) {
imageObserver.observe(img);
});
}
},
// Newsletter Subscription
newsletter: function() {
$('#newsletter-form').on('submit', function(e) {
e.preventDefault();
const email = $(this).find('input[name="email"]').val();
const $btn = $(this).find('button[type="submit"]');
const originalText = $btn.html();
$btn.prop('disabled', true).html(' Subscribing...');
$.ajax({
url: SITE_URL + 'ajax/newsletter.php',
type: 'POST',
data: { email: email, csrf_token: CSRF_TOKEN },
dataType: 'json',
success: function(response) {
if (response.success) {
toastr.success(response.message);
$('#newsletter-form')[0].reset();
} else {
toastr.error(response.message);
}
$btn.prop('disabled', false).html(originalText);
},
error: function() {
toastr.error('Something went wrong. Please try again.');
$btn.prop('disabled', false).html(originalText);
}
});
});
}
};
// Initialize on document ready
$(document).ready(function() {
Velora.init();
// Smooth scroll for anchor links
$('a[href^="#"]').on('click', function(e) {
const target = $(this).attr('href');
if (target !== '#' && $(target).length) {
e.preventDefault();
$('html, body').animate({
scrollTop: $(target).offset().top - 100
}, 600);
}
});
});
// Global AJAX setup
$.ajaxSetup({
headers: {
'X-CSRF-Token': CSRF_TOKEN
}
});
})(jQuery);
// ===================================
// Navigation Progress + Page Loader
// ===================================
(function() {
'use strict';
// Hide preloader once page is fully ready (including images)
function hidePreloader() {
document.body.classList.add('loaded');
}
if (document.readyState === 'complete') {
hidePreloader();
} else {
window.addEventListener('load', hidePreloader);
// Failsafe: hide after 2s even if some images hang
setTimeout(hidePreloader, 2000);
}
// Show progress bar on internal link clicks
document.addEventListener('click', function(e) {
var link = e.target.closest('a');
if (!link) return;
var href = link.getAttribute('href');
// Skip: empty, anchors, external, mailto, tel, target=_blank, modifier keys
if (!href || href.charAt(0) === '#') return;
if (link.target === '_blank') return;
if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return;
if (href.indexOf('javascript:') === 0 || href.indexOf('mailto:') === 0 || href.indexOf('tel:') === 0) return;
// External link check
try {
var url = new URL(href, window.location.origin);
if (url.origin !== window.location.origin) return;
} catch (err) { return; }
// Skip AJAX / form-submit links
if (link.hasAttribute('data-no-progress')) return;
var bar = document.getElementById('nav-progress');
if (bar) {
bar.classList.remove('done');
bar.classList.add('active');
}
});
// Mark progress as done on page show (back/forward cache)
window.addEventListener('pageshow', function() {
var bar = document.getElementById('nav-progress');
if (bar) {
bar.classList.remove('active');
bar.classList.add('done');
setTimeout(function() { bar.classList.remove('done'); }, 500);
}
});
// Also mark done when page becomes hidden (user navigates away)
document.addEventListener('visibilitychange', function() {
if (document.visibilityState === 'hidden') {
var bar = document.getElementById('nav-progress');
if (bar) bar.classList.add('done');
}
});
})();