| Server IP : 3.111.61.48 / Your IP : 216.73.216.67 Web Server : Apache System : Linux ip-10-0-5-176 6.8.0-1057-aws #60~22.04.1-Ubuntu SMP Wed May 27 08:16:59 UTC 2026 x86_64 User : ubuntu ( 1000) PHP Version : 8.2.31 Disable Function : exec,passthru,shell_exec,system MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /var/www/vhost/book.gyaniguru.org/js/ |
Upload File : |
/* ================================================================
GYANI GURU - Main JavaScript
================================================================ */
// ---- Store State ----
const Store = {
cart: JSON.parse(localStorage.getItem('gg_cart') || '[]'),
wishlist: JSON.parse(localStorage.getItem('gg_wishlist') || '[]'),
saveCart() { localStorage.setItem('gg_cart', JSON.stringify(this.cart)); },
saveWishlist() { localStorage.setItem('gg_wishlist', JSON.stringify(this.wishlist)); },
addToCart(book) {
const existing = this.cart.find(i => i.id === book.id);
if (existing) {
existing.qty += 1;
} else {
this.cart.push({ ...book, qty: 1 });
}
this.saveCart();
updateCartBadge();
showToast(`"${book.title}" added to cart! 🛒`, 'cart');
},
removeFromCart(id) {
this.cart = this.cart.filter(i => i.id !== id);
this.saveCart();
updateCartBadge();
},
updateQty(id, delta) {
const item = this.cart.find(i => i.id === id);
if (item) {
item.qty = Math.max(1, item.qty + delta);
this.saveCart();
}
},
toggleWishlist(book) {
const idx = this.wishlist.findIndex(i => i.id === book.id);
if (idx >= 0) {
this.wishlist.splice(idx, 1);
this.saveWishlist();
updateWishlistBadge();
showToast(`"${book.title}" removed from wishlist`, 'remove');
return false;
} else {
this.wishlist.push(book);
this.saveWishlist();
updateWishlistBadge();
showToast(`"${book.title}" added to wishlist! ❤️`, 'wish');
return true;
}
},
isWishlisted(id) { return this.wishlist.some(i => i.id === id); },
getCartTotal() {
return this.cart.reduce((sum, i) => sum + (i.price * i.qty), 0);
},
getCartCount() {
return this.cart.reduce((sum, i) => sum + i.qty, 0);
}
};
// ---- Toast Notification ----
function showToast(message, type = 'cart') {
let toast = document.getElementById('globalToast');
if (!toast) {
toast = document.createElement('div');
toast.id = 'globalToast';
toast.className = 'toast-custom';
toast.innerHTML = `<span class="toast-icon"><i id="toastIcon" class="bi bi-bag-check-fill"></i></span><span class="toast-msg" id="toastMsg"></span>`;
document.body.appendChild(toast);
}
const iconMap = {
cart: 'bi-bag-check-fill',
wish: 'bi-heart-fill',
remove: 'bi-trash3',
success: 'bi-check-circle-fill',
info: 'bi-info-circle-fill'
};
document.getElementById('toastIcon').className = `bi ${iconMap[type] || 'bi-check-circle-fill'}`;
document.getElementById('toastMsg').textContent = message;
toast.classList.add('show');
setTimeout(() => toast.classList.remove('show'), 3000);
}
// ---- Badge Updates ----
function updateCartBadge() {
const badges = document.querySelectorAll('.cart-badge');
const count = Store.getCartCount();
badges.forEach(b => { b.textContent = count; b.style.display = count > 0 ? 'flex' : 'none'; });
}
function updateWishlistBadge() {
const badges = document.querySelectorAll('.wish-badge');
const count = Store.wishlist.length;
badges.forEach(b => { b.textContent = count; b.style.display = count > 0 ? 'flex' : 'none'; });
}
// ---- Wishlist Button State ----
function syncWishlistButtons() {
document.querySelectorAll('[data-book-id][data-action="wishlist"]').forEach(btn => {
const id = parseInt(btn.dataset.bookId);
if (Store.isWishlisted(id)) {
btn.classList.add('active');
btn.querySelector('i').className = 'bi bi-heart-fill';
} else {
btn.classList.remove('active');
btn.querySelector('i').className = 'bi bi-heart';
}
});
}
// ---- Navbar Scroll Effect ----
function initNavbarScroll() {
const navbar = document.querySelector('.navbar');
if (!navbar) return;
window.addEventListener('scroll', () => {
navbar.classList.toggle('scrolled', window.scrollY > 50);
});
}
// ---- Fade In Animation ----
function initFadeIn() {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry, i) => {
if (entry.isIntersecting) {
setTimeout(() => entry.target.classList.add('visible'), i * 100);
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.fade-in-up').forEach(el => observer.observe(el));
}
// ---- Floating Particles ----
function initParticles() {
const container = document.querySelector('.hero-particles');
if (!container) return;
for (let i = 0; i < 25; i++) {
const p = document.createElement('div');
p.className = 'particle';
p.style.cssText = `
left: ${Math.random() * 100}%;
width: ${Math.random() * 4 + 2}px;
height: ${Math.random() * 4 + 2}px;
animation-duration: ${Math.random() * 15 + 8}s;
animation-delay: ${Math.random() * 10}s;
opacity: ${Math.random() * 0.5 + 0.2};
`;
container.appendChild(p);
}
}
// ---- Price Range Slider ----
function initPriceRange() {
const slider = document.getElementById('priceRange');
const display = document.getElementById('priceDisplay');
if (!slider || !display) return;
slider.addEventListener('input', () => {
display.textContent = `₹0 - ₹${slider.value}`;
const pct = ((slider.value - slider.min) / (slider.max - slider.min)) * 100;
slider.style.background = `linear-gradient(to right, var(--gerua) 0%, var(--gerua) ${pct}%, #ddd ${pct}%)`;
});
}
// ---- Search Filter ----
function initSearch() {
const searchInput = document.getElementById('shopSearch');
if (!searchInput) return;
searchInput.addEventListener('input', debounce((e) => {
const q = e.target.value.toLowerCase();
document.querySelectorAll('.shop-book-card').forEach(card => {
const title = card.dataset.title?.toLowerCase() || '';
const cat = card.dataset.category?.toLowerCase() || '';
card.closest('.col-book').style.display = (title.includes(q) || cat.includes(q)) ? '' : 'none';
});
}, 300));
}
// ---- Category Filter ----
function initCategoryFilter() {
document.querySelectorAll('.filter-cat-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.filter-cat-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const cat = btn.dataset.cat;
document.querySelectorAll('.shop-book-card').forEach(card => {
const cardCat = card.dataset.category?.toLowerCase();
card.closest('.col-book').style.display = (cat === 'all' || cardCat === cat.toLowerCase()) ? '' : 'none';
});
});
});
}
// ---- Sort ----
function initSort() {
const sortSel = document.getElementById('sortSelect');
if (!sortSel) return;
sortSel.addEventListener('change', (e) => {
const val = e.target.value;
const grid = document.getElementById('booksGrid');
if (!grid) return;
const cols = [...grid.querySelectorAll('.col-book')];
cols.sort((a, b) => {
const pa = parseFloat(a.querySelector('.price-current')?.textContent.replace('₹', '') || 0);
const pb = parseFloat(b.querySelector('.price-current')?.textContent.replace('₹', '') || 0);
const ra = parseFloat(a.querySelector('.rating-val')?.textContent || 0);
const rb = parseFloat(b.querySelector('.rating-val')?.textContent || 0);
if (val === 'price-asc') return pa - pb;
if (val === 'price-desc') return pb - pa;
if (val === 'rating') return rb - ra;
return 0;
});
cols.forEach(col => grid.appendChild(col));
});
}
// ---- View Toggle ----
function initViewToggle() {
const gridBtn = document.getElementById('viewGrid');
const listBtn = document.getElementById('viewList');
const grid = document.getElementById('booksGrid');
if (!gridBtn || !listBtn || !grid) return;
gridBtn.addEventListener('click', () => {
gridBtn.classList.add('active'); listBtn.classList.remove('active');
grid.classList.remove('list-view');
grid.querySelectorAll('.col-book').forEach(c => {
c.className = 'col-book col-sm-6 col-lg-4 mb-4';
});
});
listBtn.addEventListener('click', () => {
listBtn.classList.add('active'); gridBtn.classList.remove('active');
grid.classList.add('list-view');
grid.querySelectorAll('.col-book').forEach(c => {
c.className = 'col-book col-12 mb-3';
});
});
}
// ---- Cart Page Functions ----
function renderCart() {
const cartBody = document.getElementById('cartBody');
const wishlistBody = document.getElementById('wishlistBody');
if (!cartBody) return;
if (Store.cart.length === 0) {
cartBody.innerHTML = `
<tr><td colspan="6">
<div class="cart-empty">
<i class="bi bi-cart3"></i>
<h4>Your cart is empty</h4>
<p>Choose a book and add it to your cart</p>
<a href="shop.html" class="btn-saffron mt-3" style="text-decoration:none">Browse Books 🕉️</a>
</div>
</td></tr>`;
} else {
cartBody.innerHTML = Store.cart.map(item => `
<tr>
<td>
<div style="display:flex;align-items:center;gap:12px;">
<img src="${item.image || 'images/book1.png'}" class="cart-item-img" alt="${item.title}" onerror="this.src='images/book1.png'">
<div>
<div class="cart-item-title">${item.title}</div>
<div class="cart-item-author">by ${item.author}</div>
</div>
</div>
</td>
<td><span class="book-category">${item.category}</span></td>
<td><strong style="color:var(--gerua-dark)">₹${item.price}</strong></td>
<td>
<div class="qty-control">
<button class="qty-btn" onclick="changeQty(${item.id}, -1)">−</button>
<input type="number" class="qty-input" value="${item.qty}" min="1" onchange="setQty(${item.id}, this.value)">
<button class="qty-btn" onclick="changeQty(${item.id}, 1)">+</button>
</div>
</td>
<td><strong style="color:var(--text-dark)">₹${(item.price * item.qty).toLocaleString('en-IN')}</strong></td>
<td>
<button class="cart-remove" onclick="removeCartItem(${item.id})" title="Remove">
<i class="bi bi-trash3"></i>
</button>
</td>
</tr>`).join('');
}
if (wishlistBody) {
if (Store.wishlist.length === 0) {
wishlistBody.innerHTML = `
<div class="wishlist-empty">
<i class="bi bi-heart"></i>
<h4>Your wishlist is empty</h4>
<p>Save your favourite books to your wishlist</p>
<a href="shop.html" class="btn-saffron mt-3" style="text-decoration:none">Browse Books 🕉️</a>
</div>`;
} else {
wishlistBody.innerHTML = `<div class="row g-4">${Store.wishlist.map(item => `
<div class="col-sm-6 col-lg-4">
<div class="book-card">
<button class="wishlist-btn-card active" onclick="removeWishItem(${item.id})" title="Remove from wishlist">
<i class="bi bi-heart-fill"></i>
</button>
<div class="book-image-wrap">
<img src="${item.image || 'images/book1.png'}" alt="${item.title}" onerror="this.src='images/book1.png'">
</div>
<div class="book-card-body">
<div class="book-category">${item.category}</div>
<div class="book-title">${item.title}</div>
<div class="book-price"><span class="price-current">₹${item.price}</span></div>
<div class="book-actions">
<button class="btn-cart" onclick='Store.addToCart(${JSON.stringify(item).replace(/'/g, "\\'")}); renderCart();'>
<i class="bi bi-bag-plus"></i> Add to Cart
</button>
</div>
</div>
</div>
</div>`).join('')}</div>`;
}
}
updateOrderSummary();
}
function removeCartItem(id) {
Store.removeFromCart(id);
renderCart();
}
function removeWishItem(id) {
Store.wishlist = Store.wishlist.filter(i => i.id !== id);
Store.saveWishlist();
updateWishlistBadge();
renderCart();
}
function changeQty(id, delta) {
Store.updateQty(id, delta);
renderCart();
}
function setQty(id, val) {
const item = Store.cart.find(i => i.id === id);
if (item) { item.qty = Math.max(1, parseInt(val) || 1); Store.saveCart(); }
renderCart();
}
function updateOrderSummary() {
const subtotal = Store.getCartTotal();
const shipping = subtotal >= 999 ? 0 : 80;
const discount = 0;
const total = subtotal + shipping - discount;
const el = (id) => document.getElementById(id);
if (el('summarySubtotal')) el('summarySubtotal').textContent = `₹${subtotal.toLocaleString('en-IN')}`;
const shipEl = el('summaryShipping');
if (shipEl) {
if (shipping === 0) {
shipEl.textContent = 'FREE';
shipEl.style.color = '#27ae60';
} else {
shipEl.textContent = `₹${shipping}`;
shipEl.style.color = 'var(--text-medium)';
}
}
if (el('summaryTotal')) el('summaryTotal').textContent = `₹${total.toLocaleString('en-IN')}`;
if (el('summaryItems')) el('summaryItems').textContent = Store.getCartCount();
}
// ---- Newsletter ----
function initNewsletter() {
const form = document.getElementById('newsletterForm');
if (!form) return;
form.addEventListener('submit', (e) => {
e.preventDefault();
const email = form.querySelector('input').value;
if (!email) return;
showToast('Thank you! You have been subscribed 🙏', 'success');
form.reset();
});
}
// ---- Checkout ----
function initCheckout() {
const btn = document.getElementById('checkoutBtn');
if (!btn) return;
btn.addEventListener('click', () => {
if (Store.cart.length === 0) { showToast('Your cart is empty!', 'info'); return; }
showToast('Initiating checkout process...', 'success');
setTimeout(() => alert('Payment gateway will be integrated here! Currently in demo mode. 🙏'), 500);
});
}
// ---- Back To Top ----
function initBackToTop() {
const btn = document.getElementById('backToTop');
if (!btn) return;
window.addEventListener('scroll', () => {
btn.style.opacity = window.scrollY > 400 ? '1' : '0';
btn.style.pointerEvents = window.scrollY > 400 ? 'auto' : 'none';
});
btn.addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' }));
}
// ---- Utility ----
function debounce(fn, delay) {
let timer;
return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); };
}
// ---- Init ----
document.addEventListener('DOMContentLoaded', () => {
initNavbarScroll();
initFadeIn();
initParticles();
initPriceRange();
initSearch();
initCategoryFilter();
initSort();
initViewToggle();
renderCart();
initNewsletter();
initCheckout();
initBackToTop();
updateCartBadge();
updateWishlistBadge();
syncWishlistButtons();
// Wishlist toggle (delegated)
document.addEventListener('click', (e) => {
const wishBtn = e.target.closest('[data-action="wishlist"]');
if (!wishBtn) return;
const bookData = JSON.parse(wishBtn.dataset.book || '{}');
const added = Store.toggleWishlist(bookData);
wishBtn.classList.toggle('active', added);
const icon = wishBtn.querySelector('i');
if (icon) icon.className = added ? 'bi bi-heart-fill' : 'bi bi-heart';
});
// Add to cart (delegated)
document.addEventListener('click', (e) => {
const cartBtn = e.target.closest('[data-action="add-cart"]');
if (!cartBtn) return;
const bookData = JSON.parse(cartBtn.dataset.book || '{}');
Store.addToCart(bookData);
cartBtn.innerHTML = '<i class="bi bi-check-lg"></i> Added!';
cartBtn.style.background = 'linear-gradient(135deg, #27ae60, #2ecc71)';
setTimeout(() => {
cartBtn.innerHTML = `<i class="bi bi-bag-plus"></i> Add to Cart`;
cartBtn.style.background = '';
}, 1800);
});
});