diff --git a/.gitignore b/.gitignore index de562d7..4daae38 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,8 @@ .phpunit.result.cache /phpunit.xml ###< symfony/phpunit-bridge ### + +###> symfony/asset-mapper ### +/public/assets/ +/assets/vendor/ +###< symfony/asset-mapper ### diff --git a/assets/app.js b/assets/app.js new file mode 100644 index 0000000..05bf1f2 --- /dev/null +++ b/assets/app.js @@ -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! 🎉'); diff --git a/assets/styles/app.css b/assets/styles/app.css new file mode 100644 index 0000000..25fa51c --- /dev/null +++ b/assets/styles/app.css @@ -0,0 +1,2 @@ +body { +} diff --git a/composer.json b/composer.json index 78890e7..f9e5a06 100644 --- a/composer.json +++ b/composer.json @@ -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" diff --git a/composer.lock b/composer.lock index 7272aac..4326446 100644 --- a/composer.lock +++ b/composer.lock @@ -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", diff --git a/config/packages/asset_mapper.yaml b/config/packages/asset_mapper.yaml new file mode 100644 index 0000000..d1ac653 --- /dev/null +++ b/config/packages/asset_mapper.yaml @@ -0,0 +1,5 @@ +framework: + asset_mapper: + # The paths to make available to the asset mapper. + paths: + - assets/ diff --git a/importmap.php b/importmap.php new file mode 100644 index 0000000..70ebf14 --- /dev/null +++ b/importmap.php @@ -0,0 +1,19 @@ + [ + 'path' => './assets/app.js', + 'entrypoint' => true, + ], +]; diff --git a/public/scripts/chart_loader.js b/public/scripts/chart_loader.js index 7b27586..9e8edac 100644 --- a/public/scripts/chart_loader.js +++ b/public/scripts/chart_loader.js @@ -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,62 +33,67 @@ class ChartLoader { }) } + getShapeIndex(canvas, pos) { + let ctx = canvas.getContext('2d') - addListener(newChart) { - //Click - document.addEventListener('mousemove', (e) => { - const pos = { - x: e.clientX, - y: e.clientY - } - - console.log(pos) + // Get neigboring pixels + let imageData = Array.from(ctx.getImageData(pos.x - 2, pos.y - 2, 5, 5).data) - let ctx = this.detectionCanvas.getContext('2d'); + // Convert into indexes + let indexes = [] + while (imageData.length > 0) { + let pixel = imageData.splice(0, 4) - // Get neigboring pixels - let imageData = Array.from(ctx.getImageData(pos.x - 2, pos.y - 2, 5, 5).data) - - // Convert into indexes - let indexes = [] - while (imageData.length > 0) { - let pixel = imageData.splice(0, 4) - - // only if alpha is 100% - if (pixel[3] === 255) { - let index = (pixel[0] * 256 + pixel[1]) * 256 + pixel[2] - indexes.push(index) - } + // only if alpha is 100% + if (pixel[3] === 255) { + let index = (pixel[0] * 256 + pixel[1]) * 256 + pixel[2] + indexes.push(index) } + } - 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; - else - modeMap[el]++; - if(modeMap[el] > maxCount) - { - maxEl = el; - maxCount = modeMap[el]; - } + 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] } - return maxEl; } + return maxEl + } + + // To avoid edge cases because of anti aliasing + let mostCommonIndex = mode(indexes) + + return mostCommonIndex + } - // To avoid edge cases because of anti aliasing - let mostCommonIndex = mode(indexes) + 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 (mostCommonIndex !== null) { - let obj = newChart.objects[mostCommonIndex] + 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() - setTimeout(() => { - 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() + }) } - drawChart(graphSettings, data) { - this.chart = new Chart(this.canvas, data, graphSettings) + 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) + } - this.chart.updateLegend(graphSettings.displayLegend, this.legend) - this.chart.resizeCanvas(this.parent, this.legend.offsetHeight) + drawChart(graphSettings, data) { //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) } diff --git a/public/scripts/charts/area_chart.js b/public/scripts/charts/area_chart.js index 6b96449..7e81a35 100644 --- a/public/scripts/charts/area_chart.js +++ b/public/scripts/charts/area_chart.js @@ -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} 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)}) } diff --git a/public/scripts/charts/bar_chart.js b/public/scripts/charts/bar_chart.js index 641edc5..ef6f13a 100644 --- a/public/scripts/charts/bar_chart.js +++ b/public/scripts/charts/bar_chart.js @@ -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} 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() } } diff --git a/public/scripts/charts/chart.js b/public/scripts/charts/chart.js index f0eccfc..f127bd3 100644 --- a/public/scripts/charts/chart.js +++ b/public/scripts/charts/chart.js @@ -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} 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) + - 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 += "
  " + categ.col_name + "
