<?xml version="1.0" encoding="UTF-16"?>
<!-- This XML file has been created by WebEJS 1.0. Visit http://t.um.es/webejs -->
<!-- Please, open it with WebEJS or save the file to your hard disk on your EJS' user directory and open it with Desktop Ejs 6.01 or later. -->
<Osejs version="7.0" password="">
<Osejs.Information>
<Title><![CDATA[Line Symmetry Shading]]></Title>
<Copyright><![CDATA[]]></Copyright>
<Keywords><![CDATA[]]></Keywords>
<Password><![CDATA[unused]]></Password>
<Level><![CDATA[]]></Level>
<Language><![CDATA[]]></Language>
<Abstract><![CDATA[]]></Abstract>
<FixedNavigationBar>false</FixedNavigationBar>
<RunAlways>true</RunAlways>
<UseInterpreter>true</UseInterpreter>
<UseDeltaForODE>false</UseDeltaForODE>
<PreviewFullModel>false</PreviewFullModel>
<ModelTab></ModelTab>
<ModelTabTitle><![CDATA[]]></ModelTabTitle>
<ModelName><![CDATA[]]></ModelName>
<CSSFile></CSSFile>
<HTMLHead><![CDATA[<script 
async="true" src="https://www.googletagmanager.com/gtag/js?id=G-S9EWRY1CPJ"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-S9EWRY1CPJ');
</script>

<style type="text/css">
.metadata { display: none; }
</style>]]></HTMLHead>
<SaveInXMLFormat>true</SaveInXMLFormat>
<IncludeSource>true</IncludeSource>
<IncludeLibrary>true</IncludeLibrary>
<UglifyJS>false</UglifyJS>
<Logo></Logo>
<Author><![CDATA[]]></Author>
<AuthorLogo></AuthorLogo>
<DetectedFiles><![CDATA[]]></DetectedFiles>
<AuxiliaryFiles><![CDATA[;./lib/xapiwrapper.min.js]]></AuxiliaryFiles>
</Osejs.Information>
<Osejs.Description>
</Osejs.Description>
<Osejs.Model>
<Osejs.Model.FramesPerSecond>20</Osejs.Model.FramesPerSecond>
<Osejs.Model.StepsPerDisplay>1</Osejs.Model.StepsPerDisplay>
<Osejs.Model.RealTimeVariable></Osejs.Model.RealTimeVariable>
<Osejs.Model.Autostart>true</Osejs.Model.Autostart>
<Osejs.Model.Variables>
<Osejs.Model.Variables.Page>
<Type>undefined</Type>
<Name>Var Table 1</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<PageComment><![CDATA[]]></PageComment>
<Variable>
<Name><![CDATA[selectedLine]]></Name>
<Value><![CDATA["Vertical"]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[sideOptions]]></Name>
<Value><![CDATA[["Left", "Right"]]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[selectedSide]]></Name>
<Value><![CDATA["Left"]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[graphX]]></Name>
<Value><![CDATA[6]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[graphY]]></Name>
<Value><![CDATA[6]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[lineX1]]></Name>
<Value><![CDATA[0]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[lineY1]]></Name>
<Value><![CDATA[0]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[lineX2]]></Name>
<Value><![CDATA[0]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[lineY2]]></Name>
<Value><![CDATA[0]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[start]]></Name>
<Value><![CDATA[false]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[timer]]></Name>
<Value><![CDATA[30]]></Value>
<Type><![CDATA[int]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[]]></Name>
<Value><![CDATA[]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
</Content>
</Osejs.Model.Variables.Page>
<Osejs.Model.Variables.Page>
<Type>VARIABLE_EDITOR</Type>
<Name>shaded</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<PageComment><![CDATA[]]></PageComment>
<Variable>
<Name><![CDATA[plottedCellPositions]]></Name>
<Value><![CDATA[[]]]></Value>
<Type><![CDATA[Object]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[mirroredCellPositions]]></Name>
<Value><![CDATA[[]]]></Value>
<Type><![CDATA[Object]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[plottedPolygonPositions]]></Name>
<Value><![CDATA[[]]]></Value>
<Type><![CDATA[Object]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[mirroredPolygonPositions]]></Name>
<Value><![CDATA[[]]]></Value>
<Type><![CDATA[Object]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[displayPolygons]]></Name>
<Value><![CDATA[false]]></Value>
<Type><![CDATA[boolean]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[displayCells]]></Name>
<Value><![CDATA[false]]></Value>
<Type><![CDATA[boolean]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[plottedElementInteracted]]></Name>
<Value><![CDATA[-1]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[mirroredElementInteracted]]></Name>
<Value><![CDATA[-1]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[plottedCellColor]]></Name>
<Value><![CDATA[[]]]></Value>
<Type><![CDATA[Object]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[mirroredCellColor]]></Name>
<Value><![CDATA[[]]]></Value>
<Type><![CDATA[Object]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[plottedCellSelected]]></Name>
<Value><![CDATA[[]]]></Value>
<Type><![CDATA[Object]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[mirroredCellSelected]]></Name>
<Value><![CDATA[[]]]></Value>
<Type><![CDATA[Object]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[plottedPolygonSelected]]></Name>
<Value><![CDATA[[]]]></Value>
<Type><![CDATA[Object]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[mirroredPolygonSelected]]></Name>
<Value><![CDATA[[]]]></Value>
<Type><![CDATA[Object]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[plottedPolygonInteracted]]></Name>
<Value><![CDATA[-1]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[mirroredPolygonInteracted]]></Name>
<Value><![CDATA[-1]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[]]></Name>
<Value><![CDATA[]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
</Content>
</Osejs.Model.Variables.Page>
<Osejs.Model.Variables.Page>
<Type>VARIABLE_EDITOR</Type>
<Name>shaded2</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<PageComment><![CDATA[]]></PageComment>
<Variable>
<Name><![CDATA[plottedColor]]></Name>
<Value><![CDATA["Blue"]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[mirroredColor]]></Name>
<Value><![CDATA["Red"]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[plottedColorDisabled]]></Name>
<Value><![CDATA["#9375ff"]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[]]></Name>
<Value><![CDATA[]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
</Content>
</Osejs.Model.Variables.Page>
<Osejs.Model.Variables.Page>
<Type>VARIABLE_EDITOR</Type>
<Name>xApi</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<PageComment><![CDATA[]]></PageComment>
<Variable>
<Name><![CDATA[score]]></Name>
<Value><![CDATA[0]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[pastAttemptsByScenario]]></Name>
<Value><![CDATA[]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[scenarioStartTime]]></Name>
<Value><![CDATA[""]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[currentAttempt]]></Name>
<Value><![CDATA[{}]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
<Variable>
<Name><![CDATA[]]></Name>
<Value><![CDATA[]]></Value>
<Type><![CDATA[double]]></Type>
<Dimension><![CDATA[]]></Dimension>
<Domain><![CDATA[]]></Domain>
<Comment><![CDATA[]]></Comment>
</Variable>
</Content>
</Osejs.Model.Variables.Page>
</Osejs.Model.Variables>
<Osejs.Model.Initialization>
<Osejs.Model.Initialization.Page>
<Type>undefined</Type>
<Name>init</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
initFunctions();

if (pastAttemptsByScenario == undefined) {
    pastAttemptsByScenario = [];
}

]]></Code>
</Content>
</Osejs.Model.Initialization.Page>
</Osejs.Model.Initialization>
<Osejs.Model.Evolution>
</Osejs.Model.Evolution>
<Osejs.Model.Constraints>
</Osejs.Model.Constraints>
<Osejs.Model.Library>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>initFunctions</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
function initFunctions() {
    reset();
    initSeparationLine();
    initGrids();
    
    plottedCellColor = Array(18).fill("White");
    mirroredCellColor = Array(18).fill("White");
    
    plottedPolygonColor = Array(21).fill("White");
    mirroredPolygonColor = Array(21).fill("White");
    
    plottedCellSelected = Array(18).fill(false);
    mirroredCellSelected = Array(18).fill(false);
    
    plottedPolygonSelected = Array(21).fill(false);
    mirroredPolygonSelected = Array(21).fill(false);
}
]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>reset</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
function reset () {
    plottedCellPositions = [];
    mirroredCellPositions = [];
    plottedPolygonPositions = [];
    mirroredPolygonPositions = [];
    
    displayPolygons = false;
}
]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>undefined</Type>
<Name>initSeparationLine</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
function initSeparationLine() {
    switch (selectedLine) {
        case "Vertical":
            lineX1 = graphX / 2;
            lineY1 = 0;
            lineX2 = 0;
            lineY2 = graphY;
            break;
        case "Horizontal":
            lineX1 = 0;
            lineY1 = graphY / 2;
            lineX2 = graphX;
            lineY2 = 0;
            break;
        case "Diagonal (+)":
            lineX1 = 0;
            lineY1 = 0;
            lineX2 = graphX;
            lineY2 = graphY;
            break;
        case "Diagonal (-)":
            lineX1 = 0;
            lineY1 = graphY;
            lineX2 = graphX;
            lineY2 = -graphY;
            break;
        default:
            break;
    }
}
]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>initGrids</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
function initGrids() {
    switch (selectedLine) {
        case "Vertical":
            displayCells = true;
            displayPolygons = false;
            plottedCellPositions = undefined;
            mirroredCellPositions = undefined;
            plottedPolygonPositions = undefined;
            mirroredPolygonPositions = undefined;
                
            if (selectedSide === "Left") {
                plottedCellPositions = getPlottedCellPositions(0, 3, 0, 6, 1);
                mirroredCellPositions = getPlottedCellPositions(3, 6, 0, 6, 1);
            } else {
                plottedCellPositions = getPlottedCellPositions(3, 6, 0, 6, 1);
                mirroredCellPositions = getPlottedCellPositions(0, 3, 0, 6, 1);
            }
            break;

        case "Horizontal":
            displayCells = true;
            displayPolygons = false;
            plottedPolygonPositions = [];
            mirroredPolygonPositions = [];
            
            if (selectedSide === "Top") {
                plottedCellPositions = getPlottedCellPositions(0, 6, 6, 3, 1);
                mirroredCellPositions = getPlottedCellPositions(0, 6, 3, 0, 1);
            } else {
                plottedCellPositions = getPlottedCellPositions(0, 6, 3, 0, 1);
                mirroredCellPositions = getPlottedCellPositions(0, 6, 6, 3, 1);
            }
            break;

        case "Diagonal (+)":
            displayCells = false;
            displayPolygons = true;
            plottedPolygonPositions = [];
            mirroredPolygonPositions = [];
            
            // Diagonal (+): y = x (from bottom-left to top-right)
            const diagonalPositive = getGridWithDiagonalSplits(0, 6, 0, 6, 1, "positive");

            if (selectedSide === "Left") {
                // "Left" corresponds to polygons above the diagonal
                plottedPolygonPositions = diagonalPositive.plottedPolygonPositions;
                mirroredPolygonPositions = diagonalPositive.mirroredPolygonPositions;
            } else {
                // "Right" corresponds to polygons below the diagonal
                plottedPolygonPositions = diagonalPositive.mirroredPolygonPositions;
                mirroredPolygonPositions = diagonalPositive.plottedPolygonPositions;
            }
            break;

        case "Diagonal (-)":
            displayCells = false;
            displayPolygons = true;
            plottedPolygonPositions = [];
            mirroredPolygonPositions = [];
            
            // Diagonal (-): y = -x + 6 (from top-left to bottom-right)
            const diagonalNegative = getGridWithDiagonalSplits(0, 6, 0, 6, 1, "negative");

            if (selectedSide === "Right") {
                // "Left" corresponds to polygons above the diagonal
                plottedPolygonPositions = diagonalNegative.plottedPolygonPositions;
                mirroredPolygonPositions = diagonalNegative.mirroredPolygonPositions;
            } else {
                // "Right" corresponds to polygons below the diagonal
                plottedPolygonPositions = diagonalNegative.mirroredPolygonPositions;
                mirroredPolygonPositions = diagonalNegative.plottedPolygonPositions;
            }
            break;

        default:
            break;
    }
    
    console.log("plottedPolygonPositions", plottedPolygonPositions);
    console.log("mirroredPolygonPositions", mirroredPolygonPositions);
}

]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>checkAnswer</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
function checkAnswer() {
    console.log("plottedCellSelected:", plottedCellSelected);
    console.log("mirroredCellSelected:", mirroredCellSelected);
    console.log("plottedPolygonSelected:", plottedPolygonSelected);
    console.log("mirroredPolygonSelected:", mirroredPolygonSelected);

    let isValid = true;

    switch (selectedLine) {
        case "Vertical":
            // Vertical Split Verification
            for (let i = 0; i < plottedCellPositions.length; i++) {
                const row = Math.floor(i / 3); // 0 to 5
                const col = i % 3; // 0 to 2

                const mirroredCol = 2 - col; // 2,1,0
                const mirroredIndex = row * 3 + mirroredCol; // 0 to 17

                if (mirroredCellSelected[mirroredIndex] !== plottedCellSelected[i]) {
                    console.log(`Mismatch at row ${row}, col ${col}: Plotted=${plottedCellSelected[i]}, Mirrored=${mirroredCellSelected[mirroredIndex]}`);
                    isValid = false;
                    break;
                }
            }
            break;

        case "Horizontal":
            // Horizontal Split Verification
            for (let i = 0; i < plottedCellPositions.length; i++) {
                const row = Math.floor(i / 6); // 0 to 2
                const col = i % 6; // 0 to 5

                const mirroredRow = 2 - row; // 2,1,0
                const mirroredIndex = mirroredRow * 6 + col; // 0 to 17

                if (mirroredCellSelected[mirroredIndex] !== plottedCellSelected[i]) {
                    console.log(`Mismatch at row ${row}, col ${col}: Plotted=${plottedCellSelected[i]}, Mirrored=${mirroredCellSelected[mirroredIndex]}`);
                    isValid = false;
                    break;
                }
            }
            break;

        case "Diagonal (+)":
        case "Diagonal (-)":
            // Diagonal Split Verification
            for (let i = 0; i < plottedPolygonPositions.length; i++) {
                const plottedSelected = plottedPolygonSelected[i];
                const plottedPolygon = plottedPolygonPositions[i];
                let mirroredPolygon;

                // Apply reflection based on diagonal type
                if (selectedLine === "Diagonal (+)") {
                    // Reflection over y = x: swap x and y
                    mirroredPolygon = plottedPolygon.map(point => [point[1], point[0]]);
                } else if (selectedLine === "Diagonal (-)") {
                    // Reflection over y = -x + 6: (x, y) to (6 - y, 6 - x)
                    mirroredPolygon = plottedPolygon.map(point => [6 - point[1], 6 - point[0]]);
                }

                // Find the mirrored polygon in mirroredPolygonPositions
                const mirroredIndex = findMirroredPolygonIndex(mirroredPolygon);

                if (mirroredIndex === -1) {
                    console.log(`No mirrored polygon found for plotted polygon index ${i}`);
                    isValid = false;
                    break;
                }

                const mirroredSelected = mirroredPolygonSelected[mirroredIndex];

                if (mirroredSelected !== plottedSelected) {
                    console.log(`Mismatch at polygon index ${i}: Plotted=${plottedSelected}, Mirrored=${mirroredSelected}`);
                    isValid = false;
                    break;
                }
            }
            break;

        default:
            console.log("Invalid line selection.");
            isValid = false;
            break;
    }

    if (!pastAttemptsByScenario[pastAttemptsByScenario.length-1].completed) {
        pastAttemptsByScenario[pastAttemptsByScenario.length-1].attemptsTakenToComplete += 1;
    }

    // Record this attempt's grid snapshot
    var snapshot = getGridLinesSnapshot();
    if (snapshot) {
        var scenario = pastAttemptsByScenario[pastAttemptsByScenario.length-1];
        if (!scenario.attempts) scenario.attempts = [];
        scenario.attempts.push({
            attemptNumber: scenario.attempts.length + 1,
            originalLines: snapshot.originalLines,
            studentLines: snapshot.studentLines,
            isCorrect: isValid
        });
    }

    if (isValid) {
        if (!pastAttemptsByScenario[pastAttemptsByScenario.length-1].completed) {
            pastAttemptsByScenario[pastAttemptsByScenario.length-1].completed = true;
            pastAttemptsByScenario[pastAttemptsByScenario.length-1].timeTakenToComplete = `${Math.floor((new Date() - scenarioStartTime) / 1000 / 60)}m ${Math.floor((new Date() - scenarioStartTime) / 1000 % 60)}s`;
            score += 1;
        }
       // alert("Success! The mirrored points match the original plotted points.");
        _tools.showOkDialog("Success! The mirrored points match the original plotted points.");
    } else {
      //  alert("Try again! The mirrored points do not match.");
       _tools.showOkDialog("Try again! The mirrored points do not match.");
    }
    
    let completedCount = pastAttemptsByScenario.filter(item => item.completed).length;
    let totalCount = pastAttemptsByScenario.length;
    
    let feedback = `Configurations completed: ${completedCount}/${totalCount}<br><br>`;
    
    pastAttemptsByScenario.forEach((item, index) => {
        feedback += `<strong>${item.configuration} (Configuration ${index + 1})</strong><br>` +
                    `Number of tries to complete: ${item.attemptsTakenToComplete}<br>` +
                    `Time taken to Complete: ${item.timeTakenToComplete}<br><br>`;
        
        if (item.attempts && item.attempts.length > 0) {
            // Show original grid once (from first attempt)
            const firstAttempt = item.attempts[0];
            feedback += `<strong>Grid data (1=selected,0=unselected)</strong><br>`;
            feedback += `Original grid (1=selected,0=unselected):<br>`;
            firstAttempt.originalLines.forEach(line => {
                feedback += `${line}<br>`;
            });
            feedback += `<br>`;
            
            // Then show only student grids for each attempt, with tick/cross status
            item.attempts.forEach((attempt, aIndex) => {
                const statusIcon = attempt.isCorrect
                  ? `<span style="color:green">&#10004;</span>`
                  : `<span style="color:red">&#10008;</span>`;
                feedback += `<em>Attempt ${aIndex + 1} ${statusIcon}</em><br>`;
                feedback += `Student grid (1=selected,0=unselected):<br>`;
                attempt.studentLines.forEach(line => {
                    feedback += `${line}<br>`;
                });
                feedback += `<br>`;
            });
        } else {
            // Fallback: show current grid once if we have no per-attempt history
            const single = generateGridFeedback();
            if (single) {
                feedback += `<strong>Grid data (1=selected,0=unselected)</strong><br>`;
                feedback += single + "<br>";
            }
        }
    });
    
    // Recompute score from all scenarios so it always matches completed configurations
    let computedScore = pastAttemptsByScenario.filter(item => item.completed).length;
    score = computedScore;
    
    console.log(feedback)
    console.log(score)

    // Persist to localStorage so SLS iframe and any new-tab instances stay in sync
    writePayload(score, feedback);
    // Also push immediately from this window (iframe or new tab)
    pushToXAPI(score, feedback);
    
    initMirroredPoints();

    return isValid;
}

