"use strict"; document.addEventListener( "DOMContentLoaded", () => { /* ================================================== UTILITIES ================================================== */ function formatNumber( value, maximumFractionDigits = 2 ) { const number = Number(value); if (!Number.isFinite(number)) { return "0"; } if ( Math.abs(number) < 0.0000001 ) { return "0"; } return number.toLocaleString( "id-ID", { maximumFractionDigits } ); } function setText( element, text ) { if (element) { element.textContent = text; } } function clamp( value, minimum, maximum ) { return Math.min( maximum, Math.max( minimum, value ) ); } function gcd( first, second ) { let a = Math.abs( Math.trunc(first) ); let b = Math.abs( Math.trunc(second) ); while ( b !== 0 ) { const remainder = a % b; a = b; b = remainder; } return a || 1; } /* ================================================== DATASETS ================================================== */ const datasetButtons = document.querySelectorAll( "[data-statistics-eight-dataset]" ); const datasetText = document.getElementById( "statisticsEightDatasetText" ); const liveBars = document.getElementById( "statisticsEightLiveBars" ); const countElement = document.getElementById( "statisticsEightCount" ); const totalElement = document.getElementById( "statisticsEightTotal" ); const meanElement = document.getElementById( "statisticsEightMean" ); const medianElement = document.getElementById( "statisticsEightMedian" ); const modeElement = document.getElementById( "statisticsEightMode" ); const rangeElement = document.getElementById( "statisticsEightRange" ); const dataMessage = document.getElementById( "statisticsEightDataMessage" ); const datasets = { "dataset-a": { name: "Data A", values: [6, 8, 8, 9, 10] }, "dataset-b": { name: "Data B", values: [4, 5, 7, 7, 7, 9] }, "dataset-c": { name: "Data C", values: [10, 12, 14, 16] }, "dataset-d": { name: "Data D", values: [2, 4, 4, 6, 9] } }; /* ================================================== STATISTICAL FUNCTIONS ================================================== */ function getTotal( values ) { return values.reduce( ( total, value ) => total + value, 0 ); } function getMean( values ) { if ( values.length === 0 ) { return 0; } return ( getTotal(values) / values.length ); } function getMedian( values ) { if ( values.length === 0 ) { return 0; } const sorted = [...values].sort( ( first, second ) => first - second ); const middle = Math.floor( sorted.length / 2 ); if ( sorted.length % 2 === 1 ) { return sorted[ middle ]; } return ( sorted[ middle - 1 ] + sorted[ middle ] ) / 2; } function getModes( values ) { const frequencies = new Map(); values.forEach( (value) => { frequencies.set( value, ( frequencies.get( value ) || 0 ) + 1 ); } ); let highestFrequency = 0; frequencies.forEach( (frequency) => { highestFrequency = Math.max( highestFrequency, frequency ); } ); if ( highestFrequency <= 1 ) { return []; } const modes = []; frequencies.forEach( ( frequency, value ) => { if ( frequency === highestFrequency ) { modes.push( value ); } } ); return modes.sort( ( first, second ) => first - second ); } function getRangeValue( values ) { if ( values.length === 0 ) { return 0; } return ( Math.max(...values) - Math.min(...values) ); } /* ================================================== BAR CHART ================================================== */ function renderBars( values ) { if (!liveBars) { return; } liveBars.replaceChildren(); const maximum = Math.max( ...values, 1 ); values.forEach( ( value, index ) => { const wrapper = document.createElement( "div" ); wrapper.className = "statistics-eight-live-bar"; const valueLabel = document.createElement( "strong" ); const bar = document.createElement( "i" ); const positionLabel = document.createElement( "span" ); valueLabel.textContent = formatNumber( value ); bar.style.height = ( Math.max( 4, ( value / maximum ) * 145 ) ) + "px"; positionLabel.textContent = `D${index + 1}`; wrapper.appendChild( valueLabel ); wrapper.appendChild( bar ); wrapper.appendChild( positionLabel ); liveBars.appendChild( wrapper ); } ); } /* ================================================== RENDER DATASET ================================================== */ function renderDataset( key ) { const dataset = datasets[ key ]; if (!dataset) { return; } const values = [...dataset.values]; const total = getTotal( values ); const mean = getMean( values ); const median = getMedian( values ); const modes = getModes( values ); const range = getRangeValue( values ); setText( datasetText, values.join(", ") ); setText( countElement, formatNumber( values.length ) ); setText( totalElement, formatNumber( total ) ); setText( meanElement, formatNumber( mean ) ); setText( medianElement, formatNumber( median ) ); setText( modeElement, ( modes.length > 0 ) ? modes .map( (value) => formatNumber( value ) ) .join(", ") : "Tidak ada modus" ); setText( rangeElement, formatNumber( range ) ); renderBars( values ); if (dataMessage) { const modeText = ( modes.length > 0 ) ? modes .map( (value) => formatNumber( value ) ) .join(", ") : "tidak memiliki modus"; dataMessage.textContent = ( `${dataset.name} memiliki mean ` + `${formatNumber(mean)}, median ` + `${formatNumber(median)}, ` + ( modes.length > 0 ? `modus ${modeText}` : modeText ) + `, dan jangkauan ` + `${formatNumber(range)}.` ); } datasetButtons.forEach( (button) => { const active = button.dataset .statisticsEightDataset === key; button.classList.toggle( "active", active ); button.setAttribute( "aria-pressed", String(active) ); } ); } datasetButtons.forEach( (button) => { button.addEventListener( "click", () => { renderDataset( button.dataset .statisticsEightDataset ); } ); } ); /* ================================================== PROBABILITY MACHINE ================================================== */ const totalOutcomesRange = document.getElementById( "statisticsEightTotalOutcomes" ); const favorableOutcomesRange = document.getElementById( "statisticsEightFavorableOutcomes" ); const totalOutcomesValue = document.getElementById( "statisticsEightTotalOutcomesValue" ); const favorableOutcomesValue = document.getElementById( "statisticsEightFavorableOutcomesValue" ); const machineFavorable = document.getElementById( "statisticsEightMachineFavorable" ); const machineTotal = document.getElementById( "statisticsEightMachineTotal" ); const probabilityFraction = document.getElementById( "statisticsEightProbabilityFraction" ); const probabilityFractionResult = document.getElementById( "statisticsEightProbabilityFractionResult" ); const probabilityDecimal = document.getElementById( "statisticsEightProbabilityDecimal" ); const probabilityPercent = document.getElementById( "statisticsEightProbabilityPercent" ); const probabilityLabel = document.getElementById( "statisticsEightProbabilityLabel" ); const probabilityMessage = document.getElementById( "statisticsEightProbabilityMessage" ); const probabilityPresetButtons = document.querySelectorAll( "[data-statistics-eight-probability]" ); const probabilityState = { favorable: 3, total: 6 }; function getProbabilityLabel( probability ) { if ( probability === 0 ) { return "Mustahil"; } if ( probability === 1 ) { return "Pasti"; } if ( Math.abs( probability - 0.5 ) < 0.0000001 ) { return "Peluang seimbang"; } if ( probability < 0.5 ) { return "Peluang lebih kecil"; } return "Peluang lebih besar"; } function renderProbability() { probabilityState.total = clamp( probabilityState.total, 2, 20 ); probabilityState.favorable = clamp( probabilityState.favorable, 0, probabilityState.total ); const divisor = gcd( probabilityState.favorable, probabilityState.total ); const numerator = probabilityState.favorable / divisor; const denominator = probabilityState.total / divisor; const probability = probabilityState.favorable / probabilityState.total; const percent = probability * 100; const simplifiedFraction = `${numerator} / ${denominator}`; if (totalOutcomesRange) { totalOutcomesRange.value = String( probabilityState.total ); } if (favorableOutcomesRange) { favorableOutcomesRange.max = String( probabilityState.total ); favorableOutcomesRange.value = String( probabilityState.favorable ); } setText( totalOutcomesValue, ( `n(S) = ` + `${probabilityState.total}` ) ); setText( favorableOutcomesValue, ( `n(A) = ` + `${probabilityState.favorable}` ) ); setText( machineFavorable, String( probabilityState.favorable ) ); setText( machineTotal, String( probabilityState.total ) ); setText( probabilityFraction, simplifiedFraction ); setText( probabilityFractionResult, simplifiedFraction ); setText( probabilityDecimal, formatNumber( probability, 3 ) ); setText( probabilityPercent, ( `${formatNumber( percent, 2 )}%` ) ); setText( probabilityLabel, getProbabilityLabel( probability ) ); if (probabilityMessage) { probabilityMessage.textContent = ( `${probabilityState.favorable} dari ` + `${probabilityState.total} hasil ` + `mendukung kejadian, sehingga peluangnya ` + `${simplifiedFraction} atau ` + `${formatNumber(percent, 2)}%.` ); } } if (totalOutcomesRange) { totalOutcomesRange.addEventListener( "input", (event) => { probabilityState.total = clamp( Number( event.target.value ) || 2, 2, 20 ); if ( probabilityState.favorable > probabilityState.total ) { probabilityState.favorable = probabilityState.total; } renderProbability(); } ); } if (favorableOutcomesRange) { favorableOutcomesRange.addEventListener( "input", (event) => { probabilityState.favorable = clamp( Number( event.target.value ) || 0, 0, probabilityState.total ); renderProbability(); } ); } probabilityPresetButtons.forEach( (button) => { button.addEventListener( "click", () => { const raw = button.dataset .statisticsEightProbability || ""; const values = raw .split(",") .map( Number ); if ( values.length !== 2 || values.some( (value) => !Number.isFinite( value ) ) ) { return; } const favorable = values[0]; const total = values[1]; if ( total < 2 || favorable < 0 || favorable > total ) { return; } probabilityState.total = clamp( total, 2, 20 ); probabilityState.favorable = clamp( favorable, 0, probabilityState.total ); renderProbability(); } ); } ); /* ================================================== QUIZ ================================================== */ const quiz = document.getElementById( "statisticsEightQuiz" ); const checkQuizButton = document.getElementById( "checkStatisticsEightQuiz" ); const resetQuizButton = document.getElementById( "resetStatisticsEightQuiz" ); const quizResult = document.getElementById( "statisticsEightQuizResult" ); if (quiz) { const questions = quiz.querySelectorAll( ".statistics-eight-quiz-question" ); questions.forEach( (question) => { const buttons = question.querySelectorAll( ".statistics-eight-quiz-options button" ); buttons.forEach( (button) => { button.addEventListener( "click", () => { buttons.forEach( (otherButton) => { otherButton .classList .remove( "selected", "correct", "incorrect" ); } ); button.classList.add( "selected" ); if (quizResult) { quizResult.textContent = ""; } } ); } ); } ); if (checkQuizButton) { checkQuizButton.addEventListener( "click", () => { const unanswered = Array.from( questions ).some( (question) => !question.querySelector( ".statistics-eight-quiz-options button.selected" ) ); if (unanswered) { if (quizResult) { quizResult.textContent = "Jawab semua pertanyaan terlebih dahulu."; } return; } let score = 0; questions.forEach( (question) => { const answer = question.dataset .answer; const selected = question.querySelector( ".statistics-eight-quiz-options button.selected" ); const buttons = question.querySelectorAll( ".statistics-eight-quiz-options button" ); buttons.forEach( (button) => { button.classList.remove( "correct", "incorrect" ); if ( button.dataset.value === answer ) { button.classList.add( "correct" ); } } ); if ( selected.dataset.value === answer ) { score += 1; } else { selected.classList.add( "incorrect" ); } } ); if (!quizResult) { return; } if ( score === questions.length ) { quizResult.textContent = ( `Hebat! Semua jawaban benar ` + `(${score}/${questions.length}).` ); } else { quizResult.textContent = ( `Kamu menjawab ${score} dari ` + `${questions.length} pertanyaan dengan benar. ` + `Pelajari kembali mean, median, modus, ` + `jangkauan, ruang sampel, dan peluang.` ); } } ); } if (resetQuizButton) { resetQuizButton.addEventListener( "click", () => { questions.forEach( (question) => { const buttons = question.querySelectorAll( ".statistics-eight-quiz-options button" ); buttons.forEach( (button) => { button.classList.remove( "selected", "correct", "incorrect" ); } ); } ); if (quizResult) { quizResult.textContent = ""; } } ); } } /* ================================================== INITIAL RENDER ================================================== */ renderDataset( "dataset-a" ); renderProbability(); } );