Greenspan/DIR Social-Emotional Milestone Tracking



Social Emotional Milestones Graph

Social Emotional Milestones Tracker

    <!-- Main content container -->
    <div class="container-content">
        <!-- Input Section -->
        <div class="input-section">
            <h2 class="text-2xl font-bold text-gray-800 mb-6 text-center lg:text-left">Enter Your Data Points</h2>

            <div class="flex flex-wrap items-end gap-4 mb-6">
                <div class="flex-1 min-w-[120px]">
                    <label for="ageInput" class="block text-gray-700 text-sm font-semibold mb-2">Age in Months (0-60):</label>
                    <input type="number" id="ageInput" min="0" max="60" value="0"
                           class="shadow-sm appearance-none border rounded-lg w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-200 ease-in-out">
                </div>

                <div class="flex-1 min-w-[120px]">
                    <label for="milestoneInput" class="block text-gray-700 text-sm font-semibold mb-2">Social Emotional Milestone (0-6):</label>
                    <input type="number" id="milestoneInput" min="0" max="6" value="0"
                           class="shadow-sm appearance-none border rounded-lg w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-200 ease-in-out">
                </div>

                <button id="addDataBtn"
                        class="flex-shrink-0 text-white font-bold py-2.5 px-4 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition duration-300 ease-in-out transform hover:scale-105 shadow-md">
                    Add Your Data Point
                </button>
            </div>

            <div id="messageBox" class="mt-4 p-3 text-sm text-red-700 bg-red-100 border border-red-200 rounded-lg hidden" role="alert">
                <!-- Error messages will be displayed here -->
            </div>

            <!-- New section for Baseline Data Point Input -->
            <h2 class="text-2xl font-bold text-gray-800 mt-8 mb-6 text-center lg:text-left">Add Baseline Age Range for Milestone</h2>
            <div class="flex flex-wrap items-end gap-4 mb-6">
                <div class="flex-1 min-w-[120px]">
                    <label for="milestoneInputBaseline" class="block text-gray-700 text-sm font-semibold mb-2">Milestone (0-6):</label>
                    <input type="number" id="milestoneInputBaseline" min="0" max="6" value="0"
                           class="shadow-sm appearance-none border rounded-lg w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent transition duration-200 ease-in-out">
                </div>

                <div class="flex-1 min-w-[120px]">
                    <label for="ageInputBaselineLower" class="block text-gray-700 text-sm font-semibold mb-2">Lower Age (months, 0-60):</label>
                    <input type="number" id="ageInputBaselineLower" min="0" max="60" value="0"
                           class="shadow-sm appearance-none border rounded-lg w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent transition duration-200 ease-in-out">
                </div>

                <div class="flex-1 min-w-[120px]">
                    <label for="ageInputBaselineUpper" class="block text-gray-700 text-sm font-semibold mb-2">Upper Age (months, 0-60):</label>
                    <input type="number" id="ageInputBaselineUpper" min="0" max="60" value="10"
                           class="shadow-sm appearance-none border rounded-lg w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent transition duration-200 ease-in-out">
                </div>

                <button id="addBaselineDataBtn"
                        class="flex-shrink-0 text-white font-bold py-2.5 px-4 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 transition duration-300 ease-in-out transform hover:scale-105 shadow-md">
                    Add Baseline Age Range
                </button>
            </div>


            <h3 class="text-xl font-semibold text-gray-800 mt-8 mb-4 text-center lg:text-left">Current Baseline Age Ranges:</h3>
            <div id="baselineDataPointsList" class="bg-white p-4 rounded-lg border border-gray-200 max-h-60 overflow-y-auto mb-8">
                <p class="text-gray-500 text-center" id="noBaselineMessage">No baseline age ranges defined.</p>
                <!-- Baseline data points will be listed here -->
            </div>
            <button id="clearBaselineDataBtn" class="clear-btn">
                Clear All Baseline Data
            </button>


            <h3 class="text-xl font-semibold text-gray-800 mt-8 mb-4 text-center lg:text-left">Your Data Points:</h3>
            <div id="userDataPointsList" class="bg-white p-4 rounded-lg border border-gray-200 max-h-60 overflow-y-auto">
                <p class="text-gray-500 text-center" id="noUserDataMessage">No user data points added yet.</p>
                <!-- User data points will be listed here -->
            </div>
            <button id="clearUserDataBtn" class="clear-btn">
                Clear All Your Data
            </button>
        </div>

        <!-- Graph Section -->
        <div class="graph-section flex flex-col items-center justify-center">
            <h2 class="text-2xl font-bold text-gray-800 mb-6 text-center">Social Emotional Milestones Graph</h2>
            <canvas id="milestoneGraph" width="800" height="600"></canvas>
        </div>
    </div>
