@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,4049 @@
|
|
|
1
|
+
import me from "math-expressions";
|
|
2
|
+
import { createUniqueName } from "./naming";
|
|
3
|
+
import { flattenDeep } from "./array";
|
|
4
|
+
import { deepClone } from "./deepFunctions";
|
|
5
|
+
import { breakEmbeddedStringByCommas } from "../components/commonsugar/breakstrings";
|
|
6
|
+
import { parseAndCompile } from "../../Parser/parser";
|
|
7
|
+
import subsets from "./subset-of-reals";
|
|
8
|
+
import { retrieveTextFileForCid } from "./retrieveTextFile";
|
|
9
|
+
|
|
10
|
+
export async function expandDoenetMLsToFullSerializedComponents({
|
|
11
|
+
cids,
|
|
12
|
+
doenetMLs,
|
|
13
|
+
componentInfoObjects,
|
|
14
|
+
nPreviousDoenetMLs = 0,
|
|
15
|
+
}) {
|
|
16
|
+
let arrayOfSerializedComponents = [];
|
|
17
|
+
let cidComponents = {};
|
|
18
|
+
let allDoenetMLs = [...doenetMLs];
|
|
19
|
+
|
|
20
|
+
for (let [ind, doenetML] of doenetMLs.entries()) {
|
|
21
|
+
let serializedComponents = parseAndCompile(doenetML);
|
|
22
|
+
|
|
23
|
+
serializedComponents = cleanIfHaveJustDocument(serializedComponents);
|
|
24
|
+
|
|
25
|
+
substituteAttributeDeprecations(serializedComponents);
|
|
26
|
+
|
|
27
|
+
temporarilyRenameSourceBackToTarget(serializedComponents);
|
|
28
|
+
|
|
29
|
+
correctComponentTypeCapitalization(
|
|
30
|
+
serializedComponents,
|
|
31
|
+
componentInfoObjects.componentTypeLowerCaseMapping,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
copyTargetOrFromURIAttributeCreatesCopyComponent(
|
|
35
|
+
serializedComponents,
|
|
36
|
+
componentInfoObjects.isCompositeComponent,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
createAttributesFromProps(serializedComponents, componentInfoObjects);
|
|
40
|
+
|
|
41
|
+
breakUpTargetIntoPropsAndIndices(
|
|
42
|
+
serializedComponents,
|
|
43
|
+
componentInfoObjects,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
applyMacros(serializedComponents, componentInfoObjects);
|
|
47
|
+
|
|
48
|
+
substitutePropertyDeprecations(serializedComponents);
|
|
49
|
+
|
|
50
|
+
// remove blank string children after applying macros,
|
|
51
|
+
// as applying macros could create additional blank string children
|
|
52
|
+
removeBlankStringChildren(serializedComponents, componentInfoObjects);
|
|
53
|
+
|
|
54
|
+
decodeXMLEntities(serializedComponents);
|
|
55
|
+
|
|
56
|
+
applySugar({ serializedComponents, componentInfoObjects });
|
|
57
|
+
|
|
58
|
+
arrayOfSerializedComponents.push(serializedComponents);
|
|
59
|
+
|
|
60
|
+
let newContentComponents = findContentCopies({ serializedComponents });
|
|
61
|
+
|
|
62
|
+
for (let cid in newContentComponents.cidComponents) {
|
|
63
|
+
if (cidComponents[cid] === undefined) {
|
|
64
|
+
cidComponents[cid] = [];
|
|
65
|
+
}
|
|
66
|
+
cidComponents[cid].push(...newContentComponents.cidComponents[cid]);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
addDoenetMLIdToRange(serializedComponents, nPreviousDoenetMLs + ind);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let cidList = Object.keys(cidComponents);
|
|
73
|
+
if (cidList.length > 0) {
|
|
74
|
+
// found copies with cids
|
|
75
|
+
// so look up those cids
|
|
76
|
+
// convert to doenetMLs, and recurse on those doenetMLs
|
|
77
|
+
|
|
78
|
+
let { newDoenetMLs, newCids } = await cidsToDoenetMLs(cidList);
|
|
79
|
+
|
|
80
|
+
// check to see if got the cids requested
|
|
81
|
+
for (let [ind, cid] of cidList.entries()) {
|
|
82
|
+
if (newCids[ind] && newCids[ind].substring(0, cid.length) !== cid) {
|
|
83
|
+
return Promise.reject(
|
|
84
|
+
new Error(`Requested cid ${cid} but got back ${newCids[ind]}!`),
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let expectedN = cidList.length;
|
|
90
|
+
for (let ind = 0; ind < expectedN; ind++) {
|
|
91
|
+
let cid = newCids[ind];
|
|
92
|
+
if (!cid) {
|
|
93
|
+
// wasn't able to retrieve content
|
|
94
|
+
console.warn(`Unable to retrieve content with cid = ${cidList[ind]}`);
|
|
95
|
+
newDoenetMLs[ind] = "";
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// recurse to additional doenetMLs
|
|
100
|
+
let { fullSerializedComponents, allDoenetMLs: additionalDoenetMLs } =
|
|
101
|
+
await expandDoenetMLsToFullSerializedComponents({
|
|
102
|
+
doenetMLs: newDoenetMLs,
|
|
103
|
+
cids: newCids,
|
|
104
|
+
componentInfoObjects,
|
|
105
|
+
nPreviousDoenetMLs: nPreviousDoenetMLs + doenetMLs.length,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
allDoenetMLs.push(...additionalDoenetMLs);
|
|
109
|
+
|
|
110
|
+
for (let [ind, cid] of cidList.entries()) {
|
|
111
|
+
let serializedComponentsForCid = fullSerializedComponents[ind];
|
|
112
|
+
|
|
113
|
+
for (let originalCopyWithUri of cidComponents[cid]) {
|
|
114
|
+
if (originalCopyWithUri.children === undefined) {
|
|
115
|
+
originalCopyWithUri.children = [];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!originalCopyWithUri.doenetAttributes) {
|
|
119
|
+
originalCopyWithUri.doenetAttributes = {};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
originalCopyWithUri.doenetAttributes.copiedURI = true;
|
|
123
|
+
|
|
124
|
+
let originalChildren = JSON.parse(
|
|
125
|
+
JSON.stringify(serializedComponentsForCid),
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// remove blank string children
|
|
129
|
+
let nonBlankStringChildren = originalChildren.filter(
|
|
130
|
+
(x) => typeof x !== "string" || x.trim(),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
let haveSingleComponent =
|
|
134
|
+
nonBlankStringChildren.length === 1 &&
|
|
135
|
+
typeof nonBlankStringChildren[0] === "object";
|
|
136
|
+
|
|
137
|
+
let fromCopyFromURI =
|
|
138
|
+
originalCopyWithUri.doenetAttributes?.fromCopyFromURI;
|
|
139
|
+
|
|
140
|
+
if (fromCopyFromURI || haveSingleComponent) {
|
|
141
|
+
if (fromCopyFromURI && !haveSingleComponent) {
|
|
142
|
+
console.warn(
|
|
143
|
+
"ignoring copyFromURI as it was not a single component",
|
|
144
|
+
);
|
|
145
|
+
} else {
|
|
146
|
+
let comp = nonBlankStringChildren[0];
|
|
147
|
+
|
|
148
|
+
if (!comp.attributes) {
|
|
149
|
+
comp.attributes = {};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!originalCopyWithUri.doenetAttributes) {
|
|
153
|
+
originalCopyWithUri.doenetAttributes = {};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
originalCopyWithUri.doenetAttributes.keptNewNamespaceOfLastChild =
|
|
157
|
+
Boolean(comp.attributes.newNamespace?.primitive);
|
|
158
|
+
|
|
159
|
+
comp.attributes.newNamespace = { primitive: true };
|
|
160
|
+
|
|
161
|
+
originalCopyWithUri.children = [
|
|
162
|
+
comp,
|
|
163
|
+
...originalCopyWithUri.children,
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
// Note: name of last child will get changed by assignName (or be given unique name if no assignNames)
|
|
167
|
+
// however, when first creating component names, need to keep its original name in case nay of its children reference it
|
|
168
|
+
// When assignNames, such references will be converted to newly assigned names
|
|
169
|
+
originalCopyWithUri.doenetAttributes.nameFirstChildIndependently = true;
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
let extContent = {
|
|
173
|
+
componentType: "externalContent",
|
|
174
|
+
children: JSON.parse(JSON.stringify(serializedComponentsForCid)),
|
|
175
|
+
attributes: { newNamespace: { primitive: true } },
|
|
176
|
+
doenetAttributes: { createUniqueName: true },
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
originalCopyWithUri.children = [
|
|
180
|
+
extContent,
|
|
181
|
+
...originalCopyWithUri.children,
|
|
182
|
+
];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
cids,
|
|
190
|
+
fullSerializedComponents: arrayOfSerializedComponents,
|
|
191
|
+
allDoenetMLs,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function addDoenetMLIdToRange(serializedComponents, doenetMLId) {
|
|
196
|
+
for (let component of serializedComponents) {
|
|
197
|
+
if (component.range) {
|
|
198
|
+
component.range.doenetMLId = doenetMLId;
|
|
199
|
+
}
|
|
200
|
+
if (component.children) {
|
|
201
|
+
addDoenetMLIdToRange(component.children, doenetMLId);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function cidsToDoenetMLs(cids) {
|
|
207
|
+
let promises = [];
|
|
208
|
+
let newCids = cids;
|
|
209
|
+
|
|
210
|
+
for (let cid of cids) {
|
|
211
|
+
promises.push(retrieveTextFileForCid(cid, "doenet"));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return Promise.all(promises)
|
|
215
|
+
.then((newDoenetMLs) => {
|
|
216
|
+
// console.log({ newDoenetMLs, newCids })
|
|
217
|
+
return Promise.resolve({ newDoenetMLs, newCids });
|
|
218
|
+
})
|
|
219
|
+
.catch((err) => {
|
|
220
|
+
let message;
|
|
221
|
+
if (newCids.length === 1) {
|
|
222
|
+
message = `Could not retrieve cid ${newCids[0]}`;
|
|
223
|
+
} else {
|
|
224
|
+
message = `Could not retrieve cids ${newCids.join(",")}`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
message += ": " + err.message;
|
|
228
|
+
|
|
229
|
+
console.error(message);
|
|
230
|
+
|
|
231
|
+
return Promise.reject(new Error(message));
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function removeBlankStringChildren(
|
|
236
|
+
serializedComponents,
|
|
237
|
+
componentInfoObjects,
|
|
238
|
+
) {
|
|
239
|
+
for (let component of serializedComponents) {
|
|
240
|
+
if (component.children) {
|
|
241
|
+
let componentClass =
|
|
242
|
+
componentInfoObjects.allComponentClasses[component.componentType];
|
|
243
|
+
if (componentClass && !componentClass.includeBlankStringChildren) {
|
|
244
|
+
component.children = component.children.filter(
|
|
245
|
+
(x) => typeof x !== "string" || x.trim() !== "",
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
removeBlankStringChildren(component.children, componentInfoObjects);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// TODO: do we also need to remove blank string components
|
|
253
|
+
// from childrenForComponent of an attribute that is not yet a component?
|
|
254
|
+
for (let attrName in component.attributes) {
|
|
255
|
+
let comp = component.attributes[attrName].component;
|
|
256
|
+
if (comp && comp.children) {
|
|
257
|
+
removeBlankStringChildren([comp], componentInfoObjects);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function findContentCopies({ serializedComponents }) {
|
|
264
|
+
let cidComponents = {};
|
|
265
|
+
for (let serializedComponent of serializedComponents) {
|
|
266
|
+
if (serializedComponent.componentType === "copy") {
|
|
267
|
+
if (
|
|
268
|
+
serializedComponent.attributes &&
|
|
269
|
+
serializedComponent.attributes.uri
|
|
270
|
+
) {
|
|
271
|
+
let uri = serializedComponent.attributes.uri.primitive;
|
|
272
|
+
|
|
273
|
+
if (uri && uri.substring(0, 7).toLowerCase() === "doenet:") {
|
|
274
|
+
let result = uri.match(/[:&]cid=([^&]+)/i);
|
|
275
|
+
if (result) {
|
|
276
|
+
let cid = result[1];
|
|
277
|
+
if (cidComponents[cid] === undefined) {
|
|
278
|
+
cidComponents[cid] = [];
|
|
279
|
+
}
|
|
280
|
+
cidComponents[cid].push(serializedComponent);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
} else {
|
|
285
|
+
if (serializedComponent.children !== undefined) {
|
|
286
|
+
let results = findContentCopies({
|
|
287
|
+
serializedComponents: serializedComponent.children,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// append results on to cidComponents
|
|
291
|
+
for (let cid in results.cidComponents) {
|
|
292
|
+
if (cidComponents[cid] === undefined) {
|
|
293
|
+
cidComponents[cid] = [];
|
|
294
|
+
}
|
|
295
|
+
cidComponents[cid].push(...results.cidComponents[cid]);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return { cidComponents };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function addDocumentIfItsMissing(serializedComponents) {
|
|
304
|
+
if (
|
|
305
|
+
serializedComponents.length !== 1 ||
|
|
306
|
+
serializedComponents[0].componentType !== "document"
|
|
307
|
+
) {
|
|
308
|
+
let components = serializedComponents.splice(0);
|
|
309
|
+
serializedComponents.push({
|
|
310
|
+
componentType: "document",
|
|
311
|
+
children: components,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function substituteAttributeDeprecations(serializedComponents) {
|
|
317
|
+
// Note: attributes are XML attributes
|
|
318
|
+
// (which are called props at this point due to parser but will be renamed attributes later)
|
|
319
|
+
// that are entered as attributes in the component tag
|
|
320
|
+
|
|
321
|
+
// Note: use lower case for keys
|
|
322
|
+
let deprecatedAttributeSubstitutions = {
|
|
323
|
+
tname: "target",
|
|
324
|
+
triggerwithtnames: "triggerWith",
|
|
325
|
+
updatewithtname: "updateWith",
|
|
326
|
+
paginatortname: "paginator",
|
|
327
|
+
randomizeorder: "shuffleOrder",
|
|
328
|
+
copytarget: "copySource",
|
|
329
|
+
triggerwithtargets: "triggerWith",
|
|
330
|
+
triggerwhentargetsclicked: "triggerWhenObjectsClicked",
|
|
331
|
+
fortarget: "forObject",
|
|
332
|
+
targetattributestoignore: "sourceAttributesToIgnore",
|
|
333
|
+
targetattributestoignorerecursively: "sourceAttributesToIgnoreRecursively",
|
|
334
|
+
targetsareresponses: "sourcesAreResponses",
|
|
335
|
+
updatewithtarget: "updateWith",
|
|
336
|
+
targetsarefunctionsymbols: "sourcesAreFunctionSymbols",
|
|
337
|
+
selectforvariantnames: "selectForVariants",
|
|
338
|
+
numberdecimals: "numDecimals",
|
|
339
|
+
numberdigits: "numDigits",
|
|
340
|
+
ndimensions: "numDimensions",
|
|
341
|
+
ninputs: "numInputs",
|
|
342
|
+
noutputs: "numOutputs",
|
|
343
|
+
niterates: "numIterates",
|
|
344
|
+
nrows: "numRows",
|
|
345
|
+
ncolumns: "numColumns",
|
|
346
|
+
nvertices: "numVertices",
|
|
347
|
+
npoints: "numPoints",
|
|
348
|
+
nvariants: "numVariants",
|
|
349
|
+
nsides: "numSides",
|
|
350
|
+
niterationsrequired: "numIterationsRequired",
|
|
351
|
+
numberofsamples: "numSamples",
|
|
352
|
+
numbertoselect: "numToSelect",
|
|
353
|
+
nawardscredited: "numAwardsCredited",
|
|
354
|
+
maximumnumber: "maxNumber",
|
|
355
|
+
nsignerrorsmatched: "numSignErrorsMatched",
|
|
356
|
+
nperiodicsetmatchesrequired: "numPeriodicSetMatchesRequired",
|
|
357
|
+
npages: "numPages",
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
// Note: use lower case
|
|
361
|
+
let deprecatedAttributeDeletions = new Set([
|
|
362
|
+
"suppressautoname",
|
|
363
|
+
"suppressautonumber",
|
|
364
|
+
"targetattributestoignorerecursively",
|
|
365
|
+
"sourceattributestoignorerecursively",
|
|
366
|
+
"showlabel",
|
|
367
|
+
]);
|
|
368
|
+
|
|
369
|
+
// Note: use lower case for keys
|
|
370
|
+
let deprecatedAttributeSubstitutionsComponentSpecific = {
|
|
371
|
+
copy: {
|
|
372
|
+
target: "source",
|
|
373
|
+
tname: "source",
|
|
374
|
+
},
|
|
375
|
+
collect: {
|
|
376
|
+
target: "source",
|
|
377
|
+
tname: "source",
|
|
378
|
+
},
|
|
379
|
+
summarystatistics: {
|
|
380
|
+
target: "source",
|
|
381
|
+
},
|
|
382
|
+
answer: {
|
|
383
|
+
maximumnumberofattempts: "maxNumAttempts",
|
|
384
|
+
},
|
|
385
|
+
bestfitline: {
|
|
386
|
+
points: "data",
|
|
387
|
+
},
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// use lower case
|
|
391
|
+
let deprecatedAttributeDeletionsComponentSpecific = {
|
|
392
|
+
textinput: ["size"],
|
|
393
|
+
constraintogrid: ["ignoregraphbounds"],
|
|
394
|
+
attracttogrid: ["ignoregraphbounds"],
|
|
395
|
+
constraints: ["baseongraph"],
|
|
396
|
+
graph: ["xlabel", "ylabel"],
|
|
397
|
+
conditionalcontent: ["maximumnumbertoshow"],
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
for (let component of serializedComponents) {
|
|
401
|
+
if (typeof component !== "object") {
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (component.props) {
|
|
406
|
+
let cType = component.componentType;
|
|
407
|
+
let cTypeLower = cType.toLowerCase();
|
|
408
|
+
let typeSpecificDeps =
|
|
409
|
+
deprecatedAttributeSubstitutionsComponentSpecific[cTypeLower];
|
|
410
|
+
if (!typeSpecificDeps) {
|
|
411
|
+
typeSpecificDeps = {};
|
|
412
|
+
}
|
|
413
|
+
let typeSpecificDeletions =
|
|
414
|
+
deprecatedAttributeDeletionsComponentSpecific[cTypeLower];
|
|
415
|
+
if (!typeSpecificDeletions) {
|
|
416
|
+
typeSpecificDeletions = [];
|
|
417
|
+
}
|
|
418
|
+
let retry = true;
|
|
419
|
+
while (retry) {
|
|
420
|
+
retry = false;
|
|
421
|
+
for (let prop in component.props) {
|
|
422
|
+
let propLower = prop.toLowerCase();
|
|
423
|
+
if (propLower in typeSpecificDeps) {
|
|
424
|
+
let newProp = typeSpecificDeps[propLower];
|
|
425
|
+
|
|
426
|
+
console.warn(
|
|
427
|
+
`Attribute ${prop} of component type ${cType} is deprecated. Use ${newProp} instead.`,
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
component.props[newProp] = component.props[prop];
|
|
431
|
+
delete component.props[prop];
|
|
432
|
+
|
|
433
|
+
// since modified object over which are looping
|
|
434
|
+
// break out of loop and start over
|
|
435
|
+
retry = true;
|
|
436
|
+
break;
|
|
437
|
+
} else if (propLower in deprecatedAttributeSubstitutions) {
|
|
438
|
+
let newProp = deprecatedAttributeSubstitutions[propLower];
|
|
439
|
+
|
|
440
|
+
console.warn(
|
|
441
|
+
`Attribute ${prop} is deprecated. Use ${newProp} instead.`,
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
component.props[newProp] = component.props[prop];
|
|
445
|
+
delete component.props[prop];
|
|
446
|
+
|
|
447
|
+
// since modified object over which are looping
|
|
448
|
+
// break out of loop and start over
|
|
449
|
+
retry = true;
|
|
450
|
+
break;
|
|
451
|
+
} else if (typeSpecificDeletions.includes(propLower)) {
|
|
452
|
+
console.warn(
|
|
453
|
+
`Attribute ${prop} of component type ${cType} is deprecated. It is ignored.`,
|
|
454
|
+
);
|
|
455
|
+
delete component.props[prop];
|
|
456
|
+
|
|
457
|
+
// since modified object over which are looping
|
|
458
|
+
// break out of loop and start over
|
|
459
|
+
retry = true;
|
|
460
|
+
break;
|
|
461
|
+
} else if (deprecatedAttributeDeletions.has(propLower)) {
|
|
462
|
+
console.warn(`Attribute ${prop} is deprecated. It is ignored.`);
|
|
463
|
+
delete component.props[prop];
|
|
464
|
+
|
|
465
|
+
// since modified object over which are looping
|
|
466
|
+
// break out of loop and start over
|
|
467
|
+
retry = true;
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (component.children) {
|
|
475
|
+
substituteAttributeDeprecations(component.children);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
export const deprecatedPropertySubstitutions = {
|
|
481
|
+
maximumNumberOfAttempts: "maxNumAttempts",
|
|
482
|
+
numberFeedbacks: "numFeedbacks",
|
|
483
|
+
numberOfAttemptsLeft: "numAttemptsLeft",
|
|
484
|
+
nSubmissions: "numSubmissions",
|
|
485
|
+
nSubmittedResponses: "numSubmittedResponses",
|
|
486
|
+
nAwardsCredited: "numAwardsCredited",
|
|
487
|
+
numberChoices: "numChoices",
|
|
488
|
+
numberMinima: "numMinima",
|
|
489
|
+
numberMaxima: "numMaxima",
|
|
490
|
+
numberExtrema: "numExtrema",
|
|
491
|
+
numberDecimals: "numDecimals",
|
|
492
|
+
numberDigits: "numDigits",
|
|
493
|
+
numberOfSamples: "numSamples",
|
|
494
|
+
numberToSelect: "numToSelect",
|
|
495
|
+
numberSolutions: "numSolutions",
|
|
496
|
+
maximumNumber: "maxNumber",
|
|
497
|
+
nVertices: "numVertices",
|
|
498
|
+
nPoints: "numPoints",
|
|
499
|
+
nSignErrorsMatched: "numSignErrorsMatched",
|
|
500
|
+
nPeriodicSetMatchesRequired: "numPeriodicSetMatchesRequired",
|
|
501
|
+
nValues: "numValues",
|
|
502
|
+
nResponses: "numResponses",
|
|
503
|
+
nControls: "numControls",
|
|
504
|
+
nThroughPoints: "numThroughPoints",
|
|
505
|
+
nComponents: "numComponents",
|
|
506
|
+
nChildrenToRender: "numChildrenToRender",
|
|
507
|
+
nSelectedIndices: "numSelectedIndices",
|
|
508
|
+
nDimensions: "numDimensions",
|
|
509
|
+
nCases: "numCases",
|
|
510
|
+
nDiscretizationPoints: "numDiscretizationPoints",
|
|
511
|
+
nXCriticalPoints: "numXCriticalPoints",
|
|
512
|
+
nYCriticalPoints: "numYCriticalPoints",
|
|
513
|
+
nCurvatureChangePoints: "numCurvatureChangePoints",
|
|
514
|
+
nScoredDescendants: "numScoredDescendants",
|
|
515
|
+
nInputs: "numInputs",
|
|
516
|
+
nOutputs: "numOutputs",
|
|
517
|
+
nIterates: "numIterates",
|
|
518
|
+
nDerivatives: "numDerivatives",
|
|
519
|
+
nSources: "numSources",
|
|
520
|
+
nMatches: "numMatches",
|
|
521
|
+
nRows: "numRows",
|
|
522
|
+
nColumns: "numColumns",
|
|
523
|
+
nPages: "numPages",
|
|
524
|
+
nOffsets: "numOffsets",
|
|
525
|
+
nVariants: "numVariants",
|
|
526
|
+
nSides: "numSides",
|
|
527
|
+
nItems: "numItems",
|
|
528
|
+
nLists: "numLists",
|
|
529
|
+
nIterationsRequired: "numIterationsRequired",
|
|
530
|
+
nGradedVertices: "numGradedVertices",
|
|
531
|
+
nCorrectVertices: "numCorrectVertices",
|
|
532
|
+
nIterateValues: "numIterateValues",
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
const deprecatedPropertySubstitutionsLowerCase = {};
|
|
536
|
+
Object.keys(deprecatedPropertySubstitutions).forEach(
|
|
537
|
+
(key) =>
|
|
538
|
+
(deprecatedPropertySubstitutionsLowerCase[key.toLowerCase()] =
|
|
539
|
+
deprecatedPropertySubstitutions[key]),
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
function substitutePropertyDeprecations(serializedComponents) {
|
|
543
|
+
// Note: properties are public state variables that are referenced
|
|
544
|
+
// either using dot notation in a source/copysource or in a prop/copyprop
|
|
545
|
+
// but will be exclusively in prop by this point
|
|
546
|
+
|
|
547
|
+
for (let component of serializedComponents) {
|
|
548
|
+
if (typeof component !== "object") {
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
let propName = component.attributes?.prop?.primitive;
|
|
553
|
+
|
|
554
|
+
if (propName) {
|
|
555
|
+
let propNameLower = propName.toLowerCase();
|
|
556
|
+
|
|
557
|
+
if (propNameLower in deprecatedPropertySubstitutionsLowerCase) {
|
|
558
|
+
let newProp = deprecatedPropertySubstitutionsLowerCase[propNameLower];
|
|
559
|
+
console.warn(
|
|
560
|
+
`Property ${propName} is deprecated. Use ${newProp} instead.`,
|
|
561
|
+
);
|
|
562
|
+
|
|
563
|
+
component.attributes.prop.primitive = newProp;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (component.children) {
|
|
568
|
+
substitutePropertyDeprecations(component.children);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function temporarilyRenameSourceBackToTarget(serializedComponents) {
|
|
574
|
+
// Note: use lower case for keys
|
|
575
|
+
let backwardsDeprecatedAttributeSubstitutions = {
|
|
576
|
+
copysource: "copyTarget",
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
// Note: use lower case for keys
|
|
580
|
+
let backwardsDeprecatedAttributeSubstitutionsComponentSpecific = {
|
|
581
|
+
copy: {
|
|
582
|
+
source: "target",
|
|
583
|
+
},
|
|
584
|
+
collect: {
|
|
585
|
+
source: "target",
|
|
586
|
+
},
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
for (let component of serializedComponents) {
|
|
590
|
+
if (typeof component !== "object") {
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (component.props) {
|
|
595
|
+
let cType = component.componentType;
|
|
596
|
+
let typeSpecificDeps =
|
|
597
|
+
backwardsDeprecatedAttributeSubstitutionsComponentSpecific[
|
|
598
|
+
cType.toLowerCase()
|
|
599
|
+
];
|
|
600
|
+
if (!typeSpecificDeps) {
|
|
601
|
+
typeSpecificDeps = {};
|
|
602
|
+
}
|
|
603
|
+
let retry = true;
|
|
604
|
+
while (retry) {
|
|
605
|
+
retry = false;
|
|
606
|
+
for (let prop in component.props) {
|
|
607
|
+
let propLower = prop.toLowerCase();
|
|
608
|
+
if (propLower in typeSpecificDeps) {
|
|
609
|
+
let newProp = typeSpecificDeps[propLower];
|
|
610
|
+
|
|
611
|
+
component.props[newProp] = component.props[prop];
|
|
612
|
+
delete component.props[prop];
|
|
613
|
+
|
|
614
|
+
// since modified object over which are looping
|
|
615
|
+
// break out of loop and start over
|
|
616
|
+
retry = true;
|
|
617
|
+
break;
|
|
618
|
+
} else if (propLower in backwardsDeprecatedAttributeSubstitutions) {
|
|
619
|
+
let newProp = backwardsDeprecatedAttributeSubstitutions[propLower];
|
|
620
|
+
|
|
621
|
+
component.props[newProp] = component.props[prop];
|
|
622
|
+
delete component.props[prop];
|
|
623
|
+
|
|
624
|
+
// since modified object over which are looping
|
|
625
|
+
// break out of loop and start over
|
|
626
|
+
retry = true;
|
|
627
|
+
break;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (component.children) {
|
|
634
|
+
temporarilyRenameSourceBackToTarget(component.children);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function cleanIfHaveJustDocument(serializedComponents) {
|
|
640
|
+
let componentsWithoutBlankStrings = serializedComponents.filter(
|
|
641
|
+
(x) => typeof x !== "string" || x.trim() !== "",
|
|
642
|
+
);
|
|
643
|
+
|
|
644
|
+
if (
|
|
645
|
+
componentsWithoutBlankStrings.length === 1 &&
|
|
646
|
+
componentsWithoutBlankStrings[0].componentType === "document"
|
|
647
|
+
) {
|
|
648
|
+
return componentsWithoutBlankStrings;
|
|
649
|
+
} else {
|
|
650
|
+
return serializedComponents;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
function correctComponentTypeCapitalization(
|
|
655
|
+
serializedComponents,
|
|
656
|
+
componentTypeLowerCaseMapping,
|
|
657
|
+
) {
|
|
658
|
+
//special case for macros before application
|
|
659
|
+
// componentTypeLowerCaseMapping["macro"] = "macro";
|
|
660
|
+
for (let component of serializedComponents) {
|
|
661
|
+
if (typeof component !== "object") {
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
let componentTypeFixed =
|
|
666
|
+
componentTypeLowerCaseMapping[component.componentType.toLowerCase()];
|
|
667
|
+
|
|
668
|
+
if (componentTypeFixed) {
|
|
669
|
+
component.componentType = componentTypeFixed;
|
|
670
|
+
} else {
|
|
671
|
+
throw Error(
|
|
672
|
+
`Invalid component type${indexRangeString(component)}: ${
|
|
673
|
+
component.componentType
|
|
674
|
+
}`,
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
if (component.children) {
|
|
679
|
+
correctComponentTypeCapitalization(
|
|
680
|
+
component.children,
|
|
681
|
+
componentTypeLowerCaseMapping,
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function copyTargetOrFromURIAttributeCreatesCopyComponent(
|
|
688
|
+
serializedComponents,
|
|
689
|
+
isCompositeComponent,
|
|
690
|
+
) {
|
|
691
|
+
for (let component of serializedComponents) {
|
|
692
|
+
if (component.props) {
|
|
693
|
+
let foundCopyTarget = false;
|
|
694
|
+
let foundCopyFromURI = false;
|
|
695
|
+
let foundAssignNames = false;
|
|
696
|
+
let originalType = component.componentType;
|
|
697
|
+
let haveComposite = isCompositeComponent({
|
|
698
|
+
componentType: originalType,
|
|
699
|
+
includeNonStandard: false,
|
|
700
|
+
});
|
|
701
|
+
let haveAnyComposite = isCompositeComponent({
|
|
702
|
+
componentType: originalType,
|
|
703
|
+
includeNonStandard: true,
|
|
704
|
+
});
|
|
705
|
+
for (let prop of Object.keys(component.props)) {
|
|
706
|
+
let lowerCaseProp = prop.toLowerCase();
|
|
707
|
+
if (lowerCaseProp === "copytarget") {
|
|
708
|
+
if (foundCopyTarget) {
|
|
709
|
+
throw Error(
|
|
710
|
+
`Cannot repeat attribute ${prop}. Found in component type ${originalType}${indexRangeString(
|
|
711
|
+
component,
|
|
712
|
+
)}`,
|
|
713
|
+
);
|
|
714
|
+
} else if (foundCopyFromURI) {
|
|
715
|
+
throw Error(
|
|
716
|
+
`Cannot combine copyTarget and copyFromURI attribiutes. For in component of type ${originalType}${indexRangeString(
|
|
717
|
+
component,
|
|
718
|
+
)}`,
|
|
719
|
+
);
|
|
720
|
+
} else if (foundAssignNames) {
|
|
721
|
+
if (haveAnyComposite) {
|
|
722
|
+
throw Error(
|
|
723
|
+
`A component of type ${originalType} cannot have both assignNames and copyTarget. Found${indexRangeString(
|
|
724
|
+
component,
|
|
725
|
+
)}.`,
|
|
726
|
+
);
|
|
727
|
+
} else {
|
|
728
|
+
throw Error(
|
|
729
|
+
`Invalid attribute assignNames for component of type ${originalType}${indexRangeString(
|
|
730
|
+
component,
|
|
731
|
+
)}`,
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
foundCopyTarget = true;
|
|
736
|
+
if (!component.doenetAttributes) {
|
|
737
|
+
component.doenetAttributes = {};
|
|
738
|
+
}
|
|
739
|
+
if (!haveComposite) {
|
|
740
|
+
component.props.createComponentOfType = originalType;
|
|
741
|
+
component.doenetAttributes.nameBecomesAssignNames = true;
|
|
742
|
+
}
|
|
743
|
+
component.componentType = "copy";
|
|
744
|
+
component.props.target = component.props[prop];
|
|
745
|
+
if (typeof component.props.target !== "string") {
|
|
746
|
+
throw Error(
|
|
747
|
+
`Must specify value for copyTarget. Found in component of type ${originalType}${indexRangeString(
|
|
748
|
+
component,
|
|
749
|
+
)}`,
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
delete component.props[prop];
|
|
753
|
+
|
|
754
|
+
component.doenetAttributes.fromCopyTarget = true;
|
|
755
|
+
component.doenetAttributes.createNameFromComponentType = originalType;
|
|
756
|
+
component.props.assignNamesSkip = "1";
|
|
757
|
+
} else if (lowerCaseProp === "copyfromuri") {
|
|
758
|
+
if (foundCopyFromURI) {
|
|
759
|
+
throw Error(
|
|
760
|
+
`Cannot repeat attribute ${prop}. Found in component type ${originalType}${indexRangeString(
|
|
761
|
+
component,
|
|
762
|
+
)}`,
|
|
763
|
+
);
|
|
764
|
+
} else if (foundCopyTarget) {
|
|
765
|
+
throw Error(
|
|
766
|
+
`Cannot combine copyTarget and copyFromURI attribiutes. For in component of type ${originalType}${indexRangeString(
|
|
767
|
+
component,
|
|
768
|
+
)}`,
|
|
769
|
+
);
|
|
770
|
+
} else if (foundAssignNames) {
|
|
771
|
+
if (haveAnyComposite) {
|
|
772
|
+
throw Error(
|
|
773
|
+
`A component of type ${originalType} cannot have both assignNames and copyFromURI. Found${indexRangeString(
|
|
774
|
+
component,
|
|
775
|
+
)}.`,
|
|
776
|
+
);
|
|
777
|
+
} else {
|
|
778
|
+
throw Error(
|
|
779
|
+
`Invalid attribute assignNames for component of type ${originalType}${indexRangeString(
|
|
780
|
+
component,
|
|
781
|
+
)}`,
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
foundCopyFromURI = true;
|
|
786
|
+
if (!component.doenetAttributes) {
|
|
787
|
+
component.doenetAttributes = {};
|
|
788
|
+
}
|
|
789
|
+
if (!haveComposite) {
|
|
790
|
+
component.props.createComponentOfType = originalType;
|
|
791
|
+
component.doenetAttributes.nameBecomesAssignNames = true;
|
|
792
|
+
}
|
|
793
|
+
component.componentType = "copy";
|
|
794
|
+
component.props.uri = component.props[prop];
|
|
795
|
+
if (typeof component.props.uri !== "string") {
|
|
796
|
+
throw Error(
|
|
797
|
+
`Must specify value for copyFromURI. Found in component of type ${originalType}${indexRangeString(
|
|
798
|
+
component,
|
|
799
|
+
)}`,
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
delete component.props[prop];
|
|
803
|
+
component.doenetAttributes.fromCopyFromURI = true;
|
|
804
|
+
component.doenetAttributes.createNameFromComponentType = originalType;
|
|
805
|
+
component.props.assignNamesSkip = "1";
|
|
806
|
+
} else if (lowerCaseProp === "assignnames" && !haveComposite) {
|
|
807
|
+
if (foundCopyTarget || foundCopyFromURI) {
|
|
808
|
+
if (haveAnyComposite) {
|
|
809
|
+
throw Error(
|
|
810
|
+
`A component of type ${originalType} cannot have both assignNames and copyTarget. Found${indexRangeString(
|
|
811
|
+
component,
|
|
812
|
+
)}.`,
|
|
813
|
+
);
|
|
814
|
+
} else {
|
|
815
|
+
throw Error(
|
|
816
|
+
`Invalid attribute assignNames for component of type ${originalType}${indexRangeString(
|
|
817
|
+
component,
|
|
818
|
+
)}`,
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
foundAssignNames = true;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
if (foundCopyTarget) {
|
|
827
|
+
// give error if have prop name "prop"
|
|
828
|
+
// after that rename copyProp to "prop"
|
|
829
|
+
for (let prop of Object.keys(component.props)) {
|
|
830
|
+
let lowerCaseProp = prop.toLowerCase();
|
|
831
|
+
if (lowerCaseProp === "prop") {
|
|
832
|
+
throw Error(
|
|
833
|
+
`Invalid attribute prop for component of type ${originalType}${indexRangeString(
|
|
834
|
+
component,
|
|
835
|
+
)}`,
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
let foundCopyProp = false;
|
|
840
|
+
for (let prop of Object.keys(component.props)) {
|
|
841
|
+
let lowerCaseProp = prop.toLowerCase();
|
|
842
|
+
if (lowerCaseProp === "copyprop") {
|
|
843
|
+
if (foundCopyProp) {
|
|
844
|
+
throw Error(
|
|
845
|
+
`Cannot repeat attribute ${prop}. Found in component type ${originalType}${indexRangeString(
|
|
846
|
+
component,
|
|
847
|
+
)}`,
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
component.props.prop = component.props[prop];
|
|
851
|
+
delete component.props[prop];
|
|
852
|
+
foundCopyProp = true;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (component.children) {
|
|
859
|
+
copyTargetOrFromURIAttributeCreatesCopyComponent(
|
|
860
|
+
component.children,
|
|
861
|
+
isCompositeComponent,
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function breakUpTargetIntoPropsAndIndices(
|
|
868
|
+
serializedComponents,
|
|
869
|
+
componentInfoObjects,
|
|
870
|
+
ancestorString = "",
|
|
871
|
+
) {
|
|
872
|
+
for (let [component_ind, component] of serializedComponents.entries()) {
|
|
873
|
+
// Note: do not do this for collect, as this dot notation would be confusing for collect
|
|
874
|
+
|
|
875
|
+
if (
|
|
876
|
+
component.props &&
|
|
877
|
+
["copy", "updateValue", "animateFromSequence"].includes(
|
|
878
|
+
component.componentType,
|
|
879
|
+
)
|
|
880
|
+
) {
|
|
881
|
+
let targetPropName;
|
|
882
|
+
let sourceName;
|
|
883
|
+
let componentIndex;
|
|
884
|
+
let componentAttributes;
|
|
885
|
+
let propArray;
|
|
886
|
+
let subNames;
|
|
887
|
+
|
|
888
|
+
let originalSource;
|
|
889
|
+
|
|
890
|
+
for (let prop of Object.keys(component.props)) {
|
|
891
|
+
let lowerCaseProp = prop.toLowerCase();
|
|
892
|
+
if (lowerCaseProp === "target") {
|
|
893
|
+
if (targetPropName) {
|
|
894
|
+
throw Error(
|
|
895
|
+
`Cannot repeat attribute ${prop}. Found in component type ${
|
|
896
|
+
component.componentType
|
|
897
|
+
}${indexRangeString(component)}`,
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
targetPropName = prop;
|
|
902
|
+
originalSource = component.props[prop];
|
|
903
|
+
|
|
904
|
+
let sourcePiecesResult = buildSourcePieces(originalSource, true);
|
|
905
|
+
|
|
906
|
+
if (
|
|
907
|
+
sourcePiecesResult.success &&
|
|
908
|
+
sourcePiecesResult.matchLength === originalSource.length
|
|
909
|
+
) {
|
|
910
|
+
sourceName = sourcePiecesResult.sourceName;
|
|
911
|
+
componentIndex = sourcePiecesResult.componentIndex;
|
|
912
|
+
componentAttributes = sourcePiecesResult.componentAttributes;
|
|
913
|
+
propArray = sourcePiecesResult.propArray;
|
|
914
|
+
subNames = sourcePiecesResult.subNames;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
if (targetPropName && sourceName) {
|
|
920
|
+
if (componentIndex || componentAttributes || propArray.length > 0) {
|
|
921
|
+
// found an extended target
|
|
922
|
+
|
|
923
|
+
if (component.attributes.prop) {
|
|
924
|
+
throw Error(
|
|
925
|
+
`Cannot combine the prop attribute with an extended source attribute. Found in component type ${
|
|
926
|
+
component.componentType
|
|
927
|
+
}${indexRangeString(component)}`,
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
if (component.attributes.propIndex) {
|
|
931
|
+
throw Error(
|
|
932
|
+
`Cannot combine the propIndex attribute with an extended source attribute. Found in component type ${
|
|
933
|
+
component.componentType
|
|
934
|
+
}${indexRangeString(component)}`,
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
if (component.attributes.componentIndex) {
|
|
938
|
+
throw Error(
|
|
939
|
+
`Cannot combine the componentIndex attribute with an extended source attribute. Found in component type ${
|
|
940
|
+
component.componentType
|
|
941
|
+
}${indexRangeString(component)}`,
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
let componentResult = createComponentFromExtendedSource({
|
|
946
|
+
sourceName,
|
|
947
|
+
componentIndex,
|
|
948
|
+
subNames,
|
|
949
|
+
componentAttributes,
|
|
950
|
+
propArray,
|
|
951
|
+
componentInfoObjects,
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
if (componentResult.success) {
|
|
955
|
+
let newComponent = componentResult.newComponent;
|
|
956
|
+
|
|
957
|
+
if (component.componentType === "copy") {
|
|
958
|
+
// assign new componentType, attributes, and doenetAttributes
|
|
959
|
+
// to original component
|
|
960
|
+
delete component.props[targetPropName];
|
|
961
|
+
Object.assign(component.attributes, newComponent.attributes);
|
|
962
|
+
if (!component.doenetAttributes) {
|
|
963
|
+
component.doenetAttributes = {};
|
|
964
|
+
}
|
|
965
|
+
Object.assign(
|
|
966
|
+
component.doenetAttributes,
|
|
967
|
+
newComponent.doenetAttributes,
|
|
968
|
+
);
|
|
969
|
+
if (!component.doenetAttributes.createNameFromComponentType) {
|
|
970
|
+
component.doenetAttributes.createNameFromComponentType =
|
|
971
|
+
component.componentType;
|
|
972
|
+
}
|
|
973
|
+
component.componentType = newComponent.componentType;
|
|
974
|
+
|
|
975
|
+
if (
|
|
976
|
+
propArray.length === 0 &&
|
|
977
|
+
!(component.attributes.prop || component.attributes.propIndex)
|
|
978
|
+
) {
|
|
979
|
+
component.doenetAttributes.isPlainCopy = true;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
if (newComponent.children) {
|
|
983
|
+
component.children = newComponent.children;
|
|
984
|
+
}
|
|
985
|
+
} else {
|
|
986
|
+
// have updateValue or animateFromSequence
|
|
987
|
+
if (newComponent.componentType === "copy") {
|
|
988
|
+
// if the new component created was a copy
|
|
989
|
+
// then we can just add the attributes to the original component
|
|
990
|
+
|
|
991
|
+
// assign new componentType, attributes, and doenetAttributes
|
|
992
|
+
// to original component
|
|
993
|
+
delete component.props[targetPropName];
|
|
994
|
+
Object.assign(component.attributes, newComponent.attributes);
|
|
995
|
+
if (!component.doenetAttributes) {
|
|
996
|
+
component.doenetAttributes = {};
|
|
997
|
+
}
|
|
998
|
+
Object.assign(
|
|
999
|
+
component.doenetAttributes,
|
|
1000
|
+
newComponent.doenetAttributes,
|
|
1001
|
+
);
|
|
1002
|
+
} else {
|
|
1003
|
+
// if the new component created was an extract
|
|
1004
|
+
// then wrap the extract in a setup and append
|
|
1005
|
+
// and modify the updateValue/animateFromSequence to point to the extract
|
|
1006
|
+
|
|
1007
|
+
let longNameId =
|
|
1008
|
+
"fromExtendedSource" + ancestorString + "|" + component_ind;
|
|
1009
|
+
let nameForExtract = createUniqueName("extract", longNameId);
|
|
1010
|
+
newComponent.doenetAttributes.prescribedName = nameForExtract;
|
|
1011
|
+
newComponent.doenetAttributes.createdFromMacro = true;
|
|
1012
|
+
|
|
1013
|
+
let setupComponent = {
|
|
1014
|
+
componentType: "setup",
|
|
1015
|
+
children: [newComponent],
|
|
1016
|
+
doenetAttributes: { createdFromMacro: true },
|
|
1017
|
+
};
|
|
1018
|
+
serializedComponents.push(setupComponent);
|
|
1019
|
+
|
|
1020
|
+
delete component.props[targetPropName];
|
|
1021
|
+
|
|
1022
|
+
if (!component.doenetAttributes) {
|
|
1023
|
+
component.doenetAttributes = {};
|
|
1024
|
+
}
|
|
1025
|
+
component.doenetAttributes.target = nameForExtract;
|
|
1026
|
+
component.doenetAttributes.allowDoubleUnderscoreTarget = true;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
} else {
|
|
1030
|
+
if (component.componentType === "copy") {
|
|
1031
|
+
console.warn(`invalid copy source: ${originalSource}`);
|
|
1032
|
+
} else {
|
|
1033
|
+
console.warn(`invalid target: ${originalSource}`);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
} else {
|
|
1037
|
+
// have copy with just a simple target prop that is a targetName
|
|
1038
|
+
if (
|
|
1039
|
+
component.componentType === "copy" &&
|
|
1040
|
+
!(component.attributes.prop || component.attributes.propIndex)
|
|
1041
|
+
) {
|
|
1042
|
+
if (!component.doenetAttributes) {
|
|
1043
|
+
component.doenetAttributes = {};
|
|
1044
|
+
}
|
|
1045
|
+
component.doenetAttributes.isPlainCopy = true;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
if (component.children) {
|
|
1052
|
+
breakUpTargetIntoPropsAndIndices(
|
|
1053
|
+
component.children,
|
|
1054
|
+
componentInfoObjects,
|
|
1055
|
+
ancestorString + "|" + component_ind,
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
function createAttributesFromProps(serializedComponents, componentInfoObjects) {
|
|
1062
|
+
for (let component of serializedComponents) {
|
|
1063
|
+
if (typeof component !== "object") {
|
|
1064
|
+
continue;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
let componentClass =
|
|
1068
|
+
componentInfoObjects.allComponentClasses[component.componentType];
|
|
1069
|
+
let classAttributes = componentClass.createAttributesObject();
|
|
1070
|
+
|
|
1071
|
+
let attributeLowerCaseMapping = {};
|
|
1072
|
+
|
|
1073
|
+
for (let propName in classAttributes) {
|
|
1074
|
+
attributeLowerCaseMapping[propName.toLowerCase()] = propName;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
let attributes = {};
|
|
1078
|
+
|
|
1079
|
+
// if there are any props of json that match attributes for component class
|
|
1080
|
+
// create the specified components or primitives
|
|
1081
|
+
|
|
1082
|
+
let originalComponentProps = Object.assign({}, component.props);
|
|
1083
|
+
if (component.props) {
|
|
1084
|
+
for (let prop in component.props) {
|
|
1085
|
+
let propName = attributeLowerCaseMapping[prop.toLowerCase()];
|
|
1086
|
+
let attrObj = classAttributes[propName];
|
|
1087
|
+
if (attrObj) {
|
|
1088
|
+
if (propName in attributes) {
|
|
1089
|
+
throw Error(
|
|
1090
|
+
`Cannot repeat attribute ${propName}. Found in component type ${
|
|
1091
|
+
component.componentType
|
|
1092
|
+
}${indexRangeString(component)}`,
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
attributes[propName] = componentFromAttribute({
|
|
1097
|
+
attrObj,
|
|
1098
|
+
value: component.props[prop],
|
|
1099
|
+
originalComponentProps,
|
|
1100
|
+
componentInfoObjects,
|
|
1101
|
+
});
|
|
1102
|
+
delete component.props[prop];
|
|
1103
|
+
} else if (
|
|
1104
|
+
!["name", "assignnames", "target"].includes(prop.toLowerCase())
|
|
1105
|
+
) {
|
|
1106
|
+
if (componentClass.acceptAnyAttribute) {
|
|
1107
|
+
attributes[prop] = componentFromAttribute({
|
|
1108
|
+
value: component.props[prop],
|
|
1109
|
+
originalComponentProps,
|
|
1110
|
+
componentInfoObjects,
|
|
1111
|
+
});
|
|
1112
|
+
delete component.props[prop];
|
|
1113
|
+
} else {
|
|
1114
|
+
throw Error(
|
|
1115
|
+
`Invalid attribute ${prop} for component of type ${
|
|
1116
|
+
component.componentType
|
|
1117
|
+
}${indexRangeString(component)}`,
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// if there are any primitive attributes that define a default value
|
|
1125
|
+
// but were not specified via props, add them with their default value
|
|
1126
|
+
|
|
1127
|
+
for (let attrName in classAttributes) {
|
|
1128
|
+
let attrObj = classAttributes[attrName];
|
|
1129
|
+
|
|
1130
|
+
if (
|
|
1131
|
+
attrObj.createPrimitiveOfType &&
|
|
1132
|
+
"defaultPrimitiveValue" in attrObj &&
|
|
1133
|
+
!(attrName in attributes)
|
|
1134
|
+
) {
|
|
1135
|
+
attributes[attrName] = componentFromAttribute({
|
|
1136
|
+
attrObj,
|
|
1137
|
+
originalComponentProps,
|
|
1138
|
+
value: attrObj.defaultPrimitiveValue.toString(),
|
|
1139
|
+
componentInfoObjects,
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
component.attributes = attributes;
|
|
1145
|
+
|
|
1146
|
+
//recurse on children
|
|
1147
|
+
if (component.children !== undefined) {
|
|
1148
|
+
createAttributesFromProps(component.children, componentInfoObjects);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
export function componentFromAttribute({
|
|
1154
|
+
attrObj,
|
|
1155
|
+
value,
|
|
1156
|
+
originalComponentProps,
|
|
1157
|
+
componentInfoObjects,
|
|
1158
|
+
}) {
|
|
1159
|
+
if (typeof value !== "object") {
|
|
1160
|
+
// typically this would mean value is a string.
|
|
1161
|
+
// However, if had an attribute with no value, would get true.
|
|
1162
|
+
// Also, when get addAttributeComponentsShadowingStateVariables,
|
|
1163
|
+
// it is possible their values are not strings
|
|
1164
|
+
value = { rawString: value.toString() };
|
|
1165
|
+
} else if (value === null) {
|
|
1166
|
+
// could get null from addAttributeComponentsShadowingStateVariables
|
|
1167
|
+
value = { rawString: "" };
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
if (attrObj && attrObj.createComponentOfType) {
|
|
1171
|
+
let newComponent;
|
|
1172
|
+
let valueTrimLower = value.rawString.trim().toLowerCase();
|
|
1173
|
+
|
|
1174
|
+
if (valueTrimLower === "true" && attrObj.valueForTrue !== undefined) {
|
|
1175
|
+
newComponent = {
|
|
1176
|
+
componentType: attrObj.createComponentOfType,
|
|
1177
|
+
state: { value: attrObj.valueForTrue },
|
|
1178
|
+
};
|
|
1179
|
+
} else if (
|
|
1180
|
+
valueTrimLower === "false" &&
|
|
1181
|
+
attrObj.valueForFalse !== undefined
|
|
1182
|
+
) {
|
|
1183
|
+
newComponent = {
|
|
1184
|
+
componentType: attrObj.createComponentOfType,
|
|
1185
|
+
state: { value: attrObj.valueForFalse },
|
|
1186
|
+
};
|
|
1187
|
+
} else if (
|
|
1188
|
+
componentInfoObjects.isInheritedComponentType({
|
|
1189
|
+
inheritedComponentType: attrObj.createComponentOfType,
|
|
1190
|
+
baseComponentType: "boolean",
|
|
1191
|
+
}) &&
|
|
1192
|
+
["true", "false"].includes(valueTrimLower)
|
|
1193
|
+
) {
|
|
1194
|
+
newComponent = {
|
|
1195
|
+
componentType: attrObj.createComponentOfType,
|
|
1196
|
+
state: { value: valueTrimLower === "true" },
|
|
1197
|
+
};
|
|
1198
|
+
} else {
|
|
1199
|
+
let children = value.childrenForComponent;
|
|
1200
|
+
if (children) {
|
|
1201
|
+
children = JSON.parse(JSON.stringify(children));
|
|
1202
|
+
} else {
|
|
1203
|
+
children = [value.rawString];
|
|
1204
|
+
}
|
|
1205
|
+
newComponent = {
|
|
1206
|
+
componentType: attrObj.createComponentOfType,
|
|
1207
|
+
children,
|
|
1208
|
+
};
|
|
1209
|
+
|
|
1210
|
+
removeBlankStringChildren([newComponent], componentInfoObjects);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
if (
|
|
1214
|
+
attrObj.attributesForCreatedComponent ||
|
|
1215
|
+
attrObj.copyComponentAttributesForCreatedComponent
|
|
1216
|
+
) {
|
|
1217
|
+
if (attrObj.attributesForCreatedComponent) {
|
|
1218
|
+
newComponent.props = attrObj.attributesForCreatedComponent;
|
|
1219
|
+
} else {
|
|
1220
|
+
newComponent.props = {};
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
if (attrObj.copyComponentAttributesForCreatedComponent) {
|
|
1224
|
+
for (let attrName of attrObj.copyComponentAttributesForCreatedComponent) {
|
|
1225
|
+
if (originalComponentProps[attrName]) {
|
|
1226
|
+
newComponent.props[attrName] = JSON.parse(
|
|
1227
|
+
JSON.stringify(originalComponentProps[attrName]),
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
createAttributesFromProps([newComponent], componentInfoObjects);
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
let attr = { component: newComponent };
|
|
1237
|
+
if (attrObj.ignoreFixed) {
|
|
1238
|
+
attr.ignoreFixed = true;
|
|
1239
|
+
}
|
|
1240
|
+
return attr;
|
|
1241
|
+
} else if (attrObj && attrObj.createPrimitiveOfType) {
|
|
1242
|
+
let newPrimitive;
|
|
1243
|
+
if (attrObj.createPrimitiveOfType === "boolean") {
|
|
1244
|
+
let valueTrimLower = value.rawString.trim().toLowerCase();
|
|
1245
|
+
newPrimitive = valueTrimLower === "true";
|
|
1246
|
+
} else if (attrObj.createPrimitiveOfType === "number") {
|
|
1247
|
+
newPrimitive = Number(value.rawString);
|
|
1248
|
+
} else if (attrObj.createPrimitiveOfType === "integer") {
|
|
1249
|
+
newPrimitive = Math.round(Number(value.rawString));
|
|
1250
|
+
} else if (attrObj.createPrimitiveOfType === "stringArray") {
|
|
1251
|
+
newPrimitive = value.rawString.trim().split(/\s+/);
|
|
1252
|
+
} else if (attrObj.createPrimitiveOfType === "numberArray") {
|
|
1253
|
+
newPrimitive = value.rawString.trim().split(/\s+/).map(Number);
|
|
1254
|
+
} else {
|
|
1255
|
+
// else assume string
|
|
1256
|
+
newPrimitive = value.rawString;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
if (attrObj.validationFunction) {
|
|
1260
|
+
newPrimitive = attrObj.validationFunction(newPrimitive);
|
|
1261
|
+
}
|
|
1262
|
+
return { primitive: newPrimitive };
|
|
1263
|
+
} else if (attrObj && attrObj.createTargetComponentNames) {
|
|
1264
|
+
let newTargets = value.rawString
|
|
1265
|
+
.trim()
|
|
1266
|
+
.split(/\s+/)
|
|
1267
|
+
.map((str) => {
|
|
1268
|
+
if (str[0] === "$" && str[1] !== "$") {
|
|
1269
|
+
// remove unnecessary macro notation
|
|
1270
|
+
str = str.slice(1);
|
|
1271
|
+
|
|
1272
|
+
if (str[0] === "(" && str[str.length - 1] === ")") {
|
|
1273
|
+
// remove unnecessary parens from macro
|
|
1274
|
+
// (don't both checking that parens match, as no valid result with multiple parens)
|
|
1275
|
+
str = str.slice(1, str.length - 1);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
// absolute name will be added when namespace is known
|
|
1279
|
+
return { relativeName: str };
|
|
1280
|
+
});
|
|
1281
|
+
|
|
1282
|
+
return { targetComponentNames: newTargets };
|
|
1283
|
+
} else {
|
|
1284
|
+
if (!value.childrenForComponent) {
|
|
1285
|
+
value.childrenForComponent = [value.rawString];
|
|
1286
|
+
}
|
|
1287
|
+
return value;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
function findPreSugarIndsAndMarkFromSugar(components) {
|
|
1292
|
+
let preSugarIndsFound = [];
|
|
1293
|
+
for (let component of components) {
|
|
1294
|
+
if (typeof component !== "object") {
|
|
1295
|
+
continue;
|
|
1296
|
+
}
|
|
1297
|
+
if (component.preSugarInd !== undefined) {
|
|
1298
|
+
preSugarIndsFound.push(component.preSugarInd);
|
|
1299
|
+
} else {
|
|
1300
|
+
if (!component.doenetAttributes) {
|
|
1301
|
+
component.doenetAttributes = {};
|
|
1302
|
+
}
|
|
1303
|
+
component.doenetAttributes.createdFromSugar = true;
|
|
1304
|
+
if (component.children) {
|
|
1305
|
+
let inds = findPreSugarIndsAndMarkFromSugar(component.children);
|
|
1306
|
+
preSugarIndsFound.push(...inds);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
return preSugarIndsFound;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
export function applyMacros(serializedComponents, componentInfoObjects) {
|
|
1315
|
+
for (let component of serializedComponents) {
|
|
1316
|
+
if (component.children) {
|
|
1317
|
+
applyMacros(component.children, componentInfoObjects);
|
|
1318
|
+
}
|
|
1319
|
+
if (component.attributes) {
|
|
1320
|
+
for (let attrName in component.attributes) {
|
|
1321
|
+
let attribute = component.attributes[attrName];
|
|
1322
|
+
if (attribute.component) {
|
|
1323
|
+
applyMacros([attribute.component], componentInfoObjects);
|
|
1324
|
+
} else if (attribute.childrenForComponent) {
|
|
1325
|
+
applyMacros(attribute.childrenForComponent, componentInfoObjects);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
substituteMacros(serializedComponents, componentInfoObjects);
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
function substituteMacros(serializedComponents, componentInfoObjects) {
|
|
1335
|
+
for (
|
|
1336
|
+
let componentInd = 0;
|
|
1337
|
+
componentInd < serializedComponents.length;
|
|
1338
|
+
componentInd++
|
|
1339
|
+
) {
|
|
1340
|
+
let component = serializedComponents[componentInd];
|
|
1341
|
+
|
|
1342
|
+
if (typeof component === "string") {
|
|
1343
|
+
let startInd = 0;
|
|
1344
|
+
while (startInd < component.length) {
|
|
1345
|
+
let str = component;
|
|
1346
|
+
let result = findFirstFullMacroInString(str.slice(startInd));
|
|
1347
|
+
|
|
1348
|
+
if (!result.success) {
|
|
1349
|
+
break;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
let firstIndMatched = result.firstIndMatched + startInd;
|
|
1353
|
+
let matchLength = result.matchLength;
|
|
1354
|
+
let nDollarSigns = result.nDollarSigns;
|
|
1355
|
+
|
|
1356
|
+
let componentsFromMacro;
|
|
1357
|
+
|
|
1358
|
+
let componentResult = createComponentFromExtendedSource({
|
|
1359
|
+
sourceName: result.sourceName,
|
|
1360
|
+
componentIndex: result.componentIndex,
|
|
1361
|
+
subNames: result.subNames,
|
|
1362
|
+
componentAttributes: result.componentAttributes,
|
|
1363
|
+
propArray: result.propArray,
|
|
1364
|
+
componentInfoObjects,
|
|
1365
|
+
});
|
|
1366
|
+
|
|
1367
|
+
let newComponent;
|
|
1368
|
+
if (componentResult.success) {
|
|
1369
|
+
newComponent = componentResult.newComponent;
|
|
1370
|
+
} else {
|
|
1371
|
+
let strWithError = str.slice(
|
|
1372
|
+
firstIndMatched,
|
|
1373
|
+
firstIndMatched + matchLength,
|
|
1374
|
+
);
|
|
1375
|
+
let macroStartInd = firstIndMatched;
|
|
1376
|
+
// TODO: if previous component is a string,
|
|
1377
|
+
// keep going back and add string lengths to get actual index
|
|
1378
|
+
if (
|
|
1379
|
+
componentInd > 0 &&
|
|
1380
|
+
serializedComponents[componentInd - 1].range
|
|
1381
|
+
) {
|
|
1382
|
+
let previousRange = serializedComponents[componentInd - 1].range;
|
|
1383
|
+
if (previousRange.closeEnd) {
|
|
1384
|
+
macroStartInd += previousRange.closeEnd;
|
|
1385
|
+
} else if (previousRange.selfCloseEnd) {
|
|
1386
|
+
macroStartInd += previousRange.selfCloseBegin;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
throw Error(
|
|
1391
|
+
`${componentResult.message}. At indices ${macroStartInd}-${
|
|
1392
|
+
macroStartInd + matchLength
|
|
1393
|
+
}. Found: ${strWithError}`,
|
|
1394
|
+
);
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
markCreatedFromMacro([newComponent]);
|
|
1398
|
+
|
|
1399
|
+
if (result.propArray.length === 0) {
|
|
1400
|
+
newComponent.doenetAttributes.isPlainMacro = true;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
componentsFromMacro = [newComponent];
|
|
1404
|
+
|
|
1405
|
+
let numComponentsToRemove = 1;
|
|
1406
|
+
let stringToAddAtEnd = str.substring(firstIndMatched + matchLength);
|
|
1407
|
+
|
|
1408
|
+
if (nDollarSigns === 2) {
|
|
1409
|
+
let matchOpeningParens = str
|
|
1410
|
+
.slice(firstIndMatched + matchLength)
|
|
1411
|
+
.match(/^\s*\(/);
|
|
1412
|
+
|
|
1413
|
+
if (!matchOpeningParens) {
|
|
1414
|
+
// if don't match function,
|
|
1415
|
+
// don't replace double dollar sign macro
|
|
1416
|
+
startInd = firstIndMatched + 2;
|
|
1417
|
+
continue;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
let matchLengthWithOpeningParens =
|
|
1421
|
+
matchLength + matchOpeningParens[0].length;
|
|
1422
|
+
|
|
1423
|
+
// look for a closing parenthesis
|
|
1424
|
+
|
|
1425
|
+
// get array of the component with the rest of this string
|
|
1426
|
+
// plus the rest of the components in the array
|
|
1427
|
+
let remainingComponents = [];
|
|
1428
|
+
let includeFirstInRemaining = false;
|
|
1429
|
+
|
|
1430
|
+
if (str.length > firstIndMatched + matchLengthWithOpeningParens) {
|
|
1431
|
+
includeFirstInRemaining = true;
|
|
1432
|
+
remainingComponents.push(
|
|
1433
|
+
str.substring(firstIndMatched + matchLengthWithOpeningParens),
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
remainingComponents.push(
|
|
1438
|
+
...serializedComponents.slice(componentInd + 1),
|
|
1439
|
+
);
|
|
1440
|
+
|
|
1441
|
+
let evaluateResult = createEvaluateIfFindMatchedClosingParens({
|
|
1442
|
+
componentsFromMacro,
|
|
1443
|
+
remainingComponents,
|
|
1444
|
+
includeFirstInRemaining,
|
|
1445
|
+
componentInfoObjects,
|
|
1446
|
+
});
|
|
1447
|
+
|
|
1448
|
+
if (!evaluateResult.success) {
|
|
1449
|
+
// if couldn't create evaluate,
|
|
1450
|
+
// don't replace double dollar macro
|
|
1451
|
+
startInd = firstIndMatched + 2;
|
|
1452
|
+
continue;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
componentsFromMacro = evaluateResult.componentsFromMacro;
|
|
1456
|
+
|
|
1457
|
+
numComponentsToRemove = evaluateResult.lastComponentIndMatched + 1;
|
|
1458
|
+
if (!includeFirstInRemaining) {
|
|
1459
|
+
numComponentsToRemove++;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
// leftover string already included in componentsFromMacro
|
|
1463
|
+
stringToAddAtEnd = "";
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
let replacements = [];
|
|
1467
|
+
|
|
1468
|
+
// the string before the function name
|
|
1469
|
+
if (firstIndMatched > 0) {
|
|
1470
|
+
replacements.push(str.substring(0, firstIndMatched));
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
replacements.push(...componentsFromMacro);
|
|
1474
|
+
|
|
1475
|
+
if (stringToAddAtEnd.length > 0) {
|
|
1476
|
+
replacements.push(stringToAddAtEnd);
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
// splice new replacements into serializedComponents
|
|
1480
|
+
serializedComponents.splice(
|
|
1481
|
+
componentInd,
|
|
1482
|
+
numComponentsToRemove,
|
|
1483
|
+
...replacements,
|
|
1484
|
+
);
|
|
1485
|
+
|
|
1486
|
+
if (firstIndMatched > 0) {
|
|
1487
|
+
// increment componentInd because we now have to skip
|
|
1488
|
+
// over two components
|
|
1489
|
+
// (the component made from the beginning of the string
|
|
1490
|
+
// as well as the component made from the macro)
|
|
1491
|
+
componentInd++;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
// break out of loop processing string,
|
|
1495
|
+
// as finished current one
|
|
1496
|
+
// (possibly breaking it into pieces, so will address remainder as other component)
|
|
1497
|
+
|
|
1498
|
+
break;
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
function createComponentFromExtendedSource({
|
|
1505
|
+
sourceName,
|
|
1506
|
+
componentIndex,
|
|
1507
|
+
componentAttributes,
|
|
1508
|
+
propArray,
|
|
1509
|
+
subNames,
|
|
1510
|
+
componentInfoObjects,
|
|
1511
|
+
}) {
|
|
1512
|
+
let newComponent = {
|
|
1513
|
+
componentType: "copy",
|
|
1514
|
+
doenetAttributes: { target: sourceName },
|
|
1515
|
+
attributes: {},
|
|
1516
|
+
};
|
|
1517
|
+
|
|
1518
|
+
if (componentIndex) {
|
|
1519
|
+
let childrenForAttribute = [componentIndex];
|
|
1520
|
+
applyMacros(childrenForAttribute, componentInfoObjects);
|
|
1521
|
+
|
|
1522
|
+
newComponent.attributes.componentIndex = {
|
|
1523
|
+
component: {
|
|
1524
|
+
componentType: "integer",
|
|
1525
|
+
children: childrenForAttribute,
|
|
1526
|
+
},
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
if (subNames?.length > 0) {
|
|
1531
|
+
let sourceSubnames = [];
|
|
1532
|
+
let sourceSubnamesComponentIndex = [];
|
|
1533
|
+
|
|
1534
|
+
for (let subNameObj of subNames) {
|
|
1535
|
+
sourceSubnames.push(subNameObj.subName);
|
|
1536
|
+
if (subNameObj.subNameComponentIndex !== undefined) {
|
|
1537
|
+
if (sourceSubnamesComponentIndex.length < sourceSubnames - 1) {
|
|
1538
|
+
// TODO: NaN will presumably make it not return anything
|
|
1539
|
+
// When we enable recursing to composites, we'll need a staregy to skip subname component index
|
|
1540
|
+
sourceSubnamesComponentIndex.push(
|
|
1541
|
+
...Array[
|
|
1542
|
+
sourceSubnames - 1 - sourceSubnamesComponentIndex.length
|
|
1543
|
+
].fill(NaN),
|
|
1544
|
+
);
|
|
1545
|
+
}
|
|
1546
|
+
sourceSubnamesComponentIndex.push(subNameObj.subNameComponentIndex);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
newComponent.attributes.sourceSubnames = {
|
|
1551
|
+
primitive: sourceSubnames,
|
|
1552
|
+
};
|
|
1553
|
+
if (sourceSubnamesComponentIndex.length > 0) {
|
|
1554
|
+
let childrenForAttribute = [sourceSubnamesComponentIndex.join(" ")];
|
|
1555
|
+
applyMacros(childrenForAttribute, componentInfoObjects);
|
|
1556
|
+
|
|
1557
|
+
newComponent.attributes.sourceSubnamesComponentIndex = {
|
|
1558
|
+
component: {
|
|
1559
|
+
componentType: "numberList",
|
|
1560
|
+
children: childrenForAttribute,
|
|
1561
|
+
},
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
let propsAddExtract = false;
|
|
1567
|
+
|
|
1568
|
+
if (componentAttributes) {
|
|
1569
|
+
propsAddExtract = true;
|
|
1570
|
+
|
|
1571
|
+
let attributesResult = createAttributesFromString(
|
|
1572
|
+
componentAttributes,
|
|
1573
|
+
componentInfoObjects,
|
|
1574
|
+
);
|
|
1575
|
+
if (!attributesResult.success) {
|
|
1576
|
+
return attributesResult;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
Object.assign(newComponent.attributes, attributesResult.newAttributes);
|
|
1580
|
+
|
|
1581
|
+
if (attributesResult.assignNames) {
|
|
1582
|
+
newComponent.props = { assignNames: attributesResult.assignNames };
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
for (let propObj of propArray) {
|
|
1587
|
+
if (propsAddExtract) {
|
|
1588
|
+
newComponent.doenetAttributes.createdFromMacro = true;
|
|
1589
|
+
|
|
1590
|
+
newComponent = {
|
|
1591
|
+
componentType: "extract",
|
|
1592
|
+
attributes: {},
|
|
1593
|
+
doenetAttributes: {},
|
|
1594
|
+
children: [newComponent],
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
newComponent.attributes.prop = { primitive: propObj.prop };
|
|
1599
|
+
|
|
1600
|
+
if (propObj.propIndex) {
|
|
1601
|
+
let childrenForAttribute = [propObj.propIndex.join(" ")];
|
|
1602
|
+
applyMacros(childrenForAttribute, componentInfoObjects);
|
|
1603
|
+
|
|
1604
|
+
newComponent.attributes.propIndex = {
|
|
1605
|
+
component: {
|
|
1606
|
+
componentType: "numberList",
|
|
1607
|
+
children: childrenForAttribute,
|
|
1608
|
+
},
|
|
1609
|
+
};
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
if (propObj.attributes) {
|
|
1613
|
+
let attributesResult = createAttributesFromString(
|
|
1614
|
+
propObj.attributes,
|
|
1615
|
+
componentInfoObjects,
|
|
1616
|
+
);
|
|
1617
|
+
if (!attributesResult.success) {
|
|
1618
|
+
return attributesResult;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
Object.assign(newComponent.attributes, attributesResult.newAttributes);
|
|
1622
|
+
|
|
1623
|
+
if (attributesResult.assignNames) {
|
|
1624
|
+
newComponent.props = { assignNames: attributesResult.assignNames };
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
propsAddExtract = true;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
return { success: true, newComponent };
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
function createAttributesFromString(componentAttributes, componentInfoObjects) {
|
|
1635
|
+
// parse a copy component with those attributes
|
|
1636
|
+
// to get attributes parsed
|
|
1637
|
+
|
|
1638
|
+
let attributesDoenetML = `<copy ${componentAttributes} />`;
|
|
1639
|
+
let componentsForAttributes;
|
|
1640
|
+
try {
|
|
1641
|
+
componentsForAttributes = parseAndCompile(attributesDoenetML);
|
|
1642
|
+
} catch (e) {
|
|
1643
|
+
return { success: false, message: "Error in macro" };
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
createAttributesFromProps(componentsForAttributes, componentInfoObjects);
|
|
1647
|
+
markCreatedFromMacro(componentsForAttributes);
|
|
1648
|
+
|
|
1649
|
+
// recurse in case there were more macros in additionalAttributes
|
|
1650
|
+
applyMacros(componentsForAttributes, componentInfoObjects);
|
|
1651
|
+
|
|
1652
|
+
let newAttributes = componentsForAttributes[0].attributes;
|
|
1653
|
+
|
|
1654
|
+
if (
|
|
1655
|
+
newAttributes.prop ||
|
|
1656
|
+
newAttributes.propIndex ||
|
|
1657
|
+
newAttributes.componentIndex
|
|
1658
|
+
) {
|
|
1659
|
+
return {
|
|
1660
|
+
success: false,
|
|
1661
|
+
message:
|
|
1662
|
+
"Error in macro: macro cannot directly add attributes prop, propIndex, or componentIndex",
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
let assignNames;
|
|
1667
|
+
if (componentsForAttributes[0].props) {
|
|
1668
|
+
for (let prop in componentsForAttributes[0].props) {
|
|
1669
|
+
if (prop.toLowerCase() === "assignnames") {
|
|
1670
|
+
if (assignNames) {
|
|
1671
|
+
return {
|
|
1672
|
+
success: false,
|
|
1673
|
+
message: "Error in macro: cannot repeat assignNames",
|
|
1674
|
+
};
|
|
1675
|
+
} else {
|
|
1676
|
+
assignNames = componentsForAttributes[0].props[prop];
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
return { success: true, newAttributes, assignNames };
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
function findFirstFullMacroInString(str) {
|
|
1686
|
+
let offset = 0;
|
|
1687
|
+
let nDollarSigns;
|
|
1688
|
+
|
|
1689
|
+
while (true) {
|
|
1690
|
+
// look for a macro
|
|
1691
|
+
let matchDollars = str.substring(offset).match(/(\$+)(.?)/);
|
|
1692
|
+
|
|
1693
|
+
if (!matchDollars) {
|
|
1694
|
+
return { success: false };
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
nDollarSigns = matchDollars[1].length;
|
|
1698
|
+
offset += matchDollars.index + nDollarSigns;
|
|
1699
|
+
|
|
1700
|
+
if (nDollarSigns <= 2) {
|
|
1701
|
+
let extendedWordCharacters = false;
|
|
1702
|
+
|
|
1703
|
+
let strForMacro = str.substring(offset);
|
|
1704
|
+
let requiredLength = 0;
|
|
1705
|
+
|
|
1706
|
+
let findResult = findWordOrDelimitedGroup(
|
|
1707
|
+
strForMacro,
|
|
1708
|
+
extendedWordCharacters,
|
|
1709
|
+
);
|
|
1710
|
+
|
|
1711
|
+
if (findResult.startDelim === "(") {
|
|
1712
|
+
// if have parens, then restrict to string inside parens
|
|
1713
|
+
// and allowed extended characters in words
|
|
1714
|
+
extendedWordCharacters = true;
|
|
1715
|
+
strForMacro = findResult.group;
|
|
1716
|
+
requiredLength = findResult.group.length;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
let result = buildSourcePieces(strForMacro, extendedWordCharacters);
|
|
1720
|
+
|
|
1721
|
+
if (result.success) {
|
|
1722
|
+
if (extendedWordCharacters) {
|
|
1723
|
+
// if were in parens, then must match all characters
|
|
1724
|
+
if (result.matchLength !== requiredLength) {
|
|
1725
|
+
return { success: false };
|
|
1726
|
+
}
|
|
1727
|
+
result.matchLength += 2; // +2 for the parens
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
result.nDollarSigns = nDollarSigns;
|
|
1731
|
+
result.firstIndMatched = offset - nDollarSigns;
|
|
1732
|
+
result.matchLength += nDollarSigns;
|
|
1733
|
+
|
|
1734
|
+
return result;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
// try for another match, given that offset was shifted after last dollar signs
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
function buildSourcePieces(str, extendedWordCharacters) {
|
|
1743
|
+
let findResult = findWordOrDelimitedGroup(str, extendedWordCharacters);
|
|
1744
|
+
|
|
1745
|
+
let matchLength = 0;
|
|
1746
|
+
|
|
1747
|
+
if (findResult.withPeriod || !findResult.word) {
|
|
1748
|
+
// must start with a word without a period
|
|
1749
|
+
return { success: false };
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
let result = {
|
|
1753
|
+
sourceName: (findResult.withSlash ? "/" : "") + findResult.word,
|
|
1754
|
+
};
|
|
1755
|
+
|
|
1756
|
+
matchLength += findResult.matchLength;
|
|
1757
|
+
str = str.substring(findResult.matchLength);
|
|
1758
|
+
|
|
1759
|
+
findResult = findWordOrDelimitedGroup(str, extendedWordCharacters);
|
|
1760
|
+
|
|
1761
|
+
if (findResult.startDelim === "[") {
|
|
1762
|
+
result.componentIndex = findResult.group;
|
|
1763
|
+
matchLength += findResult.matchLength;
|
|
1764
|
+
str = str.substring(findResult.matchLength);
|
|
1765
|
+
findResult = findWordOrDelimitedGroup(str, extendedWordCharacters);
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
let subNames = [];
|
|
1769
|
+
while (findResult.withSlash) {
|
|
1770
|
+
// check for additional subname piece of /name[componentIndex]
|
|
1771
|
+
|
|
1772
|
+
let subnameObj = { subName: findResult.word };
|
|
1773
|
+
matchLength += findResult.matchLength;
|
|
1774
|
+
str = str.substring(findResult.matchLength);
|
|
1775
|
+
findResult = findWordOrDelimitedGroup(str, extendedWordCharacters);
|
|
1776
|
+
|
|
1777
|
+
if (findResult.startDelim === "[") {
|
|
1778
|
+
subnameObj.subNameComponentIndex = findResult.group;
|
|
1779
|
+
matchLength += findResult.matchLength;
|
|
1780
|
+
str = str.substring(findResult.matchLength);
|
|
1781
|
+
findResult = findWordOrDelimitedGroup(str, extendedWordCharacters);
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
subNames.push(subnameObj);
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
result.subNames = subNames;
|
|
1788
|
+
|
|
1789
|
+
if (findResult.startDelim === "{") {
|
|
1790
|
+
result.componentAttributes = findResult.group;
|
|
1791
|
+
matchLength += findResult.matchLength;
|
|
1792
|
+
str = str.substring(findResult.matchLength);
|
|
1793
|
+
findResult = findWordOrDelimitedGroup(str, extendedWordCharacters);
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
let propArray = [];
|
|
1797
|
+
|
|
1798
|
+
while (findResult.withPeriod) {
|
|
1799
|
+
// check to a prop object of prop[propIndex]{attributes}
|
|
1800
|
+
// where [] and {} parts are optional
|
|
1801
|
+
|
|
1802
|
+
let propObj = { prop: findResult.word };
|
|
1803
|
+
matchLength += findResult.matchLength;
|
|
1804
|
+
str = str.substring(findResult.matchLength);
|
|
1805
|
+
findResult = findWordOrDelimitedGroup(str, extendedWordCharacters);
|
|
1806
|
+
|
|
1807
|
+
let propIndex = [];
|
|
1808
|
+
|
|
1809
|
+
while (findResult.startDelim === "[") {
|
|
1810
|
+
propIndex.push(findResult.group);
|
|
1811
|
+
matchLength += findResult.matchLength;
|
|
1812
|
+
str = str.substring(findResult.matchLength);
|
|
1813
|
+
findResult = findWordOrDelimitedGroup(str, extendedWordCharacters);
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
if (propIndex.length > 0) {
|
|
1817
|
+
propObj.propIndex = propIndex;
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
if (findResult.startDelim === "{") {
|
|
1821
|
+
propObj.attributes = findResult.group;
|
|
1822
|
+
matchLength += findResult.matchLength;
|
|
1823
|
+
str = str.substring(findResult.matchLength);
|
|
1824
|
+
findResult = findWordOrDelimitedGroup(str, extendedWordCharacters);
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
propArray.push(propObj);
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
result.propArray = propArray;
|
|
1831
|
+
result.matchLength = matchLength;
|
|
1832
|
+
result.success = true;
|
|
1833
|
+
|
|
1834
|
+
return result;
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
function findWordOrDelimitedGroup(str, extendedWordCharacters = false) {
|
|
1838
|
+
// find the next word (possibly begininng with a period or, extendedWordCharacters, a slash ),
|
|
1839
|
+
// or a group delimited by (), [], or {},
|
|
1840
|
+
// where the word/group must start with the first character of the string
|
|
1841
|
+
|
|
1842
|
+
let withPeriod = false;
|
|
1843
|
+
let withSlash = false;
|
|
1844
|
+
if (str[0] === "." && str[1] !== ".") {
|
|
1845
|
+
withPeriod = true;
|
|
1846
|
+
str = str.substring(1);
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
let wordRe;
|
|
1850
|
+
|
|
1851
|
+
if (extendedWordCharacters) {
|
|
1852
|
+
if (withPeriod) {
|
|
1853
|
+
wordRe = /^[\w-]+/;
|
|
1854
|
+
} else {
|
|
1855
|
+
if (str[0] === "/" && str[1].match(/\w/)) {
|
|
1856
|
+
withSlash = true;
|
|
1857
|
+
str = str.substring(1);
|
|
1858
|
+
}
|
|
1859
|
+
wordRe = /^([\w\/-]|\.\.\/)+/;
|
|
1860
|
+
}
|
|
1861
|
+
} else {
|
|
1862
|
+
wordRe = /^[a-zA-Z_]\w*/;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
let match = str.match(wordRe);
|
|
1866
|
+
|
|
1867
|
+
if (match) {
|
|
1868
|
+
return {
|
|
1869
|
+
success: true,
|
|
1870
|
+
withPeriod,
|
|
1871
|
+
withSlash,
|
|
1872
|
+
word: match[0],
|
|
1873
|
+
matchLength: match[0].length + (withPeriod ? 1 : 0) + (withSlash ? 1 : 0),
|
|
1874
|
+
};
|
|
1875
|
+
} else if (withPeriod || withSlash) {
|
|
1876
|
+
// if starts with a period or slash, must have word next
|
|
1877
|
+
return { success: false };
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
let neededClosingDelimStack = [];
|
|
1881
|
+
let closingByOpeningDelim = {
|
|
1882
|
+
"(": ")",
|
|
1883
|
+
"{": "}",
|
|
1884
|
+
"[": "]",
|
|
1885
|
+
};
|
|
1886
|
+
let closeDelims = Object.values(closingByOpeningDelim);
|
|
1887
|
+
|
|
1888
|
+
let startDelim = str[0];
|
|
1889
|
+
|
|
1890
|
+
let nextClosing = closingByOpeningDelim[startDelim];
|
|
1891
|
+
|
|
1892
|
+
if (!nextClosing) {
|
|
1893
|
+
return { success: false };
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
neededClosingDelimStack.push(nextClosing);
|
|
1897
|
+
|
|
1898
|
+
for (let ind = 1; ind < str.length; ind++) {
|
|
1899
|
+
let char = str[ind];
|
|
1900
|
+
|
|
1901
|
+
if (char in closingByOpeningDelim) {
|
|
1902
|
+
neededClosingDelimStack.push(closingByOpeningDelim[char]);
|
|
1903
|
+
} else if (closeDelims.includes(char)) {
|
|
1904
|
+
if (char !== neededClosingDelimStack.pop()) {
|
|
1905
|
+
// mismatched closing delim
|
|
1906
|
+
return { success: false };
|
|
1907
|
+
}
|
|
1908
|
+
if (neededClosingDelimStack.length === 0) {
|
|
1909
|
+
// matched startDelim
|
|
1910
|
+
return {
|
|
1911
|
+
success: true,
|
|
1912
|
+
group: str.substring(1, ind), // does not include delimiters
|
|
1913
|
+
startDelim,
|
|
1914
|
+
matchLength: ind + 1,
|
|
1915
|
+
};
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
// got to end of str with closing out startDelim
|
|
1921
|
+
return { success: false };
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
function markCreatedFromMacro(serializedComponents) {
|
|
1925
|
+
for (let serializedComponent of serializedComponents) {
|
|
1926
|
+
if (!serializedComponent.doenetAttributes) {
|
|
1927
|
+
serializedComponent.doenetAttributes = {};
|
|
1928
|
+
}
|
|
1929
|
+
serializedComponent.doenetAttributes.createdFromMacro = true;
|
|
1930
|
+
|
|
1931
|
+
if (serializedComponent.children) {
|
|
1932
|
+
markCreatedFromMacro(serializedComponent.children);
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
function createEvaluateIfFindMatchedClosingParens({
|
|
1938
|
+
componentsFromMacro,
|
|
1939
|
+
remainingComponents,
|
|
1940
|
+
includeFirstInRemaining,
|
|
1941
|
+
componentInfoObjects,
|
|
1942
|
+
}) {
|
|
1943
|
+
let result = findFirstUnmatchedClosingParens(remainingComponents);
|
|
1944
|
+
|
|
1945
|
+
if (!result.success) {
|
|
1946
|
+
return result;
|
|
1947
|
+
}
|
|
1948
|
+
// found unmatched closing parenthesis, so is the one
|
|
1949
|
+
// matching the opening parenthesis
|
|
1950
|
+
|
|
1951
|
+
let lastComponentInd = result.componentInd;
|
|
1952
|
+
|
|
1953
|
+
remainingComponents = remainingComponents.slice(0, lastComponentInd + 1);
|
|
1954
|
+
|
|
1955
|
+
let lastComponentOfFunction = remainingComponents[lastComponentInd];
|
|
1956
|
+
|
|
1957
|
+
let stringAfterFunction = "";
|
|
1958
|
+
|
|
1959
|
+
// if have text after closing parenthesis
|
|
1960
|
+
// save in stringAfterFunction
|
|
1961
|
+
if (result.charInd + 1 < lastComponentOfFunction.length) {
|
|
1962
|
+
stringAfterFunction = lastComponentOfFunction.substring(result.charInd + 1);
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
// remove closing parenthesis and any subsequent text
|
|
1966
|
+
// from the last component
|
|
1967
|
+
if (result.charInd > 0) {
|
|
1968
|
+
remainingComponents[lastComponentInd] = lastComponentOfFunction.substring(
|
|
1969
|
+
0,
|
|
1970
|
+
result.charInd,
|
|
1971
|
+
);
|
|
1972
|
+
} else {
|
|
1973
|
+
// remove this component altogether as there is nothing left
|
|
1974
|
+
remainingComponents = remainingComponents.slice(0, lastComponentInd);
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
let breakResults = breakEmbeddedStringByCommas({
|
|
1978
|
+
childrenList: remainingComponents,
|
|
1979
|
+
});
|
|
1980
|
+
|
|
1981
|
+
// recurse on pieces
|
|
1982
|
+
breakResults.pieces.forEach((x) => applyMacros(x, componentInfoObjects));
|
|
1983
|
+
|
|
1984
|
+
let inputArray = breakResults.pieces.map((x) => {
|
|
1985
|
+
if (x.length === 1 && typeof x[0] !== "string") {
|
|
1986
|
+
return x[0];
|
|
1987
|
+
} else {
|
|
1988
|
+
return {
|
|
1989
|
+
componentType: "math",
|
|
1990
|
+
doenetAttributes: { createdFromMacro: true },
|
|
1991
|
+
children: x,
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
});
|
|
1995
|
+
|
|
1996
|
+
let evaluateComponent = {
|
|
1997
|
+
componentType: "evaluate",
|
|
1998
|
+
doenetAttributes: { createdFromMacro: true },
|
|
1999
|
+
attributes: {
|
|
2000
|
+
function: {
|
|
2001
|
+
component: {
|
|
2002
|
+
componentType: "function",
|
|
2003
|
+
doenetAttributes: { createdFromMacro: true },
|
|
2004
|
+
children: componentsFromMacro,
|
|
2005
|
+
},
|
|
2006
|
+
},
|
|
2007
|
+
input: {
|
|
2008
|
+
component: {
|
|
2009
|
+
componentType: "mathList",
|
|
2010
|
+
doenetAttributes: { createdFromMacro: true },
|
|
2011
|
+
children: inputArray,
|
|
2012
|
+
skipSugar: true,
|
|
2013
|
+
},
|
|
2014
|
+
},
|
|
2015
|
+
},
|
|
2016
|
+
};
|
|
2017
|
+
|
|
2018
|
+
let replacements = [evaluateComponent];
|
|
2019
|
+
|
|
2020
|
+
// if have text after function
|
|
2021
|
+
// include string component at end containing that text
|
|
2022
|
+
if (stringAfterFunction.length > 0) {
|
|
2023
|
+
replacements.push(stringAfterFunction);
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
return {
|
|
2027
|
+
success: true,
|
|
2028
|
+
componentsFromMacro: replacements,
|
|
2029
|
+
lastComponentIndMatched: lastComponentInd,
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
function findFirstUnmatchedClosingParens(components) {
|
|
2034
|
+
let Nparens = 0;
|
|
2035
|
+
|
|
2036
|
+
for (let [componentInd, component] of components.entries()) {
|
|
2037
|
+
if (typeof component === "string") {
|
|
2038
|
+
let s = component;
|
|
2039
|
+
|
|
2040
|
+
for (let charInd = 0; charInd < s.length; charInd++) {
|
|
2041
|
+
let char = s[charInd];
|
|
2042
|
+
if (char === "(") {
|
|
2043
|
+
Nparens++;
|
|
2044
|
+
} else if (char === ")") {
|
|
2045
|
+
if (Nparens === 0) {
|
|
2046
|
+
// parens didn't match
|
|
2047
|
+
return {
|
|
2048
|
+
success: true,
|
|
2049
|
+
componentInd,
|
|
2050
|
+
charInd,
|
|
2051
|
+
};
|
|
2052
|
+
} else {
|
|
2053
|
+
Nparens--;
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
// never found a closing parenthesis that wasn't matched
|
|
2061
|
+
return { success: false };
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
function decodeXMLEntities(serializedComponents) {
|
|
2065
|
+
function replaceEntities(s) {
|
|
2066
|
+
return s
|
|
2067
|
+
.replace(/'/g, "'")
|
|
2068
|
+
.replace(/"/g, '"')
|
|
2069
|
+
.replace(/>/g, ">")
|
|
2070
|
+
.replace(/</g, "<")
|
|
2071
|
+
.replace(/$/g, "$")
|
|
2072
|
+
.replace(/&/g, "&");
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
for (let [ind, serializedComponent] of serializedComponents.entries()) {
|
|
2076
|
+
if (typeof serializedComponent === "string") {
|
|
2077
|
+
serializedComponents[ind] = replaceEntities(serializedComponent);
|
|
2078
|
+
} else {
|
|
2079
|
+
if (serializedComponent.children) {
|
|
2080
|
+
decodeXMLEntities(serializedComponent.children);
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
if (serializedComponent.attributes) {
|
|
2084
|
+
for (let attrName in serializedComponent.attributes) {
|
|
2085
|
+
let attribute = serializedComponent.attributes[attrName];
|
|
2086
|
+
|
|
2087
|
+
if (attribute.component) {
|
|
2088
|
+
decodeXMLEntities([attribute.component]);
|
|
2089
|
+
} else if (attribute.primitive) {
|
|
2090
|
+
if (typeof attribute.primitive === "string") {
|
|
2091
|
+
attribute.primitive = replaceEntities(attribute.primitive);
|
|
2092
|
+
}
|
|
2093
|
+
} else {
|
|
2094
|
+
if (attribute.childrenForComponent) {
|
|
2095
|
+
decodeXMLEntities(attribute.childrenForComponent);
|
|
2096
|
+
}
|
|
2097
|
+
if (attribute.rawString) {
|
|
2098
|
+
attribute.rawString = replaceEntities(attribute.rawString);
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
export function applySugar({
|
|
2108
|
+
serializedComponents,
|
|
2109
|
+
parentParametersFromSugar = {},
|
|
2110
|
+
parentAttributes = {},
|
|
2111
|
+
componentInfoObjects,
|
|
2112
|
+
isAttributeComponent = false,
|
|
2113
|
+
}) {
|
|
2114
|
+
for (let component of serializedComponents) {
|
|
2115
|
+
if (typeof component !== "object") {
|
|
2116
|
+
continue;
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
let componentType = component.componentType;
|
|
2120
|
+
let componentClass =
|
|
2121
|
+
componentInfoObjects.allComponentClasses[componentType];
|
|
2122
|
+
if (!componentClass) {
|
|
2123
|
+
throw Error(`Unrecognized component type ${componentType}`);
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
let componentAttributes = {};
|
|
2127
|
+
// add primitive attributes to componentAttributes
|
|
2128
|
+
for (let attrName in component.attributes) {
|
|
2129
|
+
let attribute = component.attributes[attrName];
|
|
2130
|
+
if (attribute.primitive !== undefined) {
|
|
2131
|
+
componentAttributes[attrName] = attribute.primitive;
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
if (component.children) {
|
|
2136
|
+
let newParentParametersFromSugar = {};
|
|
2137
|
+
|
|
2138
|
+
if (!component.skipSugar) {
|
|
2139
|
+
for (let sugarInstruction of componentClass.returnSugarInstructions()) {
|
|
2140
|
+
// if (component.children.length === 0) {
|
|
2141
|
+
// break;
|
|
2142
|
+
// }
|
|
2143
|
+
|
|
2144
|
+
let childTypes = component.children
|
|
2145
|
+
.map((x) => (typeof x === "string" ? "s" : "n"))
|
|
2146
|
+
.join("");
|
|
2147
|
+
|
|
2148
|
+
if (sugarInstruction.childrenRegex) {
|
|
2149
|
+
let match = childTypes.match(sugarInstruction.childrenRegex);
|
|
2150
|
+
|
|
2151
|
+
if (!match || match[0].length !== component.children.length) {
|
|
2152
|
+
// sugar pattern didn't match all children
|
|
2153
|
+
// so don't apply sugar
|
|
2154
|
+
|
|
2155
|
+
continue;
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
let matchedChildren = deepClone(component.children);
|
|
2160
|
+
|
|
2161
|
+
let nNonStrings = 0;
|
|
2162
|
+
for (let child of matchedChildren) {
|
|
2163
|
+
if (typeof child !== "string") {
|
|
2164
|
+
child.preSugarInd = nNonStrings;
|
|
2165
|
+
nNonStrings++;
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
let createdFromMacro = false;
|
|
2170
|
+
if (
|
|
2171
|
+
component.doenetAttributes &&
|
|
2172
|
+
component.doenetAttributes.createdFromMacro
|
|
2173
|
+
) {
|
|
2174
|
+
createdFromMacro = true;
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
let sugarResults = sugarInstruction.replacementFunction({
|
|
2178
|
+
matchedChildren,
|
|
2179
|
+
parentParametersFromSugar,
|
|
2180
|
+
parentAttributes,
|
|
2181
|
+
componentAttributes,
|
|
2182
|
+
componentInfoObjects,
|
|
2183
|
+
isAttributeComponent,
|
|
2184
|
+
createdFromMacro,
|
|
2185
|
+
});
|
|
2186
|
+
|
|
2187
|
+
// console.log("sugarResults")
|
|
2188
|
+
// console.log(sugarResults)
|
|
2189
|
+
|
|
2190
|
+
if (sugarResults.success) {
|
|
2191
|
+
let newChildren = sugarResults.newChildren;
|
|
2192
|
+
let newAttributes = sugarResults.newAttributes;
|
|
2193
|
+
|
|
2194
|
+
let preSugarIndsFoundInChildren = [],
|
|
2195
|
+
preSugarIndsFoundInAttributes = [];
|
|
2196
|
+
|
|
2197
|
+
if (newChildren) {
|
|
2198
|
+
preSugarIndsFoundInChildren =
|
|
2199
|
+
findPreSugarIndsAndMarkFromSugar(newChildren);
|
|
2200
|
+
}
|
|
2201
|
+
if (newAttributes) {
|
|
2202
|
+
for (let attrName in newAttributes) {
|
|
2203
|
+
let comp = newAttributes[attrName].component;
|
|
2204
|
+
if (comp) {
|
|
2205
|
+
preSugarIndsFoundInAttributes.push(
|
|
2206
|
+
...findPreSugarIndsAndMarkFromSugar(comp.children),
|
|
2207
|
+
);
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
let preSugarIndsFound = [
|
|
2213
|
+
...preSugarIndsFoundInChildren,
|
|
2214
|
+
...preSugarIndsFoundInAttributes,
|
|
2215
|
+
];
|
|
2216
|
+
|
|
2217
|
+
if (
|
|
2218
|
+
preSugarIndsFound.length !== nNonStrings ||
|
|
2219
|
+
!preSugarIndsFound.sort((a, b) => a - b).every((v, i) => v === i)
|
|
2220
|
+
) {
|
|
2221
|
+
throw Error(
|
|
2222
|
+
`Invalid sugar for ${componentType} as didn't return set of original components`,
|
|
2223
|
+
);
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
if (preSugarIndsFoundInChildren.length > 0) {
|
|
2227
|
+
let sortedList = [...preSugarIndsFoundInChildren].sort(
|
|
2228
|
+
(a, b) => a - b,
|
|
2229
|
+
);
|
|
2230
|
+
if (
|
|
2231
|
+
!sortedList.every(
|
|
2232
|
+
(v, i) => v === preSugarIndsFoundInChildren[i],
|
|
2233
|
+
)
|
|
2234
|
+
) {
|
|
2235
|
+
throw Error(
|
|
2236
|
+
`Invalid sugar for ${componentType} as didn't return original components in order`,
|
|
2237
|
+
);
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
if (sugarResults.parametersForChildrenSugar) {
|
|
2242
|
+
Object.assign(
|
|
2243
|
+
newParentParametersFromSugar,
|
|
2244
|
+
sugarResults.parametersForChildrenSugar,
|
|
2245
|
+
);
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
if (newChildren) {
|
|
2249
|
+
component.children = newChildren;
|
|
2250
|
+
} else {
|
|
2251
|
+
component.children = [];
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
if (newAttributes) {
|
|
2255
|
+
if (!component.attributes) {
|
|
2256
|
+
component.attributes = {};
|
|
2257
|
+
}
|
|
2258
|
+
Object.assign(component.attributes, newAttributes);
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
if (componentClass.removeBlankStringChildrenPostSugar) {
|
|
2265
|
+
component.children = component.children.filter(
|
|
2266
|
+
(x) => typeof x !== "string" || /\S/.test(x),
|
|
2267
|
+
);
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
// Note: don't pass in isAttributeComponent
|
|
2271
|
+
// as that flag should be set just for the top level attribute component
|
|
2272
|
+
|
|
2273
|
+
applySugar({
|
|
2274
|
+
serializedComponents: component.children,
|
|
2275
|
+
parentParametersFromSugar: newParentParametersFromSugar,
|
|
2276
|
+
parentAttributes: componentAttributes,
|
|
2277
|
+
componentInfoObjects,
|
|
2278
|
+
});
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
if (component.attributes) {
|
|
2282
|
+
for (let attrName in component.attributes) {
|
|
2283
|
+
let attribute = component.attributes[attrName];
|
|
2284
|
+
|
|
2285
|
+
if (attribute.component) {
|
|
2286
|
+
applySugar({
|
|
2287
|
+
serializedComponents: [attribute.component],
|
|
2288
|
+
parentAttributes: componentAttributes,
|
|
2289
|
+
componentInfoObjects,
|
|
2290
|
+
isAttributeComponent: true,
|
|
2291
|
+
});
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
function breakStringInPiecesBySpacesOrParens(string) {
|
|
2299
|
+
if (typeof string !== "string") {
|
|
2300
|
+
return { success: false };
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
let Nparens = 0;
|
|
2304
|
+
let pieces = [];
|
|
2305
|
+
|
|
2306
|
+
string = string.trim();
|
|
2307
|
+
let beginInd = 0;
|
|
2308
|
+
|
|
2309
|
+
for (let ind = 0; ind < string.length; ind++) {
|
|
2310
|
+
let char = string[ind];
|
|
2311
|
+
if (char === "(") {
|
|
2312
|
+
if (Nparens === 0) {
|
|
2313
|
+
// beginning new parens piece
|
|
2314
|
+
// what have so far is a new piece
|
|
2315
|
+
let newPiece = string.substring(beginInd, ind).trim();
|
|
2316
|
+
if (newPiece.length > 0) {
|
|
2317
|
+
pieces.push(newPiece);
|
|
2318
|
+
}
|
|
2319
|
+
beginInd = ind;
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
Nparens++;
|
|
2323
|
+
} else if (char === ")") {
|
|
2324
|
+
if (Nparens === 0) {
|
|
2325
|
+
// parens didn't match, so return failure
|
|
2326
|
+
return { success: false };
|
|
2327
|
+
}
|
|
2328
|
+
if (Nparens === 1) {
|
|
2329
|
+
// found end of piece in parens
|
|
2330
|
+
let newPiece = string.substring(beginInd + 1, ind).trim();
|
|
2331
|
+
if (newPiece.length > 0) {
|
|
2332
|
+
// try to break further
|
|
2333
|
+
let result = breakStringInPiecesBySpacesOrParens(newPiece);
|
|
2334
|
+
if (result.success === true) {
|
|
2335
|
+
pieces.push(result.pieces);
|
|
2336
|
+
} else {
|
|
2337
|
+
pieces.push(newPiece);
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
beginInd = ind + 1;
|
|
2341
|
+
}
|
|
2342
|
+
Nparens--;
|
|
2343
|
+
} else if (Nparens === 0 && char.match(/\s/)) {
|
|
2344
|
+
// not in parens and found a space so potentially have a new piece
|
|
2345
|
+
let newPiece = string.substring(beginInd, ind).trim();
|
|
2346
|
+
if (newPiece.length > 0) {
|
|
2347
|
+
pieces.push(newPiece);
|
|
2348
|
+
}
|
|
2349
|
+
beginInd = ind;
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
// parens didn't match, so return failure
|
|
2354
|
+
if (Nparens !== 0) {
|
|
2355
|
+
return { success: false };
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
let newPiece = string.substring(beginInd, string.length).trim();
|
|
2359
|
+
if (newPiece.length > 0) {
|
|
2360
|
+
pieces.push(newPiece);
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
return {
|
|
2364
|
+
success: true,
|
|
2365
|
+
pieces: pieces,
|
|
2366
|
+
};
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
export function countRegularComponentTypesInNamespace(
|
|
2370
|
+
serializedComponents,
|
|
2371
|
+
componentCounts = {},
|
|
2372
|
+
) {
|
|
2373
|
+
for (let serializedComponent of serializedComponents) {
|
|
2374
|
+
if (typeof serializedComponent === "object") {
|
|
2375
|
+
let componentType = serializedComponent.componentType;
|
|
2376
|
+
|
|
2377
|
+
let count = componentCounts[componentType];
|
|
2378
|
+
if (count === undefined) {
|
|
2379
|
+
count = 0;
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
let doenetAttributes = serializedComponent.doenetAttributes;
|
|
2383
|
+
|
|
2384
|
+
// if created from a attribute/sugar/macro, don't include in component counts
|
|
2385
|
+
if (
|
|
2386
|
+
!(
|
|
2387
|
+
doenetAttributes?.isAttributeChild ||
|
|
2388
|
+
doenetAttributes?.createdFromSugar ||
|
|
2389
|
+
doenetAttributes?.createdFromMacro
|
|
2390
|
+
)
|
|
2391
|
+
) {
|
|
2392
|
+
componentCounts[componentType] = ++count;
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
if (
|
|
2396
|
+
serializedComponent.children &&
|
|
2397
|
+
!serializedComponent.attributes?.newNamespace?.primitive
|
|
2398
|
+
) {
|
|
2399
|
+
// if don't have new namespace, recurse to children
|
|
2400
|
+
componentCounts = countRegularComponentTypesInNamespace(
|
|
2401
|
+
serializedComponent.children,
|
|
2402
|
+
componentCounts,
|
|
2403
|
+
);
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
return componentCounts;
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
export function renameAutonameBasedOnNewCounts(
|
|
2412
|
+
serializedComponents,
|
|
2413
|
+
newComponentCounts = {},
|
|
2414
|
+
) {
|
|
2415
|
+
let componentCounts = { ...newComponentCounts };
|
|
2416
|
+
|
|
2417
|
+
for (let serializedComponent of serializedComponents) {
|
|
2418
|
+
if (typeof serializedComponent === "object") {
|
|
2419
|
+
let componentType = serializedComponent.componentType;
|
|
2420
|
+
|
|
2421
|
+
let count = componentCounts[componentType];
|
|
2422
|
+
if (count === undefined) {
|
|
2423
|
+
count = 0;
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
let doenetAttributes = serializedComponent.doenetAttributes;
|
|
2427
|
+
|
|
2428
|
+
// if created from a attribute/sugar/macro, don't include in component counts
|
|
2429
|
+
if (
|
|
2430
|
+
!(
|
|
2431
|
+
doenetAttributes?.isAttributeChild ||
|
|
2432
|
+
doenetAttributes?.createdFromSugar ||
|
|
2433
|
+
doenetAttributes?.createdFromMacro
|
|
2434
|
+
)
|
|
2435
|
+
) {
|
|
2436
|
+
componentCounts[componentType] = ++count;
|
|
2437
|
+
|
|
2438
|
+
// check if name was created from counting components
|
|
2439
|
+
|
|
2440
|
+
if (serializedComponent.componentName) {
|
|
2441
|
+
let lastSlash = serializedComponent.componentName.lastIndexOf("/");
|
|
2442
|
+
let originalName = serializedComponent.componentName.substring(
|
|
2443
|
+
lastSlash + 1,
|
|
2444
|
+
);
|
|
2445
|
+
let nameStartFromComponentType = "_" + componentType.toLowerCase();
|
|
2446
|
+
if (
|
|
2447
|
+
originalName.substring(0, nameStartFromComponentType.length) ===
|
|
2448
|
+
nameStartFromComponentType
|
|
2449
|
+
) {
|
|
2450
|
+
// recreate using new count
|
|
2451
|
+
serializedComponent.componentName =
|
|
2452
|
+
serializedComponent.componentName.substring(0, lastSlash + 1) +
|
|
2453
|
+
nameStartFromComponentType +
|
|
2454
|
+
count;
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
if (
|
|
2460
|
+
serializedComponent.children &&
|
|
2461
|
+
!serializedComponent.attributes?.newNamespace?.primitive
|
|
2462
|
+
) {
|
|
2463
|
+
// if don't have new namespace, recurse to children
|
|
2464
|
+
componentCounts = renameAutonameBasedOnNewCounts(
|
|
2465
|
+
serializedComponent.children,
|
|
2466
|
+
componentCounts,
|
|
2467
|
+
);
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
return componentCounts;
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2475
|
+
export function createComponentNames({
|
|
2476
|
+
serializedComponents,
|
|
2477
|
+
namespaceStack = [],
|
|
2478
|
+
componentInfoObjects,
|
|
2479
|
+
parentDoenetAttributes = {},
|
|
2480
|
+
parentName,
|
|
2481
|
+
useOriginalNames = false,
|
|
2482
|
+
attributesByTargetComponentName,
|
|
2483
|
+
indOffset = 0,
|
|
2484
|
+
createNameContext = "",
|
|
2485
|
+
initWithoutShadowingComposite = false,
|
|
2486
|
+
}) {
|
|
2487
|
+
if (namespaceStack.length === 0) {
|
|
2488
|
+
namespaceStack.push({ namespace: "", componentCounts: {}, namesUsed: {} });
|
|
2489
|
+
}
|
|
2490
|
+
let level = namespaceStack.length - 1;
|
|
2491
|
+
|
|
2492
|
+
// console.log("createComponentNames " + level);
|
|
2493
|
+
// console.log(serializedComponents);
|
|
2494
|
+
// console.log(namespaceStack);
|
|
2495
|
+
|
|
2496
|
+
let currentNamespace = namespaceStack[level];
|
|
2497
|
+
|
|
2498
|
+
for (let [
|
|
2499
|
+
componentInd,
|
|
2500
|
+
serializedComponent,
|
|
2501
|
+
] of serializedComponents.entries()) {
|
|
2502
|
+
if (typeof serializedComponent !== "object") {
|
|
2503
|
+
continue;
|
|
2504
|
+
}
|
|
2505
|
+
let componentType = serializedComponent.componentType;
|
|
2506
|
+
let componentClass =
|
|
2507
|
+
componentInfoObjects.allComponentClasses[componentType];
|
|
2508
|
+
|
|
2509
|
+
let doenetAttributes = serializedComponent.doenetAttributes;
|
|
2510
|
+
if (doenetAttributes === undefined) {
|
|
2511
|
+
doenetAttributes = serializedComponent.doenetAttributes = {};
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
let attributes = serializedComponent.attributes;
|
|
2515
|
+
if (!attributes) {
|
|
2516
|
+
attributes = serializedComponent.attributes = {};
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
if (doenetAttributes.createNameFromComponentType) {
|
|
2520
|
+
componentType = doenetAttributes.createNameFromComponentType;
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
let prescribedName = doenetAttributes.prescribedName;
|
|
2524
|
+
let assignNames = doenetAttributes.assignNames;
|
|
2525
|
+
let target = doenetAttributes.target;
|
|
2526
|
+
// let propName = doenetAttributes.propName;
|
|
2527
|
+
// let type = doenetAttributes.type;
|
|
2528
|
+
// let alias = doenetAttributes.alias;
|
|
2529
|
+
// let indexAlias = doenetAttributes.indexAlias;
|
|
2530
|
+
|
|
2531
|
+
let mustCreateUniqueName =
|
|
2532
|
+
doenetAttributes.isAttributeChild ||
|
|
2533
|
+
doenetAttributes.createdFromSugar ||
|
|
2534
|
+
doenetAttributes.createdFromMacro ||
|
|
2535
|
+
doenetAttributes.createUniqueName;
|
|
2536
|
+
|
|
2537
|
+
let newNamespace;
|
|
2538
|
+
if (
|
|
2539
|
+
attributes.newNamespace?.primitive ||
|
|
2540
|
+
(useOriginalNames &&
|
|
2541
|
+
serializedComponent.originalAttributes &&
|
|
2542
|
+
serializedComponent.originalAttributes.newNamespace)
|
|
2543
|
+
) {
|
|
2544
|
+
newNamespace = true;
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
let prescribedNameFromDoenetAttributes = prescribedName !== undefined;
|
|
2548
|
+
|
|
2549
|
+
let props = serializedComponent.props;
|
|
2550
|
+
if (props === undefined) {
|
|
2551
|
+
props = serializedComponent.props = {};
|
|
2552
|
+
} else {
|
|
2553
|
+
// look for a attribute that matches an prop
|
|
2554
|
+
// but case insensitive
|
|
2555
|
+
for (let key in props) {
|
|
2556
|
+
let lowercaseKey = key.toLowerCase();
|
|
2557
|
+
if (lowercaseKey === "name") {
|
|
2558
|
+
if (prescribedName === undefined) {
|
|
2559
|
+
prescribedName = props[key];
|
|
2560
|
+
delete props[key];
|
|
2561
|
+
} else {
|
|
2562
|
+
throw Error(
|
|
2563
|
+
`Cannot define name twice. Found in component of type ${componentType}${indexRangeString(
|
|
2564
|
+
serializedComponent,
|
|
2565
|
+
)}`,
|
|
2566
|
+
);
|
|
2567
|
+
}
|
|
2568
|
+
} else if (lowercaseKey === "assignnames") {
|
|
2569
|
+
if (assignNames === undefined) {
|
|
2570
|
+
let result = breakStringInPiecesBySpacesOrParens(props[key]);
|
|
2571
|
+
if (result.success) {
|
|
2572
|
+
assignNames = result.pieces;
|
|
2573
|
+
} else {
|
|
2574
|
+
throw Error(
|
|
2575
|
+
`Invalid format for assignnames. Found in component of type ${componentType}${indexRangeString(
|
|
2576
|
+
serializedComponent,
|
|
2577
|
+
)}`,
|
|
2578
|
+
);
|
|
2579
|
+
}
|
|
2580
|
+
delete props[key];
|
|
2581
|
+
} else {
|
|
2582
|
+
throw Error(
|
|
2583
|
+
`Cannot define assignNames twice for a component. Found in component of type ${componentType}${indexRangeString(
|
|
2584
|
+
serializedComponent,
|
|
2585
|
+
)}`,
|
|
2586
|
+
);
|
|
2587
|
+
}
|
|
2588
|
+
} else if (lowercaseKey === "target") {
|
|
2589
|
+
if (target === undefined) {
|
|
2590
|
+
if (typeof props[key] !== "string") {
|
|
2591
|
+
throw Error(
|
|
2592
|
+
`Must specify value for target. Found in component of type ${componentType}${indexRangeString(
|
|
2593
|
+
serializedComponent,
|
|
2594
|
+
)}`,
|
|
2595
|
+
);
|
|
2596
|
+
}
|
|
2597
|
+
target = props[key].trim();
|
|
2598
|
+
delete props[key];
|
|
2599
|
+
} else {
|
|
2600
|
+
throw Error(
|
|
2601
|
+
`Cannot define target twice for a component. Found in component of type ${componentType}${indexRangeString(
|
|
2602
|
+
serializedComponent,
|
|
2603
|
+
)}`,
|
|
2604
|
+
);
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
if (prescribedName) {
|
|
2611
|
+
if (
|
|
2612
|
+
!prescribedNameFromDoenetAttributes &&
|
|
2613
|
+
!doenetAttributes.createdFromSugar
|
|
2614
|
+
) {
|
|
2615
|
+
if (!/[a-zA-Z]/.test(prescribedName.substring(0, 1))) {
|
|
2616
|
+
throw Error(
|
|
2617
|
+
`Invalid component name: ${prescribedName}. Component name must begin with a letter. Found in component of type ${componentType}${indexRangeString(
|
|
2618
|
+
serializedComponent,
|
|
2619
|
+
)}`,
|
|
2620
|
+
);
|
|
2621
|
+
}
|
|
2622
|
+
if (!/^[a-zA-Z0-9_\-]+$/.test(prescribedName)) {
|
|
2623
|
+
throw Error(
|
|
2624
|
+
`Invalid component name: ${prescribedName}. Component name can contain only letters, numbers, hyphens, and underscores. Found in component of type ${componentType}${indexRangeString(
|
|
2625
|
+
serializedComponent,
|
|
2626
|
+
)}`,
|
|
2627
|
+
);
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2631
|
+
// name was specified
|
|
2632
|
+
// put it into doenetAttributes
|
|
2633
|
+
doenetAttributes.prescribedName = prescribedName;
|
|
2634
|
+
} else if (mustCreateUniqueName) {
|
|
2635
|
+
let longNameId = parentName + "|createUniqueName|";
|
|
2636
|
+
|
|
2637
|
+
if (serializedComponent.downstreamDependencies) {
|
|
2638
|
+
longNameId += JSON.stringify(
|
|
2639
|
+
serializedComponent.downstreamDependencies,
|
|
2640
|
+
);
|
|
2641
|
+
} else {
|
|
2642
|
+
longNameId += componentInd + "|" + indOffset + "|" + createNameContext;
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
prescribedName = createUniqueName(
|
|
2646
|
+
componentType.toLowerCase(),
|
|
2647
|
+
longNameId,
|
|
2648
|
+
);
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
if (
|
|
2652
|
+
!assignNames &&
|
|
2653
|
+
useOriginalNames &&
|
|
2654
|
+
serializedComponent.originalDoenetAttributes &&
|
|
2655
|
+
serializedComponent.originalDoenetAttributes.assignNames
|
|
2656
|
+
) {
|
|
2657
|
+
assignNames = serializedComponent.originalDoenetAttributes.assignNames;
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
if (assignNames) {
|
|
2661
|
+
let assignNamesToReplacements = componentClass.assignNamesToReplacements;
|
|
2662
|
+
if (!assignNamesToReplacements) {
|
|
2663
|
+
throw Error(
|
|
2664
|
+
`Cannot assign names for component type ${componentType}${indexRangeString(
|
|
2665
|
+
serializedComponent,
|
|
2666
|
+
)}`,
|
|
2667
|
+
);
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
// assignNames was specified
|
|
2671
|
+
// put in doenetAttributes as assignNames array
|
|
2672
|
+
doenetAttributes.assignNames = assignNames;
|
|
2673
|
+
|
|
2674
|
+
if (!doenetAttributes.createUniqueAssignNames) {
|
|
2675
|
+
let flattenedNames = flattenDeep(assignNames);
|
|
2676
|
+
if (
|
|
2677
|
+
!doenetAttributes.fromCopyTarget &&
|
|
2678
|
+
!doenetAttributes.fromCopyFromURI
|
|
2679
|
+
) {
|
|
2680
|
+
for (let name of flattenedNames) {
|
|
2681
|
+
if (!/[a-zA-Z]/.test(name.substring(0, 1))) {
|
|
2682
|
+
throw Error(
|
|
2683
|
+
`All assigned names must begin with a letter. Found in component of type ${componentType}${indexRangeString(
|
|
2684
|
+
serializedComponent,
|
|
2685
|
+
)}`,
|
|
2686
|
+
);
|
|
2687
|
+
}
|
|
2688
|
+
if (!/^[a-zA-Z0-9_\-]+$/.test(name)) {
|
|
2689
|
+
throw Error(
|
|
2690
|
+
`Assigned names can contain only letters, numbers, hyphens, and underscores. Found in component of type ${componentType}${indexRangeString(
|
|
2691
|
+
serializedComponent,
|
|
2692
|
+
)}`,
|
|
2693
|
+
);
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
// check if unique names
|
|
2698
|
+
if (flattenedNames.length !== new Set(flattenedNames).size) {
|
|
2699
|
+
throw Error(
|
|
2700
|
+
`Duplicate assigned names. Found in component of type ${componentType}${indexRangeString(
|
|
2701
|
+
serializedComponent,
|
|
2702
|
+
)}`,
|
|
2703
|
+
);
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
if (newNamespace) {
|
|
2709
|
+
// newNamespace was specified
|
|
2710
|
+
// put in attributes as boolean
|
|
2711
|
+
attributes.newNamespace = { primitive: newNamespace };
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2714
|
+
let count = currentNamespace.componentCounts[componentType];
|
|
2715
|
+
if (count === undefined) {
|
|
2716
|
+
count = 0;
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
// if created from a attribute/sugar/macro, don't include in component counts
|
|
2720
|
+
if (
|
|
2721
|
+
!(
|
|
2722
|
+
doenetAttributes.isAttributeChild ||
|
|
2723
|
+
doenetAttributes.createdFromSugar ||
|
|
2724
|
+
doenetAttributes.createdFromMacro
|
|
2725
|
+
)
|
|
2726
|
+
) {
|
|
2727
|
+
currentNamespace.componentCounts[componentType] = ++count;
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
let componentName = "";
|
|
2731
|
+
for (let l = 0; l <= level; l++) {
|
|
2732
|
+
componentName += namespaceStack[l].namespace + "/";
|
|
2733
|
+
}
|
|
2734
|
+
if (!prescribedName) {
|
|
2735
|
+
if (useOriginalNames) {
|
|
2736
|
+
if (serializedComponent.originalName) {
|
|
2737
|
+
let lastInd = serializedComponent.originalName.lastIndexOf("/");
|
|
2738
|
+
prescribedName = serializedComponent.originalName.substring(
|
|
2739
|
+
lastInd + 1,
|
|
2740
|
+
);
|
|
2741
|
+
// } else if (serializedComponent.componentName) {
|
|
2742
|
+
// let lastInd = serializedComponent.componentName.lastIndexOf("/");
|
|
2743
|
+
// prescribedName = serializedComponent.componentName.substring(lastInd + 1);
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
if (!prescribedName) {
|
|
2747
|
+
prescribedName = "_" + componentType.toLowerCase() + count;
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2751
|
+
if (doenetAttributes.nameBecomesAssignNames) {
|
|
2752
|
+
if (newNamespace) {
|
|
2753
|
+
// delete newNamespace from target but make it assignNewNamespaces
|
|
2754
|
+
attributes.assignNewNamespaces = { primitive: true };
|
|
2755
|
+
delete attributes.newNamespace;
|
|
2756
|
+
newNamespace = false;
|
|
2757
|
+
}
|
|
2758
|
+
assignNames = doenetAttributes.assignNames = [prescribedName];
|
|
2759
|
+
|
|
2760
|
+
// delete nameBecomesAssignNames so that copies
|
|
2761
|
+
// or further applications of createComponentNames
|
|
2762
|
+
// do not repeat this process and make assignNames be the randomly generated name
|
|
2763
|
+
delete doenetAttributes.nameBecomesAssignNames;
|
|
2764
|
+
|
|
2765
|
+
// create unique name for copy
|
|
2766
|
+
let longNameId = parentName + "|createUniqueName|";
|
|
2767
|
+
doenetAttributes.createUniqueName = true;
|
|
2768
|
+
delete doenetAttributes.prescribedName;
|
|
2769
|
+
|
|
2770
|
+
if (serializedComponent.downstreamDependencies) {
|
|
2771
|
+
longNameId += JSON.stringify(
|
|
2772
|
+
serializedComponent.downstreamDependencies,
|
|
2773
|
+
);
|
|
2774
|
+
} else {
|
|
2775
|
+
longNameId += componentInd + "|" + indOffset + "|" + createNameContext;
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2778
|
+
prescribedName = createUniqueName("copy", longNameId);
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
componentName += prescribedName;
|
|
2782
|
+
|
|
2783
|
+
serializedComponent.componentName = componentName;
|
|
2784
|
+
if (prescribedName) {
|
|
2785
|
+
if (prescribedName in currentNamespace.namesUsed) {
|
|
2786
|
+
throw Error(
|
|
2787
|
+
`Duplicate component name ${componentName}. Found in component of type ${componentType}${indexRangeString(
|
|
2788
|
+
serializedComponent,
|
|
2789
|
+
)}`,
|
|
2790
|
+
);
|
|
2791
|
+
}
|
|
2792
|
+
currentNamespace.namesUsed[prescribedName] = true;
|
|
2793
|
+
}
|
|
2794
|
+
|
|
2795
|
+
// if newNamespace is false,
|
|
2796
|
+
// then register assignNames as belonging to current namespace
|
|
2797
|
+
if (!newNamespace) {
|
|
2798
|
+
if (assignNames) {
|
|
2799
|
+
for (let name of flattenDeep(assignNames)) {
|
|
2800
|
+
if (name in currentNamespace.namesUsed) {
|
|
2801
|
+
throw Error(
|
|
2802
|
+
`Duplicate component name ${name} (from assignNames of ${componentName}). Found in component of type ${componentType}${indexRangeString(
|
|
2803
|
+
serializedComponent,
|
|
2804
|
+
)}`,
|
|
2805
|
+
);
|
|
2806
|
+
}
|
|
2807
|
+
currentNamespace.namesUsed[name] = true;
|
|
2808
|
+
}
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
if (
|
|
2813
|
+
serializedComponent.doenetAttributes.createUniqueAssignNames &&
|
|
2814
|
+
serializedComponent.originalName
|
|
2815
|
+
) {
|
|
2816
|
+
let originalAssignNames =
|
|
2817
|
+
serializedComponent.doenetAttributes.assignNames;
|
|
2818
|
+
if (!originalAssignNames) {
|
|
2819
|
+
originalAssignNames =
|
|
2820
|
+
serializedComponent.doenetAttributes.originalAssignNames;
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2823
|
+
let longNameIdBase = componentName + "|createUniqueName|assignNames|";
|
|
2824
|
+
|
|
2825
|
+
let namespace = "";
|
|
2826
|
+
let oldNamespace;
|
|
2827
|
+
if (!newNamespace) {
|
|
2828
|
+
for (let l = 0; l <= level; l++) {
|
|
2829
|
+
namespace += namespaceStack[l].namespace + "/";
|
|
2830
|
+
}
|
|
2831
|
+
let lastInd = serializedComponent.originalName.lastIndexOf("/");
|
|
2832
|
+
oldNamespace = serializedComponent.originalName.slice(0, lastInd + 1);
|
|
2833
|
+
} else {
|
|
2834
|
+
namespace = componentName + "/";
|
|
2835
|
+
oldNamespace = serializedComponent.originalName + "/";
|
|
2836
|
+
}
|
|
2837
|
+
|
|
2838
|
+
let newAssignNames = createNewAssignNamesAndrenameMatchingTargetNames({
|
|
2839
|
+
originalAssignNames,
|
|
2840
|
+
longNameIdBase,
|
|
2841
|
+
namespace,
|
|
2842
|
+
oldNamespace,
|
|
2843
|
+
attributesByTargetComponentName,
|
|
2844
|
+
});
|
|
2845
|
+
|
|
2846
|
+
assignNames = serializedComponent.doenetAttributes.assignNames =
|
|
2847
|
+
newAssignNames;
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
renameMatchingTargetNames(
|
|
2851
|
+
serializedComponent,
|
|
2852
|
+
attributesByTargetComponentName,
|
|
2853
|
+
);
|
|
2854
|
+
|
|
2855
|
+
if (target) {
|
|
2856
|
+
if (!componentClass.acceptTarget) {
|
|
2857
|
+
throw Error(
|
|
2858
|
+
`Component type ${componentType} does not accept a target attribute. Found in component ${componentName}${indexRangeString(
|
|
2859
|
+
serializedComponent,
|
|
2860
|
+
)}`,
|
|
2861
|
+
);
|
|
2862
|
+
}
|
|
2863
|
+
|
|
2864
|
+
if (target.includes("|")) {
|
|
2865
|
+
throw Error(
|
|
2866
|
+
`target cannot include |. Found in component of type ${componentType}${indexRangeString(
|
|
2867
|
+
serializedComponent,
|
|
2868
|
+
)}`,
|
|
2869
|
+
);
|
|
2870
|
+
}
|
|
2871
|
+
|
|
2872
|
+
// convert target to full name
|
|
2873
|
+
doenetAttributes.target = target;
|
|
2874
|
+
|
|
2875
|
+
doenetAttributes.targetComponentName = convertComponentTarget({
|
|
2876
|
+
relativeName: target,
|
|
2877
|
+
oldAbsoluteName: doenetAttributes.targetComponentName,
|
|
2878
|
+
namespaceStack,
|
|
2879
|
+
acceptDoubleUnderscore:
|
|
2880
|
+
doenetAttributes.createdFromSugar ||
|
|
2881
|
+
doenetAttributes.allowDoubleUnderscoreTarget,
|
|
2882
|
+
});
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
for (let attrName in attributes) {
|
|
2886
|
+
let attr = attributes[attrName];
|
|
2887
|
+
if (attr.targetComponentNames) {
|
|
2888
|
+
for (let nameObj of attr.targetComponentNames) {
|
|
2889
|
+
nameObj.absoluteName = convertComponentTarget({
|
|
2890
|
+
relativeName: nameObj.relativeName,
|
|
2891
|
+
oldAbsoluteName: nameObj.absoluteName,
|
|
2892
|
+
namespaceStack,
|
|
2893
|
+
acceptDoubleUnderscore:
|
|
2894
|
+
doenetAttributes.createdFromSugar ||
|
|
2895
|
+
doenetAttributes.allowDoubleUnderscoreTarget,
|
|
2896
|
+
});
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
if (serializedComponent.children) {
|
|
2902
|
+
// recurse on child, creating new namespace if specified
|
|
2903
|
+
|
|
2904
|
+
if (!(newNamespace || attributes.assignNewNamespaces?.primitive)) {
|
|
2905
|
+
let children = serializedComponent.children;
|
|
2906
|
+
|
|
2907
|
+
if (
|
|
2908
|
+
doenetAttributes.nameFirstChildIndependently &&
|
|
2909
|
+
children.length > 0
|
|
2910
|
+
) {
|
|
2911
|
+
// when creating names for first child, ignore all previous names and treat it as a separate unit
|
|
2912
|
+
|
|
2913
|
+
children = children.slice(1);
|
|
2914
|
+
|
|
2915
|
+
let originalNamesUsed = currentNamespace.namesUsed;
|
|
2916
|
+
let originalComponentCounts = currentNamespace.componentCounts;
|
|
2917
|
+
currentNamespace.namesUsed = {};
|
|
2918
|
+
currentNamespace.componentCounts = {};
|
|
2919
|
+
|
|
2920
|
+
createComponentNames({
|
|
2921
|
+
serializedComponents: [serializedComponent.children[0]],
|
|
2922
|
+
namespaceStack,
|
|
2923
|
+
componentInfoObjects,
|
|
2924
|
+
parentDoenetAttributes: doenetAttributes,
|
|
2925
|
+
parentName: componentName,
|
|
2926
|
+
useOriginalNames,
|
|
2927
|
+
attributesByTargetComponentName,
|
|
2928
|
+
});
|
|
2929
|
+
|
|
2930
|
+
currentNamespace.namesUsed = originalNamesUsed;
|
|
2931
|
+
currentNamespace.componentCounts = originalComponentCounts;
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
createComponentNames({
|
|
2935
|
+
serializedComponents: children,
|
|
2936
|
+
namespaceStack,
|
|
2937
|
+
componentInfoObjects,
|
|
2938
|
+
parentDoenetAttributes: doenetAttributes,
|
|
2939
|
+
parentName: componentName,
|
|
2940
|
+
useOriginalNames,
|
|
2941
|
+
attributesByTargetComponentName,
|
|
2942
|
+
});
|
|
2943
|
+
} else {
|
|
2944
|
+
// if newNamespace, then need to make sure that assigned names
|
|
2945
|
+
// don't conflict with new names added,
|
|
2946
|
+
// so include in namesused
|
|
2947
|
+
let namesUsed = {};
|
|
2948
|
+
// if (assignNames && !componentClass.assignNamesToChildren) {
|
|
2949
|
+
if (assignNames) {
|
|
2950
|
+
flattenDeep(assignNames).forEach((x) => (namesUsed[x] = true));
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
let children = serializedComponent.children;
|
|
2954
|
+
|
|
2955
|
+
if (
|
|
2956
|
+
doenetAttributes.nameFirstChildIndependently &&
|
|
2957
|
+
serializedComponent.children.length > 0
|
|
2958
|
+
) {
|
|
2959
|
+
// when creating names for first child, ignore all previous names and treat it as a separate unit
|
|
2960
|
+
|
|
2961
|
+
children = children.slice(1);
|
|
2962
|
+
|
|
2963
|
+
let separateNewNamespaceInfo = {
|
|
2964
|
+
namespace: prescribedName,
|
|
2965
|
+
componentCounts: {},
|
|
2966
|
+
namesUsed: {},
|
|
2967
|
+
};
|
|
2968
|
+
namespaceStack.push(separateNewNamespaceInfo);
|
|
2969
|
+
|
|
2970
|
+
createComponentNames({
|
|
2971
|
+
serializedComponents: [serializedComponent.children[0]],
|
|
2972
|
+
namespaceStack,
|
|
2973
|
+
componentInfoObjects,
|
|
2974
|
+
parentDoenetAttributes: doenetAttributes,
|
|
2975
|
+
parentName: componentName,
|
|
2976
|
+
useOriginalNames,
|
|
2977
|
+
attributesByTargetComponentName,
|
|
2978
|
+
});
|
|
2979
|
+
|
|
2980
|
+
namespaceStack.pop();
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2983
|
+
let newNamespaceInfo = {
|
|
2984
|
+
namespace: prescribedName,
|
|
2985
|
+
componentCounts: {},
|
|
2986
|
+
namesUsed,
|
|
2987
|
+
};
|
|
2988
|
+
|
|
2989
|
+
if (doenetAttributes.haveNewNamespaceOnlyFromShadow) {
|
|
2990
|
+
// if the parent component only has newNamespace from the fact that it is a shadow,
|
|
2991
|
+
// as opposed to explicitly getting it from assignNewNamespaces,
|
|
2992
|
+
// then, if a child is marked to ignore parent's newNamespace, it ignores it
|
|
2993
|
+
// Note: ignoreParentNewNamespace is only added when have fromCopyTarget
|
|
2994
|
+
|
|
2995
|
+
let addingNewNamespace = true;
|
|
2996
|
+
let remainingChildren = [...children];
|
|
2997
|
+
|
|
2998
|
+
while (remainingChildren.length > 0) {
|
|
2999
|
+
let nextChildren = [];
|
|
3000
|
+
|
|
3001
|
+
for (let child of remainingChildren) {
|
|
3002
|
+
if (
|
|
3003
|
+
Boolean(child.doenetAttributes?.ignoreParentNewNamespace) ===
|
|
3004
|
+
addingNewNamespace
|
|
3005
|
+
) {
|
|
3006
|
+
break;
|
|
3007
|
+
}
|
|
3008
|
+
nextChildren.push(child);
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
remainingChildren.splice(0, nextChildren.length);
|
|
3012
|
+
|
|
3013
|
+
if (addingNewNamespace) {
|
|
3014
|
+
namespaceStack.push(newNamespaceInfo);
|
|
3015
|
+
} else if (initWithoutShadowingComposite) {
|
|
3016
|
+
// if this is the first time through and we aren't shadowing a composite
|
|
3017
|
+
// it is possible that ignoring the namespace will lead to name conflicts,
|
|
3018
|
+
// so give the child a unique name
|
|
3019
|
+
nextChildren.forEach(
|
|
3020
|
+
(child) => (child.doenetAttributes.createUniqueName = true),
|
|
3021
|
+
);
|
|
3022
|
+
}
|
|
3023
|
+
|
|
3024
|
+
createComponentNames({
|
|
3025
|
+
serializedComponents: nextChildren,
|
|
3026
|
+
namespaceStack,
|
|
3027
|
+
componentInfoObjects,
|
|
3028
|
+
parentDoenetAttributes: doenetAttributes,
|
|
3029
|
+
parentName: componentName,
|
|
3030
|
+
useOriginalNames,
|
|
3031
|
+
attributesByTargetComponentName,
|
|
3032
|
+
});
|
|
3033
|
+
|
|
3034
|
+
if (addingNewNamespace) {
|
|
3035
|
+
namespaceStack.pop();
|
|
3036
|
+
}
|
|
3037
|
+
|
|
3038
|
+
addingNewNamespace = !addingNewNamespace;
|
|
3039
|
+
}
|
|
3040
|
+
} else {
|
|
3041
|
+
namespaceStack.push(newNamespaceInfo);
|
|
3042
|
+
createComponentNames({
|
|
3043
|
+
serializedComponents: children,
|
|
3044
|
+
namespaceStack,
|
|
3045
|
+
componentInfoObjects,
|
|
3046
|
+
parentDoenetAttributes: doenetAttributes,
|
|
3047
|
+
parentName: componentName,
|
|
3048
|
+
useOriginalNames,
|
|
3049
|
+
attributesByTargetComponentName,
|
|
3050
|
+
});
|
|
3051
|
+
namespaceStack.pop();
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
|
|
3056
|
+
if (serializedComponent.attributes) {
|
|
3057
|
+
// recurse on attributes that are components
|
|
3058
|
+
|
|
3059
|
+
for (let attrName in serializedComponent.attributes) {
|
|
3060
|
+
let attribute = serializedComponent.attributes[attrName];
|
|
3061
|
+
|
|
3062
|
+
if (attribute.component) {
|
|
3063
|
+
let comp = attribute.component;
|
|
3064
|
+
|
|
3065
|
+
if (!comp.doenetAttributes) {
|
|
3066
|
+
comp.doenetAttributes = {};
|
|
3067
|
+
}
|
|
3068
|
+
|
|
3069
|
+
comp.doenetAttributes.isAttributeChild = true;
|
|
3070
|
+
if (attribute.ignoreFixed) {
|
|
3071
|
+
comp.doenetAttributes.ignoreParentFixed = true;
|
|
3072
|
+
}
|
|
3073
|
+
|
|
3074
|
+
createComponentNames({
|
|
3075
|
+
serializedComponents: [comp],
|
|
3076
|
+
namespaceStack,
|
|
3077
|
+
componentInfoObjects,
|
|
3078
|
+
parentDoenetAttributes: doenetAttributes,
|
|
3079
|
+
parentName: componentName,
|
|
3080
|
+
useOriginalNames,
|
|
3081
|
+
attributesByTargetComponentName,
|
|
3082
|
+
createNameContext: attrName,
|
|
3083
|
+
});
|
|
3084
|
+
} else if (attribute.childrenForComponent) {
|
|
3085
|
+
// TODO: what to do about parentName/parentDoenetAttributes
|
|
3086
|
+
// since parent of these isn't created
|
|
3087
|
+
// Note: the main (only?) to recurse here is to rename targets
|
|
3088
|
+
createComponentNames({
|
|
3089
|
+
serializedComponents: attribute.childrenForComponent,
|
|
3090
|
+
namespaceStack,
|
|
3091
|
+
componentInfoObjects,
|
|
3092
|
+
parentDoenetAttributes: doenetAttributes,
|
|
3093
|
+
parentName: componentName,
|
|
3094
|
+
useOriginalNames,
|
|
3095
|
+
attributesByTargetComponentName,
|
|
3096
|
+
createNameContext: attrName,
|
|
3097
|
+
});
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
|
|
3102
|
+
// TODO: is there any reason to run createComponentNames on attribute components?
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
return serializedComponents;
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
function createNewAssignNamesAndrenameMatchingTargetNames({
|
|
3109
|
+
originalAssignNames,
|
|
3110
|
+
longNameIdBase,
|
|
3111
|
+
namespace,
|
|
3112
|
+
oldNamespace,
|
|
3113
|
+
attributesByTargetComponentName,
|
|
3114
|
+
}) {
|
|
3115
|
+
let assignNames = [];
|
|
3116
|
+
|
|
3117
|
+
for (let [ind, originalName] of originalAssignNames.entries()) {
|
|
3118
|
+
if (Array.isArray(originalName)) {
|
|
3119
|
+
// recurse to next level
|
|
3120
|
+
let assignNamesSub = createNewAssignNamesAndrenameMatchingTargetNames({
|
|
3121
|
+
originalAssignNames: originalName,
|
|
3122
|
+
longNameIdBase: longNameIdBase + ind + "_",
|
|
3123
|
+
namespace,
|
|
3124
|
+
oldNamespace,
|
|
3125
|
+
attributesByTargetComponentName,
|
|
3126
|
+
});
|
|
3127
|
+
assignNames.push(assignNamesSub);
|
|
3128
|
+
} else {
|
|
3129
|
+
let longNameId = longNameIdBase + ind;
|
|
3130
|
+
let newName = createUniqueName("fromAssignNames", longNameId);
|
|
3131
|
+
assignNames.push(newName);
|
|
3132
|
+
|
|
3133
|
+
let infoForRenaming = {
|
|
3134
|
+
componentName: namespace + newName,
|
|
3135
|
+
originalName: oldNamespace + originalName,
|
|
3136
|
+
};
|
|
3137
|
+
|
|
3138
|
+
renameMatchingTargetNames(
|
|
3139
|
+
infoForRenaming,
|
|
3140
|
+
attributesByTargetComponentName,
|
|
3141
|
+
true,
|
|
3142
|
+
);
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
|
|
3146
|
+
return assignNames;
|
|
3147
|
+
}
|
|
3148
|
+
|
|
3149
|
+
export function convertComponentTarget({
|
|
3150
|
+
relativeName,
|
|
3151
|
+
oldAbsoluteName,
|
|
3152
|
+
namespaceStack,
|
|
3153
|
+
acceptDoubleUnderscore,
|
|
3154
|
+
}) {
|
|
3155
|
+
if (!oldAbsoluteName && /__/.test(relativeName) && !acceptDoubleUnderscore) {
|
|
3156
|
+
throw Error("Invalid reference target: " + relativeName);
|
|
3157
|
+
}
|
|
3158
|
+
|
|
3159
|
+
let absoluteName;
|
|
3160
|
+
|
|
3161
|
+
// console.log(`relativeName: ${relativeName}`)
|
|
3162
|
+
// console.log(JSON.parse(JSON.stringify(namespaceStack)))
|
|
3163
|
+
|
|
3164
|
+
if (relativeName.substring(0, 1) === "/") {
|
|
3165
|
+
// if starts with /, then don't add anything to path
|
|
3166
|
+
absoluteName = relativeName;
|
|
3167
|
+
} else {
|
|
3168
|
+
// calculate full target from relativeName
|
|
3169
|
+
// putting it into the context of the current namespace
|
|
3170
|
+
|
|
3171
|
+
let lastLevel = namespaceStack.length - 1;
|
|
3172
|
+
|
|
3173
|
+
while (relativeName.substring(0, 3) === "../") {
|
|
3174
|
+
// take off one level for every ../
|
|
3175
|
+
relativeName = relativeName.substring(3);
|
|
3176
|
+
lastLevel--;
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
if (lastLevel < 0) {
|
|
3180
|
+
// the relativeName cannot possibly be valid
|
|
3181
|
+
// if there were more ../s than namespace levels
|
|
3182
|
+
lastLevel = 0;
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
absoluteName = "";
|
|
3186
|
+
for (let l = 0; l <= lastLevel; l++) {
|
|
3187
|
+
absoluteName += namespaceStack[l].namespace + "/";
|
|
3188
|
+
}
|
|
3189
|
+
absoluteName += relativeName;
|
|
3190
|
+
}
|
|
3191
|
+
|
|
3192
|
+
return absoluteName;
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
export function serializedComponentsReplacer(key, value) {
|
|
3196
|
+
if (value !== value) {
|
|
3197
|
+
return { objectType: "special-numeric", stringValue: "NaN" };
|
|
3198
|
+
} else if (value === Infinity) {
|
|
3199
|
+
return { objectType: "special-numeric", stringValue: "Infinity" };
|
|
3200
|
+
} else if (value === -Infinity) {
|
|
3201
|
+
return { objectType: "special-numeric", stringValue: "-Infinity" };
|
|
3202
|
+
}
|
|
3203
|
+
return value;
|
|
3204
|
+
}
|
|
3205
|
+
|
|
3206
|
+
let nanInfinityReviver = function (key, value) {
|
|
3207
|
+
if (value && value.objectType === "special-numeric") {
|
|
3208
|
+
if (value.stringValue === "NaN") {
|
|
3209
|
+
return NaN;
|
|
3210
|
+
} else if (value.stringValue === "Infinity") {
|
|
3211
|
+
return Infinity;
|
|
3212
|
+
} else if (value.stringValue === "-Infinity") {
|
|
3213
|
+
return -Infinity;
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
3216
|
+
|
|
3217
|
+
return value;
|
|
3218
|
+
};
|
|
3219
|
+
|
|
3220
|
+
export function serializedComponentsReviver(key, value) {
|
|
3221
|
+
return me.reviver(
|
|
3222
|
+
key,
|
|
3223
|
+
subsets.Subset.reviver(key, nanInfinityReviver(key, value)),
|
|
3224
|
+
);
|
|
3225
|
+
}
|
|
3226
|
+
|
|
3227
|
+
export function processAssignNames({
|
|
3228
|
+
assignNames = [],
|
|
3229
|
+
assignNewNamespaces = false,
|
|
3230
|
+
serializedComponents,
|
|
3231
|
+
parentName,
|
|
3232
|
+
parentCreatesNewNamespace,
|
|
3233
|
+
componentInfoObjects,
|
|
3234
|
+
indOffset = 0,
|
|
3235
|
+
originalNamesAreConsistent = false,
|
|
3236
|
+
shadowingComposite = false,
|
|
3237
|
+
}) {
|
|
3238
|
+
// console.log(`process assign names`)
|
|
3239
|
+
// console.log(deepClone(serializedComponents));
|
|
3240
|
+
// console.log(`originalNamesAreConsistent: ${originalNamesAreConsistent}`)
|
|
3241
|
+
|
|
3242
|
+
let numComponents = serializedComponents.length;
|
|
3243
|
+
|
|
3244
|
+
// normalize form so all names are originalNames,
|
|
3245
|
+
// independent of whether the components originated from a copy
|
|
3246
|
+
// or directly from a serialized state that was already given names
|
|
3247
|
+
moveComponentNamesToOriginalNames(serializedComponents);
|
|
3248
|
+
|
|
3249
|
+
let attributesByTargetComponentName = {};
|
|
3250
|
+
|
|
3251
|
+
let originalNamespace = null;
|
|
3252
|
+
|
|
3253
|
+
if (originalNamesAreConsistent) {
|
|
3254
|
+
// need to use a component for original name, as parentName is the new name
|
|
3255
|
+
if (numComponents > 0) {
|
|
3256
|
+
// find a component with an original name, i.e., not a string
|
|
3257
|
+
let component = serializedComponents.filter(
|
|
3258
|
+
(x) => typeof x === "object",
|
|
3259
|
+
)[0];
|
|
3260
|
+
if (component && component.originalName) {
|
|
3261
|
+
let lastSlash = component.originalName.lastIndexOf("/");
|
|
3262
|
+
originalNamespace = component.originalName.substring(0, lastSlash);
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
|
|
3266
|
+
if (originalNamespace !== null) {
|
|
3267
|
+
for (let component of serializedComponents) {
|
|
3268
|
+
setTargetsOutsideNamespaceToAbsoluteAndRecordAllTargetComponentNames({
|
|
3269
|
+
namespace: originalNamespace,
|
|
3270
|
+
components: [component],
|
|
3271
|
+
attributesByTargetComponentName,
|
|
3272
|
+
});
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
} else {
|
|
3276
|
+
for (let ind = 0; ind < numComponents; ind++) {
|
|
3277
|
+
let component = serializedComponents[ind];
|
|
3278
|
+
|
|
3279
|
+
if (typeof component !== "object") {
|
|
3280
|
+
continue;
|
|
3281
|
+
}
|
|
3282
|
+
|
|
3283
|
+
originalNamespace = null;
|
|
3284
|
+
// need to use a component for original name, as parentName is the new name
|
|
3285
|
+
if (numComponents > 0 && component.originalName) {
|
|
3286
|
+
let lastSlash = component.originalName.lastIndexOf("/");
|
|
3287
|
+
originalNamespace = component.originalName.substring(0, lastSlash);
|
|
3288
|
+
}
|
|
3289
|
+
|
|
3290
|
+
if (originalNamespace !== null) {
|
|
3291
|
+
setTargetsOutsideNamespaceToAbsoluteAndRecordAllTargetComponentNames({
|
|
3292
|
+
namespace: originalNamespace,
|
|
3293
|
+
components: [component],
|
|
3294
|
+
attributesByTargetComponentName,
|
|
3295
|
+
});
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
|
|
3300
|
+
let processedComponents = [];
|
|
3301
|
+
|
|
3302
|
+
// don't name strings or primitive numbers
|
|
3303
|
+
let numPrimitives = 0;
|
|
3304
|
+
|
|
3305
|
+
for (let ind = 0; ind < numComponents; ind++) {
|
|
3306
|
+
let indForNames = ind + indOffset;
|
|
3307
|
+
|
|
3308
|
+
let component = serializedComponents[ind];
|
|
3309
|
+
|
|
3310
|
+
if (typeof component !== "object") {
|
|
3311
|
+
numPrimitives++;
|
|
3312
|
+
processedComponents.push(component);
|
|
3313
|
+
continue;
|
|
3314
|
+
}
|
|
3315
|
+
|
|
3316
|
+
let name = assignNames[indForNames - numPrimitives];
|
|
3317
|
+
|
|
3318
|
+
if (!component.doenetAttributes) {
|
|
3319
|
+
component.doenetAttributes = {};
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
if (!originalNamesAreConsistent) {
|
|
3323
|
+
// attributesByTargetComponentName = {};
|
|
3324
|
+
|
|
3325
|
+
originalNamespace = null;
|
|
3326
|
+
// need to use a component for original name, as parentName is the new name
|
|
3327
|
+
if (numComponents > 0 && component.originalName) {
|
|
3328
|
+
let lastSlash = component.originalName.lastIndexOf("/");
|
|
3329
|
+
originalNamespace = component.originalName.substring(0, lastSlash);
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
|
|
3333
|
+
if (name) {
|
|
3334
|
+
if (
|
|
3335
|
+
componentInfoObjects.allComponentClasses[component.componentType]
|
|
3336
|
+
.assignNamesSkipOver
|
|
3337
|
+
) {
|
|
3338
|
+
name = [name];
|
|
3339
|
+
} else if (component.attributes?.assignNamesSkip) {
|
|
3340
|
+
let numberToSkip = component.attributes.assignNamesSkip.primitive;
|
|
3341
|
+
if (numberToSkip > 0) {
|
|
3342
|
+
for (let i = 0; i < numberToSkip; i++) {
|
|
3343
|
+
name = [name];
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
}
|
|
3348
|
+
|
|
3349
|
+
if (assignNewNamespaces) {
|
|
3350
|
+
if (!component.attributes) {
|
|
3351
|
+
component.attributes = {};
|
|
3352
|
+
}
|
|
3353
|
+
component.attributes.newNamespace = { primitive: true };
|
|
3354
|
+
}
|
|
3355
|
+
|
|
3356
|
+
if (Array.isArray(name)) {
|
|
3357
|
+
if (
|
|
3358
|
+
componentInfoObjects.allComponentClasses[component.componentType]
|
|
3359
|
+
.assignNamesToReplacements
|
|
3360
|
+
) {
|
|
3361
|
+
// give component itself an unreachable name
|
|
3362
|
+
let longNameId = parentName + "|assignName|" + indForNames.toString();
|
|
3363
|
+
component.doenetAttributes.prescribedName = createUniqueName(
|
|
3364
|
+
component.componentType.toLowerCase(),
|
|
3365
|
+
longNameId,
|
|
3366
|
+
);
|
|
3367
|
+
|
|
3368
|
+
let componentName = parentName;
|
|
3369
|
+
if (!parentCreatesNewNamespace) {
|
|
3370
|
+
let lastSlash = parentName.lastIndexOf("/");
|
|
3371
|
+
componentName = parentName.substring(0, lastSlash);
|
|
3372
|
+
}
|
|
3373
|
+
componentName += "/" + component.doenetAttributes.prescribedName;
|
|
3374
|
+
component.componentName = componentName;
|
|
3375
|
+
|
|
3376
|
+
component.doenetAttributes.assignNames = name;
|
|
3377
|
+
|
|
3378
|
+
processedComponents.push(component);
|
|
3379
|
+
continue;
|
|
3380
|
+
} else {
|
|
3381
|
+
// TODO: what to do when try to assign names recursively to non-composite?
|
|
3382
|
+
console.warn(
|
|
3383
|
+
`Cannot assign names recursively to ${component.componentType}`,
|
|
3384
|
+
);
|
|
3385
|
+
name = null;
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
|
|
3389
|
+
if (!name) {
|
|
3390
|
+
if (
|
|
3391
|
+
originalNamesAreConsistent &&
|
|
3392
|
+
component.originalName &&
|
|
3393
|
+
!component.doenetAttributes?.createUniqueName
|
|
3394
|
+
) {
|
|
3395
|
+
name = component.originalName.slice(originalNamespace.length + 1);
|
|
3396
|
+
} else {
|
|
3397
|
+
let longNameId = parentName + "|assignName|" + indForNames.toString();
|
|
3398
|
+
name = createUniqueName(
|
|
3399
|
+
component.componentType.toLowerCase(),
|
|
3400
|
+
longNameId,
|
|
3401
|
+
);
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3405
|
+
component.doenetAttributes.prescribedName = name;
|
|
3406
|
+
// delete component.originalName;
|
|
3407
|
+
|
|
3408
|
+
// even if original names are consistent, we still use component's original assignNames
|
|
3409
|
+
// (we wouldn't use assignNames of the component's children as they should have unique names)
|
|
3410
|
+
if (
|
|
3411
|
+
originalNamesAreConsistent &&
|
|
3412
|
+
!component.doenetAttributes.assignNames &&
|
|
3413
|
+
component.originalDoenetAttributes &&
|
|
3414
|
+
component.originalDoenetAttributes.assignNames
|
|
3415
|
+
) {
|
|
3416
|
+
component.doenetAttributes.assignNames =
|
|
3417
|
+
component.originalDoenetAttributes.assignNames;
|
|
3418
|
+
}
|
|
3419
|
+
|
|
3420
|
+
createComponentNamesFromParentName({
|
|
3421
|
+
parentName,
|
|
3422
|
+
ind: indForNames,
|
|
3423
|
+
component,
|
|
3424
|
+
parentCreatesNewNamespace,
|
|
3425
|
+
componentInfoObjects,
|
|
3426
|
+
attributesByTargetComponentName,
|
|
3427
|
+
originalNamesAreConsistent,
|
|
3428
|
+
shadowingComposite,
|
|
3429
|
+
});
|
|
3430
|
+
|
|
3431
|
+
processedComponents.push(component);
|
|
3432
|
+
}
|
|
3433
|
+
|
|
3434
|
+
return {
|
|
3435
|
+
serializedComponents: processedComponents,
|
|
3436
|
+
};
|
|
3437
|
+
}
|
|
3438
|
+
|
|
3439
|
+
function createComponentNamesFromParentName({
|
|
3440
|
+
parentName,
|
|
3441
|
+
component,
|
|
3442
|
+
ind,
|
|
3443
|
+
parentCreatesNewNamespace,
|
|
3444
|
+
componentInfoObjects,
|
|
3445
|
+
attributesByTargetComponentName,
|
|
3446
|
+
originalNamesAreConsistent,
|
|
3447
|
+
shadowingComposite,
|
|
3448
|
+
}) {
|
|
3449
|
+
let namespacePieces = parentName.split("/");
|
|
3450
|
+
|
|
3451
|
+
if (!parentCreatesNewNamespace) {
|
|
3452
|
+
namespacePieces.pop();
|
|
3453
|
+
}
|
|
3454
|
+
|
|
3455
|
+
let namespaceStack = namespacePieces.map((x) => ({
|
|
3456
|
+
namespace: x,
|
|
3457
|
+
componentCounts: {},
|
|
3458
|
+
namesUsed: {},
|
|
3459
|
+
}));
|
|
3460
|
+
|
|
3461
|
+
if (!(parentName[0] === "/")) {
|
|
3462
|
+
// if componentName doesn't begin with a /
|
|
3463
|
+
// still add a namespace for the root namespace at the beginning
|
|
3464
|
+
namespaceStack.splice(0, 0, {
|
|
3465
|
+
componentCounts: {},
|
|
3466
|
+
namesUsed: {},
|
|
3467
|
+
namespace: "",
|
|
3468
|
+
});
|
|
3469
|
+
}
|
|
3470
|
+
|
|
3471
|
+
if (!component.doenetAttributes) {
|
|
3472
|
+
component.doenetAttributes = {};
|
|
3473
|
+
}
|
|
3474
|
+
if (!component.attributes) {
|
|
3475
|
+
component.attributes = {};
|
|
3476
|
+
}
|
|
3477
|
+
|
|
3478
|
+
// let originalNamespaceForComponentChildren = parentName;
|
|
3479
|
+
// if (!parentCreatesNewNamespace) {
|
|
3480
|
+
// let lastSlash = parentName.lastIndexOf("/");
|
|
3481
|
+
// namespaceForComponent = parentName.substring(0, lastSlash);
|
|
3482
|
+
// }
|
|
3483
|
+
|
|
3484
|
+
let useOriginalNames;
|
|
3485
|
+
if (
|
|
3486
|
+
component.attributes.newNamespace?.primitive ||
|
|
3487
|
+
originalNamesAreConsistent
|
|
3488
|
+
) {
|
|
3489
|
+
useOriginalNames = true;
|
|
3490
|
+
} else {
|
|
3491
|
+
useOriginalNames = false;
|
|
3492
|
+
|
|
3493
|
+
if (component.children) {
|
|
3494
|
+
markToCreateAllUniqueNames(component.children);
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3498
|
+
// always mark component attributes to create unique names
|
|
3499
|
+
for (let attrName in component.attributes) {
|
|
3500
|
+
let attribute = component.attributes[attrName];
|
|
3501
|
+
if (attribute.component) {
|
|
3502
|
+
markToCreateAllUniqueNames([attribute.component]);
|
|
3503
|
+
} else if (attribute.childrenForComponent) {
|
|
3504
|
+
markToCreateAllUniqueNames(attribute.childrenForComponent);
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3507
|
+
|
|
3508
|
+
// console.log(`before create componentName`)
|
|
3509
|
+
// console.log(deepClone(component))
|
|
3510
|
+
// console.log(useOriginalNames);
|
|
3511
|
+
// console.log(component.attributes.newNamespace);
|
|
3512
|
+
|
|
3513
|
+
createComponentNames({
|
|
3514
|
+
serializedComponents: [component],
|
|
3515
|
+
namespaceStack,
|
|
3516
|
+
componentInfoObjects,
|
|
3517
|
+
parentName,
|
|
3518
|
+
useOriginalNames,
|
|
3519
|
+
attributesByTargetComponentName,
|
|
3520
|
+
indOffset: ind,
|
|
3521
|
+
initWithoutShadowingComposite: !shadowingComposite,
|
|
3522
|
+
});
|
|
3523
|
+
|
|
3524
|
+
// console.log(`result of create componentName`)
|
|
3525
|
+
// console.log(deepClone(component))
|
|
3526
|
+
}
|
|
3527
|
+
|
|
3528
|
+
function setTargetsOutsideNamespaceToAbsoluteAndRecordAllTargetComponentNames({
|
|
3529
|
+
namespace,
|
|
3530
|
+
components,
|
|
3531
|
+
attributesByTargetComponentName,
|
|
3532
|
+
}) {
|
|
3533
|
+
let namespaceLength = namespace.length;
|
|
3534
|
+
for (let component of components) {
|
|
3535
|
+
if (typeof component !== "object") {
|
|
3536
|
+
continue;
|
|
3537
|
+
}
|
|
3538
|
+
|
|
3539
|
+
if (component.doenetAttributes && component.doenetAttributes.target) {
|
|
3540
|
+
let targetComponentName = component.doenetAttributes.targetComponentName;
|
|
3541
|
+
if (targetComponentName !== undefined) {
|
|
3542
|
+
if (targetComponentName.substring(0, namespaceLength) !== namespace) {
|
|
3543
|
+
component.doenetAttributes.target = targetComponentName;
|
|
3544
|
+
}
|
|
3545
|
+
if (!attributesByTargetComponentName[targetComponentName]) {
|
|
3546
|
+
attributesByTargetComponentName[targetComponentName] = [];
|
|
3547
|
+
}
|
|
3548
|
+
attributesByTargetComponentName[targetComponentName].push(
|
|
3549
|
+
component.doenetAttributes,
|
|
3550
|
+
);
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
|
|
3554
|
+
for (let attrName in component.attributes) {
|
|
3555
|
+
let attr = component.attributes[attrName];
|
|
3556
|
+
if (attr.targetComponentNames) {
|
|
3557
|
+
for (let nameObj of attr.targetComponentNames) {
|
|
3558
|
+
let absoluteName = nameObj.absoluteName;
|
|
3559
|
+
if (absoluteName !== undefined) {
|
|
3560
|
+
if (absoluteName.substring(0, namespaceLength) !== namespace) {
|
|
3561
|
+
nameObj.relativeName = absoluteName;
|
|
3562
|
+
}
|
|
3563
|
+
if (!attributesByTargetComponentName[absoluteName]) {
|
|
3564
|
+
attributesByTargetComponentName[absoluteName] = [];
|
|
3565
|
+
}
|
|
3566
|
+
attributesByTargetComponentName[absoluteName].push(nameObj);
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
}
|
|
3571
|
+
|
|
3572
|
+
if (component.children) {
|
|
3573
|
+
setTargetsOutsideNamespaceToAbsoluteAndRecordAllTargetComponentNames({
|
|
3574
|
+
namespace,
|
|
3575
|
+
components: component.children,
|
|
3576
|
+
attributesByTargetComponentName,
|
|
3577
|
+
});
|
|
3578
|
+
}
|
|
3579
|
+
if (component.attributes) {
|
|
3580
|
+
for (let attrName in component.attributes) {
|
|
3581
|
+
let attribute = component.attributes[attrName];
|
|
3582
|
+
if (attribute.component) {
|
|
3583
|
+
setTargetsOutsideNamespaceToAbsoluteAndRecordAllTargetComponentNames({
|
|
3584
|
+
namespace,
|
|
3585
|
+
components: [attribute.component],
|
|
3586
|
+
attributesByTargetComponentName,
|
|
3587
|
+
});
|
|
3588
|
+
} else if (attribute.childrenForComponent) {
|
|
3589
|
+
setTargetsOutsideNamespaceToAbsoluteAndRecordAllTargetComponentNames({
|
|
3590
|
+
namespace,
|
|
3591
|
+
components: attribute.childrenForComponent,
|
|
3592
|
+
attributesByTargetComponentName,
|
|
3593
|
+
});
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
|
|
3600
|
+
function renameMatchingTargetNames(
|
|
3601
|
+
component,
|
|
3602
|
+
attributesByTargetComponentName,
|
|
3603
|
+
renameMatchingNamespaces = false,
|
|
3604
|
+
) {
|
|
3605
|
+
if (
|
|
3606
|
+
component.originalName &&
|
|
3607
|
+
attributesByTargetComponentName &&
|
|
3608
|
+
component.componentName !== component.originalName
|
|
3609
|
+
) {
|
|
3610
|
+
// we have a component who has been named and there are other components
|
|
3611
|
+
// whose targetComponentName refers to this component
|
|
3612
|
+
// Modify the target and targetComponentName of the other components to refer to the new name
|
|
3613
|
+
// (Must modify targetComponentName as we don't know if this component has been processed yet)
|
|
3614
|
+
if (attributesByTargetComponentName[component.originalName]) {
|
|
3615
|
+
for (let attrObj of attributesByTargetComponentName[
|
|
3616
|
+
component.originalName
|
|
3617
|
+
]) {
|
|
3618
|
+
if (attrObj.relativeName) {
|
|
3619
|
+
attrObj.relativeName = component.componentName;
|
|
3620
|
+
attrObj.absoluteName = component.componentName;
|
|
3621
|
+
} else {
|
|
3622
|
+
// must be doenetAttributes
|
|
3623
|
+
attrObj.target = component.componentName;
|
|
3624
|
+
attrObj.targetComponentName = component.componentName;
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
if (renameMatchingNamespaces) {
|
|
3629
|
+
let originalNamespace = component.originalName + "/";
|
|
3630
|
+
let nSpaceLen = originalNamespace.length;
|
|
3631
|
+
for (let originalTargetComponentName in attributesByTargetComponentName) {
|
|
3632
|
+
if (
|
|
3633
|
+
originalTargetComponentName.substring(0, nSpaceLen) ===
|
|
3634
|
+
originalNamespace
|
|
3635
|
+
) {
|
|
3636
|
+
let originalEnding = originalTargetComponentName.substring(nSpaceLen);
|
|
3637
|
+
for (let attrObj of attributesByTargetComponentName[
|
|
3638
|
+
originalTargetComponentName
|
|
3639
|
+
]) {
|
|
3640
|
+
if (attrObj.relativeName) {
|
|
3641
|
+
attrObj.relativeName =
|
|
3642
|
+
component.componentName + "/" + originalEnding;
|
|
3643
|
+
attrObj.absoluteName =
|
|
3644
|
+
component.componentName + "/" + originalEnding;
|
|
3645
|
+
} else {
|
|
3646
|
+
// must be doenetAttributes
|
|
3647
|
+
attrObj.target = component.componentName + "/" + originalEnding;
|
|
3648
|
+
attrObj.targetComponentName =
|
|
3649
|
+
component.componentName + "/" + originalEnding;
|
|
3650
|
+
}
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3657
|
+
|
|
3658
|
+
function moveComponentNamesToOriginalNames(components) {
|
|
3659
|
+
for (let component of components) {
|
|
3660
|
+
if (component.componentName) {
|
|
3661
|
+
component.originalName = component.componentName;
|
|
3662
|
+
delete component.componentName;
|
|
3663
|
+
}
|
|
3664
|
+
if (component.children) {
|
|
3665
|
+
moveComponentNamesToOriginalNames(component.children);
|
|
3666
|
+
}
|
|
3667
|
+
if (component.attributes) {
|
|
3668
|
+
for (let attrName in component.attributes) {
|
|
3669
|
+
let attribute = component.attributes[attrName];
|
|
3670
|
+
if (attribute.component) {
|
|
3671
|
+
moveComponentNamesToOriginalNames([attribute.component]);
|
|
3672
|
+
} else if (attribute.childrenForComponent) {
|
|
3673
|
+
moveComponentNamesToOriginalNames(attribute.childrenForComponent);
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
|
|
3680
|
+
export function markToCreateAllUniqueNames(components) {
|
|
3681
|
+
for (let component of components) {
|
|
3682
|
+
if (typeof component !== "object") {
|
|
3683
|
+
continue;
|
|
3684
|
+
}
|
|
3685
|
+
|
|
3686
|
+
if (!component.doenetAttributes) {
|
|
3687
|
+
component.doenetAttributes = {};
|
|
3688
|
+
}
|
|
3689
|
+
component.doenetAttributes.createUniqueName = true;
|
|
3690
|
+
delete component.doenetAttributes.prescribedName;
|
|
3691
|
+
|
|
3692
|
+
if (!component.attributes?.newNamespace?.primitive) {
|
|
3693
|
+
if (component.doenetAttributes.assignNames) {
|
|
3694
|
+
component.doenetAttributes.createUniqueAssignNames = true;
|
|
3695
|
+
component.doenetAttributes.originalAssignNames =
|
|
3696
|
+
component.doenetAttributes.assignNames;
|
|
3697
|
+
delete component.doenetAttributes.assignNames;
|
|
3698
|
+
} else if (
|
|
3699
|
+
component.originalDoenetAttributes &&
|
|
3700
|
+
component.originalDoenetAttributes.assignNames
|
|
3701
|
+
) {
|
|
3702
|
+
component.doenetAttributes.createUniqueAssignNames = true;
|
|
3703
|
+
component.doenetAttributes.originalAssignNames =
|
|
3704
|
+
component.originalDoenetAttributes.assignNames;
|
|
3705
|
+
}
|
|
3706
|
+
if (component.children) {
|
|
3707
|
+
markToCreateAllUniqueNames(component.children);
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
|
|
3711
|
+
if (component.attributes) {
|
|
3712
|
+
for (let attrName in component.attributes) {
|
|
3713
|
+
let attribute = component.attributes[attrName];
|
|
3714
|
+
if (attribute.component) {
|
|
3715
|
+
markToCreateAllUniqueNames([attribute.component]);
|
|
3716
|
+
} else if (attribute.childrenForComponent) {
|
|
3717
|
+
markToCreateAllUniqueNames(attribute.childrenForComponent);
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
}
|
|
3723
|
+
|
|
3724
|
+
export function setTNamesToAbsolute(components) {
|
|
3725
|
+
for (let component of components) {
|
|
3726
|
+
if (component.doenetAttributes && component.doenetAttributes.target) {
|
|
3727
|
+
let targetComponentName = component.doenetAttributes.targetComponentName;
|
|
3728
|
+
if (targetComponentName !== undefined) {
|
|
3729
|
+
component.doenetAttributes.target = targetComponentName;
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
|
|
3733
|
+
if (component.children) {
|
|
3734
|
+
setTNamesToAbsolute(component.children);
|
|
3735
|
+
}
|
|
3736
|
+
if (component.attributes) {
|
|
3737
|
+
for (let attrName in component.attributes) {
|
|
3738
|
+
let attribute = component.attributes[attrName];
|
|
3739
|
+
if (attribute.component) {
|
|
3740
|
+
setTNamesToAbsolute([attribute.component]);
|
|
3741
|
+
} else if (attribute.childrenForComponent) {
|
|
3742
|
+
setTNamesToAbsolute(attribute.childrenForComponent);
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
|
|
3749
|
+
export function restrictTNamesToNamespace({
|
|
3750
|
+
components,
|
|
3751
|
+
namespace,
|
|
3752
|
+
parentNamespace,
|
|
3753
|
+
parentIsCopy = false,
|
|
3754
|
+
invalidateReferencesToBaseNamespace = false,
|
|
3755
|
+
}) {
|
|
3756
|
+
if (parentNamespace === undefined) {
|
|
3757
|
+
parentNamespace = namespace;
|
|
3758
|
+
}
|
|
3759
|
+
|
|
3760
|
+
let nSpace = namespace.length;
|
|
3761
|
+
|
|
3762
|
+
for (let component of components) {
|
|
3763
|
+
if (component.doenetAttributes && component.doenetAttributes.target) {
|
|
3764
|
+
let target = component.doenetAttributes.target;
|
|
3765
|
+
|
|
3766
|
+
if (target[0] === "/") {
|
|
3767
|
+
if (target.substring(0, nSpace) !== namespace) {
|
|
3768
|
+
// if left part of target matches the left part of the namespace, delete matched part from larget
|
|
3769
|
+
// else if left part of target matches the right part of the namespace, delete matched part
|
|
3770
|
+
|
|
3771
|
+
let namespaceParts = namespace.split("/").slice(1);
|
|
3772
|
+
let targetParts = target.split("/").slice(1);
|
|
3773
|
+
let foundAMatch = false;
|
|
3774
|
+
let targetComponentName = namespace + target.slice(1);
|
|
3775
|
+
|
|
3776
|
+
while (
|
|
3777
|
+
namespaceParts.length > 0 &&
|
|
3778
|
+
namespaceParts[0] === targetParts[0]
|
|
3779
|
+
) {
|
|
3780
|
+
namespaceParts = namespaceParts.slice(1);
|
|
3781
|
+
targetParts = targetParts.slice(1);
|
|
3782
|
+
foundAMatch = true;
|
|
3783
|
+
}
|
|
3784
|
+
|
|
3785
|
+
if (foundAMatch) {
|
|
3786
|
+
targetComponentName = namespace + targetParts.join("/");
|
|
3787
|
+
} else {
|
|
3788
|
+
let namespaceParts = namespace.split("/").slice(1);
|
|
3789
|
+
for (let ind = 1; ind < namespaceParts.length; ind++) {
|
|
3790
|
+
let namespacePiece = "/" + namespaceParts.slice(ind).join("/");
|
|
3791
|
+
if (
|
|
3792
|
+
target.substring(0, namespacePiece.length) === namespacePiece
|
|
3793
|
+
) {
|
|
3794
|
+
targetComponentName =
|
|
3795
|
+
"/" + namespaceParts.slice(0, ind).join("/") + target;
|
|
3796
|
+
break;
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
|
|
3801
|
+
component.doenetAttributes.target = targetComponentName;
|
|
3802
|
+
component.doenetAttributes.targetComponentName = targetComponentName;
|
|
3803
|
+
} else if (invalidateReferencesToBaseNamespace) {
|
|
3804
|
+
let lastSlash = target.lastIndexOf("/");
|
|
3805
|
+
if (target.slice(0, lastSlash + 1) === namespace) {
|
|
3806
|
+
component.doenetAttributes.target = "";
|
|
3807
|
+
component.doenetAttributes.targetComponentName = "";
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
} else if (target.substring(0, 3) === "../") {
|
|
3811
|
+
let tNamePart = target;
|
|
3812
|
+
let namespacePart = parentNamespace;
|
|
3813
|
+
while (tNamePart.substring(0, 3) === "../") {
|
|
3814
|
+
tNamePart = tNamePart.substring(3);
|
|
3815
|
+
let lastSlash = namespacePart
|
|
3816
|
+
.substring(0, namespacePart.length - 1)
|
|
3817
|
+
.lastIndexOf("/");
|
|
3818
|
+
namespacePart = namespacePart.substring(0, lastSlash + 1);
|
|
3819
|
+
if (namespacePart.substring(0, nSpace) !== namespace) {
|
|
3820
|
+
while (tNamePart.substring(0, 3) === "../") {
|
|
3821
|
+
tNamePart = tNamePart.substring(3);
|
|
3822
|
+
}
|
|
3823
|
+
|
|
3824
|
+
let targetComponentName = namespace + tNamePart;
|
|
3825
|
+
component.doenetAttributes.target = targetComponentName;
|
|
3826
|
+
component.doenetAttributes.targetComponentName =
|
|
3827
|
+
targetComponentName;
|
|
3828
|
+
break;
|
|
3829
|
+
}
|
|
3830
|
+
}
|
|
3831
|
+
if (invalidateReferencesToBaseNamespace) {
|
|
3832
|
+
let targetComponentName =
|
|
3833
|
+
component.doenetAttributes.targetComponentName;
|
|
3834
|
+
let lastSlash = targetComponentName.lastIndexOf("/");
|
|
3835
|
+
if (targetComponentName.slice(0, lastSlash + 1) === namespace) {
|
|
3836
|
+
component.doenetAttributes.target = "";
|
|
3837
|
+
component.doenetAttributes.targetComponentName = "";
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3842
|
+
|
|
3843
|
+
if (component.children) {
|
|
3844
|
+
let adjustedNamespace = namespace;
|
|
3845
|
+
if (parentIsCopy && component.componentType === "externalContent") {
|
|
3846
|
+
// if have a external content inside a copy,
|
|
3847
|
+
// then restrict children to the namespace of the externalContent
|
|
3848
|
+
adjustedNamespace = component.componentName + "/";
|
|
3849
|
+
}
|
|
3850
|
+
let namespaceForChildren = parentNamespace;
|
|
3851
|
+
if (
|
|
3852
|
+
component.attributes &&
|
|
3853
|
+
component.attributes.newNamespace?.primitive
|
|
3854
|
+
) {
|
|
3855
|
+
namespaceForChildren = component.componentName;
|
|
3856
|
+
}
|
|
3857
|
+
restrictTNamesToNamespace({
|
|
3858
|
+
components: component.children,
|
|
3859
|
+
namespace: adjustedNamespace,
|
|
3860
|
+
parentNamespace: namespaceForChildren,
|
|
3861
|
+
parentIsCopy: component.componentType === "copy",
|
|
3862
|
+
invalidateReferencesToBaseNamespace,
|
|
3863
|
+
});
|
|
3864
|
+
}
|
|
3865
|
+
if (component.attributes) {
|
|
3866
|
+
for (let attrName in component.attributes) {
|
|
3867
|
+
let attribute = component.attributes[attrName];
|
|
3868
|
+
if (attribute.component) {
|
|
3869
|
+
restrictTNamesToNamespace({
|
|
3870
|
+
components: [attribute.component],
|
|
3871
|
+
namespace,
|
|
3872
|
+
parentNamespace,
|
|
3873
|
+
invalidateReferencesToBaseNamespace,
|
|
3874
|
+
});
|
|
3875
|
+
} else if (attribute.childrenForComponent) {
|
|
3876
|
+
restrictTNamesToNamespace({
|
|
3877
|
+
components: attribute.childrenForComponent,
|
|
3878
|
+
namespace,
|
|
3879
|
+
parentNamespace,
|
|
3880
|
+
invalidateReferencesToBaseNamespace,
|
|
3881
|
+
});
|
|
3882
|
+
}
|
|
3883
|
+
}
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
|
|
3888
|
+
function indexRangeString(serializedComponent) {
|
|
3889
|
+
let message = "";
|
|
3890
|
+
if (serializedComponent.range) {
|
|
3891
|
+
let indBegin, indEnd;
|
|
3892
|
+
if (serializedComponent.range.selfCloseBegin !== undefined) {
|
|
3893
|
+
indBegin = serializedComponent.range.selfCloseBegin;
|
|
3894
|
+
indEnd = serializedComponent.range.selfCloseEnd;
|
|
3895
|
+
} else if (serializedComponent.range.openBegin !== undefined) {
|
|
3896
|
+
indBegin = serializedComponent.range.openBegin;
|
|
3897
|
+
indEnd = serializedComponent.range.closeEnd;
|
|
3898
|
+
}
|
|
3899
|
+
if (indBegin !== undefined) {
|
|
3900
|
+
message += ` at indices ${indBegin}-${indEnd}`;
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
return message;
|
|
3904
|
+
}
|
|
3905
|
+
|
|
3906
|
+
export function extractComponentNamesAndIndices(
|
|
3907
|
+
serializedComponents,
|
|
3908
|
+
nameSubstitutions = {},
|
|
3909
|
+
) {
|
|
3910
|
+
let componentArray = [];
|
|
3911
|
+
|
|
3912
|
+
for (let serializedComponent of serializedComponents) {
|
|
3913
|
+
if (typeof serializedComponent === "object") {
|
|
3914
|
+
let componentName = serializedComponent.componentName;
|
|
3915
|
+
for (let originalName in nameSubstitutions) {
|
|
3916
|
+
componentName = componentName.replace(
|
|
3917
|
+
originalName,
|
|
3918
|
+
nameSubstitutions[originalName],
|
|
3919
|
+
);
|
|
3920
|
+
}
|
|
3921
|
+
if (serializedComponent.doenetAttributes?.fromCopyTarget) {
|
|
3922
|
+
let lastSlash = componentName.lastIndexOf("/");
|
|
3923
|
+
let originalName = componentName.slice(lastSlash + 1);
|
|
3924
|
+
let newName = serializedComponent.doenetAttributes.assignNames[0];
|
|
3925
|
+
componentName = componentName.replace(originalName, newName);
|
|
3926
|
+
nameSubstitutions[originalName] = newName;
|
|
3927
|
+
}
|
|
3928
|
+
let componentObj = {
|
|
3929
|
+
componentName,
|
|
3930
|
+
};
|
|
3931
|
+
if (serializedComponent.range) {
|
|
3932
|
+
if (serializedComponent.range.selfCloseBegin !== undefined) {
|
|
3933
|
+
componentObj.indBegin = serializedComponent.range.selfCloseBegin;
|
|
3934
|
+
componentObj.indEnd = serializedComponent.range.selfCloseEnd;
|
|
3935
|
+
} else if (serializedComponent.range.openBegin !== undefined) {
|
|
3936
|
+
componentObj.indBegin = serializedComponent.range.openBegin;
|
|
3937
|
+
componentObj.indEnd = serializedComponent.range.closeEnd;
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
|
|
3941
|
+
componentArray.push(componentObj);
|
|
3942
|
+
|
|
3943
|
+
if (serializedComponent.children) {
|
|
3944
|
+
componentArray.push(
|
|
3945
|
+
...extractComponentNamesAndIndices(serializedComponent.children, {
|
|
3946
|
+
...nameSubstitutions,
|
|
3947
|
+
}),
|
|
3948
|
+
);
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
|
|
3953
|
+
return componentArray;
|
|
3954
|
+
}
|
|
3955
|
+
|
|
3956
|
+
export function extractRangeIndexPieces({
|
|
3957
|
+
componentArray,
|
|
3958
|
+
lastInd = 0,
|
|
3959
|
+
stopInd = Infinity,
|
|
3960
|
+
enclosingComponentName,
|
|
3961
|
+
}) {
|
|
3962
|
+
let rangePieces = [];
|
|
3963
|
+
|
|
3964
|
+
let componentInd = 0;
|
|
3965
|
+
let foundComponentAfterStopInd = false;
|
|
3966
|
+
while (componentInd < componentArray.length) {
|
|
3967
|
+
let componentObj = componentArray[componentInd];
|
|
3968
|
+
|
|
3969
|
+
if (componentObj.indBegin === undefined) {
|
|
3970
|
+
componentInd++;
|
|
3971
|
+
continue;
|
|
3972
|
+
}
|
|
3973
|
+
|
|
3974
|
+
if (componentObj.indBegin > stopInd) {
|
|
3975
|
+
if (enclosingComponentName && lastInd <= stopInd) {
|
|
3976
|
+
rangePieces.push({
|
|
3977
|
+
begin: lastInd,
|
|
3978
|
+
end: stopInd,
|
|
3979
|
+
componentName: enclosingComponentName,
|
|
3980
|
+
});
|
|
3981
|
+
}
|
|
3982
|
+
foundComponentAfterStopInd = true;
|
|
3983
|
+
break;
|
|
3984
|
+
}
|
|
3985
|
+
|
|
3986
|
+
if (enclosingComponentName && componentObj.indBegin > lastInd) {
|
|
3987
|
+
rangePieces.push({
|
|
3988
|
+
begin: lastInd,
|
|
3989
|
+
end: componentObj.indBegin - 1,
|
|
3990
|
+
componentName: enclosingComponentName,
|
|
3991
|
+
});
|
|
3992
|
+
}
|
|
3993
|
+
|
|
3994
|
+
let extractResult = extractRangeIndexPieces({
|
|
3995
|
+
componentArray: componentArray.slice(componentInd + 1),
|
|
3996
|
+
lastInd: componentObj.indBegin,
|
|
3997
|
+
stopInd: componentObj.indEnd,
|
|
3998
|
+
enclosingComponentName: componentObj.componentName,
|
|
3999
|
+
});
|
|
4000
|
+
|
|
4001
|
+
componentInd += extractResult.componentsConsumed + 1;
|
|
4002
|
+
rangePieces.push(...extractResult.rangePieces);
|
|
4003
|
+
|
|
4004
|
+
lastInd = componentObj.indEnd + 1;
|
|
4005
|
+
}
|
|
4006
|
+
|
|
4007
|
+
if (
|
|
4008
|
+
!foundComponentAfterStopInd &&
|
|
4009
|
+
Number.isFinite(stopInd) &&
|
|
4010
|
+
stopInd >= lastInd
|
|
4011
|
+
) {
|
|
4012
|
+
rangePieces.push({
|
|
4013
|
+
begin: lastInd,
|
|
4014
|
+
end: stopInd,
|
|
4015
|
+
componentName: enclosingComponentName,
|
|
4016
|
+
});
|
|
4017
|
+
}
|
|
4018
|
+
|
|
4019
|
+
return { componentsConsumed: componentInd, rangePieces };
|
|
4020
|
+
}
|
|
4021
|
+
|
|
4022
|
+
export function countComponentTypes(serializedComponents) {
|
|
4023
|
+
// Count component types from the components in the array (not recursing to children).
|
|
4024
|
+
// Used for counting the sections in a document in order to increment section counts
|
|
4025
|
+
// subsequent pages.
|
|
4026
|
+
|
|
4027
|
+
let componentTypeCounts = {};
|
|
4028
|
+
|
|
4029
|
+
for (let component of serializedComponents) {
|
|
4030
|
+
if (typeof component === "object") {
|
|
4031
|
+
let cType = component.componentType;
|
|
4032
|
+
let numComponents = 1;
|
|
4033
|
+
if (component.attributes?.createComponentOfType?.primitive) {
|
|
4034
|
+
cType = component.attributes.createComponentOfType.primitive;
|
|
4035
|
+
numComponents = component.attributes.numComponents?.primitive;
|
|
4036
|
+
if (!(Number.isInteger(numComponents) && numComponents > 0)) {
|
|
4037
|
+
numComponents = 1;
|
|
4038
|
+
}
|
|
4039
|
+
}
|
|
4040
|
+
if (cType in componentTypeCounts) {
|
|
4041
|
+
componentTypeCounts[cType] += numComponents;
|
|
4042
|
+
} else {
|
|
4043
|
+
componentTypeCounts[cType] = numComponents;
|
|
4044
|
+
}
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
|
|
4048
|
+
return componentTypeCounts;
|
|
4049
|
+
}
|