1// This function handles the Standard Deviation calculation workflow 2function StandardDeviation() { 3 // Sanitize and validate input 4 $("#input").val(regexForCommaSepNumbers("#input")); 5 let input = $("#input").val(); 6 if (Array.from(input.split(","), Number).includes(NaN)) return validtionExp(); 7 8 // Fix trailing comma by adding last number 9 if (input.match(/\,$/g)) { 10 const lastComma = input.lastIndexOf(","); 11 const lastVal = input.slice(lastComma + 1).split(",").map(Number).reduce((a, b) => a + b, 0); 12 $("#input").val(input.slice(0, lastComma) + lastVal); 13 input = $("#input").val(); 14 } 15 let numbers = input.split(','); // Splits into ["10", "34", "23", "54", "9"] 16 let count = numbers.length; // Counts the numbers (5) 17 18 // Ensure input is not empty 19 if (input === "") { 20 alert_box("No input found", "Please Enter at least two comma(,) separated values!", BASE_URL + "web_assets/frontend/images/alert_box/no_words.svg"); 21 $("#result-tab").hide(); 22 return false; 23 } 24 25 // Calculate standard deviation 26 const results = CalculateSD(input); 27 const mean = parseFloat(results.mean); 28 29 // Display results 30 let check_res; 31 if ($("#population1").is(":checked")) { 32 // Population stats 33 $("#sd_sample, #variance_sample").hide(); 34 $("#title").text($("#std_population_head").val()); 35 check_res = SDSteps(1, mean, results.SD_population); 36 } else { 37 // Sample stats 38 $("#sd_pop, #variance_pop").hide(); 39 $("#title").text($("#std_sample_head").val()); 40 check_res = SDSteps(0, mean, results.SD_sample); 41 } 42 43 if (check_res == false) { 44 return false; 45 } 46 47 // Common results in sample & population 48 // Use formatValue (not round_off) so the main result matches the step-by-step 49 // solution for very small/large magnitudes â round_off collapses e.g. 50 // 8.1650E-5 to 0.0001, while the steps show the precise scientific value. 51 $("#num").text(count); 52 // SD headline shows 6 decimals (backend ans1 in the steps matches this) 53 $("#sd_pop").text(formatValue(results.SD_population, 6)); 54 $("#sd_sample").text(formatValue(results.SD_sample, 6)); 55 $("#variance_pop").text(formatValue(results.variance_population)); 56 $("#variance_sample").text(formatValue(results.variance_sample)); 57 58 $("#result_table").hide(); 59 // ", " (not the array's bare-comma join) so narrow screens wrap the list 60 // between numbers instead of splitting one number across two lines 61 $("#diff").text(results.diff.join(", ")); 62 $("#diff_sqr").text(results.diff_sqr.join(", ")); 63 $("#sum_diff_sqr").text(formatValue(results.sum_diff_sqr)); 64 $("#mean_c").text(formatValue(mean)); 65 $("#tool_works").hide(); 66 67 // Toggle view based on sample or population 68 const isPop = $("#population1").is(":checked"); 69 $("#variance_popul, #deviatio_popul, #squared_popula, #sum_of_popula ,#mean_c_population").toggle(isPop); 70 $("#variance_samp, #deviatio_sampl, #squared_sampl, #sum_of_sampl ,#mean_c_sample").toggle(!isPop); 71 $(".calc_btn").attr("disabled", false); 72 setTimeout(() => { 73 $("html, body").animate({ scrollTop: $("#result-tab").offset().top - 100 }, 400); 74 }, 400); 75 $("#tool_results, .res_tab, #result-tab").show(); 76} 77 78 79// SDSteps handles the display of solution steps 80function SDSteps(type, mean, stdDev) { 81 $(".calc_btn").attr("disabled", true); 82 let inputValues = $("#input").val().replaceAll(" ", "").split(",").filter(v => !isNaN(parseFloat(v))).map(Number); 83 84 if (inputValues.length < 2) { 85 alert_box("Validation Error", "Minimum required 2 values comma separated <strong>e.g: <code>130,45,475</code></strong>", BASE_URL + "web_assets/frontend/images/alert_box/no_words.svg"); 86 $("#result-tab").hide(); 87 return false; 88 } 89 90 $.ajaxSetup({ headers: { "X-CSRF-TOKEN": $('meta[name="_token"]').attr("content") } }); 91 92 const endpoint = type === 0 ? "/tool/sd_sample" : "/tool/sd_population"; 93 const generateFunc = type === 0 ? generate_table : generate_table_population; 94 95 $.ajax({ 96 type: "POST", 97 url: endpoint, 98 timeout: 10000, 99 data: { 100 variance: inputValues, 101 mean: mean, 102 SD_sample: stdDev, 103 'e_track_key': getETrackKey() 104 }, 105 success: function (response) { 106 const htmlData = response[0]; 107 const { multiplystr, veriance, powstr } = response[1]; 108 109 const solutionFirst = htmlData.html_first.replace(/\\text{Solution:}/g, $("#php_solution").val()) 110 .replace(/\\overline{X}/g, "XÌ") 111 .replace(/\\text{Mean = }/g, $("#phpmean").val()) 112 .replace(/\\bold{Formula:}/g, $("#phpformula").val()); 113 const solutionSecond = htmlData.html_second.replace(/\\textbf{Putting calculated values in the formula:}/g, $("#php_calculated_values").val()) 114 .replaceAll(" ", " \\space "); 115 116 render_katex(solutionFirst, "html_first"); 117 render_katex(solutionSecond, "html_second"); 118 119 const resultTable = generateFunc(multiplystr, veriance, powstr); 120 $("#steps").show(); 121 $("#html_table").html($("#php_calculated_values_from_table").val()).append(resultTable); 122 $("#show_step").text("Show Steps"); 123 124 setTimeout(() => { 125 $("html, body").animate({ scrollTop: $("#result-tab").offset().top - 100 }, 400); 126 }, 400); 127 128 hideOverlay(); 129 renderSDGraph(mean, stdDev); 130 $(".calc_btn").attr("disabled", false); 131 }, 132 error: function (XMLHttpRequest, textStatus, errorThrown) { 133 const { message, exception, file, line } = JSON.parse(XMLHttpRequest.responseText || '{}'); 134 const fileShort = (file || '').split('\\').pop(); 135 save_error( 136 `${message} | ${exception} | ${fileShort}`, 137 "standard-deviation-calculator.js", 138 line || 0, 139 0 140 ); 141 $(".calc_btn").attr("disabled", false); 142 } 143 }); 144} 145 146let sdChartInstance = null; 147let chartjsLoading = false;
148 149function loadChartJs(callback) { 150 if (typeof Chart !== "undefined") { 151 callback(); 152 return; 153 } 154 if (chartjsLoading) { 155 const checkInterval = setInterval(() => { 156 if (typeof Chart !== "undefined") { 157 clearInterval(checkInterval); 158 callback(); 159 } 160 }, 100); 161 return; 162 } 163 chartjsLoading = true; 164 const script = document.createElement("script"); 165 script.src = "https://cdn.jsdelivr.net/npm/chart.js"; 166 script.async = true; 167 script.onload = () => { 168 chartjsLoading = false; 169 callback(); 170 }; 171 document.head.appendChild(script); 172} 173 174function renderSDGraph(mean, stdDev) { 175 loadChartJs(() => { 176 _executeRenderSDGraph(mean, stdDev); 177 }); 178} 179 180function _executeRenderSDGraph(mean, stdDev) { 181 const graphContainer = $("#sdChart").parent(); 182 183 // Check for zero standard deviation to prevent division by zero 184 if (stdDev <= 0) { 185 $("#graph_show").show(); 186 // If already initialized as chart, destroy it 187 if (sdChartInstance) sdChartInstance.destroy(); 188 // Show a message or a simple line 189 graphContainer.html('<div style="height: 100%; display: flex; align-items: center; justify-content: center; font-family: sans-serif; color: #666; background: #f9f9f9; border-radius: 8px;">Standard Deviation is 0 (All values are identical). No distribution to display.</div>'); 190 // We need to restore the canvas for next calculation 191 setTimeout(() => { 192 if ($("#sdChart").length === 0) { 193 graphContainer.html('<canvas id="sdChart"></canvas>'); 194 } 195 }, 10); 196 return; 197 } 198 199 // Ensure canvas exists (in case it was replaced by zero-SD message) 200 if ($("#sdChart").length === 0) { 201 graphContainer.html('<canvas id="sdChart"></canvas>'); 202 } 203 204 const ctx = document.getElementById('sdChart').getContext('2d'); 205 const label = $("#population1").is(":checked") ? 'Ï' : 's'; 206 const meanLabel = $("#population1").is(":checked") ? 'μ' : 'xÌ'; 207 208 // Generate data points 209 const points = []; 210 const minX = mean - 4 * stdDev; 211 const maxX = mean + 4 * stdDev; 212 const count = 200; 213 const step = (maxX - minX) / count; 214 215 for (let i = 0; i <= count; i++) { 216 const x = minX + (i * step); 217 const y = (1 / (stdDev * Math.sqrt(2 * Math.PI))) * Math.exp(-0.5 * Math.pow((x - mean) / stdDev, 2)); 218 points.push({ x: x, y: y }); 219 } 220 221 const peakY = (1 / (stdDev * Math.sqrt(2 * Math.PI))); 222 223 const datasets = [ 224 { 225 label: '99.7%', 226 data: points.filter(p => p.x >= mean - 3 * stdDev && p.x <= mean + 3 * stdDev), 227 backgroundColor: 'rgba(100, 100, 255, 0.15)', 228 fill: 'origin', 229 pointRadius: 0, 230 tension: 0.4, 231 borderWidth: 0 232 }, 233 { 234 label: '95%', 235 data: points.filter(p => p.x >= mean - 2 * stdDev && p.x <= mean + 2 * stdDev), 236 backgroundColor: 'rgba(100, 100, 255, 0.3)', 237 fill: 'origin', 238 pointRadius: 0, 239 tension: 0.4, 240 borderWidth: 0 241 }, 242 { 243 label: '68%', 244 data: points.filter(p => p.x >= mean - stdDev && p.x <= mean + stdDev), 245 backgroundColor: 'rgba(100, 100, 255, 0.45)', 246 fill: 'origin', 247 pointRadius: 0, 248 tension: 0.4, 249 borderWidth: 0 250 }, 251 { 252 label: `${label}=${formatValue(stdDev, 6)}`, 253 data: points, 254 borderColor: '#0090d0', 255 borderWidth: 3, 256 pointRadius: 0, 257 fill: false, 258 tension: 0.4 259 }, 260 { 261 label: `${meanLabel}=${formatValue(mean)}`, 262 data: [{ x: mean, y: 0 }, { x: mean, y: peakY * 1.05 }], 263 borderColor: '#333', 264 borderWidth: 2, 265 borderDash: [5, 5], 266 pointRadius: 0, 267 fill: false, 268 showLine: true 269 } 270 ]; 271 272 if (sdChartInstance) { 273 sdChartInstance.destroy(); 274 } 275 276 $("#graph_show").show(); 277 278 // Custom plugin to draw percentage labels directly on the graph 279 const percentageLabelsPlugin = { 280 id: 'percentageLabels', 281 afterDraw: (chart) => { 282 const { ctx, chartArea, scales: { x, y } } = chart; 283 ctx.save(); 284 ctx.font = "bold 11px 'Noto Serif', serif"; 285 ctx.fillStyle = '#444'; 286 ctx.textAlign = 'center'; 287 288 const labels = [ 289 { text: '68%', val: mean + stdDev * 0.5 },
290 { text: '95%', val: mean + stdDev * 1.5 }, 291 { text: '99.7%', val: mean + stdDev * 2.5 } 292 ]; 293 294 // On narrow (mobile) canvases 1Ï spans fewer pixels than a label's 295 // text, so the three labels mash into one blob. Keep a minimum gap: 296 // push overlapping labels right, then pull back anything shoved past 297 // the plot's right edge. Wide canvases already satisfy the gap, so 298 // desktop positions are unchanged. 299 const gap = 6; 300 const items = labels.map(l => ({ 301 text: l.text, 302 px: x.getPixelForValue(l.val), 303 w: ctx.measureText(l.text).width 304 })); 305 for (let i = 1; i < items.length; i++) { 306 const minPx = items[i - 1].px + (items[i - 1].w + items[i].w) / 2 + gap; 307 if (items[i].px < minPx) items[i].px = minPx; 308 } 309 for (let i = items.length - 1; i >= 0; i--) { 310 const maxPx = i === items.length - 1 311 ? chartArea.right - items[i].w / 2 312 : items[i + 1].px - (items[i + 1].w + items[i].w) / 2 - gap; 313 if (items[i].px > maxPx) items[i].px = maxPx; 314 } 315 316 const py = y.getPixelForValue(peakY * 0.1); // Position near the bottom 317 items.forEach(l => { 318 ctx.fillText(l.text, l.px, py - 5); 319 }); 320 ctx.restore(); 321 } 322 }; 323 324 sdChartInstance = new Chart(ctx, { 325 type: 'line', 326 data: { 327 datasets: datasets 328 }, 329 plugins: [percentageLabelsPlugin], 330 options: { 331 responsive: true, 332 maintainAspectRatio: false, 333 interaction: { 334 intersect: false, 335 mode: 'index', 336 }, 337 plugins: { 338 title: { 339 display: true, 340 text: 'Standard Deviation Graph', 341 font: { size: 18, weight: 'bold', family: "'Noto Serif', serif" }, 342 color: '#333', 343 padding: { bottom: 20 } 344 }, 345 legend: { 346 position: 'top', 347 labels: { 348 usePointStyle: true, 349 font: { family: "'Noto Serif', serif" }, 350 filter: function(item, chart) { 351 return item.text.includes('='); 352 } 353 } 354 }, 355 tooltip: { 356 bodyFont: { family: "'Noto Serif', serif" }, 357 titleFont: { family: "'Noto Serif', serif" }, 358 callbacks: { 359 label: function(context) { 360 return `x: ${formatValue(context.parsed.x)}, y: ${formatValue(context.parsed.y)}`; 361 } 362 } 363 } 364 }, 365 scales: { 366 x: { 367 type: 'linear', 368 position: 'bottom', 369 grid: { display: false }, 370 title: { display: false }, 371 ticks: { font: { family: "'Noto Serif', serif" } } 372 }, 373 y: { 374 beginAtZero: true, 375 grid: { color: '#f5f5f5' }, 376 title: { display: false }, 377 ticks: { font: { family: "'Noto Serif', serif" } } 378 } 379 } 380 } 381 }); 382} 383 384// Helper Functions 385 386function showOverlay() { 387 $(".loading-overlay").show(); 388 $("span#submit-spinner").hide(); 389 $(".preloader").prop("disabled", true); 390} 391 392function hideOverlay() { 393 $(".loading-overlay").hide(); 394 $("span#submit-spinner").show(); 395 $(".preloader").removeAttr("disabled"); 396} 397 398function generate_table(values, diffs, squares) { 399 let rows = "", sumSquares = 0; 400 401 for (let i = 0; i < diffs.length; i++) { 402 const vRaw = (typeof values[i] === 'string') ? Number(values[i]) : values[i]; 403 const dRaw = (typeof diffs[i] === 'string') ? Number(diffs[i]) : diffs[i]; 404 const sRaw = (typeof squares[i] === 'string') ? Number(squares[i]) : squares[i]; 405 406 rows += `<tr> 407 <td>${formatValue(dRaw)}</td> 408 <td>${formatValue(vRaw)}</td> 409 <td>${formatValue(sRaw)}</td> 410 </tr>`; 411 412 sumSquares += Number(sRaw); 413 } 414 return `<div class="cod-table-container"> 415 <table class="cod-table"> 416 <thead> 417 <tr> 418 <th>X<sub>i</sub></th> 419 <th>X<sub>i</sub> - XÌ</th> 420 <th>(X<sub>i</sub> - XÌ)<sup>2</sup></th> 421 </tr> 422 </thead> 423 <tbody> 424 ${rows} 425 <tr class="total-row"> 426 <td>--</td> 427 <td>--</td> 428 <td>â(X<sub>i</sub> - XÌ)<sup>2</sup> = ${formatValue(sumSquares)}</td> 429 </tr> 430 </tbody> 431 </table> 432 </div>`; 433} 434 435 436function generate_table_population(values, diffs, squares) { 437 let rows = "", sumSquares = 0; 438 439 for (let i = 0; i < diffs.length; i++) { 440 const vRaw = (typeof values[i] === 'string') ? Number(values[i]) : values[i]; 441 const dRaw = (typeof diffs[i] === 'string') ? Number(diffs[i]) : diffs[i]; 442 const sRaw = (typeof squares[i] === 'string') ? Number(squares[i]) : squares[i]; 443 444 rows += `<tr> 445 <td>${formatValue(dRaw)}</td> 446 <td>${formatValue(vRaw)}</td> 447 <td>${formatValue(sRaw)}</td> 448 </tr>`; 449 450 sumSquares += Number(sRaw); 451 } 452 453 return `<div class="cod-table-container"> 454 <table class="cod-table"> 455 <thead> 456 <tr> 457 <th>X<sub>i</sub></th> 458 <th>X<sub>i</sub> - µ</th> 459 <th>(X<sub>i</sub> - µ)<sup>2</sup></th> 460 </tr> 461 </thead> 462 <tbody> 463 ${rows} 464 <tr class="total-row"> 465 <td>--</td> 466 <td>--</td> 467 <td>â(X<sub>i</sub> - µ)<sup>2</sup> = ${formatValue(sumSquares)}</td> 468 </tr> 469 </tbody> 470 </table> 471 </div>`; 472} 473 474 475function render_katex(expr, elementId = "") { 476 const element = document.getElementById(elementId || "render_katex"); 477 try { 478 katex.render(expr, element); 479 } catch (e) { 480 element.innerHTML = `<span class='errorMessage'>${e.message}</span>`;
481 } 482} 483 484$(document).ready(function () { 485 // Handle Enter key on input 486 $(".freq").keydown(function (e) { 487 if (e.which === 13 && !e.shiftKey) { 488 e.preventDefault(); 489 $("#input").val(regexForCommaSepNumbers("#input")); 490 calculateStaDev(); 491 } 492 }); 493 494 // Handle calculate button 495 $(".calc_btn").click(function () { 496 $("#input").val(regexForCommaSepNumbers("#input")); 497 if ($("#population1").is(":checked")) { 498 $("#stan_dev").text("Population"); 499 $("#Main_heading_pdf").text("Standard Deviation (p)"); 500 population("SD"); 501 } else { 502 $("#stan_dev").text("Sample"); 503 $("#Main_heading_pdf").text("Standard Deviation (s)"); 504 sample("SD"); 505 } 506 }); 507 508 // Reset button 509 $(".res_btn").click(function () { 510 $("#result_table").hide(); 511 $("#tool_results").hide(); 512 $("#tool_works").show(); 513 $("#result-tab").hide(); 514 $("#input").val(""); 515 }); 516});
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.