mirror of
https://github.com/allinurl/goaccess.git
synced 2025-06-18 14:35:34 -04:00
Added World Map to the Geo Location panel on the HTML report.
Area and bar charts are still available for this panel. Closes #524
This commit is contained in:
parent
d7c7dacae2
commit
5ee83fd63c
4
.gitignore
vendored
4
.gitignore
vendored
@ -97,6 +97,8 @@ Makefile.in
|
||||
/src/facss.h
|
||||
/src/hoganjs.h
|
||||
/src/tpls.h
|
||||
/src/countries110m.h
|
||||
/src/topojsonjs.h
|
||||
|
||||
# intermediate resources
|
||||
/resources/css/app.css.tmp
|
||||
@ -107,3 +109,5 @@ Makefile.in
|
||||
/resources/js/d3.v?.min.js.tmp
|
||||
/resources/js/hogan.min.js.tmp
|
||||
/resources/tpls.html.tmp
|
||||
/resources/countries-110m.json.tmp
|
||||
/resources/js/topojson.v3.min.js.tmp
|
||||
|
24
Makefile.am
24
Makefile.am
@ -9,7 +9,9 @@ dist_noinst_DATA = \
|
||||
resources/css/fa.min.css \
|
||||
resources/js/app.js \
|
||||
resources/js/charts.js \
|
||||
resources/countries-110m.json \
|
||||
resources/js/d3.v7.min.js \
|
||||
resources/js/topojson.v3.min.js \
|
||||
resources/js/hogan.min.js
|
||||
|
||||
noinst_PROGRAMS = bin2c
|
||||
@ -21,7 +23,9 @@ BUILT_SOURCES = \
|
||||
src/facss.h \
|
||||
src/appcss.h \
|
||||
src/d3js.h \
|
||||
src/topojsonjs.h \
|
||||
src/hoganjs.h \
|
||||
src/countries110m.h \
|
||||
src/chartsjs.h \
|
||||
src/appjs.h
|
||||
|
||||
@ -31,14 +35,18 @@ CLEANFILES = \
|
||||
src/facss.h \
|
||||
src/appcss.h \
|
||||
src/d3js.h \
|
||||
src/topojsonjs.h \
|
||||
src/hoganjs.h \
|
||||
src/countries110m.h \
|
||||
src/chartsjs.h \
|
||||
src/appjs.h \
|
||||
resources/tpls.html.tmp \
|
||||
resources/countries-110m.json.tmp \
|
||||
resources/css/bootstrap.min.css.tmp \
|
||||
resources/css/fa.min.css.tmp \
|
||||
resources/css/app.css.tmp \
|
||||
resources/js/d3.v7.min.js.tmp \
|
||||
resources/js/topojson.v3.min.js.tmp \
|
||||
resources/js/hogan.min.js.tmp \
|
||||
resources/js/charts.js.tmp \
|
||||
resources/js/app.js.tmp
|
||||
@ -51,6 +59,14 @@ if HAS_SEDTR
|
||||
else
|
||||
./bin2c $(srcdir)/resources/tpls.html src/tpls.h tpls
|
||||
endif
|
||||
# countries.json
|
||||
src/countries110m.h: bin2c$(EXEEXT) $(srcdir)/resources/countries-110m.json
|
||||
if HAS_SEDTR
|
||||
cat $(srcdir)/resources/countries-110m.json | sed "s/^[[:space:]]*//" | sed "/^$$/d" | tr -d "\r\n" > $(srcdir)/resources/countries-110m.json.tmp
|
||||
./bin2c $(srcdir)/resources/countries-110m.json.tmp src/countries110m.h countries_json
|
||||
else
|
||||
./bin2c $(srcdir)/resources/countries-110m.json src/countries110m.h countries_json
|
||||
endif
|
||||
# Bootstrap
|
||||
src/bootstrapcss.h: bin2c$(EXEEXT) $(srcdir)/resources/css/bootstrap.min.css
|
||||
if HAS_SEDTR
|
||||
@ -83,6 +99,14 @@ if HAS_SEDTR
|
||||
else
|
||||
./bin2c $(srcdir)/resources/js/d3.v7.min.js src/d3js.h d3_js
|
||||
endif
|
||||
# topojson.js
|
||||
src/topojsonjs.h: bin2c$(EXEEXT) $(srcdir)/resources/js/topojson.v3.min.js
|
||||
if HAS_SEDTR
|
||||
cat $(srcdir)/resources/js/topojson.v3.min.js | sed "s/^[[:space:]]*//" | sed "/^$$/d" | tr -d "\r\n" > $(srcdir)/resources/js/topojson.v3.min.js.tmp
|
||||
./bin2c $(srcdir)/resources/js/topojson.v3.min.js.tmp src/topojsonjs.h topojson_js
|
||||
else
|
||||
./bin2c $(srcdir)/resources/js/topojson.v3.min.js src/topojsonjs.h topojson_js
|
||||
endif
|
||||
# Hogan.js
|
||||
src/hoganjs.h: bin2c$(EXEEXT) $(srcdir)/resources/js/hogan.min.js
|
||||
if HAS_SEDTR
|
||||
|
1
resources/countries-110m.json
Normal file
1
resources/countries-110m.json
Normal file
File diff suppressed because one or more lines are too long
@ -1144,3 +1144,19 @@ html.dark.purple,
|
||||
.dark.purple .line1 {
|
||||
stroke: #d048b6;
|
||||
}
|
||||
.country {
|
||||
fill: #ccc;
|
||||
stroke: #fff;
|
||||
stroke-width: 0.5px;
|
||||
}
|
||||
.country:hover {
|
||||
fill: #b3b3b3;
|
||||
}
|
||||
.dark .country {
|
||||
fill: #ccc;
|
||||
stroke: #222;
|
||||
stroke-width: 0.5px;
|
||||
}
|
||||
.dark .legend-svg text {
|
||||
fill: #FFF;
|
||||
}
|
||||
|
@ -1094,6 +1094,16 @@ GoAccess.Charts = {
|
||||
return arr.join(' ');
|
||||
},
|
||||
|
||||
getWMap: function (panel, plotUI, data) {
|
||||
var chart = WorldMap(d3.select("#chart-" + panel));
|
||||
chart.width($("#chart-" + panel).getBoundingClientRect().width);
|
||||
chart.height(400);
|
||||
chart.metric(plotUI['d3']['y0']['key']);
|
||||
chart.opts(plotUI);
|
||||
|
||||
return chart;
|
||||
},
|
||||
|
||||
getAreaSpline: function (panel, plotUI, data) {
|
||||
var dualYaxis = plotUI['d3']['y1'];
|
||||
|
||||
@ -1186,6 +1196,9 @@ GoAccess.Charts = {
|
||||
case 'bar':
|
||||
chart = this.getVBar(panel, plotUI, data);
|
||||
break;
|
||||
case 'wmap':
|
||||
chart = this.getWMap(panel, plotUI, data);
|
||||
break;
|
||||
}
|
||||
|
||||
return chart;
|
||||
@ -1196,7 +1209,7 @@ GoAccess.Charts = {
|
||||
d3.select('#chart-' + panel + '>.chart-tooltip-wrap')
|
||||
.remove();
|
||||
// remove svg
|
||||
d3.select('#chart-' + panel).select('svg')
|
||||
d3.select('#chart-' + panel).selectAll('svg')
|
||||
.remove();
|
||||
// add chart to the document
|
||||
d3.select("#chart-" + panel)
|
||||
|
@ -37,6 +37,286 @@ function truncate(text, width) {
|
||||
});
|
||||
}
|
||||
|
||||
function WorldMap() {
|
||||
const maxLat = 84;
|
||||
let path = null;
|
||||
let projection = null;
|
||||
let tlast = [0, 0];
|
||||
let slast = null;
|
||||
let opts = {};
|
||||
let metric = 'hits';
|
||||
let margin = {
|
||||
top: 20,
|
||||
right: 50,
|
||||
bottom: 40,
|
||||
left: 50
|
||||
};
|
||||
let width = 760;
|
||||
let height = 170;
|
||||
|
||||
function innerW() {
|
||||
return width - margin.left - margin.right;
|
||||
}
|
||||
|
||||
function mapData(data) {
|
||||
return data.reduce((countryData, region) => {
|
||||
if (!region.items) countryData.push(region);
|
||||
else region.items.forEach(item => countryData.push({
|
||||
data: item.data,
|
||||
hits: item.hits.count,
|
||||
visitors: item.visitors.count,
|
||||
bytes: item.bytes.count,
|
||||
region: region.data
|
||||
}));
|
||||
return countryData;
|
||||
}, []);
|
||||
}
|
||||
|
||||
function formatTooltip(data) {
|
||||
const d = {...data};
|
||||
let out = {};
|
||||
|
||||
out[0] = GoAccess.Util.fmtValue(d['data'], 'str');
|
||||
out[1] = metric == 'bytes' ? GoAccess.Util.fmtValue(d['bytes'], 'bytes') : d3.format(',')(d['hits']);
|
||||
if (metric == 'hits')
|
||||
out[2] = d3.format(',')(d['visitors']);
|
||||
|
||||
const template = d3.select('#tpl-chart-tooltip').html();
|
||||
return Hogan.compile(template).render({
|
||||
'data': out
|
||||
});
|
||||
}
|
||||
|
||||
function mouseover(event, selection, data) {
|
||||
const tooltip = selection.select('.chart-tooltip-wrap');
|
||||
tooltip.html(formatTooltip(data))
|
||||
.style('left', `${d3.pointer(event)[0] + 10}px`)
|
||||
.style('top', `${d3.pointer(event)[1] + 10}px`)
|
||||
.style('display', 'block');
|
||||
}
|
||||
|
||||
function mouseout(selection, g) {
|
||||
const tooltip = selection.select('.chart-tooltip-wrap');
|
||||
tooltip.style('display', 'none');
|
||||
}
|
||||
|
||||
function drawLegend(selection, colorScale) {
|
||||
const legendHeight = 10;
|
||||
const legendPadding = 10;
|
||||
|
||||
let svg = selection.select('.legend-svg');
|
||||
if (svg.empty()) {
|
||||
svg = selection.append('svg')
|
||||
.attr('class', 'legend-svg')
|
||||
.attr('width', width + margin.left + margin.right) // Adjust the width of the SVG
|
||||
.attr('height', legendHeight + 2 * legendPadding);
|
||||
}
|
||||
|
||||
let legend = svg.select('.legend');
|
||||
if (legend.empty()) {
|
||||
legend = svg.append('g')
|
||||
.attr('class', 'legend')
|
||||
.attr('transform', `translate(${margin.left}, ${legendPadding})`); // Adjust the position of the legend
|
||||
}
|
||||
|
||||
const legendData = colorScale.quantiles();
|
||||
|
||||
const legendRects = legend.selectAll('rect')
|
||||
.data(legendData);
|
||||
|
||||
legendRects.enter().append('rect')
|
||||
.merge(legendRects)
|
||||
.attr('x', (d, i) => (i * (innerW())) / legendData.length) // Adjust the x attribute
|
||||
.attr('y', 0)
|
||||
.attr('width', (innerW()) / legendData.length) // Adjust the width of the rectangles
|
||||
.attr('height', legendHeight)
|
||||
.style('fill', d => colorScale(d));
|
||||
|
||||
legendRects.exit().remove();
|
||||
|
||||
const legendTexts = legend.selectAll('text')
|
||||
.data(legendData);
|
||||
|
||||
legendTexts.enter().append('text')
|
||||
.merge(legendTexts)
|
||||
.attr('x', (d, i) => (i * (innerW())) / legendData.length) // Adjust the x attribute
|
||||
.attr('y', legendHeight + legendPadding)
|
||||
.text(d => Math.round(d))
|
||||
.style('font-size', '10px')
|
||||
.attr('text-anchor', 'middle')
|
||||
.text(d => metric === 'bytes' ? GoAccess.Util.fmtValue(d, 'bytes') : d3.format(',')(d));
|
||||
|
||||
legendTexts.exit().remove();
|
||||
}
|
||||
|
||||
function updateMap(selection, svg, data, countries, countryNameToGeoJson) {
|
||||
data = mapData(data);
|
||||
path = d3.geoPath().projection(projection);
|
||||
|
||||
const colorScale = d3.scaleQuantile().domain(data.map(d => d[metric])).range(['#ffffccc9', '#c2e699', '#a1dab4c9', '#41b6c4c9', '#2c7fb8c9']);
|
||||
if (data.length)
|
||||
drawLegend(selection, colorScale);
|
||||
|
||||
// Create a mapping from country name to data
|
||||
const dataByName = {};
|
||||
data.forEach(d => {
|
||||
const k = d.data.split(' ')[0];
|
||||
dataByName[k] = d;
|
||||
});
|
||||
|
||||
let country = svg.select('g').selectAll('.country')
|
||||
.data(countries);
|
||||
|
||||
let countryEnter = country.enter().append('path')
|
||||
.attr('class', 'country')
|
||||
.attr('d', path)
|
||||
.attr('opacity', 0); // set initial opacity to 0 for entering elements
|
||||
|
||||
country = countryEnter.merge(country)
|
||||
.on('mouseover', function(event, d) {
|
||||
const countryData = dataByName[d.id];
|
||||
if (countryData)
|
||||
mouseover(event, selection, countryData);
|
||||
})
|
||||
.on('mouseout', function(d) {
|
||||
mouseout(selection);
|
||||
});
|
||||
|
||||
country.transition().duration(500)
|
||||
.style('fill', function(d) {
|
||||
const countryData = dataByName[d.id];
|
||||
return countryData ? colorScale(countryData[metric]) : '#cccccc54';
|
||||
})
|
||||
.attr('opacity', 1); // animate opacity to 1
|
||||
|
||||
country.exit()
|
||||
.transition().duration(500)
|
||||
.attr('opacity', 0) // animate opacity to 0
|
||||
.remove();
|
||||
|
||||
}
|
||||
|
||||
function setBounds(projection, maxLat) {
|
||||
const [yaw] = projection.rotate();
|
||||
const xymax = projection([-yaw + 180 - 1e-6, -maxLat]); // Top left corner
|
||||
const xymin = projection([-yaw - 180 + 1e-6, maxLat]); // Bottom right corner
|
||||
return [xymin, xymax];
|
||||
}
|
||||
|
||||
function zoomed(event, projection, path, scaleExtent, g) {
|
||||
const newX = event.transform.x % width;
|
||||
const newY = event.transform.y;
|
||||
const scale = event.transform.k;
|
||||
|
||||
if (scale != slast) {
|
||||
// Adjust the scale of the projection based on the zoom level
|
||||
projection.scale(scale * (innerW() / (2 * Math.PI)));
|
||||
} else {
|
||||
// Calculate the new longitude based on the x-coordinate
|
||||
let [longitude] = projection.rotate();
|
||||
|
||||
// Use the X translation to rotate, based on the current scale
|
||||
longitude += 360 * ((newX - tlast[0]) / width) * (scaleExtent[0] / scale);
|
||||
projection.rotate([longitude, 0, 0]);
|
||||
|
||||
// Calculate the new latitude based on the y-coordinate
|
||||
const b = setBounds(projection, maxLat);
|
||||
let dy = newY - tlast[1];
|
||||
if (b[0][1] + dy > 0)
|
||||
dy = -b[0][1];
|
||||
else if (b[1][1] + dy < height)
|
||||
dy = height - b[1][1];
|
||||
projection.translate([projection.translate()[0], projection.translate()[1] + dy]);
|
||||
}
|
||||
|
||||
// Redraw paths with the updated projection
|
||||
g.selectAll('path').attr('d', path);
|
||||
|
||||
// Save last values
|
||||
slast = scale;
|
||||
tlast = [newX, newY];
|
||||
}
|
||||
|
||||
function createSVG(selection) {
|
||||
const svg = d3.select(selection)
|
||||
.append('svg')
|
||||
.attr('class', 'map')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.lower();
|
||||
|
||||
const g = svg.append('g')
|
||||
.attr('transform', `translate(${margin.left}, 0)`)
|
||||
.attr('transform-origin', '50% 50%');
|
||||
|
||||
projection = d3.geoMercator()
|
||||
.center([0, 15])
|
||||
.scale([(innerW()) / (2 * Math.PI)])
|
||||
.translate([(innerW()) / 2, height / 1.5]);
|
||||
path = d3.geoPath().projection(projection);
|
||||
|
||||
// Calculate scale extent and initial scale
|
||||
const bounds = setBounds(projection, maxLat);
|
||||
const s = width / (bounds[1][0] - bounds[0][0]);
|
||||
// The minimum and maximum zoom scales
|
||||
const scaleExtent = [s, 5 * s];
|
||||
|
||||
const zoom = d3.zoom()
|
||||
.scaleExtent(scaleExtent)
|
||||
.on('zoom', event => {
|
||||
zoomed(event, projection, path, scaleExtent, g);
|
||||
});
|
||||
svg.call(zoom);
|
||||
|
||||
return svg;
|
||||
}
|
||||
|
||||
function chart(selection) {
|
||||
selection.each(function(data) {
|
||||
const worldData = window.countries110m;
|
||||
const countries = topojson.feature(worldData, worldData.objects.countries).features;
|
||||
|
||||
const countryNameToGeoJson = {};
|
||||
countries.forEach(country => {
|
||||
countryNameToGeoJson[country.properties.name] = country;
|
||||
});
|
||||
|
||||
let svg = d3.select(this).select('svg.map');
|
||||
// if the SVG element doesn't exist, create it
|
||||
if (svg.empty())
|
||||
svg = createSVG(this);
|
||||
|
||||
updateMap(selection, svg, data, countries, countryNameToGeoJson);
|
||||
});
|
||||
}
|
||||
|
||||
chart.metric = function(_) {
|
||||
if (!arguments.length) return metric;
|
||||
metric = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.opts = function (_) {
|
||||
if (!arguments.length) return opts;
|
||||
opts = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.width = function(_) {
|
||||
if (!arguments.length) return width;
|
||||
width = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.height = function(_) {
|
||||
if (!arguments.length) return height;
|
||||
height = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
function AreaChart(dualYaxis) {
|
||||
var opts = {};
|
||||
var margin = {
|
||||
|
2
resources/js/topojson.v3.min.js
vendored
Normal file
2
resources/js/topojson.v3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -99,6 +99,9 @@
|
||||
<li class="dropdown-header">{{ labels.type }}</li>
|
||||
<li><a href="javascript:void(0);" data-panel="{{id}}" data-chart-type="area-spline"><i class="fa fa-circle{{^area-spline}}-o{{/area-spline}}"></i> {{labels.area_spline}}</a></li>
|
||||
<li><a href="javascript:void(0);" data-panel="{{id}}" data-chart-type="bar"><i class="fa fa-circle{{^bar}}-o{{/bar}}"></i> {{labels.bar}}</a></li>
|
||||
{{#hasMap}}
|
||||
<li><a href="javascript:void(0);" data-panel="{{id}}" data-chart-type="wmap"><i class="fa fa-circle{{^wmap}}-o{{/wmap}}"></i> {{labels.wmap}}</a></li>
|
||||
{{/hasMap}}
|
||||
<li class="dropdown-header">{{labels.plot_metric}}</li>
|
||||
{{#plot}}
|
||||
<li><a href="javascript:void(0);" data-panel="{{id}}" data-plot="{{className}}" class="panel-plot-{{className}}"><i class="fa fa-circle{{^selected}}-o{{/selected}}"></i> {{label}}</a></li>
|
||||
|
@ -392,6 +392,8 @@
|
||||
N_("Area Spline")
|
||||
#define HTML_REPORT_PANEL_BAR \
|
||||
N_("Bar")
|
||||
#define HTML_REPORT_PANEL_WMAP \
|
||||
N_("World Map")
|
||||
#define HTML_REPORT_PANEL_PLOT_METRIC \
|
||||
N_("Plot Metric")
|
||||
#define HTML_REPORT_PANEL_TABLE_COLS \
|
||||
|
50
src/output.c
50
src/output.c
@ -53,7 +53,9 @@
|
||||
#include "facss.h"
|
||||
#include "appcss.h"
|
||||
#include "d3js.h"
|
||||
#include "topojsonjs.h"
|
||||
#include "hoganjs.h"
|
||||
#include "countries110m.h"
|
||||
#include "chartsjs.h"
|
||||
#include "appjs.h"
|
||||
|
||||
@ -67,81 +69,81 @@ static void print_host_metrics (FILE * fp, const GHTML * def, int sp);
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
static GHTML htmldef[] = {
|
||||
{VISITORS, 1, print_metrics, {
|
||||
{VISITORS, 1, 0, print_metrics, {
|
||||
{CHART_AREASPLINE, hits_visitors_plot, 1, 1, NULL, NULL} ,
|
||||
{CHART_AREASPLINE, hits_bw_plot, 1, 1, NULL, NULL} ,
|
||||
}},
|
||||
{REQUESTS, 1, print_metrics, {
|
||||
{REQUESTS, 1, 0, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_req_plot, 0, 0, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_req_plot, 0, 0, NULL, NULL},
|
||||
}},
|
||||
{REQUESTS_STATIC, 1, print_metrics, {
|
||||
{REQUESTS_STATIC, 1, 0, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_req_plot, 0, 0, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_req_plot, 0, 0, NULL, NULL},
|
||||
}},
|
||||
{NOT_FOUND, 1, print_metrics, {
|
||||
{NOT_FOUND, 1, 0, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_req_plot, 0, 0, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_req_plot, 0, 0, NULL, NULL},
|
||||
}},
|
||||
{HOSTS, 1, print_host_metrics, {
|
||||
{HOSTS, 1, 0, print_host_metrics, {
|
||||
{CHART_VBAR, hits_visitors_plot, 0, 0, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_plot, 0, 0, NULL, NULL},
|
||||
}},
|
||||
{OS, 1, print_metrics, {
|
||||
{OS, 1, 0, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_plot, 0, 1, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_plot, 0, 1, NULL, NULL},
|
||||
}},
|
||||
{BROWSERS, 1, print_metrics, {
|
||||
{BROWSERS, 1, 0, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_plot, 0, 1, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_plot, 0, 1, NULL, NULL},
|
||||
}},
|
||||
{VISIT_TIMES, 1, print_metrics, {
|
||||
{VISIT_TIMES, 1, 0, print_metrics, {
|
||||
{CHART_AREASPLINE, hits_visitors_plot, 0, 1, NULL, NULL},
|
||||
{CHART_AREASPLINE, hits_bw_plot, 0, 1, NULL, NULL},
|
||||
}},
|
||||
{VIRTUAL_HOSTS, 1, print_metrics, {
|
||||
{VIRTUAL_HOSTS, 1, 0, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_plot, 0, 0, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_plot, 0, 0, NULL, NULL},
|
||||
}},
|
||||
{REFERRERS, 1, print_metrics, {
|
||||
{REFERRERS, 1, 0, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_plot, 0, 0, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_plot, 0, 0, NULL, NULL},
|
||||
}},
|
||||
{REFERRING_SITES, 1, print_metrics, {
|
||||
{REFERRING_SITES, 1, 0, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_plot, 0, 0, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_plot, 0, 0, NULL, NULL},
|
||||
}},
|
||||
{KEYPHRASES, 1, print_metrics, {
|
||||
{KEYPHRASES, 1, 0, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_plot, 0, 0, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_plot, 0, 0, NULL, NULL},
|
||||
}},
|
||||
{STATUS_CODES, 1, print_metrics, {
|
||||
{STATUS_CODES, 1, 0, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_plot, 0, 1, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_plot, 0, 1, NULL, NULL},
|
||||
}},
|
||||
{REMOTE_USER, 1, print_metrics, {
|
||||
{REMOTE_USER, 1, 0, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_plot, 0, 0, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_plot, 0, 0, NULL, NULL},
|
||||
}},
|
||||
{CACHE_STATUS, 1, print_metrics, {
|
||||
{CACHE_STATUS, 1, 0, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_plot, 0, 0, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_plot, 0, 0, NULL, NULL},
|
||||
}},
|
||||
#ifdef HAVE_GEOLOCATION
|
||||
{GEO_LOCATION, 1, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_plot, 0, 1, NULL, NULL},
|
||||
{GEO_LOCATION, 1, 1, print_metrics, {
|
||||
{CHART_WMAP, hits_visitors_plot, 0, 1, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_plot, 0, 1, NULL, NULL},
|
||||
}},
|
||||
{ASN, 1, print_metrics, {
|
||||
{ASN, 1, 0, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_plot, 0, 0, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_plot, 0, 0, NULL, NULL},
|
||||
}},
|
||||
#endif
|
||||
{MIME_TYPE, 1, print_metrics, {
|
||||
{MIME_TYPE, 1, 0, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_plot, 0, 1, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_plot, 0, 1, NULL, NULL},
|
||||
}},
|
||||
{TLS_TYPE, 1, print_metrics, {
|
||||
{TLS_TYPE, 1, 0, print_metrics, {
|
||||
{CHART_VBAR, hits_visitors_plot, 0, 1, NULL, NULL},
|
||||
{CHART_VBAR, hits_bw_plot, 0, 1, NULL, NULL},
|
||||
}},
|
||||
@ -158,7 +160,7 @@ static int external_assets = 0;
|
||||
* On success, the chart type string is returned. */
|
||||
static const char *
|
||||
chart2str (GChartType type) {
|
||||
static const char *strings[] = { "null", "bar", "area-spline" };
|
||||
static const char *strings[] = { "null", "bar", "area-spline", "wmap" };
|
||||
return strings[type];
|
||||
}
|
||||
|
||||
@ -332,11 +334,13 @@ print_html_footer (FILE * fp, FILE *fjs)
|
||||
fprintf (fjs, "%.*s", hogan_js_length, hogan_js);
|
||||
fprintf (fjs, "%.*s", app_js_length, app_js);
|
||||
fprintf (fjs, "%.*s", charts_js_length, charts_js);
|
||||
fprintf (fjs, "%.*s", topojson_js_length, topojson_js);
|
||||
} else {
|
||||
fprintf (fp, "<script>%.*s</script>", d3_js_length, d3_js);
|
||||
fprintf (fp, "<script>%.*s</script>", hogan_js_length, hogan_js);
|
||||
fprintf (fp, "<script>%.*s</script>", app_js_length, app_js);
|
||||
fprintf (fp, "<script>%.*s</script>", charts_js_length, charts_js);
|
||||
fprintf (fp, "<script>%.*s</script>", topojson_js_length, topojson_js);
|
||||
}
|
||||
|
||||
/* load custom JS file, if any */
|
||||
@ -1088,6 +1092,7 @@ print_panel_def_meta (FILE *fp, const GHTML *def, int sp) {
|
||||
|
||||
fpskeysval (fp, "id", id, isp, 0);
|
||||
fpskeyival (fp, "table", def->table, isp, 0);
|
||||
fpskeyival (fp, "hasMap", def->has_map, isp, 0);
|
||||
|
||||
print_def_sort (fp, def, isp);
|
||||
print_def_plot (fp, def, isp);
|
||||
@ -1176,6 +1181,7 @@ print_json_i18n_def (FILE *fp) {
|
||||
{"type" , HTML_REPORT_PANEL_TYPE} ,
|
||||
{"area_spline" , HTML_REPORT_PANEL_AREA_SPLINE} ,
|
||||
{"bar" , HTML_REPORT_PANEL_BAR} ,
|
||||
{"wmap" , HTML_REPORT_PANEL_WMAP} ,
|
||||
{"plot_metric" , HTML_REPORT_PANEL_PLOT_METRIC} ,
|
||||
{"table_columns" , HTML_REPORT_PANEL_TABLE_COLS} ,
|
||||
{"thead" , T_HEAD} ,
|
||||
@ -1223,6 +1229,8 @@ print_json_defs (FILE *fp) {
|
||||
print_json_i18n_def (fp);
|
||||
fprintf (fp, ";");
|
||||
|
||||
fprintf (fp, "var html_prefs=%s;", conf.html_prefs ? conf.html_prefs : "{}");
|
||||
fprintf (fp, "var countries110m=%.*s;", countries_json_length, countries_json);
|
||||
fprintf (fp, "var html_prefs=%s;", conf.html_prefs ? conf.html_prefs : "{}");
|
||||
fprintf (fp, "var user_interface=");
|
||||
fpopen_obj (fp, 0);
|
||||
|
@ -46,6 +46,7 @@ typedef enum GChartType_ {
|
||||
CHART_NONE,
|
||||
CHART_VBAR,
|
||||
CHART_AREASPLINE,
|
||||
CHART_WMAP,
|
||||
} GChartType;
|
||||
|
||||
/* Chart axis structure */
|
||||
@ -74,6 +75,7 @@ typedef struct GHTMLPlot_ {
|
||||
typedef struct GHTML_ {
|
||||
GModule module;
|
||||
int8_t table;
|
||||
int8_t has_map;
|
||||
void (*metrics) (FILE * fp, const struct GHTML_ * def, int sp);
|
||||
GHTMLPlot chart[MAX_PLOTS];
|
||||
} GHTML;
|
||||
|
Loading…
Reference in New Issue
Block a user