You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
560 lines
17 KiB
560 lines
17 KiB
let combined_charts = [];
|
|
let player_charts = [];
|
|
|
|
const x_scale = {
|
|
display: true,
|
|
type: "linear",
|
|
stepSize: 1000 * 60 * 60 * 24,
|
|
ticks: {
|
|
callback: (value, index, ticks) => {
|
|
let tickdate = new Date(value);
|
|
return `${tickdate.getDate()}.${month_names[tickdate.getMonth()]}`;
|
|
}
|
|
}
|
|
}
|
|
const zoom_plugin = {
|
|
zoom: {
|
|
wheel: {
|
|
enabled: true,
|
|
},
|
|
pinch: {
|
|
enabled: true,
|
|
},
|
|
mode: "x",
|
|
onZoomComplete: function({chart}) {
|
|
const show_point_cutoff = 1000 * 60 * 60 * 24 * 3
|
|
const diff = parseInt(chart.scales.x.max) - parseInt(chart.scales.x.min);
|
|
const show_points = diff < show_point_cutoff;
|
|
const hide_points = diff >= show_point_cutoff;
|
|
let update_chart = false;
|
|
chart.data.datasets.forEach(dataset => {
|
|
const points_shown = dataset.pointRadius > 0
|
|
if (show_points && !points_shown) {
|
|
dataset.pointRadius = 3;
|
|
update_chart = true;
|
|
}
|
|
if (hide_points && points_shown) {
|
|
dataset.pointRadius = 0;
|
|
update_chart = true;
|
|
}
|
|
})
|
|
if (update_chart) chart.update();
|
|
}
|
|
},
|
|
pan: {
|
|
enabled: true,
|
|
mode: "x",
|
|
},
|
|
}
|
|
|
|
let hovered = false;
|
|
const trigger_legend_hover = function (legendItem, chart) {
|
|
if (legendItem.hidden || hovered) {
|
|
return;
|
|
}
|
|
hovered = true;
|
|
chart.data.datasets.forEach((dataset, i) => {
|
|
dataset.backgroundColor = (legendItem.datasetIndex === i || dataset.backgroundColor.length === 9) ? dataset.backgroundColor : dataset.backgroundColor + '3D';
|
|
dataset.borderColor = (legendItem.datasetIndex === i || dataset.borderColor.length === 9) ? dataset.borderColor : dataset.borderColor + '3D';
|
|
dataset.borderWidth = legendItem.datasetIndex === i ? 4 : 3;
|
|
dataset.pointRadius = dataset.pointRadius===0 ? 0 : 2;
|
|
});
|
|
chart.update();
|
|
}
|
|
const trigger_legend_leave = function (chart) {
|
|
chart.data.datasets.forEach((dataset, i) => {
|
|
dataset.backgroundColor = dataset.backgroundColor.length === 9 ? dataset.backgroundColor.slice(0, -2) : dataset.backgroundColor;
|
|
dataset.borderColor = dataset.borderColor.length === 9 ? dataset.borderColor.slice(0, -2) : dataset.borderColor;
|
|
dataset.borderWidth = 3;
|
|
});
|
|
chart.update();
|
|
hovered = false;
|
|
}
|
|
const trigger_legend_click = function (item) {
|
|
combined_charts.forEach(c_chart => {
|
|
if (c_chart.isDatasetVisible(item.datasetIndex)) {
|
|
trigger_legend_leave(c_chart)
|
|
c_chart.hide(item.datasetIndex);
|
|
item.hidden = true;
|
|
} else {
|
|
c_chart.show(item.datasetIndex);
|
|
item.hidden = false;
|
|
trigger_legend_hover(item, c_chart)
|
|
}
|
|
})
|
|
}
|
|
// break hover state when mouseout fails to trigger
|
|
window.onmousemove = (e) => {
|
|
if (hovered && (e.target.closest("ul") === null || !e.target.closest("ul").classList.contains("legend-list"))) {
|
|
console.log("breaking hover")
|
|
combined_charts.forEach(chart => {
|
|
trigger_legend_leave(chart);
|
|
})
|
|
}
|
|
}
|
|
const get_create_LegendList = (chart, id) => {
|
|
let created = false;
|
|
const legendContainer = document.getElementById(id);
|
|
let listContainer = legendContainer.querySelector("ul");
|
|
if (!listContainer) {
|
|
created = true;
|
|
listContainer = document.createElement("ul");
|
|
listContainer.classList.add('legend-list');
|
|
legendContainer.appendChild(listContainer);
|
|
}
|
|
return {list: listContainer, created: created};
|
|
}
|
|
const htmlLegendPlugin = {
|
|
id: 'htmlLegend',
|
|
afterUpdate(chart, args, options) {
|
|
//console.log("update")
|
|
const legendList = get_create_LegendList(chart, options.containerID);
|
|
const ul = legendList.list;
|
|
|
|
// Reuse the built-in legendItems generator
|
|
const items = chart.options.plugins.legend.labels.generateLabels(chart);
|
|
|
|
items.forEach(item => {
|
|
if (legendList.created) {
|
|
const li = document.createElement('li');
|
|
li.classList.add("legend-element");
|
|
|
|
li.onmouseover = () => {trigger_legend_hover(item, chart)};
|
|
li.onmouseout = () => {trigger_legend_leave(chart)};
|
|
li.onclick = () => {trigger_legend_click(item)};
|
|
|
|
// Color box
|
|
const boxSpan = document.createElement('span');
|
|
boxSpan.style.background = item.fillStyle;
|
|
boxSpan.style.borderColor = item.strokeStyle;
|
|
boxSpan.style.borderWidth = item.lineWidth + 'px';
|
|
|
|
// Text
|
|
const textContainer = document.createElement('p');
|
|
textContainer.style.color = item.fontColor;
|
|
textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
|
|
|
|
const text = document.createTextNode(item.text);
|
|
textContainer.appendChild(text);
|
|
|
|
li.appendChild(boxSpan);
|
|
li.appendChild(textContainer);
|
|
ul.appendChild(li);
|
|
|
|
} else {
|
|
const list_element = ul.querySelector(`li.legend-element:nth-of-type(${item.datasetIndex+1})`);
|
|
const boxSpan = list_element.querySelector("span");
|
|
const textContainer = list_element.querySelector("p");
|
|
boxSpan.style.background = item.fillStyle;
|
|
boxSpan.style.borderColor = item.strokeStyle;
|
|
boxSpan.style.borderWidth = item.lineWidth + 'px';
|
|
textContainer.style.color = item.fontColor;
|
|
textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
|
|
}
|
|
});
|
|
|
|
},
|
|
};
|
|
|
|
window.onload = create_charts;
|
|
|
|
async function create_charts() {
|
|
combined_charts.forEach(chart => chart.destroy());
|
|
combined_charts = [];
|
|
player_charts.forEach(chart => chart.destroy());
|
|
player_charts = [];
|
|
|
|
let player_rank_values = {};
|
|
let player_progress_values = {};
|
|
|
|
let player_entries = {}; // puuid zeigt auf player entries
|
|
let player_entries_byName = {}; // playername zeigt auf entries (damit im kombinierten Graphen die Tooltips korrekt gerendert werden können)
|
|
let player_accounts = {}; // puuid zeigt auf player account
|
|
|
|
const tooltip_plugin = {
|
|
callbacks: {
|
|
label: function (context) {
|
|
return format_rank(player_entries_byName[context.dataset.label][context.parsed.x / 1000]["tier"], player_entries_byName[context.dataset.label][context.parsed.x / 1000]["rank"], player_entries_byName[context.dataset.label][context.parsed.x / 1000]["points"])
|
|
},
|
|
title: items => {
|
|
let ms = items[0].raw.x;
|
|
let options = {
|
|
weekday: "short",
|
|
day: "numeric",
|
|
month: "short",
|
|
hour: "2-digit",
|
|
minute: "2-digit"
|
|
}
|
|
return new Date(ms).toLocaleDateString("de-DE", options);
|
|
},
|
|
beforeTitle: function (context) {
|
|
return context[0].dataset.label;
|
|
},
|
|
}
|
|
};
|
|
|
|
await fetch(`get.php`, {
|
|
method: "GET",
|
|
})
|
|
.then(res => res.json())
|
|
.then(result => {
|
|
player_entries = result["entries"];
|
|
player_accounts = result["accounts"];
|
|
|
|
let all_rank_yDatasets = []; // Datasets, also die einzelnen Linien im Graphen, beinhaltet player_rank_values
|
|
let all_progress_yDatasets = []; // Datasets, also die einzelnen Linien im Graphen, beinhaltet player_progress_values
|
|
|
|
let minval = -1;
|
|
let maxval = -1;
|
|
|
|
let color_counter = 0;
|
|
for (const puuid in player_entries) {
|
|
player_rank_values[puuid] = [];
|
|
player_progress_values[puuid] = [];
|
|
player_entries_byName[`${player_accounts[puuid]["gameName"]}#${player_accounts[puuid]["tagLine"]}`] = [];
|
|
|
|
let player_start_points = -1;
|
|
for (const timestamp in player_entries[puuid]) {
|
|
// Für alle Player Entries Punktzahl und Punktedifferenz berechnen
|
|
// Bei der Gelegenheit auch Entries in player_entries_byName eintragen
|
|
const current_points = rank_to_points(player_entries[puuid][timestamp]["tier"], player_entries[puuid][timestamp]["rank"], player_entries[puuid][timestamp]["points"]);
|
|
const adj_timestamp = parseInt(timestamp) * 1000;
|
|
player_rank_values[puuid].push({x: adj_timestamp, y: current_points});
|
|
player_entries_byName[`${player_accounts[puuid]["gameName"]}#${player_accounts[puuid]["tagLine"]}`][timestamp] = player_entries[puuid][timestamp];
|
|
if (player_start_points === -1) {
|
|
player_start_points = current_points;
|
|
player_progress_values[puuid].push({x: adj_timestamp, y: 0});
|
|
} else {
|
|
player_progress_values[puuid].push({
|
|
x: adj_timestamp,
|
|
y: current_points - player_start_points
|
|
});
|
|
}
|
|
if (minval === -1 || adj_timestamp < minval) {
|
|
minval = adj_timestamp;
|
|
}
|
|
if (maxval === -1 || adj_timestamp > maxval) {
|
|
maxval = adj_timestamp;
|
|
}
|
|
}
|
|
// Linie für den Spieler zu Datasets des Graphen hinzufügen
|
|
all_rank_yDatasets.push({
|
|
label: `${player_accounts[puuid]["gameName"]}#${player_accounts[puuid]["tagLine"]}`,
|
|
fill: false,
|
|
borderColor: getColor(color_counter),
|
|
backgroundColor: getColor(color_counter),
|
|
borderJoinStyle: "round",
|
|
data: player_rank_values[puuid],
|
|
spanGaps: true,
|
|
pointHitRadius: 16,
|
|
pointRadius: 0,
|
|
})
|
|
all_progress_yDatasets.push({
|
|
label: `${player_accounts[puuid]["gameName"]}#${player_accounts[puuid]["tagLine"]}`,
|
|
fill: false,
|
|
borderColor: getColor(color_counter),
|
|
backgroundColor: getColor(color_counter),
|
|
borderJoinStyle: "round",
|
|
data: player_progress_values[puuid],
|
|
spanGaps: true,
|
|
pointHitRadius: 16,
|
|
pointRadius: 0,
|
|
})
|
|
color_counter++;
|
|
}
|
|
// Graphen erstellen
|
|
combined_charts.push(new Chart(`progress-chart-combined`, {
|
|
type: "line",
|
|
data: {
|
|
datasets: all_rank_yDatasets,
|
|
},
|
|
options: {
|
|
plugins: {
|
|
tooltip: tooltip_plugin,
|
|
zoom: {...zoom_plugin},
|
|
legend: {
|
|
display: false,
|
|
},
|
|
htmlLegend: {
|
|
containerID: 'combined-chart-legend',
|
|
}
|
|
},
|
|
scales: {
|
|
x: x_scale,
|
|
y: {
|
|
display: true,
|
|
stepSize: 100,
|
|
ticks: {
|
|
callback: (value) => {
|
|
return points_to_rankstring(value, false);
|
|
}
|
|
}
|
|
},
|
|
},
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
},
|
|
plugins: [htmlLegendPlugin],
|
|
}));
|
|
combined_charts.push(new Chart(`progress-chart-combined-progress`, {
|
|
type: "line",
|
|
data: {
|
|
datasets: all_progress_yDatasets,
|
|
},
|
|
options: {
|
|
plugins: {
|
|
tooltip: tooltip_plugin,
|
|
zoom: {...zoom_plugin},
|
|
legend: {
|
|
display: false,
|
|
},
|
|
htmlLegend: {
|
|
containerID: 'combined-chart-legend',
|
|
}
|
|
},
|
|
scales: {
|
|
x: x_scale,
|
|
y: {
|
|
display: true,
|
|
},
|
|
},
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
},
|
|
plugins: [htmlLegendPlugin],
|
|
}));
|
|
minval -= 7200000;
|
|
maxval += 7200000;
|
|
combined_charts.forEach(combined_chart => {
|
|
combined_chart.options.plugins.zoom.limits = {
|
|
x: {min: minval, max: maxval},
|
|
}
|
|
})
|
|
})
|
|
.catch(e => console.error(e))
|
|
|
|
const charts = document.getElementsByClassName("progress-chart");
|
|
for (const chart of charts) {
|
|
let puuid = chart.id.split("-");
|
|
puuid.splice(0, 2);
|
|
puuid = puuid.join("-");
|
|
let values = [];
|
|
let minval = Object.keys(player_entries[puuid])[0] * 1000 - 7200000;
|
|
let maxval = Object.keys(player_entries[puuid])[Object.keys(player_entries[puuid]).length - 1] * 1000 + 7200000;
|
|
for (const entriesKey in player_entries[puuid]) {
|
|
let points = rank_to_points(player_entries[puuid][entriesKey]["tier"], player_entries[puuid][entriesKey]["rank"], player_entries[puuid][entriesKey]["points"]);
|
|
values.push({x: parseInt(entriesKey) * 1000, y: points});
|
|
}
|
|
const player_chart = new Chart(`progress-chart-${puuid}`, {
|
|
type: "line",
|
|
data: {
|
|
datasets: [{
|
|
label: `${player_accounts[puuid]["gameName"]}#${player_accounts[puuid]["tagLine"]}`,
|
|
fill: false,
|
|
borderColor: "rgba(150,150,175)",
|
|
backgroundColor: "rgba(150,150,175)",
|
|
borderJoinStyle: "round",
|
|
data: values,
|
|
pointHitRadius: 16,
|
|
pointRadius: 0,
|
|
}]
|
|
},
|
|
options: {
|
|
plugins: {
|
|
legend: {display: false},
|
|
tooltip: tooltip_plugin,
|
|
zoom: {...zoom_plugin},
|
|
},
|
|
scales: {
|
|
x: x_scale,
|
|
y: {
|
|
display: true,
|
|
stepSize: 100,
|
|
ticks: {
|
|
callback: (value) => {
|
|
return points_to_rankstring(value, false);
|
|
}
|
|
}
|
|
},
|
|
},
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
}
|
|
});
|
|
player_chart.options.plugins.zoom.limits = {
|
|
x: {min: minval, max: maxval},
|
|
}
|
|
player_charts.push(player_chart);
|
|
}
|
|
}
|
|
|
|
function rank_to_points(tier, rank, lp) {
|
|
const apex_tiers = (tier === "MASTER" || tier === "GRANDMASTER" || tier === "CHALLENGER");
|
|
const tiers = {
|
|
"DIAMOND": 2400,
|
|
"EMERALD": 2000,
|
|
"PLATINUM": 1600,
|
|
"GOLD": 1200,
|
|
"SILVER": 800,
|
|
"BRONZE": 400,
|
|
"IRON": 0,
|
|
};
|
|
const ranks = {
|
|
"I": 300,
|
|
"II": 200,
|
|
"III": 100,
|
|
"IV": 0,
|
|
};
|
|
if (apex_tiers) {
|
|
return 2800 + lp;
|
|
} else {
|
|
return tiers[tier] + ranks[rank] + lp;
|
|
}
|
|
}
|
|
|
|
function points_to_rankstring(points, include_LP = true) {
|
|
const apex_tiers = (points >= 2800);
|
|
let lp = (apex_tiers) ? points - 2800 : points % 100
|
|
let rank = (points - lp) % 400;
|
|
let tier = (points - lp - rank);
|
|
const tiers = {
|
|
2400: "Diamond",
|
|
2000: "Emerald",
|
|
1600: "Platinum",
|
|
1200: "Gold",
|
|
800: "Silver",
|
|
400: "Bronze",
|
|
0: "Iron",
|
|
};
|
|
const ranks = {
|
|
300: "I",
|
|
200: "II",
|
|
100: "III",
|
|
0: "IV",
|
|
};
|
|
|
|
let rank_string = (apex_tiers) ? "Master" : tiers[tier];
|
|
if (!apex_tiers) rank_string += ` ${ranks[rank]}`;
|
|
if (include_LP || apex_tiers) rank_string += ` ${lp} LP`;
|
|
return rank_string;
|
|
}
|
|
|
|
function format_rank(tier, rank, lp) {
|
|
tier = tier.charAt(0).toUpperCase() + tier.slice(1).toLowerCase();
|
|
const apex_tiers = (tier === "Master" || tier === "Grandmaster" || tier === "Challenger");
|
|
let rank_string = tier;
|
|
if (!apex_tiers) rank_string += ` ${rank}`;
|
|
rank_string += ` ${lp} LP`;
|
|
return rank_string;
|
|
}
|
|
|
|
function getColor(num) {
|
|
const colors = ["#33b1ff", "#d2a106", "#007d79", "#8a3ffc", "#ff7eb6", "#ba4e00", "#fa4d56", "#fff1f1", "#6fdc8c", "#4589ff", "#d12771", "#08bdba", "#bae6ff", "#d4bbff"];
|
|
|
|
return colors[num % 9];
|
|
}
|
|
|
|
function toggle_combined_chart() {
|
|
const comb_charts = this.parentNode.parentNode.querySelectorAll(`.leaderboard>.graph-wrapper`);
|
|
const chart = this.parentNode.parentNode.querySelector(`.graph-wrapper.${this.id}`);
|
|
const buttons = document.querySelectorAll("button.open-general-graph");
|
|
if (chart.classList.contains("closed")) {
|
|
comb_charts.forEach(element => element.classList.add("closed"));
|
|
buttons.forEach(element => element.classList.remove("dropdown-open"));
|
|
chart.classList.remove("closed");
|
|
this.classList.add("dropdown-open");
|
|
document.querySelector("#combined-chart-legend").classList.remove("hidden");
|
|
} else {
|
|
chart.classList.add("closed");
|
|
this.classList.remove("dropdown-open");
|
|
document.querySelector("#combined-chart-legend").classList.add("hidden");
|
|
}
|
|
}
|
|
|
|
function toggle_leaderboard_chart(event) {
|
|
if (this.classList.contains("closed")) {
|
|
this.classList.remove("closed");
|
|
} else {
|
|
if (event.target.nodeName === "CANVAS") return;
|
|
this.classList.add("closed");
|
|
}
|
|
}
|
|
|
|
document.querySelectorAll("button.open-general-graph").forEach(element => element.addEventListener("click", toggle_combined_chart));
|
|
document.querySelectorAll("button.leaderboard-element").forEach(element => element.addEventListener("mousedown", toggle_leaderboard_chart));
|
|
|
|
async function update_leaderboard_entries() {
|
|
this.disabled = true;
|
|
this.classList.add("button-updating")
|
|
let eventSource = new EventSource("./update.php");
|
|
eventSource.addEventListener("progress", e => {
|
|
this.style.setProperty("--button-loading-bar-width", `${e.data * 100}%`);
|
|
})
|
|
eventSource.addEventListener("forbidden", e => {
|
|
eventSource.close();
|
|
let response = JSON.parse(e.data);
|
|
const currenttime = new Date();
|
|
const updatetime = new Date(response.last);
|
|
let timediff = new Date(currenttime - updatetime);
|
|
let resttime = new Date( 1000 * 60 * response.limit - (currenttime - updatetime));
|
|
window.alert(`Das letzte Update wurde vor ${format_time_minsec(timediff)} durchgeführt. Versuche es in ${format_time_minsec(resttime)} noch einmal`);
|
|
reset_button(this);
|
|
})
|
|
eventSource.onerror = e => {
|
|
eventSource.close();
|
|
window.alert(`Beim Update ist ein Fehler aufgetreten. Versuche es später noch einmal`)
|
|
reset_button(this);
|
|
}
|
|
eventSource.addEventListener("done", e => {
|
|
eventSource.close();
|
|
reset_button(this);
|
|
|
|
update_leaderboard_elements();
|
|
})
|
|
|
|
async function reset_button(button) {
|
|
await new Promise(r => setTimeout(r,400)); // warten bis ladebalken am ende angekommen ist
|
|
button.style.setProperty("--button-loading-bar-opacity", `0`);
|
|
await new Promise(r => setTimeout(r,400)); // warten bis ladebalken verblasst ist
|
|
button.style.setProperty("--button-loading-bar-width", `0`);
|
|
await new Promise(r => setTimeout(r,400)); // warten bis ladebalken wieder am anfang angekommen ist
|
|
button.style.setProperty("--button-loading-bar-opacity", `1`);
|
|
button.disabled = false;
|
|
button.classList.remove("button-updating");
|
|
}
|
|
}
|
|
|
|
document.querySelector("button.update-leaderboard").addEventListener("click", update_leaderboard_entries);
|
|
|
|
function format_time_minsec(date) {
|
|
let format, trenner = "", min = "", nullausgleich = "";
|
|
if (date.getMinutes() === 0) {
|
|
format = " Sekunden";
|
|
} else {
|
|
min = date.getMinutes();
|
|
format = " Minuten";
|
|
trenner = ":";
|
|
if (date.getSeconds() < 10) {
|
|
nullausgleich = "0";
|
|
}
|
|
}
|
|
return min + trenner + nullausgleich + date.getSeconds() + format;
|
|
}
|
|
|
|
const month_names = ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"];
|
|
|
|
async function update_leaderboard_elements() {
|
|
fetch(`./leaderboard_list.php`, {
|
|
method: "GET",
|
|
})
|
|
.then(res => res.text())
|
|
.then(leaderboard_list => {
|
|
// replace updated Leaderboard
|
|
document.querySelector(".leaderboard-list").outerHTML = leaderboard_list;
|
|
// reapply EventListeners
|
|
document.querySelectorAll("button.leaderboard-element").forEach(element => element.addEventListener("mousedown", toggle_leaderboard_chart));
|
|
// recreate charts
|
|
create_charts();
|
|
})
|
|
.catch(e => console.error(e));
|
|
} |