';
}
function render(headers, rows) {
if (!headers.length) {
container.innerHTML =
'
' +
'
Таблица пуста
' +
'
';
if (rowCountEl) rowCountEl.textContent = '';
return;
}
var mapping = buildMapping(headers);
if (mapping.rideIdxs.length === 0) {
container.innerHTML =
'
' +
'
' +
'
' +
' !' +
' Квалификации' +
'
' +
'
' +
'
В шапке не найдена колонка «' + esc(RIDE_COLUMN) + '».
' +
'
';
if (rowCountEl) rowCountEl.textContent = '';
prevRows = null;
return;
}
if (mapping.qualIdxs.length === 0) {
container.innerHTML =
'
' +
'
' +
'
' +
' !' +
' Квалификации' +
'
' +
'
' +
'
Колонка «' + esc(RIDE_COLUMN) + '» найдена, но в шапке нет меток «Квал N».
' +
'
';
if (rowCountEl) rowCountEl.textContent = '';
prevRows = null;
return;
}
var qualSources = resolveQualSources(mapping);
var tableData = buildTableData(rows, qualSources);
var rendered = renderTable(tableData.headers, tableData.rows, tableData.qualSources);
var html = '
';
html += '
';
html += '
';
html += ' ★';
html += ' Квалификации';
html += '
';
html += ' ' + tableData.rows.length + ' ' + plural(tableData.rows.length, 'запись', 'записи', 'записей') + '';
html += '
';
html += renderLegend(qualSources);
html += rendered.html;
html += '
';
container.innerHTML = html;
var card = container.querySelector('.hp-sheet__card');
rendered.changedKeys.forEach(function (k) {
var tr = card && card.querySelector('tr[data-key="' + k + '"]');
if (tr) tr.classList.add('is-updated');
});
prevRows = tableData.rows;
if (rowCountEl) rowCountEl.textContent =
'Всего: ' + rows.length + ' (' + plural(rows.length, 'запись', 'записи', 'записей') + ')' +
' · Квал-колонок: ' + qualSources.length;
}
function renderError(msg, hint) {
var h = hint ? '
' + esc(hint) + '
' : '';
container.innerHTML =
'
' +
'
' + esc(msg) + '
' +
h +
'' +
'
';
if (rowCountEl) rowCountEl.textContent = '';
var r = document.getElementById('hsRetry');
if (r) r.addEventListener('click', fetchData);
}
// === ТОЛЬКО JSONP — никаких fetch, никаких CORS ===
function fetchData() {
if (isLoading) return;
setStatus('loading');
setLoading(true);
// Чистим предыдущий скрипт, если остался
if (currentScript && currentScript.parentNode) {
currentScript.parentNode.removeChild(currentScript);
currentScript = null;
}
jsonpCounter++;
var cbName = '_hsCb_' + Date.now() + '_' + jsonpCounter + '_' + Math.floor(Math.random() * 100000);
var done = false;
var timer = null;
function cleanup() {
if (timer) clearTimeout(timer);
if (currentScript && currentScript.parentNode) {
currentScript.parentNode.removeChild(currentScript);
}
currentScript = null;
try { delete window[cbName]; } catch (e) { window[cbName] = undefined; }
}
window[cbName] = function (response) {
if (done) return;
done = true;
cleanup();
try {
var table = response.table;
var headers = (table.cols || []).map(function (c) { return c.label || ''; });
var rows = (table.rows || []).map(function (r) {
return (r.c || []).map(function (cell) {
if (!cell) return '';
return cell.f !== undefined ? cell.f : (cell.v !== undefined ? cell.v : '');
});
});
render(headers, rows);
setStatus('ok');
} catch (e) {
console.error('[hp-sheet] parse error:', e);
renderError('Ошибка обработки данных', e.message);
setStatus('error');
}
setLoading(false);
resetCountdown();
};
var script = document.createElement('script');
script.src = 'https://docs.google.com/spreadsheets/d/' + SHEET_ID +
'/gviz/tq?tqx=responseHandler:' + cbName;
script.onerror = function () {
if (done) return;
done = true;
cleanup();
renderError(
'Не удалось загрузить данные из Google Таблицы.',
'Проверьте доступ: откройте таблицу, нажмите «Настройки доступа» → «Все, у кого есть ссылка».'
);
setStatus('error');
setLoading(false);
resetCountdown();
};
timer = setTimeout(function () {
if (done) return;
done = true;
cleanup();
renderError('Превышено время ожидания ответа от Google.', 'Попробуйте обновить страницу.');
setStatus('error');
setLoading(false);
resetCountdown();
}, 15000);
currentScript = script;
document.head.appendChild(script);
}
function resetCountdown() {
if (countdownId) clearInterval(countdownId);
countdown = REFRESH_INTERVAL;
if (timerEl) timerEl.textContent = 'Авто-обновление через ' + countdown + ' с';
countdownId = setInterval(function () {
countdown--;
if (countdown <= 0) {
if (timerEl) timerEl.textContent = 'Обновление…';
clearInterval(countdownId);
fetchData();
} else {
if (timerEl) timerEl.textContent = 'Авто-обновление через ' + countdown + ' с';
}
}, 1000);
}
if (refreshBtn) refreshBtn.addEventListener('click', fetchData);
fetchData();
})();