/**
 * Finds the index of the mirrored polygon in mirroredPolygonPositions that matches the given polygon.
 *
 * @param {Array} targetPolygon - The polygon to match [ [x1, y1], [x2, y2], ... ].
 * @returns {number} - The index of the matching mirrored polygon, or -1 if not found.
 */
function findMirroredPolygonIndex(targetPolygon) {
    for (let j = 0; j < mirroredPolygonPositions.length; j++) {
        const currentMirroredPolygon = mirroredPolygonPositions[j];

        if (arePolygonsEqual(targetPolygon, currentMirroredPolygon)) {
            return j;
        }
    }
    return -1;
}

/**
 * Compares two polygons for equality, regardless of the order of their points.
 *
 * @param {Array} poly1 - First polygon's points [ [x1, y1], [x2, y2], ... ].
 * @param {Array} poly2 - Second polygon's points [ [x1, y1], [x2, y2], ... ].
 * @returns {boolean} - True if polygons are equal, else false.
 */
function arePolygonsEqual(poly1, poly2) {
    if (poly1.length !== poly2.length) return false;

    // Create copies to avoid mutating original polygons
    const sortedPoly1 = [...poly1].map(point => point.join(",")).sort();
    const sortedPoly2 = [...poly2].map(point => point.join(",")).sort();

    for (let i = 0; i < sortedPoly1.length; i++) {
        if (sortedPoly1[i] !== sortedPoly2[i]) return false;
    }

    return true;
}
]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>getPlottedCellPositions</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
function getPlottedCellPositions(xMin = 0, xMax = 3, yMin = 0, yMax = 6, cellSize = 1) {
    const positions = [];

    // Determine the direction of iteration for X and Y axes
    const xStep = xMax >= xMin ? 1 : -1;
    const yStep = yMax >= yMin ? 1 : -1;

    // Calculate the number of cells along each axis
    const xCount = Math.abs(xMax - xMin);
    const yCount = Math.abs(yMax - yMin);

    for (let y = 0; y < yCount; y++) {
        // Calculate the current Y position based on direction
        const currentY = yMin + y * yStep;
        const centerY = currentY + (yStep === 1 ? cellSize / 2 : -cellSize / 2);

        for (let x = 0; x < xCount; x++) {
            // Calculate the current X position based on direction
            const currentX = xMin + x * xStep;
            const centerX = currentX + (xStep === 1 ? cellSize / 2 : -cellSize / 2);

            positions.push([centerX, centerY]);
        }
    }

    console.log("positions", positions);

    return positions;
}
]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>getGridWithDiagonalSplits</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
function getGridWithDiagonalSplits(
  xMin = 0,
  xMax = 6,
  yMin = 0,
  yMax = 6,
  cellSize = 1,
  diagonalType = "negative"
) {
  const plottedPolygonPositions = [];
  const mirroredPolygonPositions = [];

  // Define the diagonal equation based on type
  let diagonal;
  if (diagonalType === "positive") {
    // Diagonal (+): y = x
    diagonal = (x) => x;
  } else if (diagonalType === "negative") {
    // Diagonal (-): y = -x + 6
    diagonal = (x) => -x + 6;
  } else {
    throw new Error("Invalid diagonal type. Use 'positive' or 'negative'.");
  }

  for (let y = yMin; y < yMax; y += cellSize) {
    for (let x = xMin; x < xMax; x += cellSize) {
      // Define cell corners
      const topLeft = [x, y + cellSize];
      const topRight = [x + cellSize, y + cellSize];
      const bottomLeft = [x, y];
      const bottomRight = [x + cellSize, y];

      // Determine the cell's position relative to the diagonal
      const above = (pt) => pt[1] > diagonal(pt[0]);
      const below = (pt) => pt[1] < diagonal(pt[0]);

      const corners = [topLeft, topRight, bottomRight, bottomLeft];
      const cornerStatuses = corners.map((pt) => {
        // If a corner is essentially on the line, label it 'on'
        if (Math.abs(pt[1] - diagonal(pt[0])) < 1e-6) return "on";
        return above(pt) ? "above" : "below";
      });

      const allAbove = cornerStatuses.every((s) => s === "above" || s === "on");
      const allBelow = cornerStatuses.every((s) => s === "below" || s === "on");

      if (allAbove) {
        // Entire cell is above the diagonal
        // **Use 4 corners total — do NOT repeat the first corner.**
        plottedPolygonPositions.push([
          topLeft,
          topRight,
          bottomRight,
          bottomLeft
        ]);
      } else if (allBelow) {
        // Entire cell is below the diagonal
        mirroredPolygonPositions.push([
          topLeft,
          topRight,
          bottomRight,
          bottomLeft
        ]);
      } else {
        // Cell is intersected by the diagonal; split into two polygons
        if (diagonalType === "negative") {
          // Diagonal (-): top-left to bottom-right
          // Upper triangle (above)
          plottedPolygonPositions.push([topLeft, topRight, bottomRight]);
          // Lower triangle (below)
          mirroredPolygonPositions.push([topLeft, bottomRight, bottomLeft]);
        } else if (diagonalType === "positive") {
          // Diagonal (+): bottom-left to top-right
          // Upper triangle (above)
          plottedPolygonPositions.push([bottomLeft, topLeft, topRight]);
          // Lower triangle (below)
          mirroredPolygonPositions.push([bottomLeft, topRight, bottomRight]);
        }
      }
    }
  }

  return {
    plottedPolygonPositions,
    mirroredPolygonPositions
  };
}

]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>generateGridFeedback</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
function getGridLinesSnapshot() {
    try {
        const gridSize = 6;
        const scenarioGrid = [];
        const mirrorGrid = [];

        for (let r = 0; r < gridSize; r++) {
            scenarioGrid[r] = Array(gridSize).fill(0);
            mirrorGrid[r] = Array(gridSize).fill(0);
        }

        function fillGridFromCells(positions, selected, grid) {
            if (!positions || !selected) return;
            for (let i = 0; i < positions.length; i++) {
                const pos = positions[i];
                if (!pos || pos.length < 2) continue;

                const x = pos[0];
                const y = pos[1];

                const col = Math.round(x - 0.5);
                const row = Math.round(y - 0.5);

                if (row >= 0 && row < gridSize && col >= 0 && col < gridSize) {
                    grid[row][col] = selected[i] ? 1 : 0;
                }
            }
        }

        function fillGridFromPolygons(polygons, selected, grid) {
            if (!polygons || !selected) return;
            for (let i = 0; i < polygons.length; i++) {
                const poly = polygons[i];
                if (!poly || poly.length === 0) continue;

                let sumX = 0;
                let sumY = 0;
                for (let j = 0; j < poly.length; j++) {
                    sumX += poly[j][0];
                    sumY += poly[j][1];
                }
                const cx = sumX / poly.length;
                const cy = sumY / poly.length;

                const col = Math.round(cx - 0.5);
                const row = Math.round(cy - 0.5);

                if (row >= 0 && row < gridSize && col >= 0 && col < gridSize) {
                    grid[row][col] = selected[i] ? 1 : 0;
                }
            }
        }

        // Prefer cell-based grids when available
        if (displayCells && plottedCellPositions && mirroredCellPositions) {
            console.log("getGridLinesSnapshot using cells", {
                plottedCellPositions,
                mirroredCellPositions,
                plottedCellSelected,
                mirroredCellSelected
            });
            fillGridFromCells(plottedCellPositions, plottedCellSelected, scenarioGrid);
            fillGridFromCells(mirroredCellPositions, mirroredCellSelected, mirrorGrid);
        }
        // Fallback to polygon-based grids (diagonal cases)
        else if (displayPolygons && plottedPolygonPositions && mirroredPolygonPositions) {
            console.log("getGridLinesSnapshot using polygons", {
                plottedPolygonPositions,
                mirroredPolygonPositions,
                plottedPolygonSelected,
                mirroredPolygonSelected
            });
            fillGridFromPolygons(plottedPolygonPositions, plottedPolygonSelected, scenarioGrid);
            fillGridFromPolygons(mirroredPolygonPositions, mirroredPolygonSelected, mirrorGrid);
        } else {
            console.log("getGridLinesSnapshot: no suitable data, returning null");
            return null;
        }

        const originalLines = [];
        const studentLines = [];

        for (let row = gridSize - 1; row >= 0; row--) {
            originalLines.push(scenarioGrid[row].join(","));
            studentLines.push(mirrorGrid[row].join(","));
        }

        return { originalLines, studentLines };
    } catch (e) {
        console.error("Error computing grid snapshot", e);
        return null;
    }
}

