/* ===== FURRY AJAX LIBRARY ===== */ /* Moderne AJAX-Funktionen für kasico.de */ class FurryAjax { constructor() { this.csrfToken = this.getCSRFToken(); this.baseURL = window.location.origin; this.setupGlobalHandlers(); } // CSRF Token aus Meta-Tag oder Cookie holen getCSRFToken() { const metaTag = document.querySelector('meta[name=csrf-token]'); if (metaTag) return metaTag.getAttribute('content'); const cookie = document.cookie.match(/csrftoken=([^;]+)/); return cookie ? cookie[1] : ''; } // Globale Event-Handler einrichten setupGlobalHandlers() { // Loading States für alle AJAX-Requests document.addEventListener('furry-ajax-start', this.showLoading.bind(this)); document.addEventListener('furry-ajax-end', this.hideLoading.bind(this)); // Error Handler document.addEventListener('furry-ajax-error', this.handleError.bind(this)); } // Haupt-AJAX-Funktion async request(url, options = {}) { const defaultOptions = { method: 'GET', headers: { 'X-CSRFToken': this.csrfToken, 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }, credentials: 'same-origin' }; const config = { ...defaultOptions, ...options }; // Loading State triggern this.triggerEvent('furry-ajax-start'); try { const response = await fetch(url, config); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const contentType = response.headers.get('content-type'); let data; if (contentType && contentType.includes('application/json')) { data = await response.json(); } else { data = await response.text(); } this.triggerEvent('furry-ajax-end'); return { success: true, data, response }; } catch (error) { this.triggerEvent('furry-ajax-error', { error }); return { success: false, error: error.message }; } } // Warenkorb-Funktionen async addToCart(productId, quantity = 1) { const result = await this.request(`/add-to-cart/${productId}/`, { method: 'POST', body: JSON.stringify({ quantity }) }); if (result.success) { this.showSuccess('Produkt wurde zum Warenkorb hinzugefügt!'); this.updateCartCount(result.data.cart_count); } else { this.showError('Fehler beim Hinzufügen zum Warenkorb'); } return result; } async updateCartItem(itemId, quantity) { const result = await this.request(`/update-cart-item/${itemId}/`, { method: 'POST', body: JSON.stringify({ quantity }) }); if (result.success) { this.updateCartTotal(result.data.total); this.showSuccess('Warenkorb aktualisiert!'); } else { this.showError('Fehler beim Aktualisieren des Warenkorbs'); } return result; } async removeFromCart(itemId) { const result = await this.request(`/remove-cart-item/${itemId}/`, { method: 'POST' }); if (result.success) { this.showSuccess('Produkt aus Warenkorb entfernt!'); this.updateCartCount(result.data.cart_count); // Element aus DOM entfernen const cartItem = document.querySelector(`[data-cart-item="${itemId}"]`); if (cartItem) { cartItem.style.animation = 'fadeOut 0.3s ease'; setTimeout(() => cartItem.remove(), 300); } } else { this.showError('Fehler beim Entfernen aus Warenkorb'); } return result; } // Wunschliste-Funktionen async addToWishlist(productId) { const result = await this.request(`/add-to-wishlist/${productId}/`, { method: 'POST' }); if (result.success) { this.showSuccess('Produkt zur Wunschliste hinzugefügt!'); this.updateWishlistCount(result.data.wishlist_count); } else { this.showError('Fehler beim Hinzufügen zur Wunschliste'); } return result; } async removeFromWishlist(productId) { const result = await this.request(`/remove-from-wishlist/${productId}/`, { method: 'POST' }); if (result.success) { this.showSuccess('Produkt aus Wunschliste entfernt!'); this.updateWishlistCount(result.data.wishlist_count); } else { this.showError('Fehler beim Entfernen aus Wunschliste'); } return result; } // Bewertungen async submitReview(productId, rating, comment) { const result = await this.request(`/submit-review/${productId}/`, { method: 'POST', body: JSON.stringify({ rating, comment }) }); if (result.success) { this.showSuccess('Bewertung erfolgreich abgegeben!'); this.updateProductRating(productId, result.data.average_rating); } else { this.showError('Fehler beim Abgeben der Bewertung'); } return result; } // Live-Suche async searchProducts(query) { const result = await this.request(`/search-products/?q=${encodeURIComponent(query)}`); if (result.success) { this.updateSearchResults(result.data.products); } return result; } // Filter-Funktionen async applyFilters(filters) { const queryString = new URLSearchParams(filters).toString(); const result = await this.request(`/filter-products/?${queryString}`); if (result.success) { this.updateProductGrid(result.data.products); this.updateFilterCounts(result.data.counts); } return result; } // Loading States showLoading(element = null) { const target = element || document.body; target.classList.add('loading'); // Loading Spinner hinzufügen const spinner = document.createElement('div'); spinner.className = 'furry-loading furry-loading-lg'; spinner.id = 'furry-loading-spinner'; target.appendChild(spinner); } hideLoading(element = null) { const target = element || document.body; target.classList.remove('loading'); // Loading Spinner entfernen const spinner = document.getElementById('furry-loading-spinner'); if (spinner) spinner.remove(); } // Benachrichtigungen showSuccess(message, duration = 3000) { this.showNotification(message, 'success', duration); } showError(message, duration = 5000) { this.showNotification(message, 'error', duration); } showWarning(message, duration = 4000) { this.showNotification(message, 'warning', duration); } showInfo(message, duration = 3000) { this.showNotification(message, 'info', duration); } showNotification(message, type = 'info', duration = 3000) { const alert = document.createElement('div'); alert.className = `furry-alert furry-alert-${type}`; alert.style.position = 'fixed'; alert.style.top = '20px'; alert.style.right = '20px'; alert.style.zIndex = '9999'; alert.style.maxWidth = '400px'; alert.style.animation = 'slideInRight 0.3s ease'; alert.innerHTML = `
${type === 'success' ? '✅' : type === 'error' ? '❌' : type === 'warning' ? '⚠️' : 'ℹ️'}
${type.charAt(0).toUpperCase() + type.slice(1)}
${message}
`; document.body.appendChild(alert); // Auto-remove nach duration setTimeout(() => { if (alert.parentElement) { alert.style.animation = 'slideOutRight 0.3s ease'; setTimeout(() => alert.remove(), 300); } }, duration); } // DOM Updates updateCartCount(count) { const cartBadge = document.querySelector('.cart-count'); if (cartBadge) { cartBadge.textContent = count; cartBadge.style.animation = 'bounce 0.5s ease'; } } updateWishlistCount(count) { const wishlistBadge = document.querySelector('.wishlist-count'); if (wishlistBadge) { wishlistBadge.textContent = count; wishlistBadge.style.animation = 'bounce 0.5s ease'; } } updateCartTotal(total) { const totalElement = document.querySelector('.cart-total'); if (totalElement) { totalElement.textContent = `${total}€`; totalElement.style.animation = 'pulse 0.5s ease'; } } updateProductRating(productId, averageRating) { const ratingElement = document.querySelector(`[data-product-rating="${productId}"]`); if (ratingElement) { ratingElement.innerHTML = this.generateStars(averageRating); ratingElement.style.animation = 'fadeIn 0.5s ease'; } } updateSearchResults(products) { const resultsContainer = document.querySelector('.search-results'); if (resultsContainer && products) { resultsContainer.innerHTML = this.generateProductHTML(products); resultsContainer.style.animation = 'fadeIn 0.3s ease'; } } updateProductGrid(products) { const gridContainer = document.querySelector('.products-grid'); if (gridContainer && products) { gridContainer.innerHTML = this.generateProductHTML(products); gridContainer.style.animation = 'fadeIn 0.3s ease'; } } updateFilterCounts(counts) { Object.keys(counts).forEach(filterType => { const countElement = document.querySelector(`[data-filter-count="${filterType}"]`); if (countElement) { countElement.textContent = counts[filterType]; } }); } // Helper Functions generateStars(rating) { let stars = ''; for (let i = 1; i <= 5; i++) { if (i <= rating) { stars += '⭐'; } else { stars += '☆'; } } return stars; } generateProductHTML(products) { return products.map(product => `
${product.image ? `${product.name}` : `
🐾
` }

${product.name}

${product.fursuit_type} • ${product.style}

${product.featured ? '
Featured
' : ''}

${product.description}

${product.price}€
`).join(''); } // Event Handling triggerEvent(eventName, data = {}) { const event = new CustomEvent(eventName, { detail: data }); document.dispatchEvent(event); } handleError(event) { console.error('FurryAjax Error:', event.detail.error); this.showError('Ein Fehler ist aufgetreten. Bitte versuche es erneut.'); } } // Globale Instanz erstellen const furryAjax = new FurryAjax(); // CSS für Animationen const style = document.createElement('style'); style.textContent = ` @keyframes slideInRight { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slideOutRight { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } @keyframes bounce { 0%, 20%, 50%, 80%, 100% { transform: translateY(0); } 40% { transform: translateY(-10px); } 60% { transform: translateY(-5px); } } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } .loading { position: relative; pointer-events: none; } .loading::after { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(255, 255, 255, 0.8); z-index: 1000; } `; document.head.appendChild(style); // Export für Module-System if (typeof module !== 'undefined' && module.exports) { module.exports = FurryAjax; }