" + // 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() + }) + + // Set inner HTML for the legend element + element.innerHTML = "  " + categ.col_name + // Append the element to the legend container + legend.appendChild(element) }) - legend.innerHTML = legendHTML + // 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() - - ctx.globalCompositeOperation = "source-out" - ctx.lineWidth = 3 - ctx.fillStyle = 'rgba(0,0,0,0)' - ctx.strokeStyle = 'rgba(0,0,0,0.7)' - ctx.stroke() + // Clear the entire canvas to prepare for redrawing + ctx.clearRect(0, 0, this.canvas.width, this.canvas.height) + ctx.fill() + + 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() } } diff --git a/public/scripts/charts/donut_chart.js b/public/scripts/charts/donut_chart.js index 048b23e..910d8cb 100644 --- a/public/scripts/charts/donut_chart.js +++ b/public/scripts/charts/donut_chart.js @@ -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} 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() } diff --git a/public/scripts/charts/line_chart.js b/public/scripts/charts/line_chart.js index d271be5..fed8b51 100644 --- a/public/scripts/charts/line_chart.js +++ b/public/scripts/charts/line_chart.js @@ -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} 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)}) } diff --git a/public/scripts/charts/pie_chart.js b/public/scripts/charts/pie_chart.js index 2d492b7..2219006 100644 --- a/public/scripts/charts/pie_chart.js +++ b/public/scripts/charts/pie_chart.js @@ -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} 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() } diff --git a/public/scripts/charts/point_chart.js b/public/scripts/charts/point_chart.js index ab72fa8..0ccd146 100644 --- a/public/scripts/charts/point_chart.js +++ b/public/scripts/charts/point_chart.js @@ -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 + + // Skip empty points + if (values[i] === null || values[i] === undefined) + continue - let x = this.bounds.left + this.bounds.width / (this.dataLen - 1) * i - let y = this.bounds.xAxis - values[i] / this.extreme * this.scale + // 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) - this.data.forEach((categ, colId) => {this.drawPoints(categ.values, colId, categ.color)}) + setTimeout(() => { + console.time("1") + this.data.forEach((categ, colId) => {this.drawPoints(categ.values, colId, categ.color)}) + console.timeEnd("1") + }, 0); } } diff --git a/public/scripts/charts/primitives.js b/public/scripts/charts/primitives.js new file mode 100644 index 0000000..779301e --- /dev/null +++ b/public/scripts/charts/primitives.js @@ -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() + } +} diff --git a/public/scripts/charts/smooth_area_chart.js b/public/scripts/charts/smooth_area_chart.js index 15f6f65..8ffd078 100644 --- a/public/scripts/charts/smooth_area_chart.js +++ b/public/scripts/charts/smooth_area_chart.js @@ -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} 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)}) } diff --git a/public/scripts/charts/smooth_line_chart.js b/public/scripts/charts/smooth_line_chart.js index 6874450..6027c56 100644 --- a/public/scripts/charts/smooth_line_chart.js +++ b/public/scripts/charts/smooth_line_chart.js @@ -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} 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)}) diff --git a/public/scripts/charts/stacked_chart.js b/public/scripts/charts/stacked_chart.js index 5dbc48b..6dae326 100644 --- a/public/scripts/charts/stacked_chart.js +++ b/public/scripts/charts/stacked_chart.js @@ -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} 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() diff --git a/src/Api/Controller/ChartRestController.php b/src/Api/Controller/ChartRestController.php index 3fc3c0f..4b37c95 100644 --- a/src/Api/Controller/ChartRestController.php +++ b/src/Api/Controller/ChartRestController.php @@ -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]); diff --git a/src/Api/Model/ChartInput.php b/src/Api/Model/ChartInput.php new file mode 100644 index 0000000..342146b --- /dev/null +++ b/src/Api/Model/ChartInput.php @@ -0,0 +1,36 @@ +setId(); + $chart->setName($this->name); + //$chart->setName("name"); + $chart->setCode($this->code); + $chart->setMetadata($this->metadata); + $chart->setTable($this->table); + + return $chart; + } +} diff --git a/src/Form/Type/ColumnType.php b/src/Form/Type/ColumnType.php index a63e009..334a04a 100644 --- a/src/Form/Type/ColumnType.php +++ b/src/Form/Type/ColumnType.php @@ -27,7 +27,8 @@ class ColumnType extends AbstractType 'prototype_data' => 0, 'entry_options' => [ 'label' => false, - ] + ], + 'required' => false ]); } diff --git a/src/Form/Type/FontType.php b/src/Form/Type/FontType.php index 9072ff1..01f1fd3 100644 --- a/src/Form/Type/FontType.php +++ b/src/Form/Type/FontType.php @@ -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, + ]); + } } \ No newline at end of file diff --git a/src/Form/Type/MetadataType.php b/src/Form/Type/MetadataType.php index 44139e8..988170a 100644 --- a/src/Form/Type/MetadataType.php +++ b/src/Form/Type/MetadataType.php @@ -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, + ]);*/ + } } \ No newline at end of file diff --git a/symfony.lock b/symfony.lock index 1ca8016..fca5056 100644 --- a/symfony.lock +++ b/symfony.lock @@ -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": { diff --git a/templates/base.html.twig b/templates/base.html.twig index 7e4cf28..2c7023e 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -10,6 +10,7 @@ {% endblock %} {% block javascripts %} + {% block importmap %}{{ importmap('app') }}{% endblock %} {{ encore_entry_script_tags('app') }} {% endblock %} diff --git a/templates/chart.html.twig b/templates/chart.html.twig index 87513fa..ab3933f 100644 --- a/templates/chart.html.twig +++ b/templates/chart.html.twig @@ -11,6 +11,7 @@ {% block javascripts %} {{ parent() }} + diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 84415c1..056715e 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -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', diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 252ee92..2c18ec9 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.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'), ); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index ff7bcdd..2a421e9 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -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', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 61cfa62..61bcfce 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -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", diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 640df8e..21eaa96 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -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',