@doenet/doenetml 0.6.0-alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +3 -0
- package/LICENSE +661 -0
- package/README.md +146 -0
- package/cypress/e2e/ActivityViewer/activityVariants.cy.js +1770 -0
- package/cypress/e2e/ActivityViewer/compiledActivity.cy.js +83 -0
- package/cypress/e2e/ActivityViewer/relationshipsAmongPages.cy.js +697 -0
- package/cypress/e2e/answerValidation/errorinnumbers.cy.js +2125 -0
- package/cypress/e2e/answerValidation/factoring.cy.js +1945 -0
- package/cypress/e2e/answerValidation/factoringOldAlgorithm.cy.js +892 -0
- package/cypress/e2e/answerValidation/functionanswers.cy.js +314 -0
- package/cypress/e2e/answerValidation/matchingpatterns.cy.js +287 -0
- package/cypress/e2e/answerValidation/matchpartial.cy.js +6711 -0
- package/cypress/e2e/answerValidation/pointlocation.cy.js +3989 -0
- package/cypress/e2e/answerValidation/symbolicequality.cy.js +1893 -0
- package/cypress/e2e/answerValidation/videoProgress.cy.js +210 -0
- package/cypress/e2e/assignNames/basiccopy.cy.js +2376 -0
- package/cypress/e2e/assignNames/collections.cy.js +9247 -0
- package/cypress/e2e/assignNames/selects.cy.js +105 -0
- package/cypress/e2e/assignNames/sequences.cy.js +1964 -0
- package/cypress/e2e/baseComponent/basecomponentproperties.cy.js +999 -0
- package/cypress/e2e/baseComponent/doenetMLtext.cy.js +427 -0
- package/cypress/e2e/chemistry/atom.cy.js +201 -0
- package/cypress/e2e/chemistry/ion.cy.js +608 -0
- package/cypress/e2e/chemistry/ioniccompound.cy.js +133 -0
- package/cypress/e2e/dynamicalsystem/cobwebpolyline.cy.js +2653 -0
- package/cypress/e2e/dynamicalsystem/equilibriumcurve.cy.js +311 -0
- package/cypress/e2e/dynamicalsystem/equilibriumline.cy.js +279 -0
- package/cypress/e2e/dynamicalsystem/equilibriumpoint.cy.js +283 -0
- package/cypress/e2e/dynamicalsystem/odesystem.cy.js +1834 -0
- package/cypress/e2e/equality/mathexpressions.cy.js +948 -0
- package/cypress/e2e/graphing/graphreferences.cy.js +978 -0
- package/cypress/e2e/graphing/graphreferences2.cy.js +615 -0
- package/cypress/e2e/linearAlgebra/eigenDecomposition.cy.js +401 -0
- package/cypress/e2e/tagSpecific/angle.cy.js +3898 -0
- package/cypress/e2e/tagSpecific/animatefromsequence.cy.js +2306 -0
- package/cypress/e2e/tagSpecific/answer.cy.js +31647 -0
- package/cypress/e2e/tagSpecific/bestfitline.cy.js +612 -0
- package/cypress/e2e/tagSpecific/blockquote.cy.js +30 -0
- package/cypress/e2e/tagSpecific/boolean.cy.js +742 -0
- package/cypress/e2e/tagSpecific/booleaninput.cy.js +1283 -0
- package/cypress/e2e/tagSpecific/booleanlist.cy.js +588 -0
- package/cypress/e2e/tagSpecific/booleanoperators.cy.js +596 -0
- package/cypress/e2e/tagSpecific/booleanoperatorsonmath.cy.js +498 -0
- package/cypress/e2e/tagSpecific/callaction.cy.js +2835 -0
- package/cypress/e2e/tagSpecific/choiceinput.cy.js +3205 -0
- package/cypress/e2e/tagSpecific/circle.cy.js +22036 -0
- package/cypress/e2e/tagSpecific/codeeditor.cy.js +1995 -0
- package/cypress/e2e/tagSpecific/collect.cy.js +5035 -0
- package/cypress/e2e/tagSpecific/componentsize.cy.js +502 -0
- package/cypress/e2e/tagSpecific/conditionalcontent.cy.js +3495 -0
- package/cypress/e2e/tagSpecific/contentBrowser.cy.js +335 -0
- package/cypress/e2e/tagSpecific/contentpicker.cy.js +261 -0
- package/cypress/e2e/tagSpecific/copy.cy.js +12627 -0
- package/cypress/e2e/tagSpecific/copy2.cy.js +5698 -0
- package/cypress/e2e/tagSpecific/curve.bezier.cy.js +12440 -0
- package/cypress/e2e/tagSpecific/curve.cy.js +1716 -0
- package/cypress/e2e/tagSpecific/curve.function.cy.js +1471 -0
- package/cypress/e2e/tagSpecific/curve.parametrized.cy.js +920 -0
- package/cypress/e2e/tagSpecific/document.cy.js +234 -0
- package/cypress/e2e/tagSpecific/endpoint.cy.js +197 -0
- package/cypress/e2e/tagSpecific/evaluate.cy.js +8895 -0
- package/cypress/e2e/tagSpecific/extract.cy.js +2282 -0
- package/cypress/e2e/tagSpecific/feedback.cy.js +2941 -0
- package/cypress/e2e/tagSpecific/function.cy.js +9450 -0
- package/cypress/e2e/tagSpecific/functioniterates.cy.js +1178 -0
- package/cypress/e2e/tagSpecific/functionoperators.cy.js +4047 -0
- package/cypress/e2e/tagSpecific/graph.cy.js +2491 -0
- package/cypress/e2e/tagSpecific/group.cy.js +683 -0
- package/cypress/e2e/tagSpecific/hint.cy.js +204 -0
- package/cypress/e2e/tagSpecific/image.cy.js +770 -0
- package/cypress/e2e/tagSpecific/integer.cy.js +206 -0
- package/cypress/e2e/tagSpecific/label.cy.js +800 -0
- package/cypress/e2e/tagSpecific/legend.cy.js +1001 -0
- package/cypress/e2e/tagSpecific/line.cy.js +12167 -0
- package/cypress/e2e/tagSpecific/linesegment.cy.js +4749 -0
- package/cypress/e2e/tagSpecific/lorem.cy.js +289 -0
- package/cypress/e2e/tagSpecific/map.cy.js +4476 -0
- package/cypress/e2e/tagSpecific/matchespattern.cy.js +693 -0
- package/cypress/e2e/tagSpecific/math.cy.js +10990 -0
- package/cypress/e2e/tagSpecific/mathdisplay.cy.js +2689 -0
- package/cypress/e2e/tagSpecific/mathinput.cy.js +15628 -0
- package/cypress/e2e/tagSpecific/mathinputgraph.cy.js +566 -0
- package/cypress/e2e/tagSpecific/mathlist.cy.js +4073 -0
- package/cypress/e2e/tagSpecific/mathoperators.cy.js +13851 -0
- package/cypress/e2e/tagSpecific/matrix.cy.js +8825 -0
- package/cypress/e2e/tagSpecific/matrixinput.cy.js +16277 -0
- package/cypress/e2e/tagSpecific/module.cy.js +1771 -0
- package/cypress/e2e/tagSpecific/number.cy.js +2221 -0
- package/cypress/e2e/tagSpecific/numberlist.cy.js +1285 -0
- package/cypress/e2e/tagSpecific/p.cy.js +72 -0
- package/cypress/e2e/tagSpecific/paginator.cy.js +2983 -0
- package/cypress/e2e/tagSpecific/parabola.cy.js +14331 -0
- package/cypress/e2e/tagSpecific/paragraphmarkup.cy.js +104 -0
- package/cypress/e2e/tagSpecific/periodicset.cy.js +1439 -0
- package/cypress/e2e/tagSpecific/piecewisefunction.cy.js +1055 -0
- package/cypress/e2e/tagSpecific/pluralize.cy.js +274 -0
- package/cypress/e2e/tagSpecific/point.cy.js +8895 -0
- package/cypress/e2e/tagSpecific/point2.cy.js +10259 -0
- package/cypress/e2e/tagSpecific/polygon.cy.js +5039 -0
- package/cypress/e2e/tagSpecific/polyline.cy.js +4704 -0
- package/cypress/e2e/tagSpecific/problem.cy.js +2768 -0
- package/cypress/e2e/tagSpecific/ray.cy.js +10770 -0
- package/cypress/e2e/tagSpecific/rectangle.cy.js +2143 -0
- package/cypress/e2e/tagSpecific/ref.cy.js +420 -0
- package/cypress/e2e/tagSpecific/regularPolygon.cy.js +1006 -0
- package/cypress/e2e/tagSpecific/regularPolygon2.cy.js +100 -0
- package/cypress/e2e/tagSpecific/regularPolygon3.cy.js +777 -0
- package/cypress/e2e/tagSpecific/samplerandomnumbers.cy.js +3619 -0
- package/cypress/e2e/tagSpecific/sectioning.cy.js +3530 -0
- package/cypress/e2e/tagSpecific/select.cy.js +5376 -0
- package/cypress/e2e/tagSpecific/selectfromsequence.cy.js +3846 -0
- package/cypress/e2e/tagSpecific/selectrandomnumbers.cy.js +2914 -0
- package/cypress/e2e/tagSpecific/sequence.cy.js +2093 -0
- package/cypress/e2e/tagSpecific/shuffle.cy.js +490 -0
- package/cypress/e2e/tagSpecific/sidebyside.cy.js +8057 -0
- package/cypress/e2e/tagSpecific/singlecharactercomponents.cy.js +72 -0
- package/cypress/e2e/tagSpecific/slider.cy.js +914 -0
- package/cypress/e2e/tagSpecific/solution.cy.js +109 -0
- package/cypress/e2e/tagSpecific/solveequations.cy.js +1026 -0
- package/cypress/e2e/tagSpecific/sort.cy.js +1685 -0
- package/cypress/e2e/tagSpecific/spreadsheet.cy.js +5971 -0
- package/cypress/e2e/tagSpecific/subsetofreals.cy.js +2725 -0
- package/cypress/e2e/tagSpecific/substitute.cy.js +2646 -0
- package/cypress/e2e/tagSpecific/tabular.cy.js +36 -0
- package/cypress/e2e/tagSpecific/text.cy.js +975 -0
- package/cypress/e2e/tagSpecific/textinput.cy.js +2177 -0
- package/cypress/e2e/tagSpecific/textlist.cy.js +369 -0
- package/cypress/e2e/tagSpecific/triangle.cy.js +1936 -0
- package/cypress/e2e/tagSpecific/triggerset.cy.js +2023 -0
- package/cypress/e2e/tagSpecific/updatevalue.cy.js +3288 -0
- package/cypress/e2e/tagSpecific/vector.cy.js +20183 -0
- package/cypress/e2e/tagSpecific/video.cy.js +612 -0
- package/cypress/e2e/tagSpecific/when.cy.js +202 -0
- package/cypress/e2e/variants/specifysinglevariant.cy.js +6726 -0
- package/cypress/e2e/variants/uniquevariants.cy.js +4846 -0
- package/cypress/fixtures/example.json +5 -0
- package/cypress/support/commands.js +32 -0
- package/cypress/support/e2e.js +31 -0
- package/cypress.config.js +18 -0
- package/docs/codeSnippet.jsx +11 -0
- package/docs/index.html +133 -0
- package/docs/index.jsx +138 -0
- package/docs/prism.css +3 -0
- package/index.html +14 -0
- package/index.js +21 -0
- package/media/answer_example.png +0 -0
- package/media/graph_example.png +0 -0
- package/media/graph_markup_example.png +0 -0
- package/package.json +83 -0
- package/public/favicon.ico +0 -0
- package/public/fonts/files/open-sans-v18-latin-700.woff +0 -0
- package/public/fonts/files/open-sans-v18-latin-700.woff2 +0 -0
- package/public/fonts/files/open-sans-v18-latin-700italic.woff +0 -0
- package/public/fonts/files/open-sans-v18-latin-700italic.woff2 +0 -0
- package/public/fonts/files/open-sans-v18-latin-italic.woff +0 -0
- package/public/fonts/files/open-sans-v18-latin-italic.woff2 +0 -0
- package/public/fonts/files/open-sans-v18-latin-light-italic.woff +0 -0
- package/public/fonts/files/open-sans-v18-latin-light-italic.woff2 +0 -0
- package/public/fonts/files/open-sans-v18-latin-light.woff +0 -0
- package/public/fonts/files/open-sans-v18-latin-light.woff2 +0 -0
- package/public/fonts/files/open-sans-v18-latin-regular.woff +0 -0
- package/public/fonts/files/open-sans-v18-latin-regular.woff2 +0 -0
- package/src/Core/ComponentTypes.js +426 -0
- package/src/Core/Core.js +11852 -0
- package/src/Core/CoreWorker.js +127 -0
- package/src/Core/Dependencies.js +8226 -0
- package/src/Core/Numerics.js +473 -0
- package/src/Core/ParameterStack.js +36 -0
- package/src/Core/ReadOnlyProxyHandler.js +41 -0
- package/src/Core/StateProxyHandler.js +88 -0
- package/src/Core/components/Aliases.js +67 -0
- package/src/Core/components/Angle.js +758 -0
- package/src/Core/components/AnimateFromSequence.js +922 -0
- package/src/Core/components/Answer.js +2087 -0
- package/src/Core/components/AsList.js +83 -0
- package/src/Core/components/AttractTo.js +245 -0
- package/src/Core/components/AttractToAngles.js +262 -0
- package/src/Core/components/AttractToConstraint.js +104 -0
- package/src/Core/components/AttractToGrid.js +315 -0
- package/src/Core/components/Award.js +906 -0
- package/src/Core/components/BestFitLine.js +318 -0
- package/src/Core/components/BezierControls.js +719 -0
- package/src/Core/components/BlockQuote.js +35 -0
- package/src/Core/components/Boolean.js +500 -0
- package/src/Core/components/BooleanInput.js +330 -0
- package/src/Core/components/BooleanList.js +396 -0
- package/src/Core/components/BooleanOperators.js +35 -0
- package/src/Core/components/BooleanOperatorsOfMath.js +148 -0
- package/src/Core/components/CallAction.js +261 -0
- package/src/Core/components/Caption.js +73 -0
- package/src/Core/components/Case.js +56 -0
- package/src/Core/components/Cell.js +439 -0
- package/src/Core/components/CellBlock.js +64 -0
- package/src/Core/components/Chart.js +795 -0
- package/src/Core/components/Choice.js +266 -0
- package/src/Core/components/ChoiceInput.js +1407 -0
- package/src/Core/components/Circle.js +2884 -0
- package/src/Core/components/CodeEditor.js +647 -0
- package/src/Core/components/CodeViewer.js +294 -0
- package/src/Core/components/CollaborateGroupSetup.js +46 -0
- package/src/Core/components/CollaborateGroups.js +119 -0
- package/src/Core/components/Collect.js +850 -0
- package/src/Core/components/Column.js +608 -0
- package/src/Core/components/ConditionalContent.js +468 -0
- package/src/Core/components/ConsiderAsResponses.js +49 -0
- package/src/Core/components/ConstrainTo.js +161 -0
- package/src/Core/components/ConstrainToAngles.js +244 -0
- package/src/Core/components/ConstrainToGraph.js +142 -0
- package/src/Core/components/ConstrainToGrid.js +175 -0
- package/src/Core/components/ConstraintUnion.js +119 -0
- package/src/Core/components/Constraints.js +497 -0
- package/src/Core/components/ContentBrowser.js +441 -0
- package/src/Core/components/ContentPicker.js +263 -0
- package/src/Core/components/ControlVectors.js +25 -0
- package/src/Core/components/Coords.js +63 -0
- package/src/Core/components/Copy.js +3412 -0
- package/src/Core/components/Curve.js +4130 -0
- package/src/Core/components/CustomAttribute.js +175 -0
- package/src/Core/components/DataFrame.js +357 -0
- package/src/Core/components/DiscreteSimulationResultList.js +342 -0
- package/src/Core/components/DiscreteSimulationResultPolyline.js +581 -0
- package/src/Core/components/Divisions.js +55 -0
- package/src/Core/components/Document.js +888 -0
- package/src/Core/components/Embed.js +65 -0
- package/src/Core/components/Endpoint.js +62 -0
- package/src/Core/components/Evaluate.js +321 -0
- package/src/Core/components/Extract.js +656 -0
- package/src/Core/components/Extrema.js +556 -0
- package/src/Core/components/Feedback.js +200 -0
- package/src/Core/components/FeedbackDefinitions.js +97 -0
- package/src/Core/components/Figure.js +148 -0
- package/src/Core/components/Footnote.js +73 -0
- package/src/Core/components/Function.js +5344 -0
- package/src/Core/components/FunctionIterates.js +306 -0
- package/src/Core/components/FunctionOperators.js +702 -0
- package/src/Core/components/Graph.js +1679 -0
- package/src/Core/components/Group.js +7 -0
- package/src/Core/components/HasSameFactoring.js +407 -0
- package/src/Core/components/Hint.js +241 -0
- package/src/Core/components/Image.js +524 -0
- package/src/Core/components/Indexing.js +79 -0
- package/src/Core/components/IntComma.js +64 -0
- package/src/Core/components/Integer.js +81 -0
- package/src/Core/components/Intersection.js +328 -0
- package/src/Core/components/Interval.js +29 -0
- package/src/Core/components/Label.js +492 -0
- package/src/Core/components/Latex.js +104 -0
- package/src/Core/components/Legend.js +329 -0
- package/src/Core/components/Line.js +2040 -0
- package/src/Core/components/LineSegment.js +882 -0
- package/src/Core/components/Lists.js +180 -0
- package/src/Core/components/Lorem.js +249 -0
- package/src/Core/components/MMeMen.js +377 -0
- package/src/Core/components/Map.js +873 -0
- package/src/Core/components/Markers.js +101 -0
- package/src/Core/components/MatchesPattern.js +339 -0
- package/src/Core/components/Math.js +2552 -0
- package/src/Core/components/MathInput.js +948 -0
- package/src/Core/components/MathList.js +828 -0
- package/src/Core/components/MathOperators.js +1286 -0
- package/src/Core/components/Matrix.js +497 -0
- package/src/Core/components/MatrixInput.js +3157 -0
- package/src/Core/components/MdMdnMrow.js +394 -0
- package/src/Core/components/Module.js +16 -0
- package/src/Core/components/Number.js +1031 -0
- package/src/Core/components/NumberList.js +550 -0
- package/src/Core/components/Option.js +24 -0
- package/src/Core/components/P.js +71 -0
- package/src/Core/components/Paginator.js +338 -0
- package/src/Core/components/Panel.js +126 -0
- package/src/Core/components/Parabola.js +1561 -0
- package/src/Core/components/ParagraphMarkup.js +59 -0
- package/src/Core/components/Pegboard.js +43 -0
- package/src/Core/components/PeriodicSet.js +291 -0
- package/src/Core/components/PiecewiseFunction.js +832 -0
- package/src/Core/components/Pluralize.js +198 -0
- package/src/Core/components/Point.js +1295 -0
- package/src/Core/components/Polygon.js +408 -0
- package/src/Core/components/Polyline.js +841 -0
- package/src/Core/components/RandomizedTextList.js +225 -0
- package/src/Core/components/Ray.js +1737 -0
- package/src/Core/components/Rectangle.js +1535 -0
- package/src/Core/components/Ref.js +350 -0
- package/src/Core/components/RegionBetweenCurveXAxis.js +124 -0
- package/src/Core/components/RegionHalfPlane.js +107 -0
- package/src/Core/components/RegularPolygon.js +2118 -0
- package/src/Core/components/RenderDoenetML.js +181 -0
- package/src/Core/components/Row.js +780 -0
- package/src/Core/components/SampleRandomNumbers.js +653 -0
- package/src/Core/components/Sectioning.js +303 -0
- package/src/Core/components/Select.js +947 -0
- package/src/Core/components/SelectFromSequence.js +1242 -0
- package/src/Core/components/SelectRandomNumbers.js +225 -0
- package/src/Core/components/Sequence.js +444 -0
- package/src/Core/components/Setup.js +53 -0
- package/src/Core/components/Shuffle.js +470 -0
- package/src/Core/components/SideBySide.js +2130 -0
- package/src/Core/components/SingleCharacterComponents.js +41 -0
- package/src/Core/components/Slider.js +819 -0
- package/src/Core/components/SolutionContainer.js +67 -0
- package/src/Core/components/Solutions.js +334 -0
- package/src/Core/components/SolveEquations.js +568 -0
- package/src/Core/components/Sort.js +398 -0
- package/src/Core/components/Sources.js +108 -0
- package/src/Core/components/Split.js +205 -0
- package/src/Core/components/Spreadsheet.js +1507 -0
- package/src/Core/components/StyleDefinitions.js +111 -0
- package/src/Core/components/SubsetOfReals.js +348 -0
- package/src/Core/components/SubsetOfRealsInput.js +1474 -0
- package/src/Core/components/Substitute.js +496 -0
- package/src/Core/components/SummaryStatistics.js +652 -0
- package/src/Core/components/Table.js +145 -0
- package/src/Core/components/Tabular.js +384 -0
- package/src/Core/components/Template.js +360 -0
- package/src/Core/components/Text.js +341 -0
- package/src/Core/components/TextInput.js +566 -0
- package/src/Core/components/TextList.js +442 -0
- package/src/Core/components/TextListFromString.js +137 -0
- package/src/Core/components/TextOperatorsOfMath.js +21 -0
- package/src/Core/components/Triangle.js +280 -0
- package/src/Core/components/TriggerSet.js +189 -0
- package/src/Core/components/TupleList.js +43 -0
- package/src/Core/components/UpdateValue.js +435 -0
- package/src/Core/components/VariantControl.js +36 -0
- package/src/Core/components/Vector.js +2478 -0
- package/src/Core/components/Verbatim.js +125 -0
- package/src/Core/components/Video.js +673 -0
- package/src/Core/components/When.js +198 -0
- package/src/Core/components/abstract/AngleListComponent.js +140 -0
- package/src/Core/components/abstract/BaseComponent.js +1496 -0
- package/src/Core/components/abstract/BlockComponent.js +5 -0
- package/src/Core/components/abstract/BooleanBaseOperator.js +88 -0
- package/src/Core/components/abstract/BooleanBaseOperatorOfMath.js +100 -0
- package/src/Core/components/abstract/BooleanBaseOperatorOneInput.js +44 -0
- package/src/Core/components/abstract/ComponentSize.js +789 -0
- package/src/Core/components/abstract/ComponentWithSelectableType.js +537 -0
- package/src/Core/components/abstract/CompositeComponent.js +142 -0
- package/src/Core/components/abstract/ConstraintComponent.js +19 -0
- package/src/Core/components/abstract/FunctionBaseOperator.js +680 -0
- package/src/Core/components/abstract/GraphicalComponent.js +56 -0
- package/src/Core/components/abstract/InlineComponent.js +5 -0
- package/src/Core/components/abstract/InlineRenderInlineChildren.js +63 -0
- package/src/Core/components/abstract/Input.js +192 -0
- package/src/Core/components/abstract/IntervalListComponent.js +218 -0
- package/src/Core/components/abstract/LineListComponent.js +114 -0
- package/src/Core/components/abstract/MathBaseOperator.js +631 -0
- package/src/Core/components/abstract/MathBaseOperatorOneInput.js +112 -0
- package/src/Core/components/abstract/PointListComponent.js +238 -0
- package/src/Core/components/abstract/SectioningComponent.js +1262 -0
- package/src/Core/components/abstract/SingleCharacterInline.js +23 -0
- package/src/Core/components/abstract/TextBaseOperatorOfMath.js +47 -0
- package/src/Core/components/abstract/TextOrInline.js +66 -0
- package/src/Core/components/abstract/VariableName.js +31 -0
- package/src/Core/components/abstract/VariableNameList.js +83 -0
- package/src/Core/components/abstract/VectorListComponent.js +235 -0
- package/src/Core/components/chemistry/Atom.js +910 -0
- package/src/Core/components/chemistry/ElectronConfiguration.js +36 -0
- package/src/Core/components/chemistry/Ion.js +684 -0
- package/src/Core/components/chemistry/IonicCompound.js +189 -0
- package/src/Core/components/chemistry/OrbitalDiagram.js +175 -0
- package/src/Core/components/chemistry/OrbitalDiagramInput.js +753 -0
- package/src/Core/components/chemistry/index.js +6 -0
- package/src/Core/components/commonsugar/breakstrings.js +627 -0
- package/src/Core/components/commonsugar/lists.js +177 -0
- package/src/Core/components/dynamicalSystems/CobwebPolyline.js +913 -0
- package/src/Core/components/dynamicalSystems/EquilibriumCurve.js +95 -0
- package/src/Core/components/dynamicalSystems/EquilibriumLine.js +93 -0
- package/src/Core/components/dynamicalSystems/EquilibriumPoint.js +93 -0
- package/src/Core/components/dynamicalSystems/ODESystem.js +943 -0
- package/src/Core/components/dynamicalSystems/index.js +5 -0
- package/src/Core/components/linearAlgebra/EigenDecomposition.js +294 -0
- package/src/Core/utils/array.js +30 -0
- package/src/Core/utils/booleanLogic.js +965 -0
- package/src/Core/utils/checkEquality.js +818 -0
- package/src/Core/utils/cid.js +29 -0
- package/src/Core/utils/componentInfoObjects.js +100 -0
- package/src/Core/utils/constraints.js +23 -0
- package/src/Core/utils/copy.js +572 -0
- package/src/Core/utils/deepFunctions.js +173 -0
- package/src/Core/utils/descendants.js +252 -0
- package/src/Core/utils/enumeration.js +234 -0
- package/src/Core/utils/feedback.js +84 -0
- package/src/Core/utils/function.js +1343 -0
- package/src/Core/utils/graphical.js +196 -0
- package/src/Core/utils/label.js +396 -0
- package/src/Core/utils/math.js +1056 -0
- package/src/Core/utils/naming.js +45 -0
- package/src/Core/utils/periodicSetEquality.js +403 -0
- package/src/Core/utils/randomNumbers.js +70 -0
- package/src/Core/utils/retrieveMedia.js +98 -0
- package/src/Core/utils/retrieveTextFile.js +140 -0
- package/src/Core/utils/returnAllPossibleVariants.js +73 -0
- package/src/Core/utils/rounding.js +316 -0
- package/src/Core/utils/sequence.js +754 -0
- package/src/Core/utils/serializedStateProcessing.js +4049 -0
- package/src/Core/utils/size.js +22 -0
- package/src/Core/utils/stateVariables.js +138 -0
- package/src/Core/utils/style.js +535 -0
- package/src/Core/utils/subset-of-reals.js +796 -0
- package/src/Core/utils/table.js +41 -0
- package/src/Core/utils/text.js +16 -0
- package/src/Core/utils/triggering.js +167 -0
- package/src/Core/utils/variants.js +477 -0
- package/src/DoenetML.css +308 -0
- package/src/DoenetML.jsx +201 -0
- package/src/Parser/doenet.grammar +90 -0
- package/src/Parser/doenet.js +33 -0
- package/src/Parser/doenet.terms.js +20 -0
- package/src/Parser/parser.js +266 -0
- package/src/Parser/tokens.js +129 -0
- package/src/Tools/CodeMirror.jsx +440 -0
- package/src/Tools/DarkmodeController.jsx +21 -0
- package/src/Tools/Footers/MathInputSelector.jsx +34 -0
- package/src/Tools/Footers/VirtualKeyboard.jsx +751 -0
- package/src/Tools/cypressTest/CypressTest.jsx +341 -0
- package/src/Tools/cypressTest/index.html +102 -0
- package/src/Tools/cypressTest/index.jsx +40 -0
- package/src/Viewer/ActivityViewer.jsx +1461 -0
- package/src/Viewer/PageViewer.jsx +1329 -0
- package/src/Viewer/renderers/alert.jsx +17 -0
- package/src/Viewer/renderers/angle.jsx +209 -0
- package/src/Viewer/renderers/answer.jsx +206 -0
- package/src/Viewer/renderers/asList.jsx +25 -0
- package/src/Viewer/renderers/blockQuote.jsx +41 -0
- package/src/Viewer/renderers/boolean.jsx +17 -0
- package/src/Viewer/renderers/booleanInput.css +105 -0
- package/src/Viewer/renderers/booleanInput.jsx +636 -0
- package/src/Viewer/renderers/button.jsx +369 -0
- package/src/Viewer/renderers/c.jsx +17 -0
- package/src/Viewer/renderers/callAction.jsx +18 -0
- package/src/Viewer/renderers/cell.jsx +59 -0
- package/src/Viewer/renderers/chart.jsx +83 -0
- package/src/Viewer/renderers/choiceInput.css +223 -0
- package/src/Viewer/renderers/choiceInput.jsx +535 -0
- package/src/Viewer/renderers/circle.jsx +990 -0
- package/src/Viewer/renderers/cobwebPolyline.jsx +442 -0
- package/src/Viewer/renderers/codeEditor.jsx +248 -0
- package/src/Viewer/renderers/codeViewer.jsx +105 -0
- package/src/Viewer/renderers/containerBlock.jsx +41 -0
- package/src/Viewer/renderers/containerInline.jsx +17 -0
- package/src/Viewer/renderers/contentBrowser.jsx +159 -0
- package/src/Viewer/renderers/contentPicker.jsx +160 -0
- package/src/Viewer/renderers/curve.jsx +1072 -0
- package/src/Viewer/renderers/ellipsis.jsx +17 -0
- package/src/Viewer/renderers/em.jsx +17 -0
- package/src/Viewer/renderers/embed.jsx +110 -0
- package/src/Viewer/renderers/feedback.jsx +74 -0
- package/src/Viewer/renderers/figure.jsx +131 -0
- package/src/Viewer/renderers/footnote.jsx +52 -0
- package/src/Viewer/renderers/graph.jsx +925 -0
- package/src/Viewer/renderers/hint.jsx +142 -0
- package/src/Viewer/renderers/image.jsx +581 -0
- package/src/Viewer/renderers/jsxgraph-distrib/jsxgraphcore.mjs +2 -0
- package/src/Viewer/renderers/jsxgraph-distrib/jsxgraphcore.mjs.map +1 -0
- package/src/Viewer/renderers/label.jsx +470 -0
- package/src/Viewer/renderers/legend.jsx +306 -0
- package/src/Viewer/renderers/line.jsx +511 -0
- package/src/Viewer/renderers/lineSegment.jsx +754 -0
- package/src/Viewer/renderers/list.jsx +111 -0
- package/src/Viewer/renderers/lq.jsx +12 -0
- package/src/Viewer/renderers/lsq.jsx +12 -0
- package/src/Viewer/renderers/math.jsx +582 -0
- package/src/Viewer/renderers/mathInput.css +10 -0
- package/src/Viewer/renderers/mathInput.jsx +425 -0
- package/src/Viewer/renderers/mathInputog.jsx +534 -0
- package/src/Viewer/renderers/mathList.jsx +39 -0
- package/src/Viewer/renderers/matrixInput.jsx +317 -0
- package/src/Viewer/renderers/mdash.jsx +12 -0
- package/src/Viewer/renderers/nbsp.jsx +12 -0
- package/src/Viewer/renderers/ndash.jsx +12 -0
- package/src/Viewer/renderers/number.jsx +454 -0
- package/src/Viewer/renderers/numberList.jsx +35 -0
- package/src/Viewer/renderers/orbitalDiagram.jsx +247 -0
- package/src/Viewer/renderers/orbitalDiagramInput.jsx +450 -0
- package/src/Viewer/renderers/p.jsx +38 -0
- package/src/Viewer/renderers/paginatorControls.jsx +41 -0
- package/src/Viewer/renderers/pegboard.jsx +239 -0
- package/src/Viewer/renderers/point.jsx +649 -0
- package/src/Viewer/renderers/polygon.jsx +612 -0
- package/src/Viewer/renderers/polyline.jsx +608 -0
- package/src/Viewer/renderers/pre.jsx +34 -0
- package/src/Viewer/renderers/q.jsx +17 -0
- package/src/Viewer/renderers/ray.jsx +410 -0
- package/src/Viewer/renderers/ref.jsx +149 -0
- package/src/Viewer/renderers/regionBetweenCurveXAxis.jsx +182 -0
- package/src/Viewer/renderers/renderDoenetML.jsx +56 -0
- package/src/Viewer/renderers/row.jsx +31 -0
- package/src/Viewer/renderers/rq.jsx +12 -0
- package/src/Viewer/renderers/rsq.jsx +12 -0
- package/src/Viewer/renderers/section.jsx +427 -0
- package/src/Viewer/renderers/sideBySide.jsx +80 -0
- package/src/Viewer/renderers/slider.jsx +800 -0
- package/src/Viewer/renderers/solution.jsx +134 -0
- package/src/Viewer/renderers/spreadsheet.jsx +83 -0
- package/src/Viewer/renderers/sq.jsx +17 -0
- package/src/Viewer/renderers/styles/global.css +14 -0
- package/src/Viewer/renderers/subsetOfRealsInput.jsx +392 -0
- package/src/Viewer/renderers/summaryStatistics.jsx +83 -0
- package/src/Viewer/renderers/table.jsx +78 -0
- package/src/Viewer/renderers/tabular.jsx +58 -0
- package/src/Viewer/renderers/tag.jsx +26 -0
- package/src/Viewer/renderers/text.jsx +439 -0
- package/src/Viewer/renderers/textInput.jsx +774 -0
- package/src/Viewer/renderers/textList.jsx +30 -0
- package/src/Viewer/renderers/triggerSet.jsx +52 -0
- package/src/Viewer/renderers/updateValue.jsx +30 -0
- package/src/Viewer/renderers/utils/css.js +13 -0
- package/src/Viewer/renderers/utils/graph.js +159 -0
- package/src/Viewer/renderers/utils/offGraphIndicators.js +91 -0
- package/src/Viewer/renderers/vector.jsx +678 -0
- package/src/Viewer/renderers/video.jsx +494 -0
- package/src/Viewer/useDoenetRenderer.jsx +128 -0
- package/src/main.jsx +16 -0
- package/src/media/fonts/files/open-sans-v18-latin-700.woff +0 -0
- package/src/media/fonts/files/open-sans-v18-latin-700.woff2 +0 -0
- package/src/media/fonts/files/open-sans-v18-latin-700italic.woff +0 -0
- package/src/media/fonts/files/open-sans-v18-latin-700italic.woff2 +0 -0
- package/src/media/fonts/files/open-sans-v18-latin-italic.woff +0 -0
- package/src/media/fonts/files/open-sans-v18-latin-italic.woff2 +0 -0
- package/src/media/fonts/files/open-sans-v18-latin-light-italic.woff +0 -0
- package/src/media/fonts/files/open-sans-v18-latin-light-italic.woff2 +0 -0
- package/src/media/fonts/files/open-sans-v18-latin-light.woff +0 -0
- package/src/media/fonts/files/open-sans-v18-latin-light.woff2 +0 -0
- package/src/media/fonts/files/open-sans-v18-latin-regular.woff +0 -0
- package/src/media/fonts/files/open-sans-v18-latin-regular.woff2 +0 -0
- package/src/test/testCode.doenet +26 -0
- package/src/test/testViewer.jsx +158 -0
- package/src/uiComponents/ActionButton.jsx +157 -0
- package/src/uiComponents/ActionButtonGroup.jsx +93 -0
- package/src/uiComponents/Button.jsx +160 -0
- package/src/uiComponents/ButtonGroup.jsx +56 -0
- package/src/uiComponents/ToggleButton.jsx +194 -0
- package/src/uiComponents/ToggleButtonGroup.jsx +77 -0
- package/src/utils/activityUtils.js +713 -0
- package/src/utils/array.js +17 -0
- package/src/utils/cid.js +34 -0
- package/src/utils/componentInfoObjects.js +89 -0
- package/src/utils/deepFunctions.js +165 -0
- package/src/utils/enumeration.js +226 -0
- package/src/utils/math.js +624 -0
- package/src/utils/naming.js +44 -0
- package/src/utils/retrieveTextFile.js +156 -0
- package/src/utils/returnAllPossibleVariants.js +81 -0
- package/src/utils/sequence.js +715 -0
- package/src/utils/serialize.js +29 -0
- package/src/utils/serializedStateProcessing.js +2587 -0
- package/src/utils/subset-of-reals.js +783 -0
- package/src/utils/url.js +19 -0
- package/vite.config.js +14 -0
|
@@ -0,0 +1,2587 @@
|
|
|
1
|
+
import me from 'math-expressions';
|
|
2
|
+
import { createUniqueName } from './naming';
|
|
3
|
+
import { flattenDeep } from './array';
|
|
4
|
+
import { deepClone } from './deepFunctions';
|
|
5
|
+
import { parseAndCompile } from '../Parser/parser';
|
|
6
|
+
import subsets from './subset-of-reals';
|
|
7
|
+
import { retrieveTextFileForCid } from './retrieveTextFile';
|
|
8
|
+
|
|
9
|
+
export async function expandDoenetMLsToFullSerializedComponents({
|
|
10
|
+
cids, doenetMLs,
|
|
11
|
+
componentInfoObjects,
|
|
12
|
+
}) {
|
|
13
|
+
|
|
14
|
+
let arrayOfSerializedComponents = [];
|
|
15
|
+
let cidComponents = {};
|
|
16
|
+
|
|
17
|
+
for (let doenetML of doenetMLs) {
|
|
18
|
+
|
|
19
|
+
let serializedComponents = parseAndCompile(doenetML);
|
|
20
|
+
|
|
21
|
+
serializedComponents = cleanIfHaveJustDocument(serializedComponents);
|
|
22
|
+
|
|
23
|
+
substituteDeprecations(serializedComponents);
|
|
24
|
+
|
|
25
|
+
correctComponentTypeCapitalization(serializedComponents, componentInfoObjects.componentTypeLowerCaseMapping);
|
|
26
|
+
|
|
27
|
+
createAttributesFromProps(serializedComponents, componentInfoObjects);
|
|
28
|
+
|
|
29
|
+
applyMacros(serializedComponents, componentInfoObjects);
|
|
30
|
+
|
|
31
|
+
// remove blank string children after applying macros,
|
|
32
|
+
// as applying macros could create additional blank string children
|
|
33
|
+
removeBlankStringChildren(serializedComponents, componentInfoObjects)
|
|
34
|
+
|
|
35
|
+
decodeXMLEntities(serializedComponents);
|
|
36
|
+
|
|
37
|
+
applySugar({ serializedComponents, componentInfoObjects });
|
|
38
|
+
|
|
39
|
+
arrayOfSerializedComponents.push(serializedComponents);
|
|
40
|
+
|
|
41
|
+
let newContentComponents = findContentCopies({ serializedComponents });
|
|
42
|
+
|
|
43
|
+
for (let cid in newContentComponents.cidComponents) {
|
|
44
|
+
if (cidComponents[cid] === undefined) {
|
|
45
|
+
cidComponents[cid] = []
|
|
46
|
+
}
|
|
47
|
+
cidComponents[cid].push(...newContentComponents.cidComponents[cid])
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let cidList = Object.keys(cidComponents);
|
|
52
|
+
if (cidList.length > 0) {
|
|
53
|
+
// found copies with cids
|
|
54
|
+
// so look up those cids
|
|
55
|
+
// convert to doenetMLs, and recurse on those doenetMLs
|
|
56
|
+
|
|
57
|
+
let { newDoenetMLs, newCids } = await cidsToDoenetMLs(cidList);
|
|
58
|
+
|
|
59
|
+
// check to see if got the cids requested
|
|
60
|
+
for (let [ind, cid] of cidList.entries()) {
|
|
61
|
+
if (newCids[ind] && newCids[ind].substring(0, cid.length) !== cid) {
|
|
62
|
+
return Promise.reject(new Error(`Requested cid ${cid} but got back ${newCids[ind]}!`));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let expectedN = cidList.length;
|
|
67
|
+
for (let ind = 0; ind < expectedN; ind++) {
|
|
68
|
+
let cid = newCids[ind];
|
|
69
|
+
if (!cid) {
|
|
70
|
+
// wasn't able to retrieve content
|
|
71
|
+
console.warn(`Unable to retrieve content with cid = ${cidList[ind]}`)
|
|
72
|
+
newDoenetMLs[ind] = "";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// recurse to additional doenetMLs
|
|
77
|
+
let { fullSerializedComponents } = await expandDoenetMLsToFullSerializedComponents({
|
|
78
|
+
doenetMLs: newDoenetMLs,
|
|
79
|
+
cids: newCids,
|
|
80
|
+
componentInfoObjects,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
for (let [ind, cid] of cidList.entries()) {
|
|
84
|
+
let serializedComponentsForCid = fullSerializedComponents[ind];
|
|
85
|
+
|
|
86
|
+
for (let originalCopyWithUri of cidComponents[cid]) {
|
|
87
|
+
if (originalCopyWithUri.children === undefined) {
|
|
88
|
+
originalCopyWithUri.children = [];
|
|
89
|
+
}
|
|
90
|
+
originalCopyWithUri.children.push({
|
|
91
|
+
componentType: "externalContent",
|
|
92
|
+
children: JSON.parse(JSON.stringify(serializedComponentsForCid)),
|
|
93
|
+
attributes: { newNamespace: { primitive: true } },
|
|
94
|
+
doenetAttributes: { createUniqueName: true }
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
cids,
|
|
104
|
+
fullSerializedComponents: arrayOfSerializedComponents,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function cidsToDoenetMLs(cids) {
|
|
110
|
+
let promises = [];
|
|
111
|
+
let newCids = cids;
|
|
112
|
+
|
|
113
|
+
for (let cid of cids) {
|
|
114
|
+
promises.push(retrieveTextFileForCid(cid, "doenet"));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return Promise.all(promises).then((newDoenetMLs) => {
|
|
118
|
+
|
|
119
|
+
// console.log({ newDoenetMLs, newCids })
|
|
120
|
+
return Promise.resolve({ newDoenetMLs, newCids });
|
|
121
|
+
|
|
122
|
+
}).catch(err => {
|
|
123
|
+
|
|
124
|
+
let message;
|
|
125
|
+
if (newCids.length === 1) {
|
|
126
|
+
message = `Could not retrieve cid ${newCids[0]}`
|
|
127
|
+
} else {
|
|
128
|
+
message = `Could not retrieve cids ${newCids.join(',')}`
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
message += ": " + err.message;
|
|
132
|
+
|
|
133
|
+
console.error(message)
|
|
134
|
+
|
|
135
|
+
return Promise.reject(new Error(message));
|
|
136
|
+
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function removeBlankStringChildren(serializedComponents, componentInfoObjects) {
|
|
142
|
+
|
|
143
|
+
for (let component of serializedComponents) {
|
|
144
|
+
if (component.children) {
|
|
145
|
+
let componentClass = componentInfoObjects.allComponentClasses[component.componentType];
|
|
146
|
+
if (componentClass && !componentClass.includeBlankStringChildren) {
|
|
147
|
+
component.children = component.children.filter(
|
|
148
|
+
x => typeof x !== "string" || x.trim() !== ""
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
removeBlankStringChildren(component.children, componentInfoObjects)
|
|
153
|
+
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// TODO: do we also need to remove blank string components
|
|
157
|
+
// from childrenForComponent of an attribute that is not yet a component?
|
|
158
|
+
for (let attrName in component.attributes) {
|
|
159
|
+
let comp = component.attributes[attrName].component;
|
|
160
|
+
if (comp && comp.children) {
|
|
161
|
+
removeBlankStringChildren([comp], componentInfoObjects)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function findContentCopies({ serializedComponents }) {
|
|
169
|
+
|
|
170
|
+
let cidComponents = {};
|
|
171
|
+
for (let serializedComponent of serializedComponents) {
|
|
172
|
+
if (serializedComponent.componentType === "copy") {
|
|
173
|
+
if (serializedComponent.attributes && serializedComponent.attributes.uri) {
|
|
174
|
+
let uri = serializedComponent.attributes.uri.primitive;
|
|
175
|
+
|
|
176
|
+
if (uri && uri.substring(0, 7).toLowerCase() === "doenet:") {
|
|
177
|
+
|
|
178
|
+
let result = uri.match(/[:&]cid=([^&]+)/i);
|
|
179
|
+
if (result) {
|
|
180
|
+
let cid = result[1];
|
|
181
|
+
if (cidComponents[cid] === undefined) {
|
|
182
|
+
cidComponents[cid] = [];
|
|
183
|
+
}
|
|
184
|
+
cidComponents[cid].push(serializedComponent);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
if (serializedComponent.children !== undefined) {
|
|
191
|
+
let results = findContentCopies({ serializedComponents: serializedComponent.children })
|
|
192
|
+
|
|
193
|
+
// append results on to cidComponents
|
|
194
|
+
for (let cid in results.cidComponents) {
|
|
195
|
+
if (cidComponents[cid] === undefined) {
|
|
196
|
+
cidComponents[cid] = [];
|
|
197
|
+
}
|
|
198
|
+
cidComponents[cid].push(...results.cidComponents[cid]);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return { cidComponents };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function addDocumentIfItsMissing(serializedComponents) {
|
|
207
|
+
|
|
208
|
+
if (serializedComponents.length !== 1 || serializedComponents[0].componentType !== 'document') {
|
|
209
|
+
let components = serializedComponents.splice(0);
|
|
210
|
+
serializedComponents.push({ componentType: 'document', children: components });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function substituteDeprecations(serializedComponents) {
|
|
215
|
+
|
|
216
|
+
// Note: use lower case for keys
|
|
217
|
+
let deprecatedPropertySubstitutions = {
|
|
218
|
+
tname: "target",
|
|
219
|
+
triggerwithtnames: "triggerWithTargets",
|
|
220
|
+
updatewithtname: "updateWithTarget",
|
|
221
|
+
paginatortname: "paginator",
|
|
222
|
+
randomizeorder: "shuffleOrder"
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
for (let component of serializedComponents) {
|
|
226
|
+
if (typeof component !== "object") {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (component.props) {
|
|
231
|
+
let retry = true;
|
|
232
|
+
while (retry) {
|
|
233
|
+
retry = false;
|
|
234
|
+
for (let prop in component.props) {
|
|
235
|
+
let propLower = prop.toLowerCase();
|
|
236
|
+
|
|
237
|
+
if (propLower in deprecatedPropertySubstitutions) {
|
|
238
|
+
let newProp = deprecatedPropertySubstitutions[propLower];
|
|
239
|
+
|
|
240
|
+
console.warn(`Attribute ${prop} is deprecated. Use ${newProp} instead.`)
|
|
241
|
+
|
|
242
|
+
component.props[newProp] = component.props[prop];
|
|
243
|
+
delete component.props[prop];
|
|
244
|
+
|
|
245
|
+
// since modified object over which are looping
|
|
246
|
+
// break out of loop and start over
|
|
247
|
+
retry = true;
|
|
248
|
+
break;
|
|
249
|
+
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (component.children) {
|
|
256
|
+
substituteDeprecations(component.children);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function cleanIfHaveJustDocument(serializedComponents) {
|
|
264
|
+
let componentsWithoutBlankStrings = serializedComponents.filter(
|
|
265
|
+
x => typeof x !== "string" || x.trim() !== ""
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
if (componentsWithoutBlankStrings.length === 1 && componentsWithoutBlankStrings[0].componentType === 'document') {
|
|
269
|
+
return componentsWithoutBlankStrings;
|
|
270
|
+
} else {
|
|
271
|
+
return serializedComponents
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function correctComponentTypeCapitalization(serializedComponents, componentTypeLowerCaseMapping) {
|
|
276
|
+
|
|
277
|
+
//special case for macros before application
|
|
278
|
+
// componentTypeLowerCaseMapping["macro"] = "macro";
|
|
279
|
+
for (let component of serializedComponents) {
|
|
280
|
+
if (typeof component !== "object") {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
let componentTypeFixed = componentTypeLowerCaseMapping[component.componentType.toLowerCase()];
|
|
285
|
+
|
|
286
|
+
if (componentTypeFixed) {
|
|
287
|
+
component.componentType = componentTypeFixed;
|
|
288
|
+
} else {
|
|
289
|
+
throw Error(`Invalid component type${indexRangeString(component)}: ${component.componentType}`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (component.children) {
|
|
293
|
+
correctComponentTypeCapitalization(component.children, componentTypeLowerCaseMapping);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function createAttributesFromProps(serializedComponents, componentInfoObjects) {
|
|
301
|
+
for (let component of serializedComponents) {
|
|
302
|
+
if (typeof component !== "object") {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
let componentClass = componentInfoObjects.allComponentClasses[component.componentType];
|
|
307
|
+
let classAttributes = componentClass.createAttributesObject();
|
|
308
|
+
|
|
309
|
+
let attributeLowerCaseMapping = {};
|
|
310
|
+
|
|
311
|
+
for (let propName in classAttributes) {
|
|
312
|
+
attributeLowerCaseMapping[propName.toLowerCase()] = propName;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
let attributes = {};
|
|
316
|
+
|
|
317
|
+
// if there are any props of json that match attributes for component class
|
|
318
|
+
// create the specified components or primitives
|
|
319
|
+
|
|
320
|
+
let originalComponentProps = Object.assign({}, component.props)
|
|
321
|
+
if (component.props) {
|
|
322
|
+
for (let prop in component.props) {
|
|
323
|
+
let propName = attributeLowerCaseMapping[prop.toLowerCase()]
|
|
324
|
+
let attrObj = classAttributes[propName];
|
|
325
|
+
if (attrObj) {
|
|
326
|
+
|
|
327
|
+
if (propName in attributes) {
|
|
328
|
+
throw Error(`Cannot repeat prop ${propName}. Found in component type ${component.componentType}${indexRangeString(component)}`)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
attributes[propName] = componentFromAttribute({
|
|
332
|
+
attrObj,
|
|
333
|
+
value: component.props[prop],
|
|
334
|
+
originalComponentProps,
|
|
335
|
+
componentInfoObjects,
|
|
336
|
+
});
|
|
337
|
+
delete component.props[prop];
|
|
338
|
+
} else if (!["name", "assignnames", "target"].includes(prop.toLowerCase())) {
|
|
339
|
+
|
|
340
|
+
if (componentClass.acceptAnyAttribute) {
|
|
341
|
+
attributes[prop] = componentFromAttribute({
|
|
342
|
+
value: component.props[prop],
|
|
343
|
+
originalComponentProps,
|
|
344
|
+
componentInfoObjects,
|
|
345
|
+
});
|
|
346
|
+
} else {
|
|
347
|
+
throw Error(`Invalid attribute ${prop} for component of type ${component.componentType}${indexRangeString(component)}`)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
// if there are any primitive attributes that define a default value
|
|
356
|
+
// but were not specified via props, add them with their default value
|
|
357
|
+
|
|
358
|
+
for (let attrName in classAttributes) {
|
|
359
|
+
let attrObj = classAttributes[attrName];
|
|
360
|
+
|
|
361
|
+
if (attrObj.createPrimitiveOfType && ("defaultPrimitiveValue" in attrObj) && !(attrName in attributes)) {
|
|
362
|
+
attributes[attrName] = componentFromAttribute({
|
|
363
|
+
attrObj,
|
|
364
|
+
originalComponentProps,
|
|
365
|
+
value: attrObj.defaultPrimitiveValue.toString(),
|
|
366
|
+
componentInfoObjects,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
component.attributes = attributes;
|
|
372
|
+
|
|
373
|
+
//recurse on children
|
|
374
|
+
if (component.children !== undefined) {
|
|
375
|
+
createAttributesFromProps(component.children, componentInfoObjects);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export function componentFromAttribute({ attrObj, value, originalComponentProps,
|
|
381
|
+
componentInfoObjects
|
|
382
|
+
}) {
|
|
383
|
+
if (typeof value !== "object") {
|
|
384
|
+
// typically this would mean value is a string.
|
|
385
|
+
// However, if had an attribute with no value, would get true.
|
|
386
|
+
// Also, when get addAttributeComponentsShadowingStateVariables,
|
|
387
|
+
// it is possible their values are not strings
|
|
388
|
+
value = { rawString: value.toString() }
|
|
389
|
+
} else if (value === null) {
|
|
390
|
+
// could get null from addAttributeComponentsShadowingStateVariables
|
|
391
|
+
value = { rawString: "" }
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (attrObj && attrObj.createComponentOfType) {
|
|
395
|
+
let newComponent;
|
|
396
|
+
let valueTrimLower = value.rawString.trim().toLowerCase();
|
|
397
|
+
|
|
398
|
+
if (valueTrimLower === "true" && attrObj.valueForTrue !== undefined) {
|
|
399
|
+
newComponent = {
|
|
400
|
+
componentType: attrObj.createComponentOfType,
|
|
401
|
+
state: { value: attrObj.valueForTrue }
|
|
402
|
+
};
|
|
403
|
+
} else if (valueTrimLower === "false" && attrObj.valueForFalse !== undefined) {
|
|
404
|
+
newComponent = {
|
|
405
|
+
componentType: attrObj.createComponentOfType,
|
|
406
|
+
state: { value: attrObj.valueForFalse }
|
|
407
|
+
};
|
|
408
|
+
} else if (componentInfoObjects.isInheritedComponentType({
|
|
409
|
+
inheritedComponentType: attrObj.createComponentOfType,
|
|
410
|
+
baseComponentType: "boolean",
|
|
411
|
+
}) && ["true", "false"].includes(valueTrimLower)) {
|
|
412
|
+
newComponent = {
|
|
413
|
+
componentType: attrObj.createComponentOfType,
|
|
414
|
+
state: { value: valueTrimLower === "true" }
|
|
415
|
+
};
|
|
416
|
+
} else {
|
|
417
|
+
let children = value.childrenForComponent;
|
|
418
|
+
if (children) {
|
|
419
|
+
children = JSON.parse(JSON.stringify(children));
|
|
420
|
+
} else {
|
|
421
|
+
children = [value.rawString]
|
|
422
|
+
}
|
|
423
|
+
newComponent = {
|
|
424
|
+
componentType: attrObj.createComponentOfType,
|
|
425
|
+
children
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
removeBlankStringChildren([newComponent], componentInfoObjects)
|
|
429
|
+
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (attrObj.attributesForCreatedComponent || attrObj.copyComponentAttributesForCreatedComponent) {
|
|
433
|
+
if (attrObj.attributesForCreatedComponent) {
|
|
434
|
+
newComponent.props = attrObj.attributesForCreatedComponent;
|
|
435
|
+
} else {
|
|
436
|
+
newComponent.props = {};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (attrObj.copyComponentAttributesForCreatedComponent) {
|
|
440
|
+
for (let attrName of attrObj.copyComponentAttributesForCreatedComponent) {
|
|
441
|
+
if (originalComponentProps[attrName]) {
|
|
442
|
+
newComponent.props[attrName] = JSON.parse(JSON.stringify(originalComponentProps[attrName]))
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
createAttributesFromProps([newComponent], componentInfoObjects)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return { component: newComponent };
|
|
452
|
+
} else if (attrObj && attrObj.createPrimitiveOfType) {
|
|
453
|
+
let newPrimitive;
|
|
454
|
+
if (attrObj.createPrimitiveOfType === "boolean") {
|
|
455
|
+
let valueTrimLower = value.rawString.trim().toLowerCase();
|
|
456
|
+
newPrimitive = valueTrimLower === "true";
|
|
457
|
+
} else if (attrObj.createPrimitiveOfType === "number") {
|
|
458
|
+
newPrimitive = Number(value.rawString);
|
|
459
|
+
} else if (attrObj.createPrimitiveOfType === "integer") {
|
|
460
|
+
newPrimitive = Math.round(Number(value.rawString));
|
|
461
|
+
} else if (attrObj.createPrimitiveOfType === "stringArray") {
|
|
462
|
+
newPrimitive = value.rawString.trim().split(/\s+/);
|
|
463
|
+
} else if (attrObj.createPrimitiveOfType === "numberArray") {
|
|
464
|
+
newPrimitive = value.rawString.split(/\s+/).map(Number);
|
|
465
|
+
} else {
|
|
466
|
+
// else assume string
|
|
467
|
+
newPrimitive = value.rawString;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (attrObj.validationFunction) {
|
|
471
|
+
newPrimitive = attrObj.validationFunction(newPrimitive);
|
|
472
|
+
}
|
|
473
|
+
return { primitive: newPrimitive };
|
|
474
|
+
} else {
|
|
475
|
+
if (!value.childrenForComponent) {
|
|
476
|
+
value.childrenForComponent = [value.rawString]
|
|
477
|
+
}
|
|
478
|
+
return value;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function findPreSugarIndsAndMarkFromSugar(components) {
|
|
483
|
+
let preSugarIndsFound = [];
|
|
484
|
+
for (let component of components) {
|
|
485
|
+
if (typeof component !== "object") {
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
if (component.preSugarInd !== undefined) {
|
|
489
|
+
preSugarIndsFound.push(component.preSugarInd)
|
|
490
|
+
} else {
|
|
491
|
+
if (!component.doenetAttributes) {
|
|
492
|
+
component.doenetAttributes = {};
|
|
493
|
+
}
|
|
494
|
+
component.doenetAttributes.createdFromSugar = true;
|
|
495
|
+
if (component.children) {
|
|
496
|
+
let inds = findPreSugarIndsAndMarkFromSugar(component.children);
|
|
497
|
+
preSugarIndsFound.push(...inds);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return preSugarIndsFound;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
export function applyMacros(serializedComponents, componentInfoObjects) {
|
|
506
|
+
|
|
507
|
+
for (let component of serializedComponents) {
|
|
508
|
+
if (component.children) {
|
|
509
|
+
applyMacros(component.children, componentInfoObjects);
|
|
510
|
+
}
|
|
511
|
+
if (component.attributes) {
|
|
512
|
+
for (let attrName in component.attributes) {
|
|
513
|
+
let attribute = component.attributes[attrName];
|
|
514
|
+
if (attribute.component) {
|
|
515
|
+
let comp = attribute.component;
|
|
516
|
+
if (comp.children) {
|
|
517
|
+
applyMacros(comp.children, componentInfoObjects);
|
|
518
|
+
}
|
|
519
|
+
} else if (attribute.childrenForComponent) {
|
|
520
|
+
applyMacros(attribute.childrenForComponent, componentInfoObjects);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
substituteMacros(serializedComponents, componentInfoObjects);
|
|
527
|
+
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function substituteMacros(serializedComponents, componentInfoObjects) {
|
|
531
|
+
|
|
532
|
+
for (let componentInd = 0; componentInd < serializedComponents.length; componentInd++) {
|
|
533
|
+
let component = serializedComponents[componentInd];
|
|
534
|
+
|
|
535
|
+
if (typeof component === "string") {
|
|
536
|
+
|
|
537
|
+
let startInd = 0;
|
|
538
|
+
while (startInd < component.length) {
|
|
539
|
+
|
|
540
|
+
let str = component;
|
|
541
|
+
let result = findFirstFullMacroInString(str.slice(startInd));
|
|
542
|
+
|
|
543
|
+
if (!result.success) {
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
let firstIndMatched = result.firstIndMatched + startInd;
|
|
548
|
+
let matchLength = result.matchLength;
|
|
549
|
+
let nDollarSigns = result.nDollarSigns;
|
|
550
|
+
|
|
551
|
+
let componentsFromMacro;
|
|
552
|
+
|
|
553
|
+
if (result.additionalAttributes) {
|
|
554
|
+
let newDoenetML = `<copy target="${result.targetName}" ${result.additionalAttributes} />`;
|
|
555
|
+
|
|
556
|
+
let newComponents;
|
|
557
|
+
|
|
558
|
+
try {
|
|
559
|
+
newComponents = parseAndCompile(newDoenetML);
|
|
560
|
+
} catch (e) {
|
|
561
|
+
let strWithError = str.slice(firstIndMatched, firstIndMatched + matchLength);
|
|
562
|
+
let startInd = firstIndMatched;
|
|
563
|
+
if (componentInd > 0 && serializedComponents[componentInd - 1].range) {
|
|
564
|
+
let previousRange = serializedComponents[componentInd - 1].range;
|
|
565
|
+
if (previousRange.closeEnd) {
|
|
566
|
+
startInd += previousRange.closeEnd;
|
|
567
|
+
} else if (previousRange.selfCloseEnd) {
|
|
568
|
+
startInd += previousRange.selfCloseBegin;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
throw Error(`Error in macro at indices ${startInd}-${startInd + matchLength}. Found: ${strWithError}`)
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
createAttributesFromProps(newComponents, componentInfoObjects);
|
|
576
|
+
markCreatedFromMacro(newComponents);
|
|
577
|
+
|
|
578
|
+
// recurse in case there were more macros in the additionalAttributes
|
|
579
|
+
applyMacros(newComponents, componentInfoObjects)
|
|
580
|
+
|
|
581
|
+
componentsFromMacro = newComponents;
|
|
582
|
+
|
|
583
|
+
} else {
|
|
584
|
+
// no additional attributes, so no need to reparse
|
|
585
|
+
|
|
586
|
+
let doenetAttributes = { target: result.targetName, createdFromMacro: true };
|
|
587
|
+
|
|
588
|
+
// check here if additionalAttributes is undefined
|
|
589
|
+
// (even though know it is falsy)
|
|
590
|
+
// so that an empty string removes the default isPlainMacro
|
|
591
|
+
if (result.additionalAttributes === undefined) {
|
|
592
|
+
doenetAttributes.isPlainMacro = true;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
componentsFromMacro = [{
|
|
596
|
+
componentType: "copy",
|
|
597
|
+
doenetAttributes
|
|
598
|
+
}];
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
let nComponentsToRemove = 1;
|
|
602
|
+
let stringToAddAtEnd = str.substring(firstIndMatched + matchLength);
|
|
603
|
+
|
|
604
|
+
if (nDollarSigns === 2) {
|
|
605
|
+
|
|
606
|
+
let matchOpeningParens = str.slice(firstIndMatched + matchLength).match(/^\s*\(/);
|
|
607
|
+
|
|
608
|
+
if (!matchOpeningParens) {
|
|
609
|
+
// if don't match function,
|
|
610
|
+
// don't replace double dollar sign macro
|
|
611
|
+
startInd = firstIndMatched + 2;
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
let matchLengthWithOpeningParens = matchLength + matchOpeningParens[0].length;
|
|
616
|
+
|
|
617
|
+
// look for a closing parenthesis
|
|
618
|
+
|
|
619
|
+
// get array of the component with the rest of this string
|
|
620
|
+
// plus the rest of the components in the array
|
|
621
|
+
let remainingComponents = [];
|
|
622
|
+
let includeFirstInRemaining = false;
|
|
623
|
+
|
|
624
|
+
if (str.length > firstIndMatched + matchLengthWithOpeningParens) {
|
|
625
|
+
includeFirstInRemaining = true;
|
|
626
|
+
remainingComponents.push(str.substring(firstIndMatched + matchLengthWithOpeningParens))
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
remainingComponents.push(...serializedComponents.slice(componentInd + 1));
|
|
630
|
+
|
|
631
|
+
let evaluateResult = createEvaluateIfFindMatchedClosingParens({
|
|
632
|
+
componentsFromMacro,
|
|
633
|
+
remainingComponents,
|
|
634
|
+
includeFirstInRemaining,
|
|
635
|
+
componentInfoObjects
|
|
636
|
+
})
|
|
637
|
+
|
|
638
|
+
if (!evaluateResult.success) {
|
|
639
|
+
// if couldn't create evaluate,
|
|
640
|
+
// don't replace double dollar macro
|
|
641
|
+
startInd = firstIndMatched + 2;
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
componentsFromMacro = evaluateResult.componentsFromMacro;
|
|
646
|
+
|
|
647
|
+
nComponentsToRemove = evaluateResult.lastComponentIndMatched + 1;
|
|
648
|
+
if (!includeFirstInRemaining) {
|
|
649
|
+
nComponentsToRemove++;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// leftover string already included in componentsFromMacro
|
|
653
|
+
stringToAddAtEnd = "";
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
let replacements = [];
|
|
659
|
+
|
|
660
|
+
// the string before the function name
|
|
661
|
+
if (firstIndMatched > 0) {
|
|
662
|
+
replacements.push(str.substring(0, firstIndMatched))
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
replacements.push(...componentsFromMacro);
|
|
666
|
+
|
|
667
|
+
if (stringToAddAtEnd.length > 0) {
|
|
668
|
+
replacements.push(stringToAddAtEnd)
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// splice new replacements into serializedComponents
|
|
672
|
+
serializedComponents.splice(componentInd, nComponentsToRemove, ...replacements)
|
|
673
|
+
|
|
674
|
+
if (firstIndMatched > 0) {
|
|
675
|
+
// increment componentInd because we now have to skip
|
|
676
|
+
// over two components
|
|
677
|
+
// (the component made from the beginning of the string
|
|
678
|
+
// as well as the component made from the macro)
|
|
679
|
+
componentInd++;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// break out of loop processing string,
|
|
683
|
+
// as finished current one
|
|
684
|
+
// (possibly breaking it into pieces, so will address remainder as other component)
|
|
685
|
+
|
|
686
|
+
break;
|
|
687
|
+
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
function findFirstFullMacroInString(str) {
|
|
697
|
+
|
|
698
|
+
// One or two $ follwed by either
|
|
699
|
+
// - a word (starting with a letter), capturing word as third group, or
|
|
700
|
+
// - an identifier in parentheses, capturing identifier as fourth group,
|
|
701
|
+
// where the closing parenthesis could be replaced by an open brace,
|
|
702
|
+
// capturing the open brace or closing parens as fifth group
|
|
703
|
+
let reForBeginning = /(\$\$?)(([a-zA-Z_]\w*\b)|\(([a-zA-Z0-9_:.\/\-]+)\s*(\)|{))/;
|
|
704
|
+
|
|
705
|
+
let offset = 0;
|
|
706
|
+
let match;
|
|
707
|
+
|
|
708
|
+
// since Safari doesn't allow a negative lookbehind to make sure
|
|
709
|
+
// that match isn't preceeded by third dollar sign,
|
|
710
|
+
// we instead just skip any matches that include a third dollar sign
|
|
711
|
+
while (true) {
|
|
712
|
+
// look for a function macro
|
|
713
|
+
match = str.substring(offset).match(reForBeginning);
|
|
714
|
+
|
|
715
|
+
if (!match) {
|
|
716
|
+
return { success: false };
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
if (match.index === 0 || str[offset + match.index - 1] !== "$") {
|
|
720
|
+
break;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// found a third dollar sign preceeding match
|
|
724
|
+
// so skip this match and look for another match later in the string
|
|
725
|
+
offset += match.index + match[0].length;
|
|
726
|
+
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
let firstIndMatched = match.index + offset;
|
|
731
|
+
let matchLength = match[0].length;
|
|
732
|
+
let nDollarSigns = match[1].length;
|
|
733
|
+
|
|
734
|
+
if (match[3]) {
|
|
735
|
+
// found word outside parans
|
|
736
|
+
return {
|
|
737
|
+
success: true,
|
|
738
|
+
firstIndMatched,
|
|
739
|
+
matchLength,
|
|
740
|
+
targetName: match[3],
|
|
741
|
+
nDollarSigns
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// found identifier in parens
|
|
746
|
+
let targetName = match[4];
|
|
747
|
+
|
|
748
|
+
if (match[5] === ")") {
|
|
749
|
+
// found closing parens
|
|
750
|
+
return {
|
|
751
|
+
success: true,
|
|
752
|
+
firstIndMatched,
|
|
753
|
+
matchLength,
|
|
754
|
+
targetName,
|
|
755
|
+
nDollarSigns,
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// have opening brace rather than closing parens
|
|
760
|
+
// need to find matching closing brace and parens
|
|
761
|
+
|
|
762
|
+
let strAfterMatch = str.substring(firstIndMatched + matchLength);
|
|
763
|
+
|
|
764
|
+
let resultForClosingBrace = findFirstUnmatchedClosingBraceParen(strAfterMatch);
|
|
765
|
+
|
|
766
|
+
if (resultForClosingBrace.success) {
|
|
767
|
+
// found matching closing brace and parens
|
|
768
|
+
// return string enclosed by braces as additional attributes
|
|
769
|
+
return {
|
|
770
|
+
success: true,
|
|
771
|
+
firstIndMatched,
|
|
772
|
+
matchLength: matchLength + resultForClosingBrace.parenInd + 1,
|
|
773
|
+
targetName,
|
|
774
|
+
nDollarSigns,
|
|
775
|
+
additionalAttributes: strAfterMatch.substring(0, resultForClosingBrace.braceInd)
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
} else {
|
|
779
|
+
// the beginning found didn't end up matching the pattern,
|
|
780
|
+
// so we ignore that match and see if there is another one in the rest of the string
|
|
781
|
+
let findAnotherResult = findFirstFullMacroInString(strAfterMatch);
|
|
782
|
+
|
|
783
|
+
if (!findAnotherResult.success) {
|
|
784
|
+
// the rest of the string didn't have a match, so no match in the original string
|
|
785
|
+
return { success: false }
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// return match found in rest of string, with indices adjusted to be for original string
|
|
789
|
+
return {
|
|
790
|
+
success: true,
|
|
791
|
+
firstIndMatched: firstIndMatched + matchLength + findAnotherResult.firstIndMatched,
|
|
792
|
+
matchLength: findAnotherResult.matchLength,
|
|
793
|
+
targetName: findAnotherResult.targetName,
|
|
794
|
+
nDollarSigns: findAnotherResult.nDollarSigns,
|
|
795
|
+
additionalAttributes: findAnotherResult.additionalAttributes
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
function findFirstUnmatchedClosingBraceParen(strAfterMatch) {
|
|
803
|
+
let nBraces = 0;
|
|
804
|
+
|
|
805
|
+
for (let ind = 0; ind < strAfterMatch.length; ind++) {
|
|
806
|
+
let char = strAfterMatch[ind];
|
|
807
|
+
if (char === "}") {
|
|
808
|
+
if (nBraces === 0) {
|
|
809
|
+
// found unmatched closing brace
|
|
810
|
+
// now need next non whitespace character to be closing parenthesis
|
|
811
|
+
|
|
812
|
+
if (strAfterMatch.substring(ind + 1).trim()[0] === ")") {
|
|
813
|
+
let parenInd = strAfterMatch.substring(ind + 1).indexOf(")") + ind + 1;
|
|
814
|
+
return { success: true, braceInd: ind, parenInd }
|
|
815
|
+
}
|
|
816
|
+
// found closing brace, but not followed by closing parens
|
|
817
|
+
return { success: false }
|
|
818
|
+
|
|
819
|
+
}
|
|
820
|
+
nBraces--;
|
|
821
|
+
} else if (char === "{") {
|
|
822
|
+
nBraces++;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
return { success: false };
|
|
827
|
+
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function markCreatedFromMacro(serializedComponents) {
|
|
831
|
+
for (let serializedComponent of serializedComponents) {
|
|
832
|
+
if (!serializedComponent.doenetAttributes) {
|
|
833
|
+
serializedComponent.doenetAttributes = {};
|
|
834
|
+
}
|
|
835
|
+
serializedComponent.doenetAttributes.createdFromMacro = true;
|
|
836
|
+
|
|
837
|
+
if (serializedComponent.children) {
|
|
838
|
+
markCreatedFromMacro(serializedComponent.children);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
function createEvaluateIfFindMatchedClosingParens({
|
|
844
|
+
componentsFromMacro, remainingComponents, includeFirstInRemaining, componentInfoObjects
|
|
845
|
+
}) {
|
|
846
|
+
|
|
847
|
+
let result = findFirstUnmatchedClosingParens(remainingComponents);
|
|
848
|
+
|
|
849
|
+
if (!result.success) {
|
|
850
|
+
return result;
|
|
851
|
+
}
|
|
852
|
+
// found unmatched closing parenthesis, so is the one
|
|
853
|
+
// matching the opening parenthesis
|
|
854
|
+
|
|
855
|
+
let lastComponentInd = result.componentInd;
|
|
856
|
+
|
|
857
|
+
remainingComponents = remainingComponents.slice(0, lastComponentInd + 1);
|
|
858
|
+
|
|
859
|
+
let lastComponentOfFunction = remainingComponents[lastComponentInd];
|
|
860
|
+
|
|
861
|
+
let stringAfterFunction = "";
|
|
862
|
+
|
|
863
|
+
// if have text after closing parenthesis
|
|
864
|
+
// save in stringAfterFunction
|
|
865
|
+
if (result.charInd + 1 < lastComponentOfFunction.length) {
|
|
866
|
+
stringAfterFunction = lastComponentOfFunction.substring(result.charInd + 1);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// remove closing parenthesis and any subsequent text
|
|
870
|
+
// from the last component
|
|
871
|
+
if (result.charInd > 0) {
|
|
872
|
+
remainingComponents[lastComponentInd]
|
|
873
|
+
= lastComponentOfFunction.substring(0, result.charInd)
|
|
874
|
+
} else {
|
|
875
|
+
// remove this component altogether as there is nothing left
|
|
876
|
+
remainingComponents = remainingComponents.slice(0, lastComponentInd);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
let breakResults = breakEmbeddedStringByCommas({ childrenList: remainingComponents });
|
|
881
|
+
|
|
882
|
+
// recurse on pieces
|
|
883
|
+
breakResults.pieces.forEach(x => applyMacros(x, componentInfoObjects));
|
|
884
|
+
|
|
885
|
+
let inputArray = breakResults.pieces.map(x => {
|
|
886
|
+
if (x.length === 1 && typeof x[0] !== "string") {
|
|
887
|
+
return x[0]
|
|
888
|
+
} else {
|
|
889
|
+
return {
|
|
890
|
+
componentType: "math",
|
|
891
|
+
doenetAttributes: { createdFromMacro: true },
|
|
892
|
+
children: x
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
})
|
|
896
|
+
|
|
897
|
+
let evaluateComponent = {
|
|
898
|
+
componentType: "evaluate",
|
|
899
|
+
doenetAttributes: { createdFromMacro: true },
|
|
900
|
+
attributes: {
|
|
901
|
+
function: {
|
|
902
|
+
component: {
|
|
903
|
+
componentType: "function",
|
|
904
|
+
doenetAttributes: { createdFromMacro: true },
|
|
905
|
+
children: componentsFromMacro
|
|
906
|
+
}
|
|
907
|
+
},
|
|
908
|
+
input: {
|
|
909
|
+
component: {
|
|
910
|
+
componentType: "mathList",
|
|
911
|
+
doenetAttributes: { createdFromMacro: true },
|
|
912
|
+
children: inputArray
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
let replacements = [evaluateComponent];
|
|
919
|
+
|
|
920
|
+
// if have text after function
|
|
921
|
+
// include string component at end containing that text
|
|
922
|
+
if (stringAfterFunction.length > 0) {
|
|
923
|
+
replacements.push(stringAfterFunction)
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
return {
|
|
927
|
+
success: true,
|
|
928
|
+
componentsFromMacro: replacements,
|
|
929
|
+
lastComponentIndMatched: lastComponentInd,
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
function findFirstUnmatchedClosingParens(components) {
|
|
936
|
+
|
|
937
|
+
let Nparens = 0;
|
|
938
|
+
|
|
939
|
+
for (let [componentInd, component] of components.entries()) {
|
|
940
|
+
if (typeof component === "string") {
|
|
941
|
+
let s = component;
|
|
942
|
+
|
|
943
|
+
for (let charInd = 0; charInd < s.length; charInd++) {
|
|
944
|
+
let char = s[charInd];
|
|
945
|
+
if (char === "(") {
|
|
946
|
+
Nparens++;
|
|
947
|
+
} else if (char === ")") {
|
|
948
|
+
if (Nparens === 0) {
|
|
949
|
+
// parens didn't match
|
|
950
|
+
return {
|
|
951
|
+
success: true,
|
|
952
|
+
componentInd,
|
|
953
|
+
charInd
|
|
954
|
+
}
|
|
955
|
+
} else {
|
|
956
|
+
Nparens--;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// never found a closing parenthesis that wasn't matched
|
|
965
|
+
return { success: false }
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function decodeXMLEntities(serializedComponents) {
|
|
969
|
+
|
|
970
|
+
function replaceEntities(s) {
|
|
971
|
+
return s
|
|
972
|
+
.replace(/'/g, "'")
|
|
973
|
+
.replace(/"/g, '"')
|
|
974
|
+
.replace(/>/g, '>')
|
|
975
|
+
.replace(/</g, '<')
|
|
976
|
+
.replace(/$/g, '$')
|
|
977
|
+
.replace(/&/g, '&');
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
for (let [ind, serializedComponent] of serializedComponents.entries()) {
|
|
981
|
+
if (typeof serializedComponent === "string") {
|
|
982
|
+
serializedComponents[ind] = replaceEntities(serializedComponent);
|
|
983
|
+
} else {
|
|
984
|
+
|
|
985
|
+
if (serializedComponent.children) {
|
|
986
|
+
decodeXMLEntities(serializedComponent.children)
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
if (serializedComponent.attributes) {
|
|
990
|
+
for (let attrName in serializedComponent.attributes) {
|
|
991
|
+
let attribute = serializedComponent.attributes[attrName];
|
|
992
|
+
|
|
993
|
+
if (attribute.component) {
|
|
994
|
+
decodeXMLEntities([attribute.component])
|
|
995
|
+
} else if (attribute.primitive) {
|
|
996
|
+
if (typeof attribute.primitive === "string") {
|
|
997
|
+
attribute.primitive = replaceEntities(attribute.primitive);
|
|
998
|
+
}
|
|
999
|
+
} else {
|
|
1000
|
+
if (attribute.childrenForComponent) {
|
|
1001
|
+
decodeXMLEntities(attribute.childrenForComponent);
|
|
1002
|
+
}
|
|
1003
|
+
if (attribute.rawString) {
|
|
1004
|
+
attribute.rawString = replaceEntities(attribute.rawString);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
export function applySugar({ serializedComponents, parentParametersFromSugar = {},
|
|
1014
|
+
parentAttributes = {},
|
|
1015
|
+
componentInfoObjects,
|
|
1016
|
+
parentUniqueId = "",
|
|
1017
|
+
isAttributeComponent = false,
|
|
1018
|
+
}) {
|
|
1019
|
+
|
|
1020
|
+
for (let [componentInd, component] of serializedComponents.entries()) {
|
|
1021
|
+
if (typeof component !== "object") {
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
let componentType = component.componentType;
|
|
1026
|
+
let componentClass = componentInfoObjects.allComponentClasses[componentType];
|
|
1027
|
+
if (!componentClass) {
|
|
1028
|
+
throw Error(`Unrecognized component type ${componentType}`)
|
|
1029
|
+
}
|
|
1030
|
+
let uniqueId = parentUniqueId + '|' + componentType + componentInd;
|
|
1031
|
+
|
|
1032
|
+
let componentAttributes = {};
|
|
1033
|
+
// add primitive attributes to componentAttributes
|
|
1034
|
+
for (let attrName in component.attributes) {
|
|
1035
|
+
let attribute = component.attributes[attrName];
|
|
1036
|
+
if (attribute.primitive !== undefined) {
|
|
1037
|
+
componentAttributes[attrName] = attribute.primitive;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
if (component.children) {
|
|
1042
|
+
|
|
1043
|
+
let newParentParametersFromSugar = {};
|
|
1044
|
+
|
|
1045
|
+
if (!component.skipSugar) {
|
|
1046
|
+
|
|
1047
|
+
for (let [sugarInd, sugarInstruction] of componentClass.returnSugarInstructions().entries()) {
|
|
1048
|
+
|
|
1049
|
+
// if (component.children.length === 0) {
|
|
1050
|
+
// break;
|
|
1051
|
+
// }
|
|
1052
|
+
|
|
1053
|
+
let childTypes = component.children
|
|
1054
|
+
.map(x => typeof x === "string" ? "s" : "n")
|
|
1055
|
+
.join("");
|
|
1056
|
+
|
|
1057
|
+
if (sugarInstruction.childrenRegex) {
|
|
1058
|
+
let match = childTypes.match(sugarInstruction.childrenRegex);
|
|
1059
|
+
|
|
1060
|
+
if (!match || match[0].length !== component.children.length) {
|
|
1061
|
+
// sugar pattern didn't match all children
|
|
1062
|
+
// so don't apply sugar
|
|
1063
|
+
|
|
1064
|
+
continue;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
let matchedChildren = deepClone(component.children);
|
|
1071
|
+
|
|
1072
|
+
let nNonStrings = 0;
|
|
1073
|
+
for (let child of matchedChildren) {
|
|
1074
|
+
if (typeof child !== "string") {
|
|
1075
|
+
child.preSugarInd = nNonStrings;
|
|
1076
|
+
nNonStrings++;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
let createdFromMacro = false;
|
|
1081
|
+
if (component.doenetAttributes && component.doenetAttributes.createdFromMacro) {
|
|
1082
|
+
createdFromMacro = true;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
let sugarResults = sugarInstruction.replacementFunction({
|
|
1086
|
+
matchedChildren,
|
|
1087
|
+
parentParametersFromSugar,
|
|
1088
|
+
parentAttributes,
|
|
1089
|
+
componentAttributes,
|
|
1090
|
+
uniqueId: uniqueId + '|sugar' + sugarInd,
|
|
1091
|
+
componentInfoObjects,
|
|
1092
|
+
isAttributeComponent,
|
|
1093
|
+
createdFromMacro
|
|
1094
|
+
});
|
|
1095
|
+
|
|
1096
|
+
// console.log("sugarResults")
|
|
1097
|
+
// console.log(sugarResults)
|
|
1098
|
+
|
|
1099
|
+
if (sugarResults.success) {
|
|
1100
|
+
|
|
1101
|
+
let newChildren = sugarResults.newChildren;
|
|
1102
|
+
let newAttributes = sugarResults.newAttributes;
|
|
1103
|
+
|
|
1104
|
+
let preSugarIndsFoundInChildren = [], preSugarIndsFoundInAttributes = [];
|
|
1105
|
+
|
|
1106
|
+
if (newChildren) {
|
|
1107
|
+
preSugarIndsFoundInChildren = findPreSugarIndsAndMarkFromSugar(newChildren);
|
|
1108
|
+
}
|
|
1109
|
+
if (newAttributes) {
|
|
1110
|
+
for (let attrName in newAttributes) {
|
|
1111
|
+
let comp = newAttributes[attrName].component;
|
|
1112
|
+
if (comp) {
|
|
1113
|
+
preSugarIndsFoundInAttributes.push(...findPreSugarIndsAndMarkFromSugar(comp.children));
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
let preSugarIndsFound = [...preSugarIndsFoundInChildren, ...preSugarIndsFoundInAttributes];
|
|
1119
|
+
|
|
1120
|
+
if (preSugarIndsFound.length !== nNonStrings ||
|
|
1121
|
+
!preSugarIndsFound.sort((a, b) => a - b).every((v, i) => v === i)
|
|
1122
|
+
) {
|
|
1123
|
+
throw Error(`Invalid sugar for ${componentType} as didn't return set of original components`)
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
if (preSugarIndsFoundInChildren.length > 0) {
|
|
1127
|
+
let sortedList = [...preSugarIndsFoundInChildren].sort((a, b) => a - b);
|
|
1128
|
+
if (!sortedList.every((v, i) => v === preSugarIndsFoundInChildren[i])) {
|
|
1129
|
+
throw Error(`Invalid sugar for ${componentType} as didn't return original components in order`)
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
if (sugarResults.parametersForChildrenSugar) {
|
|
1135
|
+
Object.assign(newParentParametersFromSugar, sugarResults.parametersForChildrenSugar)
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
if (newChildren) {
|
|
1139
|
+
component.children = newChildren;
|
|
1140
|
+
} else {
|
|
1141
|
+
component.children = [];
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
if (newAttributes) {
|
|
1145
|
+
if (!component.attributes) {
|
|
1146
|
+
component.attributes = {}
|
|
1147
|
+
}
|
|
1148
|
+
Object.assign(component.attributes, newAttributes)
|
|
1149
|
+
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
if (componentClass.removeBlankStringChildrenPostSugar) {
|
|
1158
|
+
component.children = component.children.filter(x => typeof x !== "string" || /\S/.test(x))
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// Note: don't pass in isAttributeComponent
|
|
1162
|
+
// as that flag should be set just for the top level attribute component
|
|
1163
|
+
|
|
1164
|
+
applySugar({
|
|
1165
|
+
serializedComponents: component.children,
|
|
1166
|
+
parentParametersFromSugar: newParentParametersFromSugar,
|
|
1167
|
+
parentAttributes: componentAttributes,
|
|
1168
|
+
componentInfoObjects,
|
|
1169
|
+
parentUniqueId: uniqueId,
|
|
1170
|
+
})
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
if (component.attributes) {
|
|
1174
|
+
for (let attrName in component.attributes) {
|
|
1175
|
+
let attribute = component.attributes[attrName];
|
|
1176
|
+
|
|
1177
|
+
if (attribute.component) {
|
|
1178
|
+
|
|
1179
|
+
applySugar({
|
|
1180
|
+
serializedComponents: [attribute.component],
|
|
1181
|
+
parentAttributes: componentAttributes,
|
|
1182
|
+
componentInfoObjects,
|
|
1183
|
+
parentUniqueId: uniqueId,
|
|
1184
|
+
isAttributeComponent: true,
|
|
1185
|
+
})
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
function breakStringInPiecesBySpacesOrParens(string) {
|
|
1193
|
+
|
|
1194
|
+
if (typeof string !== "string") {
|
|
1195
|
+
return { success: false }
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
let Nparens = 0;
|
|
1199
|
+
let pieces = [];
|
|
1200
|
+
|
|
1201
|
+
string = string.trim();
|
|
1202
|
+
let beginInd = 0;
|
|
1203
|
+
|
|
1204
|
+
for (let ind = 0; ind < string.length; ind++) {
|
|
1205
|
+
let char = string[ind];
|
|
1206
|
+
if (char === "(") {
|
|
1207
|
+
if (Nparens === 0) {
|
|
1208
|
+
// beginning new parens piece
|
|
1209
|
+
// what have so far is a new piece
|
|
1210
|
+
let newPiece = string.substring(beginInd, ind).trim();
|
|
1211
|
+
if (newPiece.length > 0) {
|
|
1212
|
+
pieces.push(newPiece);
|
|
1213
|
+
}
|
|
1214
|
+
beginInd = ind;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
Nparens++;
|
|
1218
|
+
} else if (char === ")") {
|
|
1219
|
+
if (Nparens === 0) {
|
|
1220
|
+
// parens didn't match, so return failure
|
|
1221
|
+
return { success: false };
|
|
1222
|
+
}
|
|
1223
|
+
if (Nparens === 1) {
|
|
1224
|
+
// found end of piece in parens
|
|
1225
|
+
let newPiece = string.substring(beginInd + 1, ind).trim();
|
|
1226
|
+
if (newPiece.length > 0) {
|
|
1227
|
+
// try to break further
|
|
1228
|
+
let result = breakStringInPiecesBySpacesOrParens(newPiece);
|
|
1229
|
+
if (result.success === true) {
|
|
1230
|
+
pieces.push(result.pieces);
|
|
1231
|
+
} else {
|
|
1232
|
+
pieces.push(newPiece);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
beginInd = ind + 1;
|
|
1236
|
+
}
|
|
1237
|
+
Nparens--
|
|
1238
|
+
} else if (Nparens === 0 && char.match(/\s/)) {
|
|
1239
|
+
// not in parens and found a space so potentially have a new piece
|
|
1240
|
+
let newPiece = string.substring(beginInd, ind).trim();
|
|
1241
|
+
if (newPiece.length > 0) {
|
|
1242
|
+
pieces.push(newPiece);
|
|
1243
|
+
}
|
|
1244
|
+
beginInd = ind;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// parens didn't match, so return failure
|
|
1250
|
+
if (Nparens !== 0) {
|
|
1251
|
+
return { success: false };
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
let newPiece = string.substring(beginInd, string.length).trim();
|
|
1255
|
+
if (newPiece.length > 0) {
|
|
1256
|
+
pieces.push(newPiece);
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
return {
|
|
1260
|
+
success: true,
|
|
1261
|
+
pieces: pieces,
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
export function createComponentNames({ serializedComponents, namespaceStack = [],
|
|
1267
|
+
componentInfoObjects,
|
|
1268
|
+
parentDoenetAttributes = {},
|
|
1269
|
+
parentName,
|
|
1270
|
+
useOriginalNames = false,
|
|
1271
|
+
doenetAttributesByTargetComponentName,
|
|
1272
|
+
indOffset = 0,
|
|
1273
|
+
createNameContext = "",
|
|
1274
|
+
}) {
|
|
1275
|
+
|
|
1276
|
+
if (namespaceStack.length === 0) {
|
|
1277
|
+
namespaceStack.push({ namespace: '', componentCounts: {}, namesUsed: {} });
|
|
1278
|
+
}
|
|
1279
|
+
let level = namespaceStack.length - 1;
|
|
1280
|
+
|
|
1281
|
+
// console.log("createComponentNames " + level);
|
|
1282
|
+
// console.log(serializedComponents);
|
|
1283
|
+
// console.log(namespaceStack);
|
|
1284
|
+
|
|
1285
|
+
let currentNamespace = namespaceStack[level];
|
|
1286
|
+
|
|
1287
|
+
for (let [componentInd, serializedComponent] of serializedComponents.entries()) {
|
|
1288
|
+
if (typeof serializedComponent !== "object") {
|
|
1289
|
+
continue;
|
|
1290
|
+
}
|
|
1291
|
+
let componentType = serializedComponent.componentType;
|
|
1292
|
+
let componentClass = componentInfoObjects.allComponentClasses[componentType];
|
|
1293
|
+
|
|
1294
|
+
let doenetAttributes = serializedComponent.doenetAttributes;
|
|
1295
|
+
if (doenetAttributes === undefined) {
|
|
1296
|
+
doenetAttributes = serializedComponent.doenetAttributes = {};
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
let attributes = serializedComponent.attributes;
|
|
1300
|
+
if (!attributes) {
|
|
1301
|
+
attributes = serializedComponent.attributes = {};
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
|
|
1305
|
+
let prescribedName = doenetAttributes.prescribedName;
|
|
1306
|
+
let assignNames = doenetAttributes.assignNames;
|
|
1307
|
+
let target = doenetAttributes.target;
|
|
1308
|
+
// let propName = doenetAttributes.propName;
|
|
1309
|
+
// let type = doenetAttributes.type;
|
|
1310
|
+
// let alias = doenetAttributes.alias;
|
|
1311
|
+
// let indexAlias = doenetAttributes.indexAlias;
|
|
1312
|
+
|
|
1313
|
+
let mustCreateUniqueName =
|
|
1314
|
+
doenetAttributes.isAttributeChild
|
|
1315
|
+
|| doenetAttributes.createdFromSugar
|
|
1316
|
+
|| doenetAttributes.createdFromMacro
|
|
1317
|
+
|| doenetAttributes.createUniqueName;
|
|
1318
|
+
|
|
1319
|
+
|
|
1320
|
+
let newNamespace;
|
|
1321
|
+
if (attributes.newNamespace?.primitive ||
|
|
1322
|
+
(useOriginalNames && serializedComponent.originalAttributes
|
|
1323
|
+
&& serializedComponent.originalAttributes.newNamespace)
|
|
1324
|
+
) {
|
|
1325
|
+
newNamespace = true;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
let prescribedNameFromDoenetAttributes = prescribedName !== undefined;
|
|
1329
|
+
|
|
1330
|
+
let props = serializedComponent.props;
|
|
1331
|
+
if (props === undefined) {
|
|
1332
|
+
props = serializedComponent.props = {};
|
|
1333
|
+
} else {
|
|
1334
|
+
// look for a attribute that matches an prop
|
|
1335
|
+
// but case insensitive
|
|
1336
|
+
for (let key in props) {
|
|
1337
|
+
let lowercaseKey = key.toLowerCase();
|
|
1338
|
+
if (lowercaseKey === "name") {
|
|
1339
|
+
if (prescribedName === undefined) {
|
|
1340
|
+
prescribedName = props[key];
|
|
1341
|
+
delete props[key];
|
|
1342
|
+
} else {
|
|
1343
|
+
throw Error(`Cannot define name twice. Found in component of type ${serializedComponent.componentType}${indexRangeString(serializedComponent)}`)
|
|
1344
|
+
}
|
|
1345
|
+
} else if (lowercaseKey === "assignnames") {
|
|
1346
|
+
if (assignNames === undefined) {
|
|
1347
|
+
let result = breakStringInPiecesBySpacesOrParens(props[key]);
|
|
1348
|
+
if (result.success) {
|
|
1349
|
+
assignNames = result.pieces;
|
|
1350
|
+
} else {
|
|
1351
|
+
throw Error(`Invalid format for assignnames. Found in component of type ${serializedComponent.componentType}${indexRangeString(serializedComponent)}`)
|
|
1352
|
+
}
|
|
1353
|
+
delete props[key];
|
|
1354
|
+
} else {
|
|
1355
|
+
throw Error(`Cannot define assignNames twice for a component. Found in component of type ${serializedComponent.componentType}${indexRangeString(serializedComponent)}`)
|
|
1356
|
+
}
|
|
1357
|
+
} else if (lowercaseKey === "target") {
|
|
1358
|
+
if (target === undefined) {
|
|
1359
|
+
if (typeof props[key] !== "string") {
|
|
1360
|
+
throw Error(`Must specify value for target. Found in component of type ${serializedComponent.componentType}${indexRangeString(serializedComponent)}`)
|
|
1361
|
+
}
|
|
1362
|
+
target = props[key].trim();
|
|
1363
|
+
delete props[key];
|
|
1364
|
+
} else {
|
|
1365
|
+
throw Error(`Cannot define target twice for a component. Found in component of type ${serializedComponent.componentType}${indexRangeString(serializedComponent)}`)
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
|
|
1372
|
+
if (prescribedName) {
|
|
1373
|
+
|
|
1374
|
+
if (!prescribedNameFromDoenetAttributes && !doenetAttributes.createdFromSugar) {
|
|
1375
|
+
|
|
1376
|
+
if (!(/[a-zA-Z]/.test(prescribedName.substring(0, 1)))) {
|
|
1377
|
+
throw Error(`Invalid component name: ${prescribedName}. Component name must begin with a letter. Found in component of type ${serializedComponent.componentType}${indexRangeString(serializedComponent)}`)
|
|
1378
|
+
}
|
|
1379
|
+
if (!(/^[a-zA-Z0-9_\-]+$/.test(prescribedName))) {
|
|
1380
|
+
throw Error(`Invalid component name: ${prescribedName}. Component name can contain only letters, numbers, hyphens, and underscores. Found in component of type ${serializedComponent.componentType}${indexRangeString(serializedComponent)}`)
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// name was specified
|
|
1385
|
+
// put it into doenetAttributes
|
|
1386
|
+
doenetAttributes.prescribedName = prescribedName;
|
|
1387
|
+
|
|
1388
|
+
} else if (mustCreateUniqueName) {
|
|
1389
|
+
let longNameId = parentName + "|createUniqueName|";
|
|
1390
|
+
|
|
1391
|
+
if (serializedComponent.downstreamDependencies) {
|
|
1392
|
+
longNameId += JSON.stringify(serializedComponent.downstreamDependencies);
|
|
1393
|
+
} else {
|
|
1394
|
+
longNameId += componentInd + "|" + indOffset + "|" + createNameContext;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
prescribedName = createUniqueName(componentType.toLowerCase(), longNameId);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
if (!assignNames && useOriginalNames
|
|
1401
|
+
&& serializedComponent.originalDoenetAttributes
|
|
1402
|
+
&& serializedComponent.originalDoenetAttributes.assignNames
|
|
1403
|
+
) {
|
|
1404
|
+
assignNames = serializedComponent.originalDoenetAttributes.assignNames;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
if (assignNames) {
|
|
1408
|
+
|
|
1409
|
+
let assignNamesToReplacements = componentClass.assignNamesToReplacements;
|
|
1410
|
+
if (!assignNamesToReplacements) {
|
|
1411
|
+
throw Error(`Cannot assign names for component type ${serializedComponent.componentType}${indexRangeString(serializedComponent)}`);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
// assignNames was specified
|
|
1415
|
+
// put in doenetAttributes as assignNames array
|
|
1416
|
+
doenetAttributes.assignNames = assignNames;
|
|
1417
|
+
|
|
1418
|
+
if (!doenetAttributes.createUniqueAssignNames) {
|
|
1419
|
+
let flattedNames = flattenDeep(assignNames);
|
|
1420
|
+
for (let name of flattedNames) {
|
|
1421
|
+
if (!(/[a-zA-Z]/.test(name.substring(0, 1)))) {
|
|
1422
|
+
throw Error(`All assigned names must begin with a letter. Found in component of type ${serializedComponent.componentType}${indexRangeString(serializedComponent)}`);
|
|
1423
|
+
}
|
|
1424
|
+
if (!(/^[a-zA-Z0-9_\-]+$/.test(name))) {
|
|
1425
|
+
throw Error(`Assigned names can contain only letters, numbers, hyphens, and underscores. Found in component of type ${serializedComponent.componentType}${indexRangeString(serializedComponent)}`);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
// check if unique names
|
|
1429
|
+
if (flattedNames.length !== new Set(flattedNames).size) {
|
|
1430
|
+
throw Error(`Duplicate assigned names. Found in component of type ${serializedComponent.componentType}${indexRangeString(serializedComponent)}`);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
|
|
1436
|
+
if (newNamespace) {
|
|
1437
|
+
// newNamespace was specified
|
|
1438
|
+
// put in attributes as boolean
|
|
1439
|
+
attributes.newNamespace = { primitive: newNamespace };
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
|
|
1443
|
+
let count = currentNamespace.componentCounts[componentType];
|
|
1444
|
+
if (count === undefined) {
|
|
1445
|
+
count = 0;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
// if created from a attribute/sugar/macro, don't include in component counts
|
|
1449
|
+
if (!(doenetAttributes.isAttributeChild || doenetAttributes.createdFromSugar
|
|
1450
|
+
|| doenetAttributes.createdFromMacro
|
|
1451
|
+
)) {
|
|
1452
|
+
currentNamespace.componentCounts[componentType] = ++count;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
let componentName = '';
|
|
1456
|
+
for (let l = 0; l <= level; l++) {
|
|
1457
|
+
componentName += namespaceStack[l].namespace + '/';
|
|
1458
|
+
}
|
|
1459
|
+
if (!prescribedName) {
|
|
1460
|
+
if (useOriginalNames) {
|
|
1461
|
+
|
|
1462
|
+
if (serializedComponent.originalName) {
|
|
1463
|
+
let lastInd = serializedComponent.originalName.lastIndexOf("/");
|
|
1464
|
+
prescribedName = serializedComponent.originalName.substring(lastInd + 1);
|
|
1465
|
+
// } else if (serializedComponent.componentName) {
|
|
1466
|
+
// let lastInd = serializedComponent.componentName.lastIndexOf("/");
|
|
1467
|
+
// prescribedName = serializedComponent.componentName.substring(lastInd + 1);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
if (!prescribedName) {
|
|
1471
|
+
prescribedName = '_' + componentType.toLowerCase() + count;
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
componentName += prescribedName;
|
|
1476
|
+
|
|
1477
|
+
serializedComponent.componentName = componentName;
|
|
1478
|
+
if (prescribedName) {
|
|
1479
|
+
if (prescribedName in currentNamespace.namesUsed) {
|
|
1480
|
+
throw Error(`Duplicate component name ${componentName}. Found in component of type ${serializedComponent.componentType}${indexRangeString(serializedComponent)}`)
|
|
1481
|
+
}
|
|
1482
|
+
currentNamespace.namesUsed[prescribedName] = true;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
// if newNamespace is false,
|
|
1486
|
+
// then register assignNames as belonging to current namespace
|
|
1487
|
+
if (!newNamespace) {
|
|
1488
|
+
if (assignNames) {
|
|
1489
|
+
for (let name of flattenDeep(assignNames)) {
|
|
1490
|
+
if (name in currentNamespace.namesUsed) {
|
|
1491
|
+
throw Error(`Duplicate component name ${name} (from assignNames of ${componentName}). Found in component of type ${serializedComponent.componentType}${indexRangeString(serializedComponent)}`)
|
|
1492
|
+
}
|
|
1493
|
+
currentNamespace.namesUsed[name] = true;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
|
|
1499
|
+
if (serializedComponent.doenetAttributes.createUniqueAssignNames &&
|
|
1500
|
+
serializedComponent.originalName
|
|
1501
|
+
) {
|
|
1502
|
+
|
|
1503
|
+
let originalAssignNames = serializedComponent.doenetAttributes.assignNames;
|
|
1504
|
+
if (!originalAssignNames) {
|
|
1505
|
+
originalAssignNames = serializedComponent.doenetAttributes.originalAssignNames;
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
let longNameIdBase = componentName + "|createUniqueName|assignNames|";
|
|
1509
|
+
|
|
1510
|
+
let namespace = '';
|
|
1511
|
+
let oldNamespace;
|
|
1512
|
+
if (!newNamespace) {
|
|
1513
|
+
for (let l = 0; l <= level; l++) {
|
|
1514
|
+
namespace += namespaceStack[l].namespace + '/';
|
|
1515
|
+
}
|
|
1516
|
+
let lastInd = serializedComponent.originalName.lastIndexOf("/");
|
|
1517
|
+
oldNamespace = serializedComponent.originalName.slice(0, lastInd + 1)
|
|
1518
|
+
} else {
|
|
1519
|
+
namespace = componentName + '/';
|
|
1520
|
+
oldNamespace = serializedComponent.originalName + '/';
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
let newAssignNames = createNewAssignNamesAndRenameMatchingTNames({
|
|
1524
|
+
originalAssignNames, longNameIdBase,
|
|
1525
|
+
namespace, oldNamespace, doenetAttributesByTargetComponentName
|
|
1526
|
+
});
|
|
1527
|
+
|
|
1528
|
+
assignNames = serializedComponent.doenetAttributes.assignNames = newAssignNames;
|
|
1529
|
+
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
renameMatchingTNames(serializedComponent, doenetAttributesByTargetComponentName);
|
|
1533
|
+
|
|
1534
|
+
if (target) {
|
|
1535
|
+
if (!componentClass.acceptTarget) {
|
|
1536
|
+
throw Error(`Component type ${componentType} does not accept a target attribute. Found in component ${componentName}${indexRangeString(serializedComponent)}`);
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
if (target.includes('|')) {
|
|
1540
|
+
throw Error(`target cannot include |. Found in component of type ${serializedComponent.componentType}${indexRangeString(serializedComponent)}`)
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
// convert target to full name
|
|
1544
|
+
doenetAttributes.target = target;
|
|
1545
|
+
|
|
1546
|
+
doenetAttributes.targetComponentName = convertComponentTarget({
|
|
1547
|
+
target,
|
|
1548
|
+
oldTargetComponentName: doenetAttributes.targetComponentName,
|
|
1549
|
+
namespaceStack,
|
|
1550
|
+
acceptDoubleUnderscore: doenetAttributes.createdFromSugar
|
|
1551
|
+
});
|
|
1552
|
+
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
|
|
1556
|
+
if (serializedComponent.children) {
|
|
1557
|
+
|
|
1558
|
+
|
|
1559
|
+
// recurse on child, creating new namespace if specified
|
|
1560
|
+
|
|
1561
|
+
if (!newNamespace) {
|
|
1562
|
+
createComponentNames({
|
|
1563
|
+
serializedComponents: serializedComponent.children,
|
|
1564
|
+
namespaceStack,
|
|
1565
|
+
componentInfoObjects,
|
|
1566
|
+
parentDoenetAttributes: doenetAttributes,
|
|
1567
|
+
parentName: componentName,
|
|
1568
|
+
useOriginalNames,
|
|
1569
|
+
doenetAttributesByTargetComponentName,
|
|
1570
|
+
});
|
|
1571
|
+
} else {
|
|
1572
|
+
|
|
1573
|
+
|
|
1574
|
+
// if newNamespace, then need to make sure that assigned names
|
|
1575
|
+
// don't conflict with new names added,
|
|
1576
|
+
// so include in namesused
|
|
1577
|
+
let namesUsed = {};
|
|
1578
|
+
if (assignNames) {
|
|
1579
|
+
flattenDeep(assignNames).forEach(x => namesUsed[x] = true);
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
let newNamespaceInfo = { namespace: prescribedName, componentCounts: {}, namesUsed };
|
|
1583
|
+
namespaceStack.push(newNamespaceInfo);
|
|
1584
|
+
createComponentNames({
|
|
1585
|
+
serializedComponents: serializedComponent.children,
|
|
1586
|
+
namespaceStack,
|
|
1587
|
+
componentInfoObjects,
|
|
1588
|
+
parentDoenetAttributes: doenetAttributes,
|
|
1589
|
+
parentName: componentName,
|
|
1590
|
+
useOriginalNames,
|
|
1591
|
+
doenetAttributesByTargetComponentName,
|
|
1592
|
+
});
|
|
1593
|
+
namespaceStack.pop();
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
if (serializedComponent.attributes) {
|
|
1598
|
+
|
|
1599
|
+
// recurse on attributes that are components
|
|
1600
|
+
|
|
1601
|
+
for (let attrName in serializedComponent.attributes) {
|
|
1602
|
+
|
|
1603
|
+
let attribute = serializedComponent.attributes[attrName];
|
|
1604
|
+
|
|
1605
|
+
if (attribute.component) {
|
|
1606
|
+
|
|
1607
|
+
let comp = attribute.component;
|
|
1608
|
+
|
|
1609
|
+
if (!comp.doenetAttributes) {
|
|
1610
|
+
comp.doenetAttributes = {};
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
comp.doenetAttributes.isAttributeChild = true;
|
|
1614
|
+
|
|
1615
|
+
createComponentNames({
|
|
1616
|
+
serializedComponents: [comp],
|
|
1617
|
+
namespaceStack,
|
|
1618
|
+
componentInfoObjects,
|
|
1619
|
+
parentDoenetAttributes: doenetAttributes,
|
|
1620
|
+
parentName: componentName,
|
|
1621
|
+
useOriginalNames,
|
|
1622
|
+
doenetAttributesByTargetComponentName,
|
|
1623
|
+
createNameContext: attrName
|
|
1624
|
+
});
|
|
1625
|
+
|
|
1626
|
+
} else if (attribute.childrenForComponent) {
|
|
1627
|
+
|
|
1628
|
+
// TODO: what to do about parentName/parentDoenetAttributes
|
|
1629
|
+
// since parent of these isn't created
|
|
1630
|
+
// Note: the main (only?) to recurse here is to rename targets
|
|
1631
|
+
createComponentNames({
|
|
1632
|
+
serializedComponents: attribute.childrenForComponent,
|
|
1633
|
+
namespaceStack,
|
|
1634
|
+
componentInfoObjects,
|
|
1635
|
+
parentDoenetAttributes: doenetAttributes,
|
|
1636
|
+
parentName: componentName,
|
|
1637
|
+
useOriginalNames,
|
|
1638
|
+
doenetAttributesByTargetComponentName,
|
|
1639
|
+
createNameContext: attrName
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
// TODO: is there any reason to run createComponentNames on attribute components?
|
|
1647
|
+
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
return serializedComponents;
|
|
1651
|
+
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
function createNewAssignNamesAndRenameMatchingTNames({
|
|
1655
|
+
originalAssignNames, longNameIdBase,
|
|
1656
|
+
namespace, oldNamespace, doenetAttributesByTargetComponentName
|
|
1657
|
+
}) {
|
|
1658
|
+
|
|
1659
|
+
let assignNames = [];
|
|
1660
|
+
|
|
1661
|
+
for (let [ind, originalName] of originalAssignNames.entries()) {
|
|
1662
|
+
|
|
1663
|
+
if (Array.isArray(originalName)) {
|
|
1664
|
+
// recurse to next level
|
|
1665
|
+
let assignNamesSub = createNewAssignNamesAndRenameMatchingTNames({
|
|
1666
|
+
originalAssignNames: originalName,
|
|
1667
|
+
longNameIdBase: longNameIdBase + ind + "_",
|
|
1668
|
+
namespace, oldNamespace, doenetAttributesByTargetComponentName
|
|
1669
|
+
})
|
|
1670
|
+
assignNames.push(assignNamesSub);
|
|
1671
|
+
} else {
|
|
1672
|
+
|
|
1673
|
+
let longNameId = longNameIdBase + ind;
|
|
1674
|
+
let newName = createUniqueName("fromAssignNames", longNameId);
|
|
1675
|
+
assignNames.push(newName);
|
|
1676
|
+
|
|
1677
|
+
let infoForRenaming = {
|
|
1678
|
+
componentName: namespace + newName,
|
|
1679
|
+
originalName: oldNamespace + originalName
|
|
1680
|
+
};
|
|
1681
|
+
|
|
1682
|
+
renameMatchingTNames(infoForRenaming, doenetAttributesByTargetComponentName, true);
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
return assignNames;
|
|
1688
|
+
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
export function convertComponentTarget({
|
|
1692
|
+
target,
|
|
1693
|
+
oldTargetComponentName,
|
|
1694
|
+
namespaceStack,
|
|
1695
|
+
acceptDoubleUnderscore,
|
|
1696
|
+
}) {
|
|
1697
|
+
|
|
1698
|
+
|
|
1699
|
+
if (!oldTargetComponentName && /__/.test(target) && !acceptDoubleUnderscore) {
|
|
1700
|
+
throw Error("Invalid reference target: " + target);
|
|
1701
|
+
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
let targetComponentName;
|
|
1705
|
+
|
|
1706
|
+
// console.log(`target: ${target}`)
|
|
1707
|
+
// console.log(JSON.parse(JSON.stringify(namespaceStack)))
|
|
1708
|
+
|
|
1709
|
+
if (target.substring(0, 1) === '/') {
|
|
1710
|
+
// if starts with /, then don't add anything to path
|
|
1711
|
+
targetComponentName = target;
|
|
1712
|
+
} else {
|
|
1713
|
+
|
|
1714
|
+
// calculate full target from target
|
|
1715
|
+
// putting it into the context of the current namespace
|
|
1716
|
+
|
|
1717
|
+
let lastLevel = namespaceStack.length - 1;
|
|
1718
|
+
|
|
1719
|
+
while (target.substring(0, 3) === '../') {
|
|
1720
|
+
// take off one level for every ../
|
|
1721
|
+
target = target.substring(3);
|
|
1722
|
+
lastLevel--;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
if (lastLevel < 0) {
|
|
1726
|
+
// the target cannot possibly be valid
|
|
1727
|
+
// if there were more ../s than namespace levels
|
|
1728
|
+
lastLevel = 0;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
targetComponentName = '';
|
|
1732
|
+
for (let l = 0; l <= lastLevel; l++) {
|
|
1733
|
+
targetComponentName += namespaceStack[l].namespace + '/';
|
|
1734
|
+
}
|
|
1735
|
+
targetComponentName += target;
|
|
1736
|
+
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
return targetComponentName;
|
|
1740
|
+
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
export function serializedComponentsReplacer(key, value) {
|
|
1744
|
+
if (value !== value) {
|
|
1745
|
+
return { objectType: 'special-numeric', stringValue: 'NaN' };
|
|
1746
|
+
} else if (value === Infinity) {
|
|
1747
|
+
return { objectType: 'special-numeric', stringValue: 'Infinity' };
|
|
1748
|
+
} else if (value === -Infinity) {
|
|
1749
|
+
return { objectType: 'special-numeric', stringValue: '-Infinity' };
|
|
1750
|
+
}
|
|
1751
|
+
return value;
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
let nanInfinityReviver = function (key, value) {
|
|
1755
|
+
|
|
1756
|
+
if (value && value.objectType === "special-numeric") {
|
|
1757
|
+
if (value.stringValue === "NaN") {
|
|
1758
|
+
return NaN;
|
|
1759
|
+
} else if (value.stringValue === "Infinity") {
|
|
1760
|
+
return Infinity;
|
|
1761
|
+
} else if (value.stringValue === "-Infinity") {
|
|
1762
|
+
return -Infinity;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
return value;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
export function serializedComponentsReviver(key, value) {
|
|
1770
|
+
return me.reviver(key, subsets.Subset.reviver(key, nanInfinityReviver(key, value)))
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
export function gatherVariantComponents({ serializedComponents, componentInfoObjects }) {
|
|
1774
|
+
|
|
1775
|
+
// returns a list of serialized components who are variant components,
|
|
1776
|
+
// where the components are selected from serializedComponents themselves,
|
|
1777
|
+
// or, if a particular component isn't a variant component,
|
|
1778
|
+
// then recurse to find descendant variant components
|
|
1779
|
+
|
|
1780
|
+
// Also, as a side effect, mark each found variant component as a variant component
|
|
1781
|
+
// directly in the variants attribute of that component
|
|
1782
|
+
|
|
1783
|
+
let variantComponents = [];
|
|
1784
|
+
|
|
1785
|
+
for (let serializedComponent of serializedComponents) {
|
|
1786
|
+
|
|
1787
|
+
if (serializedComponent.variants?.isVariantComponent) {
|
|
1788
|
+
variantComponents.push(serializedComponent);
|
|
1789
|
+
continue;
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
let componentType = serializedComponent.componentType;
|
|
1793
|
+
|
|
1794
|
+
if (componentType in componentInfoObjects.componentTypesCreatingVariants) {
|
|
1795
|
+
serializedComponent.variants = {
|
|
1796
|
+
isVariantComponent: true
|
|
1797
|
+
}
|
|
1798
|
+
variantComponents.push(serializedComponent);
|
|
1799
|
+
continue;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
|
|
1803
|
+
if (!serializedComponent.children) {
|
|
1804
|
+
continue;
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
// check if have a variant control child, which means this component
|
|
1808
|
+
// is a variant component
|
|
1809
|
+
if (serializedComponent.children.some(x => x.componentType === "variantControl")) {
|
|
1810
|
+
serializedComponent.variants = {
|
|
1811
|
+
isVariantComponent: true
|
|
1812
|
+
}
|
|
1813
|
+
variantComponents.push(serializedComponent);
|
|
1814
|
+
continue;
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
// if a component isn't a variant component, then recurse on children
|
|
1818
|
+
|
|
1819
|
+
let descendantVariantComponents = gatherVariantComponents({
|
|
1820
|
+
serializedComponents: serializedComponent.children,
|
|
1821
|
+
componentInfoObjects,
|
|
1822
|
+
});
|
|
1823
|
+
|
|
1824
|
+
if (descendantVariantComponents.length > 0) {
|
|
1825
|
+
|
|
1826
|
+
serializedComponent.variants = {
|
|
1827
|
+
descendantVariantComponents: descendantVariantComponents
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
variantComponents.push(...descendantVariantComponents)
|
|
1831
|
+
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
return variantComponents;
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
export function getNumberOfVariants({ serializedComponent, componentInfoObjects }) {
|
|
1839
|
+
|
|
1840
|
+
// get number of variants from document (or other sectioning component)
|
|
1841
|
+
|
|
1842
|
+
if (!serializedComponent.variants) {
|
|
1843
|
+
serializedComponent.variants = {};
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
let variantControlChild
|
|
1847
|
+
for (let child of serializedComponent.children) {
|
|
1848
|
+
if (child.componentType === "variantControl") {
|
|
1849
|
+
variantControlChild = child;
|
|
1850
|
+
break;
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
if (!variantControlChild) {
|
|
1855
|
+
|
|
1856
|
+
if (serializedComponent.componentType === "document") {
|
|
1857
|
+
// if have a single child that is a section, use variants from that section
|
|
1858
|
+
|
|
1859
|
+
let nonBlankChildren = serializedComponent.children.filter(x => x.componentType || x.trim() !== "");
|
|
1860
|
+
|
|
1861
|
+
if (nonBlankChildren.length === 1 && componentInfoObjects.isInheritedComponentType({
|
|
1862
|
+
inheritedComponentType: nonBlankChildren[0].componentType,
|
|
1863
|
+
baseComponentType: "_sectioningComponent"
|
|
1864
|
+
})) {
|
|
1865
|
+
|
|
1866
|
+
let results = getNumberOfVariants({
|
|
1867
|
+
serializedComponent: nonBlankChildren[0],
|
|
1868
|
+
componentInfoObjects
|
|
1869
|
+
});
|
|
1870
|
+
|
|
1871
|
+
|
|
1872
|
+
if (results.success) {
|
|
1873
|
+
serializedComponent.variants.numberOfVariants = results.numberOfVariants;
|
|
1874
|
+
serializedComponent.variants.variantNames = results.variantNames;
|
|
1875
|
+
serializedComponent.variants.variantsFromChild = true;
|
|
1876
|
+
serializedComponent.variants.numberOfVariantsPreIgnore = results.numberOfVariantsPreIgnore;
|
|
1877
|
+
serializedComponent.variants.indicesToIgnore = results.indicesToIgnore;
|
|
1878
|
+
|
|
1879
|
+
return results;
|
|
1880
|
+
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
// either didn't have a single section child or get number of varants wan't successful
|
|
1886
|
+
|
|
1887
|
+
let numberOfVariants = 100;
|
|
1888
|
+
let numberOfVariantsPreIgnore = 100;
|
|
1889
|
+
|
|
1890
|
+
// check if have one unique variant
|
|
1891
|
+
let compClass = componentInfoObjects.allComponentClasses[serializedComponent.componentType];
|
|
1892
|
+
let result = compClass.determineNumberOfUniqueVariants({
|
|
1893
|
+
serializedComponent, componentInfoObjects
|
|
1894
|
+
})
|
|
1895
|
+
|
|
1896
|
+
// if have 100 or fewer unique variants, set to unique
|
|
1897
|
+
if (result.success && result.numberOfVariantsPreIgnore <= 100) {
|
|
1898
|
+
numberOfVariantsPreIgnore = result.numberOfVariantsPreIgnore;
|
|
1899
|
+
numberOfVariants = result.numberOfVariants;
|
|
1900
|
+
serializedComponent.variants.uniqueVariants = true;
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
serializedComponent.variants.numberOfVariantsPreIgnore = numberOfVariantsPreIgnore;
|
|
1904
|
+
serializedComponent.variants.numberOfVariants = numberOfVariants;
|
|
1905
|
+
serializedComponent.variants.indicesToIgnore = [];
|
|
1906
|
+
|
|
1907
|
+
return {
|
|
1908
|
+
success: true,
|
|
1909
|
+
numberOfVariants,
|
|
1910
|
+
numberOfVariantsPreIgnore,
|
|
1911
|
+
indicesToIgnore: []
|
|
1912
|
+
};
|
|
1913
|
+
|
|
1914
|
+
} else {
|
|
1915
|
+
// if are a section without a variant control, it doesn't determine variants
|
|
1916
|
+
return { success: false }
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
let numberOfVariants = variantControlChild.attributes.nVariants?.primitive;
|
|
1922
|
+
|
|
1923
|
+
if (numberOfVariants === undefined) {
|
|
1924
|
+
numberOfVariants = 100;
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
let variantNames = variantControlChild.attributes.variantNames?.component?.children
|
|
1928
|
+
.map(x => x.toLowerCase().substring(0, 1000));
|
|
1929
|
+
|
|
1930
|
+
let indicesToIgnore = [];
|
|
1931
|
+
if (variantControlChild.attributes.variantIndicesToIgnore) {
|
|
1932
|
+
indicesToIgnore = variantControlChild.attributes.variantIndicesToIgnore.component
|
|
1933
|
+
.children.map(Number)
|
|
1934
|
+
.filter(x => Number.isInteger(x) && x >= 1 && x <= numberOfVariants)
|
|
1935
|
+
.sort((a, b) => a - b);
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
let numberOfVariantsPreIgnore = numberOfVariants;
|
|
1939
|
+
|
|
1940
|
+
if (!variantControlChild.attributes.uniqueVariants?.primitive) {
|
|
1941
|
+
if (indicesToIgnore.length > 0) {
|
|
1942
|
+
serializedComponent.variants.numberOfVariantsPreIgnore = numberOfVariantsPreIgnore;
|
|
1943
|
+
serializedComponent.variants.indicesToIgnore = indicesToIgnore;
|
|
1944
|
+
numberOfVariants -= indicesToIgnore.length;
|
|
1945
|
+
}
|
|
1946
|
+
serializedComponent.variants.numberOfVariants = numberOfVariants;
|
|
1947
|
+
return {
|
|
1948
|
+
success: true,
|
|
1949
|
+
numberOfVariants,
|
|
1950
|
+
variantNames,
|
|
1951
|
+
numberOfVariantsPreIgnore,
|
|
1952
|
+
indicesToIgnore,
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
// have unique variants so it is more complicated!
|
|
1957
|
+
|
|
1958
|
+
let compClass = componentInfoObjects.allComponentClasses[serializedComponent.componentType];
|
|
1959
|
+
|
|
1960
|
+
let result = compClass.determineNumberOfUniqueVariants({
|
|
1961
|
+
serializedComponent, componentInfoObjects
|
|
1962
|
+
})
|
|
1963
|
+
|
|
1964
|
+
if (result.success) {
|
|
1965
|
+
numberOfVariantsPreIgnore = result.numberOfVariantsPreIgnore;
|
|
1966
|
+
numberOfVariants = result.numberOfVariants;
|
|
1967
|
+
serializedComponent.variants.uniqueVariants = true;
|
|
1968
|
+
|
|
1969
|
+
indicesToIgnore = indicesToIgnore.filter(x => x <= numberOfVariantsPreIgnore);
|
|
1970
|
+
|
|
1971
|
+
// don't have to add to serializedComponent.variants.numberOfVariants
|
|
1972
|
+
// as determineNumberOfUniqueVariants does it in this case
|
|
1973
|
+
} else {
|
|
1974
|
+
serializedComponent.variants.numberOfVariantsPreIgnore = numberOfVariantsPreIgnore;
|
|
1975
|
+
serializedComponent.variants.numberOfVariants = numberOfVariants;
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
return {
|
|
1979
|
+
success: true,
|
|
1980
|
+
numberOfVariants,
|
|
1981
|
+
variantNames,
|
|
1982
|
+
numberOfVariantsPreIgnore,
|
|
1983
|
+
indicesToIgnore,
|
|
1984
|
+
};
|
|
1985
|
+
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
export function processAssignNames({
|
|
1989
|
+
assignNames = [],
|
|
1990
|
+
serializedComponents,
|
|
1991
|
+
parentName,
|
|
1992
|
+
parentCreatesNewNamespace,
|
|
1993
|
+
componentInfoObjects,
|
|
1994
|
+
indOffset = 0,
|
|
1995
|
+
originalNamesAreConsistent = false,
|
|
1996
|
+
}) {
|
|
1997
|
+
|
|
1998
|
+
|
|
1999
|
+
// console.log(`process assign names`)
|
|
2000
|
+
// console.log(deepClone(serializedComponents));
|
|
2001
|
+
// console.log(`originalNamesAreConsistent: ${originalNamesAreConsistent}`)
|
|
2002
|
+
|
|
2003
|
+
let nComponents = serializedComponents.length;
|
|
2004
|
+
|
|
2005
|
+
// normalize form so all names are originalNames,
|
|
2006
|
+
// independent of whether the components originated from a copy
|
|
2007
|
+
// or directly from a serialized state that was already given names
|
|
2008
|
+
moveComponentNamesToOriginalNames(serializedComponents);
|
|
2009
|
+
|
|
2010
|
+
let doenetAttributesByTargetComponentName = {};
|
|
2011
|
+
|
|
2012
|
+
let originalNamespace = null;
|
|
2013
|
+
|
|
2014
|
+
if (originalNamesAreConsistent) {
|
|
2015
|
+
|
|
2016
|
+
// need to use a component for original name, as parentName is the new name
|
|
2017
|
+
if (nComponents > 0) {
|
|
2018
|
+
// find a component with an original name, i.e., not a string
|
|
2019
|
+
let component = serializedComponents.filter(x => typeof x === "object")[0];
|
|
2020
|
+
if (component && component.originalName) {
|
|
2021
|
+
let lastSlash = component.originalName.lastIndexOf('/');
|
|
2022
|
+
originalNamespace = component.originalName.substring(0, lastSlash);
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
if (originalNamespace !== null) {
|
|
2027
|
+
for (let component of serializedComponents) {
|
|
2028
|
+
setTargetsOutsideNamespaceToAbsoluteAndRecordAllTargetComponentNames({
|
|
2029
|
+
namespace: originalNamespace,
|
|
2030
|
+
components: [component],
|
|
2031
|
+
doenetAttributesByTargetComponentName
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
} else {
|
|
2036
|
+
for (let ind = 0; ind < nComponents; ind++) {
|
|
2037
|
+
|
|
2038
|
+
let component = serializedComponents[ind];
|
|
2039
|
+
|
|
2040
|
+
if (typeof component !== "object") {
|
|
2041
|
+
continue;
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
originalNamespace = null;
|
|
2045
|
+
// need to use a component for original name, as parentName is the new name
|
|
2046
|
+
if (nComponents > 0 && component.originalName) {
|
|
2047
|
+
let lastSlash = component.originalName.lastIndexOf('/');
|
|
2048
|
+
originalNamespace = component.originalName.substring(0, lastSlash);
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
if (originalNamespace !== null) {
|
|
2052
|
+
setTargetsOutsideNamespaceToAbsoluteAndRecordAllTargetComponentNames({
|
|
2053
|
+
namespace: originalNamespace,
|
|
2054
|
+
components: [component],
|
|
2055
|
+
doenetAttributesByTargetComponentName
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
|
|
2063
|
+
let processedComponents = [];
|
|
2064
|
+
|
|
2065
|
+
// don't name strings or primitive numbers
|
|
2066
|
+
let numPrimitives = 0;
|
|
2067
|
+
|
|
2068
|
+
for (let ind = 0; ind < nComponents; ind++) {
|
|
2069
|
+
|
|
2070
|
+
let indForNames = ind + indOffset;
|
|
2071
|
+
|
|
2072
|
+
let component = serializedComponents[ind];
|
|
2073
|
+
|
|
2074
|
+
if (typeof component !== "object") {
|
|
2075
|
+
numPrimitives++;
|
|
2076
|
+
processedComponents.push(component);
|
|
2077
|
+
continue;
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
let name = assignNames[indForNames - numPrimitives];
|
|
2081
|
+
|
|
2082
|
+
|
|
2083
|
+
if (!component.doenetAttributes) {
|
|
2084
|
+
component.doenetAttributes = {};
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
if (!originalNamesAreConsistent) {
|
|
2088
|
+
// doenetAttributesByTargetComponentName = {};
|
|
2089
|
+
|
|
2090
|
+
originalNamespace = null;
|
|
2091
|
+
// need to use a component for original name, as parentName is the new name
|
|
2092
|
+
if (nComponents > 0 && component.originalName) {
|
|
2093
|
+
let lastSlash = component.originalName.lastIndexOf('/');
|
|
2094
|
+
originalNamespace = component.originalName.substring(0, lastSlash);
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
if (componentInfoObjects.allComponentClasses[
|
|
2100
|
+
component.componentType].assignNamesSkipOver
|
|
2101
|
+
) {
|
|
2102
|
+
name = [name];
|
|
2103
|
+
} else if (component.attributes && component.attributes.assignNamesSkip) {
|
|
2104
|
+
let numberToSkip = component.attributes.assignNamesSkip.primitive;
|
|
2105
|
+
if (numberToSkip > 0) {
|
|
2106
|
+
for (let i = 0; i < numberToSkip; i++) {
|
|
2107
|
+
name = [name];
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
if (Array.isArray(name)) {
|
|
2113
|
+
|
|
2114
|
+
if (componentInfoObjects.allComponentClasses[
|
|
2115
|
+
component.componentType].assignNamesToReplacements
|
|
2116
|
+
) {
|
|
2117
|
+
|
|
2118
|
+
// give component itself an unreachable name
|
|
2119
|
+
let longNameId = parentName + "|assignName|" + indForNames.toString();
|
|
2120
|
+
component.doenetAttributes.prescribedName = createUniqueName(component.componentType.toLowerCase(), longNameId);
|
|
2121
|
+
|
|
2122
|
+
let componentName = parentName;
|
|
2123
|
+
if (!parentCreatesNewNamespace) {
|
|
2124
|
+
let lastSlash = parentName.lastIndexOf("/");
|
|
2125
|
+
componentName = parentName.substring(0, lastSlash);
|
|
2126
|
+
}
|
|
2127
|
+
componentName += "/" + component.doenetAttributes.prescribedName;
|
|
2128
|
+
component.componentName = componentName;
|
|
2129
|
+
|
|
2130
|
+
component.doenetAttributes.assignNames = name;
|
|
2131
|
+
|
|
2132
|
+
processedComponents.push(component);
|
|
2133
|
+
continue;
|
|
2134
|
+
|
|
2135
|
+
} else {
|
|
2136
|
+
|
|
2137
|
+
// TODO: what to do when try to assign names recursively to non-composite?
|
|
2138
|
+
console.warn(`Cannot assign names recursively to ${component.componentType}`)
|
|
2139
|
+
name = null;
|
|
2140
|
+
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
|
|
2146
|
+
if (!name) {
|
|
2147
|
+
if (originalNamesAreConsistent && component.originalName && !component.doenetAttributes?.createUniqueName) {
|
|
2148
|
+
name = component.originalName.slice(originalNamespace.length + 1);
|
|
2149
|
+
} else {
|
|
2150
|
+
let longNameId = parentName + "|assignName|" + (indForNames).toString();
|
|
2151
|
+
name = createUniqueName(component.componentType.toLowerCase(), longNameId);
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
|
|
2156
|
+
component.doenetAttributes.prescribedName = name;
|
|
2157
|
+
// delete component.originalName;
|
|
2158
|
+
|
|
2159
|
+
// even if original names are consistent, we still use component's original assignNames
|
|
2160
|
+
// (we wouldn't use assignNames of the component's children as they should have unique names)
|
|
2161
|
+
if (originalNamesAreConsistent && !component.doenetAttributes.assignNames
|
|
2162
|
+
&& component.originalDoenetAttributes
|
|
2163
|
+
&& component.originalDoenetAttributes.assignNames
|
|
2164
|
+
) {
|
|
2165
|
+
component.doenetAttributes.assignNames = component.originalDoenetAttributes.assignNames;
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
createComponentNamesFromParentName({
|
|
2169
|
+
parentName,
|
|
2170
|
+
ind: indForNames,
|
|
2171
|
+
component,
|
|
2172
|
+
parentCreatesNewNamespace, componentInfoObjects,
|
|
2173
|
+
doenetAttributesByTargetComponentName,
|
|
2174
|
+
originalNamesAreConsistent,
|
|
2175
|
+
});
|
|
2176
|
+
|
|
2177
|
+
processedComponents.push(component);
|
|
2178
|
+
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
|
|
2182
|
+
return {
|
|
2183
|
+
serializedComponents: processedComponents,
|
|
2184
|
+
};
|
|
2185
|
+
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
function createComponentNamesFromParentName({
|
|
2189
|
+
parentName, component,
|
|
2190
|
+
ind,
|
|
2191
|
+
parentCreatesNewNamespace, componentInfoObjects,
|
|
2192
|
+
doenetAttributesByTargetComponentName,
|
|
2193
|
+
originalNamesAreConsistent,
|
|
2194
|
+
}) {
|
|
2195
|
+
|
|
2196
|
+
|
|
2197
|
+
let namespacePieces = parentName.split('/');
|
|
2198
|
+
|
|
2199
|
+
if (!parentCreatesNewNamespace) {
|
|
2200
|
+
namespacePieces.pop();
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
let namespaceStack = namespacePieces.map(x => ({
|
|
2204
|
+
namespace: x,
|
|
2205
|
+
componentCounts: {},
|
|
2206
|
+
namesUsed: {}
|
|
2207
|
+
}));
|
|
2208
|
+
|
|
2209
|
+
if (!(parentName[0] === '/')) {
|
|
2210
|
+
// if componentName doesn't begin with a /
|
|
2211
|
+
// still add a namespace for the root namespace at the beginning
|
|
2212
|
+
namespaceStack.splice(0, 0, {
|
|
2213
|
+
componentCounts: {},
|
|
2214
|
+
namesUsed: {},
|
|
2215
|
+
namespace: ""
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
if (!component.doenetAttributes) {
|
|
2220
|
+
component.doenetAttributes = {};
|
|
2221
|
+
}
|
|
2222
|
+
if (!component.attributes) {
|
|
2223
|
+
component.attributes = {};
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
// let originalNamespaceForComponentChildren = parentName;
|
|
2227
|
+
// if (!parentCreatesNewNamespace) {
|
|
2228
|
+
// let lastSlash = parentName.lastIndexOf("/");
|
|
2229
|
+
// namespaceForComponent = parentName.substring(0, lastSlash);
|
|
2230
|
+
// }
|
|
2231
|
+
|
|
2232
|
+
|
|
2233
|
+
let useOriginalNames;
|
|
2234
|
+
if (component.attributes.newNamespace?.primitive
|
|
2235
|
+
|| originalNamesAreConsistent
|
|
2236
|
+
) {
|
|
2237
|
+
useOriginalNames = true;
|
|
2238
|
+
} else {
|
|
2239
|
+
useOriginalNames = false;
|
|
2240
|
+
|
|
2241
|
+
if (component.children) {
|
|
2242
|
+
markToCreateAllUniqueNames(component.children)
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
// always mark component attributes to create unique names
|
|
2247
|
+
for (let attrName in component.attributes) {
|
|
2248
|
+
let attribute = component.attributes[attrName];
|
|
2249
|
+
if (attribute.component) {
|
|
2250
|
+
markToCreateAllUniqueNames([attribute.component]);
|
|
2251
|
+
} else if (attribute.childrenForComponent) {
|
|
2252
|
+
markToCreateAllUniqueNames(attribute.childrenForComponent);
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
|
|
2257
|
+
// console.log(`before create componentName`)
|
|
2258
|
+
// console.log(deepClone(component))
|
|
2259
|
+
// console.log(useOriginalNames);
|
|
2260
|
+
// console.log(component.attributes.newNamespace);
|
|
2261
|
+
|
|
2262
|
+
createComponentNames({
|
|
2263
|
+
serializedComponents: [component],
|
|
2264
|
+
namespaceStack,
|
|
2265
|
+
componentInfoObjects,
|
|
2266
|
+
parentName,
|
|
2267
|
+
useOriginalNames,
|
|
2268
|
+
doenetAttributesByTargetComponentName,
|
|
2269
|
+
indOffset: ind,
|
|
2270
|
+
});
|
|
2271
|
+
|
|
2272
|
+
// console.log(`result of create componentName`)
|
|
2273
|
+
// console.log(deepClone(component))
|
|
2274
|
+
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
function setTargetsOutsideNamespaceToAbsoluteAndRecordAllTargetComponentNames({ namespace, components, doenetAttributesByTargetComponentName }) {
|
|
2278
|
+
|
|
2279
|
+
let namespaceLength = namespace.length;
|
|
2280
|
+
for (let component of components) {
|
|
2281
|
+
if (typeof component !== "object") {
|
|
2282
|
+
continue;
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
if (component.doenetAttributes && component.doenetAttributes.target) {
|
|
2286
|
+
let targetComponentName = component.doenetAttributes.targetComponentName;
|
|
2287
|
+
if (targetComponentName !== undefined) {
|
|
2288
|
+
if (targetComponentName.substring(0, namespaceLength) !== namespace) {
|
|
2289
|
+
component.doenetAttributes.target = targetComponentName;
|
|
2290
|
+
}
|
|
2291
|
+
if (!doenetAttributesByTargetComponentName[targetComponentName]) {
|
|
2292
|
+
doenetAttributesByTargetComponentName[targetComponentName] = [];
|
|
2293
|
+
}
|
|
2294
|
+
doenetAttributesByTargetComponentName[targetComponentName].push(component.doenetAttributes);
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
if (component.children) {
|
|
2299
|
+
setTargetsOutsideNamespaceToAbsoluteAndRecordAllTargetComponentNames({ namespace, components: component.children, doenetAttributesByTargetComponentName })
|
|
2300
|
+
}
|
|
2301
|
+
if (component.attributes) {
|
|
2302
|
+
for (let attrName in component.attributes) {
|
|
2303
|
+
let attribute = component.attributes[attrName];
|
|
2304
|
+
if (attribute.component) {
|
|
2305
|
+
setTargetsOutsideNamespaceToAbsoluteAndRecordAllTargetComponentNames({ namespace, components: [attribute.component], doenetAttributesByTargetComponentName })
|
|
2306
|
+
} else if (attribute.childrenForComponent) {
|
|
2307
|
+
setTargetsOutsideNamespaceToAbsoluteAndRecordAllTargetComponentNames({ namespace, components: attribute.childrenForComponent, doenetAttributesByTargetComponentName })
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
function renameMatchingTNames(component, doenetAttributesByTargetComponentName, renameMatchingNamespaces = false) {
|
|
2315
|
+
|
|
2316
|
+
if (component.originalName &&
|
|
2317
|
+
doenetAttributesByTargetComponentName
|
|
2318
|
+
|
|
2319
|
+
&& component.componentName !== component.originalName) {
|
|
2320
|
+
// we have a component who has been named and there are other components
|
|
2321
|
+
// whose targetComponentName refers to this component
|
|
2322
|
+
// Modify the target and targetComponentName of the other components to refer to the new name
|
|
2323
|
+
// (Must modify targetComponentName as we don't know if this component has been processed yet)
|
|
2324
|
+
if (doenetAttributesByTargetComponentName[component.originalName]) {
|
|
2325
|
+
for (let dAttributes of doenetAttributesByTargetComponentName[component.originalName]) {
|
|
2326
|
+
dAttributes.target = component.componentName;
|
|
2327
|
+
dAttributes.targetComponentName = component.componentName;
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
if (renameMatchingNamespaces) {
|
|
2331
|
+
let originalNamespace = component.originalName + "/";
|
|
2332
|
+
let nSpaceLen = originalNamespace.length;
|
|
2333
|
+
for (let originalTargetComponentName in doenetAttributesByTargetComponentName) {
|
|
2334
|
+
if (originalTargetComponentName.substring(0, nSpaceLen) === originalNamespace) {
|
|
2335
|
+
let originalEnding = originalTargetComponentName.substring(nSpaceLen);
|
|
2336
|
+
for (let dAttributes of doenetAttributesByTargetComponentName[originalTargetComponentName]) {
|
|
2337
|
+
dAttributes.target = component.componentName + "/" + originalEnding;
|
|
2338
|
+
dAttributes.targetComponentName = component.componentName + "/" + originalEnding;
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
function moveComponentNamesToOriginalNames(components) {
|
|
2347
|
+
for (let component of components) {
|
|
2348
|
+
if (component.componentName) {
|
|
2349
|
+
component.originalName = component.componentName;
|
|
2350
|
+
delete component.componentName;
|
|
2351
|
+
}
|
|
2352
|
+
if (component.children) {
|
|
2353
|
+
moveComponentNamesToOriginalNames(component.children);
|
|
2354
|
+
}
|
|
2355
|
+
if (component.attributes) {
|
|
2356
|
+
for (let attrName in component.attributes) {
|
|
2357
|
+
let attribute = component.attributes[attrName];
|
|
2358
|
+
if (attribute.component) {
|
|
2359
|
+
moveComponentNamesToOriginalNames([attribute.component]);
|
|
2360
|
+
} else if (attribute.childrenForComponent) {
|
|
2361
|
+
moveComponentNamesToOriginalNames(attribute.childrenForComponent);
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2368
|
+
export function markToCreateAllUniqueNames(components) {
|
|
2369
|
+
for (let component of components) {
|
|
2370
|
+
if (typeof component !== "object") {
|
|
2371
|
+
continue;
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
if (!component.doenetAttributes) {
|
|
2375
|
+
component.doenetAttributes = {};
|
|
2376
|
+
}
|
|
2377
|
+
component.doenetAttributes.createUniqueName = true;
|
|
2378
|
+
delete component.doenetAttributes.prescribedName;
|
|
2379
|
+
|
|
2380
|
+
if (!component.attributes?.newNamespace?.primitive) {
|
|
2381
|
+
if (component.doenetAttributes.assignNames) {
|
|
2382
|
+
component.doenetAttributes.createUniqueAssignNames = true;
|
|
2383
|
+
component.doenetAttributes.originalAssignNames = component.doenetAttributes.assignNames;
|
|
2384
|
+
delete component.doenetAttributes.assignNames;
|
|
2385
|
+
} else if (component.originalDoenetAttributes && component.originalDoenetAttributes.assignNames) {
|
|
2386
|
+
component.doenetAttributes.createUniqueAssignNames = true;
|
|
2387
|
+
component.doenetAttributes.originalAssignNames = component.originalDoenetAttributes.assignNames;
|
|
2388
|
+
}
|
|
2389
|
+
if (component.children) {
|
|
2390
|
+
markToCreateAllUniqueNames(component.children);
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
if (component.attributes) {
|
|
2395
|
+
for (let attrName in component.attributes) {
|
|
2396
|
+
let attribute = component.attributes[attrName];
|
|
2397
|
+
if (attribute.component) {
|
|
2398
|
+
markToCreateAllUniqueNames([attribute.component]);
|
|
2399
|
+
} else if (attribute.childrenForComponent) {
|
|
2400
|
+
markToCreateAllUniqueNames(attribute.childrenForComponent);
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
export function setTNamesToAbsolute(components) {
|
|
2408
|
+
|
|
2409
|
+
for (let component of components) {
|
|
2410
|
+
if (component.doenetAttributes && component.doenetAttributes.target) {
|
|
2411
|
+
let targetComponentName = component.doenetAttributes.targetComponentName;
|
|
2412
|
+
if (targetComponentName !== undefined) {
|
|
2413
|
+
component.doenetAttributes.target = targetComponentName;
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
if (component.children) {
|
|
2418
|
+
setTNamesToAbsolute(component.children)
|
|
2419
|
+
}
|
|
2420
|
+
if (component.attributes) {
|
|
2421
|
+
for (let attrName in component.attributes) {
|
|
2422
|
+
let attribute = component.attributes[attrName];
|
|
2423
|
+
if (attribute.component) {
|
|
2424
|
+
setTNamesToAbsolute([attribute.component])
|
|
2425
|
+
} else if (attribute.childrenForComponent) {
|
|
2426
|
+
setTNamesToAbsolute(attribute.childrenForComponent)
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
export function restrictTNamesToNamespace({ components, namespace, parentNamespace, parentIsCopy = false }) {
|
|
2434
|
+
|
|
2435
|
+
if (parentNamespace === undefined) {
|
|
2436
|
+
parentNamespace = namespace;
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
let nSpace = namespace.length;
|
|
2440
|
+
|
|
2441
|
+
for (let component of components) {
|
|
2442
|
+
|
|
2443
|
+
if (component.doenetAttributes && component.doenetAttributes.target) {
|
|
2444
|
+
let target = component.doenetAttributes.target;
|
|
2445
|
+
|
|
2446
|
+
if (target[0] === "/") {
|
|
2447
|
+
if (target.substring(0, nSpace) !== namespace) {
|
|
2448
|
+
let targetComponentName = namespace + target.substring(1);
|
|
2449
|
+
component.doenetAttributes.target = targetComponentName;
|
|
2450
|
+
component.doenetAttributes.targetComponentName = targetComponentName;
|
|
2451
|
+
}
|
|
2452
|
+
} else if (target.substring(0, 3) === "../") {
|
|
2453
|
+
let tNamePart = target;
|
|
2454
|
+
let namespacePart = parentNamespace;
|
|
2455
|
+
while (tNamePart.substring(0, 3) === "../") {
|
|
2456
|
+
tNamePart = tNamePart.substring(3);
|
|
2457
|
+
let lastSlash = namespacePart.substring(0, namespacePart.length - 1).lastIndexOf("/");
|
|
2458
|
+
namespacePart = namespacePart.substring(0, lastSlash + 1);
|
|
2459
|
+
if (namespacePart.substring(0, nSpace) !== namespace) {
|
|
2460
|
+
while (tNamePart.substring(0, 3) === "../") {
|
|
2461
|
+
tNamePart = tNamePart.substring(3);
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
let targetComponentName = namespace + tNamePart;
|
|
2465
|
+
component.doenetAttributes.target = targetComponentName;
|
|
2466
|
+
component.doenetAttributes.targetComponentName = targetComponentName;
|
|
2467
|
+
break;
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2475
|
+
if (component.children) {
|
|
2476
|
+
let adjustedNamespace = namespace;
|
|
2477
|
+
if (parentIsCopy && component.componentType === "externalContent") {
|
|
2478
|
+
// if have a external content inside a copy,
|
|
2479
|
+
// then restrict children to the namespace of the externalContent
|
|
2480
|
+
adjustedNamespace = component.componentName + "/";
|
|
2481
|
+
}
|
|
2482
|
+
let namespaceForChildren = parentNamespace;
|
|
2483
|
+
if (component.attributes && component.attributes.newNamespace?.primitive) {
|
|
2484
|
+
namespaceForChildren = component.componentName;
|
|
2485
|
+
}
|
|
2486
|
+
restrictTNamesToNamespace({
|
|
2487
|
+
components: component.children,
|
|
2488
|
+
namespace: adjustedNamespace,
|
|
2489
|
+
parentNamespace: namespaceForChildren,
|
|
2490
|
+
parentIsCopy: component.componentType === "copy"
|
|
2491
|
+
})
|
|
2492
|
+
}
|
|
2493
|
+
if (component.attributes) {
|
|
2494
|
+
for (let attrName in component.attributes) {
|
|
2495
|
+
let attribute = component.attributes[attrName];
|
|
2496
|
+
if (attribute.component) {
|
|
2497
|
+
restrictTNamesToNamespace({
|
|
2498
|
+
components: [attribute.component], namespace, parentNamespace
|
|
2499
|
+
})
|
|
2500
|
+
} else if (attribute.childrenForComponent) {
|
|
2501
|
+
restrictTNamesToNamespace({
|
|
2502
|
+
components: attribute.childrenForComponent, namespace, parentNamespace
|
|
2503
|
+
})
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
function indexRangeString(serializedComponent) {
|
|
2511
|
+
let message = "";
|
|
2512
|
+
if (serializedComponent.range) {
|
|
2513
|
+
let indBegin, indEnd;
|
|
2514
|
+
if (serializedComponent.range.selfCloseBegin !== undefined) {
|
|
2515
|
+
indBegin = serializedComponent.range.selfCloseBegin;
|
|
2516
|
+
indEnd = serializedComponent.range.selfCloseEnd;
|
|
2517
|
+
} else if (serializedComponent.range.openBegin !== undefined) {
|
|
2518
|
+
indBegin = serializedComponent.range.openBegin;
|
|
2519
|
+
indEnd = serializedComponent.range.openEnd;
|
|
2520
|
+
}
|
|
2521
|
+
if (indBegin !== undefined) {
|
|
2522
|
+
message += ` at indices ${indBegin}-${indEnd}`;
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
return message;
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2528
|
+
|
|
2529
|
+
export function breakEmbeddedStringByCommas({ childrenList }) {
|
|
2530
|
+
let Nparens = 0;
|
|
2531
|
+
let pieces = [];
|
|
2532
|
+
let currentPiece = [];
|
|
2533
|
+
|
|
2534
|
+
for (let component of childrenList) {
|
|
2535
|
+
|
|
2536
|
+
if (typeof component !== "string") {
|
|
2537
|
+
|
|
2538
|
+
currentPiece.push(component);
|
|
2539
|
+
continue;
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
let s = component.trim();
|
|
2543
|
+
let beginInd = 0;
|
|
2544
|
+
|
|
2545
|
+
for (let ind = 0; ind < s.length; ind++) {
|
|
2546
|
+
let char = s[ind];
|
|
2547
|
+
if (char === "(") {
|
|
2548
|
+
Nparens++;
|
|
2549
|
+
}
|
|
2550
|
+
if (char === ")") {
|
|
2551
|
+
if (Nparens === 0) {
|
|
2552
|
+
// parens didn't match, so return failure
|
|
2553
|
+
return { success: false };
|
|
2554
|
+
}
|
|
2555
|
+
Nparens--
|
|
2556
|
+
}
|
|
2557
|
+
if (char === "," && Nparens === 0) {
|
|
2558
|
+
if (ind > beginInd) {
|
|
2559
|
+
let newString = s.substring(beginInd, ind).trim()
|
|
2560
|
+
currentPiece.push(newString);
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
pieces.push(currentPiece);
|
|
2564
|
+
currentPiece = [];
|
|
2565
|
+
beginInd = ind + 1;
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
if (s.length > beginInd) {
|
|
2570
|
+
let newString = s.substring(beginInd, s.length).trim();
|
|
2571
|
+
currentPiece.push(newString);
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
}
|
|
2575
|
+
|
|
2576
|
+
// parens didn't match, so return failure
|
|
2577
|
+
if (Nparens !== 0) {
|
|
2578
|
+
return { success: false };
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2581
|
+
pieces.push(currentPiece);
|
|
2582
|
+
|
|
2583
|
+
return {
|
|
2584
|
+
success: true,
|
|
2585
|
+
pieces: pieces,
|
|
2586
|
+
}
|
|
2587
|
+
}
|