1Charts = {}; 2 3/* Exapmple: 4 <div style="width: 600px;"> 5 <canvas class="ChartClass" href="data.csv?X=IQ&Y=GDP&L=Country&TrendType=P&TrendParam=3"></canvas></div> 6*/ 7 8Charts.Init = function () { 9 // Find all link elements with the ChartClass class 10 var chartElements = document.querySelectorAll('.ChartClass'); 11 // Loop through each link element and add a click event listener 12 for (var i = 0; i < chartElements.length; i++) { 13 var chartElement = chartElements[i]; 14 // Get the href attribute of the link element 15 var href = chartElement.getAttribute('data-chart'); 16 // Parse the URL parameters from the href attribute 17 var params = new URLSearchParams(href.split('?')[1]); 18 var file = href.split('?')[0]; 19 var x = params.get('X'); 20 var y = params.get('Y'); 21 var l = params.get('L'); 22 var trendType = params.get('TrendType'); 23 var trendParam = params.get('TrendParam'); 24 // Call the CreateChart function with the parsed parameters 25 Charts.NewChart(chartElement, file, x, y, l, trendType, trendParam); 26 } 27} 28 29// Resize the chart canvas when the window is resized 30window.addEventListener('load', function () { 31 // Find all link elements with the ChartClass class 32 var charts = document.querySelectorAll('.ChartClass'); 33 // Exist if no charts found on the page. 34 if (charts.length == 0) 35 return; 36 // Load missing cripts. 37 var scripts = [ 38 { script: "/Scripts/math.js", exists: () => { return typeof polyfit !== "undefined"; } }, 39 { script: "/Scripts/chart.js", exists: () => { return typeof Chart !== "undefined"; } }, 40 { script: "/Scripts/regression.js", exists: () => { return typeof regression !== "undefined"; } }, 41 { script: "/Scripts/d3.v7.min.js", exists: () => { return typeof d3 !== "undefined"; } }, 42 ]; 43 Charts.LoadScripts( 44 scripts, 45 // Initialize charts on success. 46 () => { Charts.Init(); }, 47 // Write error message if script loading failed. 48 (s) => { console.log("Error: Load script failed: " + s); }) 49}); 50 51/** Load required missing JavaScripts scripts */ 52Charts.LoadScripts = function LoadScripts(scripts, successCallback, errorCallback) { 53 let remainingScripts = scripts.length; 54 const handleLoad = () => { 55 remainingScripts--; 56 if (remainingScripts === 0) 57 successCallback(); 58 }; 59 const handleError = (event) => { 60 if (errorCallback) 61 errorCallback(event.target.src); 62 }; 63 const findScriptElement = (src) => { 64 const scriptElements = document.getElementsByTagName("script"); 65 for (let i = 0; i < scriptElements.length; i++) { 66 if (scriptElements[i].src === src) 67 return scriptElements[i]; 68 } 69 return null; 70 }; 71 for (let i = 0; i < scripts.length; i++) { 72 const script = scripts[i].script; 73 const exists = scripts[i].exists; 74 if (exists()) { 75 handleLoad(); 76 continue; 77 } 78 let scriptElement = findScriptElement(script); 79 if (scriptElement) { 80 scriptElement.addEventListener("load", handleLoad); 81 scriptElement.addEventListener("error", handleError); 82 } else { 83 scriptElement = document.createElement("script"); 84 scriptElement.src = script; 85 scriptElement.addEventListener("load", handleLoad); 86 scriptElement.addEventListener("error", handleError); 87 document.head.appendChild(scriptElement); 88 } 89 } 90} 91 92Charts.RoundUpToNearestMultiple = function (numbers) { 93 var maxNumber = Math.max.apply(null, numbers); 94 var minNumber = Math.min.apply(null, numbers); 95 var dif = maxNumber - minNumber; 96 var n = 1; 97 if (dif > 50) n = 10 98 if (dif > 5000) n = 1000 99 return Math.ceil(maxNumber / n) * n; 100} 101 102Charts.RoundDownToNearestMultiple = function (numbers) { 103 var maxNumber = Math.max.apply(null, numbers); 104 var minNumber = Math.min.apply(null, numbers); 105 var dif = maxNumber - minNumber; 106 var n = 1; 107 if (dif > 50) n = 10 108 if (dif > 5000) n = 1000 109 return Math.floor(minNumber / n) * n; 110} 111 112Charts.GetStepSize = function (numbers) { 113 var maxNumber = Math.max.apply(null, numbers); 114 var minNumber = Math.min.apply(null, numbers); 115 var dif = maxNumber - minNumber; 116 if (dif >= 10000) return 1000; 117 if (dif >= 25) return 5; 118 return this.GetStepSizeByTicks(minNumber, maxNumber, 200);
119} 120 121Charts.GetStepSizeByTicks = function(min, max, numTicks) { 122 const range = max - min; 123 const rawStepSize = range / numTicks; 124 const magnitude = Math.floor(Math.log10(rawStepSize)); 125 const magnitudeMultiplier = Math.pow(10, magnitude); 126 const normalizedStepSize = Math.ceil(rawStepSize / magnitudeMultiplier) * magnitudeMultiplier; 127 return normalizedStepSize; 128} 129 130Charts.ShowTable = function (data) { 131 // Select the HTML element where you want to display the table 132 var table = d3.select('#table-container').append('table'); 133 // Create the table header row and columns 134 var header = table.append('thead').append('tr'); 135 header.selectAll('th') 136 .data(Object.keys(data[0])) 137 .enter() 138 .append('th') 139 .text(function (d) { return d; }); 140 // Create the table body rows and columns 141 var body = table.append('tbody'); 142 var rows = body.selectAll('tr') 143 .data(data) 144 .enter() 145 .append('tr'); 146 var cells = rows.selectAll('td') 147 .data(function (row) { 148 return Object.values(row); 149 }) 150 .enter() 151 .append('td') 152 .text(function (d) { return d; }); 153} 154 155Charts.SortData = function (data, column1, column2, order1, order2) { 156 var order1Multiplier = (order1 == 'asc') ? 1 : -1; 157 var order2Multiplier = (order2 == 'asc') ? 1 : -1; 158 data.sort(function (a, b) { 159 if (a[column1] < b[column1]) { 160 return -1 * order1Multiplier; 161 } else if (a[column1] > b[column1]) { 162 return 1 * order1Multiplier; 163 } else { 164 if (a[column2] < b[column2]) { 165 return -1 * order2Multiplier; 166 } else if (a[column2] > b[column2]) { 167 return 1 * order2Multiplier; 168 } else { 169 return 0; 170 } 171 } 172 }); 173 return data; 174} 175 176/** Create new chart. */ 177Charts.NewChart = function (element, file, xCol, yCol, lCol, trendType, trentParam) { 178 // Get the data from the CSV file 179 d3.csv(file).then(function (data) { 180 var headers = data.columns.map(function (column) { 181 var newColumn = column.trim(); 182 if (newColumn != column) { 183 data.map(function (d) { 184 d[newColumn] = d[column]; 185 delete d[column]; 186 }); 187 } 188 return newColumn; 189 }); 190 data.columns = headers; 191 // Extract the values from the data 192 data.map(function (row) { 193 if (typeof row[xCol] == "string") 194 row[xCol] = parseFloat(row[xCol].replace(",", "")); 195 if (typeof row[yCol] == "string") 196 row[yCol] = parseFloat(row[yCol].replace(",", "")); 197 }); 198 199 // Remove rows if values failed to parse to a float. 200 data = data.filter(row => { 201 return !isNaN(row[xCol]) && !isNaN(row[yCol]); 202 }); 203 204 data = Charts.SortData(data, xCol, 'desc', yCol, 'desc'); 205 if (xCol == "EDU") Charts.ShowTable(data); 206 207 // Extract the values from the data 208 var xValues = data.map(function (row) { 209 return parseFloat(row[xCol]); 210 }); 211 var yValues = data.map(function (row) { 212 return parseFloat(row[yCol]); 213 }); 214 215 var extraLabels = data.map(function (row) { return row[lCol]; }); 216 // Add cubic trendline to chart 217 var polynomialData = xValues.map(function (x, i) { 218 return [xValues[i], yValues[i]]; 219 }); 220 var polynomialOptions = { order: parseInt(trentParam), precision: 6 }; 221 var polyfit = regression.polynomial(polynomialData, polynomialOptions); 222 var trendlineDataPol = xValues.map(function (x) { 223 return polyfit.predict(x); 224 }); 225 // Define the chart data 226 var chartData = { 227 labels: xValues, 228 datasets: [], 229 }; 230 var mainData = { 231 label: lCol, 232 data: yValues, 233 backgroundColor: "rgba(75,192,192,0.8)", 234 borderWidth: 0, 235 borderColor: "rgba(75,192,192,1.0)", 236 pointRadius: 4, 237 pointHoverRadius: 8, 238 pointHoverBackgroundColor: "rgba(75,192,192,0.8)", 239 pointHoverBorderColor: "rgba(75,192,192,1.0)", 240 pointHitRadius: 8,
241 pointHitBackgroundColor: "rgba(75,192,192,0.8)", 242 pointHitBorderColor: "rgba(75,192,192,1.0)", 243 zIndex: 1 244 } 245 // Define the chart options 246 var chartOptions = { 247 layout: { 248 padding: { left: 10, right: 20, top: 10, bottom: 10 } 249 }, 250 plugins: { 251 legend: { 252 labels: { 253 filter: function (legendItem, chartData) { 254 //return legendItem.text === lCol; // Show legend for main chart. 255 return false; // Hide all legends. 256 }, 257 }, 258 }, 259 customCanvasBackgroundColor: { color: '#F0F0F0', chartAreaColor: '#FFFFFF' }, 260 title: { 261 display: true, text: xCol + " and " + yCol 262 }, 263 tooltip: { 264 callbacks: { 265 title: function (context) { 266 return xCol + ": " + xValues[context[0].dataIndex]; 267 }, 268 label: function (context) { 269 var s = ""; 270 s += extraLabels[context.dataIndex]; 271 s += " " + yCol + " = " + yValues[context.dataIndex]; 272 return s; 273 } 274 } 275 } 276 }, 277 scales: { 278 x: { 279 type: 'linear', 280 min: Charts.RoundDownToNearestMultiple(xValues), 281 max: Charts.RoundUpToNearestMultiple(xValues), 282 ticks: { stepSize: Charts.GetStepSize(xValues) }, 283 title: { display: true, text: xCol } 284 }, 285 y: { 286 type: 'linear', 287 min: Charts.RoundDownToNearestMultiple(yValues), 288 max: Charts.RoundUpToNearestMultiple(yValues), 289 ticks: { stepSize: Charts.GetStepSize(yValues) }, 290 title: { display: true, text: yCol } 291 } 292 }, 293 onResize: function (chart, size) { 294 chart.data.datasets.forEach((dataset) => { 295 if (dataset.label === lCol) { 296 dataset.pointRadius = size.width / 200; 297 dataset.pointHoverRadius = size.width / 100; 298 dataset.pointHitRadius = size.width / 100; 299 } 300 }); 301 chart.update(); 302 }, 303 }; 304 305 if (trendType == "P") { 306 } 307 var trendlinePol = { 308 label: 'Trendline: ' + polyfit.string, 309 backgroundColor: "rgba(255,255,255,0)", 310 borderColor: "rgba(255,0,0,1)", 311 borderWidth: 2, 312 data: trendlineDataPol, 313 pointRadius: 0, 314 showLine: true, 315 zIndex: 10, 316 }; 317 // Push trendline on top. 318 chartData.datasets.push(trendlinePol); 319 // Push main data. 320 chartData.datasets.push(mainData); 321 // Get the canvas element and create the chart 322 var ctx = element.getContext('2d'); 323 var chart = new Chart(ctx, { 324 type: 'line', 325 data: chartData, 326 options: chartOptions, 327 plugins: [SetChartAreaBackgroundPlugin], 328 }); 329 var textCanvas = document.createElement('canvas'); 330 textCanvas.width = element.width; 331 textCanvas.height = element.height; 332 textCanvas.style.position = 'absolute'; 333 textCanvas.style.left = element.offsetLeft + 'px'; 334 textCanvas.style.top = element.offsetTop + 'px'; 335 textCanvas.style.zIndex = 2; 336 chart.update(); 337 // Resize the chart canvas when the window is resized 338 window.addEventListener('resize', function () { 339 chart.resize(); 340 }); 341 }); 342 343 // Note: changes to the plugin code is not reflected to the chart, because the plugin is loaded at chart construction time and editor changes only trigger an chart.update(). 344 const SetChartAreaBackgroundPlugin = { 345 id: 'customCanvasBackgroundColor', 346 beforeDraw: (chart, args, options) => { 347 const { ctx } = chart; 348 const chartArea = chart.chartArea; 349 ctx.save(); 350 ctx.globalCompositeOperation = 'destination-over'; 351 if (options.color) { 352 ctx.fillStyle = options.color; 353 //ctx.fillRect(0, 0, chart.width, chart.height); 354 ctx.fillRect(0, 0, chart.width, chartArea.top); 355 ctx.fillRect(0, chartArea.top, chartArea.left, chartArea.bottom - chartArea.top); 356 ctx.fillRect(chartArea.left + chartArea.width, chartArea.top, chart.width - chartArea.left - chartArea.width, chartArea.height); 357 ctx.fillRect(0, chartArea.top + chartArea.height, chart.width, chart.height - chartArea.top - chartArea.height); 358 } 359 if (options.chartAreaColor) { 360 ctx.fillStyle = options.chartAreaColor; 361 ctx.fillRect(chartArea.left, chartArea.top, chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); 362 }
363 ctx.restore(); 364 } 365 }; 366 367} 368 369
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.