function generateGridFeedback() {
    const snapshot = getGridLinesSnapshot();
    if (!snapshot) return "";

    const originalLines = snapshot.originalLines;
    const studentLines = snapshot.studentLines;

    let text = "";
    text += "Original grid (1=selected,0=unselected):<br>";
    for (let i = 0; i < originalLines.length; i++) {
        text += originalLines[i] + "<br>";
    }
    text += "Student grid (1=selected,0=unselected):<br>";
    for (let i = 0; i < studentLines.length; i++) {
        text += studentLines[i] + "<br>";
    }

    return text;
}
]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>onPlottedElementClick</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
function onPlottedElementClick(gridIndex) {
    if (plottedCellColor[gridIndex] === "White") {
        plottedCellSelected[gridIndex] = true;
        plottedCellColor[gridIndex] = plottedColor;
    } else {
        plottedCellSelected[gridIndex] = false;
        plottedCellColor[gridIndex] = "White";
    }
}
]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>onPlottedPolygonClick</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
function onPlottedPolygonClick(gridIndex) {
    if (plottedPolygonColor[gridIndex] === "White") {
        plottedPolygonSelected[gridIndex] = true;
        plottedPolygonColor[gridIndex] = plottedColor;
    } else {
        plottedPolygonSelected[gridIndex] = false;
        plottedPolygonColor[gridIndex] = "White";
    }
}
]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>onMirroredElementClick</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
function onMirroredElementClick(gridIndex) {
    if (mirroredCellColor[gridIndex] === "White") {
        mirroredCellSelected[gridIndex] = true;
        mirroredCellColor[gridIndex] = mirroredColor;
    } else {
        mirroredCellSelected[gridIndex] = false;
        mirroredCellColor[gridIndex] = "White";
    }
}
]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>onMirroredPolygonClick</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
function onMirroredPolygonClick(gridIndex) {
    if (mirroredPolygonColor[gridIndex] === "White") {
        mirroredPolygonSelected[gridIndex] = true;
        mirroredPolygonColor[gridIndex] = mirroredColor;
    } else {
        mirroredPolygonSelected[gridIndex] = false;
        mirroredPolygonColor[gridIndex] = "White";
    }
}
]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>initMirroredPoints</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
// initMirroredPoints

