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