← back to draynov1__FX-game

Function bodies 19 total

All specs Real LLM only Function bodies
countryName function · javascript · L84-L86 (3 LOC)
app.js
function countryName(code) {
  return COUNTRY_NAMES[code] || code;
}
loadCache function · javascript · L126-L136 (11 LOC)
app.js
function loadCache(ignoreExpiry) {
  try {
    const raw = localStorage.getItem(CACHE_KEY);
    if (!raw) return null;
    const data = JSON.parse(raw);
    if (!ignoreExpiry && Date.now() - data.timestamp > CACHE_DURATION) return null;
    return data;
  } catch {
    return null;
  }
}
saveCache function · javascript · L138-L145 (8 LOC)
app.js
function saveCache(ratesObj) {
  try {
    localStorage.setItem(CACHE_KEY, JSON.stringify({
      rates: ratesObj,
      timestamp: Date.now(),
    }));
  } catch { /* quota exceeded — ignore */ }
}
buildAvailable function · javascript · L147-L162 (16 LOC)
app.js
function buildAvailable() {
  availableCurrencies = Object.keys(ALL_CURRENCIES).filter(
    (c) => c === 'EUR' || rates[c] !== undefined,
  );
  // Keep only available currencies in selection
  selectedCurrencies = selectedCurrencies.filter(
    (c) => c !== 'EUR' && availableCurrencies.includes(c),
  );
  // Fill to 10 if needed
  for (const c of availableCurrencies) {
    if (selectedCurrencies.length >= 10) break;
    if (c !== 'EUR' && !selectedCurrencies.includes(c)) {
      selectedCurrencies.push(c);
    }
  }
}
fetchRates function · javascript · L164-L192 (29 LOC)
app.js
async function fetchRates() {
  const cached = loadCache(false);
  if (cached) {
    rates = cached.rates;
    rates.EUR = 1;
    buildAvailable();
    return;
  }

  try {
    const res = await fetch('https://api.frankfurter.app/latest?from=EUR');
    if (!res.ok) throw new Error('API ' + res.status);
    const data = await res.json();
    rates = data.rates;
    rates.EUR = 1;
    saveCache(rates);
    buildAvailable();
  } catch (err) {
    // Fall back to stale cache
    const stale = loadCache(true);
    if (stale) {
      rates = stale.rates;
      rates.EUR = 1;
      buildAvailable();
    } else {
      throw err;
    }
  }
}
formatRate function · javascript · L195-L201 (7 LOC)
app.js
function formatRate(value) {
  if (!Number.isFinite(value)) return '—';
  if (value >= 1000) return value.toLocaleString('bg-BG', { maximumFractionDigits: 0 });
  if (value >= 100)  return value.toLocaleString('bg-BG', { maximumFractionDigits: 1 });
  if (value >= 1)    return value.toLocaleString('bg-BG', { maximumFractionDigits: 2 });
  return value.toLocaleString('bg-BG', { maximumFractionDigits: 4 });
}
convert function · javascript · L203-L207 (5 LOC)
app.js
function convert(amount, from, to) {
  const f = from === 'EUR' ? 1 : (rates[from] || 1);
  const t = to === 'EUR' ? 1 : (rates[to] || 1);
  return amount * t / f;
}
Repobility · code-quality intelligence · https://repobility.com
renderGrid function · javascript · L210-L233 (24 LOC)
app.js
function renderGrid() {
  const grid = $('currency-grid');
  grid.innerHTML = '';
  selectedCurrencies.forEach((code, i) => {
    const cur = ALL_CURRENCIES[code];
    if (!cur) return;
    const card = document.createElement('div');
    card.className = 'currency-card';
    card.style.borderTopColor = CARD_COLORS[i % CARD_COLORS.length];
    card.style.animationDelay = i * 0.05 + 's';
    card.innerHTML =
      '<span class="flag">' + cur.flag + '</span>' +
      '<div class="code">' + countryName(code) + '</div>' +
      '<div class="name">' + cur.name + '</div>' +
      '<div class="rate">' + formatRate(rates[code]) + '</div>';
    card.addEventListener('click', () => {
      converterTo = code;
      $('to-currency').value = code;
      updateConverter();
      $('converter-section').scrollIntoView({ behavior: 'smooth' });
    });
    grid.appendChild(card);
  });
}
populateConverterSelects function · javascript · L236-L250 (15 LOC)
app.js
function populateConverterSelects() {
  const sorted = ['EUR'].concat(
    availableCurrencies.filter((c) => c !== 'EUR'),
  );
  const html = sorted.map((code) => {
    const cur = ALL_CURRENCIES[code];
    const label = cur ? cur.flag + ' ' + countryName(code) + ' \u2014 ' + cur.name : code;
    return '<option value="' + code + '">' + label + '</option>';
  }).join('');

  $('from-currency').innerHTML = html;
  $('to-currency').innerHTML = html;
  $('from-currency').value = converterFrom;
  $('to-currency').value = converterTo;
}
updateConverter function · javascript · L252-L262 (11 LOC)
app.js
function updateConverter() {
  const amount = parseFloat($('from-amount').value) || 0;
  converterFrom = $('from-currency').value;
  converterTo = $('to-currency').value;
  const result = convert(amount, converterFrom, converterTo);
  const el = $('to-result');
  el.textContent = formatRate(result);
  el.classList.remove('pop-in');
  void el.offsetWidth; // reflow to restart animation
  el.classList.add('pop-in');
}
renderItems function · javascript · L265-L295 (31 LOC)
app.js
function renderItems() {
  const grid = $('items-grid');
  grid.innerHTML = '';
  ITEMS.forEach((item, i) => {
    const card = document.createElement('div');
    card.className = 'item-card';
    card.style.animationDelay = i * 0.04 + 's';

    let pricesHtml = '';
    selectedCurrencies.forEach((code) => {
      const cur = ALL_CURRENCIES[code];
      if (!cur) return;
      const price = convert(item.price, 'EUR', code);
      pricesHtml +=
        '<div class="item-price">' +
          '<span class="flag">' + cur.flag + '</span>' +
          '<span class="val">' + formatRate(price) + '</span>' +
          '<span class="cur">' + countryName(code) + '</span>' +
        '</div>';
    });

    card.innerHTML =
      '<div class="item-header">' +
        '<span class="item-emoji">' + item.emoji + '</span>' +
        '<span class="item-name">' + item.name + '</span>' +
        '<span class="item-eur-price">' + item.price.toFixed(2) + ' \u20AC</span>' +
      '</div>' +
      '<div class=
openModal function · javascript · L298-L370 (73 LOC)
app.js
function openModal() {
  const modal = $('modal');
  const list = $('currency-list');
  const tempSelected = new Set(selectedCurrencies);

  const regionOrder = ['Европа', 'Америка', 'Азия и Океания', 'Африка и Близък изток'];

  function renderList() {
    list.innerHTML = '';

    // Group available (non-EUR) currencies by region
    const groups = {};
    availableCurrencies.forEach((code) => {
      if (code === 'EUR') return;
      const cur = ALL_CURRENCIES[code];
      const region = cur ? cur.region : 'Други';
      if (!groups[region]) groups[region] = [];
      groups[region].push(code);
    });

    regionOrder.forEach((region) => {
      const codes = groups[region];
      if (!codes || codes.length === 0) return;

      const section = document.createElement('div');
      section.className = 'region-group';
      section.innerHTML = '<h3>' + region + '</h3>';

      codes.forEach((code) => {
        const cur = ALL_CURRENCIES[code];
        const checked = tempSelected.has
renderList function · javascript · L305-L352 (48 LOC)
app.js
  function renderList() {
    list.innerHTML = '';

    // Group available (non-EUR) currencies by region
    const groups = {};
    availableCurrencies.forEach((code) => {
      if (code === 'EUR') return;
      const cur = ALL_CURRENCIES[code];
      const region = cur ? cur.region : 'Други';
      if (!groups[region]) groups[region] = [];
      groups[region].push(code);
    });

    regionOrder.forEach((region) => {
      const codes = groups[region];
      if (!codes || codes.length === 0) return;

      const section = document.createElement('div');
      section.className = 'region-group';
      section.innerHTML = '<h3>' + region + '</h3>';

      codes.forEach((code) => {
        const cur = ALL_CURRENCIES[code];
        const checked = tempSelected.has(code);
        const disabled = !checked && tempSelected.size >= 10;

        const row = document.createElement('div');
        row.className = 'cur-option' + (disabled ? ' disabled' : '');
        row.innerHTML =
          '<
saveSelection function · javascript · L373-L377 (5 LOC)
app.js
function saveSelection() {
  try {
    localStorage.setItem(SELECTION_KEY, JSON.stringify(selectedCurrencies));
  } catch { /* ignore */ }
}
loadSelection function · javascript · L379-L386 (8 LOC)
app.js
function loadSelection() {
  try {
    const raw = localStorage.getItem(SELECTION_KEY);
    if (!raw) return;
    const arr = JSON.parse(raw);
    if (Array.isArray(arr) && arr.length > 0) selectedCurrencies = arr;
  } catch { /* ignore */ }
}
Repobility · severity-and-effort ranking · https://repobility.com
setupEvents function · javascript · L389-L403 (15 LOC)
app.js
function setupEvents() {
  $('customize-btn').addEventListener('click', openModal);
  $('refresh-btn').addEventListener('click', refreshRates);

  $('from-currency').addEventListener('change', updateConverter);
  $('to-currency').addEventListener('change', updateConverter);
  $('from-amount').addEventListener('input', updateConverter);

  $('swap-btn').addEventListener('click', () => {
    const tmp = $('from-currency').value;
    $('from-currency').value = $('to-currency').value;
    $('to-currency').value = tmp;
    updateConverter();
  });
}
refreshRates function · javascript · L406-L426 (21 LOC)
app.js
async function refreshRates() {
  const btn = $('refresh-btn');
  btn.disabled = true;
  btn.textContent = '...';
  try {
    const res = await fetch('https://api.frankfurter.app/latest?from=EUR');
    if (!res.ok) throw new Error('API ' + res.status);
    const data = await res.json();
    rates = data.rates;
    rates.EUR = 1;
    saveCache(rates);
    buildAvailable();
    renderGrid();
    updateConverter();
    renderItems();
    $('last-updated').textContent =
      'Последно обновяване: ' + new Date().toLocaleString('bg-BG');
  } catch { /* silent */ }
  btn.disabled = false;
  btn.textContent = 'Обнови';
}
startAutoRefresh function · javascript · L429-L446 (18 LOC)
app.js
function startAutoRefresh() {
  setInterval(async () => {
    try {
      const res = await fetch('https://api.frankfurter.app/latest?from=EUR');
      if (!res.ok) return;
      const data = await res.json();
      rates = data.rates;
      rates.EUR = 1;
      saveCache(rates);
      buildAvailable();
      renderGrid();
      updateConverter();
      renderItems();
      $('last-updated').textContent =
        'Последно обновяване: ' + new Date().toLocaleString('bg-BG');
    } catch { /* silent */ }
  }, CACHE_DURATION);
}
init function · javascript · L449-L479 (31 LOC)
app.js
async function init() {
  loadSelection();

  try {
    await fetchRates();
  } catch {
    $('loader').innerHTML =
      '<p style="color:#FF6B6B;font-size:1.2rem;padding:2rem;text-align:center">' +
      'Грешка при зареждане. Моля, опитай отново.' +
      '</p>';
    return;
  }

  populateConverterSelects();
  renderGrid();
  updateConverter();
  renderItems();
  setupEvents();
  startAutoRefresh();

  // Show last-updated timestamp
  const cached = loadCache(true);
  if (cached) {
    $('last-updated').textContent =
      'Последно обновяване: ' + new Date(cached.timestamp).toLocaleString('bg-BG');
  }

  // Hide loader, show app
  $('loader').classList.add('hidden');
  $('app').classList.remove('hidden');
}