function initMirroredPoints() {
    
    console.log(pastAttemptsByScenario);
    
    if (!scenarioStartTime) {
        scenarioStartTime= new Date();
        pastAttemptsByScenario.push({
            configuration: `${selectedLine} (${selectedSide})`,
            attemptsTakenToComplete: 0,
            completed: false,
            timeTakenToComplete: "Did not complete",
            attempts: []
        })
        
        start30SecondCountdown("clearScenarioBtn");
    }
    
    plottedCellColor = plottedCellColor.map(color => 
      color === plottedColor ? plottedColorDisabled : color
    );
    
    plottedPolygonColor = plottedPolygonColor.map(color => 
      color === plottedColor ? plottedColorDisabled : color
    );

    mirroredCellColor = Array(18).fill("White");
    
    mirroredPolygonColor = Array(21).fill("White");
    
    mirroredCellSelected = Array(18).fill(false);
    
    mirroredPolygonSelected = Array(21).fill(false);
    
    start = true;
    
}
]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>xApi</Name>
<Active>false</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
// Using a namespace to prevent global variable clashes
const XAPIUtils = {
  parameters: null, // Parameters store
  getParameters: function () {
    if (!this.parameters) { // Ensure fetch once
      var urlParams = new URLSearchParams(window.location.search);
      var endpoint = urlParams.get('endpoint');
      var auth = urlParams.get('auth');
      var agent = JSON.parse(urlParams.get('agent'));
      var stateId = urlParams.get('stateId');
      var activityId = urlParams.get('activityId');

      // document.querySelector("#cookieId").innerText = "Cookie: " + auth;
      // document.querySelector("#questionId").innerText = "Question ID: " + activityId;
      // document.querySelector("#userId").innerText = "User ID: " + stateId;

      ADL.XAPIWrapper.changeConfig({
        "endpoint": endpoint + "/",
        "auth": `Basic ${auth}`
      });
      this.parameters = {
        agent,
        stateId,
        activityId
      };
    }

    return this.parameters;
  }
};

// Immediately invoke getParameters on page load
document.addEventListener("DOMContentLoaded", function () {
  XAPIUtils.getParameters(); // Fetch parameters once on load
});

function storeState(stateValue) { // Removed async
  try {
    const parameters = XAPIUtils.getParameters(); // Retrieve parameters from store
    const activityId = parameters.activityId;
    const stateId = parameters.stateId;
    const agent = parameters.agent;
    const registration = null;

    ADL.XAPIWrapper.sendState(activityId, agent, stateId, registration, stateValue);
    console.log("Submitted: " + JSON.stringify(stateValue, null, 2));
  } catch (err) {
    console.error("An error has occurred!", err);
  }
}

