/* The address-list dialog of the analytics pages (markup: the "analytics_dialog" template) and the tooltip that goes with it. A page loads a list with AnalyticsDialog.load(url, title), then shows it with AnalyticsDialog.fill(m); copying and the CSV download work on the list shown last. */ (function () { 'use strict'; function $(id) { return document.getElementById(id); } function fmt(n) { return Math.round(n).toLocaleString('ru-RU'); } function esc(s) { return String(s).replace(/[&<>"']/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; }); } function tipAttr(t) { return 'data-tip="' + esc(t) + '"'; } var cur = null; // the list shown in the dialog function csvText(cols, rows) { function q(v) { return '"' + String(v).replace(/"/g, '""') + '"'; } return [cols].concat(rows).map(function (r) { return r.map(q).join(','); }).join('\r\n') + '\r\n'; } function open() { var d = $('an-dlg'); if (d.open) return; if (d.showModal) d.showModal(); else d.setAttribute('open', ''); } /* m: title, note, scope ("Запуск" by default), runLabel, cols, rows, hints (column index -> text), cellHints (column index -> function of the value), dist (HTML above the table), csvURL (the file behind "Скачать CSV"). */ function fill(m) { cur = m; $('an-dlg-title').textContent = m.title + ' · ' + fmt(m.rows.length); $('an-dlg-note').textContent = (m.scope || 'Запуск') + ': ' + m.runLabel + '. ' + m.note; $('an-dlg-dist').hidden = !m.dist; $('an-dlg-dist').innerHTML = m.dist || ''; var hint = m.hints || {}; $('an-dlg-tbl').innerHTML = '' + m.cols.map(function (c, i) { return hint[i] ? '' + esc(c) + '' : '' + esc(c) + ''; }).join('') + '' + m.rows.map(function (x) { return '' + x.map(function (v, i) { var h = m.cellHints && m.cellHints[i] ? m.cellHints[i](v) : ''; return '' + esc(v) + ''; }).join('') + ''; }).join('') + ''; $('an-dlg-msg').textContent = ''; } /* Opens the dialog with the title and "Загрузка…", fetches the list as JSON; resolves with it, or with null (and a message in the dialog) on a failure. */ function load(url, title) { $('an-dlg-title').textContent = title; $('an-dlg-note').textContent = 'Загрузка…'; $('an-dlg-dist').hidden = true; $('an-dlg-tbl').innerHTML = ''; $('an-dlg-msg').textContent = ''; cur = null; open(); return fetch(url, { headers: { Accept: 'application/json' }, credentials: 'same-origin' }) .then(function (r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); }) .then(function (l) { return l; }) .catch(function (e) { $('an-dlg-note').textContent = 'Не удалось загрузить список: ' + e.message; return null; }); } function copyCsv() { var msg = $('an-dlg-msg'); if (!cur) return; var text = csvText(cur.cols, cur.rows); var done = function () { msg.textContent = 'CSV скопирован в буфер обмена.'; }; var fail = function () { msg.textContent = 'Буфер обмена недоступен в этом окне.'; }; if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(text).then(done, fail); else fail(); } function downloadCsv() { if (!cur) return; window.location.href = cur.csvURL; } $('an-dlg-csv').addEventListener('click', downloadCsv); $('an-dlg-copy').addEventListener('click', copyCsv); $('an-dlg-close').addEventListener('click', function () { $('an-dlg').close(); }); $('an-dlg').addEventListener('click', function (e) { if (e.target === $('an-dlg')) $('an-dlg').close(); }); /* tooltip: moved into the open dialog, otherwise the modal's top layer covers it */ var tip = $('an-tip'); function tipHost() { var d = $('an-dlg'); return d && d.open ? d : document.body; } document.addEventListener('mouseover', function (e) { var t = e.target.closest('[data-tip]'); if (!t) return; var h = tipHost(); if (tip.parentNode !== h) h.appendChild(tip); tip.textContent = t.dataset.tip; tip.style.opacity = 1; }); document.addEventListener('mousemove', function (e) { tip.style.left = Math.max(8, Math.min(e.clientX + 14, window.innerWidth - 376)) + 'px'; tip.style.top = (e.clientY + 16) + 'px'; }); document.addEventListener('mouseout', function (e) { if (e.target.closest('[data-tip]')) tip.style.opacity = 0; }); window.AnalyticsDialog = { $: $, fmt: fmt, esc: esc, tipAttr: tipAttr, load: load, fill: fill }; })();