Files
gtav-src/tools_ng/web/release/stats/js/pie+barchart-profile.js
T
2025-09-29 00:52:08 +02:00

784 lines
24 KiB
JavaScript
Executable File

var singlePrefix = "SP",
multiPrefix = "MP",
singleLength = 3,
multiLength = 5;
var barHeight = 15;
/*
var pieChartLegend = "Pie Chart :: ";
barChartLegend = "Bar Chart :: ";
*/
var pieChartId = "piechart",
barChartId = "barchart";
var weaponNamesMap = {};
var rangeWarningFlag = false,
rangeWarningText = "<span style='color:#AB6F1B'>WARNING!</span> There are values returned outside "
+ "the expected range and they are not shown on the graph."
+ " If you think that there are missing values please "
+ "<a href='mailto:*Tools@rockstarnorth.com?subject=Stats Value Ranges' "
+ "title='Contact Tools' target='_blank'>Contact Tools</a>";
var commasFixed2 = function(d) {return d3.format(",")(d.toFixed(2)); };
var currentStat = (getUrlVars()["stat"]) ? getUrlVars()["stat"] : 0;
var currentFilename = config.currentFilename + "?stat=" + currentStat;
var marginLeft = 135;
//var queryAsyncUrl = config.restHost + config.profileStatPath + config.queryAsyncPath;
// Object used for local storage of the user selections
//var pageData = {
// availableChart: 0,
//}
function initPage() {
// function from generic.js, variable from config file
initHeaderAndFilters(headerAndFilters);
$("#content")
.append(
$("<div>")
.attr("id", "friendlier-names-field")
.append(
$("<input>")
.attr("id", "friendlier-names")
.attr("type", "checkbox")
.attr("disabled", true)
)
.append(
$("<label>")
.attr("for", "friendlier-names")
.attr("title", "Check to Switch to Friedlier Names")
.text("Friendlier Names")
)
);
$("#content-body")
.append(
$("<div>").attr("id", pieChartId)
)
.append(
$("<div>").attr("id", barChartId)
);
initSvg(pieChartId);
initSvg(barChartId);
/*
// Disable the start date
$("#date-from")
.attr("disabled", true)
.addClass("disabled");
// Disable the builds
$("#builds")
.attr("disabled", true)
.addClass("disabled");
*/
//var localData = retrieveLocalObject(config.currentFilename);
//pageData = (localData) ? localData : pageData;
var currentA = $("#navigation a[href$='" + currentFilename + "']");
currentA.addClass("active");
var currentUl = currentA.parent().parent();
currentUl.prev().addClass("active");
currentUl.prev().parent().addClass("expanded");
var topUl = currentUl.parent().parent();
if (topUl.css("display") == "none") {
topUl.prev().addClass("active");
topUl.prev().parent().addClass("expanded");
topUl.slideToggle();
}
updateTitle();
/*
var currentUl = $("#navigation a.active[href$='" + currentFilename + "']:first").next();
// Populate the chart drop down menu from the config file variables
$.each(reportOptions.availableCharts, function(i, availableChart) {
currentUl.append(
$("<li />").append(
$("<a />")
//.addClass(function() {if (pageData && pageData.availableChart == i) return "active"; })
.addClass(function() {return (i==0) ? "active" : ""; })
.text(availableChart.name)
.attr("href", i)
.attr("title", availableChart.name)
.click(function(e) {
e.preventDefault();
$(this).parent().parent().find("a").removeClass("active");
$(this).addClass("active");
//pageData.availableChart = parseInt($(this).attr("href"));
//storeLocalObject(config.currentFilename, pageData);
generateCharts();
})
)
);
});
*/
if (reportOptions.availableCharts.length > 1)
currentUl.slideToggle();
//$("#nav-ul a.active").next().slideToggle();
//if (reportOptions.availableCharts[parseInt(currentUl.find("a.active").attr("href"))].isWeaponStat) {
if (reportOptions.availableCharts[parseInt(currentStat)].isWeaponStat) {
populateWeaponNamesMap();
marginLeft = 180;
$("#friendlier-names").attr("disabled", false);
$("#friendlier-names").prop("checked", true);
$("#friendlier-names").click(function() {
generateCharts();
});
}
// Post header filter addition
$("#filter").click(function() {
generateCharts();
});
//updateChartHeight();
generateCharts();
}
/*
function updateChartHeight() {
// Adjust the height of the content automatically
$("#content").height($(window).height() - $("#header").height() - 40); // 20 is the padding top/bottom
}
*/
function generateCharts() {
//var selectedChart = availableCharts[parseInt($("select#available-charts-options").val())];
//var selectedChart = availableCharts[parseInt($("#nav-ul a.active").next().find("a.active").attr("href"))];
//var currentUl = $("#navigation a.active[href$='" + config.currentFilename + "']:first").next();
//var selectedChart = reportOptions.availableCharts[parseInt(currentUl.find("a.active").attr("href"))];
selectedChart = reportOptions.availableCharts[currentStat];
//toggleGameTypeOptions(selectedChart);
var gameTypes = [];
if (!$("select#game-types").val() && !selectedChart.singleVsMulti)
$("select#game-types option").each(function() {
gameTypes.push($(this).val());
});
else
gameTypes = $("select#game-types").val();
//console.log(gameTypes);
var character = ($("#character").val()) ? $("#character").val() : null;
if (selectedChart.hideStartDate){
$("#date-from").addClass("hidden");
$("#date-from").parent().addClass("hidden");
}
else {
$("#date-from").removeClass("hidden");
$("#date-from").parent().removeClass("hidden");
}
//if (selectedChart.isWeaponStat)
// selectedChart.types = Object.keys(weaponNamesMap).sort();
if (!selectedChart.divided) { // In case of requesting Combined or Separate Profile Stats
var pValuesArray = [];
// sort the types alphabetically
// selectedChart.types.sort()
$.each(selectedChart.types, function(i, selectedChartType) {
var finaleStatNames;
finalStatNames = constructStatNames(selectedChart, selectedChartType, gameTypes, character);
// get the social club header filtering parameter values, headerAndFilters.headerType comes from local conf
var pValues = config.headerOptions[headerAndFilters.headerType].getParamValues();
if (!pValues)
return;
// Add the extra Profile stats param values
pValues.Pairs["StatNames"] = finalStatNames.join(",");
pValues.Pairs["BucketSize"] = selectedChart.bucketSize;
pValuesArray.push(pValues);
}); //end of $.each() loop
if (pValuesArray.length == 0)
return;
var req = new ReportRequest(null, "json", null, config.restHost + config.reportsQueryAsync);
var endpointObject = {
restUrl: config.restHost + reportOptions.restEndpoint,
restAsyncUrl: config.restHost + reportOptions.restEndpointAsync + pValuesArray[0].ForceUrlSuffix,
pValues: pValuesArray,
}
if (!pValuesArray[0].Pairs.hasOwnProperty("StartDate")) {
$("#date-from").addClass("hidden");
$("#date-from").parent().addClass("hidden");
}
req.sendMultipleAsyncRequest([endpointObject], handleCombinedStatRespone);
//queryMultipleAsyncProcesses(queryAsyncUrl, monitoredProcesses, handleCombinedStatRespone);
} // end of if (!divided)
else {
// Code to execute when the divided flag is set, requests the fraction of two profile stats
var combinedStatNamesA = constructStatNames(selectedChart, selectedChart.types[0], gameTypes, character);
var combinedStatNamesB = constructStatNames(selectedChart, selectedChart.types[1], gameTypes, character);
// get the social club header filtering parameter values, headerAndFilters.headerType comes from local conf
var pValues = config.headerOptions[headerAndFilters.headerType].getParamValues();
if (!pValues)
return;
// Add the extra Profile stats param values
pValues.Pairs["NumeratorStatNames"] = combinedStatNamesA.join(",");
pValues.Pairs["DenominatorStatNames"] = combinedStatNamesB.join(",");
pValues.Pairs["BucketSize"] = selectedChart.bucketSize;
var req = new ReportRequest(config.restHost + config.profileStatsDividedDiff,
"json",
config.restHost + config.profileStatsDividedDiffAsync + pValues.ForceUrlSuffix,
config.restHost + config.reportsQueryAsync);
req.sendSingleAsyncRequest(pValues, handleDividedStatResponse);
}
}
function handleCombinedStatRespone(monitoredProcesses) {
//var selectedChart = availableCharts[parseInt($("select#available-charts-options").val())];
//var selectedChart = availableCharts[parseInt($("#nav-ul a.active").next().find("a.active").attr("href"))];
//var currentUl = $("#navigation a.active[href$='" + config.currentFilename + "']:first").next();
//var selectedChart = reportOptions.availableCharts[parseInt(currentUl.find("a.active").attr("href"))];
selectedChart = reportOptions.availableCharts[currentStat];
var chartData = [];
//console.log(monitoredProcesses[0]);
$.each(selectedChart.types, function(i, selectedChartType) {
//if (response == config.asyncRequestFailTxt)
// return;
if (selectedChart.types.length != 1) {
var response = monitoredProcesses[0][i].response[0];
var total;
if (selectedChart.convertToKm)
total = Number(response.Total)/1000.0; // convert from metres to kilometres
else if (selectedChart.convertToHours)
total = Number(response.Total)/config.anHourInMillisecs; // convert from millisecs to hours
else
total = Number(response.Total);
if (selectedChart.isAverage) {
total = (Number(response.YValue)) ? (total / Number(response.YValue)) : 0;
//console.log(response.YValue);
}
var name = selectedChartType;
/*
if (selectedChart.singleVsMulti) {
if (selectedChartType.charAt(0) == singlePrefix.charAt(0))
name = $("select#game-types option:eq(0)").val();
else if (selectedChartType.charAt(0) == multiPrefix.charAt(0))
name = $("select#game-types option:eq(1)").val();
}
*/
if (selectedChart.isWeaponStat) {
// If there are matched rest service object with game and friendly names use that
if (weaponNamesMap.hasOwnProperty(selectedChartType)) {
name = selectedChart.getName(weaponNamesMap[selectedChartType]);
}
else // Otherwise revert to the profile stat name
name = selectedChartType;
}
else if (selectedChart.altNames) {
name = selectedChart.altNames[i];
}
chartData.push({
Name: name,
Value: total,
});
} // end of (if length > 1)
else {
var response = monitoredProcesses[0][i].response;
$.each(response, function(i, item) {
var name;
var value = Number(item.YValue);
if (selectedChart.convertToHours) { // Total Playing Time chart
// Convert the label names from milliseconds to hours
/*
name = item.Bucket.split("-")
.map(function(d) {return (Number(d) / config.anHourInMillisecs)})
.join("-")
+ (" (hours)");
*/
name = [Number(item.BucketMin) / config.anHourInMillisecs, Number(item.BucketMax) / config.anHourInMillisecs]
.join("-")
+ (" (hours)");
}
else if (selectedChart.addPercentageLabel) { // Percentage Chart
/*
var name = item.Bucket;
if (selectedChart.multiplyToPercentage) {
name = name.split("-")
.map(function(d) {
return (Number(d) * selectedChart.multiplyToPercentage)
})
.join("-");
}
// Convert the label names from decimal to percentages
var lowerRange = name.split("-")[0];
name = ((lowerRange == 100) ? lowerRange : name) + " %";
*/
if (selectedChart.multiplyToPercentage) {
name = [Number(item.BucketMin) * selectedChart.multiplyToPercentage,
Number(item.BucketMax) * selectedChart.multiplyToPercentage]
.join("-");
}
name = ((Number(item.BucketMin) * selectedChart.multiplyToPercentage == 100)
? Number(item.BucketMin) * selectedChart.multiplyToPercentage
: name)
+ " %";
}
else if (selectedChart.altNames) {
// replace the bucket names with the given alt ones
// The altNames id should come from the buckets lower range
var altId = Number(item.BucketMin);
if ((altId !== "undefined") && (selectedChart.altNames.length > altId))
name = selectedChart.altNames[altId];
else {
rangeWarningFlag = true;
return;
}
//name = ((altId !== "undefined") && (selectedChart.altNames.length > altId)) ?
// selectedChart.altNames[altId] : item.Bucket;
}
else if (selectedChart.showOnlyLowerRange) {
var lowerValue = Number(item.BucketMin);
// If the results have to be in specific value ranges
if (selectedChart.validRange
&& (lowerValue < selectedChart.validRange[0] || lowerValue > selectedChart.validRange[1])) {
rangeWarningFlag = true;
return;
}
else
name = lowerValue;
}
else
name = item.Bucket;
chartData.push({
Name: name,
Value: value
});
});
}
}); // end of each
//hideProgressbar();
drawCharts(chartData, selectedChart);
}
function handleDividedStatResponse(response) {
//var selectedChart = availableCharts[parseInt($("select#available-charts-options").val())];
//var selectedChart = availableCharts[parseInt($("#nav-ul a.active").next().find("a.active").attr("href"))];
//var currentUl = $("#navigation a.active[href$='" + config.currentFilename + "']:first").next();
//var selectedChart = reportOptions.availableCharts[parseInt(currentUl.find("a.active").attr("href"))];
selectedChart = reportOptions.availableCharts[currentStat];
var chartData = [];
//console.log(response);
if (response != config.asyncRequestFailTxt) {
$.each(response, function(i, item) {
var value = Number(item.YValue);
var name;
if (selectedChart.perHour) { // Metric Per Hour Chart
// Convert the label names from milliseconds to hours
/*
var bucketNames = item.Bucket.split(/-/);
if (bucketNames.length > 2) // In case of more than one "-", the numbers are very small decimals (i.e 1.2E-10)
bucketNames = item.Bucket.split(/-(?=\d\D)/); // so match only the "-" which is followed by a '\d\D' (i.e '2.')
//console.log(item.Bucket);
//console.log(bucketNames);
name = bucketNames
.map(function(d) {
return (Number(d) * config.anHourInMillisecs).toFixed(1);
})
.join(" - ");
*/
name = [(Number(item.BucketMin) * config.anHourInMillisecs).toFixed(1),
(Number(item.BucketMax) * config.anHourInMillisecs).toFixed(1)]
.join(" - ");
}
else
name = item.Bucket;
chartData.push({
Name: name,
Value: value
});
});
}
//hideProgressbar();
drawCharts(chartData, selectedChart);
}
function drawCharts(chartData, selectedChart) {
//console.log(chartData);
var chartValuesSum = chartData.reduce(function(accumulator, current) {
return accumulator + ((current.Value) ? current.Value : 0)}, 0
);
if (chartValuesSum == 0) {
Sexy.alert(config.noDataText);
cleanGraph(pieChartId);
cleanGraph(barChartId);
return;
}
$("#content-description").empty();
if (rangeWarningFlag) {
$("#content-description").html(rangeWarningText);
rangeWarningFlag = false;
}
else
$("#content-description").html(selectedChart.description);
//updateChartHeight();
//var datum = [{values: chartData, color: config.chartColour1}];
chartData.sort(function (a, b) {return (a.Value >= b.Value) ? -1 : 1} )
var datum = [{values: chartData}];
drawPiechart(datum, selectedChart);
} // end of generateChart()
function drawPiechart(datum, selectedChart) {
nv.addGraph({
generate: function() {
var chart = nv.models.pieChart()
.x(function(d) { return d.Name; })
.y(function(d) { return d.Value; })
.showLegend(false)
.showLabels(true)
.donut(true)
.donutLabelsOutside(true);
chart.tooltipContent(function(key, y, e, graph) {
var sum = d3.sum(graph.container.__data__[0].values, function(d) {return d.Value});
// Use e.value instead of y, y is a formated string and parsing to number fails
var percentage = ((e.value/sum)*100).toFixed(2);
// need to get that realtime, otherwise the pieachart tooltip vars are not being updated
//var thisChart =
// availableCharts[parseInt($("#nav-ul a.active").next().find("a.active").attr("href"))];
//var currentUl = $("#navigation a.active[href$='" + config.currentFilename + "']:first").next();
//var thisChart = reportOptions.availableCharts[parseInt(currentUl.find("a.active").attr("href"))];
var thisChart = reportOptions.availableCharts[currentStat];
/*
return "<h3>" + key + "</h3>"
+ "<p>" + y + " " + thisChart.units + " - " + percentage + "%</p>";
*/
var tooltipValue, tooltipSum;
if (thisChart.units == "hours") {
tooltipValue = formatSecsWithDays(e.value*config.anHourInSecs);
tooltipSum = formatSecsWithDays(sum*config.anHourInSecs);
}
else {
tooltipValue = (commasFixed(e.value, 4) + " " + thisChart.units);
tooltipSum = (commasFixed2(sum, 4) + " " + thisChart.units);
}
return "<h3>" + key + "</h3>"
+ "<p>" + tooltipValue
+ " - " + percentage + "%" + "<br />"
+ " (of " + tooltipSum + ")" + "</p>";
});
d3.select("#" + pieChartId + " svg")
.datum(datum)
.transition()
.duration(config.transitionDuration)
.call(chart);
nv.utils.windowResize(chart.update);
//nv.utils.windowResize(updateChartHeight);
//nv.utils.windowResize(function() { decorateKeys(pieChartId); });
nv.utils.windowResize(function() {
drawDonutLabel(pieChartId, selectedChart.name);
decorateKeys(pieChartId);
});
return chart;
},
callback: function() {
drawDonutLabel(pieChartId, selectedChart.name);
decorateKeys(pieChartId);
d3.select("#" + pieChartId + " svg").selectAll(".nv-slice").each(function(d, i) {
//colours.push(d3.select(this).attr("fill"));
datum[0].values[i]["Colour"] = d3.select(this).attr("fill");
});
drawBarchart(datum, selectedChart)
},
});
//$("#piechart legend").text(pieChartLegend + selectedChart.name);
}
function drawBarchart(datum, selectedChart) {
var graphHeight = (barHeight * datum[0].values.length); // pixels
var containerHeight = $("#content-body").height();
if (graphHeight < containerHeight) {
$("#" + barChartId).css("height", containerHeight);
$("#" + barChartId + " svg").css("height", containerHeight);
}
else {
$("#" + barChartId).css("height", graphHeight);
$("#" + barChartId + " svg").css("height", graphHeight);
}
if (selectedChart.orderDescBarchart)
datum[0].values.sort(function(a, b) { return (Number(a.Name) > Number(b.Name)) ? -1 : 1});
nv.addGraph({
generate: function() {
var chart = nv.models.multiBarHorizontalChart()
.x(function(d) { return d.Name; })
.y(function(d) { return d.Value; })
.margin({top: 20, right: 20, bottom: 50, left: marginLeft})
.showValues(true)
.tooltips(false)
.showControls(false)
.showLegend(false);
chart.yAxis
.axisLabel(selectedChart.label)
.tickFormat(d3.format(".02f"));
d3.select("#" + barChartId + " svg")
.datum(datum)
.transition()
.duration(config.transitionDuration)
.call(chart);
nv.utils.windowResize(chart.update);
//nv.utils.windowResize(updateChartHeight);
return chart;
},
callback: function() {
matchColours(datum, barChartId)
}
});
//$("#barchart legend").text(barChartLegend + selectedChart.name);
}
function matchColours(datum, barChartId) {
/*
var colours = [];
d3.select("#" + piechartID + " svg").selectAll(".nv-slice").each(function(d, i) {
colours.push(d3.select(this).attr("fill"));
//console.log(d3.select(this).attr("fill"));
});
*/
d3.select("#" + barChartId + " svg").selectAll(".nv-bar").each(function(d, i) {
d3.select(this).attr("fill", datum[0].values[i].Colour);
});
}
function drawDonutLabel(elementID, labelText) {
var svg = d3.select("#" + elementID + " svg");
svg.selectAll(".donut-label").remove();
svg.append("svg:text")
.attr("class", "donut-label")
.attr("x", $("#" + elementID).width() / 2)
.attr("y", $("#" + elementID).height() / 2)
.attr("text-anchor", "middle")
.attr("stroke", 5)
.text(labelText);
}
function decorateKeys(elementID) {
$(".nv-legend rect").remove();
var legend = d3.select("#" + elementID + " svg")
.select("g.nv-legend g");
if (!legend[0][0])
return;
var bbox = legend[0][0].getBBox();
var margin = 5;
legend.append("svg:rect")
.attr("x", bbox.x - margin)
.attr("y", bbox.y - margin)
.attr("width", bbox.width + 2*margin)
.attr("height", bbox.height + 2*margin)
.attr("rx", margin)
.attr("ry", margin)
.attr("pointer-events", "none");
}
function constructStatNames(selectedChart, selectedChartType, gameTypes, character) {
var singleStatNames = [],
multiStatNames = [],
finalStatNames = [];
if (character) {
var charStat;
if (character < singleLength) {
if (selectedChart.isWeaponStat)
charStat = singlePrefix + character + "_" + selectedChartType + selectedChart.requestSubString;
else
charStat = singlePrefix + character + selectedChart.requestSubString + selectedChartType;
}
else {
if (selectedChart.isWeaponStat)
charStat = multiPrefix + (character-singleLength) + "_" + selectedChartType + selectedChart.requestSubString;
else
charStat = multiPrefix + (character-singleLength) + selectedChart.requestSubString + selectedChartType;
}
finalStatNames.push(charStat);
}
else if (!selectedChart.singleMetric) {
for (var i=0; i<singleLength; i++) {
if (selectedChart.isWeaponStat) // Weapon profile stats have different name patterns
singleStatNames.push(singlePrefix + i + "_" + selectedChartType + selectedChart.requestSubString);
else
singleStatNames.push(singlePrefix + i + selectedChart.requestSubString + selectedChartType);
}
for (var i=0; i<multiLength; i++) {
if (selectedChart.isWeaponStat) // Weapon profile stats have different name patterns
multiStatNames.push(multiPrefix + i + "_" + selectedChartType + selectedChart.requestSubString);
else
multiStatNames.push(multiPrefix + i + selectedChart.requestSubString + selectedChartType);
}
$.each(gameTypes, function(i, gameType) {
if (gameType == config.gameTypes[0] && !selectedChart.multiOnly)
finalStatNames = finalStatNames.concat(singleStatNames);
if (gameType == config.gameTypes[1] && !selectedChart.singleOnly)
finalStatNames = finalStatNames.concat(multiStatNames);
});
}
else {
finalStatNames.push(selectedChartType);
}
return finalStatNames;
}
function toggleGameTypeOptions(selectedChart) {
/*
if (selectedChart.singleOnly || selectedChart.multiOnly || selectedChart.singleMetric) {
$("#game-types-field select")
.attr("disabled", true)
.addClass("disabled");
}
else {
$("#game-types-field select")
.attr("disabled", false)
.removeClass("disabled");
}
*/
}
function populateWeaponNamesMap() {
weaponNamesMap = getWeaponStatnames();
//var weaponNamePrefix = "WEAPON_";
/*
//block();
$.ajax({
url: config.restHost + config.weaponsAll,
type: "GET",
data: {},
dataType: "xml",
async: false,
success: function(xml, textStatus, jqXHR) {
$(xml).find("Weapon").each(function() {
var gameName = $(this).find("Name").text().toUpperCase(),
statName = $(this).find("StatName").text().toUpperCase(),
friendlyName = $(this).find("FriendlyName").text();
if (statName && !weaponNamesMap.hasOwnProperty(statName)) {
weaponNamesMap[statName] = {
Name: gameName.replace(weaponNamePrefix, ""),
//Name: gameName,
FriendlyName: friendlyName,
};
}
});
},
error: function (xhr, ajaxOptions, thrownError){
console.error(this.url + "\n" + ajaxOptions + " " + xhr.status + " " + thrownError );
},
complete: function() {
//unBlock();
}
});
*/
}
//End of convertXml group of functions