function getState() {
  try {
    const parameters = XAPIUtils.getParameters(); // Retrieve parameters from store
    const activityId = parameters.activityId;
    const stateId = parameters.stateId;
    const agent = parameters.agent;

    const result = ADL.XAPIWrapper.getState(activityId, agent, stateId);
    document.querySelector("#getState").innerText = "First Load State: " + JSON.stringify(result, null, 2);
    return result;
  } catch (err) {
    console.error("An error has occurred!", err);
    document.querySelector("#getState").innerText = "Error has occurred: " + err;
  }
}

// ===== SLS bridge: keep score/feedback in sync across iframe and new-tab =====

// Use activityId from xAPI as the scope key so all windows for this activity share data
const APP_SCOPE = (function () {
  try {
    const p = XAPIUtils.getParameters();
    return String(p.activityId || window.location.href);
  } catch (e) {
    return String(window.location.href);
  }
})();

// Single payload slot for this activity
const PAYLOAD_KEY = `sls_unlike_payload::${APP_SCOPE}::v1`;

// Store latest score + feedback in localStorage
function writePayload(score, feedback) {
  try {
    const obj = { score, feedback, t: Date.now() };
    localStorage.setItem(PAYLOAD_KEY, JSON.stringify(obj));
  } catch (e) {
    // ignore storage errors
  }
}

function readPayload() {
  try {
    const raw = localStorage.getItem(PAYLOAD_KEY);
    return raw ? JSON.parse(raw) : null;
  } catch (e) {
    return null;
  }
}

// Wrapper around storeState so we have a single integration point to xAPI
function pushToXAPI(score, feedback) {
  try {
    storeState({ score, feedback });
  } catch (e) {
    // ignore xAPI errors here; SLS will still keep running
  }
}

// When this page (iframe or new tab) is active, ensure latest payload is pushed to xAPI
function syncFromLocalToXAPI() {
  const payload = readPayload();
  if (!payload) return;
  pushToXAPI(payload.score, payload.feedback);
}

window.addEventListener("load", syncFromLocalToXAPI);
window.addEventListener("visibilitychange", () => {
  if (!document.hidden) syncFromLocalToXAPI();
});
window.addEventListener("focus", syncFromLocalToXAPI);
window.addEventListener("storage", (e) => {
  if (e.key === PAYLOAD_KEY && e.newValue) {
    syncFromLocalToXAPI();
  }
});

]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>xAPINewTab</Name>
<Active>false</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
// Using a namespace to prevent global variable clashes
const XAPIUtils = {
  parameters: null,
  configured: false,

  // Storage key for persisting xAPI config across tabs
  CONFIG_KEY: 'sls_xapi_config::v1',

  // Save config to localStorage so new tabs can access it
  _persistConfig: function(config) {
    try {
      localStorage.setItem(this.CONFIG_KEY, JSON.stringify(config));
    } catch (e) {
      console.warn("Could not persist xAPI config", e);
    }
  },

  // Retrieve config from localStorage
  _loadPersistedConfig: function() {
    try {
      const raw = localStorage.getItem(this.CONFIG_KEY);
      return raw ? JSON.parse(raw) : null;
    } catch (e) {
      return null;
    }
  },

  getParameters: function() {
    if (this.parameters) {
      return this.parameters;
    }

    const urlParams = new URLSearchParams(window.location.search);
    let endpoint = urlParams.get('endpoint');
    let auth = urlParams.get('auth');
    let agent = urlParams.get('agent');
    let stateId = urlParams.get('stateId');
    let activityId = urlParams.get('activityId');

    // If URL params exist (iframe context), use them and persist
    if (endpoint && auth && agent && stateId && activityId) {
      try {
        agent = JSON.parse(agent);
      } catch (e) {
        console.error("Failed to parse agent JSON", e);
      }

      const config = { endpoint, auth, agent, stateId, activityId };
      this._persistConfig(config);
      this._applyConfig(config);
      return this.parameters;
    }

    // Fallback: try to load from localStorage (new tab context)
    const persisted = this._loadPersistedConfig();
    if (persisted) {
      console.log("Loaded xAPI config from localStorage (new tab mode)");
      this._applyConfig(persisted);
      return this.parameters;
    }

    console.warn("No xAPI parameters available - neither in URL nor localStorage");
    return null;
  },

  _applyConfig: function(config) {
    if (this.configured) return;

    try {
      ADL.XAPIWrapper.changeConfig({
        "endpoint": config.endpoint + (config.endpoint.endsWith('/') ? '' : '/'),
        "auth": `Basic ${config.auth}`
      });
      this.configured = true;
    } catch (e) {
      console.error("Failed to configure ADL.XAPIWrapper", e);
    }

    this.parameters = {
      agent: config.agent,
      stateId: config.stateId,
      activityId: config.activityId
    };
  },

  // Check if we have valid config
  isConfigured: function() {
    return this.configured && this.parameters !== null;
  }
};

// Initialize on page load
document.addEventListener("DOMContentLoaded", function() {
  XAPIUtils.getParameters();
});

function storeState(stateValue) {
  try {
    const parameters = XAPIUtils.getParameters();
    if (!parameters) {
      console.warn("Cannot store state - xAPI not configured");
      return false;
    }

    const { activityId, stateId, agent } = parameters;
    ADL.XAPIWrapper.sendState(activityId, agent, stateId, null, stateValue);
    console.log("Submitted:", JSON.stringify(stateValue, null, 2));
    return true;
  } catch (err) {
    console.error("An error has occurred!", err);
    return false;
  }
}

function getState() {
  try {
    const parameters = XAPIUtils.getParameters();
    if (!parameters) {
      console.warn("Cannot get state - xAPI not configured");
      return null;
    }

    const { activityId, stateId, agent } = parameters;
    const result = ADL.XAPIWrapper.getState(activityId, agent, stateId);

    const el = document.querySelector("#getState");
    if (el) {
      el.innerText = "First Load State: " + JSON.stringify(result, null, 2);
    }
    return result;
  } catch (err) {
    console.error("An error has occurred!", err);
    const el = document.querySelector("#getState");
    if (el) {
      el.innerText = "Error has occurred: " + err;
    }
    return null;
  }
}

// ===== SLS bridge: keep score/feedback in sync across iframe and new-tab =====
const APP_SCOPE = (function() {
  try {
    const p = XAPIUtils.getParameters();
    return p ? String(p.activityId) : String(window.location.pathname);
  } catch (e) {
    return String(window.location.pathname);
  }
})();

const PAYLOAD_KEY = `sls_unlike_payload::${APP_SCOPE}::v1`;

function writePayload(score, feedback) {
  try {
    const obj = { score, feedback, t: Date.now() };
    localStorage.setItem(PAYLOAD_KEY, JSON.stringify(obj));
  } catch (e) {
    // ignore storage errors
  }
}

function readPayload() {
  try {
    const raw = localStorage.getItem(PAYLOAD_KEY);
    return raw ? JSON.parse(raw) : null;
  } catch (e) {
    return null;
  }
}

function pushToXAPI(score, feedback) {
  if (!XAPIUtils.isConfigured()) {
    console.warn("xAPI not configured, skipping push");
    return false;
  }
  try {
    return storeState({ score, feedback });
  } catch (e) {
    console.error("pushToXAPI error:", e);
    return false;
  }
}

// Combined function: write to localStorage AND push to xAPI
function saveScore(score, feedback) {
  writePayload(score, feedback);
  pushToXAPI(score, feedback);
}

function syncFromLocalToXAPI() {
  const payload = readPayload();
  if (!payload) return;
  pushToXAPI(payload.score, payload.feedback);
}

