@instructure/canvas-rce 5.14.1 → 5.14.2

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