class CurrencyManager {
static CONFIG = {
rub: { name: 'Рубль', default: 1, precision: 2 },
usd: { name: 'Доллар США', default: 75, precision: 2, code: 'USD' },
eur: { name: 'Евро', default: 85, precision: 2, code: 'EUR' },
cny: { name: 'Китайский юань', default: 12, precision: 2, code: 'CNY' }
};
constructor() {
this.cacheKey = 'currencyData_v3';
this.apiUrl = 'https://www.cbr-xml-daily.ru/daily_json.js';
this.cacheDuration = 6 * 60 * 60 * 1000; // 6 часов
}
async getRates() {
try {
// Пробуем загрузить свежие курсы
const freshRates = await this.fetchRates();
if (freshRates) return freshRates;
// Если не получилось, пробуем кэш
const cachedRates = this.getCachedRates();
if (cachedRates) return cachedRates;
// Возвращаем дефолтные значения
return this.getDefaultRates();
} catch (error) {
console.error('Ошибка получения курсов:', error);
return this.getDefaultRates();
}
}
async fetchRates() {
try {
const response = await fetch(this.apiUrl);
if (!response.ok) throw new Error(`Ошибка HTTP: ${response.status}`);
const data = await response.json();
if (!data?.Valute) throw new Error('Некорректный формат данных');
const rates = {};
let hasValidData = false;
for (const [key, config] of Object.entries(CurrencyManager.CONFIG)) {
if (key === 'rub') {
rates.rub = 1;
continue;
}
const currencyData = data.Valute[config.code];
if (!currencyData) continue;
const value = parseFloat(currencyData.Value);
if (isNaN(value)) continue;
rates[key] = parseFloat(value.toFixed(config.precision));
hasValidData = true;
}
if (hasValidData) {
this.cacheRates(rates);
return rates;
}
throw new Error('Нет данных по валютам');
} catch (error) {
console.warn('Ошибка загрузки курсов:', error.message);
return null;
}
}
getCachedRates() {
try {
const cache = localStorage.getItem(this.cacheKey);
if (!cache) return null;
const { rates, timestamp } = JSON.parse(cache);
if (!rates || !timestamp) return null;
const cacheAge = Date.now() - timestamp;
if (cacheAge > this.cacheDuration) return null;
return rates;
} catch (error) {
console.warn('Ошибка чтения кэша:', error);
return null;
}
}
cacheRates(rates) {
try {
const cacheData = {
rates,
timestamp: Date.now()
};
localStorage.setItem(this.cacheKey, JSON.stringify(cacheData));
} catch (error) {
console.warn('Ошибка сохранения в кэш:', error);
}
}
getDefaultRates() {
const defaults = {};
for (const [key, config] of Object.entries(CurrencyManager.CONFIG)) {
defaults[key] = config.default;
}
return defaults;
}
}
class CurrencyUI {
constructor(manager) {
this.manager = manager;
this.ratesContainer = document.getElementById('rates-info');
this.currencySelect = document.getElementById('currency');
this.exchangeInput = document.getElementById('exchange-rate');
}
async init() {
if (!this.ratesContainer) {
console.error('Не найден контейнер для курсов');
return;
}
try {
const rates = await this.manager.getRates();
this.displayRates(rates);
this.setupCurrencySelect(rates);
} catch (error) {
console.error('Ошибка инициализации:', error);
this.showError();
}
}
displayRates(rates) {
let html = '
';
html += '
Курсы валют ЦБ РФ
';
html += '
';
for (const [key, config] of Object.entries(CurrencyManager.CONFIG)) {
if (key === 'rub') continue;
const value = rates[key] || config.default;
html += `
${config.name} ${value.toFixed(config.precision)} ₽
`;
}
html += '
';
html += `
Обновлено: ${new Date().toLocaleString('ru-RU')}
`;
html += '
';
this.ratesContainer.innerHTML = html;
}
setupCurrencySelect(rates) {
if (!this.currencySelect || !this.exchangeInput) return;
this.currencySelect.addEventListener('change', () => {
const currency = this.currencySelect.value;
const config = CurrencyManager.CONFIG[currency];
if (!config) return;
const rate = rates[currency] || config.default;
this.exchangeInput.value = rate.toFixed(config.precision);
this.exchangeInput.readOnly = currency === 'rub';
});
}
showError() {
if (this.ratesContainer) {
this.ratesContainer.innerHTML = `
⚠️ Не удалось загрузить курсы валют
`;
}
}
}
// Инициализация при загрузке страницы
document.addEventListener('DOMContentLoaded', () => {
const currencyManager = new CurrencyManager();
const currencyUI = new CurrencyUI(currencyManager);
currencyUI.init();
// Обновление каждые 2 часа
setInterval(() => currencyUI.init(), 2 * 60 * 60 * 1000);
});