window.addEventListener("load", syncFromLocalToXAPI);
window.addEventListener("visibilitychange", () => {
  if (!document.hidden) syncFromLocalToXAPI();
});
window.addEventListener("focus", syncFromLocalToXAPI);
window.addEventListener("storage", (e) => {
  if (e.key === PAYLOAD_KEY && e.newValue) {
    syncFromLocalToXAPI();
  }
});
]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>xAPInewTablookang</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
(function () {
  'use strict';

  // --- Stable Scope Identification ---
  // ALWAYS use the folder path as the unique ID for the activity scope.
  // This ensures that ALL tabs (SLS frame, tool's new tab, SLS native button)
  // use the exact same localStorage keys for config and sync data.
  var getStablePath = function () {
    try {
      var p = window.location.pathname;
      var folder = p.substring(0, p.lastIndexOf('/')) || p;
      return folder;
    } catch (e) {
      return 'default_scope';
    }
  };

  var APP_SCOPE = getStablePath();
  var CONFIG_KEY = 'xapi_config::' + APP_SCOPE;
  var PAYLOAD_KEY = 'sls_unlike_payload::' + APP_SCOPE + '::v1';

  var XAPIUtils = {
    parameters: null,

    getParameters: function () {
      if (this.parameters) return this.parameters;

      try {
        var urlParams = new URLSearchParams(window.location.search);
        var endpoint = urlParams.get('endpoint');
        var auth = urlParams.get('auth');
        var agentRaw = urlParams.get('agent');
        var stateId = urlParams.get('stateId');
        var activityId = urlParams.get('activityId');

        // Check if we have core parameters in URL
        if (endpoint && auth && endpoint !== 'null' && auth !== 'null') {
          var config = {
            endpoint: endpoint,
            auth: auth,
            agentRaw: agentRaw,
            stateId: stateId,
            activityId: activityId,
            t: Date.now()
          };
          // Persist to localStorage for SLS native "New Tab" support
          try {
            localStorage.setItem(CONFIG_KEY, JSON.stringify(config));
            console.log('[xAPI] Configuration persisted for scope:', APP_SCOPE);
          } catch (e) { }
        } else {
          // Fallback to localStorage if URL params are missing
          try {
            var cached = localStorage.getItem(CONFIG_KEY);
            if (cached) {
              var config = JSON.parse(cached);
              endpoint = endpoint || config.endpoint;
              auth = auth || config.auth;
              agentRaw = agentRaw || config.agentRaw;
              stateId = stateId || config.stateId;
              activityId = activityId || config.activityId;
              console.log('[xAPI] Configuration recovered for scope:', APP_SCOPE);
            }
          } catch (e) { }
        }

        // --- Resilience: Synthesize missing activityId or agent if absolutely necessary ---
        // If we still lack activityId but have endpoint, use the folder path as a fallback IRI
        if (!activityId && endpoint) {
          activityId = 'http://sls.native.fallback/' + APP_SCOPE.replace(/^\//, '');
          console.warn('[xAPI] Missing activityId. Using fallback:', activityId);
        }

        // Configure ADL.XAPIWrapper if we have valid endpoint and auth
        if (endpoint && auth && endpoint !== 'null' && auth !== 'null' && typeof window.ADL !== 'undefined' && window.ADL.XAPIWrapper) {
          var ep = endpoint;
          if (ep.charAt(ep.length - 1) !== '/') {
            ep += '/';
          }
          window.ADL.XAPIWrapper.changeConfig({
            endpoint: ep,
            auth: 'Basic ' + auth
          });
        }

        // Parse agent
        var agent = null;
        if (agentRaw && agentRaw !== 'null') {
          try {
            agent = JSON.parse(agentRaw);
          } catch (e) {
            console.warn('[xAPI] Invalid agent JSON:', e);
          }
        } else if (endpoint) {
          // Mock agent if missing but endpoint present (last resort for native new tab)
          agent = { "mbox": "mailto:student@sls.native.fallback", "name": "SLS Student (New Tab)" };
          console.warn('[xAPI] Missing agent. Using anonymous fallback.');
        }

        var params = { agent: agent, stateId: stateId || 'default_state', activityId: activityId, endpoint: endpoint, auth: auth };

        // Only cache if we actually found something useful
        if (endpoint && auth) {
          this.parameters = params;
        }
        return params;
      } catch (e) {
        console.warn('[xAPI] getParameters failed:', e);
        return null;
      }
    }
  };

  window.XAPIUtils = XAPIUtils;

  window.storeState = function (stateValue) {
    try {
      if (!window.ADL || !window.ADL.XAPIWrapper) {
        console.warn('[xAPI] ADL.XAPIWrapper not available');
        return;
      }
      var params = XAPIUtils.getParameters();

      // Detailed Guard Notification
      if (!params) {
        console.warn('[xAPI] storeState: No parameters available. Tracking locally only.');
        return;
      }

      var missing = [];
      if (!params.endpoint) missing.push('endpoint');
      if (!params.auth) missing.push('auth');
      if (!params.activityId) missing.push('activityId');
      if (!params.agent) missing.push('agent');

      if (missing.length > 0) {
        console.warn('[xAPI] storeState: Cannot send to SLS server. Missing: ' + missing.join(', '));
        return;
      }

      window.ADL.XAPIWrapper.sendState(params.activityId, params.agent, params.stateId, null, stateValue);
      console.log('[xAPI] Submitted to SLS:', stateValue);
    } catch (err) {
      console.error('[xAPI] storeState error (handled):', err);
    }
  };

  window.getState = function () {
    try {
      if (!window.ADL || !window.ADL.XAPIWrapper) return null;
      var params = XAPIUtils.getParameters();
      if (!params || !params.activityId || !params.agent || !params.endpoint) return null;

      var result = window.ADL.XAPIWrapper.getState(params.activityId, params.agent, params.stateId);
      console.log('[xAPI] Retrieved state:', result);
      return result;
    } catch (err) {
      console.error('[xAPI] getState error (handled):', err);
      return null;
    }
  };

  window.updateStore = function () {
    try {
      var sInput = document.getElementById("score-input");
      var fInput = document.getElementById("feedback-input");
      if (sInput || fInput) {
        window.storeState({
          score: sInput ? sInput.value : 0,
          feedback: fInput ? fInput.value : ""
        });
      }
    } catch (e) { }
  };

  // Initial parse on load
  XAPIUtils.getParameters();
  
  // ===== SLS bridge: keep score/feedback in sync across iframe and new-tab =====
  
  // Store latest score + feedback in localStorage
  window.writePayload = function(score, feedback) {
    try {
      var obj = { score: score, feedback: feedback, t: Date.now() };
      localStorage.setItem(PAYLOAD_KEY, JSON.stringify(obj));
    } catch (e) {
      // ignore storage errors
    }
  };
  
  window.readPayload = function() {
    try {
      var raw = localStorage.getItem(PAYLOAD_KEY);
      return raw ? JSON.parse(raw) : null;
    } catch (e) {
      return null;
    }
  };
  
  // Wrapper around storeState so we have a single integration point to xAPI
  window.pushToXAPI = function(score, feedback) {
    try {
      window.storeState({ score: score, feedback: feedback });
    } catch (e) {
      // ignore xAPI errors here; SLS will still keep running
    }
  };
  
  // When this page (iframe or new tab) is active, ensure latest payload is pushed to xAPI
  function syncFromLocalToXAPI() {
    var payload = window.readPayload();
    if (!payload) return;
    window.pushToXAPI(payload.score, payload.feedback);
  }
  
  window.addEventListener("load", syncFromLocalToXAPI);
  window.addEventListener("visibilitychange", function() {
    if (!document.hidden) syncFromLocalToXAPI();
  });
  window.addEventListener("focus", syncFromLocalToXAPI);
  window.addEventListener("storage", function(e) {
    if (e.key === PAYLOAD_KEY && e.newValue) {
      syncFromLocalToXAPI();
    }
  });
})();

]]></Code>
</Content>
</Osejs.Model.Library.Page>
<Osejs.Model.Library.Page>
<Type>CODE_EDITOR</Type>
<Name>timer</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<Comment><![CDATA[]]></Comment>
<Code><![CDATA[
let countdownTimeout = null;
let countdownInterval = null;

function start30SecondCountdown(className) {
  // e.g. className = "clearScenarioBtn"
  const displayEl = document.querySelector('.' + className);

  // Clear previous countdown
  if (countdownTimeout !== null) {
    clearTimeout(countdownTimeout);
    countdownTimeout = null;
  }
  if (countdownInterval !== null) {
    clearInterval(countdownInterval);
    countdownInterval = null;
  }

  let remaining = 30;

  function render() {
    if (!displayEl) return;
    displayEl.textContent = "Clear Scenario (" + remaining + "s)";
  }

  render(); // initial 30s

  countdownTimeout = setTimeout(function () {
    countdownTimeout = null;
    if (countdownInterval !== null) {
      clearInterval(countdownInterval);
      countdownInterval = null;
    }

    if (typeof reset === "function") {
      _reset();         
    }
  }, 30000);

  countdownInterval = setInterval(function () {
    remaining = Math.max(0, remaining - 1);
    render();
    if (remaining <= 0) {
      clearInterval(countdownInterval);
      countdownInterval = null;
    }
  }, 1000);
}
]]></Code>
</Content>
</Osejs.Model.Library.Page>
</Osejs.Model.Library>
<Osejs.Model.Elements>
</Osejs.Model.Elements>
</Osejs.Model>
<Osejs.HtmlView>
<Osejs.HtmlView.Page>
<Type>HTML_VIEW_EDITOR</Type>
<Name>HtmlView</Name>
<Active>true</Active>
<Internal>false</Internal>
<Content>
<SizeOption>0</SizeOption>
<X>0</X>
<Y>0</Y>
<Width>800</Width>
<Height>600</Height>
<KeepHidden>true</KeepHidden>
<RootProperties>
</RootProperties>
<Tree>
<HtmlView.Element>
<Expanded>true</Expanded>
<Type>Elements.Panel</Type>
<Name><![CDATA[wrapper]]></Name>
</HtmlView.Element>
<HtmlView.Element>
<Expanded>true</Expanded>
<Type>Elements.Panel</Type>
<Name><![CDATA[firstRow]]></Name>
<Parent><![CDATA[wrapper]]></Parent>
<Property name="CSS"><![CDATA["{ margin-bottom: 10px }"]]></Property>
</HtmlView.Element>
<HtmlView.Element>
<Type>Elements.Label</Type>
<Name><![CDATA[desccription]]></Name>
<Parent><![CDATA[firstRow]]></Parent>
<Property name="Text"><![CDATA["Click on the grids to create a figure.<br/>Press 'Start' to complete the symmetric figure"]]></Property>
</HtmlView.Element>
<HtmlView.Element>
<Expanded>true</Expanded>
<Type>Elements.Panel</Type>
<Name><![CDATA[secondRow]]></Name>
<Parent><![CDATA[wrapper]]></Parent>
<Property name="CSS"><![CDATA["{ margin-bottom: 25px }"]]></Property>
</HtmlView.Element>
<HtmlView.Element>
<Type>Elements.ComboBox</Type>
<Name><![CDATA[line]]></Name>
<Parent><![CDATA[secondRow]]></Parent>
<Property name="CSS"><![CDATA["{ margin-right: 10px }"]]></Property>
<Property name="OnChange"><![CDATA[var opts = _view.line.getProperty("SelectedOptions");  
selectedLine = (opts.length > 0)? opts[0]:"";
scenarioStartTime = "";
if (selectedLine === "Horizontal") {
sideOptions = ["Top", "Bottom"];
selectedSide = "Top";
} else {
sideOptions = ["Left", "Right"];
selectedSide = selectedSide === "Right" ? "Right" : "Left";
}
console.log(selectedLine, selectedSide);
initFunctions();]]></Property>
<Property name="Options"><![CDATA[["Vertical", "Horizontal", "Diagonal (+)", "Diagonal (-)"]]]></Property>
<Property name="Disabled"><![CDATA[start]]></Property>
<Property name="Tooltip"><![CDATA[["Vertical", "Horizontal", "Diagonal (+)", "Diagonal (-)"]]]></Property>
</HtmlView.Element>
<HtmlView.Element>
<Type>Elements.ComboBox</Type>
<Name><![CDATA[side]]></Name>
<Parent><![CDATA[secondRow]]></Parent>
<Property name="CSS"><![CDATA["{ margin-right: 10px }"]]></Property>
<Property name="Options"><![CDATA[sideOptions]]></Property>
<Property name="OnChange"><![CDATA[var opts = _view.side.getProperty("SelectedOptions");  
selectedSide = (opts.length > 0)? opts[0]:"";
scenarioStartTime = "";
console.log(selectedLine, selectedSide);
initFunctions();]]></Property>
<Property name="Disabled"><![CDATA[start]]></Property>
</HtmlView.Element>
<HtmlView.Element>
<Type>Elements.Button</Type>
<Name><![CDATA[start]]></Name>
<Parent><![CDATA[secondRow]]></Parent>
<Property name="Text"><![CDATA["Start"]]></Property>
<Property name="CSS"><![CDATA["{ margin-right: 10px }"]]></Property>
<Property name="OnPress"><![CDATA[initMirroredPoints();]]></Property>
<Property name="Disabled"><![CDATA[start]]></Property>
</HtmlView.Element>
<HtmlView.Element>
<Type>Elements.Button</Type>
<Name><![CDATA[clearScenario]]></Name>
<Parent><![CDATA[secondRow]]></Parent>
<Property name="Text"><![CDATA["Clear Scenario"]]></Property>
<Property name="OnPress"><![CDATA[_reset();]]></Property>
<Property name="CSS"><![CDATA["{ margin-right: 10px }"]]></Property>
<Property name="ClassName"><![CDATA["clearScenarioBtn"]]></Property>
</HtmlView.Element>
<HtmlView.Element>
<Type>Elements.Button</Type>
<Name><![CDATA[check]]></Name>
<Parent><![CDATA[secondRow]]></Parent>
<Property name="Text"><![CDATA["check"]]></Property>
<Property name="CSS"><![CDATA["{ margin-right: 10px }"]]></Property>
<Property name="OnPress"><![CDATA[checkAnswer();]]></Property>
<Property name="Disabled"><![CDATA[start ? false : true]]></Property>
</HtmlView.Element>
<HtmlView.Element>
<Type>Elements.Button</Type>
<Name><![CDATA[clearAnswer]]></Name>
<Parent><![CDATA[secondRow]]></Parent>
<Property name="Text"><![CDATA["Clear Answer"]]></Property>
<Property name="CSS"><![CDATA["{ margin-right: 10px }"]]></Property>
<Property name="OnPress"><![CDATA[
pastAttemptsByScenario[pastAttemptsByScenario.length-1].attemptsTakenToComplete += 1;

// Record this attempt's grid snapshot
var snapshot = getGridLinesSnapshot();
if (snapshot) {
    var scenario = pastAttemptsByScenario[pastAttemptsByScenario.length-1];
    if (!scenario.attempts) scenario.attempts = [];
    scenario.attempts.push({
        attemptNumber: scenario.attempts.length + 1,
        originalLines: snapshot.originalLines,
        studentLines: snapshot.studentLines,
        isCorrect: false
    });
}

let completedCount = pastAttemptsByScenario.filter(item => item.completed).length;
let totalCount = pastAttemptsByScenario.length;

let feedback = `Configurations completed: ${completedCount}/${totalCount}<br><br>`;

pastAttemptsByScenario.forEach((item, index) => {
    feedback += `<strong>${item.configuration} (Configuration ${index + 1})</strong><br>` +
                `Number of tries to complete: ${item.attemptsTakenToComplete}<br>` +
                `Time taken to Complete: ${item.timeTakenToComplete}<br><br>`;
    
    if (item.attempts && item.attempts.length > 0) {
        // Show original grid once (from first attempt)
        const firstAttempt = item.attempts[0];
        feedback += `<strong>Grid data (1=selected,0=unselected)</strong><br>`;
        feedback += `Original grid (1=selected,0=unselected):<br>`;
        firstAttempt.originalLines.forEach(line => {
            feedback += `${line}<br>`;
        });
        feedback += `<br>`;
        
        // Then show only student grids for each attempt, with tick/cross status
        item.attempts.forEach((attempt, aIndex) => {
            const statusIcon = attempt.isCorrect
              ? `<span style="color:green">&#10004;</span>`
              : `<span style="color:red">&#10008;</span>`;
            feedback += `<em>Attempt ${aIndex + 1} ${statusIcon}</em><br>`;
            feedback += `Student grid (1=selected,0=unselected):<br>`;
            attempt.studentLines.forEach(line => {
                feedback += `${line}<br>`;
            });
            feedback += `<br>`;
        });
    } else {
        // Fallback: show current grid once if we have no per-attempt history
        const single = generateGridFeedback();
        if (single) {
            feedback += `<strong>Grid data (1=selected,0=unselected)</strong><br>`;
            feedback += single + "<br>";
        }
    }
});

console.log(feedback)
console.log(score)

// Persist to localStorage so SLS iframe and any new-tab instances stay in sync
writePayload(score, feedback);
// Also push immediately from this window (iframe or new tab)
pushToXAPI(score, feedback);

initMirroredPoints();
]]></Property>
<Property name="Disabled"><![CDATA[start ? false : true]]></Property>
</HtmlView.Element>
<HtmlView.Element>
<Expanded>true</Expanded>
<Type>Elements.PlottingPanel</Type>
<Name><![CDATA[plottingPanel]]></Name>
<Parent><![CDATA[wrapper]]></Parent>
<Property name="MinimumX"><![CDATA[0]]></Property>
<Property name="MaximumX"><![CDATA[graphX]]></Property>
<Property name="MinimumY"><![CDATA[0]]></Property>
<Property name="MaximumY"><![CDATA[graphY]]></Property>
<Property name="Enabled"><![CDATA[true]]></Property>
<Property name="AutoScaleX"><![CDATA[false]]></Property>
<Property name="Height"><![CDATA["70vh"]]></Property>
<Property name="Width"><![CDATA["70vh"]]></Property>
<Property name="AutoScaleY"><![CDATA[false]]></Property>
<Property name="XFixedTick"><![CDATA[0]]></Property>
<Property name="YFixedTick"><![CDATA[0]]></Property>
<Property name="XTickStep"><![CDATA[1]]></Property>
<Property name="YTickStep"><![CDATA[1]]></Property>
<Property name="XAutoTicks"><![CDATA[false]]></Property>
<Property name="YAutoTicks"><![CDATA[false]]></Property>
<Property name="Gutters"><![CDATA[[0,0,0,0]]]></Property>
</HtmlView.Element>
<HtmlView.Element>
<Type>Elements.ShapeSet2D</Type>
<Name><![CDATA[plottedCells]]></Name>
<Parent><![CDATA[plottingPanel]]></Parent>
<Property name="LineColor"><![CDATA["Blue"]]></Property>
<Property name="ShapeType"><![CDATA["RECTANGLE"]]></Property>
<Property name="LineWidth"><![CDATA[!start ? 1.0 : 0.5]]></Property>
<Property name="FillColor"><![CDATA[plottedCellColor]]></Property>
<Property name="Position"><![CDATA[plottedCellPositions]]></Property>
<Property name="SizeX"><![CDATA[1]]></Property>
<Property name="SizeY"><![CDATA[1]]></Property>
<Property name="Visibility"><![CDATA[displayCells]]></Property>
<Property name="ElementInteracted"><![CDATA[plottedElementInteracted]]></Property>
<Property name="EnabledPosition"><![CDATA[!start ? "ENABLED_NO_MOVE" : "ENABLED_NONE"]]></Property>
<Property name="OnPress"><![CDATA[plottedElementInteracted=plottedElementInteracted
if (plottedElementInteracted==plottedElementInteracted){
    console.log("plottedElementInteracted", plottedElementInteracted);
    onPlottedElementClick(plottedElementInteracted);
}]]></Property>
<Property name="DrawFill"><![CDATA[true]]></Property>
<Property name="Sensitivity"><![CDATA[0]]></Property>
</HtmlView.Element>
<HtmlView.Element>
<Type>Elements.ShapeSet2D</Type>
<Name><![CDATA[mirroredCells]]></Name>
<Parent><![CDATA[plottingPanel]]></Parent>
<Property name="Position"><![CDATA[mirroredCellPositions]]></Property>
<Property name="SizeX"><![CDATA[1]]></Property>
<Property name="SizeY"><![CDATA[1]]></Property>
<Property name="ShapeType"><![CDATA["RECTANGLE"]]></Property>
<Property name="LineColor"><![CDATA["Red"]]></Property>
<Property name="LineWidth"><![CDATA[start ? 1.0 : 0.5]]></Property>
<Property name="FillColor"><![CDATA[mirroredCellColor]]></Property>
<Property name="Visibility"><![CDATA[displayCells]]></Property>
<Property name="Sensitivity"><![CDATA[0]]></Property>
<Property name="DrawFill"><![CDATA[true]]></Property>
<Property name="ElementInteracted"><![CDATA[mirroredElementInteracted]]></Property>
<Property name="EnabledPosition"><![CDATA[start ? "ENABLED_NO_MOVE" : "ENABLED_NONE"]]></Property>
<Property name="OnPress"><![CDATA[mirroredElementInteracted=mirroredElementInteracted
if (mirroredElementInteracted==mirroredElementInteracted){
    console.log("mirroredElementInteracted", mirroredElementInteracted);
    onMirroredElementClick(mirroredElementInteracted);
}]]></Property>
</HtmlView.Element>
<HtmlView.Element>
<Type>Elements.PolygonSet2D</Type>
<Name><![CDATA[plottedPolygons]]></Name>
<Parent><![CDATA[plottingPanel]]></Parent>
<Property name="LineColor"><![CDATA["Blue"]]></Property>
<Property name="LineWidth"><![CDATA[!start ? 1.0 : 0.5]]></Property>
<Property name="FillColor"><![CDATA[plottedPolygonColor]]></Property>
<Property name="Points"><![CDATA[plottedPolygonPositions]]></Property>
<Property name="Visibility"><![CDATA[displayPolygons]]></Property>
<Property name="DrawFill"><![CDATA[true]]></Property>
<Property name="ElementInteracted"><![CDATA[plottedPolygonInteracted]]></Property>
<Property name="EnabledPosition"><![CDATA[!start ? "ENABLED_NO_MOVE" : "ENABLED_NONE"]]></Property>
<Property name="Sensitivity"><![CDATA[0]]></Property>
<Property name="OnPress"><![CDATA[plottedPolygonInteracted=plottedPolygonInteracted
if (plottedPolygonInteracted==plottedPolygonInteracted){
    console.log("plottedPolygonInteracted", plottedPolygonInteracted);
    onPlottedPolygonClick(plottedPolygonInteracted);
}]]></Property>
</HtmlView.Element>
<HtmlView.Element>
<Type>Elements.PolygonSet2D</Type>
<Name><![CDATA[mirroredPolygons]]></Name>
<Parent><![CDATA[plottingPanel]]></Parent>
<Property name="Points"><![CDATA[mirroredPolygonPositions]]></Property>
<Property name="LineColor"><![CDATA["Red"]]></Property>
<Property name="LineWidth"><![CDATA[start ? 1.0 : 0.5]]></Property>
<Property name="FillColor"><![CDATA[mirroredPolygonColor]]></Property>
<Property name="Visibility"><![CDATA[displayPolygons]]></Property>
<Property name="DrawFill"><![CDATA[true]]></Property>
<Property name="ElementInteracted"><![CDATA[mirroredPolygonInteracted]]></Property>
<Property name="EnabledPosition"><![CDATA[start ? "ENABLED_NO_MOVE" : "ENABLED_NONE"]]></Property>
<Property name="OnPress"><![CDATA[mirroredPolygonInteracted=mirroredPolygonInteracted
if (mirroredPolygonInteracted==mirroredPolygonInteracted){
    console.log("mirroredPolygonInteracted", mirroredPolygonInteracted);
    onMirroredPolygonClick(mirroredPolygonInteracted);
}]]></Property>
<Property name="Sensitivity"><![CDATA[0]]></Property>
</HtmlView.Element>
<HtmlView.Element>
<Type>Elements.Segment2D</Type>
<Name><![CDATA[separationLine]]></Name>
<Parent><![CDATA[plottingPanel]]></Parent>
<Property name="SizeX"><![CDATA[lineX2]]></Property>
<Property name="SizeY"><![CDATA[lineY2]]></Property>
<Property name="X"><![CDATA[lineX1]]></Property>
<Property name="Y"><![CDATA[lineY1]]></Property>
<Property name="LineColor"><![CDATA["Magenta"]]></Property>
<Property name="LineWidth"><![CDATA[1]]></Property>
</HtmlView.Element>
</Tree>
</Content>
</Osejs.HtmlView.Page>
</Osejs.HtmlView>
</Osejs>