Geo-Address-Parser
view release on metacpan or search on metacpan
scripts/generate_index.pl view on Meta::CPAN
}));
}
const dataPoints = [ $js_data ];
HTML
push @html, <<'HTML';
const regressionPoints = linearRegression(dataPoints);
// Try to register the zoom plugin (handles different UMD builds)
(function registerZoomPlugin(){
try {
const candidates = ['chartjsPluginZoom','ChartZoom','zoomPlugin','chartjs_plugin_zoom','ChartjsPluginZoom','chartjsPluginZoom'];
for (const name of candidates) {
if (window[name]) {
try { Chart.register(window[name]); console.log('Registered zoom plugin:', name); return; } catch(e) { console.warn('zoom register failed for', name, e); }
}
}
// Some CDN builds auto-register the plugin; if nothing found that's OK (feature disabled).
} catch(e) {
console.warn('registerZoomPlugin error', e);
}
})();
const ctx = document.getElementById('coverageTrend').getContext('2d');
const chart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'Total Coverage (%)',
data: dataPoints,
borderColor: 'green',
backgroundColor: 'rgba(0,128,0,0.1)',
pointRadius: 5,
pointHoverRadius: 7,
pointStyle: 'circle',
fill: false,
tension: 0.3,
pointBackgroundColor: function(context) {
return context.raw.pointBackgroundColor || 'gray';
}
}, {
label: 'Regression Line',
data: regressionPoints,
borderColor: 'blue',
borderDash: [5, 5],
pointRadius: 0,
fill: false,
tension: 0.0
}]
}, options: {
scales: {
x: {
type: 'time',
time: {
tooltipFormat: 'MMM d, yyyy HH:mm:ss',
unit: 'day'
},
title: { display: true, text: 'Commit Date' }
},
y: { beginAtZero: true, max: 100, title: { display: true, text: 'Coverage (%)' } }
}, plugins: {
legend: {
display: true,
position: 'top', // You can also use 'bottom', 'left', or 'right'
labels: {
boxWidth: 12,
padding: 10,
font: {
size: 12,
weight: 'bold'
}
}
}, tooltip: {
callbacks: {
label: function(context) {
const raw = context.raw;
const coverage = raw.y.toFixed(1);
const delta = raw.delta?.toFixed(1) ?? '0.0';
const sign = delta > 0 ? '+' : delta < 0 ? '-' : '±';
// const baseLine = `${raw.label}: ${coverage}% (${sign}${Math.abs(delta)}%)`;
const baseLine = `${coverage}% (${sign}${Math.abs(delta)}%)`;
const commentLine = raw.comment ? raw.comment : null;
return commentLine ? [baseLine, commentLine] : [baseLine];
}
}
} , zoom: { // Enable zoom & pan on the x-axis for the trend chart
pan: {
enabled: true,
mode: 'x'
}, zoom: {
wheel: {
enabled: true
}, pinch: {
enabled: true
}, mode: 'x'
}
}
}, onClick: (e) => {
const points = chart.getElementsAtEventForMode(e, 'nearest', { intersect: true }, true);
if (points.length) {
const url = chart.data.datasets[0].data[points[0].index].url;
window.open(url, '_blank');
}
}
}
});
document.getElementById('toggleTrend').addEventListener('change', function(e) {
const show = e.target.checked;
const trendDataset = chart.data.datasets.find(ds => ds.label === 'Regression Line');
trendDataset.hidden = !show;
chart.update();
});
// Reset Zoom button handler (calls plugin API if available)
const resetBtn = document.getElementById('resetZoomBtn');
if (resetBtn) {
resetBtn.addEventListener('click', function() {
try {
if (chart && typeof chart.resetZoom === 'function') {
chart.resetZoom();
} else {
scripts/generate_index.pl view on Meta::CPAN
if (!arrow) continue;
if (i === colIndex) {
arrow.textContent = asc ? "â²" : "â¼";
arrow.classList.add("active");
} else {
arrow.textContent = "â²";
arrow.classList.remove("active");
}
}
table.setAttribute("data-sort-col", colIndex);
table.setAttribute("data-sort-order", asc ? "asc" : "desc");
}
// Initial display.
// The table has been set up sorted in ascending order on the filename; reflect that in the GUI
document.addEventListener("DOMContentLoaded", () => {
const table = document.querySelector("table");
if (!table) return;
const headers = table.tHead.rows[0].cells;
for (let i = 0; i < headers.length; i++) {
const arrow = headers[i].querySelector(".arrow");
if (!arrow) continue;
if (i === 0) {
arrow.textContent = "â²";
arrow.classList.add("active");
} else {
arrow.textContent = "â²";
arrow.classList.remove("active");
}
}
});
document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll("canvas.sparkline").forEach(canvas => {
const raw = canvas.getAttribute("data-points");
if (!raw) return;
const points = raw.split(",").map(v => parseFloat(v));
new Chart(canvas.getContext("2d"), {
type: 'line',
data: {
labels: points.map((_, i) => i+1),
datasets: [{
data: points,
borderColor: points.length > 1 && points[points.length-1] >= points[0] ? "green" : "red",
borderWidth: 1,
fill: false,
tension: 0.3,
pointRadius: 0
}]
}, options: {
responsive: false,
maintainAspectRatio: false,
elements: { line: { borderJoinStyle: 'round' } },
plugins: {
legend: { display: false },
tooltip: { enabled: false },
zoom: { // Enable zoom and pan
pan: {
enabled: true,
mode: 'x',
}, zoom: {
wheel: {
enabled: true,
},
pinch: {
enabled: true
},
mode: 'x',
}
}
}, scales: { x: { display: false }, y: { display: false } }
}
});
});
});
function refresh(){
window.location.reload("Refresh")
}
</script>
HTML
push @html, '<p><center>Use mouse wheel or pinch to zoom; drag to pan</center></p>';
# -------------------------------
# Issues flagged on RT
# -------------------------------
{
my $rt_count = fetch_open_rt_ticket_count($config{github_repo});
my $rt_url = "https://rt.cpan.org/Public/Dist/Display.html?Name=$config{github_repo}";
if(defined $rt_count && $rt_count > 0) {
push @html, '<p class="notice rt-issues">',
'<strong>RT issues:</strong>',
"<a href=\"$rt_url\" target=\"_blank\" rel=\"noopener\">",
"$rt_count open ticket" . @{[ $rt_count == 1 ? '' : 's' ]},
'</a>',
'</p>';
} else {
push @html, "<p>No issues active on <a href=\"$rt_url\">RT</a></p>";
}
}
# -------------------------------
# CPAN Testers failing reports table
# -------------------------------
my $dist_name = $config{github_repo};
my $cpan_api = "https://api.cpantesters.org/v3/summary/" . uri_escape($dist_name);
my $http = HTTP::Tiny->new(agent => 'cpan-coverage-html/1.0', timeout => 15);
my $retry = 0;
my $success = 0;
my $res;
( run in 2.672 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )