@instructure/canvas-rce 5.14.1 → 5.15.0

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 (489) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/DEVELOPMENT.md +2 -2
  3. package/README.md +0 -8
  4. package/__tests__/common/indicate.test.js +84 -0
  5. package/__tests__/common/mimeClass.test.js +85 -0
  6. package/__tests__/module/contentInsertionUtils.test.js +52 -0
  7. package/__tests__/module/indicatorRegion.test.js +75 -0
  8. package/__tests__/module/normalizeLocale.test.js +46 -0
  9. package/__tests__/module/normalizeProps.test.js +51 -0
  10. package/__tests__/module/sanitizePlugins.test.js +48 -0
  11. package/__tests__/module/wrapInitCb.test.js +56 -0
  12. package/__tests__/rcs/api.test.js +819 -0
  13. package/{mocha-reporter-config.js → __tests__/sidebar/actions/all_files.test.js} +10 -9
  14. package/__tests__/sidebar/actions/data.test.js +196 -0
  15. package/__tests__/sidebar/actions/utils.js +44 -0
  16. package/{es/rce/__mocks__/_mockStudioPlayer.js → __tests__/sidebar/reducers/all_files.test.js} +12 -4
  17. package/babel.config.js +3 -1
  18. package/es/bridge/Bridge.js +18 -73
  19. package/es/bridge/index.js +1 -0
  20. package/es/canvasFileBrowser/FileBrowser.js +21 -77
  21. package/es/canvasFileBrowser/en-US.js +3 -6
  22. package/es/common/FlashAlert.js +15 -39
  23. package/es/common/browser.js +4 -2
  24. package/es/common/fileUrl.js +105 -64
  25. package/es/common/incremental-loading/LoadMoreButton.js +4 -4
  26. package/es/common/incremental-loading/LoadingIndicator.js +1 -2
  27. package/es/common/incremental-loading/LoadingStatus.js +5 -13
  28. package/es/common/incremental-loading/index.js +1 -0
  29. package/es/common/incremental-loading/useIncrementalLoading.js +1 -3
  30. package/es/common/indicate.js +16 -10
  31. package/es/common/mimeClass.js +3 -4
  32. package/es/common/natcompare.js +1 -4
  33. package/es/defaultTinymceConfig.js +5 -3
  34. package/es/elementDenylist.js +1 -0
  35. package/es/enhance-user-content/doc_previews.js +24 -35
  36. package/es/enhance-user-content/enhance_user_content.js +32 -67
  37. package/es/enhance-user-content/external_links.js +6 -9
  38. package/es/enhance-user-content/index.js +1 -0
  39. package/es/enhance-user-content/instructure_helper.js +22 -50
  40. package/es/enhance-user-content/jqueryish_funcs.js +8 -11
  41. package/es/enhance-user-content/mathml.js +48 -107
  42. package/es/enhance-user-content/media_comment_thumbnail.js +6 -25
  43. package/es/format-message.js +4 -5
  44. package/es/getThemeVars.js +8 -6
  45. package/es/getTranslations.js +1 -78
  46. package/es/index.d.ts +59 -0
  47. package/es/index.js +6 -6
  48. package/es/rce/AlertMessageArea.js +15 -16
  49. package/es/rce/DraggingBlocker.js +4 -2
  50. package/es/rce/KeyboardShortcutModal.js +3 -2
  51. package/es/rce/RCE.js +16 -17
  52. package/es/rce/RCEGlobals.js +12 -10
  53. package/es/rce/RCEVariants.js +29 -14
  54. package/es/rce/RCEWrapper.js +530 -641
  55. package/es/rce/RCEWrapper.utils.js +131 -0
  56. package/es/rce/RCEWrapperProps.js +9 -5
  57. package/es/rce/RceHtmlEditor.js +17 -19
  58. package/es/rce/ResizeHandle.js +4 -10
  59. package/es/rce/RestoreAutoSaveModal.js +1 -2
  60. package/es/rce/ShowOnFocusButton/index.js +2 -8
  61. package/es/rce/StatusBar.js +10 -44
  62. package/es/rce/alertHandler.js +1 -4
  63. package/es/rce/contentInsertion.js +36 -59
  64. package/es/rce/contentInsertionUtils.js +6 -8
  65. package/es/rce/contentRendering.js +13 -17
  66. package/es/rce/customEvents.js +1 -0
  67. package/es/rce/editorLanguage.js +23 -11
  68. package/es/rce/indicatorRegion.js +7 -7
  69. package/es/rce/normalizeLocale.js +5 -3
  70. package/es/rce/normalizeProps.js +7 -5
  71. package/es/rce/plugins/instructure-ui-icons/plugin.js +21 -3
  72. package/es/rce/plugins/instructure_color/clickCallback.js +82 -0
  73. package/es/rce/plugins/instructure_color/components/ColorPicker.js +294 -0
  74. package/es/rce/plugins/instructure_color/components/ColorPopup.js +67 -0
  75. package/es/rce/plugins/instructure_color/components/colorUtils.js +60 -0
  76. package/es/rce/plugins/instructure_color/plugin.js +40 -0
  77. package/es/rce/plugins/instructure_condensed_buttons/core/ListUtils.js +10 -3
  78. package/es/rce/plugins/instructure_condensed_buttons/plugin.js +1 -0
  79. package/es/rce/plugins/instructure_condensed_buttons/ui/alignment-button.js +1 -2
  80. package/es/rce/plugins/instructure_condensed_buttons/ui/directionality-button.js +3 -2
  81. package/es/rce/plugins/instructure_condensed_buttons/ui/indent-outdent-button.js +1 -0
  82. package/es/rce/plugins/instructure_condensed_buttons/ui/list-button.js +26 -25
  83. package/es/rce/plugins/instructure_condensed_buttons/ui/subscript-superscript-button.js +2 -3
  84. package/es/rce/plugins/instructure_documents/clickCallback.js +1 -0
  85. package/es/rce/plugins/instructure_documents/components/DocumentsPanel.js +1 -9
  86. package/es/rce/plugins/instructure_documents/components/Link.js +4 -20
  87. package/es/rce/plugins/instructure_documents/plugin.js +7 -14
  88. package/es/rce/plugins/instructure_equation/EquationEditorModal/advancedOnlySyntax.js +4 -2
  89. package/es/rce/plugins/instructure_equation/EquationEditorModal/advancedPreference.js +1 -2
  90. package/es/rce/plugins/instructure_equation/EquationEditorModal/index.js +17 -37
  91. package/es/rce/plugins/instructure_equation/EquationEditorModal/latexTextareaUtil.js +14 -15
  92. package/es/rce/plugins/instructure_equation/EquationEditorModal/parseLatex.js +6 -5
  93. package/es/rce/plugins/instructure_equation/EquationEditorModal/styles.js +4 -2
  94. package/es/rce/plugins/instructure_equation/EquationEditorToolbar/buttons.js +14 -8
  95. package/es/rce/plugins/instructure_equation/EquationEditorToolbar/index.js +13 -18
  96. package/es/rce/plugins/instructure_equation/MathIcon/index.js +4 -5
  97. package/es/rce/plugins/instructure_equation/MathIcon/svgs.js +1 -1
  98. package/es/rce/plugins/instructure_equation/clickCallback.js +2 -5
  99. package/es/rce/plugins/instructure_equation/mathlive/index.js +167 -16
  100. package/es/rce/plugins/instructure_equation/plugin.js +7 -10
  101. package/es/rce/plugins/instructure_fullscreen/plugin.js +1 -6
  102. package/es/rce/plugins/instructure_html_view/clickCallback.js +1 -0
  103. package/es/rce/plugins/instructure_html_view/plugin.js +5 -4
  104. package/es/rce/plugins/instructure_icon_maker/clickCallback.js +5 -8
  105. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ColorSection.js +47 -51
  106. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/CreateIconMakerForm.js +10 -10
  107. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Footer.js +11 -11
  108. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Group.js +6 -6
  109. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Header.js +8 -10
  110. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/Course.js +32 -31
  111. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageOptions.js +24 -35
  112. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageSection.js +32 -32
  113. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ModeSelect.js +11 -11
  114. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/MultiColor/index.js +16 -15
  115. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/MultiColor/svg.js +1 -0
  116. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/SVGList.js +11 -11
  117. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/SVGThumbnail.js +9 -13
  118. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/SingleColor/index.js +12 -13
  119. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/SingleColor/svg.js +33 -80
  120. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/Upload.js +34 -28
  121. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/index.js +1 -0
  122. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/propTypes.js +1 -0
  123. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/utils.js +5 -5
  124. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Preview.js +7 -8
  125. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ShapeSection.js +5 -7
  126. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/TextSection.js +5 -10
  127. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/index.js +1 -0
  128. package/es/rce/plugins/instructure_icon_maker/components/IconMakerTray.js +38 -60
  129. package/es/rce/plugins/instructure_icon_maker/components/SavedIconMakerList.js +4 -4
  130. package/es/rce/plugins/instructure_icon_maker/plugin.js +10 -14
  131. package/es/rce/plugins/instructure_icon_maker/reducers/imageSection.js +37 -38
  132. package/es/rce/plugins/instructure_icon_maker/reducers/svgSettings.js +24 -24
  133. package/es/rce/plugins/instructure_icon_maker/registerEditToolbar.js +2 -4
  134. package/es/rce/plugins/instructure_icon_maker/svg/constants.js +4 -3
  135. package/es/rce/plugins/instructure_icon_maker/svg/font.js +3 -1
  136. package/es/rce/plugins/instructure_icon_maker/svg/image.js +74 -90
  137. package/es/rce/plugins/instructure_icon_maker/svg/index.js +17 -24
  138. package/es/rce/plugins/instructure_icon_maker/svg/metadata.js +1 -0
  139. package/es/rce/plugins/instructure_icon_maker/svg/settings.js +48 -58
  140. package/es/rce/plugins/instructure_icon_maker/svg/shape.js +5 -54
  141. package/es/rce/plugins/instructure_icon_maker/svg/text.js +35 -124
  142. package/es/rce/plugins/instructure_icon_maker/svg/utils.js +3 -11
  143. package/es/rce/plugins/instructure_icon_maker/utils/IconMakerClose.js +4 -9
  144. package/es/rce/plugins/instructure_icon_maker/utils/IconMakerFormHasChanges.js +1 -15
  145. package/es/rce/plugins/instructure_icon_maker/utils/addIconMakerAttributes.js +3 -4
  146. package/es/rce/plugins/instructure_icon_maker/utils/iconValidation.js +2 -3
  147. package/es/rce/plugins/instructure_icon_maker/utils/iconsLabels.js +1 -0
  148. package/es/rce/plugins/instructure_icon_maker/utils/useDebouncedValue.js +12 -13
  149. package/es/rce/plugins/instructure_image/ImageEmbedOptions.js +9 -31
  150. package/es/rce/plugins/instructure_image/ImageList/Image.js +8 -14
  151. package/es/rce/plugins/instructure_image/ImageList/index.js +8 -10
  152. package/es/rce/plugins/instructure_image/ImageOptionsTray/TrayController.js +9 -31
  153. package/es/rce/plugins/instructure_image/ImageOptionsTray/index.js +6 -19
  154. package/es/rce/plugins/instructure_image/Images/index.js +1 -3
  155. package/es/rce/plugins/instructure_image/clickCallback.js +1 -0
  156. package/es/rce/plugins/instructure_image/plugin.js +14 -20
  157. package/es/rce/plugins/instructure_links/clickCallback.js +1 -0
  158. package/es/rce/plugins/instructure_links/components/AccordionSection.js +8 -8
  159. package/es/rce/plugins/instructure_links/components/CollectionPanel.js +1 -3
  160. package/es/rce/plugins/instructure_links/components/Link.js +68 -84
  161. package/es/rce/plugins/instructure_links/components/LinkOptionsDialog/LinkOptionsDialogController.js +2 -23
  162. package/es/rce/plugins/instructure_links/components/LinkOptionsDialog/index.js +3 -6
  163. package/es/rce/plugins/instructure_links/components/LinkOptionsTray/LinkOptionsTrayController.js +3 -20
  164. package/es/rce/plugins/instructure_links/components/LinkOptionsTray/index.js +3 -14
  165. package/es/rce/plugins/instructure_links/components/LinkSet.js +32 -57
  166. package/es/rce/plugins/instructure_links/components/LinksPanel.js +22 -10
  167. package/es/rce/plugins/instructure_links/components/NavigationPanel.js +7 -9
  168. package/es/rce/plugins/instructure_links/components/NoResults.js +7 -14
  169. package/es/rce/plugins/instructure_links/plugin.js +23 -49
  170. package/es/rce/plugins/instructure_links/validateURL.js +81 -36
  171. package/es/rce/plugins/instructure_media_embed/clickCallback.js +5 -9
  172. package/es/rce/plugins/instructure_media_embed/components/Embed.js +7 -7
  173. package/es/rce/plugins/instructure_media_embed/plugin.js +7 -3
  174. package/es/rce/plugins/instructure_paste/pasteMenuCommand.js +1 -5
  175. package/es/rce/plugins/instructure_paste/plugin.js +29 -33
  176. package/es/rce/plugins/instructure_rce_external_tools/ExternalToolsEnv.js +31 -79
  177. package/es/rce/plugins/instructure_rce_external_tools/RceToolWrapper.js +24 -83
  178. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialog.js +39 -69
  179. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogModal.js +1 -2
  180. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogTray.js +1 -1
  181. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionDialog.js +5 -14
  182. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionItem.js +1 -2
  183. package/es/rce/plugins/instructure_rce_external_tools/components/util/ExpandoText.js +1 -0
  184. package/es/rce/plugins/instructure_rce_external_tools/components/util/ToolLaunchIframe.js +2 -1
  185. package/es/rce/plugins/instructure_rce_external_tools/constants.js +28 -0
  186. package/es/rce/plugins/instructure_rce_external_tools/dialog-helper.js +20 -6
  187. package/es/rce/plugins/instructure_rce_external_tools/helpers/tags.js +0 -2
  188. package/es/rce/plugins/instructure_rce_external_tools/jquery/jquery.dropdownList.js +129 -136
  189. package/es/rce/plugins/instructure_rce_external_tools/lti11-content-items/RceLti11ContentItem.js +110 -112
  190. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/Lti13ContentItemJson.js +1 -0
  191. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/RceLti13ContentItem.js +4 -21
  192. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/models/BaseLinkContentItem.js +5 -19
  193. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/models/HtmlFragmentContentItem.js +1 -6
  194. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/models/ImageContentItem.js +1 -9
  195. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/models/LinkContentItem.js +1 -1
  196. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/models/ResourceLinkContentItem.js +3 -5
  197. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/processEditorContentItems.js +23 -16
  198. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/rceLti13ContentItemFromJson.js +3 -4
  199. package/es/rce/plugins/instructure_rce_external_tools/plugin.js +11 -20
  200. package/es/rce/plugins/instructure_rce_external_tools/util/addParentFrameContextToUrl.js +1 -1
  201. package/es/rce/plugins/instructure_rce_external_tools/util/externalToolsForToolbar.js +42 -0
  202. package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.js +6 -35
  203. package/es/rce/plugins/instructure_record/AudioOptionsTray/index.js +13 -17
  204. package/es/rce/plugins/instructure_record/MediaPanel/index.js +1 -9
  205. package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.js +16 -66
  206. package/es/rce/plugins/instructure_record/VideoOptionsTray/index.js +21 -35
  207. package/es/rce/plugins/instructure_record/clickCallback.js +32 -44
  208. package/es/rce/plugins/instructure_record/mediaTranslations.js +1 -0
  209. package/es/rce/plugins/instructure_record/plugin.js +11 -18
  210. package/es/rce/plugins/instructure_search_and_replace/clickCallback.js +4 -8
  211. package/es/rce/plugins/instructure_search_and_replace/components/FindReplaceTray.js +34 -51
  212. package/es/rce/plugins/instructure_search_and_replace/components/FindReplaceTrayController.js +12 -30
  213. package/es/rce/plugins/instructure_search_and_replace/getSelectionContext.js +2 -9
  214. package/es/rce/plugins/instructure_search_and_replace/plugin.js +2 -5
  215. package/es/rce/plugins/instructure_studio_media_options/plugin.js +1 -1
  216. package/es/rce/plugins/instructure_wordcount/clickCallback.js +5 -9
  217. package/es/rce/plugins/instructure_wordcount/components/WordCountModal.js +27 -37
  218. package/es/rce/plugins/instructure_wordcount/plugin.js +1 -0
  219. package/es/rce/plugins/instructure_wordcount/utils/countContent.js +4 -11
  220. package/es/rce/plugins/instructure_wordcount/utils/tableContent.js +6 -8
  221. package/es/rce/plugins/shared/CanvasContentTray.js +29 -63
  222. package/es/rce/plugins/shared/CheckerboardStyling.js +1 -1
  223. package/es/rce/plugins/shared/ColorInput.js +27 -39
  224. package/es/rce/plugins/shared/ConditionalTooltip.js +6 -6
  225. package/es/rce/plugins/shared/ContentSelection.js +29 -78
  226. package/es/rce/plugins/shared/DimensionUtils.js +3 -12
  227. package/es/rce/plugins/shared/DimensionsInput/DimensionInput.js +6 -6
  228. package/es/rce/plugins/shared/DimensionsInput/index.js +37 -15
  229. package/es/rce/plugins/shared/DimensionsInput/useDimensionsState.js +5 -29
  230. package/es/rce/plugins/shared/ErrorBoundary.js +2 -5
  231. package/es/rce/plugins/shared/EventUtils.js +2 -4
  232. package/es/rce/plugins/shared/Filter.js +8 -38
  233. package/es/rce/plugins/shared/FixedContentTray.js +16 -17
  234. package/es/rce/plugins/shared/ImageCropper/DirectionRegion.js +4 -12
  235. package/es/rce/plugins/shared/ImageCropper/Modal.js +16 -20
  236. package/es/rce/plugins/shared/ImageCropper/Preview.js +18 -24
  237. package/es/rce/plugins/shared/ImageCropper/constants.js +1 -0
  238. package/es/rce/plugins/shared/ImageCropper/controls/CustomNumberInput.js +10 -14
  239. package/es/rce/plugins/shared/ImageCropper/controls/ResetControls.js +4 -4
  240. package/es/rce/plugins/shared/ImageCropper/controls/RotationControls.js +5 -15
  241. package/es/rce/plugins/shared/ImageCropper/controls/ShapeControls.js +8 -11
  242. package/es/rce/plugins/shared/ImageCropper/controls/ZoomControls.js +5 -16
  243. package/es/rce/plugins/shared/ImageCropper/controls/index.js +5 -5
  244. package/es/rce/plugins/shared/ImageCropper/controls/useDebouncedNumericValue.js +16 -31
  245. package/es/rce/plugins/shared/ImageCropper/controls/utils.js +1 -2
  246. package/es/rce/plugins/shared/ImageCropper/imageCropUtils.js +19 -31
  247. package/es/rce/plugins/shared/ImageCropper/index.js +1 -0
  248. package/es/rce/plugins/shared/ImageCropper/propTypes.js +1 -0
  249. package/es/rce/plugins/shared/ImageCropper/reducers/imageCropper.js +15 -14
  250. package/es/rce/plugins/shared/ImageCropper/shape.js +1 -0
  251. package/es/rce/plugins/shared/ImageCropper/svg/index.js +1 -2
  252. package/es/rce/plugins/shared/ImageCropper/svg/shape.js +5 -22
  253. package/es/rce/plugins/shared/ImageCropper/svg/utils.js +3 -4
  254. package/es/rce/plugins/shared/ImageCropper/useKeyMouseEvents.js +20 -50
  255. package/es/rce/plugins/shared/ImageCropper/useMouseWheel.js +8 -10
  256. package/es/rce/plugins/shared/ImageOptionsForm.js +18 -20
  257. package/es/rce/plugins/shared/LinkDisplay.js +9 -11
  258. package/es/rce/plugins/shared/PreviewIcon.js +9 -15
  259. package/es/rce/plugins/shared/Previewable.js +1 -0
  260. package/es/rce/plugins/shared/RceFileBrowser.js +7 -10
  261. package/es/rce/plugins/shared/StoreContext.js +9 -12
  262. package/es/rce/plugins/shared/StudioLtiSupportUtils.js +15 -12
  263. package/es/rce/plugins/shared/UnknownFileTypePanel.js +1 -0
  264. package/es/rce/plugins/shared/Upload/CanvasContentPanel.js +19 -25
  265. package/es/rce/plugins/shared/Upload/CategoryProcessor.js +2 -3
  266. package/es/rce/plugins/shared/Upload/ComputerPanel.js +19 -40
  267. package/es/rce/plugins/shared/Upload/PanelFilter.js +10 -20
  268. package/es/rce/plugins/shared/Upload/SvgCategoryProcessor.js +4 -3
  269. package/es/rce/plugins/shared/Upload/UploadFile.js +32 -38
  270. package/es/rce/plugins/shared/Upload/UploadFileModal.js +37 -59
  271. package/es/rce/plugins/shared/Upload/UrlPanel.js +5 -5
  272. package/es/rce/plugins/shared/Upload/UsageRightsSelectBox.js +25 -36
  273. package/es/rce/plugins/shared/Upload/doFileUpload.js +10 -13
  274. package/es/rce/plugins/shared/Upload/index.js +1 -0
  275. package/es/rce/plugins/shared/ai_tools/AIResponseModal.js +8 -11
  276. package/es/rce/plugins/shared/ai_tools/AIToolsTray.js +19 -40
  277. package/es/rce/plugins/shared/ai_tools/aiicons.js +3 -2
  278. package/es/rce/plugins/shared/ai_tools/index.js +1 -0
  279. package/es/rce/plugins/shared/buildDownloadUrl.js +0 -2
  280. package/es/rce/plugins/shared/canvasContentUtils.js +7 -11
  281. package/es/rce/plugins/shared/compressionUtils.js +18 -28
  282. package/es/rce/plugins/shared/dateUtils.js +1 -1
  283. package/es/rce/plugins/shared/do-fetch-api-effect/defaultFetchOptions.js +4 -2
  284. package/es/rce/plugins/shared/do-fetch-api-effect/doFetchApi.js +18 -24
  285. package/es/rce/plugins/shared/do-fetch-api-effect/get-cookie.js +1 -1
  286. package/es/rce/plugins/shared/do-fetch-api-effect/index.js +1 -0
  287. package/es/rce/plugins/shared/do-fetch-api-effect/parse-link-header.js +6 -20
  288. package/es/rce/plugins/shared/do-fetch-api-effect/query-string-encoding.js +5 -3
  289. package/es/rce/plugins/shared/fileShape.js +4 -9
  290. package/es/rce/plugins/shared/fileTypeUtils.js +34 -47
  291. package/es/rce/plugins/shared/fileUtils.js +1 -2
  292. package/es/rce/plugins/shared/linkUtils.js +1 -16
  293. package/es/rce/plugins/shared/round.js +2 -2
  294. package/es/rce/plugins/shared/trayUtils.js +7 -3
  295. package/es/rce/plugins/shared/useDataUrl.js +13 -14
  296. package/es/rce/plugins/shared/useFilterSettings.js +3 -3
  297. package/es/rce/plugins/tinymce-a11y-checker/components/ColorField.js +4 -8
  298. package/es/rce/plugins/tinymce-a11y-checker/components/checker.js +12 -72
  299. package/es/rce/plugins/tinymce-a11y-checker/components/color-picker.js +1 -2
  300. package/es/rce/plugins/tinymce-a11y-checker/components/placeholder-svg.js +1 -0
  301. package/es/rce/plugins/tinymce-a11y-checker/components/pointer.js +1 -0
  302. package/es/rce/plugins/tinymce-a11y-checker/node-checker.js +2 -9
  303. package/es/rce/plugins/tinymce-a11y-checker/plugin.js +18 -24
  304. package/es/rce/plugins/tinymce-a11y-checker/rules/adjacent-links.js +3 -26
  305. package/es/rce/plugins/tinymce-a11y-checker/rules/headings-sequence.js +9 -38
  306. package/es/rce/plugins/tinymce-a11y-checker/rules/headings-start-at-h2.js +2 -7
  307. package/es/rce/plugins/tinymce-a11y-checker/rules/img-alt-filename.js +1 -2
  308. package/es/rce/plugins/tinymce-a11y-checker/rules/img-alt-length.js +1 -1
  309. package/es/rce/plugins/tinymce-a11y-checker/rules/img-alt.js +1 -2
  310. package/es/rce/plugins/tinymce-a11y-checker/rules/index.js +1 -0
  311. package/es/rce/plugins/tinymce-a11y-checker/rules/large-text-contrast.js +2 -6
  312. package/es/rce/plugins/tinymce-a11y-checker/rules/list-structure.js +5 -24
  313. package/es/rce/plugins/tinymce-a11y-checker/rules/paragraphs-for-headings.js +1 -3
  314. package/es/rce/plugins/tinymce-a11y-checker/rules/small-text-contrast.js +2 -8
  315. package/es/rce/plugins/tinymce-a11y-checker/rules/table-caption.js +1 -3
  316. package/es/rce/plugins/tinymce-a11y-checker/rules/table-header-scope.js +1 -2
  317. package/es/rce/plugins/tinymce-a11y-checker/rules/table-header.js +1 -9
  318. package/es/rce/plugins/tinymce-a11y-checker/utils/colors.js +1 -0
  319. package/es/rce/plugins/tinymce-a11y-checker/utils/describe.js +1 -7
  320. package/es/rce/plugins/tinymce-a11y-checker/utils/dom.js +3 -30
  321. package/es/rce/plugins/tinymce-a11y-checker/utils/indicate.js +18 -18
  322. package/es/rce/plugins/tinymce-a11y-checker/utils/rgb-hex.js +6 -12
  323. package/es/rce/plugins/tinymce-a11y-checker/utils/strings.js +1 -4
  324. package/es/rce/root.js +17 -16
  325. package/es/rce/sanitizePlugins.js +1 -3
  326. package/es/rce/style.js +1 -4
  327. package/es/rce/tinyRCE.js +14 -9
  328. package/es/rce/tinymce.oxide.content.min.css.js +1 -0
  329. package/es/rce/tinymce.oxide.skin.min.css.js +1 -0
  330. package/es/rce/transformContent.js +9 -11
  331. package/es/rce/types.js +1 -0
  332. package/es/rce/userOS.js +1 -1
  333. package/es/rce/wrapInitCb.js +50 -43
  334. package/es/rcs/api.js +100 -171
  335. package/es/rcs/buildError.js +8 -20
  336. package/es/rcs/fake.js +9 -20
  337. package/es/sidebar/actions/all_files.js +2 -0
  338. package/es/sidebar/actions/data.js +4 -7
  339. package/es/sidebar/actions/documents.js +19 -18
  340. package/es/sidebar/actions/files.js +21 -28
  341. package/es/sidebar/actions/filter.js +5 -5
  342. package/es/sidebar/actions/flickr.js +1 -1
  343. package/es/sidebar/actions/images.js +32 -37
  344. package/es/sidebar/actions/links.js +1 -0
  345. package/es/sidebar/actions/media.js +27 -28
  346. package/es/sidebar/actions/session.js +2 -5
  347. package/es/sidebar/actions/ui.js +1 -0
  348. package/es/sidebar/actions/upload.js +38 -74
  349. package/es/sidebar/containers/Sidebar.js +1 -2
  350. package/es/sidebar/containers/sidebarHandlers.js +9 -13
  351. package/es/sidebar/dragHtml.js +11 -5
  352. package/es/sidebar/reducers/all_files.js +5 -6
  353. package/es/sidebar/reducers/collection.js +12 -15
  354. package/es/sidebar/reducers/collections.js +6 -8
  355. package/es/sidebar/reducers/documents.js +7 -16
  356. package/es/sidebar/reducers/files.js +4 -6
  357. package/es/sidebar/reducers/filter.js +8 -23
  358. package/es/sidebar/reducers/flickr.js +10 -12
  359. package/es/sidebar/reducers/folder.js +16 -18
  360. package/es/sidebar/reducers/folders.js +4 -6
  361. package/es/sidebar/reducers/images.js +4 -16
  362. package/es/sidebar/reducers/index.js +3 -1
  363. package/es/sidebar/reducers/media.js +7 -16
  364. package/es/sidebar/reducers/newPageLinkExpanded.js +2 -5
  365. package/es/sidebar/reducers/noop.js +2 -2
  366. package/es/sidebar/reducers/rootFolderId.js +2 -5
  367. package/es/sidebar/reducers/session.js +4 -6
  368. package/es/sidebar/reducers/ui.js +6 -25
  369. package/es/sidebar/reducers/upload.js +16 -64
  370. package/es/sidebar/store/configureStore.js +1 -0
  371. package/es/sidebar/store/initialState.js +14 -26
  372. package/es/translations/locales/ab.js +1 -0
  373. package/es/translations/locales/ar.js +72 -8
  374. package/es/translations/locales/ca.js +72 -8
  375. package/es/translations/locales/cs.js +1 -0
  376. package/es/translations/locales/cs_CZ.js +1 -0
  377. package/es/translations/locales/cy.js +72 -8
  378. package/es/translations/locales/da-x-k12.js +72 -8
  379. package/es/translations/locales/da.js +72 -8
  380. package/es/translations/locales/da_DK.js +1 -0
  381. package/es/translations/locales/de.js +72 -8
  382. package/es/translations/locales/el.js +4 -0
  383. package/es/translations/locales/en-AU-x-unimelb.js +72 -8
  384. package/es/translations/locales/en-GB-x-ukhe.js +72 -8
  385. package/es/translations/locales/en.js +72 -8
  386. package/es/translations/locales/en_AU.js +72 -8
  387. package/es/translations/locales/en_CA.js +72 -8
  388. package/es/translations/locales/en_CY.js +72 -8
  389. package/es/translations/locales/en_GB.js +72 -8
  390. package/es/translations/locales/en_NZ.js +1 -0
  391. package/es/translations/locales/en_SE.js +1 -0
  392. package/es/translations/locales/en_US.js +1 -0
  393. package/es/translations/locales/es.js +72 -8
  394. package/es/translations/locales/es_ES.js +72 -8
  395. package/es/translations/locales/es_GT.js +1 -0
  396. package/es/translations/locales/fa_IR.js +7 -0
  397. package/es/translations/locales/fi.js +72 -8
  398. package/es/translations/locales/fr.js +72 -8
  399. package/es/translations/locales/fr_CA.js +73 -9
  400. package/es/translations/locales/ga.js +5 -13
  401. package/es/translations/locales/he.js +7 -0
  402. package/es/translations/locales/hi.js +72 -8
  403. package/es/translations/locales/ht.js +72 -8
  404. package/es/translations/locales/hu.js +7 -6
  405. package/es/translations/locales/hu_HU.js +1 -0
  406. package/es/translations/locales/hy.js +1 -0
  407. package/es/translations/locales/id.js +72 -8
  408. package/es/translations/locales/id_ID.js +1 -0
  409. package/es/translations/locales/is.js +72 -8
  410. package/es/translations/locales/it.js +72 -8
  411. package/es/translations/locales/ja.js +72 -8
  412. package/es/translations/locales/ko.js +1 -0
  413. package/es/translations/locales/ko_KR.js +1 -0
  414. package/es/translations/locales/lt.js +1 -0
  415. package/es/translations/locales/lt_LT.js +1 -0
  416. package/es/translations/locales/mi.js +72 -8
  417. package/es/translations/locales/mn_MN.js +1 -0
  418. package/es/translations/locales/ms.js +72 -8
  419. package/es/translations/locales/nb-x-k12.js +72 -8
  420. package/es/translations/locales/nb.js +72 -8
  421. package/es/translations/locales/nl.js +72 -8
  422. package/es/translations/locales/nl_NL.js +1 -0
  423. package/es/translations/locales/nn.js +7 -6
  424. package/es/translations/locales/pl.js +72 -8
  425. package/es/translations/locales/pt.js +72 -8
  426. package/es/translations/locales/pt_BR.js +72 -8
  427. package/es/translations/locales/ro.js +1 -0
  428. package/es/translations/locales/ru.js +72 -8
  429. package/es/translations/locales/se.js +1 -0
  430. package/es/translations/locales/sl.js +72 -8
  431. package/es/translations/locales/sv-x-k12.js +72 -8
  432. package/es/translations/locales/sv.js +72 -8
  433. package/es/translations/locales/sv_SE.js +1 -0
  434. package/es/translations/locales/tg.js +1 -0
  435. package/es/translations/locales/th.js +72 -8
  436. package/es/translations/locales/th_TH.js +1 -0
  437. package/es/translations/locales/tl_PH.js +1 -0
  438. package/es/translations/locales/tr.js +7 -0
  439. package/es/translations/locales/uk_UA.js +7 -0
  440. package/es/translations/locales/vi.js +72 -8
  441. package/es/translations/locales/vi_VN.js +1 -0
  442. package/es/translations/locales/zh-Hans.js +72 -8
  443. package/es/translations/locales/zh-Hant.js +72 -8
  444. package/es/translations/locales/zh.js +72 -8
  445. package/es/translations/locales/zh_HK.js +72 -8
  446. package/es/translations/locales/zh_TW.Big5.js +1 -0
  447. package/es/translations/locales/zh_TW.js +1 -0
  448. package/es/translations/tinymce/ar_SA.js +1 -0
  449. package/es/translations/tinymce/fi.js +1 -0
  450. package/es/translations/tinymce/ga.js +1 -0
  451. package/es/translations/tinymce/id.js +1 -0
  452. package/es/translations/tinymce/ru.js +1 -0
  453. package/es/translations/tinymce/ru_RU.js +1 -0
  454. package/es/translations/tinymce/sl.js +1 -0
  455. package/es/translations/tinymce/sr.js +1 -0
  456. package/es/translations/tinymce/th.js +1 -0
  457. package/es/translations/tinymce/uk_UA.js +1 -0
  458. package/es/translations/tinymce/vi_VN.js +1 -0
  459. package/es/util/TypedDict.js +4 -2
  460. package/es/util/elem-util.js +1 -1
  461. package/es/util/encrypted-storage.js +3 -13
  462. package/es/util/file-url-util.js +2 -7
  463. package/es/util/fullscreenHelpers.js +9 -9
  464. package/es/util/instui-icon-helper.js +4 -3
  465. package/es/util/loadingPlaceholder.js +39 -41
  466. package/es/util/simpleCache.js +1 -5
  467. package/es/util/string-util.js +1 -1
  468. package/es/util/textarea-editing-util.js +3 -7
  469. package/es/util/tinymce-plugin-util.js +0 -5
  470. package/es/util/url-util.js +20 -29
  471. package/eslint.config.js +250 -0
  472. package/jest.config.js +1 -1
  473. package/locales/en.json +190 -10
  474. package/package.json +78 -82
  475. package/scripts/build-canvas +2 -1
  476. package/scripts/build.js +4 -4
  477. package/scripts/installTranslations.js +7 -8
  478. package/testcafe/RCEWrapper.test.js +0 -1
  479. package/testcafe/StatusBar.test.js +0 -1
  480. package/testcafe/axe.test.js +3 -4
  481. package/testcafe/enhanceUserContent.test.js +0 -1
  482. package/tsconfig.json +21 -16
  483. package/{es/rce/__mocks__/styleMock.js → types/format-message-generate-id.d.ts} +6 -2
  484. package/{es/rce/plugins/shared/__mocks__/screenfull.js → types/js-beautify.d.ts} +4 -7
  485. package/.eslintrc +0 -45
  486. package/.prettierignore +0 -6
  487. package/es/rce/__mocks__/_mockCryptoEs.js +0 -124
  488. package/es/rce/__mocks__/tinymceReact.js +0 -55
  489. package/es/rce/plugins/tinymce-a11y-checker/rules/__mocks__/index.js +0 -53
@@ -1,5 +1,4 @@
1
- var _Intl, _Intl$DateTimeFormat, _Intl$DateTimeFormat$;
2
-
1
+ import _pt from "prop-types";
3
2
  /*
4
3
  * Copyright (C) 2018 - present Instructure, Inc.
5
4
  *
@@ -17,6 +16,7 @@ var _Intl, _Intl$DateTimeFormat, _Intl$DateTimeFormat$;
17
16
  * You should have received a copy of the GNU Affero General Public License along
18
17
  * with this program. If not, see <http://www.gnu.org/licenses/>.
19
18
  */
19
+
20
20
  import React, { Suspense } from 'react';
21
21
  import { Editor } from '@tinymce/tinymce-react';
22
22
  import _ from 'lodash';
@@ -32,12 +32,12 @@ import getCookie from '../common/getCookie';
32
32
  import formatMessage from '../format-message';
33
33
  import * as contentInsertion from './contentInsertion';
34
34
  import indicatorRegion from './indicatorRegion';
35
- import editorLanguage from './editorLanguage';
35
+ import { editorLanguage } from './editorLanguage';
36
36
  import normalizeLocale from './normalizeLocale';
37
37
  import { sanitizePlugins } from './sanitizePlugins';
38
38
  import RCEGlobals from './RCEGlobals';
39
39
  import defaultTinymceConfig from '../defaultTinymceConfig';
40
- import { FS_CHANGEEVENT, FS_ELEMENT, FS_ENABLED, FS_EXIT, FS_REQUEST, instuiPopupMountNode } from '../util/fullscreenHelpers';
40
+ import { FS_CHANGEEVENT, FS_ELEMENT, FS_ENABLED, FS_EXIT, FS_REQUEST, instuiPopupMountNodeFn } from '../util/fullscreenHelpers';
41
41
  import indicate from '../common/indicate';
42
42
  import bridge from '../bridge';
43
43
  import CanvasContentTray from './plugins/shared/CanvasContentTray';
@@ -56,74 +56,50 @@ import contentCSS from './tinymce.oxide.content.min.css';
56
56
  import { rceWrapperPropTypes } from './RCEWrapperProps';
57
57
  import { insertPlaceholder, placeholderInfoFor, removePlaceholder } from '../util/loadingPlaceholder';
58
58
  import { transformRceContentForEditing } from './transformContent';
59
+ // @ts-expect-error
59
60
  import { IconMoreSolid } from '@instructure/ui-icons/es/svg';
60
61
  import EncryptedStorage from '../util/encrypted-storage';
61
62
  import buildStyle from './style';
62
- import { externalToolsForToolbar } from './plugins/instructure_rce_external_tools/RceToolWrapper';
63
63
  import { getMenubarForVariant, getMenuForVariant, getToolbarForVariant, getStatusBarFeaturesForVariant } from './RCEVariants';
64
+ import { focusFirstMenuButton, focusToolbar, isElementWithinTable, mergeMenu, mergeMenuItems, mergePlugins, mergeToolbar, parsePluginsToExclude, patchAutosavedContent } from './RCEWrapper.utils';
65
+ import { externalToolsForToolbar } from './plugins/instructure_rce_external_tools/util/externalToolsForToolbar';
64
66
  const RestoreAutoSaveModal = /*#__PURE__*/React.lazy(() => import('./RestoreAutoSaveModal'));
65
67
  const RceHtmlEditor = /*#__PURE__*/React.lazy(() => import('./RceHtmlEditor'));
66
68
  const ASYNC_FOCUS_TIMEOUT = 250;
67
69
  const DEFAULT_RCE_HEIGHT = '400px';
68
-
69
70
  function addKebabIcon(editor) {
70
71
  // This has to be done here instead of of in plugins/instructure-ui-icons/plugin.ts
71
72
  // presumably because the toolbar gets created before that plugin is loaded?
72
73
  editor.ui.registry.addIcon('more-drawer', IconMoreSolid.src);
73
- } // Get oxide the default skin injected into the DOM before the overrides loaded by themeable
74
-
74
+ }
75
75
 
76
+ // Get oxide the default skin injected into the DOM before the overrides loaded by themeable
76
77
  let inserted = false;
77
-
78
78
  function injectTinySkin() {
79
79
  if (inserted) return;
80
80
  inserted = true;
81
81
  const style = document.createElement('style');
82
82
  style.setAttribute('data-skin', 'tiny oxide skin');
83
- style.appendChild(document.createTextNode(skinCSS)); // there's CSS from discussions that turns the instui Selectors bold
83
+ style.appendChild(document.createTextNode(skinCSS));
84
+ // there's CSS from discussions that turns the instui Selectors bold
84
85
  // and in classic quizzes that also mucks with padding
85
-
86
86
  style.appendChild(document.createTextNode(`
87
87
  #discussion-edit-view .rce-wrapper input[readonly] {font-weight: normal;}
88
88
  #quiz_edit_wrapper .rce-wrapper input[readonly] {font-weight: normal; padding-left: .75rem;}
89
89
  `));
90
- const beforeMe = document.head.querySelector('style[data-glamor]') || // find instui's themeable stylesheet
91
- document.head.querySelector('style') || // find any stylesheet
90
+ const beforeMe = document.head.querySelector('style[data-glamor]') ||
91
+ // find instui's themeable stylesheet
92
+ document.head.querySelector('style') ||
93
+ // find any stylesheet
92
94
  document.head.firstElementChild;
93
95
  document.head.insertBefore(style, beforeMe);
94
96
  }
95
-
96
97
  const editorWrappers = new WeakMap();
97
98
 
98
- function focusToolbar(el) {
99
- const $firstToolbarButton = el.querySelector('.tox-tbtn');
100
- $firstToolbarButton && $firstToolbarButton.focus();
101
- }
102
-
103
- function focusFirstMenuButton(el) {
104
- const $firstMenu = el.querySelector('.tox-mbtn');
105
- $firstMenu && $firstMenu.focus();
106
- }
107
-
108
- function isElementWithinTable(node) {
109
- let elem = node;
110
-
111
- while (elem) {
112
- if (elem.tagName === 'TABLE' || elem.tagName === 'TD' || elem.tagName === 'TH') {
113
- return true;
114
- }
115
-
116
- elem = elem.parentElement;
117
- }
118
-
119
- return false;
120
- } // determines if localStorage is available for our use.
99
+ // determines if localStorage is available for our use.
121
100
  // see https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
122
-
123
-
124
101
  export function storageAvailable() {
125
102
  let storage;
126
-
127
103
  try {
128
104
  storage = window.localStorage;
129
105
  const x = '__storage_test__';
@@ -131,163 +107,174 @@ export function storageAvailable() {
131
107
  storage.removeItem(x);
132
108
  return true;
133
109
  } catch (e) {
134
- return e instanceof DOMException && ( // everything except Firefox
135
- e.code === 22 || // Firefox
136
- e.code === 1014 || // test name field too, because code might not be present
110
+ return e instanceof DOMException && (
137
111
  // everything except Firefox
138
- e.name === 'QuotaExceededError' || // Firefox
139
- e.name === 'NS_ERROR_DOM_QUOTA_REACHED') && // acknowledge QuotaExceededError only if there's something already stored
112
+ e.code === 22 ||
113
+ // Firefox
114
+ e.code === 1014 ||
115
+ // test name field too, because code might not be present
116
+ // everything except Firefox
117
+ e.name === 'QuotaExceededError' ||
118
+ // Firefox
119
+ e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
120
+ // acknowledge QuotaExceededError only if there's something already stored
140
121
  storage && storage.length !== 0;
141
122
  }
142
123
  }
143
-
144
124
  function renderLoading() {
145
125
  return formatMessage('Loading');
146
126
  }
147
-
148
127
  let alertIdValue = 0;
149
-
150
128
  class RCEWrapper extends React.Component {
151
129
  static getByEditor(editor) {
152
130
  return editorWrappers.get(editor);
153
131
  }
154
-
155
132
  constructor(props) {
156
- var _this, _window, _window$location, _props$editorOptions, _props$editorOptions2;
157
-
158
133
  super(props);
159
- _this = this;
160
-
134
+ this._destroyCalled = false;
135
+ this._editorPlaceholderRef = void 0;
136
+ this._elementRef = void 0;
137
+ this._focusRegio = void 0;
138
+ this._focusRegion = void 0;
139
+ this._mceSerializedInitialHtmlCached = void 0;
140
+ this._showOnFocusButton = void 0;
141
+ this._statusBarId = void 0;
142
+ this._textareaEl = void 0;
143
+ this.AIToolsTray = void 0;
144
+ this.editor = void 0;
145
+ this.initialContent = void 0;
146
+ this.intersectionObserver = void 0;
147
+ this.language = void 0;
148
+ this.ltiToolFavorites = void 0;
149
+ this.mutationObserver = void 0;
150
+ this.pendingEventHandlers = void 0;
151
+ this.pluginsToExclude = void 0;
152
+ this.resizeObserver = void 0;
153
+ this.storage = void 0;
154
+ this.variant = void 0;
155
+ this.style = void 0;
156
+ this.insert_code = void 0;
157
+ this.get_code = void 0;
158
+ this.set_code = void 0;
161
159
  this.onRemove = () => {
162
160
  bridge.detachEditor(this);
163
- this.props.onRemove && this.props.onRemove(this);
161
+ if (this.props.onRemove) {
162
+ this.props.onRemove(this);
163
+ }
164
164
  };
165
-
166
165
  this.toggleView = newView => {
167
166
  // coming from the menubar, we don't have a newView,
168
- let newState;
169
167
 
168
+ let newState;
170
169
  switch (this.state.editorView) {
171
170
  case WYSIWYG_VIEW:
172
- newState = {
173
- editorView: newView || PRETTY_HTML_EDITOR_VIEW
174
- };
175
- break;
176
-
171
+ {
172
+ newState = {
173
+ editorView: newView || PRETTY_HTML_EDITOR_VIEW
174
+ };
175
+ break;
176
+ }
177
177
  case PRETTY_HTML_EDITOR_VIEW:
178
- newState = {
179
- editorView: newView || WYSIWYG_VIEW
180
- };
181
- break;
182
-
178
+ {
179
+ newState = {
180
+ editorView: newView || WYSIWYG_VIEW
181
+ };
182
+ break;
183
+ }
183
184
  case RAW_HTML_EDITOR_VIEW:
184
- newState = {
185
- editorView: newView || WYSIWYG_VIEW
186
- };
185
+ {
186
+ newState = {
187
+ editorView: newView || WYSIWYG_VIEW
188
+ };
189
+ break;
190
+ }
191
+ default:
192
+ return;
187
193
  }
188
-
194
+ // @ts-expect-error
189
195
  this.setState(newState);
190
196
  this.checkAccessibility();
191
-
192
197
  if (newView === PRETTY_HTML_EDITOR_VIEW || newView === RAW_HTML_EDITOR_VIEW) {
193
- var _this$storage, _this$storage$setItem;
194
-
195
- (_this$storage = this.storage) === null || _this$storage === void 0 ? void 0 : (_this$storage$setItem = _this$storage.setItem) === null || _this$storage$setItem === void 0 ? void 0 : _this$storage$setItem.call(_this$storage, 'rce.htmleditor', newView);
196
- } // Emit view change event
197
-
198
+ this.storage?.setItem?.('rce.htmleditor', newView);
199
+ }
198
200
 
201
+ // Emit view change event
199
202
  this.mceInstance().fire(VIEW_CHANGE, {
200
203
  target: this.editor,
201
204
  newView: newState.editorView
202
205
  });
203
206
  };
204
-
205
207
  this.toggleFullscreen = () => {
206
208
  this.handleClickFullscreen();
207
209
  };
208
-
209
210
  this._onFullscreenChange = event => {
210
211
  if (document[FS_ELEMENT]) {
211
- var _window$visualViewpor;
212
-
212
+ // @ts-expect-error
213
213
  this.resizeObserver.observe(document[FS_ELEMENT]);
214
- (_window$visualViewpor = window.visualViewport) === null || _window$visualViewpor === void 0 ? void 0 : _window$visualViewpor.addEventListener('resize', this._handleFullscreenResize);
215
-
214
+ window.visualViewport?.addEventListener('resize', this._handleFullscreenResize);
216
215
  this._handleFullscreenResize();
217
-
216
+ // @ts-expect-error
218
217
  this._focusRegion = FocusRegionManager.activateRegion(document[FS_ELEMENT], {
219
218
  shouldContainFocus: true
220
219
  });
221
220
  } else {
222
- var _window$visualViewpor2;
223
-
224
221
  event.target.removeEventListener(FS_CHANGEEVENT, this._onFullscreenChange);
225
222
  this.resizeObserver.unobserve(event.target);
226
- (_window$visualViewpor2 = window.visualViewport) === null || _window$visualViewpor2 === void 0 ? void 0 : _window$visualViewpor2.removeEventListener('resize', this._handleFullscreenResize);
227
-
223
+ window.visualViewport?.removeEventListener('resize', this._handleFullscreenResize);
228
224
  this._setHeight(this.state.fullscreenState.prevHeight);
229
-
230
225
  if (this._focusRegion) {
231
226
  FocusRegionManager.blurRegion(event.target, this._focusRegion.id);
232
227
  }
233
228
  }
234
-
235
229
  this.focusCurrentView();
236
230
  };
237
-
238
231
  this._handleFullscreenResize = () => {
239
- var _window$visualViewpor3, _document$FS_ELEMENT;
240
-
241
- const ht = ((_window$visualViewpor3 = window.visualViewport) === null || _window$visualViewpor3 === void 0 ? void 0 : _window$visualViewpor3.height) || ((_document$FS_ELEMENT = document[FS_ELEMENT]) === null || _document$FS_ELEMENT === void 0 ? void 0 : _document$FS_ELEMENT.offsetHeight);
242
-
243
- this._setHeight(ht - this._getStatusBarHeight());
232
+ const ht = window.visualViewport?.height || document[FS_ELEMENT]?.offsetHeight;
233
+ this._setHeight((ht || 0) - this._getStatusBarHeight());
244
234
  };
245
-
246
235
  this.contentTrayClosing = false;
247
236
  this.blurTimer = 0;
248
-
249
- this.handleFocusRCE = event => {
250
- this.handleFocus(event);
237
+ this.handleFocusRCE = () => {
238
+ this.handleFocus();
251
239
  };
252
-
253
240
  this.handleBlurRCE = event => {
254
- var _this$_elementRef$cur;
255
-
256
241
  if (event.relatedTarget === null) {
257
242
  // focus might be moving to tinymce
258
243
  this.handleBlur(event);
259
244
  }
260
-
261
- if (!((_this$_elementRef$cur = this._elementRef.current) !== null && _this$_elementRef$cur !== void 0 && _this$_elementRef$cur.contains(event.relatedTarget))) {
245
+ if (!this._elementRef.current?.contains(event.relatedTarget)) {
262
246
  this.handleBlur(event);
263
247
  }
264
248
  };
265
-
266
- this.handleFocusEditor = (event, _editor) => {
249
+ this.handleFocusEditor = _event => {
267
250
  // use .active to put a focus ring around the content area
268
251
  // when the editor has focus. This isn't perfect, but it's
269
252
  // what we've got for now.
270
253
  const ifr = this.iframe;
271
- ifr && ifr.parentElement.classList.add('active');
272
- this.handleFocus(event);
254
+ if (ifr?.parentElement) {
255
+ ifr.parentElement.classList.add('active');
256
+ }
257
+ this.handleFocus();
273
258
  };
274
-
275
- this.handleBlurEditor = (event, _editor) => {
259
+ this.handleBlurEditor = event => {
276
260
  const ifr = this.iframe;
277
- ifr && ifr.parentElement.classList.remove('active');
261
+ if (ifr?.parentElement) {
262
+ ifr.parentElement.classList.remove('active');
263
+ }
278
264
  this.handleBlur(event);
279
265
  };
280
-
281
266
  this.handleKey = event => {
282
267
  if (event.code === 'F9' && event.altKey) {
283
268
  event.preventDefault();
284
269
  event.stopPropagation();
285
270
  this.setFocusAbilityForHeader(true);
271
+ // @ts-expect-error
286
272
  focusFirstMenuButton(this._elementRef.current);
287
273
  } else if (event.code === 'F10' && event.altKey) {
288
274
  event.preventDefault();
289
275
  event.stopPropagation();
290
276
  this.setFocusAbilityForHeader(true);
277
+ // @ts-expect-error
291
278
  focusToolbar(this._elementRef.current);
292
279
  } else if (event.code === 'F8' && event.altKey) {
293
280
  event.preventDefault();
@@ -300,7 +287,6 @@ class RCEWrapper extends React.Component {
300
287
  event.stopPropagation();
301
288
  }
302
289
  };
303
-
304
290
  this.handleClickFullscreen = () => {
305
291
  if (this._isFullscreen()) {
306
292
  this._exitFullscreen();
@@ -308,127 +294,122 @@ class RCEWrapper extends React.Component {
308
294
  this._enterFullscreen();
309
295
  }
310
296
  };
311
-
312
297
  this.handleInputChange = () => {
313
298
  this.checkAccessibility();
314
299
  };
315
-
316
300
  this.onInit = (_event, editor) => {
317
- var _this$props$onInitted, _this$props;
318
-
301
+ // @ts-expect-error
319
302
  editor.rceWrapper = this;
320
303
  this.editor = editor;
321
- const textarea = this.editor.getElement(); // expected by canvas
304
+ const textarea = this.editor.getElement();
322
305
 
323
- textarea.dataset.rich_text = true; // start with the textarea and tinymce in sync
306
+ // expected by canvas
307
+ // @ts-expect-error
308
+ textarea.dataset.rich_text = true;
324
309
 
310
+ // start with the textarea and tinymce in sync
311
+ // @ts-expect-error
325
312
  textarea.value = this.getCode();
326
313
  textarea.style.height = this.state.height;
327
-
328
314
  if (document.body.classList.contains('Underline-All-Links__enabled')) {
329
- this.iframe.contentDocument.body.classList.add('Underline-All-Links__enabled');
315
+ if (this.iframe?.contentDocument) {
316
+ this.iframe.contentDocument.body.classList.add('Underline-All-Links__enabled');
317
+ }
330
318
  }
331
-
332
- editor.on('wordCountUpdate', this.onWordCountUpdate); // add an aria-label to the application div that wraps RCE
319
+ editor.on('wordCountUpdate', this.onWordCountUpdate);
320
+ // add an aria-label to the application div that wraps RCE
333
321
  // and change role from "application" to "document" to ensure
334
322
  // the editor gets properly picked up by screen readers
335
-
336
323
  const tinyapp = document.querySelector('.tox-tinymce[role="application"]');
337
-
338
324
  if (tinyapp) {
339
325
  tinyapp.setAttribute('aria-label', formatMessage('Rich Content Editor'));
340
326
  tinyapp.setAttribute('role', 'document');
341
327
  tinyapp.setAttribute('tabIndex', '-1');
342
- } // Adds a focusout event listener for handling screen reader navigation focus
343
-
344
-
345
- const header = this._elementRef.current.querySelector('.tox-editor-header');
328
+ }
346
329
 
330
+ // Adds a focusout event listener for handling screen reader navigation focus
331
+ const header = this._elementRef.current?.querySelector('.tox-editor-header');
347
332
  if (header) {
333
+ // @ts-expect-error
348
334
  header.addEventListener('focusout', e => {
335
+ // @ts-expect-error
349
336
  const leavingHeader = !header.contains(e.relatedTarget);
350
-
351
337
  if (leavingHeader) {
352
338
  this.setFocusAbilityForHeader(false);
353
339
  }
354
340
  });
355
341
  }
342
+ this.setFocusAbilityForHeader(false);
356
343
 
357
- this.setFocusAbilityForHeader(false); // Probably should do this in tinymce.scss, but we only want it in new rce
358
-
344
+ // Probably should do this in tinymce.scss, but we only want it in new rce
359
345
  textarea.style.resize = 'none';
360
346
  editor.on('keydown', this.handleKey);
361
- editor.on('FullscreenStateChanged', this._onFullscreenChange); // This propagates click events on the editor out of the iframe to the parent
347
+ editor.on('FullscreenStateChanged', this._onFullscreenChange);
348
+ // This propagates click events on the editor out of the iframe to the parent
362
349
  // document. We need this so that click events get captured properly by instui
363
350
  // focus-trapping components, so they properly ignore trapping focus on click.
364
-
365
351
  editor.on('click', () => window.document.body.click(), true);
366
352
  editor.on('Cut Change input Undo Redo', debounce(this.handleInputChange, 1000));
367
353
  this.announceContextToolbars(editor);
368
-
369
354
  if (this.isAutoSaving) {
370
355
  this.initAutoSave(editor);
371
- } // first view
356
+ }
372
357
 
358
+ // first view
359
+ this.setEditorView(this.state.editorView);
373
360
 
374
- this.setEditorView(this.state.editorView); // readonly should have been handled via the init property passed
361
+ // readonly should have been handled via the init property passed
375
362
  // to <Editor>, but it's not.
363
+ editor.mode.set(this.props.readOnly ? 'readonly' : 'design');
376
364
 
377
- editor.mode.set(this.props.readOnly ? 'readonly' : 'design'); // Not using iframe_aria_text because compatibility issues.
365
+ // Not using iframe_aria_text because compatibility issues.
378
366
  // Not using iframe_attrs because library overwriting.
379
-
380
367
  if (this.iframe) {
381
368
  this.iframe.setAttribute('title', formatMessage('Rich Text Area. Press {OSKey}+F8 for Rich Content Editor shortcuts.', {
382
369
  OSKey: determineOSDependentKey()
383
370
  }));
384
371
  }
385
-
386
372
  this._setupSelectionSaving(editor);
387
-
388
373
  this.checkAccessibility();
389
374
  this.fixToolbarKeyboardNavigation();
390
- (_this$props$onInitted = (_this$props = this.props).onInitted) === null || _this$props$onInitted === void 0 ? void 0 : _this$props$onInitted.call(_this$props, editor); // cleans up highlight artifacts from findreplace plugin
375
+ if (this.props.onInitted) {
376
+ this.props.onInitted(editor);
377
+ }
391
378
 
379
+ // cleans up highlight artifacts from findreplace plugin
392
380
  if (this.getRequiredFeatureStatuses().rce_find_replace) {
393
- editor.on('undo redo', e => {
394
- var _editor$dom, _editor$dom$doc, _editor$dom$doc$getEl, _editor$dom$doc$getEl2;
395
-
396
- if ((editor === null || editor === void 0 ? void 0 : (_editor$dom = editor.dom) === null || _editor$dom === void 0 ? void 0 : (_editor$dom$doc = _editor$dom.doc) === null || _editor$dom$doc === void 0 ? void 0 : (_editor$dom$doc$getEl = _editor$dom$doc.getElementsByClassName) === null || _editor$dom$doc$getEl === void 0 ? void 0 : (_editor$dom$doc$getEl2 = _editor$dom$doc$getEl.call(_editor$dom$doc, 'mce-match-marker')) === null || _editor$dom$doc$getEl2 === void 0 ? void 0 : _editor$dom$doc$getEl2.length) > 0) {
397
- var _editor$plugins, _editor$plugins$searc;
398
-
399
- (_editor$plugins = editor.plugins) === null || _editor$plugins === void 0 ? void 0 : (_editor$plugins$searc = _editor$plugins.searchreplace) === null || _editor$plugins$searc === void 0 ? void 0 : _editor$plugins$searc.done();
381
+ editor.on('undo redo', _e => {
382
+ if (editor?.dom?.doc?.getElementsByClassName?.('mce-match-marker')?.length > 0) {
383
+ editor.plugins?.searchreplace?.done();
400
384
  }
401
385
  });
402
386
  }
403
387
  };
404
-
405
388
  this.fixToolbarKeyboardNavigation = () => {
406
- var _this$_elementRef$cur2;
407
-
408
389
  // The keyboard navigation config in tinymce for the expanded toolbar is incorrectly configured,
409
390
  // and stops at [data-alloy-tabstop] elements.
410
391
  // It should be configured to stop on .tox-toolbar__group elements.
411
392
  // This workaround removes attribute, thusly causing navigation to work correctly again.
412
393
  // For the correct solution, Keying.config should have { selector: '.tox-toolbar__group' }
413
394
  // in https://github.com/tinymce/tinymce/blob/develop/modules/alloy/src/main/ts/ephox/alloy/ui/schema/SplitSlidingToolbarSchema.ts
414
- (_this$_elementRef$cur2 = this._elementRef.current) === null || _this$_elementRef$cur2 === void 0 ? void 0 : _this$_elementRef$cur2.querySelectorAll('.tox-toolbar-overlord button[data-alloy-tabstop]').forEach(it => it.removeAttribute('data-alloy-tabstop'));
395
+ this._elementRef.current?.querySelectorAll('.tox-toolbar-overlord button[data-alloy-tabstop]').forEach(it => it.removeAttribute('data-alloy-tabstop'));
415
396
  };
416
-
417
397
  this._setupSelectionSaving = editor => {
398
+ // @ts-expect-error
418
399
  let savedSelection = null;
419
400
  let selectionWasReset = false;
420
401
  let editorHasFocus = false;
421
-
422
402
  const restoreSelectionIfNecessary = () => {
423
- if (savedSelection && selectionWasReset) {
403
+ // @ts-expect-error
404
+ if (this.editor && savedSelection && selectionWasReset) {
424
405
  this.editor.selection.setRng(savedSelection.range, savedSelection.isForward);
425
406
  selectionWasReset = false;
426
407
  }
427
408
  };
428
-
429
409
  editor.on('blur', () => {
430
410
  editorHasFocus = false;
431
411
  selectionWasReset = false;
412
+ if (!this.editor) return;
432
413
  savedSelection = {
433
414
  range: this.editor.selection.getRng().cloneRange(),
434
415
  isForward: this.editor.selection.isForward()
@@ -444,24 +425,25 @@ class RCEWrapper extends React.Component {
444
425
  selectionWasReset = false;
445
426
  });
446
427
  editor.on('SelectionChange', () => {
447
- var _selection$startConta;
448
-
449
428
  if (editorHasFocus) {
450
429
  // We don't care if a selection reset occurs when the editor has focus, the user probably intended that
451
430
  // At least they will see the effect
452
431
  return;
453
432
  }
433
+ if (!this.editor) return;
434
+ const selection = this.editor.selection.normalize();
454
435
 
455
- const selection = this.editor.selection.normalize(); // Detect a browser-reset selection (e.g. From invoking the Find command)
456
-
457
- if (((_selection$startConta = selection.startContainer) === null || _selection$startConta === void 0 ? void 0 : _selection$startConta.nodeName) === 'BODY' && selection.startContainer === selection.endContainer && selection.startOffset === 0 && selection.endOffset === 0) {
436
+ // Detect a browser-reset selection (e.g. From invoking the Find command)
437
+ if (selection.startContainer?.nodeName === 'BODY' && selection.startContainer === selection.endContainer && selection.startOffset === 0 && selection.endOffset === 0) {
458
438
  selectionWasReset = true;
459
439
  }
460
440
  });
461
441
  editor.on('BeforeExecCommand', () => {
462
442
  restoreSelectionIfNecessary();
463
443
  });
464
- editor.on('ExecCommand', () => {
444
+ editor.on('ExecCommand', (/* event */
445
+ ) => {
446
+ if (!this.editor) return;
465
447
  // Commands may have modified the selection, we need to recapture it
466
448
  savedSelection = {
467
449
  range: this.editor.selection.getRng().cloneRange(),
@@ -469,35 +451,29 @@ class RCEWrapper extends React.Component {
469
451
  };
470
452
  });
471
453
  };
472
-
473
454
  this.announcing = 0;
474
-
475
455
  this.initAutoSave = editor => {
476
456
  var _this$props$userCache;
477
-
478
457
  this.storage = new EncryptedStorage((_this$props$userCache = this.props.userCacheKey) !== null && _this$props$userCache !== void 0 ? _this$props$userCache : '');
479
-
480
458
  if (this.storage) {
481
459
  editor.on('change Undo Redo', this.doAutoSave);
482
460
  editor.on('blur', this.doAutoSave);
483
461
  this.cleanupAutoSave();
484
-
485
462
  try {
486
463
  const autosaved = this.getAutoSaved(this.autoSaveKey);
487
-
488
464
  if (autosaved && autosaved.content) {
489
465
  // We'll compare just the text of the autosave content, since
490
466
  // Canvas is prone to swizzling images and iframes which will
491
467
  // make the editor content and autosave content never match up
492
- const editorContent = this.patchAutosavedContent(editor.getContent({
468
+ const editorContent = patchAutosavedContent(editor.getContent({
493
469
  no_events: true
494
470
  }), true);
495
- const autosavedContent = this.patchAutosavedContent(autosaved.content, true);
496
-
471
+ const autosavedContent = patchAutosavedContent(autosaved.content, true);
497
472
  if (autosavedContent !== editorContent) {
498
473
  this.setState({
499
474
  confirmAutoSave: true,
500
- autoSavedContent: this.patchAutosavedContent(autosaved.content)
475
+ // @ts-expect-error
476
+ autoSavedContent: patchAutosavedContent(autosaved.content)
501
477
  });
502
478
  } else {
503
479
  this.storage.removeItem(this.autoSaveKey);
@@ -505,80 +481,66 @@ class RCEWrapper extends React.Component {
505
481
  }
506
482
  } catch (ex) {
507
483
  // log and ignore
508
- // eslint-disable-next-line no-console
484
+
509
485
  console.error('Failed initializing rce autosave', ex);
510
486
  }
511
487
  }
512
488
  };
513
-
514
- this.cleanupAutoSave = function () {
515
- let deleteAll = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
516
-
517
- if (_this.storage) {
518
- const expiry = deleteAll ? Date.now() : Date.now() - _this.props.autosave.maxAge;
489
+ this.cleanupAutoSave = (deleteAll = false) => {
490
+ if (this.storage) {
491
+ const expiry = deleteAll ? Date.now() : Date.now() - (this.props.autosave?.maxAge || 0);
519
492
  let i = 0;
520
493
  let key;
521
-
522
- while (key = _this.storage.key(i++)) {
494
+ while (key = this.storage.key(i++)) {
523
495
  if (/^rceautosave:/.test(key)) {
524
- const autosaved = _this.getAutoSaved(key);
525
-
496
+ const autosaved = this.getAutoSaved(key);
526
497
  if (autosaved && autosaved.autosaveTimestamp < expiry) {
527
- _this.storage.removeItem(key);
498
+ this.storage.removeItem(key);
528
499
  }
529
500
  }
530
501
  }
531
502
  }
532
503
  };
533
-
534
504
  this.restoreAutoSave = ans => {
535
505
  this.setState({
536
506
  confirmAutoSave: false
537
507
  }, () => {
538
508
  const editor = this.mceInstance();
539
-
540
509
  if (ans) {
541
510
  editor.setContent(this.state.autoSavedContent, {});
542
511
  }
543
-
512
+ // @ts-expect-error
544
513
  this.storage.removeItem(this.autoSaveKey);
545
- }); // let the content be restored
546
-
514
+ });
515
+ // let the content be restored
547
516
  debounce(this.checkAccessibility, 1000)();
548
517
  };
549
-
550
- this.doAutoSave = function (e) {
551
- let retry = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
552
-
553
- if (_this.storage) {
554
- const editor = _this.mceInstance(); // if the editor is empty don't save
555
-
556
-
518
+ this.doAutoSave = (e, retry = false) => {
519
+ if (this.storage) {
520
+ const editor = this.mceInstance();
521
+ // if the editor is empty don't save
557
522
  if (editor.dom.isEmpty(editor.getBody())) {
558
523
  return;
559
524
  }
560
-
561
525
  const content = editor.getContent({
562
526
  no_events: true
563
527
  });
564
-
565
528
  try {
566
- _this.storage.setItem(_this.autoSaveKey, content);
529
+ this.storage.setItem(this.autoSaveKey, content);
567
530
  } catch (ex) {
568
531
  if (!retry) {
569
532
  // probably failed because there's not enough space
570
533
  // delete up all the other entries and try again
571
- _this.cleanupAutoSave(true);
572
-
573
- _this.doAutoSave(e, true);
534
+ this.cleanupAutoSave(true);
535
+ this.doAutoSave(e, true);
574
536
  } else {
575
- console.error('Autosave failed:', ex); // eslint-disable-line no-console
537
+ console.error('Autosave failed:', ex);
576
538
  }
577
539
  }
578
540
  }
579
541
  };
580
-
581
542
  this.onWordCountUpdate = e => {
543
+ if (!this.editor) return;
582
544
  const shouldIgnore = countShouldIgnore(this.editor, 'body', 'words');
583
545
  const updatedCount = e.wordCount.words - shouldIgnore;
584
546
  this.setState(state => {
@@ -589,51 +551,48 @@ class RCEWrapper extends React.Component {
589
551
  } else return null;
590
552
  });
591
553
  };
592
-
593
554
  this.onNodeChange = e => {
594
555
  // This is basically copied out of the tinymce silver theme code for the status bar
595
- const path = e.parents.filter(p => p.nodeName !== 'BR' && !p.getAttribute('data-mce-bogus') && p.getAttribute('data-mce-type') !== 'bookmark').map(p => p.nodeName.toLowerCase()).reverse();
556
+ const path = e.parents.filter(p => p.nodeName !== 'BR' && !p.getAttribute('data-mce-bogus') && p.getAttribute('data-mce-type') !== 'bookmark')
557
+ // @ts-expect-error
558
+ .map(p => p.nodeName.toLowerCase()).reverse();
596
559
  this.setState({
597
560
  path
598
561
  });
599
562
  };
600
-
601
563
  this.onEditorChange = (content, _editor) => {
602
- var _this$props$onContent, _this$props2;
603
-
604
- (_this$props$onContent = (_this$props2 = this.props).onContentChange) === null || _this$props$onContent === void 0 ? void 0 : _this$props$onContent.call(_this$props2, content); // check accessibility when clearing the editor,
564
+ this.props.onContentChange?.(content);
565
+ // check accessibility when clearing the editor,
605
566
  // all other times should be checked by handleInputChange
606
-
607
567
  if (content === '') {
608
568
  this.checkAccessibility();
609
569
  }
610
570
  };
611
-
612
571
  this.onResize = (_e, coordinates) => {
613
572
  const editor = this.mceInstance();
614
-
615
573
  if (editor) {
616
574
  const container = editor.getContainer();
617
575
  if (!container) return;
618
576
  const currentContainerHeight = Number.parseInt(container.style.height, 10);
619
- if (isNaN(currentContainerHeight)) return; // eslint-disable-line no-restricted-globals
620
-
577
+ if (isNaN(currentContainerHeight)) return;
621
578
  const modifiedHeight = currentContainerHeight + coordinates.deltaY;
622
579
  const newHeight = `${modifiedHeight}px`;
623
580
  container.style.height = newHeight;
624
- this.getTextarea().style.height = newHeight;
581
+ const textarea = this.getTextarea();
582
+ if (textarea) {
583
+ textarea.style.height = newHeight;
584
+ }
625
585
  this.setState({
626
586
  height: newHeight
627
- }); // play nice and send the same event that the silver theme would send
628
-
587
+ });
588
+ // play nice and send the same event that the silver theme would send
629
589
  editor.fire('ResizeEditor');
630
590
  }
631
591
  };
632
-
633
592
  this.onA11yChecker = triggerElementId => {
634
593
  const editor = this.mceInstance();
635
594
  editor.execCommand('openAccessibilityChecker', false, {
636
- mountNode: instuiPopupMountNode,
595
+ mountNode: instuiPopupMountNodeFn,
637
596
  triggerElementId,
638
597
  onFixError: errors => {
639
598
  this.setState({
@@ -644,10 +603,10 @@ class RCEWrapper extends React.Component {
644
603
  skip_focus: true
645
604
  });
646
605
  };
647
-
648
606
  this.checkAccessibility = () => {
649
607
  const editor = this.mceInstance();
650
608
  editor.execCommand('checkAccessibility', false, {
609
+ // @ts-expect-error
651
610
  done: errors => {
652
611
  this.setState({
653
612
  a11yErrorsCount: errors.length
@@ -657,86 +616,75 @@ class RCEWrapper extends React.Component {
657
616
  skip_focus: true
658
617
  });
659
618
  };
660
-
661
619
  this.openKBShortcutModal = () => {
662
620
  this.setState({
663
621
  KBShortcutModalOpen: true,
622
+ // @ts-expect-error
664
623
  KBShortcutFocusReturn: document.activeElement
665
624
  });
666
625
  };
667
-
668
626
  this.closeKBShortcutModal = () => {
669
627
  this.setState({
670
628
  KBShortcutModalOpen: false
671
629
  });
672
630
  };
673
-
674
631
  this.KBShortcutModalExited = () => {
675
632
  if (this.state.KBShortcutFocusReturn === this.iframe) {
676
633
  // launched using a kb shortcut
677
634
  // the iframe has focus so we need to forward it on to tinymce editor
678
- this.editor.focus(false);
635
+ if (this.editor) {
636
+ this.editor.focus(false);
637
+ }
679
638
  } else if (this.state.KBShortcutFocusReturn === document.getElementById(`show-on-focus-btn-${this.id}`)) {
680
- var _this$_showOnFocusBut;
681
-
682
639
  // launched from showOnFocus button
683
640
  // edge case where focusing KBShortcutFocusReturn doesn't work
684
- (_this$_showOnFocusBut = this._showOnFocusButton) === null || _this$_showOnFocusBut === void 0 ? void 0 : _this$_showOnFocusBut.focus();
641
+ this._showOnFocusButton?.focus();
685
642
  } else {
686
- var _this$state$KBShortcu;
687
-
688
643
  // launched from kb shortcut button on status bar
689
- (_this$state$KBShortcu = this.state.KBShortcutFocusReturn) === null || _this$state$KBShortcu === void 0 ? void 0 : _this$state$KBShortcu.focus();
644
+ this.state.KBShortcutFocusReturn?.focus();
690
645
  }
691
646
  };
692
-
693
647
  this.handleAIClick = () => {
694
648
  import('./plugins/shared/ai_tools').then(module => {
649
+ // @ts-expect-error
695
650
  this.AIToolsTray = module.AIToolsTray;
696
651
  this.setState({
697
652
  AIToolsOpen: true,
698
653
  AITToolsFocusReturn: document.activeElement
699
654
  });
700
655
  }).catch(ex => {
701
- // eslint-disable-next-line no-console
702
656
  console.error('Failed loading the AIToolsTray', ex);
703
657
  });
704
658
  };
705
-
706
659
  this.closeAITools = () => {
707
660
  this.setState({
708
661
  AIToolsOpen: false
709
662
  });
710
663
  };
711
-
712
664
  this.AIToolsExited = () => {
713
665
  if (this.state.AITToolsFocusReturn === this.iframe) {
714
666
  // launched using a kb shortcut
715
667
  // the iframe has focus so we need to forward it on to tinymce editor
716
- this.editor.focus(false);
668
+ if (this.editor) {
669
+ this.editor.focus(false);
670
+ }
717
671
  } else if (this.state.AITToolsFocusReturn === document.getElementById(`show-on-focus-btn-${this.id}`)) {
718
- var _this$_showOnFocusBut2;
719
-
720
672
  // launched from showOnFocus button
721
673
  // edge case where focusing KBShortcutFocusReturn doesn't work
722
- (_this$_showOnFocusBut2 = this._showOnFocusButton) === null || _this$_showOnFocusBut2 === void 0 ? void 0 : _this$_showOnFocusBut2.focus();
674
+ this._showOnFocusButton?.focus();
723
675
  } else {
724
- var _this$state$AITToolsF;
725
-
726
676
  // launched from kb shortcut button on status bar
727
- (_this$state$AITToolsF = this.state.AITToolsFocusReturn) === null || _this$state$AITToolsF === void 0 ? void 0 : _this$state$AITToolsF.focus();
677
+ // @ts-expect-error
678
+ this.state.AITToolsFocusReturn?.focus();
728
679
  }
729
680
  };
730
-
731
681
  this.handleInsertAIContent = content => {
732
682
  const editor = this.mceInstance();
733
683
  contentInsertion.insertContent(editor, content);
734
684
  };
735
-
736
685
  this.handleReplaceAIContent = content => {
737
686
  const ed = this.mceInstance();
738
687
  const selection = ed.selection;
739
-
740
688
  if (selection.getContent().length > 0) {
741
689
  selection.setContent(content);
742
690
  } else {
@@ -744,7 +692,6 @@ class RCEWrapper extends React.Component {
744
692
  selection.setContent(content);
745
693
  }
746
694
  };
747
-
748
695
  this.getCurrentContentForAI = () => {
749
696
  const selected = this.mceInstance().selection.getContent();
750
697
  return selected ? {
@@ -755,35 +702,30 @@ class RCEWrapper extends React.Component {
755
702
  content: this.mceInstance().getContent()
756
703
  };
757
704
  };
758
-
759
705
  this.setFocusAbilityForHeader = focusable => {
760
706
  // Sets aria-hidden to prevent screen readers focus in RCE menus and toolbar
761
- const header = this._elementRef.current.querySelector('.tox-editor-header');
762
-
707
+ const header = this._elementRef.current?.querySelector('.tox-editor-header');
763
708
  if (header) {
764
709
  header.setAttribute('aria-hidden', focusable ? 'false' : 'true');
765
710
  }
766
711
  };
767
-
768
712
  this.handleTextareaChange = () => {
769
713
  if (this.isHidden()) {
770
714
  this.setCode(this.textareaValue());
715
+ // @ts-expect-error
771
716
  this.doAutoSave();
772
717
  }
773
718
  };
774
-
775
719
  this.addAlert = alert => {
776
720
  alert.id = alertIdValue++;
777
721
  this.setState(state => {
778
722
  let messages = state.messages.concat(alert);
779
723
  messages = _.uniqBy(messages, 'text'); // Don't show the same message twice
780
-
781
724
  return {
782
725
  messages
783
726
  };
784
727
  });
785
728
  };
786
-
787
729
  this.removeAlert = messageId => {
788
730
  this.setState(state => {
789
731
  const messages = state.messages.filter(message => message.id !== messageId);
@@ -792,47 +734,50 @@ class RCEWrapper extends React.Component {
792
734
  };
793
735
  });
794
736
  };
795
-
796
737
  this.resetAlertId = () => {
797
738
  if (this.state.messages.length > 0) {
798
739
  throw new Error('There are messages currently, you cannot reset when they are non-zero');
799
740
  }
800
-
801
741
  alertIdValue = 0;
802
742
  };
743
+ this.style = buildStyle();
803
744
 
804
- this.style = buildStyle(); // Set up some limited global state that can be referenced
745
+ // Set up some limited global state that can be referenced
805
746
  // as needed in RCE's components and function / plugin definitions
806
747
  // Not intended to be dynamically changed!
807
-
808
748
  RCEGlobals.setFeatures(this.getRequiredFeatureStatuses());
809
749
  RCEGlobals.setConfig(this.getRequiredConfigValues());
810
750
  this.editor = null; // my tinymce editor instance
751
+ this.language = normalizeLocale(this.props.language);
811
752
 
812
- this.language = normalizeLocale(this.props.language); // interface consistent with editorBox
813
-
753
+ // interface consistent with editorBox
814
754
  this.get_code = this.getCode;
815
755
  this.set_code = this.setCode;
816
- this.insert_code = this.insertCode; // test override points
756
+ this.insert_code = this.insertCode;
817
757
 
758
+ // test override points
759
+ // @ts-expect-error
818
760
  this.indicator = false;
819
761
  this._elementRef = /*#__PURE__*/React.createRef();
820
762
  this._editorPlaceholderRef = /*#__PURE__*/React.createRef();
763
+ // @ts-expect-error
821
764
  this._prettyHtmlEditorRef = /*#__PURE__*/React.createRef();
822
- this._showOnFocusButton = null; // Process initial content
765
+ // @ts-expect-error
766
+ this._showOnFocusButton = null;
823
767
 
768
+ // Process initial content
769
+ // @ts-expect-error
824
770
  this.initialContent = this.getRequiredFeatureStatuses().rce_transform_loaded_content ? transformRceContentForEditing(this.props.defaultContent, {
825
- origin: this.props.canvasOrigin || ((_window = window) === null || _window === void 0 ? void 0 : (_window$location = _window.location) === null || _window$location === void 0 ? void 0 : _window$location.origin)
771
+ origin: this.props.canvasOrigin || window?.location?.origin
826
772
  }) : this.props.defaultContent;
827
- injectTinySkin(); // FWIW, for historic reaasons, the height does not include the
828
- // height of the status bar (which used to be tinymce's)
829
-
830
- let _ht = ((_props$editorOptions = props.editorOptions) === null || _props$editorOptions === void 0 ? void 0 : _props$editorOptions.height) || DEFAULT_RCE_HEIGHT;
773
+ injectTinySkin();
831
774
 
775
+ // FWIW, for historic reaasons, the height does not include the
776
+ // height of the status bar (which used to be tinymce's)
777
+ let _ht = props.editorOptions?.height || DEFAULT_RCE_HEIGHT;
832
778
  if (!Number.isNaN(_ht)) {
833
779
  _ht = `${_ht}px`;
834
780
  }
835
-
836
781
  const currentRCECount = document.querySelectorAll('.rce-wrapper').length;
837
782
  const maxInitRenderedRCEs = Number.isNaN(props.maxInitRenderedRCEs) ? RCEWrapper.defaultProps.maxInitRenderedRCEs : props.maxInitRenderedRCEs;
838
783
  this.state = {
@@ -845,9 +790,12 @@ class RCEWrapper extends React.Component {
845
790
  announcement: null,
846
791
  confirmAutoSave: false,
847
792
  autoSavedContent: '',
793
+ // @ts-expect-error
848
794
  id: this.props.id || this.props.textareaId || `${uid('rce', 2)}`,
795
+ // @ts-expect-error
849
796
  height: _ht,
850
797
  fullscreenState: {
798
+ // @ts-expect-error
851
799
  prevHeight: _ht
852
800
  },
853
801
  a11yErrorsCount: 0,
@@ -856,45 +804,47 @@ class RCEWrapper extends React.Component {
856
804
  };
857
805
  this._statusBarId = `${this.state.id}_statusbar`;
858
806
  this.pendingEventHandlers = [];
807
+
808
+ // @ts-expect-error
859
809
  this.ltiToolFavorites = externalToolsForToolbar(this.props.ltiTools).map(e => `instructure_external_button_${e.id}`);
860
- this.pluginsToExclude = parsePluginsToExclude(((_props$editorOptions2 = props.editorOptions) === null || _props$editorOptions2 === void 0 ? void 0 : _props$editorOptions2.plugins) || []);
810
+ this.pluginsToExclude = parsePluginsToExclude(props.editorOptions?.plugins || []);
811
+
812
+ // @ts-expect-error
861
813
  this.resourceType = props.resourceType;
814
+ // @ts-expect-error
862
815
  this.resourceId = props.resourceId;
816
+
817
+ // @ts-expect-error
863
818
  this.variant = window.RCE_VARIANT || props.variant; // to facilitate testing
864
819
 
820
+ // @ts-expect-error
865
821
  this.tinymceInitOptions = this.wrapOptions(props.editorOptions);
866
822
  alertHandler.alertFunc = this.addAlert;
867
823
  this.handleContentTrayClosing = this.handleContentTrayClosing.bind(this);
868
- this.resizeObserver = new ResizeObserver(_entries => {
824
+ this.resizeObserver = new ResizeObserver(() => {
869
825
  this._handleFullscreenResize();
870
826
  });
871
827
  this.AIToolsTray = undefined;
872
- } // when the RCE is put into fullscreen we need to move the div
828
+ }
829
+
830
+ // when the RCE is put into fullscreen we need to move the div
873
831
  // tinymce mounts popup menus into from the body to the rce-wrapper
874
832
  // or the menus wind up behind the RCE. I can't find a way to
875
833
  // configure tinymce to say where that div is mounted, do this
876
834
  // is a bit of a hack to tag the div that is this RCE's
877
-
878
-
879
835
  _tagTinymceAuxDiv() {
880
836
  const tinyauxlist = document.querySelectorAll('.tox-tinymce-aux');
881
-
882
837
  if (tinyauxlist.length) {
883
838
  const myaux = tinyauxlist[tinyauxlist.length - 1];
884
-
885
839
  if (myaux.id) {
886
- // eslint-disable-next-line no-console
887
840
  console.error('Unexpected ID on my tox-tinymce-aux element');
888
841
  }
889
-
890
842
  myaux.id = `tinyaux-${this.id}`;
891
843
  }
892
844
  }
893
-
894
845
  _myTinymceAuxDiv() {
895
846
  return document.getElementById(`tinyaux-${this.id}`);
896
847
  }
897
-
898
848
  getRequiredFeatureStatuses() {
899
849
  const {
900
850
  new_math_equation_handling = false,
@@ -915,57 +865,54 @@ class RCEWrapper extends React.Component {
915
865
  consolidated_media_player
916
866
  };
917
867
  }
918
-
919
868
  getRequiredConfigValues() {
920
869
  return {
921
870
  locale: normalizeLocale(this.props.language),
871
+ // @ts-expect-error
922
872
  flashAlertTimeout: this.props.flashAlertTimeout,
873
+ // @ts-expect-error
923
874
  timezone: this.props.timezone
924
875
  };
925
876
  }
926
-
927
877
  getCanvasUrl() {
928
878
  return this.props.canvasOrigin;
929
879
  }
930
-
931
880
  getResourceIdentifiers() {
932
881
  return {
882
+ // @ts-expect-error
933
883
  resourceType: this.resourceType,
884
+ // @ts-expect-error
934
885
  resourceId: this.resourceId
935
886
  };
936
- } // getCode and setCode naming comes from tinyMCE
937
- // kind of strange but want to be consistent
938
-
887
+ }
939
888
 
889
+ // getCode and setCode naming comes from tinyMCE
890
+ // kind of strange but want to be consistent
940
891
  getCode() {
941
892
  return this.isHidden() ? this.textareaValue() : this.mceInstance().getContent();
942
893
  }
943
894
 
895
+ // @ts-expect-error
944
896
  checkReadyToGetCode(promptFunc) {
945
- let status = true; // Check for remaining placeholders
946
-
897
+ let status = true;
898
+ // Check for remaining placeholders
947
899
  if (this.mceInstance().dom.doc.querySelector(`[data-placeholder-for]`)) {
948
900
  status = promptFunc(formatMessage('Content is still being uploaded, if you continue it will not be embedded properly.'));
949
901
  }
950
-
951
902
  return status;
952
903
  }
953
-
954
904
  setCode(newContent) {
955
- var _this$mceInstance;
905
+ this.mceInstance()?.setContent(newContent);
906
+ }
956
907
 
957
- (_this$mceInstance = this.mceInstance()) === null || _this$mceInstance === void 0 ? void 0 : _this$mceInstance.setContent(newContent);
958
- } // This function is called imperatively by the page that renders the RCE.
908
+ // This function is called imperatively by the page that renders the RCE.
959
909
  // It should be called when the RCE content is done being edited.
960
-
961
-
962
910
  RCEClosed() {
963
911
  // We want to clear the autosaved content, since the page was legitimately closed.
964
912
  if (this.storage) {
965
913
  this.storage.removeItem(this.autoSaveKey);
966
914
  }
967
915
  }
968
-
969
916
  indicateEditor(element) {
970
917
  if (document.querySelector('[role="dialog"][data-mce-component]')) {
971
918
  // there is a modal open, which zeros out the vertical scroll
@@ -975,42 +922,46 @@ class RCEWrapper extends React.Component {
975
922
  }, 100);
976
923
  return;
977
924
  }
978
-
979
925
  const editor = this.mceInstance();
980
-
926
+ // @ts-expect-error
981
927
  if (this.indicator) {
928
+ // @ts-expect-error
982
929
  this.indicator(editor, element);
983
930
  } else if (!this.isHidden()) {
984
931
  indicate(indicatorRegion(editor, element));
985
932
  }
986
933
  }
987
-
988
934
  contentInserted(element) {
989
935
  this.indicateEditor(element);
990
936
  this.checkImageLoadError(element);
991
937
  this.sizeEditorForContent(element);
992
- } // make a attempt at sizing the editor so that the new content fits.
938
+ }
939
+
940
+ // make a attempt at sizing the editor so that the new content fits.
993
941
  // works under the assumptions the body's box-sizing is not content-box
994
942
  // and that the content is w/in a <p> whose margin is 12px top and bottom
995
943
  // (which, in canvas, is set in app/stylesheets/components/_ic-typography.scss)
996
-
997
-
998
944
  sizeEditorForContent(elem) {
999
945
  let height;
1000
-
1001
946
  if (elem && elem.nodeType === 1) {
1002
947
  height = elem.clientHeight;
1003
948
  }
1004
-
1005
949
  if (height) {
1006
950
  const ifr = this.iframe;
1007
-
1008
951
  if (ifr) {
1009
- const editor_body_style = ifr.contentWindow.getComputedStyle(this.iframe.contentDocument.body);
1010
- const editor_ht = ifr.contentDocument.body.clientHeight - parseInt(editor_body_style['padding-top'], 10) - parseInt(editor_body_style['padding-bottom'], 10);
952
+ // @ts-expect-error
953
+ const editor_body_style = ifr.contentWindow.getComputedStyle(
954
+ // @ts-expect-error
955
+ this.iframe.contentDocument.body);
956
+ const editor_ht =
957
+ // @ts-expect-error
958
+ ifr.contentDocument.body.clientHeight -
959
+ // @ts-expect-error
960
+ parseInt(editor_body_style['padding-top'], 10) -
961
+ // @ts-expect-error
962
+ parseInt(editor_body_style['padding-bottom'], 10);
1011
963
  const para_margin_ht = 24;
1012
964
  const reserve_ht = Math.ceil(height + para_margin_ht);
1013
-
1014
965
  if (reserve_ht > editor_ht) {
1015
966
  this.onResize(null, {
1016
967
  deltaY: reserve_ht - editor_ht
@@ -1019,90 +970,82 @@ class RCEWrapper extends React.Component {
1019
970
  }
1020
971
  }
1021
972
  }
1022
-
1023
973
  checkImageLoadError(element) {
1024
974
  if (!element || element.tagName !== 'IMG') {
1025
975
  return;
1026
976
  }
1027
-
977
+ // @ts-expect-error
1028
978
  if (!element.complete) {
979
+ // @ts-expect-error
1029
980
  element.onload = () => this.checkImageLoadError(element);
1030
-
1031
981
  return;
1032
- } // checking naturalWidth in a future event loop run prevents a race
982
+ }
983
+ // checking naturalWidth in a future event loop run prevents a race
1033
984
  // condition between the onload callback and naturalWidth being set.
1034
-
1035
-
1036
985
  setTimeout(() => {
986
+ // @ts-expect-error
1037
987
  if (element.naturalWidth === 0) {
988
+ // @ts-expect-error
1038
989
  element.style.border = '1px solid #000';
990
+ // @ts-expect-error
1039
991
  element.style.padding = '2px';
1040
992
  }
1041
993
  }, 0);
1042
994
  }
1043
-
1044
995
  insertCode(code) {
1045
996
  const editor = this.mceInstance();
1046
997
  const element = contentInsertion.insertContent(editor, code);
1047
998
  this.contentInserted(element);
1048
999
  }
1049
-
1050
1000
  replaceCode(code) {
1051
1001
  if (code !== '' && window.confirm(formatMessage('Content in the editor will be changed. Press Cancel to keep the original content.'))) {
1052
1002
  this.mceInstance().setContent(code);
1053
1003
  }
1054
1004
  }
1055
-
1056
1005
  insertEmbedCode(code) {
1057
- const editor = this.mceInstance(); // don't replace selected text, but embed after
1006
+ const editor = this.mceInstance();
1007
+
1008
+ // don't replace selected text, but embed after
1009
+ editor.selection.collapse();
1058
1010
 
1059
- editor.selection.collapse(); // tinymce treats iframes uniquely, and doesn't like adding attributes
1011
+ // tinymce treats iframes uniquely, and doesn't like adding attributes
1060
1012
  // once it's in the editor, and I'd rather not parse the incomming html
1061
1013
  // string with a regex, so let's create a temp copy, then add a title
1062
1014
  // attribute if one doesn't exist. This will let screenreaders announce
1063
1015
  // that there's some embedded content helper
1064
1016
  // From what I've read, "title" is more reliable than "aria-label" for
1065
1017
  // elements like iframes and embeds.
1066
-
1067
1018
  const temp = document.createElement('div');
1068
1019
  temp.innerHTML = code;
1069
1020
  const code_elem = temp.firstElementChild;
1070
-
1071
1021
  if (code_elem) {
1072
1022
  if (!code_elem.hasAttribute('title') && !code_elem.hasAttribute('aria-label')) {
1073
1023
  code_elem.setAttribute('title', formatMessage('embedded content'));
1074
1024
  }
1075
-
1076
1025
  code = code_elem.outerHTML;
1077
- } // inserting an iframe in tinymce (as is often the case with
1026
+ }
1027
+
1028
+ // inserting an iframe in tinymce (as is often the case with
1078
1029
  // embedded content) causes it to wrap it in a span
1079
1030
  // and it's often inserted into a <p> on top of that. Find the
1080
1031
  // iframe and use it to flash the indicator.
1081
-
1082
-
1083
1032
  const element = contentInsertion.insertContent(editor, code);
1084
1033
  const ifr = element && element.querySelector && element.querySelector('iframe');
1085
-
1086
1034
  if (ifr) {
1087
1035
  this.contentInserted(ifr);
1088
1036
  } else {
1089
1037
  this.contentInserted(element);
1090
1038
  }
1091
1039
  }
1092
-
1093
1040
  insertImage(image) {
1094
- var _element$nextSibling, _element$nextSibling$;
1095
-
1096
1041
  const editor = this.mceInstance();
1097
- const element = contentInsertion.insertImage(editor, image, this.getCanvasUrl()); // Removes TinyMCE's caret &nbsp; text if exists.
1042
+ const element = contentInsertion.insertImage(editor, image, this.getCanvasUrl());
1098
1043
 
1099
- if (element !== null && element !== void 0 && (_element$nextSibling = element.nextSibling) !== null && _element$nextSibling !== void 0 && (_element$nextSibling$ = _element$nextSibling.data) !== null && _element$nextSibling$ !== void 0 && _element$nextSibling$.startsWith('\xA0'
1100
- /* nbsp */
1101
- )) {
1044
+ // Removes TinyMCE's caret &nbsp; text if exists.
1045
+ if (element?.nextSibling?.data?.startsWith('\xA0' /* nbsp */)) {
1102
1046
  element.nextSibling.splitText(1);
1103
1047
  element.nextSibling.remove();
1104
1048
  }
1105
-
1106
1049
  return {
1107
1050
  imageElem: element,
1108
1051
  loadingPromise: new Promise((resolve, reject) => {
@@ -1114,7 +1057,6 @@ class RCEWrapper extends React.Component {
1114
1057
  this.contentInserted(element);
1115
1058
  resolve();
1116
1059
  };
1117
-
1118
1060
  element.onerror = e => {
1119
1061
  this.checkImageLoadError(element);
1120
1062
  reject(e);
@@ -1123,330 +1065,320 @@ class RCEWrapper extends React.Component {
1123
1065
  })
1124
1066
  };
1125
1067
  }
1126
-
1127
1068
  insertImagePlaceholder(fileMetaProps) {
1128
1069
  return insertPlaceholder(this.mceInstance(), fileMetaProps.name, placeholderInfoFor(fileMetaProps));
1129
1070
  }
1130
-
1131
1071
  insertVideo(video) {
1132
1072
  const editor = this.mceInstance();
1133
1073
  const element = contentInsertion.insertVideo(editor, video, this.getCanvasUrl());
1134
1074
  this.contentInserted(element);
1135
1075
  }
1136
-
1137
1076
  insertAudio(audio) {
1138
1077
  const editor = this.mceInstance();
1139
1078
  const element = contentInsertion.insertAudio(editor, audio, this.getCanvasUrl());
1140
1079
  this.contentInserted(element);
1141
1080
  }
1142
-
1143
1081
  insertMathEquation(tex) {
1144
1082
  const editor = this.mceInstance();
1145
1083
  contentInsertion.insertEquation(editor, tex);
1146
1084
  }
1147
-
1148
1085
  removePlaceholders(name) {
1149
1086
  removePlaceholder(this.mceInstance(), name);
1150
1087
  }
1151
-
1152
1088
  insertLink(link) {
1153
1089
  const editor = this.mceInstance();
1154
1090
  const element = contentInsertion.insertLink(editor, link, this.getCanvasUrl());
1155
1091
  this.contentInserted(element);
1156
1092
  }
1157
-
1158
1093
  existingContentToLink() {
1159
1094
  const editor = this.mceInstance();
1160
1095
  return contentInsertion.existingContentToLink(editor);
1161
1096
  }
1162
-
1163
1097
  existingContentToLinkIsImg() {
1164
1098
  const editor = this.mceInstance();
1165
1099
  return contentInsertion.existingContentToLinkIsImg(editor);
1166
- } // since we may defer rendering tinymce, queue up any tinymce event handlers
1167
-
1100
+ }
1168
1101
 
1102
+ // since we may defer rendering tinymce, queue up any tinymce event handlers
1103
+ // @ts-expect-error
1169
1104
  tinymceOn(tinymceEventName, handler) {
1170
1105
  if (this.state.shouldShowEditor) {
1171
1106
  this.mceInstance().on(tinymceEventName, handler);
1172
1107
  } else {
1108
+ // @ts-expect-error
1173
1109
  this.pendingEventHandlers.push({
1174
1110
  name: tinymceEventName,
1175
1111
  handler
1176
1112
  });
1177
1113
  }
1178
1114
  }
1179
-
1180
1115
  mceInstance() {
1181
1116
  if (this.editor) {
1182
1117
  return this.editor;
1183
1118
  }
1184
-
1185
1119
  const editors = this.props.tinymce.editors || [];
1186
1120
  return editors.filter(ed => ed.id === this.props.textareaId)[0];
1187
1121
  }
1188
1122
 
1189
- onTinyMCEInstance(command) {
1123
+ // @ts-expect-error
1124
+ onTinyMCEInstance(command, ...args) {
1190
1125
  const editor = this.mceInstance();
1191
-
1192
1126
  if (editor) {
1193
1127
  if (command === 'mceRemoveEditor') {
1194
1128
  editor.execCommand('mceNewDocument');
1195
1129
  } // makes sure content can't persist past removal
1196
-
1197
-
1198
- for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
1199
- args[_key - 1] = arguments[_key];
1200
- }
1201
-
1202
1130
  editor.execCommand(command, false, ...args);
1203
1131
  }
1204
1132
  }
1205
-
1206
1133
  destroy() {
1207
1134
  this._destroyCalled = true;
1208
1135
  this.unhandleTextareaChange();
1209
- this.props.handleUnmount && this.props.handleUnmount();
1136
+ if (this.props.handleUnmount) {
1137
+ this.props.handleUnmount();
1138
+ }
1210
1139
  }
1211
-
1212
1140
  getTextarea() {
1213
- return document.getElementById(this.props.textareaId);
1141
+ const node = this.props.textareaId && document.getElementById(this.props.textareaId);
1142
+ if (node instanceof HTMLTextAreaElement) {
1143
+ return node;
1144
+ }
1145
+ return null;
1214
1146
  }
1215
-
1216
1147
  textareaValue() {
1217
- return this.getTextarea().value;
1148
+ return this.getTextarea()?.value || '';
1218
1149
  }
1219
-
1220
1150
  get id() {
1221
1151
  return this.state.id;
1222
1152
  }
1223
-
1224
1153
  getHtmlEditorStorage() {
1225
- var _this$storage2, _this$storage2$getIte, _this$storage2$getIte2;
1226
-
1227
1154
  const cookieValue = getCookie('rce.htmleditor');
1228
-
1229
1155
  if (cookieValue) {
1230
1156
  document.cookie = `rce.htmleditor=${cookieValue};path=/;max-age=0`;
1231
1157
  }
1232
-
1233
- const value = cookieValue || ((_this$storage2 = this.storage) === null || _this$storage2 === void 0 ? void 0 : (_this$storage2$getIte = _this$storage2.getItem) === null || _this$storage2$getIte === void 0 ? void 0 : (_this$storage2$getIte2 = _this$storage2$getIte.call(_this$storage2, 'rce.htmleditor')) === null || _this$storage2$getIte2 === void 0 ? void 0 : _this$storage2$getIte2.content);
1158
+ const value = cookieValue || this.storage?.getItem?.('rce.htmleditor')?.content;
1234
1159
  return value === RAW_HTML_EDITOR_VIEW || value === PRETTY_HTML_EDITOR_VIEW ? value : PRETTY_HTML_EDITOR_VIEW;
1235
1160
  }
1236
-
1237
1161
  _isFullscreen() {
1238
1162
  return !!(this.state.fullscreenState.isTinyFullscreen || document[FS_ELEMENT]);
1239
1163
  }
1240
-
1241
1164
  _enterFullscreen() {
1242
1165
  // tinymce mounts its menus and toolbars in this element, which is in the DOM
1243
1166
  // at the bottom of the body. When we're fullscreen the menus need to be mounted
1244
1167
  // in the fullscreen element or they won't show up. Let's move tinymce's mount point
1245
1168
  // when we go into fullscreen, then put it back when we're finished.
1246
1169
  const tinymenuhost = this._myTinymceAuxDiv();
1247
-
1248
1170
  if (tinymenuhost) {
1249
1171
  tinymenuhost.remove();
1250
-
1251
- this._elementRef.current.appendChild(tinymenuhost);
1172
+ this._elementRef.current?.appendChild(tinymenuhost);
1252
1173
  }
1253
-
1254
- this._elementRef.current.addEventListener(FS_CHANGEEVENT, this._onFullscreenChange);
1255
-
1256
- this.setState({
1257
- fullscreenState: {
1258
- prevHeight: this._elementRef.current.offsetHeight - this._getStatusBarHeight()
1259
- }
1260
- });
1261
-
1174
+ this._elementRef.current?.addEventListener(FS_CHANGEEVENT, this._onFullscreenChange);
1175
+ if (typeof this._elementRef.current?.offsetHeight === 'number') {
1176
+ this.setState({
1177
+ fullscreenState: {
1178
+ prevHeight: this._elementRef.current.offsetHeight - this._getStatusBarHeight()
1179
+ }
1180
+ });
1181
+ }
1182
+ // @ts-expect-error
1262
1183
  this._elementRef.current[FS_REQUEST]();
1263
1184
  }
1264
-
1265
1185
  _exitFullscreen() {
1266
1186
  if (document[FS_ELEMENT]) {
1267
1187
  const tinymenuhost = this._myTinymceAuxDiv();
1268
-
1269
1188
  if (tinymenuhost) {
1270
1189
  tinymenuhost.remove();
1271
1190
  document.body.appendChild(tinymenuhost);
1272
1191
  }
1273
-
1274
1192
  document[FS_EXIT]();
1275
1193
  }
1276
1194
  }
1277
1195
 
1196
+ // @ts-expect-error
1197
+
1278
1198
  _getStatusBarHeight() {
1279
1199
  // the height prop is the height of the editor and does not include
1280
1200
  // the status bar. we'll need this later.
1281
- return document.getElementById(this._statusBarId).offsetHeight;
1201
+ const node = document.getElementById(this._statusBarId);
1202
+ return node?.offsetHeight || 0;
1282
1203
  }
1283
-
1284
1204
  _setHeight(newHeight) {
1285
1205
  const cssHeight = `${newHeight}px`;
1286
1206
  const ed = this.mceInstance();
1287
1207
  const container = ed.getContainer();
1288
-
1289
1208
  if (container) {
1290
1209
  container.style.height = cssHeight;
1291
1210
  ed.fire('ResizeEditor');
1292
1211
  }
1293
-
1294
- this.getTextarea().style.height = cssHeight;
1212
+ const textarea = this.getTextarea();
1213
+ if (textarea) {
1214
+ textarea.style.height = cssHeight;
1215
+ }
1295
1216
  this.setState({
1296
1217
  height: cssHeight
1297
1218
  });
1298
1219
  }
1299
-
1300
1220
  focus() {
1301
- this.onTinyMCEInstance('mceFocus'); // tinymce doesn't always call the focus handler.
1302
-
1221
+ this.onTinyMCEInstance('mceFocus');
1222
+ // tinymce doesn't always call the focus handler.
1223
+ // @ts-expect-error
1303
1224
  this.handleFocusEditor(new Event('focus', {
1304
1225
  target: this.mceInstance()
1305
1226
  }));
1306
1227
  }
1307
-
1308
1228
  focusCurrentView() {
1309
1229
  switch (this.state.editorView) {
1310
1230
  case WYSIWYG_VIEW:
1311
- this.mceInstance().focus();
1312
- break;
1313
-
1231
+ {
1232
+ this.mceInstance().focus();
1233
+ break;
1234
+ }
1314
1235
  case PRETTY_HTML_EDITOR_VIEW:
1315
- break;
1316
-
1236
+ {
1237
+ break;
1238
+ }
1317
1239
  case RAW_HTML_EDITOR_VIEW:
1318
- this.getTextarea().focus();
1319
- break;
1240
+ {
1241
+ const textarea = this.getTextarea();
1242
+ if (textarea) {
1243
+ textarea.focus();
1244
+ }
1245
+ break;
1246
+ }
1320
1247
  }
1321
1248
  }
1322
-
1323
1249
  is_dirty() {
1324
- var _this$mceInstance2;
1325
-
1326
1250
  if (this.mceInstance().isDirty()) {
1327
1251
  return true;
1328
1252
  }
1329
-
1330
- const currentHtml = this.isHidden() ? this.textareaValue() : (_this$mceInstance2 = this.mceInstance()) === null || _this$mceInstance2 === void 0 ? void 0 : _this$mceInstance2.getContent();
1253
+ const currentHtml = this.isHidden() ? this.textareaValue() : this.mceInstance()?.getContent();
1331
1254
  return currentHtml !== this._mceSerializedInitialHtml;
1332
1255
  }
1256
+
1333
1257
  /**
1334
1258
  * Holds a copy of the initial content of the editor as serialized by tinyMCE to normalize it.
1335
1259
  */
1336
-
1337
-
1338
1260
  get _mceSerializedInitialHtml() {
1339
1261
  if (!this._mceSerializedInitialHtmlCached) {
1340
1262
  const el = window.document.createElement('div');
1263
+ // @ts-expect-error
1341
1264
  el.innerHTML = this.initialContent;
1342
1265
  const serializer = this.mceInstance().serializer;
1343
1266
  this._mceSerializedInitialHtmlCached = serializer.serialize(el, {
1344
1267
  getInner: true
1345
1268
  });
1346
1269
  }
1347
-
1348
1270
  return this._mceSerializedInitialHtmlCached;
1349
1271
  }
1350
-
1351
1272
  isHtmlView() {
1352
1273
  return this.state.editorView !== WYSIWYG_VIEW;
1353
1274
  }
1354
-
1355
1275
  isHidden() {
1356
1276
  return this.mceInstance().isHidden();
1357
1277
  }
1358
-
1359
1278
  get iframe() {
1360
1279
  return document.getElementById(`${this.props.textareaId}_ifr`);
1361
- } // these focus and blur event handlers work together so that RCEWrapper
1362
- // can report focus and blur events from the RCE at-large
1363
-
1280
+ }
1364
1281
 
1282
+ // these focus and blur event handlers work together so that RCEWrapper
1283
+ // can report focus and blur events from the RCE at-large
1365
1284
  get focused() {
1366
1285
  return this === bridge.getEditor();
1367
1286
  }
1368
-
1369
- handleFocus(_event) {
1287
+ handleFocus() {
1370
1288
  if (!this.focused) {
1371
1289
  bridge.focusEditor(this);
1372
- this.props.onFocus && this.props.onFocus(this);
1290
+ if (this.props.onFocus) {
1291
+ this.props.onFocus(this);
1292
+ }
1373
1293
  }
1374
1294
  }
1375
-
1376
1295
  handleContentTrayClosing(isClosing) {
1377
1296
  this.contentTrayClosing = isClosing;
1378
1297
  }
1379
-
1380
1298
  handleBlur(event) {
1381
1299
  if (this.blurTimer) return;
1382
-
1383
1300
  if (this.focused) {
1384
1301
  // because the old active element fires blur before the next element gets focus
1385
1302
  // we often need a moment to see if focus comes back
1303
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
1386
1304
  event && event.persist && event.persist();
1387
1305
  this.blurTimer = window.setTimeout(() => {
1388
- var _this$_elementRef$cur3, _event$focusedEditor, _event$relatedTarget, _event$relatedTarget$;
1389
-
1390
1306
  this.blurTimer = 0;
1391
-
1392
1307
  if (this.contentTrayClosing) {
1393
1308
  // the CanvasContentTray is in the process of closing
1394
1309
  // wait until it finishes
1395
1310
  return;
1396
1311
  }
1397
-
1398
- if ((_this$_elementRef$cur3 = this._elementRef.current) !== null && _this$_elementRef$cur3 !== void 0 && _this$_elementRef$cur3.contains(document.activeElement)) {
1312
+ if (this._elementRef.current?.contains(document.activeElement)) {
1399
1313
  // focus is still somewhere w/in me
1400
1314
  return;
1401
1315
  }
1402
-
1403
1316
  const activeClass = document.activeElement && document.activeElement.getAttribute('class');
1404
-
1405
- if ((event.focusedEditor === undefined || event.target.id === ((_event$focusedEditor = event.focusedEditor) === null || _event$focusedEditor === void 0 ? void 0 : _event$focusedEditor.id)) && activeClass !== null && activeClass !== void 0 && activeClass.includes('tox-')) {
1317
+ if (
1318
+ // @ts-expect-error
1319
+ (event.focusedEditor === undefined || event.target.id === event.focusedEditor?.id) && activeClass?.includes('tox-')) {
1406
1320
  // if a toolbar button has focus, then the user clicks on the "more" button
1407
1321
  // focus jumps to the body, then eventually to the popped up toolbar. This
1408
1322
  // catches that case.
1409
1323
  return;
1410
1324
  }
1411
-
1412
- if (event !== null && event !== void 0 && (_event$relatedTarget = event.relatedTarget) !== null && _event$relatedTarget !== void 0 && (_event$relatedTarget$ = _event$relatedTarget.getAttribute('class')) !== null && _event$relatedTarget$ !== void 0 && _event$relatedTarget$.includes('tox-')) {
1325
+ if (event?.relatedTarget?.getAttribute('class')?.includes('tox-')) {
1413
1326
  // a tinymce popup has focus
1414
1327
  return;
1415
1328
  }
1416
-
1417
1329
  const popups = document.querySelectorAll('[data-mce-component]');
1418
-
1419
1330
  for (const popup of popups) {
1420
1331
  if (popup.contains(document.activeElement)) {
1421
1332
  // one of our popups has focus
1422
1333
  return;
1423
1334
  }
1424
1335
  }
1425
-
1426
1336
  bridge.blurEditor(this);
1427
- this.props.onBlur && this.props.onBlur(event);
1337
+ if (this.props.onBlur) {
1338
+ this.props.onBlur(event);
1339
+ }
1428
1340
  }, ASYNC_FOCUS_TIMEOUT);
1429
1341
  }
1430
1342
  }
1431
1343
 
1432
- call(methodName) {
1344
+ // @ts-expect-error
1345
+
1346
+ // @ts-expect-error
1347
+ call(methodName, ...args) {
1433
1348
  // since exists? has a ? and cant be a regular function just return true
1434
1349
  // rather than calling as a fn on the editor
1435
1350
  if (methodName === 'exists?') {
1436
1351
  return true;
1437
1352
  }
1438
-
1439
- for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
1440
- args[_key2 - 1] = arguments[_key2];
1441
- }
1442
-
1353
+ // @ts-expect-error
1443
1354
  return this[methodName](...args);
1444
1355
  }
1445
1356
 
1357
+ /**
1358
+ * Fix keyboard navigation in the expanded toolbar
1359
+ *
1360
+ * NOTE: This is a workaround for https://github.com/tinymce/tinymce/issues/8618
1361
+ * and should be removed once that issue is resolved and the tinymce dependency is updated to include it.
1362
+ */
1363
+
1364
+ /**
1365
+ * Sets up selection saving and restoration logic.
1366
+ *
1367
+ * There are certain actions a user can take when the RCE is not focused that clear the selection inside the
1368
+ * editor, such as invoking the Find feature of the browser. If the user then tries to insert content without
1369
+ * going back to the editor, the content would be inserted at the top of the RCE, instead of where their cursor
1370
+ * was.
1371
+ *
1372
+ * This method adds logic that saves and restores the selection to work around the issue.
1373
+ *
1374
+ * @private
1375
+ */
1376
+ // @ts-expect-error
1377
+
1446
1378
  announceContextToolbars(editor) {
1447
1379
  editor.on('NodeChange', () => {
1448
1380
  const node = editor.selection.getNode();
1449
-
1381
+ // @ts-expect-error
1450
1382
  if (isImageEmbed(node, editor)) {
1451
1383
  if (this.announcing !== 1) {
1452
1384
  this.setState({
@@ -1465,7 +1397,7 @@ class RCEWrapper extends React.Component {
1465
1397
  });
1466
1398
  this.announcing = 2;
1467
1399
  }
1468
- } else if (isElementWithinTable(node, editor)) {
1400
+ } else if (isElementWithinTable(node)) {
1469
1401
  if (this.announcing !== 3) {
1470
1402
  this.setState({
1471
1403
  announcement: formatMessage('type Control F9 to access table options. {text}', {
@@ -1482,121 +1414,109 @@ class RCEWrapper extends React.Component {
1482
1414
  }
1483
1415
  });
1484
1416
  }
1417
+
1485
1418
  /* ********** autosave support *************** */
1486
1419
 
1420
+ // remove any autosaved value that's too old
1487
1421
 
1488
- // if a placeholder image shows up in autosaved content, we have to remove it
1489
- // because the data url gets converted to a blob, which is not valid when restored.
1490
- // besides, the placeholder is intended to be temporary while the file
1491
- // is being uploaded
1492
- patchAutosavedContent(content, asText) {
1493
- const temp = document.createElement('div');
1494
- temp.innerHTML = content;
1495
- temp.querySelectorAll('[data-placeholder-for]').forEach(placeholder => {
1496
- placeholder.parentElement.removeChild(placeholder);
1497
- });
1498
- if (asText) return temp.textContent;
1499
- return temp.innerHTML;
1500
- }
1422
+ // @ts-expect-error
1501
1423
 
1502
1424
  getAutoSaved(key) {
1503
1425
  let autosaved = null;
1504
-
1505
1426
  try {
1506
1427
  autosaved = this.storage && this.storage.getItem(key);
1507
1428
  } catch (_ex) {
1429
+ // @ts-expect-error
1508
1430
  this.storage.removeItem(this.autoSaveKey);
1509
1431
  }
1510
-
1511
1432
  return autosaved;
1512
- } // only autosave if the feature flag is set, and there is only 1 RCE on the page
1433
+ }
1434
+
1435
+ // only autosave if the feature flag is set, and there is only 1 RCE on the page
1513
1436
  // the latter condition is necessary because the popup RestoreAutoSaveModal
1514
1437
  // is lousey UX when there are >1
1515
-
1516
-
1517
1438
  get isAutoSaving() {
1439
+ if (!this.editor) return false;
1440
+
1518
1441
  // If the editor is invisible for some reason, don't show the autosave modal
1519
1442
  // This doesn't apply if the editor is off-screen or has visibility:hidden;
1520
1443
  // only if it isn't rendered or has display:none;
1521
1444
  const editorVisible = this.editor.getContainer().offsetParent;
1522
- return this.props.autosave.enabled && editorVisible && document.querySelectorAll('.rce-wrapper').length === 1 && storageAvailable();
1445
+ return this.props.autosave?.enabled && editorVisible && document.querySelectorAll('.rce-wrapper').length === 1 && storageAvailable();
1523
1446
  }
1524
-
1525
1447
  get autoSaveKey() {
1526
- var _this$props$trayProps;
1527
-
1528
- const userId = (_this$props$trayProps = this.props.trayProps) === null || _this$props$trayProps === void 0 ? void 0 : _this$props$trayProps.containingContext.userId;
1448
+ // @ts-expect-error
1449
+ const userId = this.props.trayProps?.containingContext.userId;
1529
1450
  return `rceautosave:${userId}${window.location.href}:${this.props.textareaId}`;
1530
1451
  }
1531
1452
 
1453
+ // @ts-expect-error
1454
+
1455
+ /* *********** end autosave support *************** */
1456
+
1457
+ // @ts-expect-error
1458
+
1532
1459
  componentWillUnmount() {
1533
1460
  if (this.state.shouldShowEditor) {
1534
- var _this$mutationObserve, _this$intersectionObs;
1535
-
1536
1461
  window.clearTimeout(this.blurTimer);
1537
-
1538
1462
  if (!this._destroyCalled) {
1539
1463
  this.destroy();
1540
1464
  }
1541
-
1542
- this._elementRef.current.removeEventListener('keydown', this.handleKey, true);
1543
-
1544
- (_this$mutationObserve = this.mutationObserver) === null || _this$mutationObserve === void 0 ? void 0 : _this$mutationObserve.disconnect();
1545
- (_this$intersectionObs = this.intersectionObserver) === null || _this$intersectionObs === void 0 ? void 0 : _this$intersectionObs.disconnect();
1465
+ if (this._elementRef.current) {
1466
+ this._elementRef.current.removeEventListener('keydown', this.handleKey, true);
1467
+ }
1468
+ this.mutationObserver?.disconnect();
1469
+ this.intersectionObserver?.disconnect();
1546
1470
  }
1547
1471
  }
1548
-
1549
- wrapOptions() {
1550
- var _this$props$trayProps2, _this$props$trayProps3, _this$props$trayProps4, _sanitizePlugins;
1551
-
1552
- let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1553
- const rcsExists = !!((_this$props$trayProps2 = this.props.trayProps) !== null && _this$props$trayProps2 !== void 0 && _this$props$trayProps2.host && (_this$props$trayProps3 = this.props.trayProps) !== null && _this$props$trayProps3 !== void 0 && _this$props$trayProps3.jwt);
1472
+ wrapOptions(options = {}) {
1473
+ const rcsExists = !!(this.props.trayProps?.host && this.props.trayProps?.jwt);
1554
1474
  const userLocale = editorLanguage(this.language);
1475
+
1476
+ // @ts-expect-error
1555
1477
  const setupCallback = options.setup;
1556
1478
  const canvasPlugins = rcsExists ? ['instructure_image', 'instructure_documents', 'instructure_equation'] : [];
1557
-
1558
1479
  if (rcsExists && !this.props.instRecordDisabled) {
1559
1480
  canvasPlugins.splice(2, 0, 'instructure_record');
1560
1481
  }
1561
-
1562
1482
  const pastePlugins = rcsExists ? ['instructure_paste', 'paste'] : ['paste'];
1563
-
1564
- if (rcsExists && this.props.use_rce_icon_maker && ((_this$props$trayProps4 = this.props.trayProps) === null || _this$props$trayProps4 === void 0 ? void 0 : _this$props$trayProps4.contextType) === 'course') {
1483
+ if (rcsExists && this.props.use_rce_icon_maker && this.props.trayProps?.contextType === 'course') {
1565
1484
  canvasPlugins.push('instructure_icon_maker');
1566
1485
  }
1567
-
1568
1486
  if (document[FS_ENABLED]) {
1569
1487
  canvasPlugins.push('instructure_fullscreen');
1570
1488
  }
1571
-
1572
1489
  if (this.getRequiredFeatureStatuses().rce_find_replace) {
1573
1490
  canvasPlugins.push('searchreplace');
1574
1491
  canvasPlugins.push('instructure_search_and_replace');
1575
1492
  }
1576
-
1577
1493
  const possibleNewMenubarItems = this.props.editorOptions.menu ? Object.keys(this.props.editorOptions.menu).join(' ') : undefined;
1578
- const wrappedOpts = { ...defaultTinymceConfig,
1494
+ const wrappedOpts = {
1495
+ ...defaultTinymceConfig,
1579
1496
  ...options,
1580
1497
  readonly: this.props.readOnly,
1581
1498
  theme: 'silver',
1582
1499
  // some older code specified 'modern', which doesn't exist any more
1500
+
1501
+ // @ts-expect-error
1583
1502
  height: options.height || DEFAULT_RCE_HEIGHT,
1584
1503
  language: userLocale,
1585
1504
  document_base_url: this.props.canvasOrigin,
1586
- block_formats: options.block_formats || [`${formatMessage('Heading 2')}=h2`, `${formatMessage('Heading 3')}=h3`, `${formatMessage('Heading 4')}=h4`, `${formatMessage('Preformatted')}=pre`, `${formatMessage('Paragraph')}=p`].join('; '),
1505
+ block_formats:
1506
+ // @ts-expect-error
1507
+ options.block_formats || [`${formatMessage('Heading 2')}=h2`, `${formatMessage('Heading 3')}=h3`, `${formatMessage('Heading 4')}=h4`, `${formatMessage('Preformatted')}=pre`, `${formatMessage('Paragraph')}=p`].join('; '),
1587
1508
  setup: editor => {
1588
- var _bridge$trayProps;
1589
-
1590
1509
  addKebabIcon(editor);
1591
1510
  editorWrappers.set(editor, this);
1592
1511
  const trayPropsWithColor = {
1512
+ // @ts-expect-error
1593
1513
  brandColor: this.style.theme.canvasBrandColor,
1594
1514
  ...this.props.trayProps
1595
1515
  };
1596
- (_bridge$trayProps = bridge.trayProps) === null || _bridge$trayProps === void 0 ? void 0 : _bridge$trayProps.set(editor, trayPropsWithColor);
1516
+ bridge.trayProps?.set(editor, trayPropsWithColor);
1517
+ // @ts-expect-error
1597
1518
  bridge.userLocale = userLocale;
1598
1519
  bridge.canvasOrigin = this.props.canvasOrigin;
1599
-
1600
1520
  if (typeof setupCallback === 'function') {
1601
1521
  setupCallback(editor);
1602
1522
  }
@@ -1605,8 +1525,10 @@ class RCEWrapper extends React.Component {
1605
1525
  // in the editor matches the styles of the app it will be displayed in when saved.
1606
1526
  // This is just so we inject the helper class names that tinyMCE uses for
1607
1527
  // things like table resizing and stuff.
1528
+ // @ts-expect-error
1608
1529
  content_css: options.content_css || [],
1609
- content_style: contentCSS,
1530
+ // @ts-expect-error
1531
+ content_style: contentCSS + (options.content_style || ''),
1610
1532
  menubar: mergeMenuItems(getMenubarForVariant(this.variant), possibleNewMenubarItems),
1611
1533
  // default menu options listed at https://www.tiny.cloud/docs/configure/editor-appearance/#menu
1612
1534
  // tinymce's default edit and table menus are fine
@@ -1614,10 +1536,16 @@ class RCEWrapper extends React.Component {
1614
1536
  // since we currently can't effectively paste using the clipboard api anyway.
1615
1537
  // we include all the canvas specific items in the menu and toolbar
1616
1538
  // and rely on tinymce only showing them if the plugin is provided.
1539
+ // @ts-expect-error
1617
1540
  menu: mergeMenu(getMenuForVariant(this.variant), options.menu),
1618
- toolbar: mergeToolbar(getToolbarForVariant(this.variant, this.ltiToolFavorites), options.toolbar),
1541
+ toolbar: mergeToolbar(
1542
+ // @ts-expect-error
1543
+ getToolbarForVariant(this.variant, this.ltiToolFavorites),
1544
+ // @ts-expect-error
1545
+ options.toolbar),
1619
1546
  contextmenu: '',
1620
1547
  // show the browser's native context menu
1548
+
1621
1549
  toolbar_mode: 'sliding',
1622
1550
  toolbar_sticky: true,
1623
1551
  // In regards to the ability to disable plugins:
@@ -1627,8 +1555,10 @@ class RCEWrapper extends React.Component {
1627
1555
  // handles all of that complexity. It that ever changes in the
1628
1556
  // future in an upgraded version, we will have to update the
1629
1557
  // logic in those other places as well.
1630
- plugins: mergePlugins(['autolink', 'media', 'table', 'link', 'directionality', 'lists', 'textpattern', 'hr', 'instructure-ui-icons', 'instructure_condensed_buttons', 'instructure_links', 'instructure_html_view', 'instructure_media_embed', 'a11y_checker', 'wordcount', 'instructure_wordcount', 'instructure_studio_media_options', 'instructure_rce_external_tools', ...pastePlugins, ...canvasPlugins], // filter out the plugins designated for removal
1631
- (_sanitizePlugins = sanitizePlugins(options.plugins)) === null || _sanitizePlugins === void 0 ? void 0 : _sanitizePlugins.filter(p => p.length > 0 && p[0] !== '-'), this.pluginsToExclude),
1558
+ plugins: mergePlugins(['autolink', 'media', 'table', 'link', 'directionality', 'lists', 'textpattern', 'hr', 'instructure_color', 'instructure-ui-icons', 'instructure_condensed_buttons', 'instructure_links', 'instructure_html_view', 'instructure_media_embed', 'a11y_checker', 'wordcount', 'instructure_wordcount', 'instructure_studio_media_options', 'instructure_rce_external_tools', ...pastePlugins, ...canvasPlugins],
1559
+ // filter out the plugins designated for removal
1560
+ // @ts-expect-error
1561
+ sanitizePlugins(options.plugins)?.filter(p => p.length > 0 && p[0] !== '-'), this.pluginsToExclude),
1632
1562
  textpattern_patterns: [{
1633
1563
  start: '* ',
1634
1564
  cmd: 'InsertUnorderedList'
@@ -1637,128 +1567,120 @@ class RCEWrapper extends React.Component {
1637
1567
  cmd: 'InsertUnorderedList'
1638
1568
  }]
1639
1569
  };
1640
-
1641
1570
  if (this.props.trayProps) {
1571
+ // @ts-expect-error
1642
1572
  wrappedOpts.canvas_rce_user_context = {
1643
1573
  type: this.props.trayProps.contextType,
1644
1574
  id: this.props.trayProps.contextId
1645
1575
  };
1576
+
1577
+ // @ts-expect-error
1646
1578
  wrappedOpts.canvas_rce_containing_context = {
1579
+ // @ts-expect-error
1647
1580
  type: this.props.trayProps.containingContext.contextType,
1581
+ // @ts-expect-error
1648
1582
  id: this.props.trayProps.containingContext.contextId
1649
1583
  };
1650
1584
  }
1651
-
1652
1585
  return wrappedOpts;
1653
1586
  }
1654
-
1655
1587
  unhandleTextareaChange() {
1656
1588
  if (this._textareaEl) {
1657
1589
  this._textareaEl.removeEventListener('input', this.handleTextareaChange);
1658
1590
  }
1659
1591
  }
1660
-
1661
1592
  registerTextareaChange() {
1662
1593
  const el = this.getTextarea();
1663
-
1664
1594
  if (this._textareaEl !== el) {
1665
1595
  this.unhandleTextareaChange();
1666
-
1667
1596
  if (el) {
1668
1597
  el.addEventListener('input', this.handleTextareaChange);
1669
-
1670
1598
  if (this.props.textareaClassName) {
1671
1599
  // split the string on whitespace because classList doesn't let you add multiple
1672
1600
  // space seperated classes at a time but does let you add an array of them
1673
1601
  el.classList.add(...this.props.textareaClassName.split(/\s+/));
1674
1602
  }
1675
-
1676
1603
  this._textareaEl = el;
1677
1604
  }
1678
1605
  }
1679
1606
  }
1680
-
1681
1607
  componentDidMount() {
1682
1608
  if (this.state.shouldShowEditor) {
1683
1609
  this.editorReallyDidMount();
1684
1610
  } else {
1685
1611
  this.intersectionObserver = new IntersectionObserver(entries => {
1686
1612
  const entry = entries[0];
1687
-
1688
1613
  if (entry.isIntersecting || entry.intersectionRatio > 0) {
1689
1614
  this.setState({
1690
1615
  shouldShowEditor: true
1691
1616
  });
1692
1617
  }
1693
- }, // initialize the RCE when it gets close to entering the viewport
1618
+ },
1619
+ // initialize the RCE when it gets close to entering the viewport
1694
1620
  {
1695
1621
  root: null,
1696
1622
  rootMargin: '200px 0px',
1697
1623
  threshold: 0.0
1698
1624
  });
1625
+ // @ts-expect-error
1699
1626
  this.intersectionObserver.observe(this._editorPlaceholderRef.current);
1700
1627
  }
1701
1628
  }
1702
-
1703
1629
  componentDidUpdate(prevProps, prevState) {
1704
1630
  if (this.state.shouldShowEditor) {
1705
1631
  if (!prevState.shouldShowEditor) {
1706
- var _this$intersectionObs2;
1707
-
1708
1632
  this.editorReallyDidMount();
1709
- (_this$intersectionObs2 = this.intersectionObserver) === null || _this$intersectionObs2 === void 0 ? void 0 : _this$intersectionObs2.disconnect();
1633
+ this.intersectionObserver?.disconnect();
1710
1634
  } else {
1711
1635
  this.registerTextareaChange();
1712
-
1713
1636
  if (prevState.editorView !== this.state.editorView) {
1714
1637
  this.setEditorView(this.state.editorView);
1715
1638
  this.focusCurrentView();
1716
1639
  }
1717
-
1718
1640
  if (prevProps.readOnly !== this.props.readOnly) {
1719
1641
  this.mceInstance().mode.set(this.props.readOnly ? 'readonly' : 'design');
1720
1642
  }
1721
1643
  }
1722
1644
  }
1723
1645
  }
1724
-
1725
1646
  editorReallyDidMount() {
1726
1647
  const myTiny = this.mceInstance();
1727
1648
  this.pendingEventHandlers.forEach(e => {
1649
+ // @ts-expect-error
1728
1650
  myTiny.on(e.name, e.handler);
1729
1651
  });
1730
-
1731
1652
  this._tagTinymceAuxDiv();
1732
-
1733
1653
  this.registerTextareaChange();
1734
-
1735
- this._elementRef.current.addEventListener('keydown', this.handleKey, true); // give the textarea its initial size
1736
-
1737
-
1654
+ // @ts-expect-error
1655
+ this._elementRef.current.addEventListener('keydown', this.handleKey, true);
1656
+ // give the textarea its initial size
1738
1657
  this.onResize(null, {
1739
1658
  deltaY: 0
1740
- }); // Preload the LTI Tools modal
1659
+ });
1660
+ // Preload the LTI Tools modal
1741
1661
  // This helps with loading the favorited external tools
1742
-
1743
1662
  if (this.ltiToolFavorites.length > 0) {
1744
1663
  import('./plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionDialog');
1745
1664
  }
1746
-
1747
1665
  bridge.renderEditor(this);
1748
1666
  }
1749
1667
 
1668
+ // @ts-expect-error
1750
1669
  setEditorView(view) {
1751
1670
  switch (view) {
1752
1671
  case WYSIWYG_VIEW:
1753
1672
  this.setCode(this.textareaValue());
1754
1673
  this.mceInstance().show();
1755
1674
  break;
1756
-
1757
1675
  default:
1758
1676
  this.mceInstance().hide();
1759
1677
  }
1760
1678
  }
1761
1679
 
1680
+ /**
1681
+ * Used for reseting the value during tests
1682
+ */
1683
+
1762
1684
  renderHtmlEditor() {
1763
1685
  // the div keeps the editor from collapsing while the code editor is downloaded
1764
1686
  return /*#__PURE__*/React.createElement(Suspense, {
@@ -1777,27 +1699,29 @@ class RCEWrapper extends React.Component {
1777
1699
  as: "div",
1778
1700
  borderRadius: "medium",
1779
1701
  borderWidth: "small"
1780
- }, /*#__PURE__*/React.createElement(RceHtmlEditor, {
1702
+ }, /*#__PURE__*/React.createElement(RceHtmlEditor
1703
+ // @ts-expect-error
1704
+ , {
1781
1705
  ref: this._prettyHtmlEditorRef,
1782
1706
  height: this.state.height,
1783
1707
  code: this.getCode(),
1784
1708
  onChange: value => {
1785
- this.getTextarea().value = value;
1709
+ const node = this.getTextarea();
1710
+ if (node) {
1711
+ node.value = value;
1712
+ }
1786
1713
  this.handleTextareaChange();
1787
1714
  }
1788
1715
  })));
1789
1716
  }
1790
-
1791
1717
  render() {
1792
- var _this$props$trayProps5, _this$props$trayProps6, _this$props$trayProps7, _this$props$trayProps8, _this$props$trayProps9;
1793
-
1794
1718
  const {
1795
1719
  trayProps,
1796
1720
  ...mceProps
1797
1721
  } = this.props;
1798
-
1799
1722
  if (!this.state.shouldShowEditor) {
1800
1723
  return /*#__PURE__*/React.createElement("div", {
1724
+ // @ts-expect-error
1801
1725
  ref: this._editorPlaceholderRef,
1802
1726
  style: {
1803
1727
  height: `${this.props.editorOptions.height}px`,
@@ -1805,21 +1729,22 @@ class RCEWrapper extends React.Component {
1805
1729
  }
1806
1730
  });
1807
1731
  }
1808
-
1809
1732
  const statusBarFeatures = getStatusBarFeaturesForVariant(this.variant, this.props.ai_text_tools);
1810
1733
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("style", null, this.style.css), /*#__PURE__*/React.createElement(StoreProvider, {
1811
- jwt: (_this$props$trayProps5 = this.props.trayProps) === null || _this$props$trayProps5 === void 0 ? void 0 : _this$props$trayProps5.jwt,
1812
- refreshToken: (_this$props$trayProps6 = this.props.trayProps) === null || _this$props$trayProps6 === void 0 ? void 0 : _this$props$trayProps6.refreshToken,
1813
- host: (_this$props$trayProps7 = this.props.trayProps) === null || _this$props$trayProps7 === void 0 ? void 0 : _this$props$trayProps7.host,
1814
- contextType: (_this$props$trayProps8 = this.props.trayProps) === null || _this$props$trayProps8 === void 0 ? void 0 : _this$props$trayProps8.contextType,
1815
- contextId: (_this$props$trayProps9 = this.props.trayProps) === null || _this$props$trayProps9 === void 0 ? void 0 : _this$props$trayProps9.contextId,
1734
+ jwt: this.props.trayProps?.jwt,
1735
+ refreshToken: this.props.trayProps?.refreshToken,
1736
+ host: this.props.trayProps?.host,
1737
+ contextType: this.props.trayProps?.contextType,
1738
+ contextId: this.props.trayProps?.contextId,
1816
1739
  canvasOrigin: this.props.canvasOrigin
1817
1740
  }, storeProps => {
1818
- var _this$props$trayProps10;
1819
-
1820
1741
  return /*#__PURE__*/React.createElement("div", {
1821
- key: this.id,
1822
- className: `${this.style.classNames.root} rce-wrapper`,
1742
+ key: this.id
1743
+ // @ts-expect-error
1744
+ ,
1745
+ className: `${this.style.classNames.root} rce-wrapper`
1746
+ // @ts-expect-error
1747
+ ,
1823
1748
  ref: this._elementRef,
1824
1749
  style: this.variant === 'full' ? {
1825
1750
  marginBottom: '.5rem'
@@ -1830,10 +1755,14 @@ class RCEWrapper extends React.Component {
1830
1755
  id: `show-on-focus-btn-${this.id}`,
1831
1756
  onClick: this.openKBShortcutModal,
1832
1757
  margin: "xx-small",
1833
- screenReaderLabel: formatMessage('View keyboard shortcuts'),
1758
+ screenReaderLabel: formatMessage('View keyboard shortcuts')
1759
+ // @ts-expect-error
1760
+ ,
1834
1761
  ref: el => this._showOnFocusButton = el
1835
1762
  }, /*#__PURE__*/React.createElement(IconKeyboardShortcutsLine, null)), /*#__PURE__*/React.createElement(AlertMessageArea, {
1836
- messages: this.state.messages,
1763
+ messages: this.state.messages
1764
+ // @ts-expect-error
1765
+ ,
1837
1766
  liveRegion: this.props.liveRegion,
1838
1767
  afterDismiss: this.removeAlert
1839
1768
  }), this.state.editorView === PRETTY_HTML_EDITOR_VIEW && this.renderHtmlEditor(), /*#__PURE__*/React.createElement("div", {
@@ -1842,15 +1771,25 @@ class RCEWrapper extends React.Component {
1842
1771
  }
1843
1772
  }, /*#__PURE__*/React.createElement(Editor, {
1844
1773
  id: mceProps.textareaId,
1845
- textareaName: mceProps.name,
1774
+ textareaName: mceProps.name
1775
+ // @ts-expect-error
1776
+ ,
1846
1777
  init: this.tinymceInitOptions,
1847
- initialValue: this.initialContent,
1778
+ initialValue: this.initialContent
1779
+ // @ts-expect-error
1780
+ ,
1848
1781
  onInit: this.onInit,
1849
1782
  onClick: this.handleFocusEditor,
1850
- onKeypress: this.handleFocusEditor,
1783
+ onKeypress: this.handleFocusEditor
1784
+ // @ts-expect-error
1785
+ ,
1851
1786
  onActivate: this.handleFocusEditor,
1852
- onRemove: this.onRemove,
1853
- onFocus: this.handleFocusEditor,
1787
+ onRemove: this.onRemove
1788
+ // @ts-expect-error
1789
+ ,
1790
+ onFocus: this.handleFocusEditor
1791
+ // @ts-expect-error
1792
+ ,
1854
1793
  onBlur: this.handleBlurEditor,
1855
1794
  onNodeChange: this.onNodeChange,
1856
1795
  onEditorChange: this.onEditorChange,
@@ -1867,7 +1806,9 @@ class RCEWrapper extends React.Component {
1867
1806
  onResize: this.onResize,
1868
1807
  onKBShortcutModalOpen: this.openKBShortcutModal,
1869
1808
  onA11yChecker: this.onA11yChecker,
1870
- onFullscreen: this.handleClickFullscreen,
1809
+ onFullscreen: this.handleClickFullscreen
1810
+ // @ts-expect-error
1811
+ ,
1871
1812
  a11yBadgeColor: this.style.theme.canvasBadgeBackgroundColor,
1872
1813
  a11yErrorsCount: this.state.a11yErrorsCount,
1873
1814
  onWordcountModalOpen: () => launchWordcountModal(this.mceInstance(), document, {
@@ -1876,8 +1817,8 @@ class RCEWrapper extends React.Component {
1876
1817
  disabledPlugins: this.pluginsToExclude,
1877
1818
  features: statusBarFeatures,
1878
1819
  onAI: this.handleAIClick
1879
- }), ((_this$props$trayProps10 = this.props.trayProps) === null || _this$props$trayProps10 === void 0 ? void 0 : _this$props$trayProps10.containingContext) && /*#__PURE__*/React.createElement(CanvasContentTray, Object.assign({
1880
- mountNode: instuiPopupMountNode,
1820
+ }), this.props.trayProps?.containingContext && /*#__PURE__*/React.createElement(CanvasContentTray, Object.assign({
1821
+ mountNode: instuiPopupMountNodeFn,
1881
1822
  key: this.id,
1882
1823
  canvasOrigin: this.getCanvasUrl(),
1883
1824
  bridge: bridge,
@@ -1885,15 +1826,19 @@ class RCEWrapper extends React.Component {
1885
1826
  onTrayClosing: this.handleContentTrayClosing,
1886
1827
  use_rce_icon_maker: this.props.use_rce_icon_maker
1887
1828
  }, trayProps, {
1829
+ // @ts-expect-error
1888
1830
  storeProps: storeProps
1889
1831
  })), /*#__PURE__*/React.createElement(KeyboardShortcutModal, {
1890
1832
  onExited: this.KBShortcutModalExited,
1891
1833
  onDismiss: this.closeKBShortcutModal,
1892
1834
  open: this.state.KBShortcutModalOpen
1893
- }), this.props.ai_text_tools && this.AIToolsTray && /*#__PURE__*/React.createElement(this.AIToolsTray, {
1835
+ }), this.props.ai_text_tools && this.AIToolsTray &&
1836
+ /*#__PURE__*/
1837
+ // @ts-expect-error
1838
+ React.createElement(this.AIToolsTray, {
1894
1839
  open: this.state.AIToolsOpen,
1895
1840
  container: document.querySelector('[role="main"]'),
1896
- mountNode: instuiPopupMountNode,
1841
+ mountNode: instuiPopupMountNodeFn,
1897
1842
  contextId: trayProps.contextId,
1898
1843
  contextType: trayProps.contextId,
1899
1844
  currentContent: this.getCurrentContentForAI(),
@@ -1916,13 +1861,37 @@ class RCEWrapper extends React.Component {
1916
1861
  }, this.state.announcement));
1917
1862
  }));
1918
1863
  }
1919
-
1920
- } // standard: string of tinymce menu commands
1921
- // e.g. 'instructure_links | inserttable instructure_media_embed | hr'
1922
- // custom: a string of tinymce menu commands
1923
- // returns: standard + custom with any duplicate commands removed from custom
1924
-
1925
-
1864
+ }
1865
+ RCEWrapper.propTypes = {
1866
+ ai_text_tools: _pt.bool,
1867
+ autosave: _pt.shape({
1868
+ enabled: _pt.bool,
1869
+ maxAge: _pt.number,
1870
+ interval: _pt.number
1871
+ }),
1872
+ canvasOrigin: _pt.string,
1873
+ defaultContent: _pt.string,
1874
+ editorView: _pt.string,
1875
+ features: _pt.objectOf(_pt.any),
1876
+ handleUnmount: _pt.func,
1877
+ instRecordDisabled: _pt.bool,
1878
+ language: _pt.string,
1879
+ ltiToolFavorites: _pt.arrayOf(_pt.string),
1880
+ maxInitRenderedRCEs: _pt.number,
1881
+ name: _pt.string,
1882
+ onBlur: _pt.func,
1883
+ onContentChange: _pt.func,
1884
+ onFocus: _pt.func,
1885
+ onInitted: _pt.func,
1886
+ onRemove: _pt.func,
1887
+ readOnly: _pt.bool,
1888
+ renderKBShortcutModal: _pt.bool,
1889
+ textareaClassName: _pt.string,
1890
+ textareaId: _pt.string,
1891
+ tinymce: _pt.any.isRequired,
1892
+ use_rce_icon_maker: _pt.bool,
1893
+ userCacheKey: _pt.string
1894
+ };
1926
1895
  RCEWrapper.propTypes = rceWrapperPropTypes;
1927
1896
  RCEWrapper.defaultProps = {
1928
1897
  trayProps: null,
@@ -1933,90 +1902,10 @@ RCEWrapper.defaultProps = {
1933
1902
  ltiTools: [],
1934
1903
  maxInitRenderedRCEs: -1,
1935
1904
  features: {},
1936
- timezone: (_Intl = Intl) === null || _Intl === void 0 ? void 0 : (_Intl$DateTimeFormat = _Intl.DateTimeFormat()) === null || _Intl$DateTimeFormat === void 0 ? void 0 : (_Intl$DateTimeFormat$ = _Intl$DateTimeFormat.resolvedOptions()) === null || _Intl$DateTimeFormat$ === void 0 ? void 0 : _Intl$DateTimeFormat$.timeZone,
1905
+ timezone: Intl?.DateTimeFormat()?.resolvedOptions()?.timeZone,
1937
1906
  canvasOrigin: '',
1938
1907
  variant: 'full'
1939
1908
  };
1940
1909
  RCEWrapper.skinCssInjected = false;
1941
-
1942
- function mergeMenuItems(standard, custom) {
1943
- var _custom$trim;
1944
-
1945
- let c = custom === null || custom === void 0 ? void 0 : (_custom$trim = custom.trim) === null || _custom$trim === void 0 ? void 0 : _custom$trim.call(custom);
1946
- if (!c) return standard;
1947
- const s = new Set(standard.split(/[\s|]+/)); // remove any duplicates
1948
-
1949
- c = c.split(/\s+/).filter(m => !s.has(m));
1950
- c = c.join(' ').replace(/^\s*\|\s*/, '').replace(/\s*\|\s*$/, '');
1951
- return `${standard} | ${c}`;
1952
- } // standard: the incoming tinymce menu object
1953
- // custom: tinymce menu object to merge into standard
1954
- // returns: the merged result by mutating incoming standard arg.
1955
- // It will add commands to existing menus, or add a new menu
1956
- // if the custom one does not exist
1957
-
1958
-
1959
- function mergeMenu(standard, custom) {
1960
- if (!custom) return standard;
1961
- Object.keys(custom).forEach(k => {
1962
- const curr_m = standard[k];
1963
-
1964
- if (curr_m) {
1965
- curr_m.items = mergeMenuItems(curr_m.items, custom[k].items);
1966
- } else {
1967
- standard[k] = { ...custom[k]
1968
- };
1969
- }
1970
- });
1971
- return standard;
1972
- } // standard: incoming tinymce toolbar array
1973
- // custom: tinymce toolbar array to merge into standard
1974
- // returns: the merged result by mutating the incoming standard arg.
1975
- // It will add commands to existing toolbars, or add a new toolbar
1976
- // if the custom one does not exist
1977
-
1978
-
1979
- function mergeToolbar(standard, custom) {
1980
- if (!custom) return standard; // merge given toolbar data into the default toolbar
1981
-
1982
- custom.forEach(tb => {
1983
- const curr_tb = standard.find(t => tb.name && formatMessage(tb.name) === t.name);
1984
-
1985
- if (curr_tb) {
1986
- curr_tb.items.splice(curr_tb.items.length, 0, ...tb.items);
1987
- } else {
1988
- standard.push(tb);
1989
- }
1990
- });
1991
- return standard;
1992
- } // standard: incoming array of plugin names
1993
- // custom: array of plugin names to merge
1994
- // exclusions: array of plugins to remove
1995
- // returns: the merged result, duplicates and exclusions removed
1996
-
1997
-
1998
- function mergePlugins(standard) {
1999
- let custom = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
2000
- let exclusions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
2001
- const union = new Set(standard);
2002
-
2003
- for (const c of custom) {
2004
- union.add(c);
2005
- }
2006
-
2007
- for (const e of exclusions) {
2008
- union.delete(e);
2009
- }
2010
-
2011
- return [...union];
2012
- } // plugins is an array of strings
2013
- // the convention is that plugins starting with '-',
2014
- // i.e. a hyphen, are to be disabled in the RCE instance
2015
-
2016
-
2017
- function parsePluginsToExclude(plugins) {
2018
- return plugins.filter(plugin => plugin.length > 0 && plugin[0] === '-').map(pluginToIgnore => pluginToIgnore.slice(1));
2019
- }
2020
-
2021
1910
  export default RCEWrapper;
2022
1911
  export { mergeMenuItems, mergeMenu, mergeToolbar, mergePlugins, parsePluginsToExclude };