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