UNPKG

maher-tradingview-api

Version:

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

556 lines (470 loc) 19.7 kB
/* eslint-disable linebreak-style */ /* eslint-disable no-plusplus */ /* eslint-disable no-unused-vars */ /* eslint-disable prefer-const */ /* eslint-disable no-undef */ /** * connection-manager.js * مدير الاتصال بالإنترنت وتحسين تجربة المستخدم عند انقطاع الاتصال */ // عند تحميل الصفحة document.addEventListener('DOMContentLoaded', function() { // التحقق من حالة الاتصال checkConnectionStatus(); // إضافة مراقب لتغييرات حالة الاتصال window.addEventListener('online', updateConnectionStatus); window.addEventListener('offline', updateConnectionStatus); // تنفيذ اختبار دوري للاتصال setInterval(pingServer, 30000); // كل 30 ثانية // تهيئة مدير مصادر البيانات إذا كان متوفراً initializeDataSourcesManager(); // التحقق من حالة مخزن التخزين المؤقت checkCacheStorage(); }); /** * التحقق من توفر مدير مصادر البيانات وتهيئته */ function initializeDataSourcesManager() { if (!window.DataSourcesManager) { console.warn('مدير مصادر البيانات غير متوفر، سيتم استخدام المصدر الأساسي فقط'); return false; } console.log('مدير مصادر البيانات متوفر، جارٍ التهيئة...'); // الاستماع لتغييرات مصدر البيانات document.addEventListener('dataSourceChanged', handleDataSourceChange); // محاولة استعادة المصدر المحفوظ في التخزين المحلي try { const savedSource = localStorage.getItem('preferredDataSource'); if (savedSource && window.DataSourcesManager.DATA_SOURCES) { const validSources = Object.values(window.DataSourcesManager.DATA_SOURCES); if (validSources.includes(savedSource)) { console.log(`استخدام مصدر البيانات المحفوظ: ${savedSource}`); window.DataSourcesManager.setDataSource(savedSource); } } } catch (e) { console.warn('خطأ في استعادة مصدر البيانات المفضل:', e); } // التحقق من صحة المصادر المتوفرة checkAvailableDataSources(); return true; } /** * معالجة حدث تغيير مصدر البيانات * @param {CustomEvent} event - حدث تغيير المصدر */ function handleDataSourceChange(event) { if (event.detail && event.detail.source) { const { source, reason } = event.detail; console.log(`تم تغيير مصدر البيانات إلى: ${source}، السبب: ${reason || 'غير محدد'}`); // عرض إشعار للمستخدم if (typeof showDataSourceChangedNotification === 'function') { const sourceName = getDataSourceDisplayName(source); showDataSourceChangedNotification(sourceName); } // حفظ المصدر المفضل في التخزين المحلي localStorage.setItem('preferredDataSource', source); // تحديث مؤشرات واجهة المستخدم updateDataSourceIndicators(source); // إرسال إحصائيات استخدام المصدر للتتبع (اختياري) logDataSourceUsage(source, reason); } } /** * الحصول على اسم العرض لمصدر البيانات * @param {string} source - كود مصدر البيانات * @return {string} - الاسم المعروض للمستخدم */ function getDataSourceDisplayName(source) { const sourceNames = { 'tradingview': 'TradingView API', 'alphavantage': 'Alpha Vantage', 'yahoo': 'Yahoo Finance', 'iex': 'IEX Cloud', 'finnhub': 'Finnhub' }; return sourceNames[source] || source; } /** * تحديث مؤشرات واجهة المستخدم لمصدر البيانات * @param {string} source - مصدر البيانات الحالي */ function updateDataSourceIndicators(source) { // تحديث مؤشر مصدر البيانات في الواجهة const sourceNameElement = document.getElementById('currentDataSourceName'); const sourceStatusElement = document.getElementById('dataSourceStatus'); if (sourceNameElement && sourceStatusElement) { // تحديد اسم المصدر وفئة الحالة const sourceName = getDataSourceDisplayName(source); const sourceClass = source === 'tradingview' ? '' : source === 'alphavantage' ? 'secondary' : source === 'yahoo' ? 'fallback' : source === 'iex' ? 'historical' : 'realtime'; // تعيين النص والفئة sourceNameElement.textContent = sourceName; // إزالة جميع الفئات الحالية المتعلقة بالمصدر sourceStatusElement.classList.remove('secondary', 'fallback', 'historical', 'realtime'); // إضافة الفئة المناسبة if (sourceClass) { sourceStatusElement.classList.add(sourceClass); } // تحديث النص sourceStatusElement.textContent = source === 'tradingview' ? 'أساسي' : source === 'alphavantage' ? 'ثانوي' : source === 'yahoo' ? 'بديل' : source === 'iex' ? 'تاريخي' : 'فوري'; } // تحديث لوحة التحكم في مصادر البيانات updateDataSourceControlPanel(source); } /** * تحديث لوحة التحكم في مصادر البيانات * @param {string} source - المصدر الحالي */ function updateDataSourceControlPanel(source) { // التحقق من وجود لوحة التحكم const sourceItems = document.querySelectorAll('.data-source-item'); if (!sourceItems || sourceItems.length === 0) return; // إزالة الفئة النشطة من جميع العناصر sourceItems.forEach(item => { item.classList.remove('active'); }); // العثور على العنصر النشط وتمييزه const activeItem = document.getElementById(`source-${source}`); if (activeItem) { activeItem.classList.add('active'); // تحديث معلومات المصدر const infoElement = activeItem.querySelector('.data-source-item-info'); if (infoElement) { infoElement.textContent = 'مصدر البيانات الحالي'; } // تحديث حالة المصدر const statusElement = activeItem.querySelector('.data-source-item-status'); if (statusElement) { statusElement.textContent = 'متصل'; statusElement.classList.remove('error'); } } } /** * تسجيل استخدام مصدر البيانات للتحليلات * @param {string} source - مصدر البيانات المستخدم * @param {string} reason - سبب تغيير المصدر */ function logDataSourceUsage(source, reason) { // تخزين إحصائيات الاستخدام محلياً try { const usage = JSON.parse(localStorage.getItem('dataSourceUsageStats') || '{}'); if (!usage[source]) { usage[source] = { count: 0, reasons: {} }; } usage[source].count++; if (reason) { if (!usage[source].reasons[reason]) { usage[source].reasons[reason] = 0; } usage[source].reasons[reason]++; } // حفظ الإحصائيات localStorage.setItem('dataSourceUsageStats', JSON.stringify(usage)); } catch (error) { console.error('فشل في حفظ إحصائيات استخدام مصدر البيانات:', error); } } /** * التحقق من حالة مخزن التخزين المؤقت */ function checkCacheStorage() { // التأكد من وجود التخزين المؤقت للبيانات if (typeof initCacheStorage === 'function') { initCacheStorage(); console.log('تم تهيئة مخزن التخزين المؤقت'); return true; } else { console.warn('وظيفة تهيئة مخزن التخزين المؤقت غير متوفرة'); return false; } } /** * استخدام البيانات المخزنة محليًا عند انقطاع الاتصال */ function useOfflineData() { console.log('محاولة استخدام البيانات المخزنة محليًا...'); // محاولة استخدام البيانات المخزنة مؤقتًا if (typeof getCachedChartData === 'function') { // الحصول على الرمز let symbol = 'AAPL'; // قيمة افتراضية const urlParams = new URLSearchParams(window.location.search); if (urlParams.has('symbol')) { symbol = urlParams.get('symbol'); } else if (window.currentSymbol) { symbol = window.currentSymbol; } // محاولة استعادة البيانات المخزنة مؤقتًا const cachedData = getCachedChartData(symbol); if (cachedData) { console.log(`استخدام البيانات المخزنة مؤقتًا لـ ${symbol}`); // استخدام البيانات المخزنة window.candleData = cachedData; // إعادة تحميل الرسم البياني if (typeof window.initializeChartsWithData === 'function') { window.initializeChartsWithData(); } return true; } else { console.warn('لا توجد بيانات مخزنة مؤقتًا لهذا الرمز'); } } else { console.warn('وظيفة استرجاع البيانات المخزنة مؤقتًا غير متاحة'); } return false; } /** * تهيئة نظام تخزين البيانات مؤقتًا */ function initCacheStorage() { // التحقق من دعم واجهة Cache Storage if (typeof caches === 'undefined') { console.warn('Cache Storage API غير مدعومة في هذا المتصفح'); return; } // إنشاء وظائف التخزين المؤقت window.cacheChartData = async function(symbol, data) { try { // تخزين البيانات في Local Storage const cacheKey = `chart_data_${symbol}`; localStorage.setItem(cacheKey, JSON.stringify({ timestamp: Date.now(), data: data })); console.log(`تم تخزين بيانات ${symbol} مؤقتًا`); return true; } catch (error) { console.error('خطأ في تخزين البيانات مؤقتًا:', error); return false; } }; window.getCachedChartData = function(symbol) { try { const cacheKey = `chart_data_${symbol}`; const cachedItem = localStorage.getItem(cacheKey); if (cachedItem) { const parsed = JSON.parse(cachedItem); // التحقق من أن البيانات لم تتجاوز يوم واحد const isExpired = (Date.now() - parsed.timestamp) > (24 * 60 * 60 * 1000); if (!isExpired) { return parsed.data; } else { console.log(`البيانات المخزنة مؤقتًا لـ ${symbol} منتهية الصلاحية`); localStorage.removeItem(cacheKey); } } } catch (error) { console.error('خطأ في استرجاع البيانات المخزنة مؤقتًا:', error); } return null; }; } /** * التحقق من حالة الاتصال الحالية */ function checkConnectionStatus() { if (navigator.onLine) { setOnlineStatus(true); } else { setOnlineStatus(false); } } /** * تحديث حالة الاتصال عند تغييرها */ function updateConnectionStatus(event) { setOnlineStatus(navigator.onLine); // إذا عاد الاتصال وكان هناك خطأ سابق، يمكن تحديث الصفحة if (navigator.onLine && document.querySelector('.error') && document.querySelector('.error').textContent.includes('انتهت مهلة')) { // عرض خيار إعادة المحاولة showRetryOption(); } } /** * تعيين حالة الاتصال في واجهة المستخدم */ function setOnlineStatus(isOnline) { // التحقق من وجود عنصر حالة الاتصال let statusElement = document.getElementById('connection-status'); // إنشاء العنصر إذا لم يكن موجوداً if (!statusElement) { statusElement = document.createElement('div'); statusElement.id = 'connection-status'; statusElement.className = 'connection-status'; document.body.appendChild(statusElement); } // تحديث حالة الاتصال if (isOnline) { statusElement.textContent = '✓ متصل بالإنترنت'; statusElement.className = 'connection-status online'; } else { statusElement.textContent = '✗ غير متصل بالإنترنت'; statusElement.className = 'connection-status offline'; // عرض رسالة للمستخدم showOfflineMessage(); } } /** * عرض رسالة عدم الاتصال */ function showOfflineMessage() { // التحقق من عدم وجود الرسالة بالفعل if (document.getElementById('offline-message')) { return; } // إنشاء عنصر الرسالة const messageEl = document.createElement('div'); messageEl.id = 'offline-message'; messageEl.className = 'error-message'; messageEl.innerHTML = ` <h3>انقطع الاتصال بالإنترنت</h3> <p>يبدو أنك غير متصل بالإنترنت حالياً. تأكد من اتصالك وحاول مرة أخرى.</p> `; // إدراج الرسالة في بداية المحتوى const container = document.querySelector('.container'); if (container) { container.insertBefore(messageEl, container.firstChild); } } /** * إزالة رسالة عدم الاتصال */ function removeOfflineMessage() { const messageEl = document.getElementById('offline-message'); if (messageEl) { messageEl.remove(); } } /** * اختبار الاتصال بالخادم */ function pingServer() { // إنشاء طلب بسيط للتحقق من الاتصال fetch(window.location.origin + '/ping', { method: 'GET', cache: 'no-store', headers: { 'Cache-Control': 'no-cache' }, signal: AbortSignal.timeout(5000) // وقت انتهاء مهلة 5 ثواني }) .then(response => { if (response.ok) { setOnlineStatus(true); removeOfflineMessage(); // إذا كان هناك خطأ WebSocket سابق، نحاول إعادة الاتصال checkForWebSocketErrors(); } else { setOnlineStatus(false); } }) .catch((error) => { // في حالة فشل الاتصال console.warn('فشل اتصال ping:', error); setOnlineStatus(false); // إذا كان الخطأ بسبب انتهاء المهلة، نظهر رسالة خاصة if (error.name === 'TimeoutError' || error.name === 'AbortError') { showTimeoutWarning(); } }); } /** * إظهار تحذير بسبب انتهاء مهلة ping */ function showTimeoutWarning() { // التحقق من عدم وجود تحذير بالفعل if (document.getElementById('timeout-warning')) { return; } // إنشاء عنصر التحذير const warningEl = document.createElement('div'); warningEl.id = 'timeout-warning'; warningEl.className = 'connection-alert warning'; warningEl.innerHTML = ` <strong>⚠️ تنبيه:</strong> استغرق الاتصال بالخادم وقتًا طويلاً. قد تواجه بطئًا في تحميل البيانات. <button id="check-connection-btn" style="margin-left: 10px; background-color: #0088cc; color: white; border: none; border-radius: 4px; padding: 3px 8px; cursor: pointer;">فحص الاتصال</button> `; // تنسيق التحذير warningEl.style.backgroundColor = '#fff3cd'; warningEl.style.color = '#856404'; warningEl.style.padding = '10px 15px'; warningEl.style.borderRadius = '5px'; warningEl.style.margin = '10px 0'; warningEl.style.textAlign = 'center'; // إضافة التحذير إلى الصفحة const container = document.querySelector('.container') || document.body; container.insertBefore(warningEl, container.firstChild); // إضافة معالج النقر لزر فحص الاتصال document.getElementById('check-connection-btn').addEventListener('click', function() { this.textContent = 'جارٍ الفحص...'; this.disabled = true; // إجراء فحص للاتصال pingServer(); // بعد ثانيتين، إما إزالة التحذير أو تحديثه setTimeout(() => { if (navigator.onLine) { warningEl.remove(); } else { this.textContent = 'فحص الاتصال'; this.disabled = false; } }, 2000); }); } /** * التحقق من وجود أخطاء WebSocket سابقة */ function checkForWebSocketErrors() { // التحقق من وجود أخطاء WebSocket محددة في الصفحة const webSocketErrorEl = document.querySelector('.websocket-error'); if (webSocketErrorEl) { console.log('تم العثور على خطأ WebSocket سابق، محاولة إصلاحه تلقائيًا...'); // إضافة ملاحظة بالمحاولة const noteEl = document.createElement('p'); noteEl.innerHTML = '<strong>ملاحظة:</strong> الاتصال بالإنترنت مستقر الآن. جارٍ محاولة إعادة الاتصال...'; noteEl.style.color = '#27ae60'; noteEl.style.marginTop = '10px'; webSocketErrorEl.appendChild(noteEl); // إعادة تحميل الصفحة بعد ثانيتين setTimeout(() => { window.location.reload(); }, 2000); } } /** * عرض خيار إعادة المحاولة */ function showRetryOption() { // التحقق من وجود قسم الخطأ const errorSection = document.querySelector('.error'); if (!errorSection || document.getElementById('retry-option')) { return; } // إنشاء عنصر إعادة المحاولة const retryEl = document.createElement('div'); retryEl.id = 'retry-option'; retryEl.className = 'timeout-counter'; retryEl.textContent = 'تم استعادة الاتصال! يمكنك إعادة المحاولة الآن.'; // إنشاء زر إعادة المحاولة const retryButton = document.createElement('button'); retryButton.textContent = 'إعادة المحاولة'; retryButton.className = 'retry-button'; retryButton.addEventListener('click', function() { window.location.reload(); }); retryEl.appendChild(document.createElement('br')); retryEl.appendChild(retryButton); // إضافة العنصر إلى قسم الخطأ errorSection.appendChild(retryEl); } // عرض الوظائف للاستخدام العام window.ConnectionManager = { checkStatus: checkConnectionStatus, updateStatus: updateConnectionStatus, pingServer: pingServer };