mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-09-12 01:25:21 +08:00
moved all charts to a module. part of #495
This commit is contained in:
parent
9e93688778
commit
c9626396d8
9 changed files with 780 additions and 862 deletions
|
@ -101,6 +101,7 @@ const refactoredSrc = [
|
|||
"./src/js/test/test-stats.js",
|
||||
"./src/js/theme-colors.js",
|
||||
"./src/js/test/out-of-focus.js",
|
||||
"./src/js/chart-controller.js",
|
||||
];
|
||||
|
||||
//legacy files
|
||||
|
|
|
@ -478,543 +478,71 @@ function getAccountDataAndInit() {
|
|||
});
|
||||
}
|
||||
|
||||
var resultHistoryChart = new Chart($(".pageAccount #resultHistoryChart"), {
|
||||
animationSteps: 60,
|
||||
type: "line",
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
yAxisID: "wpm",
|
||||
label: "wpm",
|
||||
fill: false,
|
||||
data: [],
|
||||
borderColor: "#f44336",
|
||||
borderWidth: 2,
|
||||
trendlineLinear: {
|
||||
style: "rgba(255,105,180, .8)",
|
||||
lineStyle: "dotted",
|
||||
width: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
yAxisID: "acc",
|
||||
label: "acc",
|
||||
fill: false,
|
||||
data: [],
|
||||
borderColor: "#cccccc",
|
||||
borderWidth: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
// Disable the on-canvas tooltip
|
||||
enabled: true,
|
||||
intersect: false,
|
||||
custom: function (tooltip) {
|
||||
if (!tooltip) return;
|
||||
// disable displaying the color box;
|
||||
tooltip.displayColors = false;
|
||||
},
|
||||
callbacks: {
|
||||
// HERE YOU CUSTOMIZE THE LABELS
|
||||
title: function () {
|
||||
return;
|
||||
},
|
||||
beforeLabel: function (tooltipItem, data) {
|
||||
let resultData =
|
||||
data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
|
||||
if (tooltipItem.datasetIndex !== 0) {
|
||||
return `error rate: ${Misc.roundTo2(
|
||||
resultData.y
|
||||
)}%\nacc: ${Misc.roundTo2(100 - resultData.y)}%`;
|
||||
}
|
||||
let label =
|
||||
`${data.datasets[tooltipItem.datasetIndex].label}: ${
|
||||
tooltipItem.yLabel
|
||||
}` +
|
||||
"\n" +
|
||||
`raw: ${resultData.raw}` +
|
||||
"\n" +
|
||||
`acc: ${resultData.acc}` +
|
||||
"\n\n" +
|
||||
`mode: ${resultData.mode} `;
|
||||
|
||||
if (resultData.mode == "time") {
|
||||
label += resultData.mode2;
|
||||
} else if (resultData.mode == "words") {
|
||||
label += resultData.mode2;
|
||||
}
|
||||
|
||||
let diff = resultData.difficulty;
|
||||
if (diff == undefined) {
|
||||
diff = "normal";
|
||||
}
|
||||
label += "\n" + `difficulty: ${diff}`;
|
||||
|
||||
label +=
|
||||
"\n" +
|
||||
`punctuation: ${resultData.punctuation}` +
|
||||
"\n" +
|
||||
`language: ${resultData.language}` +
|
||||
"\n\n" +
|
||||
`date: ${moment(resultData.timestamp).format("DD MMM YYYY HH:mm")}`;
|
||||
|
||||
return label;
|
||||
},
|
||||
label: function () {
|
||||
return;
|
||||
},
|
||||
afterLabel: function () {
|
||||
return;
|
||||
},
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
duration: 250,
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
fontColor: "#ffffff",
|
||||
},
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
hover: {
|
||||
mode: "nearest",
|
||||
intersect: false,
|
||||
},
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
ticks: {},
|
||||
type: "time",
|
||||
bounds: "ticks",
|
||||
distribution: "series",
|
||||
display: false,
|
||||
offset: true,
|
||||
scaleLabel: {
|
||||
display: false,
|
||||
labelString: "Date",
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
id: "wpm",
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
stepSize: 10,
|
||||
},
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Words per Minute",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "acc",
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
max: 100,
|
||||
},
|
||||
display: true,
|
||||
position: "right",
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Error rate (100 - accuracy)",
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
let activityChart = new Chart($(".pageAccount #activityChart"), {
|
||||
animationSteps: 60,
|
||||
type: "bar",
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
yAxisID: "count",
|
||||
label: "Seconds",
|
||||
data: [],
|
||||
trendlineLinear: {
|
||||
style: "rgba(255,105,180, .8)",
|
||||
lineStyle: "dotted",
|
||||
width: 2,
|
||||
},
|
||||
order: 3,
|
||||
},
|
||||
{
|
||||
yAxisID: "avgWpm",
|
||||
label: "Average Wpm",
|
||||
data: [],
|
||||
type: "line",
|
||||
order: 2,
|
||||
lineTension: 0,
|
||||
fill: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
// HERE YOU CUSTOMIZE THE LABELS
|
||||
title: function (tooltipItem, data) {
|
||||
let resultData =
|
||||
data.datasets[tooltipItem[0].datasetIndex].data[
|
||||
tooltipItem[0].index
|
||||
];
|
||||
return moment(resultData.x).format("DD MMM YYYY");
|
||||
},
|
||||
beforeLabel: function (tooltipItem, data) {
|
||||
let resultData =
|
||||
data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
|
||||
if (tooltipItem.datasetIndex === 0) {
|
||||
return `Time Typing: ${Misc.secondsToString(
|
||||
resultData.y
|
||||
)}\nTests Completed: ${resultData.amount}`;
|
||||
} else if (tooltipItem.datasetIndex === 1) {
|
||||
return `Average Wpm: ${Misc.roundTo2(resultData.y)}`;
|
||||
}
|
||||
},
|
||||
label: function () {
|
||||
return;
|
||||
},
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
duration: 250,
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
fontColor: "#ffffff",
|
||||
},
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
hover: {
|
||||
mode: "nearest",
|
||||
intersect: false,
|
||||
},
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
ticks: {
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
type: "time",
|
||||
time: {
|
||||
unit: "day",
|
||||
displayFormats: {
|
||||
day: "D MMM",
|
||||
},
|
||||
},
|
||||
bounds: "ticks",
|
||||
distribution: "series",
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: false,
|
||||
labelString: "Date",
|
||||
},
|
||||
offset: true,
|
||||
},
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
id: "count",
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
stepSize: 10,
|
||||
},
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Time Typing",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "avgWpm",
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
stepSize: 10,
|
||||
},
|
||||
display: true,
|
||||
position: "right",
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Average Wpm",
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
let hoverChart = new Chart($(".pageAccount #hoverChart"), {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
label: "wpm",
|
||||
data: [],
|
||||
borderColor: "rgba(125, 125, 125, 1)",
|
||||
borderWidth: 2,
|
||||
yAxisID: "wpm",
|
||||
order: 2,
|
||||
radius: 2,
|
||||
},
|
||||
{
|
||||
label: "raw",
|
||||
data: [],
|
||||
borderColor: "rgba(125, 125, 125, 1)",
|
||||
borderWidth: 2,
|
||||
yAxisID: "raw",
|
||||
order: 3,
|
||||
radius: 2,
|
||||
},
|
||||
{
|
||||
label: "errors",
|
||||
data: [],
|
||||
borderColor: "rgba(255, 125, 125, 1)",
|
||||
pointBackgroundColor: "rgba(255, 125, 125, 1)",
|
||||
borderWidth: 2,
|
||||
order: 1,
|
||||
yAxisID: "error",
|
||||
maxBarThickness: 10,
|
||||
type: "scatter",
|
||||
pointStyle: "crossRot",
|
||||
radius: function (context) {
|
||||
var index = context.dataIndex;
|
||||
var value = context.dataset.data[index];
|
||||
return value <= 0 ? 0 : 3;
|
||||
},
|
||||
pointHoverRadius: function (context) {
|
||||
var index = context.dataIndex;
|
||||
var value = context.dataset.data[index];
|
||||
return value <= 0 ? 0 : 5;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
mode: "index",
|
||||
intersect: false,
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {},
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
ticks: {
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: false,
|
||||
labelString: "Seconds",
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
id: "wpm",
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Words per Minute",
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
gridLines: {
|
||||
display: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "raw",
|
||||
display: false,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Raw Words per Minute",
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "error",
|
||||
display: true,
|
||||
position: "right",
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Errors",
|
||||
},
|
||||
ticks: {
|
||||
precision: 0,
|
||||
beginAtZero: true,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
annotation: {
|
||||
annotations: [
|
||||
{
|
||||
enabled: false,
|
||||
type: "line",
|
||||
mode: "horizontal",
|
||||
scaleID: "wpm",
|
||||
value: "-30",
|
||||
borderColor: "red",
|
||||
borderWidth: 1,
|
||||
borderDash: [2, 2],
|
||||
label: {
|
||||
// Background color of label, default below
|
||||
backgroundColor: "blue",
|
||||
|
||||
// Font size of text, inherits from global
|
||||
fontSize: 11,
|
||||
|
||||
// Font style of text, default below
|
||||
fontStyle: "normal",
|
||||
|
||||
// Font color of text, default below
|
||||
fontColor: "#fff",
|
||||
|
||||
// Padding of label to add left/right, default below
|
||||
xPadding: 6,
|
||||
|
||||
// Padding of label to add top/bottom, default below
|
||||
yPadding: 6,
|
||||
|
||||
// Radius of label rectangle, default below
|
||||
cornerRadius: 3,
|
||||
|
||||
// Anchor position of label on line, can be one of: top, bottom, left, right, center. Default below.
|
||||
position: "center",
|
||||
|
||||
// Whether the label is enabled and should be displayed
|
||||
enabled: true,
|
||||
|
||||
// Text to display in label - default is null. Provide an array to display values on a new line
|
||||
content: "PB",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function updateHoverChart(filteredId) {
|
||||
function updateMiniResultChart(filteredId) {
|
||||
let data = filteredResults[filteredId].chartData;
|
||||
let labels = [];
|
||||
for (let i = 1; i <= data.wpm.length; i++) {
|
||||
labels.push(i.toString());
|
||||
}
|
||||
hoverChart.data.labels = labels;
|
||||
hoverChart.data.datasets[0].data = data.wpm;
|
||||
hoverChart.data.datasets[1].data = data.raw;
|
||||
hoverChart.data.datasets[2].data = data.err;
|
||||
ChartController.miniResult.data.labels = labels;
|
||||
ChartController.miniResult.data.datasets[0].data = data.wpm;
|
||||
ChartController.miniResult.data.datasets[1].data = data.raw;
|
||||
ChartController.miniResult.data.datasets[2].data = data.err;
|
||||
|
||||
hoverChart.options.scales.xAxes[0].ticks.minor.fontColor = ThemeColors.sub;
|
||||
hoverChart.options.scales.xAxes[0].scaleLabel.fontColor = ThemeColors.sub;
|
||||
hoverChart.options.scales.yAxes[0].ticks.minor.fontColor = ThemeColors.sub;
|
||||
hoverChart.options.scales.yAxes[2].ticks.minor.fontColor = ThemeColors.sub;
|
||||
hoverChart.options.scales.yAxes[0].scaleLabel.fontColor = ThemeColors.sub;
|
||||
hoverChart.options.scales.yAxes[2].scaleLabel.fontColor = ThemeColors.sub;
|
||||
|
||||
hoverChart.data.datasets[0].borderColor = ThemeColors.main;
|
||||
hoverChart.data.datasets[0].pointBackgroundColor = ThemeColors.main;
|
||||
hoverChart.data.datasets[1].borderColor = ThemeColors.sub;
|
||||
hoverChart.data.datasets[1].pointBackgroundColor = ThemeColors.sub;
|
||||
|
||||
hoverChart.options.annotation.annotations[0].borderColor = ThemeColors.sub;
|
||||
hoverChart.options.annotation.annotations[0].label.backgroundColor =
|
||||
ThemeColors.sub;
|
||||
hoverChart.options.annotation.annotations[0].label.fontColor = ThemeColors.bg;
|
||||
ChartController.miniResult.updateColors();
|
||||
|
||||
let maxChartVal = Math.max(...[Math.max(...data.wpm), Math.max(...data.raw)]);
|
||||
let minChartVal = Math.min(...[Math.min(...data.wpm), Math.min(...data.raw)]);
|
||||
hoverChart.options.scales.yAxes[0].ticks.max = Math.round(maxChartVal);
|
||||
hoverChart.options.scales.yAxes[1].ticks.max = Math.round(maxChartVal);
|
||||
ChartController.miniResult.options.scales.yAxes[0].ticks.max = Math.round(
|
||||
maxChartVal
|
||||
);
|
||||
ChartController.miniResult.options.scales.yAxes[1].ticks.max = Math.round(
|
||||
maxChartVal
|
||||
);
|
||||
|
||||
if (!config.startGraphsAtZero) {
|
||||
hoverChart.options.scales.yAxes[0].ticks.min = Math.round(minChartVal);
|
||||
hoverChart.options.scales.yAxes[1].ticks.min = Math.round(minChartVal);
|
||||
ChartController.miniResult.options.scales.yAxes[0].ticks.min = Math.round(
|
||||
minChartVal
|
||||
);
|
||||
ChartController.miniResult.options.scales.yAxes[1].ticks.min = Math.round(
|
||||
minChartVal
|
||||
);
|
||||
} else {
|
||||
hoverChart.options.scales.yAxes[0].ticks.min = 0;
|
||||
hoverChart.options.scales.yAxes[1].ticks.min = 0;
|
||||
ChartController.miniResult.options.scales.yAxes[0].ticks.min = 0;
|
||||
ChartController.miniResult.options.scales.yAxes[1].ticks.min = 0;
|
||||
}
|
||||
|
||||
hoverChart.update({ duration: 0 });
|
||||
ChartController.miniResult.update({ duration: 0 });
|
||||
}
|
||||
|
||||
function showHoverChart() {
|
||||
$(".pageAccount .hoverChartWrapper").stop(true, true).fadeIn(125);
|
||||
$(".pageAccount .hoverChartBg").stop(true, true).fadeIn(125);
|
||||
function showMiniResultChart() {
|
||||
$(".pageAccount .miniResultChartWrapper").stop(true, true).fadeIn(125);
|
||||
$(".pageAccount .miniResultChartBg").stop(true, true).fadeIn(125);
|
||||
}
|
||||
|
||||
function hideHoverChart() {
|
||||
$(".pageAccount .hoverChartWrapper").stop(true, true).fadeOut(125);
|
||||
$(".pageAccount .hoverChartBg").stop(true, true).fadeOut(125);
|
||||
function hideMiniResultChart() {
|
||||
$(".pageAccount .miniResultChartWrapper").stop(true, true).fadeOut(125);
|
||||
$(".pageAccount .miniResultChartBg").stop(true, true).fadeOut(125);
|
||||
}
|
||||
|
||||
function updateHoverChartPosition(x, y) {
|
||||
$(".pageAccount .hoverChartWrapper").css({ top: y, left: x });
|
||||
function updateMiniResultChartPosition(x, y) {
|
||||
$(".pageAccount .miniResultChartWrapper").css({ top: y, left: x });
|
||||
}
|
||||
|
||||
$(document).on("click", ".pageAccount .hoverChartButton", (event) => {
|
||||
$(document).on("click", ".pageAccount .miniResultChartButton", (event) => {
|
||||
console.log("updating");
|
||||
let filterid = $(event.currentTarget).attr("filteredResultsId");
|
||||
if (filterid === undefined) return;
|
||||
updateHoverChart(filterid);
|
||||
showHoverChart();
|
||||
updateHoverChartPosition(
|
||||
event.pageX - $(".pageAccount .hoverChartWrapper").outerWidth(),
|
||||
updateMiniResultChart(filterid);
|
||||
showMiniResultChart();
|
||||
updateMiniResultChartPosition(
|
||||
event.pageX - $(".pageAccount .miniResultChartWrapper").outerWidth(),
|
||||
event.pageY + 30
|
||||
);
|
||||
});
|
||||
|
||||
$(document).on("click", ".pageAccount .hoverChartBg", (event) => {
|
||||
hideHoverChart();
|
||||
$(document).on("click", ".pageAccount .miniResultChartBg", (event) => {
|
||||
hideMiniResultChart();
|
||||
});
|
||||
|
||||
Misc.getLanguageList().then((languages) => {
|
||||
|
@ -1641,11 +1169,11 @@ function loadMoreLines() {
|
|||
}
|
||||
|
||||
if (result.chartData === undefined) {
|
||||
icons += `<span class="hoverChartButton" aria-label="No chart data found" data-balloon-pos="up"><i class="fas fa-chart-line"></i></span>`;
|
||||
icons += `<span class="miniResultChartButton" aria-label="No chart data found" data-balloon-pos="up"><i class="fas fa-chart-line"></i></span>`;
|
||||
} else if (result.chartData === "toolong") {
|
||||
icons += `<span class="hoverChartButton" aria-label="Chart history is not available for long tests" data-balloon-pos="up"><i class="fas fa-chart-line"></i></span>`;
|
||||
icons += `<span class="miniResultChartButton" aria-label="Chart history is not available for long tests" data-balloon-pos="up"><i class="fas fa-chart-line"></i></span>`;
|
||||
} else {
|
||||
icons += `<span class="hoverChartButton" aria-label="View graph" data-balloon-pos="up" filteredResultsId="${i}" style="opacity: 1"><i class="fas fa-chart-line"></i></span>`;
|
||||
icons += `<span class="miniResultChartButton" aria-label="View graph" data-balloon-pos="up" filteredResultsId="${i}" style="opacity: 1"><i class="fas fa-chart-line"></i></span>`;
|
||||
}
|
||||
|
||||
let tagNames = "";
|
||||
|
@ -1767,7 +1295,8 @@ let totalSecondsFiltered = 0;
|
|||
function refreshAccountPage() {
|
||||
function cont() {
|
||||
ThemeColors.update();
|
||||
updateChartColors();
|
||||
ChartController.accountHistory.updateColors();
|
||||
ChartController.accountActivity.updateColors();
|
||||
refreshGlobalStats();
|
||||
fillPbTables();
|
||||
|
||||
|
@ -2116,63 +1645,26 @@ function refreshAccountPage() {
|
|||
lastTimestamp = date;
|
||||
});
|
||||
|
||||
activityChart.data.datasets[0].data = activityChartData_time;
|
||||
activityChart.data.datasets[1].data = activityChartData_avgWpm;
|
||||
ChartController.accountActivity.data.datasets[0].data = activityChartData_time;
|
||||
ChartController.accountActivity.data.datasets[1].data = activityChartData_avgWpm;
|
||||
|
||||
activityChart.options.legend.labels.fontColor = ThemeColors.sub;
|
||||
|
||||
activityChart.options.scales.xAxes[0].ticks.minor.fontColor =
|
||||
ThemeColors.sub;
|
||||
activityChart.options.scales.yAxes[0].ticks.minor.fontColor =
|
||||
ThemeColors.sub;
|
||||
activityChart.options.scales.yAxes[0].scaleLabel.fontColor =
|
||||
ThemeColors.sub;
|
||||
activityChart.data.datasets[0].borderColor = ThemeColors.main;
|
||||
activityChart.data.datasets[0].backgroundColor = ThemeColors.main;
|
||||
|
||||
activityChart.data.datasets[0].trendlineLinear.style = ThemeColors.sub;
|
||||
|
||||
activityChart.options.scales.yAxes[1].ticks.minor.fontColor =
|
||||
ThemeColors.sub;
|
||||
activityChart.options.scales.yAxes[1].scaleLabel.fontColor =
|
||||
ThemeColors.sub;
|
||||
activityChart.data.datasets[1].borderColor = ThemeColors.sub;
|
||||
|
||||
activityChart.options.legend.labels.fontColor = ThemeColors.sub;
|
||||
|
||||
resultHistoryChart.options.scales.xAxes[0].ticks.minor.fontColor =
|
||||
ThemeColors.sub;
|
||||
resultHistoryChart.options.scales.yAxes[0].ticks.minor.fontColor =
|
||||
ThemeColors.sub;
|
||||
resultHistoryChart.options.scales.yAxes[0].scaleLabel.fontColor =
|
||||
ThemeColors.sub;
|
||||
resultHistoryChart.options.scales.yAxes[1].ticks.minor.fontColor =
|
||||
ThemeColors.sub;
|
||||
resultHistoryChart.options.scales.yAxes[1].scaleLabel.fontColor =
|
||||
ThemeColors.sub;
|
||||
resultHistoryChart.data.datasets[0].borderColor = ThemeColors.main;
|
||||
resultHistoryChart.data.datasets[1].borderColor = ThemeColors.sub;
|
||||
|
||||
resultHistoryChart.options.legend.labels.fontColor = ThemeColors.sub;
|
||||
resultHistoryChart.data.datasets[0].trendlineLinear.style = ThemeColors.sub;
|
||||
|
||||
resultHistoryChart.data.datasets[0].data = chartData;
|
||||
resultHistoryChart.data.datasets[1].data = accChartData;
|
||||
ChartController.accountHistory.data.datasets[0].data = chartData;
|
||||
ChartController.accountHistory.data.datasets[1].data = accChartData;
|
||||
|
||||
let wpms = chartData.map((r) => r.y);
|
||||
let minWpmChartVal = Math.min(...wpms);
|
||||
let maxWpmChartVal = Math.max(...wpms);
|
||||
|
||||
// let accuracies = accChartData.map((r) => r.y);
|
||||
resultHistoryChart.options.scales.yAxes[0].ticks.max =
|
||||
ChartController.accountHistory.options.scales.yAxes[0].ticks.max =
|
||||
Math.floor(maxWpmChartVal) + (10 - (Math.floor(maxWpmChartVal) % 10));
|
||||
|
||||
if (!config.startGraphsAtZero) {
|
||||
resultHistoryChart.options.scales.yAxes[0].ticks.min = Math.floor(
|
||||
ChartController.accountHistory.options.scales.yAxes[0].ticks.min = Math.floor(
|
||||
minWpmChartVal
|
||||
);
|
||||
} else {
|
||||
resultHistoryChart.options.scales.yAxes[0].ticks.min = 0;
|
||||
ChartController.accountHistory.options.scales.yAxes[0].ticks.min = 0;
|
||||
}
|
||||
|
||||
if (chartData == [] || chartData.length == 0) {
|
||||
|
@ -2254,16 +1746,16 @@ function refreshAccountPage() {
|
|||
(testRestarts / testCount).toFixed(1)
|
||||
);
|
||||
|
||||
if (resultHistoryChart.data.datasets[0].data.length > 0) {
|
||||
resultHistoryChart.options.plugins.trendlineLinear = true;
|
||||
if (ChartController.accountHistory.data.datasets[0].data.length > 0) {
|
||||
ChartController.accountHistory.options.plugins.trendlineLinear = true;
|
||||
} else {
|
||||
resultHistoryChart.options.plugins.trendlineLinear = false;
|
||||
ChartController.accountHistory.options.plugins.trendlineLinear = false;
|
||||
}
|
||||
|
||||
if (activityChart.data.datasets[0].data.length > 0) {
|
||||
activityChart.options.plugins.trendlineLinear = true;
|
||||
if (ChartController.accountActivity.data.datasets[0].data.length > 0) {
|
||||
ChartController.accountActivity.options.plugins.trendlineLinear = true;
|
||||
} else {
|
||||
activityChart.options.plugins.trendlineLinear = false;
|
||||
ChartController.accountActivity.options.plugins.trendlineLinear = false;
|
||||
}
|
||||
|
||||
let wpmPoints = filteredResults.map((r) => r.wpm).reverse();
|
||||
|
@ -2282,8 +1774,8 @@ function refreshAccountPage() {
|
|||
} wpm.`
|
||||
);
|
||||
|
||||
resultHistoryChart.update({ duration: 0 });
|
||||
activityChart.update({ duration: 0 });
|
||||
ChartController.accountHistory.update({ duration: 0 });
|
||||
ChartController.accountActivity.update({ duration: 0 });
|
||||
|
||||
swapElements($(".pageAccount .preloader"), $(".pageAccount .content"), 250);
|
||||
}
|
||||
|
|
666
src/js/chart-controller.js
Normal file
666
src/js/chart-controller.js
Normal file
|
@ -0,0 +1,666 @@
|
|||
import Chart from "chart.js";
|
||||
import * as TestStats from "./test-stats";
|
||||
import * as ThemeColors from "./theme-colors";
|
||||
import * as Misc from "./misc";
|
||||
|
||||
export let result = new Chart($("#wpmChart"), {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
label: "wpm",
|
||||
data: [],
|
||||
borderColor: "rgba(125, 125, 125, 1)",
|
||||
borderWidth: 2,
|
||||
yAxisID: "wpm",
|
||||
order: 2,
|
||||
radius: 2,
|
||||
},
|
||||
{
|
||||
label: "raw",
|
||||
data: [],
|
||||
borderColor: "rgba(125, 125, 125, 1)",
|
||||
borderWidth: 2,
|
||||
yAxisID: "raw",
|
||||
order: 3,
|
||||
radius: 2,
|
||||
},
|
||||
{
|
||||
label: "errors",
|
||||
data: [],
|
||||
borderColor: "rgba(255, 125, 125, 1)",
|
||||
pointBackgroundColor: "rgba(255, 125, 125, 1)",
|
||||
borderWidth: 2,
|
||||
order: 1,
|
||||
yAxisID: "error",
|
||||
maxBarThickness: 10,
|
||||
type: "scatter",
|
||||
pointStyle: "crossRot",
|
||||
radius: function (context) {
|
||||
var index = context.dataIndex;
|
||||
var value = context.dataset.data[index];
|
||||
return value <= 0 ? 0 : 3;
|
||||
},
|
||||
pointHoverRadius: function (context) {
|
||||
var index = context.dataIndex;
|
||||
var value = context.dataset.data[index];
|
||||
return value <= 0 ? 0 : 5;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
mode: "index",
|
||||
intersect: false,
|
||||
callbacks: {
|
||||
afterLabel: function (ti) {
|
||||
try {
|
||||
$(".wordInputAfter").remove();
|
||||
|
||||
let wordsToHighlight =
|
||||
TestStats.keypressPerSecond[parseInt(ti.xLabel) - 1].words;
|
||||
|
||||
let unique = [...new Set(wordsToHighlight)];
|
||||
unique.forEach((wordIndex) => {
|
||||
let wordEl = $($("#resultWordsHistory .words .word")[wordIndex]);
|
||||
let input = wordEl.attr("input");
|
||||
if (input != undefined)
|
||||
wordEl.append(`<div class="wordInputAfter">${input}</div>`);
|
||||
});
|
||||
} catch {}
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {},
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
ticks: {
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: false,
|
||||
labelString: "Seconds",
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
id: "wpm",
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Words per Minute",
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
gridLines: {
|
||||
display: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "raw",
|
||||
display: false,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Raw Words per Minute",
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "error",
|
||||
display: true,
|
||||
position: "right",
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Errors",
|
||||
},
|
||||
ticks: {
|
||||
precision: 0,
|
||||
beginAtZero: true,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
annotation: {
|
||||
annotations: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export let accountHistory = new Chart($(".pageAccount #accountHistoryChart"), {
|
||||
animationSteps: 60,
|
||||
type: "line",
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
yAxisID: "wpm",
|
||||
label: "wpm",
|
||||
fill: false,
|
||||
data: [],
|
||||
borderColor: "#f44336",
|
||||
borderWidth: 2,
|
||||
trendlineLinear: {
|
||||
style: "rgba(255,105,180, .8)",
|
||||
lineStyle: "dotted",
|
||||
width: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
yAxisID: "acc",
|
||||
label: "acc",
|
||||
fill: false,
|
||||
data: [],
|
||||
borderColor: "#cccccc",
|
||||
borderWidth: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
// Disable the on-canvas tooltip
|
||||
enabled: true,
|
||||
intersect: false,
|
||||
custom: function (tooltip) {
|
||||
if (!tooltip) return;
|
||||
// disable displaying the color box;
|
||||
tooltip.displayColors = false;
|
||||
},
|
||||
callbacks: {
|
||||
// HERE YOU CUSTOMIZE THE LABELS
|
||||
title: function () {
|
||||
return;
|
||||
},
|
||||
beforeLabel: function (tooltipItem, data) {
|
||||
let resultData =
|
||||
data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
|
||||
if (tooltipItem.datasetIndex !== 0) {
|
||||
return `error rate: ${Misc.roundTo2(
|
||||
resultData.y
|
||||
)}%\nacc: ${Misc.roundTo2(100 - resultData.y)}%`;
|
||||
}
|
||||
let label =
|
||||
`${data.datasets[tooltipItem.datasetIndex].label}: ${
|
||||
tooltipItem.yLabel
|
||||
}` +
|
||||
"\n" +
|
||||
`raw: ${resultData.raw}` +
|
||||
"\n" +
|
||||
`acc: ${resultData.acc}` +
|
||||
"\n\n" +
|
||||
`mode: ${resultData.mode} `;
|
||||
|
||||
if (resultData.mode == "time") {
|
||||
label += resultData.mode2;
|
||||
} else if (resultData.mode == "words") {
|
||||
label += resultData.mode2;
|
||||
}
|
||||
|
||||
let diff = resultData.difficulty;
|
||||
if (diff == undefined) {
|
||||
diff = "normal";
|
||||
}
|
||||
label += "\n" + `difficulty: ${diff}`;
|
||||
|
||||
label +=
|
||||
"\n" +
|
||||
`punctuation: ${resultData.punctuation}` +
|
||||
"\n" +
|
||||
`language: ${resultData.language}` +
|
||||
"\n\n" +
|
||||
`date: ${moment(resultData.timestamp).format("DD MMM YYYY HH:mm")}`;
|
||||
|
||||
return label;
|
||||
},
|
||||
label: function () {
|
||||
return;
|
||||
},
|
||||
afterLabel: function () {
|
||||
return;
|
||||
},
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
duration: 250,
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
fontColor: "#ffffff",
|
||||
},
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
hover: {
|
||||
mode: "nearest",
|
||||
intersect: false,
|
||||
},
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
ticks: {},
|
||||
type: "time",
|
||||
bounds: "ticks",
|
||||
distribution: "series",
|
||||
display: false,
|
||||
offset: true,
|
||||
scaleLabel: {
|
||||
display: false,
|
||||
labelString: "Date",
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
id: "wpm",
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
stepSize: 10,
|
||||
},
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Words per Minute",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "acc",
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
max: 100,
|
||||
},
|
||||
display: true,
|
||||
position: "right",
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Error rate (100 - accuracy)",
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export let accountActivity = new Chart(
|
||||
$(".pageAccount #accountActivityChart"),
|
||||
{
|
||||
animationSteps: 60,
|
||||
type: "bar",
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
yAxisID: "count",
|
||||
label: "Seconds",
|
||||
data: [],
|
||||
trendlineLinear: {
|
||||
style: "rgba(255,105,180, .8)",
|
||||
lineStyle: "dotted",
|
||||
width: 2,
|
||||
},
|
||||
order: 3,
|
||||
},
|
||||
{
|
||||
yAxisID: "avgWpm",
|
||||
label: "Average Wpm",
|
||||
data: [],
|
||||
type: "line",
|
||||
order: 2,
|
||||
lineTension: 0,
|
||||
fill: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
// HERE YOU CUSTOMIZE THE LABELS
|
||||
title: function (tooltipItem, data) {
|
||||
let resultData =
|
||||
data.datasets[tooltipItem[0].datasetIndex].data[
|
||||
tooltipItem[0].index
|
||||
];
|
||||
return moment(resultData.x).format("DD MMM YYYY");
|
||||
},
|
||||
beforeLabel: function (tooltipItem, data) {
|
||||
let resultData =
|
||||
data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
|
||||
if (tooltipItem.datasetIndex === 0) {
|
||||
return `Time Typing: ${Misc.secondsToString(
|
||||
resultData.y
|
||||
)}\nTests Completed: ${resultData.amount}`;
|
||||
} else if (tooltipItem.datasetIndex === 1) {
|
||||
return `Average Wpm: ${Misc.roundTo2(resultData.y)}`;
|
||||
}
|
||||
},
|
||||
label: function () {
|
||||
return;
|
||||
},
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
duration: 250,
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
fontColor: "#ffffff",
|
||||
},
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
hover: {
|
||||
mode: "nearest",
|
||||
intersect: false,
|
||||
},
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
ticks: {
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
type: "time",
|
||||
time: {
|
||||
unit: "day",
|
||||
displayFormats: {
|
||||
day: "D MMM",
|
||||
},
|
||||
},
|
||||
bounds: "ticks",
|
||||
distribution: "series",
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: false,
|
||||
labelString: "Date",
|
||||
},
|
||||
offset: true,
|
||||
},
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
id: "count",
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
stepSize: 10,
|
||||
},
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Time Typing",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "avgWpm",
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
stepSize: 10,
|
||||
},
|
||||
display: true,
|
||||
position: "right",
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Average Wpm",
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export let miniResult = new Chart($(".pageAccount #miniResultChart"), {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
label: "wpm",
|
||||
data: [],
|
||||
borderColor: "rgba(125, 125, 125, 1)",
|
||||
borderWidth: 2,
|
||||
yAxisID: "wpm",
|
||||
order: 2,
|
||||
radius: 2,
|
||||
},
|
||||
{
|
||||
label: "raw",
|
||||
data: [],
|
||||
borderColor: "rgba(125, 125, 125, 1)",
|
||||
borderWidth: 2,
|
||||
yAxisID: "raw",
|
||||
order: 3,
|
||||
radius: 2,
|
||||
},
|
||||
{
|
||||
label: "errors",
|
||||
data: [],
|
||||
borderColor: "rgba(255, 125, 125, 1)",
|
||||
pointBackgroundColor: "rgba(255, 125, 125, 1)",
|
||||
borderWidth: 2,
|
||||
order: 1,
|
||||
yAxisID: "error",
|
||||
maxBarThickness: 10,
|
||||
type: "scatter",
|
||||
pointStyle: "crossRot",
|
||||
radius: function (context) {
|
||||
var index = context.dataIndex;
|
||||
var value = context.dataset.data[index];
|
||||
return value <= 0 ? 0 : 3;
|
||||
},
|
||||
pointHoverRadius: function (context) {
|
||||
var index = context.dataIndex;
|
||||
var value = context.dataset.data[index];
|
||||
return value <= 0 ? 0 : 5;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
mode: "index",
|
||||
intersect: false,
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {},
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
ticks: {
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: false,
|
||||
labelString: "Seconds",
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
id: "wpm",
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Words per Minute",
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
gridLines: {
|
||||
display: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "raw",
|
||||
display: false,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Raw Words per Minute",
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "error",
|
||||
display: true,
|
||||
position: "right",
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Errors",
|
||||
},
|
||||
ticks: {
|
||||
precision: 0,
|
||||
beginAtZero: true,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
annotation: {
|
||||
annotations: [
|
||||
{
|
||||
enabled: false,
|
||||
type: "line",
|
||||
mode: "horizontal",
|
||||
scaleID: "wpm",
|
||||
value: "-30",
|
||||
borderColor: "red",
|
||||
borderWidth: 1,
|
||||
borderDash: [2, 2],
|
||||
label: {
|
||||
// Background color of label, default below
|
||||
backgroundColor: "blue",
|
||||
|
||||
// Font size of text, inherits from global
|
||||
fontSize: 11,
|
||||
|
||||
// Font style of text, default below
|
||||
fontStyle: "normal",
|
||||
|
||||
// Font color of text, default below
|
||||
fontColor: "#fff",
|
||||
|
||||
// Padding of label to add left/right, default below
|
||||
xPadding: 6,
|
||||
|
||||
// Padding of label to add top/bottom, default below
|
||||
yPadding: 6,
|
||||
|
||||
// Radius of label rectangle, default below
|
||||
cornerRadius: 3,
|
||||
|
||||
// Anchor position of label on line, can be one of: top, bottom, left, right, center. Default below.
|
||||
position: "center",
|
||||
|
||||
// Whether the label is enabled and should be displayed
|
||||
enabled: true,
|
||||
|
||||
// Text to display in label - default is null. Provide an array to display values on a new line
|
||||
content: "PB",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Chart.prototype.updateColors = function () {
|
||||
updateColors(this);
|
||||
};
|
||||
|
||||
export function updateColors(chart) {
|
||||
if (ThemeColors.main == "") {
|
||||
ThemeColors.update();
|
||||
}
|
||||
chart.data.datasets[0].borderColor = ThemeColors.main;
|
||||
chart.data.datasets[0].pointBackgroundColor = ThemeColors.main;
|
||||
chart.data.datasets[1].borderColor = ThemeColors.sub;
|
||||
chart.data.datasets[1].pointBackgroundColor = ThemeColors.sub;
|
||||
|
||||
try {
|
||||
chart.options.scales.xAxes[0].ticks.minor.fontColor = ThemeColors.sub;
|
||||
chart.options.scales.xAxes[0].scaleLabel.fontColor = ThemeColors.sub;
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
chart.options.scales.yAxes[0].ticks.minor.fontColor = ThemeColors.sub;
|
||||
chart.options.scales.yAxes[0].scaleLabel.fontColor = ThemeColors.sub;
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
chart.options.scales.yAxes[2].ticks.minor.fontColor = ThemeColors.sub;
|
||||
chart.options.scales.yAxes[2].scaleLabel.fontColor = ThemeColors.sub;
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
chart.options.scales.yAxes[2].ticks.minor.fontColor = ThemeColors.sub;
|
||||
chart.options.scales.yAxes[2].scaleLabel.fontColor = ThemeColors.sub;
|
||||
} catch {}
|
||||
chart.update();
|
||||
}
|
||||
|
||||
export function updateAllChartColors() {
|
||||
ThemeColors.update();
|
||||
accountHistory.updateColors();
|
||||
result.updateColors();
|
||||
accountActivity.updateColors();
|
||||
miniResult.updateColors();
|
||||
}
|
|
@ -39,3 +39,4 @@ import * as ShiftTracker from "./shift-tracker";
|
|||
import * as TestStats from "./test-stats";
|
||||
import * as ThemeColors from "./theme-colors";
|
||||
import * as OutOfFocus from "./out-of-focus";
|
||||
import * as ChartController from "./chart-controller";
|
||||
|
|
213
src/js/script.js
213
src/js/script.js
|
@ -1705,7 +1705,7 @@ function showResult(difficultyFailed = false) {
|
|||
let afkseconds = TestStats.calculateAfkSeconds();
|
||||
let afkSecondsPercent = Misc.roundTo2((afkseconds / testtime) * 100);
|
||||
|
||||
wpmOverTimeChart.options.annotation.annotations = [];
|
||||
ChartController.result.options.annotation.annotations = [];
|
||||
|
||||
$("#result #resultWordsHistory").addClass("hidden");
|
||||
|
||||
|
@ -1856,24 +1856,9 @@ function showResult(difficultyFailed = false) {
|
|||
}
|
||||
}
|
||||
|
||||
if (ThemeColors.main == "") {
|
||||
ThemeColors.update();
|
||||
}
|
||||
ChartController.result.updateColors();
|
||||
|
||||
wpmOverTimeChart.options.scales.xAxes[0].ticks.minor.fontColor =
|
||||
ThemeColors.sub;
|
||||
wpmOverTimeChart.options.scales.xAxes[0].scaleLabel.fontColor =
|
||||
ThemeColors.sub;
|
||||
wpmOverTimeChart.options.scales.yAxes[0].ticks.minor.fontColor =
|
||||
ThemeColors.sub;
|
||||
wpmOverTimeChart.options.scales.yAxes[2].ticks.minor.fontColor =
|
||||
ThemeColors.sub;
|
||||
wpmOverTimeChart.options.scales.yAxes[0].scaleLabel.fontColor =
|
||||
ThemeColors.sub;
|
||||
wpmOverTimeChart.options.scales.yAxes[2].scaleLabel.fontColor =
|
||||
ThemeColors.sub;
|
||||
|
||||
wpmOverTimeChart.data.labels = labels;
|
||||
ChartController.result.data.labels = labels;
|
||||
|
||||
let rawWpmPerSecondRaw = TestStats.keypressPerSecond.map((f) =>
|
||||
Math.round((f.count / 5) * 60)
|
||||
|
@ -1914,26 +1899,21 @@ function showResult(difficultyFailed = false) {
|
|||
);
|
||||
}
|
||||
|
||||
wpmOverTimeChart.data.datasets[0].borderColor = ThemeColors.main;
|
||||
wpmOverTimeChart.data.datasets[0].pointBackgroundColor = ThemeColors.main;
|
||||
wpmOverTimeChart.data.datasets[0].data = TestStats.wpmHistory;
|
||||
wpmOverTimeChart.data.datasets[1].borderColor = ThemeColors.sub;
|
||||
wpmOverTimeChart.data.datasets[1].pointBackgroundColor = ThemeColors.sub;
|
||||
wpmOverTimeChart.data.datasets[1].data = rawWpmPerSecond;
|
||||
ChartController.result.data.datasets[1].data = rawWpmPerSecond;
|
||||
|
||||
let maxChartVal = Math.max(
|
||||
...[Math.max(...rawWpmPerSecond), Math.max(...TestStats.wpmHistory)]
|
||||
);
|
||||
if (!config.startGraphsAtZero) {
|
||||
wpmOverTimeChart.options.scales.yAxes[0].ticks.min = Math.min(
|
||||
ChartController.result.options.scales.yAxes[0].ticks.min = Math.min(
|
||||
...TestStats.wpmHistory
|
||||
);
|
||||
wpmOverTimeChart.options.scales.yAxes[1].ticks.min = Math.min(
|
||||
ChartController.result.options.scales.yAxes[1].ticks.min = Math.min(
|
||||
...TestStats.wpmHistory
|
||||
);
|
||||
} else {
|
||||
wpmOverTimeChart.options.scales.yAxes[0].ticks.min = 0;
|
||||
wpmOverTimeChart.options.scales.yAxes[1].ticks.min = 0;
|
||||
ChartController.result.options.scales.yAxes[0].ticks.min = 0;
|
||||
ChartController.result.options.scales.yAxes[1].ticks.min = 0;
|
||||
}
|
||||
|
||||
// let errorsNoZero = [];
|
||||
|
@ -1950,7 +1930,7 @@ function showResult(difficultyFailed = false) {
|
|||
errorsArray.push(TestStats.keypressPerSecond[i].errors);
|
||||
}
|
||||
|
||||
wpmOverTimeChart.data.datasets[2].data = errorsArray;
|
||||
ChartController.result.data.datasets[2].data = errorsArray;
|
||||
|
||||
let kps = TestStats.keypressPerSecond.slice(
|
||||
Math.max(TestStats.keypressPerSecond.length - 5, 0)
|
||||
|
@ -2133,7 +2113,7 @@ function showResult(difficultyFailed = false) {
|
|||
}
|
||||
}
|
||||
if (lpb > 0) {
|
||||
wpmOverTimeChart.options.annotation.annotations.push({
|
||||
ChartController.result.options.annotation.annotations.push({
|
||||
enabled: false,
|
||||
type: "line",
|
||||
mode: "horizontal",
|
||||
|
@ -2159,13 +2139,13 @@ function showResult(difficultyFailed = false) {
|
|||
if (maxChartVal >= lpb - 15 && maxChartVal <= lpb + 15) {
|
||||
maxChartVal = lpb + 15;
|
||||
}
|
||||
wpmOverTimeChart.options.scales.yAxes[0].ticks.max = Math.round(
|
||||
ChartController.result.options.scales.yAxes[0].ticks.max = Math.round(
|
||||
maxChartVal
|
||||
);
|
||||
wpmOverTimeChart.options.scales.yAxes[1].ticks.max = Math.round(
|
||||
ChartController.result.options.scales.yAxes[1].ticks.max = Math.round(
|
||||
maxChartVal
|
||||
);
|
||||
wpmOverTimeChart.update({ duration: 0 });
|
||||
ChartController.result.update({ duration: 0 });
|
||||
}
|
||||
if (config.mode === "time" && (mode2 === 15 || mode2 === 60)) {
|
||||
$("#result .stats .leaderboards").removeClass("hidden");
|
||||
|
@ -2215,7 +2195,7 @@ function showResult(difficultyFailed = false) {
|
|||
);
|
||||
// console.log("new pb for tag " + tag.name);
|
||||
} else {
|
||||
wpmOverTimeChart.options.annotation.annotations.push({
|
||||
ChartController.result.options.annotation.annotations.push({
|
||||
enabled: false,
|
||||
type: "line",
|
||||
mode: "horizontal",
|
||||
|
@ -2650,15 +2630,15 @@ function showResult(difficultyFailed = false) {
|
|||
$("#result .stats .source").addClass("hidden");
|
||||
}
|
||||
|
||||
wpmOverTimeChart.options.scales.yAxes[0].ticks.max = maxChartVal;
|
||||
wpmOverTimeChart.options.scales.yAxes[1].ticks.max = maxChartVal;
|
||||
ChartController.result.options.scales.yAxes[0].ticks.max = maxChartVal;
|
||||
ChartController.result.options.scales.yAxes[1].ticks.max = maxChartVal;
|
||||
|
||||
wpmOverTimeChart.update({ duration: 0 });
|
||||
wpmOverTimeChart.resize();
|
||||
ChartController.result.update({ duration: 0 });
|
||||
ChartController.result.resize();
|
||||
swapElements($("#typingTest"), $("#result"), 250, () => {
|
||||
resultCalculating = false;
|
||||
$("#words").empty();
|
||||
wpmOverTimeChart.resize();
|
||||
ChartController.result.resize();
|
||||
if (config.alwaysShowWordsHistory) {
|
||||
toggleResultWordsDisplay();
|
||||
}
|
||||
|
@ -2822,6 +2802,7 @@ function restartTest(withSameWordset = false, nosave = false, event) {
|
|||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if (testRestarting || resultCalculating) {
|
||||
try {
|
||||
event.preventDefault();
|
||||
|
@ -3032,7 +3013,7 @@ function restartTest(withSameWordset = false, nosave = false, event) {
|
|||
hideCrown();
|
||||
clearTimeout(timer);
|
||||
if ($("#commandLineWrapper").hasClass("hidden")) focusWords();
|
||||
wpmOverTimeChart.update();
|
||||
ChartController.result.update();
|
||||
updateTestModesNotice();
|
||||
pageTransition = false;
|
||||
// console.log(TestStats.incompleteSeconds);
|
||||
|
@ -5978,155 +5959,3 @@ async function setupChallenge(challengeName) {
|
|||
Notifications.add("Something went wrong: " + e, -1);
|
||||
}
|
||||
}
|
||||
|
||||
let ctx = $("#wpmChart");
|
||||
let wpmOverTimeChart = new Chart(ctx, {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
label: "wpm",
|
||||
data: [],
|
||||
borderColor: "rgba(125, 125, 125, 1)",
|
||||
borderWidth: 2,
|
||||
yAxisID: "wpm",
|
||||
order: 2,
|
||||
radius: 2,
|
||||
},
|
||||
{
|
||||
label: "raw",
|
||||
data: [],
|
||||
borderColor: "rgba(125, 125, 125, 1)",
|
||||
borderWidth: 2,
|
||||
yAxisID: "raw",
|
||||
order: 3,
|
||||
radius: 2,
|
||||
},
|
||||
{
|
||||
label: "errors",
|
||||
data: [],
|
||||
borderColor: "rgba(255, 125, 125, 1)",
|
||||
pointBackgroundColor: "rgba(255, 125, 125, 1)",
|
||||
borderWidth: 2,
|
||||
order: 1,
|
||||
yAxisID: "error",
|
||||
maxBarThickness: 10,
|
||||
type: "scatter",
|
||||
pointStyle: "crossRot",
|
||||
radius: function (context) {
|
||||
var index = context.dataIndex;
|
||||
var value = context.dataset.data[index];
|
||||
return value <= 0 ? 0 : 3;
|
||||
},
|
||||
pointHoverRadius: function (context) {
|
||||
var index = context.dataIndex;
|
||||
var value = context.dataset.data[index];
|
||||
return value <= 0 ? 0 : 5;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
mode: "index",
|
||||
intersect: false,
|
||||
callbacks: {
|
||||
afterLabel: function (ti) {
|
||||
try {
|
||||
$(".wordInputAfter").remove();
|
||||
|
||||
let wordsToHighlight =
|
||||
TestStats.keypressPerSecond[parseInt(ti.xLabel) - 1].words;
|
||||
|
||||
let unique = [...new Set(wordsToHighlight)];
|
||||
unique.forEach((wordIndex) => {
|
||||
let wordEl = $($("#resultWordsHistory .words .word")[wordIndex]);
|
||||
let input = wordEl.attr("input");
|
||||
if (input != undefined)
|
||||
wordEl.append(`<div class="wordInputAfter">${input}</div>`);
|
||||
});
|
||||
} catch {}
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {},
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
ticks: {
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: false,
|
||||
labelString: "Seconds",
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
id: "wpm",
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Words per Minute",
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
gridLines: {
|
||||
display: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "raw",
|
||||
display: false,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Raw Words per Minute",
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "error",
|
||||
display: true,
|
||||
position: "right",
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Errors",
|
||||
},
|
||||
ticks: {
|
||||
precision: 0,
|
||||
beginAtZero: true,
|
||||
autoSkip: true,
|
||||
autoSkipPadding: 40,
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
annotation: {
|
||||
annotations: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1013,8 +1013,7 @@ $(".pageSettings #loadCustomColorsFromPreset").click((e) => {
|
|||
});
|
||||
|
||||
setTimeout(() => {
|
||||
ThemeColors.update();
|
||||
updateChartColors();
|
||||
ChartController.updateAllChartColors();
|
||||
|
||||
colorVars.forEach((colorName) => {
|
||||
let color;
|
||||
|
|
|
@ -261,20 +261,21 @@ function setBlindMode(blind, nosave) {
|
|||
}
|
||||
|
||||
function updateChartAccuracy() {
|
||||
resultHistoryChart.data.datasets[1].hidden = !config.chartAccuracy;
|
||||
resultHistoryChart.options.scales.yAxes[1].display = config.chartAccuracy;
|
||||
resultHistoryChart.update();
|
||||
ChartController.accountHistory.data.datasets[1].hidden = !config.chartAccuracy;
|
||||
ChartController.accountHistory.options.scales.yAxes[1].display =
|
||||
config.chartAccuracy;
|
||||
ChartController.accountHistory.update();
|
||||
}
|
||||
|
||||
function updateChartStyle() {
|
||||
if (config.chartStyle == "scatter") {
|
||||
resultHistoryChart.data.datasets[0].showLine = false;
|
||||
resultHistoryChart.data.datasets[1].showLine = false;
|
||||
ChartController.accountHistory.data.datasets[0].showLine = false;
|
||||
ChartController.accountHistory.data.datasets[1].showLine = false;
|
||||
} else {
|
||||
resultHistoryChart.data.datasets[0].showLine = true;
|
||||
resultHistoryChart.data.datasets[1].showLine = true;
|
||||
ChartController.accountHistory.data.datasets[0].showLine = true;
|
||||
ChartController.accountHistory.data.datasets[1].showLine = true;
|
||||
}
|
||||
resultHistoryChart.update();
|
||||
ChartController.accountHistory.update();
|
||||
}
|
||||
|
||||
function toggleChartAccuracy() {
|
||||
|
@ -1060,66 +1061,6 @@ function setIndicateTypos(it, nosave) {
|
|||
if (!nosave) saveConfigToCookie();
|
||||
}
|
||||
|
||||
function updateChartColors() {
|
||||
hoverChart.options.scales.xAxes[0].ticks.minor.fontColor = ThemeColors.sub;
|
||||
hoverChart.options.scales.xAxes[0].scaleLabel.fontColor = ThemeColors.sub;
|
||||
hoverChart.options.scales.yAxes[0].ticks.minor.fontColor = ThemeColors.sub;
|
||||
hoverChart.options.scales.yAxes[2].ticks.minor.fontColor = ThemeColors.sub;
|
||||
hoverChart.options.scales.yAxes[0].scaleLabel.fontColor = ThemeColors.sub;
|
||||
hoverChart.options.scales.yAxes[2].scaleLabel.fontColor = ThemeColors.sub;
|
||||
|
||||
hoverChart.data.datasets[0].borderColor = ThemeColors.main;
|
||||
hoverChart.data.datasets[0].pointBackgroundColor = ThemeColors.main;
|
||||
hoverChart.data.datasets[1].borderColor = ThemeColors.sub;
|
||||
hoverChart.data.datasets[1].pointBackgroundColor = ThemeColors.sub;
|
||||
|
||||
hoverChart.options.annotation.annotations[0].borderColor = ThemeColors.sub;
|
||||
hoverChart.options.annotation.annotations[0].label.backgroundColor =
|
||||
ThemeColors.sub;
|
||||
hoverChart.options.annotation.annotations[0].label.fontColor = ThemeColors.bg;
|
||||
|
||||
activityChart.options.legend.labels.fontColor = ThemeColors.sub;
|
||||
|
||||
activityChart.options.scales.xAxes[0].ticks.minor.fontColor = ThemeColors.sub;
|
||||
activityChart.options.scales.yAxes[0].ticks.minor.fontColor = ThemeColors.sub;
|
||||
activityChart.options.scales.yAxes[0].scaleLabel.fontColor = ThemeColors.sub;
|
||||
activityChart.data.datasets[0].borderColor = ThemeColors.main;
|
||||
activityChart.data.datasets[0].backgroundColor = ThemeColors.main;
|
||||
|
||||
activityChart.data.datasets[0].trendlineLinear.style = ThemeColors.sub;
|
||||
|
||||
activityChart.options.scales.yAxes[1].ticks.minor.fontColor = ThemeColors.sub;
|
||||
activityChart.options.scales.yAxes[1].scaleLabel.fontColor = ThemeColors.sub;
|
||||
activityChart.data.datasets[1].borderColor = ThemeColors.sub;
|
||||
|
||||
activityChart.options.legend.labels.fontColor = ThemeColors.sub;
|
||||
|
||||
resultHistoryChart.options.scales.xAxes[0].ticks.minor.fontColor =
|
||||
ThemeColors.sub;
|
||||
resultHistoryChart.options.scales.yAxes[0].ticks.minor.fontColor =
|
||||
ThemeColors.sub;
|
||||
resultHistoryChart.options.scales.yAxes[0].scaleLabel.fontColor =
|
||||
ThemeColors.sub;
|
||||
resultHistoryChart.options.scales.yAxes[1].ticks.minor.fontColor =
|
||||
ThemeColors.sub;
|
||||
resultHistoryChart.options.scales.yAxes[1].scaleLabel.fontColor =
|
||||
ThemeColors.sub;
|
||||
resultHistoryChart.data.datasets[0].borderColor = ThemeColors.main;
|
||||
resultHistoryChart.data.datasets[1].borderColor = ThemeColors.sub;
|
||||
|
||||
resultHistoryChart.options.legend.labels.fontColor = ThemeColors.sub;
|
||||
resultHistoryChart.data.datasets[0].trendlineLinear.style = ThemeColors.sub;
|
||||
wpmOverTimeChart.data.datasets[0].borderColor = ThemeColors.main;
|
||||
wpmOverTimeChart.data.datasets[0].pointBackgroundColor = ThemeColors.main;
|
||||
wpmOverTimeChart.data.datasets[1].borderColor = ThemeColors.sub;
|
||||
wpmOverTimeChart.data.datasets[1].pointBackgroundColor = ThemeColors.sub;
|
||||
|
||||
hoverChart.update();
|
||||
wpmOverTimeChart.update();
|
||||
resultHistoryChart.update();
|
||||
activityChart.update();
|
||||
}
|
||||
|
||||
let isPreviewingTheme = false;
|
||||
function previewTheme(name, setIsPreviewingVar = true) {
|
||||
if (
|
||||
|
@ -1133,8 +1074,7 @@ function previewTheme(name, setIsPreviewingVar = true) {
|
|||
clearCustomTheme();
|
||||
$("#currentTheme").attr("href", `themes/${name}.css`);
|
||||
setTimeout(() => {
|
||||
ThemeColors.update();
|
||||
updateChartColors();
|
||||
ChartController.updateAllChartColors();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
|
@ -1166,8 +1106,7 @@ function setTheme(name, nosave) {
|
|||
// applyCustomThemeColors();
|
||||
setTimeout(() => {
|
||||
$(".keymap-key").attr("style", "");
|
||||
ThemeColors.update();
|
||||
updateChartColors();
|
||||
ChartController.updateAllChartColors();
|
||||
|
||||
$("#metaThemeColor").attr("content", ThemeColors.main);
|
||||
}, 500);
|
||||
|
@ -1232,8 +1171,7 @@ function applyCustomThemeColors() {
|
|||
clearCustomTheme();
|
||||
}
|
||||
setTimeout(() => {
|
||||
ThemeColors.update();
|
||||
updateChartColors();
|
||||
ChartController.updateAllChartColors();
|
||||
updateFavicon(32, 14);
|
||||
$(".keymap-key").attr("style", "");
|
||||
}, 500);
|
||||
|
|
|
@ -110,7 +110,7 @@ body {
|
|||
background: var(--bg-color);
|
||||
font-family: var(--font);
|
||||
color: var(--main-color);
|
||||
// overflow-x: hidden;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
html {
|
||||
|
@ -2878,17 +2878,7 @@ key {
|
|||
}
|
||||
}
|
||||
|
||||
// .hoverChartWrapper {
|
||||
// z-index: 999;
|
||||
// display: none;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
// background: rgba(0, 0, 0, 0.25);
|
||||
// position: fixed;
|
||||
// left: 0;
|
||||
// top: 0;
|
||||
// }
|
||||
.hoverChartWrapper {
|
||||
.miniResultChartWrapper {
|
||||
// pointer-events: none;
|
||||
z-index: 999;
|
||||
display: none;
|
||||
|
@ -2901,7 +2891,7 @@ key {
|
|||
// box-shadow: 0 0 1rem rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.hoverChartBg {
|
||||
.miniResultChartBg {
|
||||
display: none;
|
||||
z-index: 998;
|
||||
width: 100%;
|
||||
|
@ -3061,7 +3051,7 @@ key {
|
|||
td.infoIcons span {
|
||||
margin: 0 0.1rem;
|
||||
}
|
||||
.hoverChartButton {
|
||||
.miniResultChartButton {
|
||||
opacity: 0.25;
|
||||
transition: 0.25s;
|
||||
cursor: pointer;
|
||||
|
|
|
@ -151,26 +151,28 @@
|
|||
<div id="quoteSearchPopupWrapper" class="hidden">
|
||||
<div id="quoteSearchPopup" mode="">
|
||||
<div class="title">Quote Search</div>
|
||||
<input id="searchBox" class="searchBox" type="text" maxLength="200" autocomplete="off"/>
|
||||
<input
|
||||
id="searchBox"
|
||||
class="searchBox"
|
||||
type="text"
|
||||
maxlength="200"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<table class="searchResultTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="alignRight" width="5%">#</td>
|
||||
<td class="alignRight" width="10%">length</td>
|
||||
<td>
|
||||
text
|
||||
</td>
|
||||
<td width="30%">
|
||||
source
|
||||
</td>
|
||||
<td>text</td>
|
||||
<td width="30%">source</td>
|
||||
<td width="1%"></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="searchResultList">
|
||||
|
||||
</tbody>
|
||||
<tbody id="searchResultList"></tbody>
|
||||
</table>
|
||||
<div id="extraResults" class="quoteSearchResults">No search results</div>
|
||||
<div id="extraResults" class="quoteSearchResults">
|
||||
No search results
|
||||
</div>
|
||||
<div class="button">ok</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3268,10 +3270,10 @@
|
|||
<i class="fas fa-fw fa-spin fa-circle-notch"></i>
|
||||
</div>
|
||||
<div class="content hidden">
|
||||
<div class="hoverChartWrapper">
|
||||
<canvas id="hoverChart"></canvas>
|
||||
<div class="miniResultChartWrapper">
|
||||
<canvas id="miniResultChart"></canvas>
|
||||
</div>
|
||||
<div class="hoverChartBg"></div>
|
||||
<div class="miniResultChartBg"></div>
|
||||
<div class="triplegroup">
|
||||
<div class="group globalTestsStarted">
|
||||
<div class="title">tests started</div>
|
||||
|
@ -3494,7 +3496,7 @@
|
|||
</div> -->
|
||||
<div class="above"></div>
|
||||
<div class="chart" style="height: 400px">
|
||||
<canvas id="resultHistoryChart"></canvas>
|
||||
<canvas id="accountHistoryChart"></canvas>
|
||||
</div>
|
||||
<div class="below">
|
||||
<div class="text"></div>
|
||||
|
@ -3512,7 +3514,7 @@
|
|||
</div>
|
||||
<div class="group dailyActivityChart">
|
||||
<div class="chart" style="height: 200px">
|
||||
<canvas id="activityChart"></canvas>
|
||||
<canvas id="accountActivityChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="triplegroup stats">
|
||||
|
|
Loading…
Add table
Reference in a new issue