UNPKG

maher-tradingview-api

Version:

Tradingview instant stocks API, indicator alerts, trading bot, and more !

1,408 lines (1,207 loc) 56.4 kB
/* eslint-disable linebreak-style */ /* eslint-disable no-plusplus */ /* eslint-disable no-unused-vars */ /* eslint-disable prefer-const */ /* eslint-disable no-undef */ /** * loading-error-handler.js * معالج الأخطاء أثناء تحميل البيانات من TradingView * يقوم بمراقبة الأخطاء ومحاولة إعادة التحميل تلقائياً */ // عدد محاولات إعادة التحميل التلقائية const MAX_AUTO_RETRIES = 3; // وقت الانتظار بين المحاولات (بالمللي ثانية) const RETRY_DELAY = 3000; // متغير لتخزين عدد المحاولات الحالية let currentRetryAttempt = 0; // مراقب عالمي للأخطاء window.addEventListener('error', function(event) { console.warn('تم اكتشاف خطأ عام:', event.error); // التحقق مما إذا كان الخطأ متعلقاً بـ WebSocket const errorMsg = event.message || ''; if ( errorMsg.includes('WebSocket') || (event.error && event.error.message && ( event.error.message.includes('WebSocket') || event.error.message.includes('connect') || event.error.message.includes('ETIMEDOUT') || event.error.message.includes('timeout') )) ) { console.error('خطأ في اتصال WebSocket:', event.error); handleWebSocketError(); } // تسجيل أخطاء الرسوم البيانية if ( errorMsg.includes('chart') || errorMsg.includes('Canvas') || errorMsg.includes('رسم بياني') || (event.error && event.error.message && ( event.error.message.includes('chart') || event.error.message.includes('Canvas') || event.error.message.includes('رسم بياني') )) ) { logError('windowError', 'خطأ متعلق بالرسوم البيانية', event.error); } }); // إنشاء مستمع الحدث المخصص لتحميل بيانات الرسم البياني window.dispatchChartDataLoaded = function(symbol, data) { if (symbol && data) { // حفظ البيانات في التخزين المؤقت cacheChartData(symbol, data); // إرسال الحدث const event = new CustomEvent('chartDataLoaded', { detail: { symbol: symbol, data: data, timestamp: new Date().getTime() } }); document.dispatchEvent(event); console.log(`تم إرسال حدث تحميل بيانات الرسم البياني لـ ${symbol}`); } }; // عند تحميل الصفحة document.addEventListener('DOMContentLoaded', function() { // تحقق من وجود قسم الخطأ const errorSection = document.querySelector('.error'); if (errorSection) { console.log('تم اكتشاف رسالة خطأ في الصفحة، التحقق من نوع الخطأ...'); // التحقق من نوع الخطأ const errorText = errorSection.textContent || ''; if (errorText.includes('انتهت مهلة الانتظار') || errorText.includes('Timeout')) { handleTimeoutError(); } else if (errorText.includes('WebSocket') || errorText.includes('اتصال')) { handleWebSocketError(); } } // إضافة مراقب للأخطاء في الرسوم البيانية monitorChartsForErrors(); // التحقق من حالة الاتصال checkNetworkConnection(); // إضافة الدوال إلى النافذة العالمية window.reloadChart = reloadChart; window.reloadIndicator = reloadIndicator; }); /** * معالجة أخطاء انتهاء المهلة */ function handleTimeoutError() { console.log('تم اكتشاف خطأ انتهاء المهلة، التحضير لإعادة المحاولة...'); // تحديث واجهة المستخدم const errorSection = document.querySelector('.error'); if (errorSection) { // إنشاء عنصر عداد تنازلي const countdownEl = document.createElement('div'); countdownEl.id = 'retry-countdown'; countdownEl.style.marginTop = '15px'; countdownEl.style.padding = '10px'; countdownEl.style.backgroundColor = '#f8f9fa'; countdownEl.style.borderRadius = '4px'; countdownEl.style.textAlign = 'center'; countdownEl.style.fontWeight = 'bold'; // إضافة العنصر إلى قسم الخطأ errorSection.appendChild(countdownEl); // بدء العد التنازلي let countdownSeconds = 5; updateCountdown(countdownEl, countdownSeconds); const countdownInterval = setInterval(() => { countdownSeconds--; updateCountdown(countdownEl, countdownSeconds); if (countdownSeconds <= 0) { clearInterval(countdownInterval); // إعادة تحميل الصفحة window.location.reload(); } }, 1000); } } /** * تحديث عرض العد التنازلي */ function updateCountdown(element, seconds) { if (element) { element.textContent = `ستتم محاولة إعادة التحميل تلقائياً خلال ${seconds} ثانية...`; } } /** * مراقبة الرسوم البيانية للأخطاء */ function monitorChartsForErrors() { console.log('بدء مراقبة جميع الرسوم البيانية للأخطاء...'); // التحقق من وجود الرسم البياني الرئيسي const priceChart = document.getElementById('priceChart'); if (priceChart) { console.log('مراقبة الرسم البياني الرئيسي للأخطاء...'); // التحقق بعد مهلة زمنية كافية للسماح بتحميل البيانات setTimeout(() => { try { const chart = Chart.getChart(priceChart); // إذا لم يتم تهيئة الرسم البياني أو لم تكن هناك بيانات if (!chart || !chart.data || !chart.data.datasets || chart.data.datasets.length === 0 || (chart.data.datasets[0].data && chart.data.datasets[0].data.length === 0)) { console.warn('الرسم البياني الرئيسي غير مهيأ أو لا يحتوي على بيانات'); // التحقق من توفر البيانات الخام if (window.candleData && Array.isArray(window.candleData) && window.candleData.length > 0) { console.log(`البيانات الخام متوفرة (${window.candleData.length} نقطة)، محاولة إعادة تهيئة الرسم البياني...`); // محاولة إصلاح الرسم البياني أولاً if (typeof reloadChart === 'function') { const success = reloadChart('priceChart'); if (!success) { // إذا فشلت المحاولة، إظهار رسالة الخطأ showChartLoadingError(priceChart); } } else { // إذا لم تكن الوظيفة متاحة بعد، عرض رسالة الخطأ showChartLoadingError(priceChart); } } else { console.warn('لا توجد بيانات خام متاحة للرسم البياني'); showChartLoadingError(priceChart); } } else { console.log('الرسم البياني الرئيسي محمل بشكل صحيح'); } } catch (error) { console.error('خطأ أثناء التحقق من الرسم البياني:', error); showChartLoadingError(priceChart); } }, 5000); } // مراقبة جميع الرسوم البيانية الأخرى (المؤشرات الفنية) const technicalCharts = [ 'maChart', 'bbChart', 'rsiChart', 'macdChart', 'volumeChart', 'emaChart', 'stochasticChart', 'adxChart', 'obvChart', 'trendChart' ]; technicalCharts.forEach(chartId => { const chartElement = document.getElementById(chartId); if (chartElement) { console.log(`إعداد مراقبة المؤشر الفني: ${chartId}`); // استخدام مهلة أطول قليلاً للمؤشرات الفنية للسماح بتحميلها بالكامل setTimeout(() => { try { const chart = Chart.getChart(chartElement); if (!chart || !chart.data || !chart.data.datasets || chart.data.datasets.length === 0 || (chart.data.datasets[0] && chart.data.datasets[0].data && chart.data.datasets[0].data.length === 0)) { console.warn(`المؤشر الفني ${chartId} غير مهيأ أو لا يحتوي على بيانات`); // التحقق من توفر البيانات الخام if (window.candleData && Array.isArray(window.candleData) && window.candleData.length > 0) { console.log(`البيانات الخام متوفرة للمؤشر ${chartId}، محاولة إعادة تهيئة...`); // محاولة إصلاح المؤشر أولاً if (typeof reloadIndicator === 'function') { const success = reloadIndicator(chartId); if (!success) { // إذا فشلت المحاولة، إظهار رسالة الخطأ showIndicatorError(chartElement); } } else { // إذا لم تكن الوظيفة متاحة بعد، عرض رسالة الخطأ showIndicatorError(chartElement); } } else { console.warn(`لا توجد بيانات خام متاحة للمؤشر ${chartId}`); showIndicatorError(chartElement); } } else { console.log(`المؤشر الفني ${chartId} محمل بشكل صحيح`); } } catch (error) { console.error(`خطأ في مؤشر ${chartId}:`, error); showIndicatorError(chartElement); } }, 6000); } }); } /** * إظهار خطأ تحميل الرسم البياني الرئيسي */ function showChartLoadingError(canvas) { try { // التحقق من وجود العنصر if (!canvas) return; // التحقق من أن Canvas لم يتم التعامل معه بالفعل if (canvas.getAttribute('data-error-handled') === 'true') { return; } const ctx = canvas.getContext('2d'); // تنظيف الـ canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // رسم خلفية بيضاء ctx.fillStyle = '#f8f8f8'; ctx.fillRect(0, 0, canvas.width, canvas.height); // رسم رسالة الخطأ ctx.fillStyle = '#e74c3c'; ctx.font = 'bold 14px Arial'; ctx.textAlign = 'center'; ctx.fillText('فشل تحميل بيانات الرسم البياني', canvas.width / 2, canvas.height / 2 - 10); // رسم رسالة إضافية ctx.fillStyle = '#666'; ctx.font = '12px Arial'; ctx.fillText('قد تكون هناك مشكلة في الاتصال بالخادم أو في تحميل البيانات', canvas.width / 2, canvas.height / 2 + 20); // إضافة زر إعادة المحاولة const container = canvas.parentElement; if (container) { container.style.position = 'relative'; // إزالة أزرار قديمة إن وجدت const oldButtons = container.querySelectorAll('.retry-button, .reload-chart-button, .alt-source-button'); oldButtons.forEach(btn => btn.remove()); // إنشاء زر إعادة المحاولة const retryButton = document.createElement('button'); retryButton.textContent = 'إعادة تحميل البيانات'; retryButton.className = 'retry-button'; retryButton.style.position = 'absolute'; retryButton.style.top = '50%'; retryButton.style.left = '50%'; retryButton.style.transform = 'translate(-50%, -50%)'; retryButton.style.padding = '8px 16px'; retryButton.style.backgroundColor = '#3498db'; retryButton.style.color = 'white'; retryButton.style.border = 'none'; retryButton.style.borderRadius = '4px'; retryButton.style.cursor = 'pointer'; // تطبيق زر أنيق retryButton.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.1)'; retryButton.style.transition = 'background-color 0.3s'; // تغيير اللون عند المرور فوق الزر retryButton.addEventListener('mouseover', function() { this.style.backgroundColor = '#2980b9'; }); retryButton.addEventListener('mouseout', function() { this.style.backgroundColor = '#3498db'; }); // إضافة حدث النقر retryButton.addEventListener('click', function() { this.disabled = true; this.textContent = 'جاري إعادة التحميل...'; // محاولة إعادة تهيئة الرسم البياني أولاً try { if (typeof reloadChart === 'function') { reloadChart(canvas.id); // تعطيل الزر مؤقتًا setTimeout(() => { if (checkCanvasHasData(canvas)) { // تم التحميل بنجاح، إزالة الخطأ this.remove(); // إزالة الأزرار الأخرى const otherButtons = container.querySelectorAll('.alt-source-button, .reload-page-button'); otherButtons.forEach(btn => btn.remove()); } else { // فشل التحميل، إعادة تمكين الزر this.disabled = false; this.textContent = 'إعادة تحميل البيانات'; // إضافة زر إعادة تحميل الصفحة بالكامل addReloadPageButton(container); } }, 2000); } else { // إذا لم تكن وظيفة إعادة تحميل الرسم البياني متوفرة، إعادة تحميل الصفحة window.location.reload(); } } catch (error) { console.error('فشل في إعادة تحميل الرسم البياني:', error); // إعادة تحميل الصفحة كحل أخير window.location.reload(); } }); // إنشاء زر استخدام مصدر بديل const altSourceButton = document.createElement('button'); altSourceButton.textContent = 'استخدام مصدر بيانات بديل'; altSourceButton.className = 'alt-source-button'; altSourceButton.style.position = 'absolute'; altSourceButton.style.top = '50%'; altSourceButton.style.left = '50%'; altSourceButton.style.transform = 'translate(-50%, 30px)'; altSourceButton.style.padding = '8px 16px'; altSourceButton.style.backgroundColor = '#2ecc71'; altSourceButton.style.color = 'white'; altSourceButton.style.border = 'none'; altSourceButton.style.borderRadius = '4px'; altSourceButton.style.cursor = 'pointer'; altSourceButton.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.1)'; altSourceButton.style.transition = 'background-color 0.3s'; // تغيير اللون عند المرور فوق الزر altSourceButton.addEventListener('mouseover', function() { this.style.backgroundColor = '#27ae60'; }); altSourceButton.addEventListener('mouseout', function() { this.style.backgroundColor = '#2ecc71'; }); // إضافة حدث النقر للزر البديل altSourceButton.addEventListener('click', async function() { this.disabled = true; this.textContent = 'جارٍ تحميل مصدر بديل...'; // محاولة استخدام مصدر بديل try { // البحث عن رمز السهم من خلال URL أو متغيرات عالمية let symbol = ''; // محاولة استخراج الرمز من URL const urlParams = new URLSearchParams(window.location.search); if (urlParams.has('symbol')) { symbol = urlParams.get('symbol'); } else if (window.currentSymbol) { // استخدام متغير عالمي إذا كان موجودًا symbol = window.currentSymbol; } else { // قيمة افتراضية symbol = 'AAPL'; } // البحث عن الإطار الزمني let timeframe = 'D'; if (urlParams.has('timeframe')) { timeframe = urlParams.get('timeframe'); } else if (window.currentTimeframe) { timeframe = window.currentTimeframe; } // محاولة استخدام مصدر بديل const success = await tryAlternativeDataSource(symbol, timeframe); if (success) { // تم تحميل البيانات بنجاح، إزالة رسالة الخطأ والزر this.remove(); retryButton.remove(); // إزالة علامة الخطأ من الرسم البياني canvas.removeAttribute('data-error-handled'); } else { // فشل التحميل، إعادة تمكين الزر this.disabled = false; this.textContent = 'استخدام مصدر بيانات بديل آخر'; // إظهار زر إعادة تحميل الصفحة addReloadPageButton(container); } } catch (error) { console.error('فشل في تحميل المصدر البديل:', error); this.disabled = false; this.textContent = 'فشل تحميل المصدر البديل'; // إظهار زر إعادة تحميل الصفحة addReloadPageButton(container); } }); // إضافة الأزرار إلى الحاوية container.appendChild(retryButton); container.appendChild(altSourceButton); } // وضع علامة على Canvas بأنه تم معالجة الخطأ canvas.setAttribute('data-error-handled', 'true'); } catch (error) { console.error('خطأ أثناء عرض رسالة خطأ التحميل:', error); } } /** * إظهار خطأ في المؤشر الفني */ function showIndicatorError(canvas) { try { if (!canvas) return; // التحقق مما إذا كان الخطأ قد تم معالجته بالفعل if (canvas.getAttribute('data-error-handled') === 'true') return; const ctx = canvas.getContext('2d'); // تنظيف الـ canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // رسم خلفية فاتحة ctx.fillStyle = '#f9f9f9'; ctx.fillRect(0, 0, canvas.width, canvas.height); // رسم إطار برتقالي خفيف ctx.strokeStyle = '#e67e22'; ctx.lineWidth = 2; ctx.strokeRect(2, 2, canvas.width - 4, canvas.height - 4); // رسم رسالة الخطأ ctx.fillStyle = '#e67e22'; ctx.font = '12px Arial'; ctx.textAlign = 'center'; ctx.fillText('فشل تحميل المؤشر الفني', canvas.width / 2, canvas.height / 2 - 5); // إضافة زر إعادة المحاولة صغير const container = canvas.parentElement; if (container) { // إزالة أزرار قديمة إن وجدت const oldButtons = container.querySelectorAll('.indicator-retry-button'); oldButtons.forEach(btn => btn.remove()); // إنشاء زر إعادة المحاولة const retryButton = document.createElement('button'); retryButton.textContent = 'إعادة تحميل المؤشر'; retryButton.className = 'indicator-retry-button'; retryButton.style.position = 'absolute'; retryButton.style.bottom = '10px'; retryButton.style.left = '50%'; retryButton.style.transform = 'translateX(-50%)'; retryButton.style.padding = '5px 10px'; retryButton.style.backgroundColor = '#e67e22'; retryButton.style.color = 'white'; retryButton.style.border = 'none'; retryButton.style.borderRadius = '3px'; retryButton.style.fontSize = '11px'; retryButton.style.cursor = 'pointer'; retryButton.addEventListener('click', function() { this.disabled = true; this.textContent = 'جارٍ التحميل...'; // محاولة إعادة تهيئة المؤشر setTimeout(() => { try { reloadIndicator(canvas.id); } catch (error) { console.error('فشل إعادة تحميل المؤشر:', error); } finally { setTimeout(() => { // تمكين الزر مرة أخرى إذا استمر الخطأ if (!checkCanvasHasData(canvas)) { this.disabled = false; this.textContent = 'إعادة تحميل المؤشر'; } else { this.remove(); } }, 1000); } }, 300); }); // إضافة الزر إلى الحاوية container.appendChild(retryButton); } // وضع علامة على Canvas بأنه تم معالجة الخطأ canvas.setAttribute('data-error-handled', 'true'); } catch (error) { console.error('خطأ أثناء عرض خطأ المؤشر:', error); } } /** * إضافة زر إعادة تحميل الصفحة بالكامل */ function addReloadPageButton(container) { // إزالة أزرار قديمة إن وجدت const oldButtons = container.querySelectorAll('.reload-page-button'); oldButtons.forEach(btn => btn.remove()); const reloadButton = document.createElement('button'); reloadButton.textContent = 'إعادة تحميل الصفحة بالكامل'; reloadButton.className = 'reload-page-button'; reloadButton.style.position = 'absolute'; reloadButton.style.top = '60%'; reloadButton.style.left = '50%'; reloadButton.style.transform = 'translate(-50%, 30px)'; reloadButton.style.padding = '6px 12px'; reloadButton.style.backgroundColor = '#95a5a6'; reloadButton.style.color = 'white'; reloadButton.style.border = 'none'; reloadButton.style.borderRadius = '3px'; reloadButton.style.fontSize = '12px'; reloadButton.style.cursor = 'pointer'; reloadButton.addEventListener('click', function() { window.location.reload(); }); container.appendChild(reloadButton); } /** * محاولة تحميل البيانات من مصدر بديل * @param {string} symbol - رمز السهم * @param {string} timeframe - الإطار الزمني * @return {Promise<boolean>} وعد بنجاح أو فشل العملية */ async function tryAlternativeDataSource(symbol, timeframe = 'D') { // التحقق من وجود مدير مصادر البيانات if (!window.DataSourcesManager) { console.warn('مدير مصادر البيانات غير متوفر'); return false; } try { console.log('محاولة استخدام مصدر بيانات بديل...'); // تبديل المصدر إلى المصدر التالي const nextSource = window.DataSourcesManager.switchToNextSource(); // محاولة جلب البيانات من المصدر البديل const data = await window.DataSourcesManager.fetchChartData(symbol, timeframe); if (!data || data.length === 0) { console.error('فشل في جلب البيانات من المصدر البديل'); return false; } console.log(`تم جلب البيانات بنجاح من المصدر البديل: ${nextSource} (${data.length} نقطة)`); // تحديث البيانات العالمية window.candleData = data; // إعادة رسم جميع الرسوم البيانية if (typeof reloadChart === 'function') { reloadChart('priceChart'); } // إعادة رسم جميع المؤشرات الفنية const technicalCharts = [ 'maChart', 'bbChart', 'rsiChart', 'macdChart', 'volumeChart', 'emaChart', 'stochasticChart', 'adxChart', 'obvChart', 'trendChart' ]; technicalCharts.forEach(chartId => { if (document.getElementById(chartId) && typeof reloadIndicator === 'function') { reloadIndicator(chartId); } }); // إظهار إشعار للمستخدم showDataSourceChangedNotification(nextSource); return true; } catch (error) { console.error('فشل في استخدام مصدر البيانات البديل:', error); return false; } } /** * إضافة زر "استخدام مصدر بيانات بديل" إلى رسالة الخطأ */ function addAlternativeSourceButton() { // التحقق من وجود مدير مصادر البيانات if (typeof DataSourcesManager === 'undefined' || !DataSourcesManager.switchToNextSource) { return; } // البحث عن حاوية رسالة الخطأ const errorContainer = document.querySelector('.error-container'); if (!errorContainer) { return; } // التحقق مما إذا كان الزر موجودًا بالفعل if (errorContainer.querySelector('.alt-source-btn')) { return; } // إنشاء زر جديد const altSourceBtn = document.createElement('button'); altSourceBtn.className = 'alt-source-btn'; altSourceBtn.textContent = 'استخدام مصدر بيانات بديل'; // إضافة معالج النقر altSourceBtn.addEventListener('click', function() { // محاولة التبديل إلى المصدر التالي const success = tryAlternativeDataSource(); if (success) { // إخفاء رسالة الخطأ hideLoadingError(); // إعادة تحديث الرسم البياني const urlParams = new URLSearchParams(window.location.search); const symbol = urlParams.get('symbol') || 'AAPL'; const timeframe = urlParams.get('timeframe') || 'D'; if (typeof fetchAndRenderChart === 'function') { fetchAndRenderChart(symbol, timeframe); } } else { // عرض رسالة خطأ إذا فشلت جميع المصادر showLoadingError('جميع مصادر البيانات غير متاحة', 'للأسف، جميع مصادر البيانات البديلة غير متاحة حاليًا. يرجى المحاولة مرة أخرى لاحقًا.', false); } }); // إضافة الزر إلى حاوية الخطأ errorContainer.appendChild(altSourceBtn); } /** * عرض إشعار بتغيير مصدر البيانات */ function showDataSourceChangedNotification(sourceName) { // التحقق من وجود حاوية الإشعارات const notificationsContainer = document.getElementById('dataSourceNotifications'); if (!notificationsContainer) { return; } // إنشاء إشعار جديد const notification = document.createElement('div'); notification.className = 'data-source-notification'; notification.innerHTML = ` <strong>تم تغيير مصدر البيانات!</strong> <p>يتم الآن استخدام: ${sourceName}</p> <button class="dismiss-button">إغلاق</button> `; // إضافة معالج النقر لزر الإغلاق const dismissButton = notification.querySelector('.dismiss-button'); dismissButton.addEventListener('click', function() { notification.remove(); }); // إضافة الإشعار إلى الحاوية notificationsContainer.appendChild(notification); // إزالة الإشعار تلقائيًا بعد 10 ثوانٍ setTimeout(() => { if (notification.parentNode) { notification.remove(); } }, 10000); } /** * التحقق مما إذا كان Canvas يحتوي على بيانات */ function checkCanvasHasData(canvas) { try { const chart = Chart.getChart(canvas); return chart && chart.data && chart.data.datasets && chart.data.datasets.length > 0 && chart.data.datasets[0].data && chart.data.datasets[0].data.length > 0; } catch (error) { console.error('خطأ أثناء فحص بيانات Canvas:', error); return false; } } /** * معالجة أخطاء WebSocket */ function handleWebSocketError() { console.log('معالجة خطأ اتصال WebSocket...'); // قراءة معلمات URL للحصول على رمز السهم والإطار الزمني const urlParams = new URLSearchParams(window.location.search); const symbol = urlParams.get('symbol') || 'AAPL'; const timeframe = urlParams.get('timeframe') || 'D'; // عرض رسالة خطأ للمستخدم showLoadingError('خطأ في الاتصال', 'لا يمكن الاتصال بخدمة البيانات. يرجى التحقق من اتصالك بالإنترنت ومحاولة تحديث الصفحة.', true); // إضافة زر "استخدام مصدر بيانات بديل" addAlternativeSourceButton(); // إذا كان عدد المحاولات أقل من الحد الأقصى، قم بإعادة المحاولة تلقائيًا if (currentRetryAttempt < MAX_AUTO_RETRIES) { currentRetryAttempt++; console.log(`محاولة إعادة الاتصال (${currentRetryAttempt}/${MAX_AUTO_RETRIES})...`); // تأخير قبل إعادة المحاولة setTimeout(() => { // محاولة استخدام مصدر بديل const switched = tryAlternativeDataSource(); if (!switched) { // إعادة تحميل البيانات if (typeof fetchAndRenderChart === 'function') { fetchAndRenderChart(symbol, timeframe); } } }, RETRY_DELAY); } else { console.error('تم الوصول إلى الحد الأقصى من محاولات إعادة الاتصال التلقائية'); // محاولة استخدام مصدر بديل كملاذ أخير tryAlternativeDataSource(); } } /** * محاولة استخدام مصدر بيانات بديل عند فشل المصدر الحالي * @param {Error} error الخطأ الذي تسبب في الفشل * @returns {boolean} ما إذا كان قد تم التبديل بنجاح */ function tryAlternativeDataSource(error) { console.log('محاولة استخدام مصدر بيانات بديل...'); if (typeof DataSourcesManager !== 'undefined' && DataSourcesManager.switchToNextSource) { // تحديث حالة المصدر الحالي const currentSource = DataSourcesManager.currentSource; // زيادة عداد الأخطاء للمصدر الحالي if (DataSourcesManager.sourcesStatus && DataSourcesManager.sourcesStatus[currentSource]) { DataSourcesManager.sourcesStatus[currentSource].errorCount++; DataSourcesManager.sourcesStatus[currentSource].available = false; DataSourcesManager.sourcesStatus[currentSource].lastCheck = Date.now(); } // محاولة التبديل إلى المصدر التالي const success = DataSourcesManager.switchToNextSource(); if (success) { console.log('تم التبديل بنجاح إلى مصدر بيانات بديل'); return true; } else { console.error('فشل التبديل إلى مصدر بيانات بديل'); return false; } } console.error('مدير مصادر البيانات غير متاح'); return false; } // نظام تسجيل الأخطاء /** * نظام تسجيل الأخطاء * @param {string} source - مصدر الخطأ * @param {string} message - رسالة الخطأ * @param {Error} [error] - كائن الخطأ (اختياري) */ function logError(source, message, error = null) { const timestamp = new Date().toISOString(); const logMessage = `[${timestamp}] [${source}] ${message}`; console.error(logMessage); if (error) { console.error('تفاصيل الخطأ:', error); } // يمكن إضافة منطق هنا لإرسال السجلات إلى الخادم أو تخزينها محلياً try { const errorLogs = JSON.parse(localStorage.getItem('chartErrorLogs') || '[]'); errorLogs.push({ timestamp, source, message, details: error ? error.toString() : null }); // الاحتفاظ بآخر 50 خطأ فقط if (errorLogs.length > 50) { errorLogs.shift(); // إزالة أقدم خطأ } localStorage.setItem('chartErrorLogs', JSON.stringify(errorLogs)); } catch (e) { console.error('فشل في تخزين سجل الأخطاء:', e); } } /** * فحص شامل لصحة الرسوم البيانية * يتم استدعاء هذه الوظيفة دورياً للتحقق من صحة جميع الرسوم البيانية */ function performFullChartsHealthCheck() { console.log('بدء فحص شامل لصحة جميع الرسوم البيانية...'); // فحص الرسم البياني الرئيسي const priceChart = document.getElementById('priceChart'); if (priceChart) { checkChartHealth(priceChart, 'priceChart', true); } // فحص المؤشرات الفنية const technicalCharts = [ 'maChart', 'bbChart', 'rsiChart', 'macdChart', 'volumeChart', 'emaChart', 'stochasticChart', 'adxChart', 'obvChart', 'trendChart' ]; technicalCharts.forEach(chartId => { const chartElement = document.getElementById(chartId); if (chartElement) { checkChartHealth(chartElement, chartId, false); } }); } /** * فحص صحة رسم بياني محدد * @param {HTMLElement} canvas - عنصر الـ canvas * @param {string} chartId - معرف الرسم البياني * @param {boolean} isPrimaryChart - هل هو الرسم البياني الرئيسي */ function checkChartHealth(canvas, chartId, isPrimaryChart) { try { const chart = Chart.getChart(canvas); // التحقق من وجود الرسم البياني if (!chart) { logError('healthCheck', `الرسم البياني ${chartId} غير موجود`); if (isPrimaryChart) { showChartLoadingError(canvas); } else { showIndicatorError(canvas); } return false; } // التحقق من وجود البيانات if (!chart.data || !chart.data.datasets || chart.data.datasets.length === 0) { logError('healthCheck', `الرسم البياني ${chartId} لا يحتوي على مجموعات بيانات`); if (isPrimaryChart) { showChartLoadingError(canvas); } else { showIndicatorError(canvas); } return false; } // التحقق من أن البيانات ليست فارغة const hasData = chart.data.datasets.some(dataset => dataset.data && dataset.data.length > 0); if (!hasData) { logError('healthCheck', `الرسم البياني ${chartId} يحتوي على بيانات فارغة`); if (isPrimaryChart) { showChartLoadingError(canvas); } else { showIndicatorError(canvas); } return false; } // التحقق من أن جميع البيانات ليست صفرية أو غير معرفة const allDataUndefinedOrZero = chart.data.datasets.every(dataset => !dataset.data || dataset.data.every(point => point === undefined || point === null || point === 0)); if (allDataUndefinedOrZero) { logError('healthCheck', `الرسم البياني ${chartId} يحتوي على بيانات غير صالحة (كلها صفر أو غير معرفة)`); if (isPrimaryChart) { showChartLoadingError(canvas); } else { showIndicatorError(canvas); } return false; } return true; } catch (error) { logError('healthCheck', `خطأ أثناء فحص صحة الرسم البياني ${chartId}`, error); if (isPrimaryChart) { showChartLoadingError(canvas); } else { showIndicatorError(canvas); } return false; } } // بدء فحص صحة الرسوم البيانية بعد وقت كافٍ للتحميل setTimeout(performFullChartsHealthCheck, 10000); // تشغيل فحص صحة دوري كل 30 ثانية setInterval(performFullChartsHealthCheck, 30000); /** * إعادة تحميل الرسم البياني الرئيسي * @param {string} chartId - معرف عنصر الرسم البياني * @return {boolean} - نجاح أو فشل عملية إعادة التحميل */ function reloadChart(chartId) { try { console.log(`محاولة إعادة تحميل الرسم البياني: ${chartId}...`); // التأكد من وجود البيانات if (!window.candleData || !Array.isArray(window.candleData) || window.candleData.length === 0) { console.error('لا توجد بيانات متاحة لإعادة تحميل الرسم البياني'); return false; } const canvas = document.getElementById(chartId); if (!canvas) { console.error(`لا يمكن العثور على عنصر الرسم البياني: ${chartId}`); return false; } // محاولة تدمير الرسم البياني الحالي إذا وُجد try { const existingChart = Chart.getChart(canvas); if (existingChart) { existingChart.destroy(); console.log('تم تدمير الرسم البياني القديم بنجاح'); } } catch (destroyError) { console.warn('خطأ أثناء تدمير الرسم البياني القديم:', destroyError); // نستمر في المحاولة رغم ذلك } // إعادة رسم البيانات استنادا إلى نوع الرسم البياني if (chartId === 'priceChart' && window.ChartRenderer) { const data = window.candleData; const labels = data.map(candle => typeof candle.time === 'string' ? candle.time.split(',')[0] : candle.time); const values = data.map(candle => parseFloat(candle.close)); console.log(`إعادة رسم البياني الرئيسي مع ${values.length} نقطة بيانات...`); window.ChartRenderer.renderPriceChart(chartId, labels, values, ''); // إزالة علامة الخطأ canvas.removeAttribute('data-error-handled'); return true; } else { console.warn('لا يمكن إعادة تحميل رسم بياني غير معروف:', chartId); return false; } } catch (error) { console.error(`فشل في إعادة تحميل الرسم البياني ${chartId}:`, error); return false; } } /** * إعادة تحميل المؤشر الفني * @param {string} indicatorId - معرف عنصر المؤشر الفني * @return {boolean} - نجاح أو فشل عملية إعادة التحميل */ function reloadIndicator(indicatorId) { try { console.log(`محاولة إعادة تحميل المؤشر الفني: ${indicatorId}...`); // التأكد من وجود البيانات if (!window.candleData || !Array.isArray(window.candleData) || window.candleData.length === 0) { console.error('لا توجد بيانات متاحة لإعادة تحميل المؤشر'); return false; } const canvas = document.getElementById(indicatorId); if (!canvas) { console.error(`لا يمكن العثور على عنصر المؤشر: ${indicatorId}`); return false; } // محاولة تدمير المؤشر الحالي إذا وُجد try { const existingChart = Chart.getChart(canvas); if (existingChart) { existingChart.destroy(); console.log(`تم تدمير المؤشر القديم ${indicatorId} بنجاح`); } } catch (destroyError) { console.warn(`خطأ أثناء تدمير المؤشر القديم ${indicatorId}:`, destroyError); // نستمر في المحاولة رغم ذلك } // تحديد نوع المؤشر من المعرف وإعادة رسمه const indicatorType = indicatorId.replace('Chart', '').toLowerCase(); const data = window.candleData; // إعادة رسم المؤشر حسب نوعه if (!window.TechnicalIndicators) { console.error('مكتبة المؤشرات الفنية غير متاحة'); return false; } console.log(`إعادة رسم المؤشر ${indicatorType}...`); switch (indicatorType) { case 'ma': window.TechnicalIndicators.renderMAChart(indicatorId, data, [20, 50, 200]); break; case 'ema': window.TechnicalIndicators.renderMAChart(indicatorId, data, [12, 26]); break; case 'bb': window.TechnicalIndicators.renderBollingerBandsChart(indicatorId, data, 20, 2); break; case 'rsi': window.TechnicalIndicators.renderRSIChart(indicatorId, data, 14); break; case 'macd': window.TechnicalIndicators.renderMACDChart(indicatorId, data, 12, 26, 9); break; case 'volume': // عادة ما تكون هناك وظيفة خاصة للرسم البياني للحجم if (window.TechnicalIndicators.renderVolumeChart) { window.TechnicalIndicators.renderVolumeChart(indicatorId, data); } break; case 'stochastic': if (window.AdditionalIndicators && window.AdditionalIndicators.renderStochasticChart) { window.AdditionalIndicators.renderStochasticChart(indicatorId, data, 14, 3); } break; case 'adx': if (window.AdditionalIndicators && window.AdditionalIndicators.renderADXChart) { window.AdditionalIndicators.renderADXChart(indicatorId, data, 14); } break; case 'obv': if (window.AdditionalIndicators && window.AdditionalIndicators.renderOBVChart) { window.AdditionalIndicators.renderOBVChart(indicatorId, data); } break; case 'trend': // يعتمد على التنفيذ المحدد لمؤشر الاتجاه break; default: console.warn(`نوع المؤشر غير معروف أو غير مدعوم: ${indicatorType}`); return false; } // إزالة علامة الخطأ canvas.removeAttribute('data-error-handled'); return true; } catch (error) { console.error(`فشل في إعادة تحميل المؤشر ${indicatorId}:`, error); return false; } } /** * التحقق من حالة الاتصال بالشبكة */ function checkNetworkConnection() { // التحقق من حالة الاتصال بالشبكة if (navigator.onLine) { console.log('متصل بالإنترنت'); // محاولة استخدام البيانات المخزنة مؤقتًا إذا كانت متاحة const symbol = 'AAPL'; // استبدلها بالرمز المناسب tryUseCache(symbol); } else { console.warn('غير متصل بالإنترنت'); // عرض رسالة خطأ مخصصة let errorSection = document.querySelector('.network-error'); if (!errorSection) { errorSection = document.createElement('div'); errorSection.className = 'error network-error'; errorSection.style.backgroundColor = '#fff3cd'; errorSection.style.color = '#856404'; errorSection.style.padding = '10px'; errorSection.style.borderRadius = '4px'; errorSection.style.margin = '10px 0'; errorSection.style.borderLeft = '4px solid #856404'; const message = document.createElement('p'); message.textContent = 'لا يوجد اتصال بالإنترنت. يرجى التحقق من اتصالك ومحاولة إعادة التحميل.'; errorSection.appendChild(message); const details = document.createElement('p'); details.style.fontSize = '0.9em'; details.style.color = '#666'; details.textContent = 'تفاصيل تقنية: قد يكون هناك مشكلة في الاتصال بالشبكة أو تم حظر الاتصال بواسطة جدار الحماية.'; errorSection.appendChild(details); // إضافة القسم إلى الصفحة const container = document.querySelector('.container') || document.body; const firstCard = container.querySelector('.card'); if (firstCard) { container.insertBefore(errorSection, firstCard); } else { container.appendChild(errorSection); } } } } /** * نظام تخزين مؤقت للبيانات * هذه الوظائف تساعد في تخزين بيانات الرسوم البيانية مؤقتًا واسترجاعها عند الحاجة */ /** * حفظ بيانات الرسم البياني مؤقتًا * @param {string} symbol - رمز السهم أو الأداة المالية * @param {Array} data - بيانات الشموع اليابانية */ function cacheChartData(symbol, data) { if (!symbol || !data || !Array.isArray(data) || data.length === 0) return; try { const timestamp = new Date().getTime(); const cacheData = { timestamp: timestamp, symbol: symbol, data: data, expiresAt: timestamp + (24 * 60 * 60 * 1000) // تنتهي الصلاحية بعد 24 ساعة }; localStorage.setItem(`chart_data_${symbol}`, JSON.stringify(cacheData)); console.log(`تم تخزين بيانات الرسم البياني لـ ${symbol} مؤقتًا (${data.length} نقطة)`); } catch (error) { console.error('فشل في تخزين بيانات الرسم البياني مؤقتًا:', error); } } /** * استرجاع بيانات الرسم البياني المخزنة مؤقتًا * @param {string} symbol - رمز السهم أو الأداة المالية * @return {Array|null} - بيانات الشموع اليابانية أو null إذا لم تكن متاحة */ function getCachedChartData(symbol) { if (!symbol) return null; try { const cachedDataString = localStorage.getItem(`chart_data_${symbol}`); if (!cachedDataString) return null; const cachedData = JSON.parse(cachedDataString); // التحقق من انتهاء صلاحية البيانات const now = new Date().getTime(); if (cachedData.expiresAt < now) { console.log(`البيانات المخزنة مؤقتًا لـ ${symbol} منتهية الصلاحية، سيتم حذفها`); localStorage.removeItem(`chart_data_${symbol}`); return null; } // حساب عمر البيانات بالدقائق const cacheAgeMinutes = Math.round((now - cachedData.timestamp) / (60 * 1000)); console.log(`استرجاع بيانات مخزنة مؤقتًا لـ ${symbol} (${cachedData.data.length} نقطة، عمر البيانات: ${cacheAgeMinutes} دقيقة)`); return cachedData.data; } catch (error) { console.error('فشل في استرجاع بيانات الرسم البياني المخزنة مؤقتًا:', error); return null; } } /** * استخدام البيانات المخزنة مؤقتًا لإعادة رسم الرسوم البيانية في حالة فشل التحميل * @param {string} symbol - رمز السهم أو الأداة المالية * @return {boolean} - ما إذا كان تم استرجاع وتطبيق البيانات بنجاح */ function tryUseCache(symbol) { if (!symbol) return false; const cachedData = getCachedChartData(symbol); if (!cachedData) return false; try { console.log(`محاولة استخدام البيانات المخزنة مؤقتًا لـ ${symbol}...`); // تعيين البيانات المخزنة مؤقتًا كبيانات حالية window.candleData = cachedData; // إعادة رسم الرسم البياني الرئيسي const priceChart = document.getElementById('priceChart'); if (priceChart && window.ChartRenderer) { const labels = cachedData.map(candle => typeof candle.time === 'string' ? candle.time.split(',')[0] : candle.time); const values = cachedData.map(candle => parseFloat(candle.close)); window.ChartRenderer.renderPriceChart('priceChart', labels, values, symbol); // إعادة رسم المؤشرات الفنية const technicalCharts = [ 'maChart', 'bbChart', 'rsiChart', 'macdChart', 'volumeChart', 'emaChart', 'stochasticChart', 'adxChart', 'obvChart', 'trendChart' ]; technicalCharts.forEach(chartId => { const chartElement = document.getElementById(chartId); if (chartElement && typeof reloadIndicator === 'function') { reloadIndicator(chartId); } }); // إضافة إشعار لإخبار المستخدم أنه يتم عرض بيانات مخزنة مؤقتًا showCachedDataNotification(); return true; } } catch (error) { console.error('فشل في استخدام البيانات المخزنة مؤقتًا:', error); } return false; } /** * عرض إشعار للمستخدم يوضح أنه يتم عرض بيانات مخزنة مؤقتًا */ function showCachedDataNotification() { const notification = document.createElement('div'); notification.className = 'cached-data-notification'; notification.innerHTML = ` <strong>⚠️ تنبيه:</strong> يتم عرض بيانات محفوظة مؤقتًا. قد لا تكون هذه أحدث البيانات المتاحة. <button class="refresh-button">تحديث البيانات</button> `; // تنسيق الإشعار notification.style.backgroundColor = '#fff3cd'; notification.style.color = '#856404'; notification.style.padding = '10px 15px'; notification.style.borderRadius = '5px'; notification.style.margin = '10px 0'; notification.style.textAlign = 'center'; notification.style.position = 'relative'; // تنسيق زر التحديث const refreshButton = notification.querySelector('.refresh-button'); refreshButton.style.backgroundColor = '#856404'; refreshButton.style.color = 'white'; refreshButton.style.border = 'none'; refreshButton.style.borderRadius = '3px'; refreshButton.style.padding = '5px 10px'; refreshButton.style.marginLeft = '10px'; refreshButton.style.cursor = 'pointer'; // إضافة حدث النقر لزر التحديث refreshButton.addEventListener('click', function() { window.location.reload(); }); // إضافة الإشعار في أعلى المحتوى const container = document.querySelector('.container') || document.body; container.insertBefore(notification, container.firstChild); } // مراقبة تغييرات البيانات لتحديث التخزين المؤقت document.addEventListener('chartDataLoaded', function(event) { if (event.detail && event.detail.symbol && event.detail.data) { cacheChartData(event.detail.symbol, event.detail.data); } }); /** * عرض رسالة خطأ التحميل للمستخدم * @param {string} title - عنوان الخطأ * @param {string} message - رسالة الخطأ * @param {boolean} showAlternativeBtn - ما إذا كان يجب عرض زر المصدر البديل */ function showLoadingError(title, message, showAlternativeBtn = false) { console.log(`عرض رسالة الخطأ: ${title} - ${message}`); // البحث عن حاوية الخطأ الموجودة أو إنشاء واحدة جديدة let errorContainer = document.querySelector('.error-container'); if (!errorContainer) { errorContainer = document.createElement('div'); errorContainer.className = 'error-container'; errorContainer.style.backgroundColor = '#fff3f3'; errorContainer.style.border = '1px solid #ffcccc'; errorContainer.style.borderLeft = '4px solid #e74c3c'; errorContainer.style.padding = '15px 20px'; errorContainer.style.margin = '15px 0'; errorContainer.style.borderRadius = '4px'; // إضافة الحاوية إلى الصفحة const container = document.querySelector('.container') || document.body; const chartContainer = container.querySelector('.chart-container'); if (chartContainer) { chartContainer.parentNode.insertBefore(errorContainer, chartContainer); } else { container.insertBefore(errorContainer, container.firstChild); } } else { // مسح المحتوى الحالي errorContainer.innerHTML = ''; } // إضافة العنوان const titleEl = document.createElement('h3'); titleEl.textContent = title; titleEl.style.color = '#e74c3c'; titleEl.style.margin = '0 0 10px 0'; titleEl.style.fontSize = '16px'; errorContainer.appendChild(titleEl); // إضافة الرسالة const messageEl = document.createElement('p'); messageEl.textContent = message; messageEl.style.margin = '0 0 15px 0'; messageEl.style.fontSize = '14px'; messageEl.style.color = '#333'; errorContainer.appendChild(messageEl); // إضافة زر "استخدام مصدر بيانات بديل" إذا كانت مطلوبة if (showAlternativeBtn) { addAlternativeSourceButton(errorContainer); } else { // إضافة زر إغلاق const dismissButton = document.createElement('button'); dismissButton.textContent = 'إغلاق'; dismissButton.style.padding = '5px 12px'; dismissButton.style.backgroundColor = '#e74c3c'; dismissButton.style.color = 'white'; dismissButton.style.border = 'none'; dismissButton.style.borderRadius = '3px'; dismissButton.style.cursor = 'pointer'; dismissButton.style.fontSize = '12px'; dismissButton.addEventListener('click', hideLoadingError); errorContainer.appendChild(dismissButton); } } /** * إخفاء رسالة خطأ التحميل */ function hideLoadingError() { const errorContainer = document.querySelector('.error-container'); if (errorContainer) { errorContainer.style.display = 'none'; } } // عرض الوظائف للاستخدام العام window.LoadingErrorHandler = { handleTimeoutError: handleTimeoutError, monitorChartsForErrors: monitorChartsForErrors, showChartLoadingError: showChartLoadingError, handleWebSocketError: handleWebSocketError, checkNetworkConnection: checkNetworkConnection, reloadChart: reloadChart, reloadIndicator: reloadIndicator };