Added comments

main
František Špaček 2 years ago
parent b602df13c4
commit 2b3b9e3e3e

5
.gitignore vendored

@ -18,3 +18,8 @@
.phpunit.result.cache
/phpunit.xml
###< symfony/phpunit-bridge ###
###> symfony/asset-mapper ###
/public/assets/
/assets/vendor/
###< symfony/asset-mapper ###

@ -0,0 +1,9 @@
/*
* Welcome to your app's main JavaScript file!
*
* This file will be included onto the page via the importmap() Twig function,
* which should already be in your base.html.twig.
*/
import './styles/app.css';
//console.log('This log comes from assets/app.js - welcome to AssetMapper! 🎉');

@ -0,0 +1,2 @@
body {
}

@ -20,6 +20,7 @@
"phpstan/phpdoc-parser": "^1.27",
"symfony/apache-pack": "^1.0",
"symfony/asset": "7.0.*",
"symfony/asset-mapper": "7.0.*",
"symfony/config": "6.4.*",
"symfony/console": "7.0.*",
"symfony/dependency-injection": "^5.4|^6.0",
@ -89,7 +90,8 @@
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
"assets:install %PUBLIC_DIR%": "symfony-cmd",
"importmap:install": "symfony-cmd"
},
"post-install-cmd": [
"@auto-scripts"

161
composer.lock generated

@ -4,8 +4,89 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "551cd15cfa0d95c7d21f0bc50e237c4b",
"content-hash": "48698e7028e7374e541d2dae6c243570",
"packages": [
{
"name": "composer/semver",
"version": "3.4.0",
"source": {
"type": "git",
"url": "https://github.com/composer/semver.git",
"reference": "35e8d0af4486141bc745f23a29cc2091eb624a32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32",
"reference": "35e8d0af4486141bc745f23a29cc2091eb624a32",
"shasum": ""
},
"require": {
"php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
"phpstan/phpstan": "^1.4",
"symfony/phpunit-bridge": "^4.2 || ^5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Composer\\Semver\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nils Adermann",
"email": "naderman@naderman.de",
"homepage": "http://www.naderman.de"
},
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
},
{
"name": "Rob Bast",
"email": "rob.bast@gmail.com",
"homepage": "http://robbast.nl"
}
],
"description": "Semver library that offers utilities, version constraint parsing and validation.",
"keywords": [
"semantic",
"semver",
"validation",
"versioning"
],
"support": {
"irc": "ircs://irc.libera.chat:6697/composer",
"issues": "https://github.com/composer/semver/issues",
"source": "https://github.com/composer/semver/tree/3.4.0"
},
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"time": "2023-08-31T09:50:34+00:00"
},
{
"name": "doctrine/cache",
"version": "2.2.0",
@ -2844,6 +2925,84 @@
],
"time": "2024-01-23T15:02:46+00:00"
},
{
"name": "symfony/asset-mapper",
"version": "v7.0.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/asset-mapper.git",
"reference": "07133d369eb9f644985584d0877d294c8847a4f0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/asset-mapper/zipball/07133d369eb9f644985584d0877d294c8847a4f0",
"reference": "07133d369eb9f644985584d0877d294c8847a4f0",
"shasum": ""
},
"require": {
"composer/semver": "^3.0",
"php": ">=8.2",
"symfony/filesystem": "^6.4|^7.0",
"symfony/http-client": "^6.4|^7.0"
},
"conflict": {
"symfony/framework-bundle": "<6.4"
},
"require-dev": {
"symfony/asset": "^6.4|^7.0",
"symfony/browser-kit": "^6.4|^7.0",
"symfony/console": "^6.4|^7.0",
"symfony/event-dispatcher-contracts": "^3.0",
"symfony/finder": "^6.4|^7.0",
"symfony/framework-bundle": "^6.4|^7.0",
"symfony/http-foundation": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/web-link": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\AssetMapper\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Maps directories of assets & makes them available in a public directory with versioned filenames.",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/asset-mapper/tree/v7.0.5"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-03-04T12:47:58+00:00"
},
{
"name": "symfony/cache",
"version": "v7.0.6",

@ -0,0 +1,5 @@
framework:
asset_mapper:
# The paths to make available to the asset mapper.
paths:
- assets/

@ -0,0 +1,19 @@
<?php
/**
* Returns the importmap for this application.
*
* - "path" is a path inside the asset mapper system. Use the
* "debug:asset-map" command to see the full list of paths.
*
* - "entrypoint" (JavaScript only) set to true for any module that will
* be used as an "entrypoint" (and passed to the importmap() Twig function).
*
* The "importmap:require" command can be used to add new entries to this file.
*/
return [
'app' => [
'path' => './assets/app.js',
'entrypoint' => true,
],
];

@ -6,6 +6,8 @@ class ChartLoader {
this.parent = parent
this.legend = legend
this.dataDiv = dataDiv
this.clickedShapeIndex = null
this.selectedShapeIndex = null
}
//Načtení dat
@ -18,7 +20,7 @@ class ChartLoader {
})
.then(response => response.json())
.then(data => {
let metadata = data.metadata;
let metadata = data.metadata
metadata.custom_x_values = ""
let table = data.table
@ -31,18 +33,8 @@ class ChartLoader {
})
}
addListener(newChart) {
//Click
document.addEventListener('mousemove', (e) => {
const pos = {
x: e.clientX,
y: e.clientY
}
console.log(pos)
let ctx = this.detectionCanvas.getContext('2d');
getShapeIndex(canvas, pos) {
let ctx = canvas.getContext('2d')
// Get neigboring pixels
let imageData = Array.from(ctx.getImageData(pos.x - 2, pos.y - 2, 5, 5).data)
@ -59,34 +51,49 @@ class ChartLoader {
}
}
function mode(array)
{
if(array.length == 0)
return null;
var modeMap = {};
var maxEl = array[0], maxCount = 1;
for(var i = 0; i < array.length; i++)
{
var el = array[i];
if(modeMap[el] == null)
modeMap[el] = 1;
function mode(array) {
if (array.length == 0)
return null
let modeMap = {}
let maxEl = array[0], maxCount = 1
for (let i = 0; i < array.length; i++) {
let el = array[i]
if (modeMap[el] == null)
modeMap[el] = 1
else
modeMap[el]++;
if(modeMap[el] > maxCount)
{
maxEl = el;
maxCount = modeMap[el];
modeMap[el]++
if (modeMap[el] > maxCount) {
maxEl = el
maxCount = modeMap[el]
}
}
return maxEl;
return maxEl
}
// To avoid edge cases because of anti aliasing
let mostCommonIndex = mode(indexes)
return mostCommonIndex
}
if (mostCommonIndex !== null) {
let obj = newChart.objects[mostCommonIndex]
getEffectObjects() {
}
addListener(newChart) {
//Click
document.addEventListener('mousemove', (e) => {
const pos = {
x: e.clientX,
y: e.clientY
}
let shapeIndex = this.selectedShapeIndex
if (shapeIndex === null)
shapeIndex = this.getShapeIndex(this.detectionCanvas, pos)
if (shapeIndex !== null) {
let obj = newChart.objects[shapeIndex]
// Effect
let effectCtx = this.effectCanvas.getContext("2d")
@ -117,26 +124,54 @@ class ChartLoader {
}
})
document.addEventListener("mousedown", (e) => {
const pos = {
x: e.clientX,
y: e.clientY
}
this.clickedShapeIndex = this.getShapeIndex(this.detectionCanvas, pos)
})
document.addEventListener("mouseup", (e) => {
const pos = {
x: e.clientX,
y: e.clientY
}
if (this.clickedShapeIndex === null ||
this.clickedShapeIndex !== this.getShapeIndex(this.detectionCanvas, pos))
this.selectedShapeIndex = null
else
this.selectedShapeIndex = this.clickedShapeIndex
console.log(this.clickedShapeIndex + " " +
this.getShapeIndex(this.detectionCanvas, pos) + " " +
this.shapeSelected)
})
window.addEventListener("resize", e => {
//newChart.updateLegend(graphSettings.displayLegend, data)
newChart.resizeCanvas(this.parent, this.legend.offsetHeight)
newChart.draw()
this.addInteractivity()
})
}
async addInteractivity() {
setTimeout(() => {
console.time("2")
this.effectCanvas.width = this.canvas.width
this.effectCanvas.height = this.canvas.height
this.detectionCanvas.width = this.canvas.width
this.detectionCanvas.height = this.canvas.height
this.chart.drawDetectionMap(this.detectionCanvas.getContext("2d"))
console.timeEnd("2")
}, 0)
});
}
drawChart(graphSettings, data) {
this.chart = new Chart(this.canvas, data, graphSettings)
this.chart.updateLegend(graphSettings.displayLegend, this.legend)
this.chart.resizeCanvas(this.parent, this.legend.offsetHeight)
//Choose the correct graph
switch (graphSettings.type) {
@ -169,20 +204,12 @@ class ChartLoader {
break
}
console.time("1")
setTimeout(this.chart.updateLegend(graphSettings.displayLegend, this.legend, this), 0)
this.chart.resizeCanvas(this.parent, this.legend.offsetHeight)
this.chart.draw()
console.timeEnd("1")
setTimeout(() => {
console.time("2")
console.timeEnd("2")
this.effectCanvas.width = this.canvas.width
this.effectCanvas.height = this.canvas.height
this.detectionCanvas.width = this.canvas.width
this.detectionCanvas.height = this.canvas.height
this.chart.drawDetectionMap(this.detectionCanvas.getContext("2d"))
}, 0); {
}
this.addInteractivity()
this.addListener(this.chart)
}

@ -1,30 +1,49 @@
/**
* Area Chart
*/
class AreaChart extends PointChart {
/**
* Creates a new instance of the AreaChart class
*
* @param {HTMLCanvasElement} canvas - The canvas element to draw the chart on
* @param {Array<Object>} data - The data to visualize on the chart
* @param {Object} settings - The settings for the chart
*/
constructor(canvas, data, settings) {
super(canvas, data, settings)
}
/**
* Draws the area chart on the canvas.
*/
draw() {
// Clear the canvas and draw the axis
this.clear()
this.drawAxis()
// Iterate over each category in the data
this.data.forEach(categ => {
//Lines
// Begin a new path for drawing lines
this.ctx.beginPath()
this.ctx.lineJoin = "round"
this.ctx.strokeStyle = categ.color
let xmax = 0
let xmax = 0 // rightmost point (some lines can be shorter)
// Iterate over each value in the category
for (let i = 0; i < this.dataLen; i++) {
// Skip empty points
if (categ.values[i] === null) continue
// Calculate the x and y coordinates for the current value
let x = this.bounds.left + this.bounds.width / (this.dataLen - 1) * i
let y = (this.bounds.xAxis - categ.values[i] / this.extreme * this.scale)
let y = this.bounds.xAxis - categ.values[i] / this.extreme * this.scale
xmax = x
this.ctx.lineTo(x, y)
}
this.ctx.stroke()
// Complete the area by drawing lines to the x-axis and back to the starting point
this.ctx.lineTo(xmax, this.bounds.xAxis)
this.ctx.lineTo(this.bounds.left, this.bounds.xAxis)
this.ctx.globalAlpha = 0.5
@ -32,10 +51,11 @@ class AreaChart extends PointChart {
this.ctx.closePath()
this.ctx.fill()
// Reset global alpha for future drawings
this.ctx.globalAlpha = 1
})
//Points
// Draw points on the chart if required
if (this.settings.displayPoints)
this.data.forEach((categ, colId) => {this.drawPoints(categ.values, colId, categ.color)})
}

@ -1,38 +1,55 @@
/**
* Bar Chart
*/
class BarChart extends Chart {
/**
* Creates a new instance of the BarChart class
*
* @param {HTMLCanvasElement} canvas - The canvas element to draw the chart on
* @param {Array<Object>} data - The data to visualize on the chart
* @param {Object} settings - The settings for the chart
*/
constructor(canvas, data, settings) {
super(canvas, data, settings)
}
/**
* Draws the bar chart on the canvas.
*/
draw() {
this.ctx.shadowOffsetX = 15
this.ctx.shadowOffsetY = 15
this.ctx.shadowBlur = 4
// Calculate the number of bars/categories in the chart
let barCount = this.data.length
if (this.smallest < 0)
this.bounds.xAxis = this.bounds.bottom - (this.bounds.height / ((this.largest <= 0 ? 0 : Math.abs(this.largest)) + Math.abs(this.smallest)) * Math.abs(this.smallest))
// Draw the axis without displaying axis values
this.drawAxis(false)
// Divide the space into equal section
// Calculate section size
let size = this.bounds.width / this.dataLen
// Callculate inner size with margin
let innerSize = size * 0.8
// Width of each chart
let bar_width = innerSize * 0.7 / barCount
// Iterate over each data point to draw the bars
for (let i = 0; i < this.dataLen; i++) {
// Counter to determine the position of each bar within a data point
let num = 0
this.data.forEach((categ, colId) => {
// Value of the bar
let value = categ.values[i]
// The left position of the bar in section
let left = this.bounds.left + (size * (i + 0.15) + (innerSize * num / barCount))
// The height of the bar relative to the chart scale
let bar_height = value / this.extreme * this.scale
let top = (this.bounds.xAxis - categ.values[i] / this.extreme * this.scale)
// The top position of the bar
let top = this.bounds.xAxis - categ.values[i] / this.extreme * this.scale
//x value
// Draw x-axis labels
// Only for first category to avoid repeated drawings
if (num === 0 && this.settings.displayAxisValues) {
let text = (i + 1).toString()
/*if (this.settings.custom_x_values !== "")
text = this.settings.custom_x_values.split(';')[i]*/
this.ctx.beginPath()
this.ctx.font = "16px Arial"
@ -43,6 +60,7 @@ class BarChart extends Chart {
this.ctx.closePath()
}
// Set the fill color and draw the rectangle for the current bar
num++
this.ctx.fillStyle = categ.color
this.ctx.lineWidth = 0
@ -51,8 +69,5 @@ class BarChart extends Chart {
this.objects.push(new_object)
})
}
if (this.settings.displayTitle)
this.drawTitle()
}
}

@ -1,99 +1,43 @@
class Shape {
static globalIndex = 0
constructor(ctx, value, colId, x, y) {
this.ctx = ctx
this.value = value
this.colId = colId
this.x = x
this.y = y
this.index = Shape.globalIndex++
}
draw() { }
}
class Rectangle extends Shape {
constructor(ctx, value, colId, x, y, w, h) {
super(ctx, value, colId, x, y)
this.w = w
this.h = h
}
draw(ctx = this.ctx) {
ctx.beginPath()
ctx.rect(this.x, this.y, this.w, this.h)
ctx.fill()
//ctx.closePath()
}
}
class Circle extends Shape {
constructor(ctx, value, colId, x, y, r) {
super(ctx, value, colId, x, y)
this.r = r
}
draw(ctx = this.ctx) {
ctx.beginPath()
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI)
ctx.fill()
//ctx.closePath()
}
}
class PieSlice extends Circle {
constructor(ctx, value, colId, x, y, r, sAngle, eAngle) {
super(ctx, value, colId, x, y, r)
this.sAngle = sAngle
this.eAngle = eAngle
}
draw(ctx = this.ctx) {
ctx.beginPath()
ctx.moveTo(this.x, this.y)
ctx.arc(this.x, this.y, this.r, this.sAngle, this.eAngle)
ctx.lineTo(this.x, this.y)
ctx.fill()
//ctx.closePath()
}
}
class DonutSlice extends PieSlice {
constructor(ctx, value, colId, x, y, r, sAngle, eAngle, r2) {
super(ctx, value, colId, x, y, r, sAngle, eAngle)
this.r2 = r2
}
draw(ctx = this.ctx) {
ctx.beginPath()
// move to start of the slice using polar coordinates
ctx.moveTo(this.x + this.r2 * Math.cos(this.sAngle), this.y + this.r2 * Math.sin(this.sAngle))
ctx.arc(this.x, this.y, this.r, this.sAngle, this.eAngle, false)
ctx.arc(this.x, this.y, this.r2, this.eAngle, this.sAngle, true)
ctx.fill()
//ctx.closePath()
}
}
/**
* Find the largest value among the provided data points.
*
* @param {Array} data - An array of columns with associated values.
* @returns {number} - The largest value found in the data.
*/
function getLargest(data) {
// Initialize largest with the first value of the first category
let largest = +data[0].values[0]
// Iterate through each category(column)
data.forEach(categ => {
// Iterate through the values of each category
for (let i = 0; i < categ.values.length; i++)
// Convert the value to a number and compare with the current largest
if (+categ.values[i] > +largest)
// If the current value is larger, update the largest value
largest = +categ.values[i]
})
return largest
}
/**
* Find the smallest value among the provided data points.
*
* @param {Array} data - An array of columns with associated values.
* @returns {number} - The smallest value found in the data.
*/
function getSmallest(data) {
// Initialize smallest with the first value of the first category
let smallest = +data[0].values[0]
// Iterate through each category(column)
data.forEach(categ => {
// Iterate through the values of each category
for (let i = 0; i < categ.values.length; i++)
// Convert the value to a number and compare with the current smallest
if (+categ.values[i] < +smallest)
// If the current value is smaller, update the smallest value
smallest = +categ.values[i]
})
@ -102,32 +46,66 @@ function getSmallest(data) {
// Lighten or darken a hex color
function adjustColor(color, amount) {
return '#' + color.replace(/^#/, '').replace(/../g, color => ('0'+Math.min(255, Math.max(0, parseInt(color, 16) + amount)).toString(16)).substr(-2));
return '#' + color.replace(/^#/, '').replace(/../g, color => ('0' + Math.min(255, Math.max(0, parseInt(color, 16) + amount)).toString(16)).substr(-2))
}
/**
* Class representing a generic chart
*/
class Chart {
/**
* Creates a new instance of the Chart class
*
* @param {HTMLCanvasElement} canvas - The canvas element to draw the chart on
* @param {Array<Object>} data - The data to visualize on the chart
* @param {Object} settings - The settings for the chart
*/
constructor(canvas, data, settings) {
this.data = data
this.settings = settings
this.canvas = canvas
this.ctx = canvas.getContext("2d")
this.largest = getLargest(data)
this.smallest = getSmallest(data)
this.dataLen = data[0].values.length
this.data = data // The data to visualize on the chart
this.settings = settings // The settings for the chart
this.canvas = canvas // The canvas element to draw the chart on
this.ctx = canvas.getContext("2d") // The 2D drawing context for the canvas
// Calculate the largest and smallest values from the data
this.largest = getLargest(data) // The largest value in the data
this.smallest = getSmallest(data) // The smallest value in the data
// Find the largest length of values from all categories
this.dataLen = Math.max( // get max index
...data.map(category => // from each category
Object.keys(category.values)[Object.keys(category.values).length - 1] // gets last(largest) key
)) + 1 // indexes start at zero, so the length is +1
// Update the bounds of the chart based on the canvas size and margins
this.updateBounds()
// Initialize an empty array to store chart objects (shapes)
this.objects = []
}
/**
* Update the bounds based on the canvas size and margin settings.
*/
updateBounds() {
// Calculate the bounds using the canvas and margin settings
this.bounds = this.getBounds(this.canvas, this.settings.margin)
// Calculate the scale and extreme values based on the largest and smallest data points
this.scale = this.bounds.height
- (this.largest >= 0 ? (this.bounds.bottom - this.bounds.xAxis) : 0)
this.extreme = this.largest <= 0 ? Math.abs(this.smallest) : Math.abs(this.largest)
}
/**
* Calculate the bounds of the graph area based on the canvas size and margin.
* @param {HTMLCanvasElement} canvas - The canvas element.
* @param {number} graphMargin - The margin around the graph area.
* @returns {Object} - The calculated bounds.
*/
getBounds(canvas, graphMargin) {
// Check if settings are valid
if (isNaN(graphMargin))
throw new Error("Invalid margin: " + graphMargin)
let result = {
top: graphMargin,
bottom: canvas.height - graphMargin,
@ -137,176 +115,335 @@ class Chart {
width: canvas.width - 2 * graphMargin,
}
// Calculate the xAxis position based on the smallest and largest data points
if (this.smallest >= 0)
// If the smallest data point is non-negative, set the xAxis to the bottom border
result.xAxis = result.bottom
else if (this.largest <= 0)
// If the largest data point is non-positive, set the xAxis to the top margin
result.xAxis = result.top
else
// If the smallest and largest data points have different signs,
// calculate the position of the xAxis to ensure proportional distribution
// between the two points on the graph area.
result.xAxis = result.bottom
- result.height / ((Math.abs(this.largest)) + Math.abs(this.smallest)) * Math.abs(this.smallest)
return result
}
/**
* Draw the title on the canvas.
*/
drawTitle() {
// Check if settings are valid
if (typeof this.settings.title !== "string" && typeof this.settings.title !== "number")
throw new Error("Invalid title. Expected number or string, received: " + typeof this.settings.titleFont)
// Calculate the x and y coordinates for the title
// place in in the middle of the chart
let x = this.canvas.width / 2
let y = 25
// Set the font style and alignment
this.ctx.font = this.settings.titleFont.size + "px " + this.settings.titleFont.font
this.ctx.fillStyle = "black"
this.ctx.textAlign = "center"
// Draw the title text on the canvas
this.ctx.fillText(this.settings.title, x, y)
}
updateLegend(displayLegend, legend) {
/**
* Update the legend based on the displayLegend flag and provided legend element.
* @param {boolean} displayLegend - Whether to display the legend.
* @param {HTMLElement} legend - The HTML element to display the legend.
*/
updateLegend(displayLegend, legend, chartLoader = null) {
// Check if chart legend is enabled
if (displayLegend) {
let legendHTML = ""
this.data.forEach(categ => {
legendHTML += "<div><span style='display:inline-block;width:20px;background-color:" + categ.color + ";'>&nbsp;</span> " + categ.col_name + "</div>"
// Generate HTML for each category/column
this.data.forEach((categ, index) => {
// Create a new div element for each category
let element = document.createElement("div")
// Set attribute to store column ID
//element.setAttribute("col-id", index)
// Add event listener to handle mouse down event
element.addEventListener("mousemove", e => {
// Retrieve the column ID from the clicked element
//let colId = e.target.getAttribute("col-id")
// Filter objects based on the column ID
let objects = chartLoader.chart.objects.filter(object => object.colId == index)
// Set effect canvas opacity and draw effect
chartLoader.effectCanvas.style.opacity = 1
chartLoader.chart.drawEffect(chartLoader.effectCanvas.getContext("2d"), objects)
e.stopImmediatePropagation()
})
legend.innerHTML = legendHTML
// Set inner HTML for the legend element
element.innerHTML = "<span style='display:inline-block;width:20px;background-color:" + categ.color + ";'>&nbsp;</span> " + categ.col_name
// Append the element to the legend container
legend.appendChild(element)
})
// Update the legend HTML and display it
//legend.innerHTML = legendHTML
legend.style.display = "block"
} else {
// Hide the legend
legend.style.display = "none"
}
}
/**
* Resize the canvas to match its container's size.
* This function clears the canvas, updates its dimensions, and updates bounds.
*/
resizeCanvas() {
this.clear()
//set size
// Set the canvas width to match the client width of the container
this.canvas.width = this.canvas.clientWidth
// Set the canvas height to match the client height of the container
this.canvas.height = this.canvas.clientHeight
// Remove old content
this.clear()
// Update the charts bounds after resizing
this.updateBounds()
}
// Clear the canvas and remove all objects
/**
* Clear the canvas and remove all stored objects.
*/
clear() {
//reset canvas color
// Check if a custom background color is set
if (this.settings.backgroundColor == null) {
// Clear the canvas using the default clear method
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
} else {
// Set the canvas background color to the custom color
this.ctx.fillStyle = this.settings.backgroundColor
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height)
}
// Reset the global index for Shapes
Shape.globalIndex = 0
// Clear the objects array
this.objects = []
}
/**
* Draw a detection map. Draws all shapes on new canvas and endcodes indexes into colors.
*
* @param {CanvasRenderingContext2D} ctx - The 2D drawing context for the canvas.
*/
drawDetectionMap(ctx) {
// Move the drawing point to (0.5, 0.5) to avoid anti-aliasing issues
ctx.moveTo(0.5, 0.5)
ctx.lineWidth = 3
// Set the line width to make the map slightly bigger than visible shapes
//ctx.lineWidth = 3
// Clear the entire canvas to prepare for redrawing
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
ctx.fill()
ctx.stroke()
this.objects.forEach(object => {
// Encode shape index into color
let color = "#" + object.index.toString(16).padStart(6, '0')
ctx.fillStyle = color
ctx.strokeStyle = color
// Draw the shape
object.draw(ctx)
})
}
/**
* Draw effects for given shape. Lightens the color and creates shadow border
*
* @param {CanvasRenderingContext2D} ctx - The 2D drawing context for the canvas.
* @param {Array} objects - An array of shapes on the canvas.
*/
drawEffect(ctx, objects) {
objects.forEach(object => {
/*ctx.globalCompositeOperation = "source-over"
ctx.fillStyle = 'rgba(0,0,0,1)'
ctx.strokeStyle = 'rgba(0,0,0,0)'
object.draw(ctx)
ctx.stroke()
// Clear the entire canvas to prepare for redrawing
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
ctx.fill()
ctx.globalCompositeOperation = "source-out"
ctx.lineWidth = 3
ctx.fillStyle = 'rgba(0,0,0,0)'
ctx.strokeStyle = 'rgba(0,0,0,0.7)'
ctx.stroke()
ctx.scale(2, 2)
ctx.globalCompositeOperation = "source-over"
ctx.fillStyle = 'rgba(255,255,255,0.2)'
ctx.strokeStyle = 'rgba(0,0,0,0)'
object.draw(ctx)
ctx.stroke()*/
ctx.shadowBlur = 15
objects.forEach(object => {
// Get the color of the object based on its column ID
let color = this.data[object.colId].color
// Adjust the color to make it lighter by 20 units
let lighterColor = adjustColor(color, 20)
ctx.fillStyle = lighterColor
ctx.strokeStyle = 'rgba(0,0,0,0.3)'
ctx.lineWidth = 3
// Set the stroke style to a semi-transparent black
ctx.strokeStyle = lighterColor//'rgba(0,0,0,0.3)'
//ctx.lineWidth = 0
ctx.shadowColor = 'rgba(0,0,0,1)'
// Draw the object
object.draw(ctx)
ctx.shadowColor = 'rgba(0,0,0,0)'
// Stroke to draw the border
ctx.stroke()
})
ctx.scale(0.5, 0.5)
}
/**
* Draws the axis on the chart
*
* @param {boolean} [displayAxisValues=this.settings.displayAxisValues] - If axis values should be displayed
*/
drawAxis(displayAxisValues = this.settings.displayAxisValues) {
// Set the font for the axis labels
this.ctx.font = this.settings.labelFont.size + "px " + this.settings.labelFont.font
if (this.settings.yStep <= 0 || !this.settings.yStep) this.settings.yStep = 1
// Ensure yStep is positive and defined
if (this.settings.yStep <= 0 || !this.settings.yStep)
this.settings.yStep = 1
// Draw ticks and labels for the Y-axis
this.drawYAxisTicks()
// Draw ticks and labels for the X-axis
this.drawXAxisTicks(displayAxisValues)
// Draw lines for both X and Y axis
this.drawAxisLines()
// Draw labels for both X and Y axis
this.drawAxisLabels()
// Draw the title if enabled
if (this.settings.displayTitle)
this.drawTitle()
}
/**
* Draws the ticks and labels on the Y-axis
*/
drawYAxisTicks() {
this.ctx.beginPath()
// Loop through the Y-axis values to draw ticks and labels
for (let i = (this.smallest < 0) ? this.smallest : 0; i <= (this.largest >= 0 ? this.largest : 0); i += parseFloat(this.settings.yStep)) {
// Set stroke style and line width for the ticks
this.ctx.strokeStyle = "#BBB"
this.ctx.lineWidth = 1
// Calculate the Y-position for the tick
let yPos = Math.round(this.bounds.xAxis - i * this.scale / this.extreme)
//support line
// Draw support lines if enabled
if (this.settings.displaySupportLines) {
this.ctx.moveTo(this.bounds.left, yPos)
this.ctx.lineTo(this.bounds.right, yPos)
}
//Y axis value
// Set text style and alignment for the Y-axis values
this.ctx.fillStyle = "black"
this.ctx.textAlign = "center"
this.ctx.textAlign = "end"
this.ctx.fillText(i, this.bounds.left - 3, yPos)
// Draw the tick
this.ctx.stroke()
}
}
//X axis value
/**
* Draws the ticks and labels on the X-axis
*
* @param {boolean} displayAxisValues - Whether to display axis values
*/
drawXAxisTicks(displayAxisValues) {
// Check if axis values should be displayed
if (displayAxisValues) {
// Set text style and alignment for the X-axis values
this.ctx.fillStyle = "black"
this.ctx.textAlign = "center"
// Loop through the data to draw X-axis ticks and labels
for (let i = 0; i < this.dataLen; i++) {
// Calculate the X-position for the tick
let x = this.bounds.left + this.bounds.width / (this.dataLen - 1) * i
// Set the text for the X-axis label
let text = (i + 1).toString()
if (this.settings.custom_x_values !== "")
text = this.settings.custom_x_values.split(';')[i]
// Draw the X-axis label
this.ctx.fillText(text, x, this.bounds.bottom + 18)
}
this.ctx.closePath()
}
}
//X and Y axis
/**
* Draws the lines for both X and Y axis
*/
drawAxisLines() {
// Set the stroke style and line width for the axis lines
this.ctx.strokeStyle = "black"
this.ctx.lineWidth = "2px"
// Begin the path for drawing the axis lines
this.ctx.beginPath()
// Draw the vertical Y-axis line
this.ctx.moveTo(this.bounds.left, this.bounds.top)
this.ctx.lineTo(this.bounds.left, this.bounds.bottom)
// Draw the horizontal X-axis line
this.ctx.moveTo(this.bounds.left, this.bounds.xAxis)
this.ctx.lineTo(this.bounds.right, this.bounds.xAxis)
this.ctx.stroke()
}
//Axis labels
//X axis text
/**
* Draws the labels for both X and Y axis
*/
drawAxisLabels() {
// Begin the path for drawing the axis labels
this.ctx.beginPath()
// Set font and text style for the X-axis label
this.ctx.font = "20px Arial"
this.ctx.fillStyle = "black"
this.ctx.textAlign = "center"
this.ctx.fillText(this.settings.xLabel, this.bounds.width / 2 + this.bounds.left, this.bounds.height + 2 * this.bounds.top - 5)
//Y axis text
// Draw the X-axis label at the center of the axis
this.ctx.fillText(
this.settings.xLabel, // Text to display (X-axis label)
this.bounds.width / 2 + this.bounds.left, // X coordinate
this.bounds.height + 2 * this.bounds.top - 5 // Y coordinate
)
// Save the current context before rotating for the Y-axis label
this.ctx.save()
// Rotate the context to draw the Y-axis label vertically
this.ctx.rotate(-Math.PI / 2)
// Recenter on the rotated axis
this.ctx.textAlign = "center"
this.ctx.fillText(this.settings.yLabel, -(this.bounds.left + this.bounds.height / 2), 18)
// Draw the Y-axis label at the center of the axis
this.ctx.fillText(
this.settings.yLabel, // Text to display (Y-axis label)
-(this.bounds.left + this.bounds.height / 2), // Y coordinate
18 // X coordinate
)
// Restore the context to its previous state
this.ctx.restore()
this.ctx.stroke()
if (this.settings.displayTitle)
this.drawTitle()
this.ctx.stroke()
}
}

@ -1,25 +1,45 @@
/**
* Donut Chart
*/
class DonutChart extends Chart {
/**
* Creates a new instance of the DonutChart class
*
* @param {HTMLCanvasElement} canvas - The canvas element to draw the chart on
* @param {Array<Object>} data - The data to visualize on the chart
* @param {Object} settings - The settings for the chart
*/
constructor(canvas, data, settings) {
super(canvas, data, settings)
}
/**
* Draws the donut chart on the canvas.
*/
draw() {
let index = 0
// Starting angle for the current donut slice
let start_angle = 0
// Total value of all data categories to calculate percentages
let total_value = 0
// Calculate the total value of all data categories
this.data.forEach(categ => {
let val = categ.values[0]
if (val !== null)
total_value += val
})
// Clear the canvas before drawing
this.clear()
// Iterate over each data category to draw the donut slices
this.data.forEach((categ, colId) => {
// The value of the current slice
let val = categ.values[0]
// Angle for the current donut slice
let slice_angle = 2 * Math.PI * val / total_value
// Calculate the center and radius of the donut chart
let x = this.canvas.width / 2
let y = this.canvas.height / 2
let r = Math.min(this.bounds.width / 2, this.bounds.height / 2)
@ -30,10 +50,11 @@ class DonutChart extends Chart {
new_slice.draw()
this.objects.push(new_slice)
// Update the start angle for the next donut slice
start_angle = end_angle
index++
})
// Draw the chart title if displayTitle is enabled
if (this.settings.displayTitle)
this.drawTitle()
}

@ -1,22 +1,40 @@
/**
* Line Chart
*/
class LineChart extends PointChart {
/**
* Creates a new instance of the LineChart class
*
* @param {HTMLCanvasElement} canvas - The canvas element to draw the chart on
* @param {Array<Object>} data - The data to visualize on the chart
* @param {Object} settings - The settings for the chart
*/
constructor(canvas, data, settings) {
super(canvas, data, settings)
}
/**
* Draws the line chart on the canvas.
*/
draw() {
// Clear the canvas and draw the axis
this.clear()
this.drawAxis()
// Iterate over each category in the data
this.data.forEach(categ => {
// Begin a new path for drawing lines
this.ctx.beginPath()
this.ctx.lineJoin = "round"
this.ctx.strokeStyle = categ.color
// Iterate over each value in the category
for (let i = 0; i < this.dataLen; i++) {
if (categ.values[i] === null) continue
// Calculate the x and y coordinates for the current value
let x = this.bounds.left + this.bounds.width / (this.dataLen - 1) * i
let y = (this.bounds.xAxis - categ.values[i] / this.extreme * this.scale)
let y = this.bounds.xAxis - categ.values[i] / this.extreme * this.scale
this.ctx.lineTo(x, y)
}
@ -24,7 +42,7 @@ class LineChart extends PointChart {
this.ctx.closePath()
})
// Points
// Draw points on the chart if required
if (this.settings.displayPoints)
this.data.forEach((categ, colId) => {this.drawPoints(categ.values, colId, categ.color)})
}

@ -1,39 +1,62 @@
/**
* Pie Chart
*/
class PieChart extends Chart {
/**
* Creates a new instance of the PieChart class
*
* @param {HTMLCanvasElement} canvas - The canvas element to draw the chart on
* @param {Array<Object>} data - The data to visualize on the chart
* @param {Object} settings - The settings for the chart
*/
constructor(canvas, data, settings) {
super(canvas, data, settings)
}
/**
* Draws the pie chart on the canvas.
*/
draw() {
let index = 0
// Starting angle for the current pie slice
let start_angle = 0
// Total value of all data categories to calculate percentages
let total_value = 0
// Calculate the total value of all data categories
this.data.forEach(categ => {
let val = categ.values[0]
if (val !== null)
total_value += val
})
// Clear the canvas before drawing
this.clear()
// Iterate over each data category to draw the pie slices
this.data.forEach((categ, colId) => {
// The value of the current slice
let val = categ.values[0]
// Angle for the current pie slice
let slice_angle = 2 * Math.PI * val / total_value
// Calculate the center and radius of the pie chart
let x = this.canvas.width / 2
let y = this.canvas.height / 2
let r = Math.min(this.bounds.width / 2, this.bounds.height / 2)
// Calculate the end angle for the current pie slice
let end_angle = start_angle + slice_angle
// Set the fill color and draw the pie slice
this.ctx.fillStyle = categ.color
let new_slice = new PieSlice(this.ctx, val + " (" + Math.round(val / total_value * 100) + "%)", colId, x, y, r, start_angle, end_angle)
new_slice.draw()
this.objects.push(new_slice)
// Update the start angle for the next pie slice
start_angle = end_angle
index++
})
// Draw the chart title if displayTitle is enabled
if (this.settings.displayTitle)
this.drawTitle()
}

@ -1,29 +1,73 @@
/**
* PointChart class extends the Chart class to represent data with points
*/
class PointChart extends Chart {
/**
* Constructor for the PointChart class
* @param {HTMLElement} canvas - The canvas element to draw the chart on
* @param {Array} data - The data array containing values for the chart
* @param {Object} settings - The settings object for configuring the chart
*/
constructor(canvas, data, settings) {
// Call the constructor of the parent class (Chart)
super(canvas, data, settings)
}
/**
* Draws the data points on the chart
* @param {Array} values - The array of values to be plotted
* @param {string} name - The name or label associated with the values
* @param {string} color - The color to use for drawing the points
*/
drawPoints(values, name, color) {
// Set the fill and stroke styles for the points
this.ctx.fillStyle = color
this.ctx.strokeStyle = color
// Set border values if enabled
if (this.settings.pointBorderSize > 0) {
this.ctx.lineWidth = this.settings.pointBorderSize
this.ctx.strokeStyle = this.settings.pointBorderColor
}
/*console.log(values)
console.log(this.dataLen)*/
// Iterate over the values to plot each data point
for (let i = 0; i < this.dataLen; i++) {
if (values[i] === null) continue
let x = this.bounds.left + this.bounds.width / (this.dataLen - 1) * i
let y = this.bounds.xAxis - values[i] / this.extreme * this.scale
// Skip empty points
if (values[i] === null || values[i] === undefined)
continue
// Calculate the x and y coordinates for the data point
let x = Math.round(this.bounds.left + this.bounds.width / (this.dataLen - 1) * i)
let y = Math.round(this.bounds.xAxis - values[i] / this.extreme * this.scale)
// Create a new Circle object for the data point
let new_object = new Circle(this.ctx, values[i], name, x, y, this.settings.pointSize)
new_object.draw()
this.objects.push(new_object)
// Draw a border if enabled
if (this.settings.pointBorderSize > 0)
this.ctx.stroke()
}
}
/**
* Draws the entire PointChart
*/
draw() {
// Clear the canvas and draw the axis
this.clear()
this.drawAxis()
// Draw the data points
if (this.settings.displayPoints)
setTimeout(() => {
console.time("1")
this.data.forEach((categ, colId) => {this.drawPoints(categ.values, colId, categ.color)})
console.timeEnd("1")
}, 0);
}
}

@ -0,0 +1,181 @@
/**
* Class representing a generic shape.
*/
class Shape {
// Static property to keep track of the global index for different shapes.
static globalIndex = 0
/**
* Creates a new instance of the Shape class.
*
* @param {CanvasRenderingContext2D} ctx - The 2D drawing context for the canvas.
* @param {any} value - The value associated with the shape.
* @param {number} colId - The column ID associated with the shape.
* @param {number} x - The x-coordinate of the shape.
* @param {number} y - The y-coordinate of the shape.
*/
constructor(ctx, value, colId, x, y) {
this.ctx = ctx
this.value = value
this.colId = colId
this.x = x
this.y = y
// Assign a unique index to the shape
this.index = Shape.globalIndex++
}
/**
* Abstract method for drawing the shape.
* This method should be implemented by subclasses.
*/
draw() { }
}
/**
* Class representing a rectangle shape, extending the Shape class.
*/
class Rectangle extends Shape {
/**
* Creates a new instance of the Rectangle class.
*
* @param {CanvasRenderingContext2D} ctx - The 2D drawing context for the canvas.
* @param {any} value - The value associated with the rectangle.
* @param {number} colId - The column ID associated with the rectangle.
* @param {number} x - The x-coordinate of the top-left corner of the rectangle.
* @param {number} y - The y-coordinate of the top-left corner of the rectangle.
* @param {number} w - The width of the rectangle.
* @param {number} h - The height of the rectangle.
*/
constructor(ctx, value, colId, x, y, w, h) {
super(ctx, value, colId, x, y) // Call the constructor of the Shape class.
this.w = w // The width of the rectangle.
this.h = h // The height of the rectangle.
}
/**
* Draws the rectangle on the canvas.
*
* @param {CanvasRenderingContext2D} [ctx=this.ctx] - The 2D drawing context for the canvas.
*/
draw(ctx = this.ctx) {
ctx.beginPath()
// Define the rectangle
ctx.rect(this.x, this.y, this.w, this.h)
// Fill the rectangle with the current fill style
ctx.fill()
}
}
/**
* Class representing a circle shape, extending the Shape class.
*/
class Circle extends Shape {
/**
* Creates a new instance of the Circle class.
*
* @param {CanvasRenderingContext2D} ctx - The 2D drawing context for the canvas.
* @param {any} value - The value associated with the circle.
* @param {number} colId - The column ID associated with the circle.
* @param {number} x - The x-coordinate of the center of the circle.
* @param {number} y - The y-coordinate of the center of the circle.
* @param {number} r - The radius of the circle.
*/
constructor(ctx, value, colId, x, y, r) {
super(ctx, value, colId, x, y) // Call the constructor of the Shape class.
this.r = r // The radius of the circle.
}
/**
* Draws the circle on the canvas
*
* @param {CanvasRenderingContext2D} [ctx=this.ctx] - The 2D drawing context for the canvas
*/
draw(ctx = this.ctx) {
ctx.beginPath()
// Define the circle
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI)
// Fill the circle with the current fill style
ctx.fill()
}
}
/**
* Class representing a pie slice shape, extending the Circle class
*/
class PieSlice extends Circle {
/**
* Creates a new instance of the PieSlice class
*
* @param {CanvasRenderingContext2D} ctx - The 2D drawing context for the canvas
* @param {any} value - The value associated with the pie slice
* @param {number} colId - The column ID associated with the pie slice
* @param {number} x - The x-coordinate of the center of the pie slice
* @param {number} y - The y-coordinate of the center of the pie slice
* @param {number} r - The radius of the pie slice
* @param {number} sAngle - The starting angle of the pie slice in radians
* @param {number} eAngle - The ending angle of the pie slice in radians
*/
constructor(ctx, value, colId, x, y, r, sAngle, eAngle) {
super(ctx, value, colId, x, y, r) // Call the constructor of the Circle class
this.sAngle = sAngle // The starting angle of the pie slice
this.eAngle = eAngle // The ending angle of the pie slice
}
/**
* Draws the pie slice on the canvas
*
* @param {CanvasRenderingContext2D} [ctx=this.ctx] - The 2D drawing context for the canvas
*/
draw(ctx = this.ctx) {
ctx.beginPath()
// Move to the center of the circle
ctx.moveTo(this.x, this.y)
// Define the pie slice
ctx.arc(this.x, this.y, this.r, this.sAngle, this.eAngle)
// Draw a line back to the center
ctx.lineTo(this.x, this.y)
// Fill the pie slice with the current fill style
ctx.fill()
}
}
/**
* Class representing a donut slice shape, extending the PieSlice class
*/
class DonutSlice extends PieSlice {
/**
* Creates a new instance of the DonutSlice class
*
* @param {CanvasRenderingContext2D} ctx - The 2D drawing context for the canvas
* @param {any} value - The value associated with the donut slice
* @param {number} colId - The column ID associated with the donut slice
* @param {number} x - The x-coordinate of the center of the donut slice
* @param {number} y - The y-coordinate of the center of the donut slice
* @param {number} r - The outer radius of the donut slice
* @param {number} sAngle - The starting angle of the donut slice in radians
* @param {number} eAngle - The ending angle of the donut slice in radians
* @param {number} r2 - The inner radius of the donut slice
*/
constructor(ctx, value, colId, x, y, r, sAngle, eAngle, r2) {
super(ctx, value, colId, x, y, r, sAngle, eAngle) // Call the constructor of the PieSlice class
this.r2 = r2 // The inner radius of the donut slice
}
/**
* Draws the donut slice on the canvas
*
* @param {CanvasRenderingContext2D} [ctx=this.ctx] - The 2D drawing context for the canvas
*/
draw(ctx = this.ctx) {
ctx.beginPath()
// Move to the starting point of the slice using polar coordinates
ctx.moveTo(this.x + this.r2 * Math.cos(this.sAngle), this.y + this.r2 * Math.sin(this.sAngle))
// Draw the outer arc of the donut slice
ctx.arc(this.x, this.y, this.r, this.sAngle, this.eAngle, false)
// Draw the inner arc of the donut slice in reverse order to create a hole
ctx.arc(this.x, this.y, this.r2, this.eAngle, this.sAngle, true)
// Fill the donut slice with the current fill style
ctx.fill()
}
}

@ -1,49 +1,76 @@
/**
* Smooth Area Chart
*/
class SmoothAreaChart extends PointChart {
/**
* Creates a new instance of the SmoothAreaChart class
*
* @param {HTMLCanvasElement} canvas - The canvas element to draw the chart on
* @param {Array<Object>} data - The data to visualize on the chart
* @param {Object} settings - The settings for the chart
*/
constructor(canvas, data, settings) {
super(canvas, data, settings)
}
/**
* Draws the smooth area chart on the canvas.
*/
draw() {
// Clear the canvas and draw the axis
this.clear()
this.drawAxis()
// Iterate over each category in the data
this.data.forEach(categ => {
//Lines
// Begin a new path for drawing lines
this.ctx.beginPath()
this.ctx.lineJoin = "round"
this.ctx.strokeStyle = categ.color
// Calculate the starting point for the line
let x = this.bounds.left
let y = (this.bounds.xAxis - categ.values[0] / this.extreme * this.scale)
this.ctx.moveTo(x, y)
let xmax = 0
let xmax = 0 // rightmost point (some lines can be shorter)
// Draw the Bezier curve
for (let i = 0; i < this.dataLen - 1; i++) {
// Calculate left point coodrinates
let x1 = this.bounds.left + this.bounds.width / (this.dataLen - 1) * i
let y1 = (this.bounds.xAxis - categ.values[i] / this.extreme * this.scale)
let y1 = this.bounds.xAxis - categ.values[i] / this.extreme * this.scale
// Calculate right point coodrinates
let x2 = this.bounds.left + this.bounds.width / (this.dataLen - 1) * (i + 1)
let y2 = (this.bounds.xAxis - categ.values[i + 1] / this.extreme * this.scale)
let y2 = this.bounds.xAxis - categ.values[i + 1] / this.extreme * this.scale
// Find middle point
let xm = (x1 + x2) / 2
// Find quarter points
let xl = (x1 + xm) / 2
let xr = (x2 + xm) / 2
// Draw a curve that smoothly connects the points
this.ctx.bezierCurveTo(xl, y1, xr, y2, x2, y2);
xmax = x2
}
// Complete the area by connecting the last point to the x-axis
this.ctx.lineTo(xmax, this.bounds.xAxis)
this.ctx.lineTo(this.bounds.left, this.bounds.xAxis)
// Set transparency and fill the area
this.ctx.globalAlpha = 0.5
this.ctx.fillStyle = categ.color
this.ctx.closePath()
this.ctx.fill()
// Reset transparency
this.ctx.globalAlpha = 1
})
// Points
// Draw points on the chart if required
if (this.settings.displayPoints)
this.data.forEach((categ, colId) => {this.drawPoints(categ.values, colId, categ.color)})
}

@ -1,39 +1,62 @@
/**
* Smooth Line Chart
*/
class SmoothLineChart extends PointChart {
/**
* Creates a new instance of the SmoothLineChart class
*
* @param {HTMLCanvasElement} canvas - The canvas element to draw the chart on
* @param {Array<Object>} data - The data to visualize on the chart
* @param {Object} settings - The settings for the chart
*/
constructor(canvas, data, settings) {
super(canvas, data, settings)
}
/**
* Draws the smooth line chart on the canvas.
*/
draw() {
// Clear the canvas and draw the axis
this.clear()
this.drawAxis()
// Iterate over each category in the data
this.data.forEach(categ => {
// Begin a new path for drawing lines
this.ctx.beginPath()
this.ctx.lineJoin = "round"
this.ctx.strokeStyle = categ.color
// Calculate the starting point for the line
let x = this.bounds.left
let y = (this.bounds.xAxis - categ.values[0] / this.extreme * this.scale)
this.ctx.moveTo(x, y)
// Draw the Bezier curve for the smooth line
for (let i = 0; i < this.dataLen - 1; i++) {
// Calculate left point coodrinates
let x1 = this.bounds.left + this.bounds.width / (this.dataLen - 1) * i
let y1 = (this.bounds.xAxis - categ.values[i] / this.extreme * this.scale)
let y1 = this.bounds.xAxis - categ.values[i] / this.extreme * this.scale
// Calculate right point coodrinates
let x2 = this.bounds.left + this.bounds.width / (this.dataLen - 1) * (i + 1)
let y2 = (this.bounds.xAxis - categ.values[i + 1] / this.extreme * this.scale)
let y2 = this.bounds.xAxis - categ.values[i + 1] / this.extreme * this.scale
// Find middle point
let xm = (x1 + x2) / 2
// Find quarter points
let xl = (x1 + xm) / 2
let xr = (x2 + xm) / 2
// Draw a curve that smoothly connects the points
this.ctx.bezierCurveTo(xl, y1, xr, y2, x2, y2);
}
this.ctx.stroke()
this.ctx.closePath()
})
// Points
// Draw points on the chart if required
if (this.settings.displayPoints)
this.data.forEach((categ, colId) => {this.drawPoints(categ.values, colId, categ.color)})

@ -1,13 +1,27 @@
/**
* Stacked Chart
*/
class StackedChart extends Chart {
/**
* Creates a new instance of the StackedChart class
*
* @param {HTMLCanvasElement} canvas - The canvas element to draw the chart on
* @param {Array<Object>} data - The data to visualize on the chart
* @param {Object} settings - The settings for the chart
*/
constructor(canvas, data, settings) {
super(canvas, data, settings)
}
/**
* Draws the stacked bar chart on the canvas.
*/
draw() {
this.ctx.shadowOffsetX = 15
/*this.ctx.shadowOffsetX = 15
this.ctx.shadowOffsetY = 15
this.ctx.shadowBlur = 4
this.ctx.shadowBlur = 4*/
// Calculate the largest total value across all categories for normalization
let largest = 0
for (let i = 0; i < this.dataLen; i++) {
let sum = 0
@ -18,24 +32,38 @@ class StackedChart extends Chart {
largest = sum > largest ? sum : largest
}
// Clear the canvas
this.clear()
// Draw the axis without displaying axis values
this.drawAxis(false)
// Calculate the size of each bar segment based on the canvas width and number of data points
let size = this.bounds.width / this.dataLen
// Calculate the actual width of each bar, making it 70% of the calculated size
let bar_width = size * 0.7
// Iterate over each data point to draw the stacked bars
for (let i = 0; i < this.dataLen; i++) {
// The top position of the last stacked bar segment
let last_top = this.bounds.xAxis
// Counter to determine the first category in each data point
let num = 0
this.data.forEach((categ, colId) => {
// Value of the bar segment
let value = categ.values[i]
// The height of the bar segment relative to the largest total value
let bar_height = value / largest * this.bounds.height
// The left position of the bar segment
let left = this.bounds.left + size * (i + 0.15)
// The top position of the bar segment
let top = last_top - bar_height
// Update the last top position for the next iteration
last_top = top
//x value
// Draw x-axis labels
// Only for first category to avoid repeated drawings
if (num === 0) {
let text = (i + 1).toString()
/*if (this.settings.custom_x_values !== "")
@ -51,6 +79,7 @@ class StackedChart extends Chart {
}
num++
// Set the fill color and draw the rectangle for the current bar segment
this.ctx.fillStyle = categ.color
let new_object = new Rectangle(this.ctx, value, colId, left, top, bar_width, bar_height)
new_object.draw()

@ -3,6 +3,7 @@
namespace App\Api\Controller;
use App\Document\Chart;
use App\Api\Model\ChartInput;
use App\Api\Model\ChartOutput;
use Doctrine\ODM\MongoDB\DocumentManager;
use App\Api\Controller\AbstractRestController;
@ -30,6 +31,22 @@ class ChartRestController extends AbstractRestController
return $this->respond( $chartDto, Response::HTTP_OK);
}
#[Rest\Put('/insert', name: '_insert')]
public function insert(DocumentManager $dm, Request $request): Response
{
//$chart = $newChart->toEntity();
$data = json_decode($request->getContent());
$chart = new Chart();
$chart->setName($data->name);
$chart->setCode($data->code);
$chart->setMetadata($data->metadata);
$chart->setTable($data->table);
$dm->persist($chart);
$dm->flush();
return $this->respond( $chart->getName(), Response::HTTP_OK);
}
private function findOrFail(DocumentManager $dm, int $id): Chart
{
$room = $dm->getRepository(Chart::class)->findOneBy(['code' => $id]);

@ -0,0 +1,36 @@
<?php
namespace App\Api\Model;
use App\Document\Chart;
use Symfony\Component\Validator\Constraints as Assert;
class ChartInput
{
public function __construct(
//public ?string $id = "",
#[Assert\Type('string')]
#[Assert\NotBlank()]
public string $name,
#[Assert\Type('string')]
#[Assert\NotBlank()]
public string $code,
public array $metadata,
public array $table,
) {
}
public function toEntity(): Chart
{
$chart = new Chart();
//$chart->setId();
$chart->setName($this->name);
//$chart->setName("name");
$chart->setCode($this->code);
$chart->setMetadata($this->metadata);
$chart->setTable($this->table);
return $chart;
}
}

@ -27,7 +27,8 @@ class ColumnType extends AbstractType
'prototype_data' => 0,
'entry_options' => [
'label' => false,
]
],
'required' => false
]);
}

@ -5,11 +5,15 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class FontType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$resolver = new OptionsResolver();
$this->configureOptions($resolver);
$builder
->add('font', ChoiceType::class, [
'choices' => [
@ -28,4 +32,12 @@ class FontType extends AbstractType
'label' => false
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'font' => 'Arial',
'size'=> 12,
]);
}
}

@ -18,6 +18,9 @@ class MetadataType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$resolver = new OptionsResolver();
$this->configureOptions($resolver);
$builder
->add('type', ChoiceType::class, [
'choices' => [
@ -35,7 +38,7 @@ class MetadataType extends AbstractType
])
->add('margin', NumberType::class, [
'label' => 'Margin',
'required' => false,
'required' => false
])
// Title settings
->add(
@ -47,6 +50,7 @@ class MetadataType extends AbstractType
->add('title', TextType::class, [
'label' => 'Title',
'required' => false,
'empty_data' => ''
])
->add('displayTitle', CheckboxType::class, [
'label' => 'Display title',
@ -120,6 +124,14 @@ class MetadataType extends AbstractType
'label' => 'Point size',
'required' => false,
])
->add('pointBorderSize', NumberType::class, [
'label' => 'Point border size',
'required' => false,
])
->add('pointBorderColor', ColorType::class, [
'label' => 'Point border color',
'required' => false,
])
)
->add('backgroundColor', ColorType::class, [
'label' => 'Background color',
@ -128,10 +140,13 @@ class MetadataType extends AbstractType
->add('submit', SubmitType::class, ['label' => 'Provést']);
}
/*public function configureOptions(OptionsResolver $resolver)
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}*/
/*$resolver->setDefaults([
'type' => 'point',
'margin'=> 5,
'title'=> '',
'displayTitle' => true,
]);*/
}
}

@ -86,6 +86,21 @@
"public/.htaccess"
]
},
"symfony/asset-mapper": {
"version": "7.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.4",
"ref": "6c28c471640cc2c6e60812ebcb961c526ef8997f"
},
"files": [
"assets/app.js",
"assets/styles/app.css",
"config/packages/asset_mapper.yaml",
"importmap.php"
]
},
"symfony/console": {
"version": "6.1",
"recipe": {

@ -10,6 +10,7 @@
{% endblock %}
{% block javascripts %}
{% block importmap %}{{ importmap('app') }}{% endblock %}
{{ encore_entry_script_tags('app') }}
{% endblock %}
</head>

@ -11,6 +11,7 @@
{% block javascripts %}
{{ parent() }}
<script type="text/javascript" src={{ asset('/scripts/charts/primitives.js') }}></script>
<script type="text/javascript" src={{ asset('/scripts/charts/chart.js') }}></script>
<script type="text/javascript" src={{ asset('/scripts/charts/point_chart.js') }}></script>
<script type="text/javascript" src={{ asset('/scripts/charts/area_chart.js') }}></script>

@ -8,6 +8,7 @@ $baseDir = dirname($vendorDir);
return array(
'App\\Api\\Controller\\AbstractRestController' => $baseDir . '/src/Api/Controller/AbstractRestController.php',
'App\\Api\\Controller\\ChartRestController' => $baseDir . '/src/Api/Controller/ChartRestController.php',
'App\\Api\\Model\\ChartInput' => $baseDir . '/src/Api/Model/ChartInput.php',
'App\\Api\\Model\\ChartOutput' => $baseDir . '/src/Api/Model/ChartOutput.php',
'App\\Controller\\ChartController' => $baseDir . '/src/Controller/ChartController.php',
'App\\Controller\\IndexController' => $baseDir . '/src/Controller/IndexController.php',
@ -25,6 +26,18 @@ return array(
'App\\Repository\\UserRepository' => $baseDir . '/src/Repository/UserRepository.php',
'Collator' => $vendorDir . '/symfony/polyfill-intl-icu/Resources/stubs/Collator.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'Composer\\Semver\\Comparator' => $vendorDir . '/composer/semver/src/Comparator.php',
'Composer\\Semver\\CompilingMatcher' => $vendorDir . '/composer/semver/src/CompilingMatcher.php',
'Composer\\Semver\\Constraint\\Bound' => $vendorDir . '/composer/semver/src/Constraint/Bound.php',
'Composer\\Semver\\Constraint\\Constraint' => $vendorDir . '/composer/semver/src/Constraint/Constraint.php',
'Composer\\Semver\\Constraint\\ConstraintInterface' => $vendorDir . '/composer/semver/src/Constraint/ConstraintInterface.php',
'Composer\\Semver\\Constraint\\MatchAllConstraint' => $vendorDir . '/composer/semver/src/Constraint/MatchAllConstraint.php',
'Composer\\Semver\\Constraint\\MatchNoneConstraint' => $vendorDir . '/composer/semver/src/Constraint/MatchNoneConstraint.php',
'Composer\\Semver\\Constraint\\MultiConstraint' => $vendorDir . '/composer/semver/src/Constraint/MultiConstraint.php',
'Composer\\Semver\\Interval' => $vendorDir . '/composer/semver/src/Interval.php',
'Composer\\Semver\\Intervals' => $vendorDir . '/composer/semver/src/Intervals.php',
'Composer\\Semver\\Semver' => $vendorDir . '/composer/semver/src/Semver.php',
'Composer\\Semver\\VersionParser' => $vendorDir . '/composer/semver/src/VersionParser.php',
'DateError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateError.php',
'DateException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateException.php',
'DateInvalidOperationException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateInvalidOperationException.php',
@ -3839,6 +3852,60 @@ return array(
'Symfony\\Bundle\\WebProfilerBundle\\Profiler\\TemplateManager' => $vendorDir . '/symfony/web-profiler-bundle/Profiler/TemplateManager.php',
'Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension' => $vendorDir . '/symfony/web-profiler-bundle/Twig/WebProfilerExtension.php',
'Symfony\\Bundle\\WebProfilerBundle\\WebProfilerBundle' => $vendorDir . '/symfony/web-profiler-bundle/WebProfilerBundle.php',
'Symfony\\Component\\AssetMapper\\AssetMapper' => $vendorDir . '/symfony/asset-mapper/AssetMapper.php',
'Symfony\\Component\\AssetMapper\\AssetMapperCompiler' => $vendorDir . '/symfony/asset-mapper/AssetMapperCompiler.php',
'Symfony\\Component\\AssetMapper\\AssetMapperDevServerSubscriber' => $vendorDir . '/symfony/asset-mapper/AssetMapperDevServerSubscriber.php',
'Symfony\\Component\\AssetMapper\\AssetMapperInterface' => $vendorDir . '/symfony/asset-mapper/AssetMapperInterface.php',
'Symfony\\Component\\AssetMapper\\AssetMapperRepository' => $vendorDir . '/symfony/asset-mapper/AssetMapperRepository.php',
'Symfony\\Component\\AssetMapper\\Command\\AssetMapperCompileCommand' => $vendorDir . '/symfony/asset-mapper/Command/AssetMapperCompileCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\DebugAssetMapperCommand' => $vendorDir . '/symfony/asset-mapper/Command/DebugAssetMapperCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\ImportMapAuditCommand' => $vendorDir . '/symfony/asset-mapper/Command/ImportMapAuditCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\ImportMapInstallCommand' => $vendorDir . '/symfony/asset-mapper/Command/ImportMapInstallCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\ImportMapOutdatedCommand' => $vendorDir . '/symfony/asset-mapper/Command/ImportMapOutdatedCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\ImportMapRemoveCommand' => $vendorDir . '/symfony/asset-mapper/Command/ImportMapRemoveCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\ImportMapRequireCommand' => $vendorDir . '/symfony/asset-mapper/Command/ImportMapRequireCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\ImportMapUpdateCommand' => $vendorDir . '/symfony/asset-mapper/Command/ImportMapUpdateCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\VersionProblemCommandTrait' => $vendorDir . '/symfony/asset-mapper/Command/VersionProblemCommandTrait.php',
'Symfony\\Component\\AssetMapper\\CompiledAssetMapperConfigReader' => $vendorDir . '/symfony/asset-mapper/CompiledAssetMapperConfigReader.php',
'Symfony\\Component\\AssetMapper\\Compiler\\AssetCompilerInterface' => $vendorDir . '/symfony/asset-mapper/Compiler/AssetCompilerInterface.php',
'Symfony\\Component\\AssetMapper\\Compiler\\CssAssetUrlCompiler' => $vendorDir . '/symfony/asset-mapper/Compiler/CssAssetUrlCompiler.php',
'Symfony\\Component\\AssetMapper\\Compiler\\JavaScriptImportPathCompiler' => $vendorDir . '/symfony/asset-mapper/Compiler/JavaScriptImportPathCompiler.php',
'Symfony\\Component\\AssetMapper\\Compiler\\SourceMappingUrlsCompiler' => $vendorDir . '/symfony/asset-mapper/Compiler/SourceMappingUrlsCompiler.php',
'Symfony\\Component\\AssetMapper\\Event\\PreAssetsCompileEvent' => $vendorDir . '/symfony/asset-mapper/Event/PreAssetsCompileEvent.php',
'Symfony\\Component\\AssetMapper\\Exception\\CircularAssetsException' => $vendorDir . '/symfony/asset-mapper/Exception/CircularAssetsException.php',
'Symfony\\Component\\AssetMapper\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/asset-mapper/Exception/ExceptionInterface.php',
'Symfony\\Component\\AssetMapper\\Exception\\LogicException' => $vendorDir . '/symfony/asset-mapper/Exception/LogicException.php',
'Symfony\\Component\\AssetMapper\\Exception\\RuntimeException' => $vendorDir . '/symfony/asset-mapper/Exception/RuntimeException.php',
'Symfony\\Component\\AssetMapper\\Factory\\CachedMappedAssetFactory' => $vendorDir . '/symfony/asset-mapper/Factory/CachedMappedAssetFactory.php',
'Symfony\\Component\\AssetMapper\\Factory\\MappedAssetFactory' => $vendorDir . '/symfony/asset-mapper/Factory/MappedAssetFactory.php',
'Symfony\\Component\\AssetMapper\\Factory\\MappedAssetFactoryInterface' => $vendorDir . '/symfony/asset-mapper/Factory/MappedAssetFactoryInterface.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapAuditor' => $vendorDir . '/symfony/asset-mapper/ImportMap/ImportMapAuditor.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapConfigReader' => $vendorDir . '/symfony/asset-mapper/ImportMap/ImportMapConfigReader.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapEntries' => $vendorDir . '/symfony/asset-mapper/ImportMap/ImportMapEntries.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapEntry' => $vendorDir . '/symfony/asset-mapper/ImportMap/ImportMapEntry.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapGenerator' => $vendorDir . '/symfony/asset-mapper/ImportMap/ImportMapGenerator.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapManager' => $vendorDir . '/symfony/asset-mapper/ImportMap/ImportMapManager.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapPackageAudit' => $vendorDir . '/symfony/asset-mapper/ImportMap/ImportMapPackageAudit.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapPackageAuditVulnerability' => $vendorDir . '/symfony/asset-mapper/ImportMap/ImportMapPackageAuditVulnerability.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapRenderer' => $vendorDir . '/symfony/asset-mapper/ImportMap/ImportMapRenderer.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapType' => $vendorDir . '/symfony/asset-mapper/ImportMap/ImportMapType.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapUpdateChecker' => $vendorDir . '/symfony/asset-mapper/ImportMap/ImportMapUpdateChecker.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapVersionChecker' => $vendorDir . '/symfony/asset-mapper/ImportMap/ImportMapVersionChecker.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\JavaScriptImport' => $vendorDir . '/symfony/asset-mapper/ImportMap/JavaScriptImport.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\PackageRequireOptions' => $vendorDir . '/symfony/asset-mapper/ImportMap/PackageRequireOptions.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\PackageUpdateInfo' => $vendorDir . '/symfony/asset-mapper/ImportMap/PackageUpdateInfo.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\PackageVersionProblem' => $vendorDir . '/symfony/asset-mapper/ImportMap/PackageVersionProblem.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\RemotePackageDownloader' => $vendorDir . '/symfony/asset-mapper/ImportMap/RemotePackageDownloader.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\RemotePackageStorage' => $vendorDir . '/symfony/asset-mapper/ImportMap/RemotePackageStorage.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\Resolver\\JsDelivrEsmResolver' => $vendorDir . '/symfony/asset-mapper/ImportMap/Resolver/JsDelivrEsmResolver.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\Resolver\\PackageResolverInterface' => $vendorDir . '/symfony/asset-mapper/ImportMap/Resolver/PackageResolverInterface.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\Resolver\\ResolvedImportMapPackage' => $vendorDir . '/symfony/asset-mapper/ImportMap/Resolver/ResolvedImportMapPackage.php',
'Symfony\\Component\\AssetMapper\\MappedAsset' => $vendorDir . '/symfony/asset-mapper/MappedAsset.php',
'Symfony\\Component\\AssetMapper\\MapperAwareAssetPackage' => $vendorDir . '/symfony/asset-mapper/MapperAwareAssetPackage.php',
'Symfony\\Component\\AssetMapper\\Path\\LocalPublicAssetsFilesystem' => $vendorDir . '/symfony/asset-mapper/Path/LocalPublicAssetsFilesystem.php',
'Symfony\\Component\\AssetMapper\\Path\\PublicAssetsFilesystemInterface' => $vendorDir . '/symfony/asset-mapper/Path/PublicAssetsFilesystemInterface.php',
'Symfony\\Component\\AssetMapper\\Path\\PublicAssetsPathResolver' => $vendorDir . '/symfony/asset-mapper/Path/PublicAssetsPathResolver.php',
'Symfony\\Component\\AssetMapper\\Path\\PublicAssetsPathResolverInterface' => $vendorDir . '/symfony/asset-mapper/Path/PublicAssetsPathResolverInterface.php',
'Symfony\\Component\\Asset\\Context\\ContextInterface' => $vendorDir . '/symfony/asset/Context/ContextInterface.php',
'Symfony\\Component\\Asset\\Context\\NullContext' => $vendorDir . '/symfony/asset/Context/NullContext.php',
'Symfony\\Component\\Asset\\Context\\RequestStackContext' => $vendorDir . '/symfony/asset/Context/RequestStackContext.php',

@ -67,6 +67,7 @@ return array(
'Symfony\\Component\\Cache\\' => array($vendorDir . '/symfony/cache'),
'Symfony\\Component\\BrowserKit\\' => array($vendorDir . '/symfony/browser-kit'),
'Symfony\\Component\\Asset\\' => array($vendorDir . '/symfony/asset'),
'Symfony\\Component\\AssetMapper\\' => array($vendorDir . '/symfony/asset-mapper'),
'Symfony\\Bundle\\WebProfilerBundle\\' => array($vendorDir . '/symfony/web-profiler-bundle'),
'Symfony\\Bundle\\TwigBundle\\' => array($vendorDir . '/symfony/twig-bundle'),
'Symfony\\Bundle\\SecurityBundle\\' => array($vendorDir . '/symfony/security-bundle'),
@ -115,6 +116,7 @@ return array(
'Doctrine\\Bundle\\MigrationsBundle\\' => array($vendorDir . '/doctrine/doctrine-migrations-bundle'),
'Doctrine\\Bundle\\DoctrineBundle\\' => array($vendorDir . '/doctrine/doctrine-bundle/src'),
'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'),
'App\\Tests\\' => array($baseDir . '/tests'),
'App\\' => array($baseDir . '/src'),
);

@ -97,6 +97,7 @@ class ComposerStaticInit51f3c6df917af85305ff4843868663eb
'Symfony\\Component\\Cache\\' => 24,
'Symfony\\Component\\BrowserKit\\' => 29,
'Symfony\\Component\\Asset\\' => 24,
'Symfony\\Component\\AssetMapper\\' => 30,
'Symfony\\Bundle\\WebProfilerBundle\\' => 33,
'Symfony\\Bundle\\TwigBundle\\' => 26,
'Symfony\\Bundle\\SecurityBundle\\' => 30,
@ -170,6 +171,10 @@ class ComposerStaticInit51f3c6df917af85305ff4843868663eb
'Doctrine\\Bundle\\DoctrineBundle\\' => 31,
'DeepCopy\\' => 9,
),
'C' =>
array (
'Composer\\Semver\\' => 16,
),
'A' =>
array (
'App\\Tests\\' => 10,
@ -424,6 +429,10 @@ class ComposerStaticInit51f3c6df917af85305ff4843868663eb
array (
0 => __DIR__ . '/..' . '/symfony/asset',
),
'Symfony\\Component\\AssetMapper\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/asset-mapper',
),
'Symfony\\Bundle\\WebProfilerBundle\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/web-profiler-bundle',
@ -616,6 +625,10 @@ class ComposerStaticInit51f3c6df917af85305ff4843868663eb
array (
0 => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy',
),
'Composer\\Semver\\' =>
array (
0 => __DIR__ . '/..' . '/composer/semver/src',
),
'App\\Tests\\' =>
array (
0 => __DIR__ . '/../..' . '/tests',
@ -639,6 +652,7 @@ class ComposerStaticInit51f3c6df917af85305ff4843868663eb
public static $classMap = array (
'App\\Api\\Controller\\AbstractRestController' => __DIR__ . '/../..' . '/src/Api/Controller/AbstractRestController.php',
'App\\Api\\Controller\\ChartRestController' => __DIR__ . '/../..' . '/src/Api/Controller/ChartRestController.php',
'App\\Api\\Model\\ChartInput' => __DIR__ . '/../..' . '/src/Api/Model/ChartInput.php',
'App\\Api\\Model\\ChartOutput' => __DIR__ . '/../..' . '/src/Api/Model/ChartOutput.php',
'App\\Controller\\ChartController' => __DIR__ . '/../..' . '/src/Controller/ChartController.php',
'App\\Controller\\IndexController' => __DIR__ . '/../..' . '/src/Controller/IndexController.php',
@ -656,6 +670,18 @@ class ComposerStaticInit51f3c6df917af85305ff4843868663eb
'App\\Repository\\UserRepository' => __DIR__ . '/../..' . '/src/Repository/UserRepository.php',
'Collator' => __DIR__ . '/..' . '/symfony/polyfill-intl-icu/Resources/stubs/Collator.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'Composer\\Semver\\Comparator' => __DIR__ . '/..' . '/composer/semver/src/Comparator.php',
'Composer\\Semver\\CompilingMatcher' => __DIR__ . '/..' . '/composer/semver/src/CompilingMatcher.php',
'Composer\\Semver\\Constraint\\Bound' => __DIR__ . '/..' . '/composer/semver/src/Constraint/Bound.php',
'Composer\\Semver\\Constraint\\Constraint' => __DIR__ . '/..' . '/composer/semver/src/Constraint/Constraint.php',
'Composer\\Semver\\Constraint\\ConstraintInterface' => __DIR__ . '/..' . '/composer/semver/src/Constraint/ConstraintInterface.php',
'Composer\\Semver\\Constraint\\MatchAllConstraint' => __DIR__ . '/..' . '/composer/semver/src/Constraint/MatchAllConstraint.php',
'Composer\\Semver\\Constraint\\MatchNoneConstraint' => __DIR__ . '/..' . '/composer/semver/src/Constraint/MatchNoneConstraint.php',
'Composer\\Semver\\Constraint\\MultiConstraint' => __DIR__ . '/..' . '/composer/semver/src/Constraint/MultiConstraint.php',
'Composer\\Semver\\Interval' => __DIR__ . '/..' . '/composer/semver/src/Interval.php',
'Composer\\Semver\\Intervals' => __DIR__ . '/..' . '/composer/semver/src/Intervals.php',
'Composer\\Semver\\Semver' => __DIR__ . '/..' . '/composer/semver/src/Semver.php',
'Composer\\Semver\\VersionParser' => __DIR__ . '/..' . '/composer/semver/src/VersionParser.php',
'DateError' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateError.php',
'DateException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateException.php',
'DateInvalidOperationException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateInvalidOperationException.php',
@ -4470,6 +4496,60 @@ class ComposerStaticInit51f3c6df917af85305ff4843868663eb
'Symfony\\Bundle\\WebProfilerBundle\\Profiler\\TemplateManager' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/Profiler/TemplateManager.php',
'Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/Twig/WebProfilerExtension.php',
'Symfony\\Bundle\\WebProfilerBundle\\WebProfilerBundle' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/WebProfilerBundle.php',
'Symfony\\Component\\AssetMapper\\AssetMapper' => __DIR__ . '/..' . '/symfony/asset-mapper/AssetMapper.php',
'Symfony\\Component\\AssetMapper\\AssetMapperCompiler' => __DIR__ . '/..' . '/symfony/asset-mapper/AssetMapperCompiler.php',
'Symfony\\Component\\AssetMapper\\AssetMapperDevServerSubscriber' => __DIR__ . '/..' . '/symfony/asset-mapper/AssetMapperDevServerSubscriber.php',
'Symfony\\Component\\AssetMapper\\AssetMapperInterface' => __DIR__ . '/..' . '/symfony/asset-mapper/AssetMapperInterface.php',
'Symfony\\Component\\AssetMapper\\AssetMapperRepository' => __DIR__ . '/..' . '/symfony/asset-mapper/AssetMapperRepository.php',
'Symfony\\Component\\AssetMapper\\Command\\AssetMapperCompileCommand' => __DIR__ . '/..' . '/symfony/asset-mapper/Command/AssetMapperCompileCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\DebugAssetMapperCommand' => __DIR__ . '/..' . '/symfony/asset-mapper/Command/DebugAssetMapperCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\ImportMapAuditCommand' => __DIR__ . '/..' . '/symfony/asset-mapper/Command/ImportMapAuditCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\ImportMapInstallCommand' => __DIR__ . '/..' . '/symfony/asset-mapper/Command/ImportMapInstallCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\ImportMapOutdatedCommand' => __DIR__ . '/..' . '/symfony/asset-mapper/Command/ImportMapOutdatedCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\ImportMapRemoveCommand' => __DIR__ . '/..' . '/symfony/asset-mapper/Command/ImportMapRemoveCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\ImportMapRequireCommand' => __DIR__ . '/..' . '/symfony/asset-mapper/Command/ImportMapRequireCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\ImportMapUpdateCommand' => __DIR__ . '/..' . '/symfony/asset-mapper/Command/ImportMapUpdateCommand.php',
'Symfony\\Component\\AssetMapper\\Command\\VersionProblemCommandTrait' => __DIR__ . '/..' . '/symfony/asset-mapper/Command/VersionProblemCommandTrait.php',
'Symfony\\Component\\AssetMapper\\CompiledAssetMapperConfigReader' => __DIR__ . '/..' . '/symfony/asset-mapper/CompiledAssetMapperConfigReader.php',
'Symfony\\Component\\AssetMapper\\Compiler\\AssetCompilerInterface' => __DIR__ . '/..' . '/symfony/asset-mapper/Compiler/AssetCompilerInterface.php',
'Symfony\\Component\\AssetMapper\\Compiler\\CssAssetUrlCompiler' => __DIR__ . '/..' . '/symfony/asset-mapper/Compiler/CssAssetUrlCompiler.php',
'Symfony\\Component\\AssetMapper\\Compiler\\JavaScriptImportPathCompiler' => __DIR__ . '/..' . '/symfony/asset-mapper/Compiler/JavaScriptImportPathCompiler.php',
'Symfony\\Component\\AssetMapper\\Compiler\\SourceMappingUrlsCompiler' => __DIR__ . '/..' . '/symfony/asset-mapper/Compiler/SourceMappingUrlsCompiler.php',
'Symfony\\Component\\AssetMapper\\Event\\PreAssetsCompileEvent' => __DIR__ . '/..' . '/symfony/asset-mapper/Event/PreAssetsCompileEvent.php',
'Symfony\\Component\\AssetMapper\\Exception\\CircularAssetsException' => __DIR__ . '/..' . '/symfony/asset-mapper/Exception/CircularAssetsException.php',
'Symfony\\Component\\AssetMapper\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/asset-mapper/Exception/ExceptionInterface.php',
'Symfony\\Component\\AssetMapper\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/asset-mapper/Exception/LogicException.php',
'Symfony\\Component\\AssetMapper\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/asset-mapper/Exception/RuntimeException.php',
'Symfony\\Component\\AssetMapper\\Factory\\CachedMappedAssetFactory' => __DIR__ . '/..' . '/symfony/asset-mapper/Factory/CachedMappedAssetFactory.php',
'Symfony\\Component\\AssetMapper\\Factory\\MappedAssetFactory' => __DIR__ . '/..' . '/symfony/asset-mapper/Factory/MappedAssetFactory.php',
'Symfony\\Component\\AssetMapper\\Factory\\MappedAssetFactoryInterface' => __DIR__ . '/..' . '/symfony/asset-mapper/Factory/MappedAssetFactoryInterface.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapAuditor' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/ImportMapAuditor.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapConfigReader' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/ImportMapConfigReader.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapEntries' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/ImportMapEntries.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapEntry' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/ImportMapEntry.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapGenerator' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/ImportMapGenerator.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapManager' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/ImportMapManager.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapPackageAudit' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/ImportMapPackageAudit.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapPackageAuditVulnerability' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/ImportMapPackageAuditVulnerability.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapRenderer' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/ImportMapRenderer.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapType' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/ImportMapType.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapUpdateChecker' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/ImportMapUpdateChecker.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapVersionChecker' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/ImportMapVersionChecker.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\JavaScriptImport' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/JavaScriptImport.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\PackageRequireOptions' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/PackageRequireOptions.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\PackageUpdateInfo' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/PackageUpdateInfo.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\PackageVersionProblem' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/PackageVersionProblem.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\RemotePackageDownloader' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/RemotePackageDownloader.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\RemotePackageStorage' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/RemotePackageStorage.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\Resolver\\JsDelivrEsmResolver' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/Resolver/JsDelivrEsmResolver.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\Resolver\\PackageResolverInterface' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/Resolver/PackageResolverInterface.php',
'Symfony\\Component\\AssetMapper\\ImportMap\\Resolver\\ResolvedImportMapPackage' => __DIR__ . '/..' . '/symfony/asset-mapper/ImportMap/Resolver/ResolvedImportMapPackage.php',
'Symfony\\Component\\AssetMapper\\MappedAsset' => __DIR__ . '/..' . '/symfony/asset-mapper/MappedAsset.php',
'Symfony\\Component\\AssetMapper\\MapperAwareAssetPackage' => __DIR__ . '/..' . '/symfony/asset-mapper/MapperAwareAssetPackage.php',
'Symfony\\Component\\AssetMapper\\Path\\LocalPublicAssetsFilesystem' => __DIR__ . '/..' . '/symfony/asset-mapper/Path/LocalPublicAssetsFilesystem.php',
'Symfony\\Component\\AssetMapper\\Path\\PublicAssetsFilesystemInterface' => __DIR__ . '/..' . '/symfony/asset-mapper/Path/PublicAssetsFilesystemInterface.php',
'Symfony\\Component\\AssetMapper\\Path\\PublicAssetsPathResolver' => __DIR__ . '/..' . '/symfony/asset-mapper/Path/PublicAssetsPathResolver.php',
'Symfony\\Component\\AssetMapper\\Path\\PublicAssetsPathResolverInterface' => __DIR__ . '/..' . '/symfony/asset-mapper/Path/PublicAssetsPathResolverInterface.php',
'Symfony\\Component\\Asset\\Context\\ContextInterface' => __DIR__ . '/..' . '/symfony/asset/Context/ContextInterface.php',
'Symfony\\Component\\Asset\\Context\\NullContext' => __DIR__ . '/..' . '/symfony/asset/Context/NullContext.php',
'Symfony\\Component\\Asset\\Context\\RequestStackContext' => __DIR__ . '/..' . '/symfony/asset/Context/RequestStackContext.php',

@ -1,5 +1,89 @@
{
"packages": [
{
"name": "composer/semver",
"version": "3.4.0",
"version_normalized": "3.4.0.0",
"source": {
"type": "git",
"url": "https://github.com/composer/semver.git",
"reference": "35e8d0af4486141bc745f23a29cc2091eb624a32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32",
"reference": "35e8d0af4486141bc745f23a29cc2091eb624a32",
"shasum": ""
},
"require": {
"php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
"phpstan/phpstan": "^1.4",
"symfony/phpunit-bridge": "^4.2 || ^5"
},
"time": "2023-08-31T09:50:34+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Composer\\Semver\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nils Adermann",
"email": "naderman@naderman.de",
"homepage": "http://www.naderman.de"
},
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
},
{
"name": "Rob Bast",
"email": "rob.bast@gmail.com",
"homepage": "http://robbast.nl"
}
],
"description": "Semver library that offers utilities, version constraint parsing and validation.",
"keywords": [
"semantic",
"semver",
"validation",
"versioning"
],
"support": {
"irc": "ircs://irc.libera.chat:6697/composer",
"issues": "https://github.com/composer/semver/issues",
"source": "https://github.com/composer/semver/tree/3.4.0"
},
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"install-path": "./semver"
},
{
"name": "doctrine/cache",
"version": "2.2.0",
@ -4720,6 +4804,87 @@
],
"install-path": "../symfony/asset"
},
{
"name": "symfony/asset-mapper",
"version": "v7.0.5",
"version_normalized": "7.0.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/asset-mapper.git",
"reference": "07133d369eb9f644985584d0877d294c8847a4f0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/asset-mapper/zipball/07133d369eb9f644985584d0877d294c8847a4f0",
"reference": "07133d369eb9f644985584d0877d294c8847a4f0",
"shasum": ""
},
"require": {
"composer/semver": "^3.0",
"php": ">=8.2",
"symfony/filesystem": "^6.4|^7.0",
"symfony/http-client": "^6.4|^7.0"
},
"conflict": {
"symfony/framework-bundle": "<6.4"
},
"require-dev": {
"symfony/asset": "^6.4|^7.0",
"symfony/browser-kit": "^6.4|^7.0",
"symfony/console": "^6.4|^7.0",
"symfony/event-dispatcher-contracts": "^3.0",
"symfony/finder": "^6.4|^7.0",
"symfony/framework-bundle": "^6.4|^7.0",
"symfony/http-foundation": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/web-link": "^6.4|^7.0"
},
"time": "2024-03-04T12:47:58+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\AssetMapper\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Maps directories of assets & makes them available in a public directory with versioned filenames.",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/asset-mapper/tree/v7.0.5"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"install-path": "../symfony/asset-mapper"
},
{
"name": "symfony/browser-kit",
"version": "v7.0.3",

@ -19,6 +19,15 @@
'aliases' => array(),
'dev_requirement' => false,
),
'composer/semver' => array(
'pretty_version' => '3.4.0',
'version' => '3.4.0.0',
'reference' => '35e8d0af4486141bc745f23a29cc2091eb624a32',
'type' => 'library',
'install_path' => __DIR__ . '/./semver',
'aliases' => array(),
'dev_requirement' => false,
),
'doctrine/cache' => array(
'pretty_version' => '2.2.0',
'version' => '2.2.0.0',
@ -671,6 +680,15 @@
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/asset-mapper' => array(
'pretty_version' => 'v7.0.5',
'version' => '7.0.5.0',
'reference' => '07133d369eb9f644985584d0877d294c8847a4f0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/asset-mapper',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/browser-kit' => array(
'pretty_version' => 'v7.0.3',
'version' => '7.0.3.0',

Loading…
Cancel
Save

Powered by TurnKey Linux.