</div>

<script>
    // Get references to HTML elements for user data
    const ageInput = document.getElementById('ageInput');
    const milestoneInput = document.getElementById('milestoneInput');
    const addDataBtn = document.getElementById('addDataBtn');
    const userDataPointsList = document.getElementById('userDataPointsList');
    const noUserDataMessage = document.getElementById('noUserDataMessage');
    const clearUserDataBtn = document.getElementById('clearUserDataBtn');

    // Get references to HTML elements for baseline data
    const milestoneInputBaseline = document.getElementById('milestoneInputBaseline');
    const ageInputBaselineLower = document.getElementById('ageInputBaselineLower');
    const ageInputBaselineUpper = document.getElementById('ageInputBaselineUpper');
    const addBaselineDataBtn = document.getElementById('addBaselineDataBtn');
    const baselineDataPointsList = document.getElementById('baselineDataPointsList');
    const noBaselineMessage = document.getElementById('noBaselineMessage');
    const clearBaselineDataBtn = document.getElementById('clearBaselineDataBtn');

    const messageBox = document.getElementById('messageBox');

    const canvas = document.getElementById('milestoneGraph');
    const ctx = canvas.getContext('2d');

    // Define milestone labels, including "No Milestone" for index 0
    const milestoneLabels = [
        "No Milestone", // Index 0
        "Milestone 1",
        "Milestone 2",
        "Milestone 3",
        "Milestone 4",
        "Milestone 5",
        "Milestone 6"
    ];

    // Editable set of baseline data points, now storing milestone, lowerAge, upperAge
    let baselineDataPoints = [
        // Updated default ranges as per user's request
        { milestone: 0, lowerAge: 0, upperAge: 0, type: 'baseline' },
        { milestone: 1, lowerAge: 0, upperAge: 3, type: 'baseline' },
        { milestone: 2, lowerAge: 2, upperAge: 7, type: 'baseline' },
        { milestone: 3, lowerAge: 3, upperAge: 10, type: 'baseline' },
        { milestone: 4, lowerAge: 9, upperAge: 18, type: 'baseline' },
        { milestone: 5, lowerAge: 18, upperAge: 36, type: 'baseline' },
        { milestone: 6, lowerAge: 30, upperAge: 48, type: 'baseline' }
    ];

    // Store user-added data points
    let userDataPoints = []; // Array of { age: number, milestone: number, type: 'user' }

    // Graph dimensions and padding
    const padding = 60;
    const graphWidth = canvas.width - 2 * padding;
    const graphHeight = canvas.height - 2 * padding;

    // X-axis: Age in Months (0-60)
    const xAxisMax = 60;
    const xAxisInterval = 6; // Mark every 6 months
    const xAxisLabel = "Age in Months";

    // Y-axis: Social Emotional Milestones (0-6)
    const yAxisMin = 0;
    const yAxisMax = 6;
    const yAxisInterval = 1; // Mark every milestone
    const yAxisLabel = "Social Emotional Milestones";

    // Cross-hatch pattern for shading
    let crossHatchPattern = null;

    /**
     * Creates a cross-hatch pattern for canvas filling.
     * @returns {CanvasPattern} The created pattern.
     */
    function createCrossHatchPattern() {
        const patternCanvas = document.createElement('canvas');
        patternCanvas.width = 10;
        patternCanvas.height = 10;
        const pctx = patternCanvas.getContext('2d');
        pctx.strokeStyle = 'rgba(66, 135, 245, 0.4)'; /* Blue with transparency */
        pctx.lineWidth = 1;

        pctx.beginPath();
        pctx.moveTo(0, 10);
        pctx.lineTo(10, 0);
        pctx.stroke();

        pctx.beginPath();
        pctx.moveTo(0, 0);
        pctx.lineTo(10, 10);
        pctx.stroke();

        return ctx.createPattern(patternCanvas, 'repeat');
    }

    /**
     * Displays a message in the message box.
     * @param {string} message - The message to display.
     * @param {string} type - The type of message ('success', 'error', 'info').
     */
    function showMessage(message, type = 'error') {
        messageBox.textContent = message;
        messageBox.className = `mt-4 p-3 text-sm rounded-lg`;
        if (type === 'error') {
            messageBox.classList.add('text-red-700', 'bg-red-100', 'border', 'border-red-200');
        } else if (type === 'success') {
            messageBox.classList.add('text-green-700', 'bg-green-100', 'border', 'border-green-200');
        } else { // info
            messageBox.classList.add('text-blue-700', 'bg-blue-100', 'border', 'border-blue-200');
        }
        messageBox.classList.remove('hidden');
        // Hide message after 5 seconds
        setTimeout(() => {
            messageBox.classList.add('hidden');
        }, 5000);
    }

    /**
     * Converts an age value to its corresponding X-coordinate on the canvas.
     * @param {number} age - The age in months.
     * @returns {number} The X-coordinate.
     */
    function getXCoordinate(age) {
        return padding + (age / xAxisMax) * graphWidth;
    }

    /**
     * Converts a milestone value to its corresponding Y-coordinate on the canvas.
     * Note: Y-axis is inverted (0 at top, max at bottom).
     * @param {number} milestone - The milestone value (0-6).
     * @returns {number} The Y-coordinate.
     */
    function getYCoordinate(milestone) {
        // Clamp milestone value to the valid range [yAxisMin, yAxisMax] for plotting
        const clampedMilestone = Math.max(yAxisMin, Math.min(yAxisMax, milestone));
        return padding + graphHeight - ((clampedMilestone - yAxisMin) / (yAxisMax - yAxisMin)) * graphHeight;
    }

    /**
     * Draws the X and Y axes, labels, and tick marks.
     */
    function drawAxes() {
        ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear canvas before drawing

        ctx.beginPath();
        ctx.strokeStyle = '#333'; // Darker color for axes
        ctx.lineWidth = 2;

        // X-axis
        ctx.moveTo(padding, padding + graphHeight);
        ctx.lineTo(padding + graphWidth, padding + graphHeight);

        // Y-axis
        ctx.moveTo(padding, padding);
        ctx.lineTo(padding, padding + graphHeight);
        ctx.stroke();

        ctx.fillStyle = '#333';
        ctx.font = '14px Inter';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';

        // X-axis labels and ticks
        for (let i = 0; i <= xAxisMax; i += xAxisInterval) {
            const x = getXCoordinate(i);
            ctx.beginPath();
            ctx.moveTo(x, padding + graphHeight);
            ctx.lineTo(x, padding + graphHeight + 5); // Tick mark
            ctx.stroke();
            ctx.fillText(i.toString(), x, padding + graphHeight + 20);
        }
        ctx.fillText(xAxisLabel, padding + graphWidth / 2, padding + graphHeight + 45);

        // Y-axis labels and ticks
        ctx.textAlign = 'right';
        for (let i = yAxisMin; i <= yAxisMax; i += yAxisInterval) {
            const y = getYCoordinate(i);
            ctx.beginPath();
            ctx.moveTo(padding, y);
            ctx.lineTo(padding - 5, y); // Tick mark
            ctx.stroke();
            // Display milestone text label
            ctx.fillText(milestoneLabels[i], padding - 10, y);
        }
        ctx.save(); // Save context state
        ctx.translate(padding - 45, padding + graphHeight / 2); // Translate to position for vertical text
        ctx.rotate(-Math.PI / 2); // Rotate 90 degrees counter-clockwise
        ctx.fillText(yAxisLabel, 0, 0); // Draw text
        ctx.restore(); // Restore context state
    }

    /**
     * Plots a single line dataset, ensuring it starts from (0,0) if no point at age 0 exists.
     * @param {Array<Object>} points - An array of data points to plot (e.g., {age, milestone}).
     * @param {string} lineColor - The color of the line.
     * @param {string} pointColor - The color of the points.
     */
    function plotSingleLine(points, lineColor, pointColor) {
        let pointsToPlot = [...points]; // Copy original points

        // Add (0,0) if not explicitly present to ensure line starts from origin
        const originExists = pointsToPlot.some(p => p.age === 0 && p.milestone === 0);
        if (!originExists) {
            pointsToPlot.push({ age: 0, milestone: 0 });
        }

        // Sort points by age for correct line plotting
        pointsToPlot.sort((a, b) => a.age - b.age);

        ctx.beginPath();
        ctx.strokeStyle = lineColor;
        ctx.lineWidth = 3;

        // Move to the first point
        ctx.moveTo(getXCoordinate(pointsToPlot[0].age), getYCoordinate(pointsToPlot[0].milestone));

        // Draw lines to subsequent points
        for (let i = 1; i < pointsToPlot.length; i++) {
            const point = pointsToPlot[i];
            ctx.lineTo(getXCoordinate(point.age), getYCoordinate(point.milestone));
        }
        ctx.stroke();

        // Draw points (only for the original data points, not the synthetic (0,0) if it was added)
        ctx.fillStyle = pointColor;
        points.forEach(point => {
            const x = getXCoordinate(point.age);
            const y = getYCoordinate(point.milestone);
            ctx.beginPath();
            ctx.arc(x, y, 5, 0, Math.PI * 2); // Draw a circle for each point
            ctx.fill();
            ctx.closePath();
        });

        // If (0,0) was not an original point, draw a point for it.
        if (!originExists) {
            const x = getXCoordinate(0);
            const y = getYCoordinate(0);
            ctx.beginPath();
            ctx.arc(x, y, 5, 0, Math.PI * 2);
            ctx.fill();
            ctx.closePath();
        }
    }

    /**
     * Plots the baseline range as a shaded area with two bounding lines.
     * @param {Array<Object>} baselinePoints - Array of {milestone, lowerAge, upperAge}.
     * @param {CanvasPattern} pattern - The pattern for shading.
     */
    function plotBaselineRange(baselinePoints, pattern) {
        if (baselinePoints.length === 0) return;

        // Sort baseline points by milestone for correct plotting
        baselinePoints.sort((a, b) => a.milestone - b.milestone);

        // Prepare points for lower and upper age lines, including (0,0) if not present
        const lowerAgeLinePoints = [];
        const upperAgeLinePoints = [];

        // Add (0,0) to ensure lines start from origin if no milestone 0 point is provided
        const originMilestone0 = baselinePoints.find(p => p.milestone === 0);
        if (!originMilestone0) {
            lowerAgeLinePoints.push({ age: 0, milestone: 0 });
            upperAgeLinePoints.push({ age: 0, milestone: 0 });
        }

        baselinePoints.forEach(p => {
            lowerAgeLinePoints.push({ age: p.lowerAge, milestone: p.milestone });
            upperAgeLinePoints.push({ age: p.upperAge, milestone: p.milestone });
        });

        // Sort these temporary arrays by age
        lowerAgeLinePoints.sort((a, b) => a.age - b.age);
        upperAgeLinePoints.sort((a, b) => a.age - b.age);


        ctx.beginPath();

        // Draw along the lower boundary
        ctx.moveTo(getXCoordinate(lowerAgeLinePoints[0].age), getYCoordinate(lowerAgeLinePoints[0].milestone));
        for (let i = 1; i < lowerAgeLinePoints.length; i++) {
            const point = lowerAgeLinePoints[i];
            ctx.lineTo(getXCoordinate(point.age), getYCoordinate(point.milestone));
        }

        // Draw back along the upper boundary in reverse order
        for (let i = upperAgeLinePoints.length - 1; i >= 0; i--) {
            const point = upperAgeLinePoints[i];
            ctx.lineTo(getXCoordinate(point.age), getYCoordinate(point.milestone));
        }
        ctx.closePath();
        ctx.fillStyle = pattern;
        ctx.fill();

        // Draw the lower age boundary line
        ctx.beginPath();
        ctx.strokeStyle = '#6b7280'; // Darker gray
        ctx.lineWidth = 2;
        ctx.moveTo(getXCoordinate(lowerAgeLinePoints[0].age), getYCoordinate(lowerAgeLinePoints[0].milestone));
        for (let i = 1; i < lowerAgeLinePoints.length; i++) {
            const point = lowerAgeLinePoints[i];
            ctx.lineTo(getXCoordinate(point.age), getYCoordinate(point.milestone));
        }
        ctx.stroke();

        // Draw the upper age boundary line
        ctx.beginPath();
        ctx.strokeStyle = '#9ca3af'; // Lighter gray
        ctx.lineWidth = 2;
        ctx.moveTo(getXCoordinate(upperAgeLinePoints[0].age), getYCoordinate(upperAgeLinePoints[0].milestone));
        for (let i = 1; i < upperAgeLinePoints.length; i++) {
            const point = upperAgeLinePoints[i];
            ctx.lineTo(getXCoordinate(point.age), getYCoordinate(point.milestone));
        }
        ctx.stroke();

        // Draw points for lower and upper bounds
        ctx.fillStyle = '#4b5563'; // Darker gray for points
        baselinePoints.forEach(point => {
            ctx.beginPath();
            ctx.arc(getXCoordinate(point.lowerAge), getYCoordinate(point.milestone), 5, 0, Math.PI * 2);
            ctx.fill();
            ctx.closePath();

            ctx.beginPath();
            ctx.arc(getXCoordinate(point.upperAge), getYCoordinate(point.milestone), 5, 0, Math.PI * 2);
            ctx.fill();
            ctx.closePath();
        });
    }


    /**
     * Redraws the entire graph.
     */
    function redrawGraph() {
        drawAxes();
        plotBaselineRange(baselineDataPoints, crossHatchPattern); // Plot baseline range first
        plotSingleLine(userDataPoints, '#3b82f6', '#1e40af'); // Then plot user data on top
    }

    /**
     * Updates the list of baseline data points displayed on the page.
     */
    function updateBaselineDataPointsList() {
        baselineDataPointsList.innerHTML = ''; // Clear existing list

        if (baselineDataPoints.length === 0) {
            noBaselineMessage.classList.remove('hidden');
            baselineDataPointsList.appendChild(noBaselineMessage);
            return;
        } else {
            noBaselineMessage.classList.add('hidden');
        }

        baselineDataPoints.forEach((point, index) => {
            const listItem = document.createElement('div');
            listItem.className = 'baseline-point-item text-gray-700 text-base';
            let milestoneLabelText = milestoneLabels[point.milestone];

            if (point.milestone < yAxisMin || point.milestone > yAxisMax) {
                milestoneLabelText += ' (Out of Range)';
            }
            if (point.lowerAge < 0 || point.lowerAge > xAxisMax || point.upperAge < 0 || point.upperAge > xAxisMax) {
                milestoneLabelText += ' (Age Out of Range)';
            }


            listItem.innerHTML = `
                <span>Milestone: ${point.milestone} (${milestoneLabelText}), Age Range: ${point.lowerAge} - ${point.upperAge} months</span>
                <button class="delete-btn" data-index="${index}" data-type="baseline">Delete</button>
            `;
            baselineDataPointsList.appendChild(listItem);
        });

        // Add event listeners to delete buttons for baseline points
        document.querySelectorAll('#baselineDataPointsList .delete-btn').forEach(button => {
            button.addEventListener('click', (event) => {
                const indexToDelete = parseInt(event.target.dataset.index);
                deleteDataPoint(indexToDelete, 'baseline');
            });
        });
    }

    /**
     * Updates the list of user-added data points displayed on the page.
     */
    function updateUserDataPointsList() {
        userDataPointsList.innerHTML = ''; // Clear existing list

        if (userDataPoints.length === 0) {
            noUserDataMessage.classList.remove('hidden');
            userDataPointsList.appendChild(noUserDataMessage);
            return;
        } else {
            noUserDataMessage.classList.add('hidden');
        }

        userDataPoints.forEach((point, index) => {
            const listItem = document.createElement('div');
            listItem.className = 'data-point-item text-gray-700 text-base';
            listItem.innerHTML = `
                <span>Age: ${point.age} months, Milestone: ${milestoneLabels[point.milestone]}</span>
                <button class="delete-btn" data-index="${index}" data-type="user">Delete</button>
            `;
            userDataPointsList.appendChild(listItem);
        });

        // Add event listeners to delete buttons for user points
        document.querySelectorAll('#userDataPointsList .delete-btn').forEach(button => {
            button.addEventListener('click', (event) => {
                const indexToDelete = parseInt(event.target.dataset.index);
                deleteDataPoint(indexToDelete, 'user');
            });
        });
    }

    /**
     * Deletes a data point from the specified array.
     * @param {number} index - The index of the data point to delete.
     * @param {string} type - The type of data ('user' or 'baseline').
     */
    function deleteDataPoint(index, type) {
        if (type === 'user') {
            if (index >= 0 && index < userDataPoints.length) {
                userDataPoints.splice(index, 1);
                updateUserDataPointsList();
                showMessage('Your data point deleted.', 'info');
            }
        } else if (type === 'baseline') {
            if (index >= 0 && index < baselineDataPoints.length) {
                baselineDataPoints.splice(index, 1);
                updateBaselineDataPointsList();
                showMessage('Baseline data point deleted.', 'info');
            }
        }
        redrawGraph();
    }

    // Event listener for adding user data points
    addDataBtn.addEventListener('click', () => {
        const age = parseInt(ageInput.value);
        const milestone = parseInt(milestoneInput.value);

        // Input validation
        if (isNaN(age) || isNaN(milestone)) {
            showMessage('Please enter valid numbers for Age and Milestone for your data.', 'error');
            return;
        }
        if (age < 0 || age > xAxisMax) {
            showMessage(`Age in Months for your data must be between 0 and ${xAxisMax}.`, 'error');
            return;
        }
        if (milestone < 0 || milestone > yAxisMax) {
            showMessage(`Social Emotional Milestone for your data must be between 0 and ${yAxisMax}.`, 'error');
            return;
        }

        // Check for duplicate age in user data, if so, update the milestone
        const existingPointIndex = userDataPoints.findIndex(point => point.age === age);
        if (existingPointIndex !== -1) {
            userDataPoints[existingPointIndex].milestone = milestone;
            showMessage(`Updated milestone for Age ${age} in your data to ${milestoneLabels[milestone]}.`, 'success');
        } else {
            userDataPoints.push({ age, milestone, type: 'user' });
            showMessage('Your data point added successfully!', 'success');
        }

        updateUserDataPointsList();
        redrawGraph();

        // Reset input fields
        ageInput.value = 0;
        milestoneInput.value = 0;
    });

    // Event listener for adding baseline data points
    addBaselineDataBtn.addEventListener('click', () => {
        const milestone = parseInt(milestoneInputBaseline.value);
        const lowerAge = parseInt(ageInputBaselineLower.value);
        const upperAge = parseInt(ageInputBaselineUpper.value);

        // Input validation
        if (isNaN(milestone) || isNaN(lowerAge) || isNaN(upperAge)) {
            showMessage('Please enter valid numbers for Milestone, Lower Age, and Upper Age for baseline data.', 'error');
            return;
        }
        if (milestone < 0 || milestone > yAxisMax) {
            showMessage(`Milestone for baseline data must be between 0 and ${yAxisMax}.`, 'error');
            return;
        }
        if (lowerAge < 0 || lowerAge > xAxisMax || upperAge < 0 || upperAge > xAxisMax) {
             showMessage(`Lower and Upper Ages for baseline data must be between 0 and ${xAxisMax}.`, 'error');
             return;
        }
        if (lowerAge > upperAge) {
            showMessage('Lower Age cannot be greater than Upper Age for baseline data.', 'error');
            return;
        }

        // Check for duplicate milestone in baseline data, if so, update the range
        const existingPointIndex = baselineDataPoints.findIndex(point => point.milestone === milestone);
        if (existingPointIndex !== -1) {
            baselineDataPoints[existingPointIndex].lowerAge = lowerAge;
            baselineDataPoints[existingPointIndex].upperAge = upperAge;
            showMessage(`Updated baseline age range for Milestone ${milestone} to ${lowerAge}-${upperAge} months.`, 'success');
        } else {
            baselineDataPoints.push({ milestone, lowerAge, upperAge, type: 'baseline' });
            showMessage('Baseline age range added successfully!', 'success');
        }

        updateBaselineDataPointsList();
        redrawGraph();

        // Reset input fields
        milestoneInputBaseline.value = 0;
        ageInputBaselineLower.value = 0;
        ageInputBaselineUpper.value = 10;
    });

    // Event listener for clearing all user data
    clearUserDataBtn.addEventListener('click', () => {
        userDataPoints = []; // Clear the array
        updateUserDataPointsList(); // Update the displayed list
        redrawGraph(); // Redraw the graph
        showMessage('All your data points cleared.', 'info');
    });

    // Event listener for clearing all baseline data
    clearBaselineDataBtn.addEventListener('click', () => {
        baselineDataPoints = []; // Clear the array
        updateBaselineDataPointsList(); // Update the displayed list
        redrawGraph(); // Redraw the graph
        showMessage('All baseline data points cleared.', 'info');
    });


    // Initial drawing of the graph and list
    window.onload = function() {
        crossHatchPattern = createCrossHatchPattern(); // Initialize pattern once
        redrawGraph();
        updateBaselineDataPointsList();
        updateUserDataPointsList();
    };

    // Handle canvas resizing for responsiveness (optional, but good practice)
    window.addEventListener('resize', () => {
        redrawGraph();
    });

</script>

Study Questions

  1. What is the Greenspan/DIR model?
    1. An intervention that follows a chronological order to teach skills.
    2. Something you do to mitigate affective experiences available to children.
    3. Teaching a behavior to address a specific symptom a child displays.
    4. A model used to identify a child’s social-emotional strengths and weaknesses.
  2. Which is NOT a part of the Greenspan approach?
    1. Relating within meaningful, positive relationships
    2. Communication across all therapeutic curriculums
    3. Teaching a child an outcome, what to say, and what to do
    4. Encouraging a child to do the thinking
  3. The Greenspan approach is a parent-_____ approach and a therapist-____ approach.
    1. supported; centered
    2. centered; supported
    3. supported; directed
    4. advocated; centered