@instructure/canvas-rce 5.14.1 → 5.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (489) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/DEVELOPMENT.md +2 -2
  3. package/README.md +0 -8
  4. package/__tests__/common/indicate.test.js +84 -0
  5. package/__tests__/common/mimeClass.test.js +85 -0
  6. package/__tests__/module/contentInsertionUtils.test.js +52 -0
  7. package/__tests__/module/indicatorRegion.test.js +75 -0
  8. package/__tests__/module/normalizeLocale.test.js +46 -0
  9. package/__tests__/module/normalizeProps.test.js +51 -0
  10. package/__tests__/module/sanitizePlugins.test.js +48 -0
  11. package/__tests__/module/wrapInitCb.test.js +56 -0
  12. package/__tests__/rcs/api.test.js +819 -0
  13. package/{mocha-reporter-config.js → __tests__/sidebar/actions/all_files.test.js} +10 -9
  14. package/__tests__/sidebar/actions/data.test.js +196 -0
  15. package/__tests__/sidebar/actions/utils.js +44 -0
  16. package/{es/rce/__mocks__/_mockStudioPlayer.js → __tests__/sidebar/reducers/all_files.test.js} +12 -4
  17. package/babel.config.js +3 -1
  18. package/es/bridge/Bridge.js +18 -73
  19. package/es/bridge/index.js +1 -0
  20. package/es/canvasFileBrowser/FileBrowser.js +21 -77
  21. package/es/canvasFileBrowser/en-US.js +3 -6
  22. package/es/common/FlashAlert.js +15 -39
  23. package/es/common/browser.js +4 -2
  24. package/es/common/fileUrl.js +105 -64
  25. package/es/common/incremental-loading/LoadMoreButton.js +4 -4
  26. package/es/common/incremental-loading/LoadingIndicator.js +1 -2
  27. package/es/common/incremental-loading/LoadingStatus.js +5 -13
  28. package/es/common/incremental-loading/index.js +1 -0
  29. package/es/common/incremental-loading/useIncrementalLoading.js +1 -3
  30. package/es/common/indicate.js +16 -10
  31. package/es/common/mimeClass.js +3 -4
  32. package/es/common/natcompare.js +1 -4
  33. package/es/defaultTinymceConfig.js +5 -3
  34. package/es/elementDenylist.js +1 -0
  35. package/es/enhance-user-content/doc_previews.js +24 -35
  36. package/es/enhance-user-content/enhance_user_content.js +32 -67
  37. package/es/enhance-user-content/external_links.js +6 -9
  38. package/es/enhance-user-content/index.js +1 -0
  39. package/es/enhance-user-content/instructure_helper.js +22 -50
  40. package/es/enhance-user-content/jqueryish_funcs.js +8 -11
  41. package/es/enhance-user-content/mathml.js +48 -107
  42. package/es/enhance-user-content/media_comment_thumbnail.js +6 -25
  43. package/es/format-message.js +4 -5
  44. package/es/getThemeVars.js +8 -6
  45. package/es/getTranslations.js +1 -78
  46. package/es/index.d.ts +59 -0
  47. package/es/index.js +6 -6
  48. package/es/rce/AlertMessageArea.js +15 -16
  49. package/es/rce/DraggingBlocker.js +4 -2
  50. package/es/rce/KeyboardShortcutModal.js +3 -2
  51. package/es/rce/RCE.js +16 -17
  52. package/es/rce/RCEGlobals.js +12 -10
  53. package/es/rce/RCEVariants.js +29 -14
  54. package/es/rce/RCEWrapper.js +530 -641
  55. package/es/rce/RCEWrapper.utils.js +131 -0
  56. package/es/rce/RCEWrapperProps.js +9 -5
  57. package/es/rce/RceHtmlEditor.js +17 -19
  58. package/es/rce/ResizeHandle.js +4 -10
  59. package/es/rce/RestoreAutoSaveModal.js +1 -2
  60. package/es/rce/ShowOnFocusButton/index.js +2 -8
  61. package/es/rce/StatusBar.js +10 -44
  62. package/es/rce/alertHandler.js +1 -4
  63. package/es/rce/contentInsertion.js +36 -59
  64. package/es/rce/contentInsertionUtils.js +6 -8
  65. package/es/rce/contentRendering.js +13 -17
  66. package/es/rce/customEvents.js +1 -0
  67. package/es/rce/editorLanguage.js +23 -11
  68. package/es/rce/indicatorRegion.js +7 -7
  69. package/es/rce/normalizeLocale.js +5 -3
  70. package/es/rce/normalizeProps.js +7 -5
  71. package/es/rce/plugins/instructure-ui-icons/plugin.js +21 -3
  72. package/es/rce/plugins/instructure_color/clickCallback.js +82 -0
  73. package/es/rce/plugins/instructure_color/components/ColorPicker.js +294 -0
  74. package/es/rce/plugins/instructure_color/components/ColorPopup.js +67 -0
  75. package/es/rce/plugins/instructure_color/components/colorUtils.js +60 -0
  76. package/es/rce/plugins/instructure_color/plugin.js +40 -0
  77. package/es/rce/plugins/instructure_condensed_buttons/core/ListUtils.js +10 -3
  78. package/es/rce/plugins/instructure_condensed_buttons/plugin.js +1 -0
  79. package/es/rce/plugins/instructure_condensed_buttons/ui/alignment-button.js +1 -2
  80. package/es/rce/plugins/instructure_condensed_buttons/ui/directionality-button.js +3 -2
  81. package/es/rce/plugins/instructure_condensed_buttons/ui/indent-outdent-button.js +1 -0
  82. package/es/rce/plugins/instructure_condensed_buttons/ui/list-button.js +26 -25
  83. package/es/rce/plugins/instructure_condensed_buttons/ui/subscript-superscript-button.js +2 -3
  84. package/es/rce/plugins/instructure_documents/clickCallback.js +1 -0
  85. package/es/rce/plugins/instructure_documents/components/DocumentsPanel.js +1 -9
  86. package/es/rce/plugins/instructure_documents/components/Link.js +4 -20
  87. package/es/rce/plugins/instructure_documents/plugin.js +7 -14
  88. package/es/rce/plugins/instructure_equation/EquationEditorModal/advancedOnlySyntax.js +4 -2
  89. package/es/rce/plugins/instructure_equation/EquationEditorModal/advancedPreference.js +1 -2
  90. package/es/rce/plugins/instructure_equation/EquationEditorModal/index.js +17 -37
  91. package/es/rce/plugins/instructure_equation/EquationEditorModal/latexTextareaUtil.js +14 -15
  92. package/es/rce/plugins/instructure_equation/EquationEditorModal/parseLatex.js +6 -5
  93. package/es/rce/plugins/instructure_equation/EquationEditorModal/styles.js +4 -2
  94. package/es/rce/plugins/instructure_equation/EquationEditorToolbar/buttons.js +14 -8
  95. package/es/rce/plugins/instructure_equation/EquationEditorToolbar/index.js +13 -18
  96. package/es/rce/plugins/instructure_equation/MathIcon/index.js +4 -5
  97. package/es/rce/plugins/instructure_equation/MathIcon/svgs.js +1 -1
  98. package/es/rce/plugins/instructure_equation/clickCallback.js +2 -5
  99. package/es/rce/plugins/instructure_equation/mathlive/index.js +167 -16
  100. package/es/rce/plugins/instructure_equation/plugin.js +7 -10
  101. package/es/rce/plugins/instructure_fullscreen/plugin.js +1 -6
  102. package/es/rce/plugins/instructure_html_view/clickCallback.js +1 -0
  103. package/es/rce/plugins/instructure_html_view/plugin.js +5 -4
  104. package/es/rce/plugins/instructure_icon_maker/clickCallback.js +5 -8
  105. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ColorSection.js +47 -51
  106. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/CreateIconMakerForm.js +10 -10
  107. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Footer.js +11 -11
  108. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Group.js +6 -6
  109. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Header.js +8 -10
  110. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/Course.js +32 -31
  111. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageOptions.js +24 -35
  112. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageSection.js +32 -32
  113. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ModeSelect.js +11 -11
  114. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/MultiColor/index.js +16 -15
  115. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/MultiColor/svg.js +1 -0
  116. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/SVGList.js +11 -11
  117. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/SVGThumbnail.js +9 -13
  118. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/SingleColor/index.js +12 -13
  119. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/SingleColor/svg.js +33 -80
  120. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/Upload.js +34 -28
  121. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/index.js +1 -0
  122. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/propTypes.js +1 -0
  123. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/utils.js +5 -5
  124. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Preview.js +7 -8
  125. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ShapeSection.js +5 -7
  126. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/TextSection.js +5 -10
  127. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/index.js +1 -0
  128. package/es/rce/plugins/instructure_icon_maker/components/IconMakerTray.js +38 -60
  129. package/es/rce/plugins/instructure_icon_maker/components/SavedIconMakerList.js +4 -4
  130. package/es/rce/plugins/instructure_icon_maker/plugin.js +10 -14
  131. package/es/rce/plugins/instructure_icon_maker/reducers/imageSection.js +37 -38
  132. package/es/rce/plugins/instructure_icon_maker/reducers/svgSettings.js +24 -24
  133. package/es/rce/plugins/instructure_icon_maker/registerEditToolbar.js +2 -4
  134. package/es/rce/plugins/instructure_icon_maker/svg/constants.js +4 -3
  135. package/es/rce/plugins/instructure_icon_maker/svg/font.js +3 -1
  136. package/es/rce/plugins/instructure_icon_maker/svg/image.js +74 -90
  137. package/es/rce/plugins/instructure_icon_maker/svg/index.js +17 -24
  138. package/es/rce/plugins/instructure_icon_maker/svg/metadata.js +1 -0
  139. package/es/rce/plugins/instructure_icon_maker/svg/settings.js +48 -58
  140. package/es/rce/plugins/instructure_icon_maker/svg/shape.js +5 -54
  141. package/es/rce/plugins/instructure_icon_maker/svg/text.js +35 -124
  142. package/es/rce/plugins/instructure_icon_maker/svg/utils.js +3 -11
  143. package/es/rce/plugins/instructure_icon_maker/utils/IconMakerClose.js +4 -9
  144. package/es/rce/plugins/instructure_icon_maker/utils/IconMakerFormHasChanges.js +1 -15
  145. package/es/rce/plugins/instructure_icon_maker/utils/addIconMakerAttributes.js +3 -4
  146. package/es/rce/plugins/instructure_icon_maker/utils/iconValidation.js +2 -3
  147. package/es/rce/plugins/instructure_icon_maker/utils/iconsLabels.js +1 -0
  148. package/es/rce/plugins/instructure_icon_maker/utils/useDebouncedValue.js +12 -13
  149. package/es/rce/plugins/instructure_image/ImageEmbedOptions.js +9 -31
  150. package/es/rce/plugins/instructure_image/ImageList/Image.js +8 -14
  151. package/es/rce/plugins/instructure_image/ImageList/index.js +8 -10
  152. package/es/rce/plugins/instructure_image/ImageOptionsTray/TrayController.js +9 -31
  153. package/es/rce/plugins/instructure_image/ImageOptionsTray/index.js +6 -19
  154. package/es/rce/plugins/instructure_image/Images/index.js +1 -3
  155. package/es/rce/plugins/instructure_image/clickCallback.js +1 -0
  156. package/es/rce/plugins/instructure_image/plugin.js +14 -20
  157. package/es/rce/plugins/instructure_links/clickCallback.js +1 -0
  158. package/es/rce/plugins/instructure_links/components/AccordionSection.js +8 -8
  159. package/es/rce/plugins/instructure_links/components/CollectionPanel.js +1 -3
  160. package/es/rce/plugins/instructure_links/components/Link.js +68 -84
  161. package/es/rce/plugins/instructure_links/components/LinkOptionsDialog/LinkOptionsDialogController.js +2 -23
  162. package/es/rce/plugins/instructure_links/components/LinkOptionsDialog/index.js +3 -6
  163. package/es/rce/plugins/instructure_links/components/LinkOptionsTray/LinkOptionsTrayController.js +3 -20
  164. package/es/rce/plugins/instructure_links/components/LinkOptionsTray/index.js +3 -14
  165. package/es/rce/plugins/instructure_links/components/LinkSet.js +32 -57
  166. package/es/rce/plugins/instructure_links/components/LinksPanel.js +22 -10
  167. package/es/rce/plugins/instructure_links/components/NavigationPanel.js +7 -9
  168. package/es/rce/plugins/instructure_links/components/NoResults.js +7 -14
  169. package/es/rce/plugins/instructure_links/plugin.js +23 -49
  170. package/es/rce/plugins/instructure_links/validateURL.js +81 -36
  171. package/es/rce/plugins/instructure_media_embed/clickCallback.js +5 -9
  172. package/es/rce/plugins/instructure_media_embed/components/Embed.js +7 -7
  173. package/es/rce/plugins/instructure_media_embed/plugin.js +7 -3
  174. package/es/rce/plugins/instructure_paste/pasteMenuCommand.js +1 -5
  175. package/es/rce/plugins/instructure_paste/plugin.js +29 -33
  176. package/es/rce/plugins/instructure_rce_external_tools/ExternalToolsEnv.js +31 -79
  177. package/es/rce/plugins/instructure_rce_external_tools/RceToolWrapper.js +24 -83
  178. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialog.js +39 -69
  179. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogModal.js +1 -2
  180. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogTray.js +1 -1
  181. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionDialog.js +5 -14
  182. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionItem.js +1 -2
  183. package/es/rce/plugins/instructure_rce_external_tools/components/util/ExpandoText.js +1 -0
  184. package/es/rce/plugins/instructure_rce_external_tools/components/util/ToolLaunchIframe.js +2 -1
  185. package/es/rce/plugins/instructure_rce_external_tools/constants.js +28 -0
  186. package/es/rce/plugins/instructure_rce_external_tools/dialog-helper.js +20 -6
  187. package/es/rce/plugins/instructure_rce_external_tools/helpers/tags.js +0 -2
  188. package/es/rce/plugins/instructure_rce_external_tools/jquery/jquery.dropdownList.js +129 -136
  189. package/es/rce/plugins/instructure_rce_external_tools/lti11-content-items/RceLti11ContentItem.js +110 -112
  190. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/Lti13ContentItemJson.js +1 -0
  191. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/RceLti13ContentItem.js +4 -21
  192. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/models/BaseLinkContentItem.js +5 -19
  193. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/models/HtmlFragmentContentItem.js +1 -6
  194. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/models/ImageContentItem.js +1 -9
  195. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/models/LinkContentItem.js +1 -1
  196. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/models/ResourceLinkContentItem.js +3 -5
  197. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/processEditorContentItems.js +23 -16
  198. package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/rceLti13ContentItemFromJson.js +3 -4
  199. package/es/rce/plugins/instructure_rce_external_tools/plugin.js +11 -20
  200. package/es/rce/plugins/instructure_rce_external_tools/util/addParentFrameContextToUrl.js +1 -1
  201. package/es/rce/plugins/instructure_rce_external_tools/util/externalToolsForToolbar.js +42 -0
  202. package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.js +6 -35
  203. package/es/rce/plugins/instructure_record/AudioOptionsTray/index.js +13 -17
  204. package/es/rce/plugins/instructure_record/MediaPanel/index.js +1 -9
  205. package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.js +16 -66
  206. package/es/rce/plugins/instructure_record/VideoOptionsTray/index.js +21 -35
  207. package/es/rce/plugins/instructure_record/clickCallback.js +32 -44
  208. package/es/rce/plugins/instructure_record/mediaTranslations.js +1 -0
  209. package/es/rce/plugins/instructure_record/plugin.js +11 -18
  210. package/es/rce/plugins/instructure_search_and_replace/clickCallback.js +4 -8
  211. package/es/rce/plugins/instructure_search_and_replace/components/FindReplaceTray.js +34 -51
  212. package/es/rce/plugins/instructure_search_and_replace/components/FindReplaceTrayController.js +12 -30
  213. package/es/rce/plugins/instructure_search_and_replace/getSelectionContext.js +2 -9
  214. package/es/rce/plugins/instructure_search_and_replace/plugin.js +2 -5
  215. package/es/rce/plugins/instructure_studio_media_options/plugin.js +1 -1
  216. package/es/rce/plugins/instructure_wordcount/clickCallback.js +5 -9
  217. package/es/rce/plugins/instructure_wordcount/components/WordCountModal.js +27 -37
  218. package/es/rce/plugins/instructure_wordcount/plugin.js +1 -0
  219. package/es/rce/plugins/instructure_wordcount/utils/countContent.js +4 -11
  220. package/es/rce/plugins/instructure_wordcount/utils/tableContent.js +6 -8
  221. package/es/rce/plugins/shared/CanvasContentTray.js +29 -63
  222. package/es/rce/plugins/shared/CheckerboardStyling.js +1 -1
  223. package/es/rce/plugins/shared/ColorInput.js +27 -39
  224. package/es/rce/plugins/shared/ConditionalTooltip.js +6 -6
  225. package/es/rce/plugins/shared/ContentSelection.js +29 -78
  226. package/es/rce/plugins/shared/DimensionUtils.js +3 -12
  227. package/es/rce/plugins/shared/DimensionsInput/DimensionInput.js +6 -6
  228. package/es/rce/plugins/shared/DimensionsInput/index.js +37 -15
  229. package/es/rce/plugins/shared/DimensionsInput/useDimensionsState.js +5 -29
  230. package/es/rce/plugins/shared/ErrorBoundary.js +2 -5
  231. package/es/rce/plugins/shared/EventUtils.js +2 -4
  232. package/es/rce/plugins/shared/Filter.js +8 -38
  233. package/es/rce/plugins/shared/FixedContentTray.js +16 -17
  234. package/es/rce/plugins/shared/ImageCropper/DirectionRegion.js +4 -12
  235. package/es/rce/plugins/shared/ImageCropper/Modal.js +16 -20
  236. package/es/rce/plugins/shared/ImageCropper/Preview.js +18 -24
  237. package/es/rce/plugins/shared/ImageCropper/constants.js +1 -0
  238. package/es/rce/plugins/shared/ImageCropper/controls/CustomNumberInput.js +10 -14
  239. package/es/rce/plugins/shared/ImageCropper/controls/ResetControls.js +4 -4
  240. package/es/rce/plugins/shared/ImageCropper/controls/RotationControls.js +5 -15
  241. package/es/rce/plugins/shared/ImageCropper/controls/ShapeControls.js +8 -11
  242. package/es/rce/plugins/shared/ImageCropper/controls/ZoomControls.js +5 -16
  243. package/es/rce/plugins/shared/ImageCropper/controls/index.js +5 -5
  244. package/es/rce/plugins/shared/ImageCropper/controls/useDebouncedNumericValue.js +16 -31
  245. package/es/rce/plugins/shared/ImageCropper/controls/utils.js +1 -2
  246. package/es/rce/plugins/shared/ImageCropper/imageCropUtils.js +19 -31
  247. package/es/rce/plugins/shared/ImageCropper/index.js +1 -0
  248. package/es/rce/plugins/shared/ImageCropper/propTypes.js +1 -0
  249. package/es/rce/plugins/shared/ImageCropper/reducers/imageCropper.js +15 -14
  250. package/es/rce/plugins/shared/ImageCropper/shape.js +1 -0
  251. package/es/rce/plugins/shared/ImageCropper/svg/index.js +1 -2
  252. package/es/rce/plugins/shared/ImageCropper/svg/shape.js +5 -22
  253. package/es/rce/plugins/shared/ImageCropper/svg/utils.js +3 -4
  254. package/es/rce/plugins/shared/ImageCropper/useKeyMouseEvents.js +20 -50
  255. package/es/rce/plugins/shared/ImageCropper/useMouseWheel.js +8 -10
  256. package/es/rce/plugins/shared/ImageOptionsForm.js +18 -20
  257. package/es/rce/plugins/shared/LinkDisplay.js +9 -11
  258. package/es/rce/plugins/shared/PreviewIcon.js +9 -15
  259. package/es/rce/plugins/shared/Previewable.js +1 -0
  260. package/es/rce/plugins/shared/RceFileBrowser.js +7 -10
  261. package/es/rce/plugins/shared/StoreContext.js +9 -12
  262. package/es/rce/plugins/shared/StudioLtiSupportUtils.js +15 -12
  263. package/es/rce/plugins/shared/UnknownFileTypePanel.js +1 -0
  264. package/es/rce/plugins/shared/Upload/CanvasContentPanel.js +19 -25
  265. package/es/rce/plugins/shared/Upload/CategoryProcessor.js +2 -3
  266. package/es/rce/plugins/shared/Upload/ComputerPanel.js +19 -40
  267. package/es/rce/plugins/shared/Upload/PanelFilter.js +10 -20
  268. package/es/rce/plugins/shared/Upload/SvgCategoryProcessor.js +4 -3
  269. package/es/rce/plugins/shared/Upload/UploadFile.js +32 -38
  270. package/es/rce/plugins/shared/Upload/UploadFileModal.js +37 -59
  271. package/es/rce/plugins/shared/Upload/UrlPanel.js +5 -5
  272. package/es/rce/plugins/shared/Upload/UsageRightsSelectBox.js +25 -36
  273. package/es/rce/plugins/shared/Upload/doFileUpload.js +10 -13
  274. package/es/rce/plugins/shared/Upload/index.js +1 -0
  275. package/es/rce/plugins/shared/ai_tools/AIResponseModal.js +8 -11
  276. package/es/rce/plugins/shared/ai_tools/AIToolsTray.js +19 -40
  277. package/es/rce/plugins/shared/ai_tools/aiicons.js +3 -2
  278. package/es/rce/plugins/shared/ai_tools/index.js +1 -0
  279. package/es/rce/plugins/shared/buildDownloadUrl.js +0 -2
  280. package/es/rce/plugins/shared/canvasContentUtils.js +7 -11
  281. package/es/rce/plugins/shared/compressionUtils.js +18 -28
  282. package/es/rce/plugins/shared/dateUtils.js +1 -1
  283. package/es/rce/plugins/shared/do-fetch-api-effect/defaultFetchOptions.js +4 -2
  284. package/es/rce/plugins/shared/do-fetch-api-effect/doFetchApi.js +18 -24
  285. package/es/rce/plugins/shared/do-fetch-api-effect/get-cookie.js +1 -1
  286. package/es/rce/plugins/shared/do-fetch-api-effect/index.js +1 -0
  287. package/es/rce/plugins/shared/do-fetch-api-effect/parse-link-header.js +6 -20
  288. package/es/rce/plugins/shared/do-fetch-api-effect/query-string-encoding.js +5 -3
  289. package/es/rce/plugins/shared/fileShape.js +4 -9
  290. package/es/rce/plugins/shared/fileTypeUtils.js +34 -47
  291. package/es/rce/plugins/shared/fileUtils.js +1 -2
  292. package/es/rce/plugins/shared/linkUtils.js +1 -16
  293. package/es/rce/plugins/shared/round.js +2 -2
  294. package/es/rce/plugins/shared/trayUtils.js +7 -3
  295. package/es/rce/plugins/shared/useDataUrl.js +13 -14
  296. package/es/rce/plugins/shared/useFilterSettings.js +3 -3
  297. package/es/rce/plugins/tinymce-a11y-checker/components/ColorField.js +4 -8
  298. package/es/rce/plugins/tinymce-a11y-checker/components/checker.js +12 -72
  299. package/es/rce/plugins/tinymce-a11y-checker/components/color-picker.js +1 -2
  300. package/es/rce/plugins/tinymce-a11y-checker/components/placeholder-svg.js +1 -0
  301. package/es/rce/plugins/tinymce-a11y-checker/components/pointer.js +1 -0
  302. package/es/rce/plugins/tinymce-a11y-checker/node-checker.js +2 -9
  303. package/es/rce/plugins/tinymce-a11y-checker/plugin.js +18 -24
  304. package/es/rce/plugins/tinymce-a11y-checker/rules/adjacent-links.js +3 -26
  305. package/es/rce/plugins/tinymce-a11y-checker/rules/headings-sequence.js +9 -38
  306. package/es/rce/plugins/tinymce-a11y-checker/rules/headings-start-at-h2.js +2 -7
  307. package/es/rce/plugins/tinymce-a11y-checker/rules/img-alt-filename.js +1 -2
  308. package/es/rce/plugins/tinymce-a11y-checker/rules/img-alt-length.js +1 -1
  309. package/es/rce/plugins/tinymce-a11y-checker/rules/img-alt.js +1 -2
  310. package/es/rce/plugins/tinymce-a11y-checker/rules/index.js +1 -0
  311. package/es/rce/plugins/tinymce-a11y-checker/rules/large-text-contrast.js +2 -6
  312. package/es/rce/plugins/tinymce-a11y-checker/rules/list-structure.js +5 -24
  313. package/es/rce/plugins/tinymce-a11y-checker/rules/paragraphs-for-headings.js +1 -3
  314. package/es/rce/plugins/tinymce-a11y-checker/rules/small-text-contrast.js +2 -8
  315. package/es/rce/plugins/tinymce-a11y-checker/rules/table-caption.js +1 -3
  316. package/es/rce/plugins/tinymce-a11y-checker/rules/table-header-scope.js +1 -2
  317. package/es/rce/plugins/tinymce-a11y-checker/rules/table-header.js +1 -9
  318. package/es/rce/plugins/tinymce-a11y-checker/utils/colors.js +1 -0
  319. package/es/rce/plugins/tinymce-a11y-checker/utils/describe.js +1 -7
  320. package/es/rce/plugins/tinymce-a11y-checker/utils/dom.js +3 -30
  321. package/es/rce/plugins/tinymce-a11y-checker/utils/indicate.js +18 -18
  322. package/es/rce/plugins/tinymce-a11y-checker/utils/rgb-hex.js +6 -12
  323. package/es/rce/plugins/tinymce-a11y-checker/utils/strings.js +1 -4
  324. package/es/rce/root.js +17 -16
  325. package/es/rce/sanitizePlugins.js +1 -3
  326. package/es/rce/style.js +1 -4
  327. package/es/rce/tinyRCE.js +14 -9
  328. package/es/rce/tinymce.oxide.content.min.css.js +1 -0
  329. package/es/rce/tinymce.oxide.skin.min.css.js +1 -0
  330. package/es/rce/transformContent.js +9 -11
  331. package/es/rce/types.js +1 -0
  332. package/es/rce/userOS.js +1 -1
  333. package/es/rce/wrapInitCb.js +50 -43
  334. package/es/rcs/api.js +100 -171
  335. package/es/rcs/buildError.js +8 -20
  336. package/es/rcs/fake.js +9 -20
  337. package/es/sidebar/actions/all_files.js +2 -0
  338. package/es/sidebar/actions/data.js +4 -7
  339. package/es/sidebar/actions/documents.js +19 -18
  340. package/es/sidebar/actions/files.js +21 -28
  341. package/es/sidebar/actions/filter.js +5 -5
  342. package/es/sidebar/actions/flickr.js +1 -1
  343. package/es/sidebar/actions/images.js +32 -37
  344. package/es/sidebar/actions/links.js +1 -0
  345. package/es/sidebar/actions/media.js +27 -28
  346. package/es/sidebar/actions/session.js +2 -5
  347. package/es/sidebar/actions/ui.js +1 -0
  348. package/es/sidebar/actions/upload.js +38 -74
  349. package/es/sidebar/containers/Sidebar.js +1 -2
  350. package/es/sidebar/containers/sidebarHandlers.js +9 -13
  351. package/es/sidebar/dragHtml.js +11 -5
  352. package/es/sidebar/reducers/all_files.js +5 -6
  353. package/es/sidebar/reducers/collection.js +12 -15
  354. package/es/sidebar/reducers/collections.js +6 -8
  355. package/es/sidebar/reducers/documents.js +7 -16
  356. package/es/sidebar/reducers/files.js +4 -6
  357. package/es/sidebar/reducers/filter.js +8 -23
  358. package/es/sidebar/reducers/flickr.js +10 -12
  359. package/es/sidebar/reducers/folder.js +16 -18
  360. package/es/sidebar/reducers/folders.js +4 -6
  361. package/es/sidebar/reducers/images.js +4 -16
  362. package/es/sidebar/reducers/index.js +3 -1
  363. package/es/sidebar/reducers/media.js +7 -16
  364. package/es/sidebar/reducers/newPageLinkExpanded.js +2 -5
  365. package/es/sidebar/reducers/noop.js +2 -2
  366. package/es/sidebar/reducers/rootFolderId.js +2 -5
  367. package/es/sidebar/reducers/session.js +4 -6
  368. package/es/sidebar/reducers/ui.js +6 -25
  369. package/es/sidebar/reducers/upload.js +16 -64
  370. package/es/sidebar/store/configureStore.js +1 -0
  371. package/es/sidebar/store/initialState.js +14 -26
  372. package/es/translations/locales/ab.js +1 -0
  373. package/es/translations/locales/ar.js +72 -8
  374. package/es/translations/locales/ca.js +72 -8
  375. package/es/translations/locales/cs.js +1 -0
  376. package/es/translations/locales/cs_CZ.js +1 -0
  377. package/es/translations/locales/cy.js +72 -8
  378. package/es/translations/locales/da-x-k12.js +72 -8
  379. package/es/translations/locales/da.js +72 -8
  380. package/es/translations/locales/da_DK.js +1 -0
  381. package/es/translations/locales/de.js +72 -8
  382. package/es/translations/locales/el.js +4 -0
  383. package/es/translations/locales/en-AU-x-unimelb.js +72 -8
  384. package/es/translations/locales/en-GB-x-ukhe.js +72 -8
  385. package/es/translations/locales/en.js +72 -8
  386. package/es/translations/locales/en_AU.js +72 -8
  387. package/es/translations/locales/en_CA.js +72 -8
  388. package/es/translations/locales/en_CY.js +72 -8
  389. package/es/translations/locales/en_GB.js +72 -8
  390. package/es/translations/locales/en_NZ.js +1 -0
  391. package/es/translations/locales/en_SE.js +1 -0
  392. package/es/translations/locales/en_US.js +1 -0
  393. package/es/translations/locales/es.js +72 -8
  394. package/es/translations/locales/es_ES.js +72 -8
  395. package/es/translations/locales/es_GT.js +1 -0
  396. package/es/translations/locales/fa_IR.js +7 -0
  397. package/es/translations/locales/fi.js +72 -8
  398. package/es/translations/locales/fr.js +72 -8
  399. package/es/translations/locales/fr_CA.js +73 -9
  400. package/es/translations/locales/ga.js +5 -13
  401. package/es/translations/locales/he.js +7 -0
  402. package/es/translations/locales/hi.js +72 -8
  403. package/es/translations/locales/ht.js +72 -8
  404. package/es/translations/locales/hu.js +7 -6
  405. package/es/translations/locales/hu_HU.js +1 -0
  406. package/es/translations/locales/hy.js +1 -0
  407. package/es/translations/locales/id.js +72 -8
  408. package/es/translations/locales/id_ID.js +1 -0
  409. package/es/translations/locales/is.js +72 -8
  410. package/es/translations/locales/it.js +72 -8
  411. package/es/translations/locales/ja.js +72 -8
  412. package/es/translations/locales/ko.js +1 -0
  413. package/es/translations/locales/ko_KR.js +1 -0
  414. package/es/translations/locales/lt.js +1 -0
  415. package/es/translations/locales/lt_LT.js +1 -0
  416. package/es/translations/locales/mi.js +72 -8
  417. package/es/translations/locales/mn_MN.js +1 -0
  418. package/es/translations/locales/ms.js +72 -8
  419. package/es/translations/locales/nb-x-k12.js +72 -8
  420. package/es/translations/locales/nb.js +72 -8
  421. package/es/translations/locales/nl.js +72 -8
  422. package/es/translations/locales/nl_NL.js +1 -0
  423. package/es/translations/locales/nn.js +7 -6
  424. package/es/translations/locales/pl.js +72 -8
  425. package/es/translations/locales/pt.js +72 -8
  426. package/es/translations/locales/pt_BR.js +72 -8
  427. package/es/translations/locales/ro.js +1 -0
  428. package/es/translations/locales/ru.js +72 -8
  429. package/es/translations/locales/se.js +1 -0
  430. package/es/translations/locales/sl.js +72 -8
  431. package/es/translations/locales/sv-x-k12.js +72 -8
  432. package/es/translations/locales/sv.js +72 -8
  433. package/es/translations/locales/sv_SE.js +1 -0
  434. package/es/translations/locales/tg.js +1 -0
  435. package/es/translations/locales/th.js +72 -8
  436. package/es/translations/locales/th_TH.js +1 -0
  437. package/es/translations/locales/tl_PH.js +1 -0
  438. package/es/translations/locales/tr.js +7 -0
  439. package/es/translations/locales/uk_UA.js +7 -0
  440. package/es/translations/locales/vi.js +72 -8
  441. package/es/translations/locales/vi_VN.js +1 -0
  442. package/es/translations/locales/zh-Hans.js +72 -8
  443. package/es/translations/locales/zh-Hant.js +72 -8
  444. package/es/translations/locales/zh.js +72 -8
  445. package/es/translations/locales/zh_HK.js +72 -8
  446. package/es/translations/locales/zh_TW.Big5.js +1 -0
  447. package/es/translations/locales/zh_TW.js +1 -0
  448. package/es/translations/tinymce/ar_SA.js +1 -0
  449. package/es/translations/tinymce/fi.js +1 -0
  450. package/es/translations/tinymce/ga.js +1 -0
  451. package/es/translations/tinymce/id.js +1 -0
  452. package/es/translations/tinymce/ru.js +1 -0
  453. package/es/translations/tinymce/ru_RU.js +1 -0
  454. package/es/translations/tinymce/sl.js +1 -0
  455. package/es/translations/tinymce/sr.js +1 -0
  456. package/es/translations/tinymce/th.js +1 -0
  457. package/es/translations/tinymce/uk_UA.js +1 -0
  458. package/es/translations/tinymce/vi_VN.js +1 -0
  459. package/es/util/TypedDict.js +4 -2
  460. package/es/util/elem-util.js +1 -1
  461. package/es/util/encrypted-storage.js +3 -13
  462. package/es/util/file-url-util.js +2 -7
  463. package/es/util/fullscreenHelpers.js +9 -9
  464. package/es/util/instui-icon-helper.js +4 -3
  465. package/es/util/loadingPlaceholder.js +39 -41
  466. package/es/util/simpleCache.js +1 -5
  467. package/es/util/string-util.js +1 -1
  468. package/es/util/textarea-editing-util.js +3 -7
  469. package/es/util/tinymce-plugin-util.js +0 -5
  470. package/es/util/url-util.js +20 -29
  471. package/eslint.config.js +250 -0
  472. package/jest.config.js +1 -1
  473. package/locales/en.json +190 -10
  474. package/package.json +78 -82
  475. package/scripts/build-canvas +2 -1
  476. package/scripts/build.js +4 -4
  477. package/scripts/installTranslations.js +7 -8
  478. package/testcafe/RCEWrapper.test.js +0 -1
  479. package/testcafe/StatusBar.test.js +0 -1
  480. package/testcafe/axe.test.js +3 -4
  481. package/testcafe/enhanceUserContent.test.js +0 -1
  482. package/tsconfig.json +21 -16
  483. package/{es/rce/__mocks__/styleMock.js → types/format-message-generate-id.d.ts} +6 -2
  484. package/{es/rce/plugins/shared/__mocks__/screenfull.js → types/js-beautify.d.ts} +4 -7
  485. package/.eslintrc +0 -45
  486. package/.prettierignore +0 -6
  487. package/es/rce/__mocks__/_mockCryptoEs.js +0 -124
  488. package/es/rce/__mocks__/tinymceReact.js +0 -55
  489. package/es/rce/plugins/tinymce-a11y-checker/rules/__mocks__/index.js +0 -53
@@ -1,5 +1,3 @@
1
- // @ts-nocheck
2
-
3
1
  /*
4
2
  * Copyright (C) 2019 - present Instructure, Inc.
5
3
  *
@@ -62,6 +60,7 @@
62
60
  * href with the new URL. if the link text is updated, replace the link's
63
61
  * content with the new plain text (deleting the image)
64
62
  */
63
+
65
64
  import formatMessage from '../../../format-message';
66
65
  import clickCallback from './clickCallback';
67
66
  import bridge from '../../../bridge';
@@ -72,23 +71,17 @@ import tinymce from 'tinymce';
72
71
  const trayController = new LinkOptionsTrayController();
73
72
  const COURSE_PLUGIN_KEY = 'course_links';
74
73
  const GROUP_PLUGIN_KEY = 'group_links';
75
-
76
74
  function getCommandName(selectedNode) {
77
75
  const isCourseLink = selectedNode.getAttribute('data-course-type');
78
76
  return isCourseLink ? 'instructureTrayForCourseLinks' : 'instructureTrayToEditLink';
79
77
  }
80
-
81
78
  function selectedAnchorCount(ed) {
82
79
  return ed.selection.getRng().cloneContents().querySelectorAll('a').length;
83
80
  }
84
-
85
81
  function getMenuItems(ed) {
86
- var _ed$settings$canvas_r;
87
-
88
- const contextType = (_ed$settings$canvas_r = ed.settings.canvas_rce_containing_context) === null || _ed$settings$canvas_r === void 0 ? void 0 : _ed$settings$canvas_r.type;
82
+ const contextType = ed.settings.canvas_rce_containing_context?.type;
89
83
  const sel_anchors = ed.selection.isCollapsed() ? 0 : selectedAnchorCount(ed);
90
84
  let items;
91
-
92
85
  if (getAnchorElement(ed, ed.selection.getNode())) {
93
86
  // cursor is on an anchor, edit or remove it
94
87
  items = [{
@@ -103,7 +96,6 @@ function getMenuItems(ed) {
103
96
  text: formatMessage('External Link'),
104
97
  value: 'instructureLinkCreate'
105
98
  }];
106
-
107
99
  if (contextType === 'course') {
108
100
  items.push({
109
101
  text: formatMessage('Course Link'),
@@ -115,7 +107,6 @@ function getMenuItems(ed) {
115
107
  value: 'instructure_group_links'
116
108
  });
117
109
  }
118
-
119
110
  if (sel_anchors > 0) {
120
111
  // selection contains anchor(s), so the user can remove them
121
112
  items.push({
@@ -127,10 +118,8 @@ function getMenuItems(ed) {
127
118
  });
128
119
  }
129
120
  }
130
-
131
121
  return items;
132
122
  }
133
-
134
123
  function removeAnchorFromSelectedElement(ed) {
135
124
  const selectedElem = ed.selection.getNode();
136
125
  const anchorElem = getAnchorElement(ed, selectedElem);
@@ -138,7 +127,6 @@ function removeAnchorFromSelectedElement(ed) {
138
127
  ed.undoManager.add();
139
128
  ed.execCommand('Unlink');
140
129
  }
141
-
142
130
  function doMenuItem(ed, actionName) {
143
131
  switch (actionName) {
144
132
  case 'instructureTrayToEditLink':
@@ -146,30 +134,23 @@ function doMenuItem(ed, actionName) {
146
134
  case 'instructureLinkCreate':
147
135
  ed.execCommand(actionName);
148
136
  break;
149
-
150
137
  case 'instructureUnlink':
151
138
  removeAnchorFromSelectedElement(ed);
152
139
  break;
153
-
154
140
  case 'instructureUnlinkAll':
155
141
  ed.undoManager.add();
156
142
  ed.execCommand('unlink');
157
143
  break;
158
-
159
144
  case 'instructure_course_links':
160
145
  ed.focus(true); // activate the editor without changing focus
161
-
162
146
  ed.execCommand('instructureTrayForLinks', false, COURSE_PLUGIN_KEY);
163
147
  break;
164
-
165
148
  case 'instructure_group_links':
166
149
  ed.focus(true); // activate the editor without changing focus
167
-
168
150
  ed.execCommand('instructureTrayForLinks', false, GROUP_PLUGIN_KEY);
169
151
  break;
170
152
  }
171
153
  }
172
-
173
154
  tinymce.PluginManager.add('instructure_links', function (ed) {
174
155
  // Register commands
175
156
  ed.addCommand('instructureLinkCreate', () => clickCallback(ed, CREATE_LINK));
@@ -183,10 +164,12 @@ tinymce.PluginManager.add('instructure_links', function (ed) {
183
164
  ed.addCommand('instructureTrayForCourseLinks', () => {
184
165
  ed.selection.select(ed.selection.getNode());
185
166
  return bridge.showTrayForPlugin('course_link_edit', ed.id);
186
- }); // Register shortcuts
167
+ });
187
168
 
188
- ed.addShortcut('Meta+K', '', 'instructureLinkCreate'); // Register menu item
169
+ // Register shortcuts
170
+ ed.addShortcut('Meta+K', '', 'instructureLinkCreate');
189
171
 
172
+ // Register menu item
190
173
  ed.ui.registry.addNestedMenuItem('instructure_links', {
191
174
  text: formatMessage('Link'),
192
175
  icon: 'link',
@@ -201,8 +184,9 @@ tinymce.PluginManager.add('instructure_links', function (ed) {
201
184
  }
202
185
  };
203
186
  })
204
- }); // Register toolbar button
187
+ });
205
188
 
189
+ // Register toolbar button
206
190
  ed.ui.registry.addMenuButton('instructure_links', {
207
191
  tooltip: formatMessage('Links'),
208
192
  icon: 'link',
@@ -212,52 +196,43 @@ tinymce.PluginManager.add('instructure_links', function (ed) {
212
196
  value: item.value,
213
197
  onAction: () => doMenuItem(ed, item.value)
214
198
  }))),
215
-
216
199
  onSetup(api) {
200
+ // @ts-expect-error
217
201
  function handleNodeChange(e) {
218
- if (e !== null && e !== void 0 && e.element) {
202
+ if (e?.element) {
219
203
  api.setActive(!!getAnchorElement(ed, e.element));
220
204
  }
205
+ if (ed.selection) {
206
+ api.setDisabled(!isOKToLink(ed.selection.getContent()));
207
+ }
208
+ }
221
209
 
222
- api.setDisabled(!isOKToLink(ed.selection.getContent()));
223
- } // if the user selects all the content w/in a link and deletes it via the keyboard
210
+ // if the user selects all the content w/in a link and deletes it via the keyboard
224
211
  // make sure the surrounding <a> gets deleted too.
225
-
226
-
227
212
  function deleteEmptyLink() {
228
213
  let node = null;
229
-
230
214
  if (ed.selection.getNode().tagName === 'A') {
231
215
  node = ed.selection.getNode();
232
216
  } else {
233
- var _rng$endContainer$nex, _rng$nextSibling;
234
-
235
217
  // Type checking is disabled here because the code below isn't type safe. The code below
236
218
  // should be updated, specifically rng.endContainer.nextSibling?.tagName
237
219
  const rng = ed.selection.getRng();
238
-
239
- if (rng.commonAncestorContainer === rng.endContainer && ((_rng$endContainer$nex = rng.endContainer.nextSibling) === null || _rng$endContainer$nex === void 0 ? void 0 : _rng$endContainer$nex.tagName) === 'A') {
220
+ if (rng.commonAncestorContainer === rng.endContainer && rng.endContainer.nextSibling?.tagName === 'A') {
240
221
  node = rng.endContainer.nextSibling;
241
- } else if (((_rng$nextSibling = rng.nextSibling) === null || _rng$nextSibling === void 0 ? void 0 : _rng$nextSibling.tagName) === 'A') {
222
+ } else if (rng.nextSibling?.tagName === 'A') {
242
223
  node = rng.nextSibling;
243
224
  }
244
225
  }
245
-
246
226
  if (node) {
247
- var _node$textContent;
248
-
249
227
  if (node.firstElementChild) {
250
228
  return;
251
229
  }
252
-
253
- const txt = (_node$textContent = node.textContent) === null || _node$textContent === void 0 ? void 0 : _node$textContent.trim();
254
-
255
- if ((txt === null || txt === void 0 ? void 0 : txt.length) === 0) {
230
+ const txt = node.textContent?.trim();
231
+ if (txt?.length === 0) {
256
232
  ed.execCommand('Unlink');
257
233
  }
258
234
  }
259
235
  }
260
-
261
236
  setTimeout(handleNodeChange, 0, null);
262
237
  ed.on('NodeChange', handleNodeChange);
263
238
  ed.on('Change', deleteEmptyLink);
@@ -266,14 +241,14 @@ tinymce.PluginManager.add('instructure_links', function (ed) {
266
241
  ed.off('Change', deleteEmptyLink);
267
242
  };
268
243
  }
244
+ });
269
245
 
270
- }); // the context toolbar buttons
271
-
246
+ // the context toolbar buttons
272
247
  ed.ui.registry.addButton('instructure-link-options', {
273
- onAction() {
248
+ onAction(/* buttonApi */
249
+ ) {
274
250
  ed.execCommand(getCommandName(ed.selection.getNode()), false, ed);
275
251
  },
276
-
277
252
  text: formatMessage('Link Options'),
278
253
  tooltip: formatMessage('Show link options')
279
254
  });
@@ -282,7 +257,6 @@ tinymce.PluginManager.add('instructure_links', function (ed) {
282
257
  onAction() {
283
258
  removeAnchorFromSelectedElement(ed);
284
259
  },
285
-
286
260
  text: remButtonLabel
287
261
  });
288
262
  ed.ui.registry.addContextToolbar('instructure-link-toolbar', {
@@ -15,56 +15,101 @@
15
15
  * You should have received a copy of the GNU Affero General Public License along
16
16
  * with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
- import { parse } from 'url';
19
- import formatMessage from '../../../format-message'; // keep in sync with the protocols permitted for <a href>
20
- // as listed in gems/canvas_sanitize/lib/canvas_sanitize/canvas_sanitize.rb
21
18
 
22
- const allowed_protocols = ['ftp:', 'http:', 'https:', 'mailto:', 'tel:', 'skype:']; // weakly validate a url
23
- // return true if valid
24
- // return false if not valid yet, but incomplete
25
- // throw an Error if invalid
19
+ import formatMessage from '../../../format-message';
26
20
 
21
+ // Keep in sync with the protocols permitted for <a href>
22
+ // as listed in gems/canvas_sanitize/lib/canvas_sanitize/canvas_sanitize.rb
23
+ const allowed_protocols = ['ftp:', 'http:', 'https:', 'mailto:', 'tel:', 'skype:'];
24
+
25
+ /**
26
+ * Weakly validate a URL
27
+ * @param {string} url URL to validate
28
+ * @returns {boolean} true if valid, false if not valid yet, but incomplete, throws if invalid
29
+ */
27
30
  export default function validateURL(url) {
28
31
  const href = url.trim();
29
- const parsed = parse(href, false, true);
30
- const protocol = parsed.protocol;
31
32
 
32
- if (!protocol) {
33
- if (parsed.href[0] === ':') {
34
- // ":anything" is invalid
35
- // need this check as an artifact of parse(href)
33
+ // Handle special cases for partial URLs
34
+ if (href === '' || href === 'mailto:' || href === 'tel:' || href === 'skype:') {
35
+ return false;
36
+ }
37
+ if (/^\/\/$/.test(href)) {
38
+ return false;
39
+ }
40
+ try {
41
+ // Handle URLs starting with :// (invalid protocol)
42
+ if (href.startsWith('://')) {
36
43
  throw new Error(formatMessage('Protocol must be ftp, http, https, mailto, skype, tel or may be omitted'));
37
44
  }
38
45
 
39
- if (/^\/\/$/.test(href)) {
40
- // "//" by itself is not a URL yet
41
- return false;
42
- }
43
- } else if (protocol) {
44
- if (!allowed_protocols.includes(protocol)) {
45
- throw new Error(formatMessage('{p} is not a valid protocol which must be ftp, http, https, mailto, skype, tel or may be omitted', {
46
- p: protocol.replace(/:$/, '')
47
- }));
48
- } // ftp, http, https require ://anything
49
-
46
+ // For URLs with protocols, we can use the URL constructor directly
47
+ if (/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(href)) {
48
+ const protocol = href.substring(0, href.indexOf(':') + 1);
49
+ if (!allowed_protocols.includes(protocol)) {
50
+ throw new Error(formatMessage('{p} is not a valid protocol.', {
51
+ p: protocol.slice(0, -1)
52
+ }));
53
+ }
50
54
 
51
- if (/(?:ftp|http|https):\/\/.+/.test(href)) {
52
- return true;
53
- } // allow mailto:address, skype:participant, tel:911 or
54
- // mailto://address, skype://participant, tel://911,
55
+ // For absolute URLs with protocols that require //, validate the format
56
+ if (['http:', 'https:', 'ftp:'].includes(protocol) && !href.startsWith(protocol + '//')) {
57
+ throw new Error(formatMessage('Invalid URL'));
58
+ }
55
59
 
60
+ // Special handling for mailto:, tel:, and skype: URLs
61
+ if (['mailto:', 'tel:', 'skype:'].includes(protocol)) {
62
+ // Consider protocol:// as incomplete for these protocols
63
+ if (href === protocol + '//' || href === protocol + '///') {
64
+ return false;
65
+ }
66
+ return href.length > protocol.length;
67
+ }
56
68
 
57
- if (/(?:mailto|skype|tel):(\/\/)?[^/].*/.test(href)) {
58
- return true;
59
- } // http:/xyzzy is invalid
69
+ // Handle partial URLs
70
+ if (href === protocol || href === protocol + '/' || href === protocol + '//') {
71
+ return false;
72
+ }
60
73
 
74
+ // Try constructing the URL to validate the format
75
+ try {
76
+ if (href.includes('//')) {
77
+ new URL(href);
78
+ }
79
+ return true;
80
+ } catch {
81
+ // If URL construction fails but we have more than just protocol://, consider it valid
82
+ // This allows for test URLs like ftp://host:port/path
83
+ return href.length > protocol.length + 2;
84
+ }
85
+ }
61
86
 
62
- if (/^(?:ftp|http|https):\/{0,1}[^/]/.test(href)) {
63
- throw new Error(formatMessage('Invalid URL'));
87
+ // Handle protocol-relative URLs
88
+ if (href.startsWith('//')) {
89
+ if (href === '//' || href === '///') {
90
+ return false;
91
+ }
92
+ try {
93
+ new URL('http:' + href);
94
+ return true;
95
+ } catch {
96
+ // If URL construction fails but we have more than just //, consider it valid
97
+ return href.length > 2;
98
+ }
64
99
  }
65
100
 
66
- return false;
101
+ // Handle relative paths
102
+ return true;
103
+ } catch (e) {
104
+ if (e instanceof Error) {
105
+ if (e.message.includes('Invalid URL')) {
106
+ if (href.match(/^(https?|ftp):\/?\/?$/)) {
107
+ return false;
108
+ }
109
+ throw new Error(formatMessage('Invalid URL'));
110
+ }
111
+ throw e;
112
+ }
113
+ throw e;
67
114
  }
68
-
69
- return true;
70
115
  }
@@ -15,32 +15,28 @@
15
15
  * You should have received a copy of the GNU Affero General Public License along
16
16
  * with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
+
18
19
  import React from 'react';
19
20
  import ReactDOM from 'react-dom';
20
21
  import Bridge from '../../../bridge';
21
22
  export default function (ed, document) {
22
- return import('./components/Embed').then(_ref => {
23
- let {
24
- Embed
25
- } = _ref;
23
+ return import('./components/Embed').then(({
24
+ Embed
25
+ }) => {
26
26
  let container = document.querySelector('.canvas-rce-embed-container');
27
-
28
27
  if (!container) {
29
28
  container = document.createElement('div');
30
29
  container.className = 'canvas-rce-embed-container';
31
30
  document.body.appendChild(container);
32
31
  }
33
-
34
32
  const handleDismiss = () => {
35
33
  ReactDOM.unmountComponentAtNode(container);
36
34
  ed.focus(false);
37
35
  };
38
-
39
36
  const handleEmbedCode = embedCode => {
40
37
  Bridge.insertEmbedCode(embedCode);
41
38
  };
42
-
43
- ReactDOM.render( /*#__PURE__*/React.createElement(Embed, {
39
+ ReactDOM.render(/*#__PURE__*/React.createElement(Embed, {
44
40
  onSubmit: handleEmbedCode,
45
41
  onDismiss: handleDismiss
46
42
  }), container);
@@ -15,6 +15,7 @@
15
15
  * You should have received a copy of the GNU Affero General Public License along
16
16
  * with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
+
18
19
  import React, { useState } from 'react';
19
20
  import { Modal } from '@instructure/ui-modal';
20
21
  import formatMessage from '../../../../format-message';
@@ -22,17 +23,16 @@ import { Button, CloseButton } from '@instructure/ui-buttons';
22
23
  import { Heading } from '@instructure/ui-heading';
23
24
  import { func } from 'prop-types';
24
25
  import { TextArea } from '@instructure/ui-text-area';
25
- import { instuiPopupMountNode } from '../../../../util/fullscreenHelpers';
26
- export function Embed(_ref) {
27
- let {
28
- onSubmit,
29
- onDismiss
30
- } = _ref;
26
+ import { instuiPopupMountNodeFn } from '../../../../util/fullscreenHelpers';
27
+ export function Embed({
28
+ onSubmit,
29
+ onDismiss
30
+ }) {
31
31
  const [embedCode, setEmbedCode] = useState('');
32
32
  return /*#__PURE__*/React.createElement(Modal, {
33
33
  "data-mce-component": true,
34
34
  label: formatMessage('Embed'),
35
- mountNode: instuiPopupMountNode,
35
+ mountNode: instuiPopupMountNodeFn,
36
36
  size: "medium",
37
37
  onDismiss: onDismiss,
38
38
  open: true,
@@ -15,19 +15,23 @@
15
15
  * You should have received a copy of the GNU Affero General Public License along
16
16
  * with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
+
18
19
  import clickCallback from './clickCallback';
19
20
  import formatMessage from '../../../format-message';
20
- import tinymce from 'tinymce'; // Register plugin
21
+ import tinymce from 'tinymce';
21
22
 
23
+ // Register plugin
22
24
  tinymce.PluginManager.add('instructure_media_embed', function (ed) {
23
- ed.addCommand('instructureMediaEmbed', () => clickCallback(ed, document)); // Register menu item
25
+ ed.addCommand('instructureMediaEmbed', () => clickCallback(ed, document));
24
26
 
27
+ // Register menu item
25
28
  ed.ui.registry.addMenuItem('instructure_media_embed', {
26
29
  text: formatMessage('Embed'),
27
30
  icon: 'embed',
28
31
  onAction: () => ed.execCommand('instructureMediaEmbed')
29
- }); // Register toolbar button
32
+ });
30
33
 
34
+ // Register toolbar button
31
35
  ed.ui.registry.addButton('instructure_media_embed', {
32
36
  tooltip: formatMessage('Embed'),
33
37
  icon: 'embed',
@@ -23,6 +23,7 @@
23
23
  * because figuring this stuff out wasn't trivial and
24
24
  * I didn't want to lose it.
25
25
  ********************************************************* */
26
+
26
27
  import formatMessage from '../../../format-message';
27
28
  import { showFlashAlert } from '../../../common/FlashAlert';
28
29
  export function initPasteMenuCommand(ed, handlePasteOrDrop) {
@@ -39,7 +40,6 @@ export function initPasteMenuCommand(ed, handlePasteOrDrop) {
39
40
  text: formatMessage('Paste'),
40
41
  icon: 'paste',
41
42
  onAction: () => ed.execCommand('instructurePaste'),
42
-
43
43
  onSetup(_api) {
44
44
  // If I add the shortcut, then it overwrites the browsers
45
45
  // built-in keyboard shortcut for paste. we don't want that
@@ -47,15 +47,12 @@ export function initPasteMenuCommand(ed, handlePasteOrDrop) {
47
47
  ed.execCommand('instructurePaste');
48
48
  });
49
49
  }
50
-
51
50
  });
52
-
53
51
  async function handlePasteMenuCommand() {
54
52
  try {
55
53
  const cbitems = await window.navigator.clipboard.read();
56
54
  const cbitem = cbitems[0];
57
55
  const imageType = cbitem.types.find(t => /^image\//.test(t));
58
-
59
56
  if (imageType) {
60
57
  const blob = await cbitem.getType(imageType);
61
58
  const file = new File([blob], imageType.replace('/', '.'), {
@@ -92,7 +89,6 @@ export function initPasteMenuCommand(ed, handlePasteOrDrop) {
92
89
  });
93
90
  } else {
94
91
  const textType = cbitem.types.find(t => /^text\//.test(t));
95
-
96
92
  if (textType) {
97
93
  const blob = await cbitem.getType(textType);
98
94
  const text = await blob.text();
@@ -1,5 +1,3 @@
1
- // @ts-nocheck
2
-
3
1
  /*
4
2
  * Copyright (C) 2022 - present Instructure, Inc.
5
3
  *
@@ -17,6 +15,7 @@
17
15
  * You should have received a copy of the GNU Affero General Public License along
18
16
  * with this program. If not, see <http://www.gnu.org/licenses/>.
19
17
  */
18
+
20
19
  import tinymce from 'tinymce';
21
20
  import bridge from '../../../bridge';
22
21
  import configureStore from '../../../sidebar/store/configureStore';
@@ -34,7 +33,9 @@ const config = {
34
33
  session: null,
35
34
  // null: we haven't gotten it yet, false: we don't need it
36
35
  sessionPromise: null
37
- }; // when UploadFile renders
36
+ };
37
+
38
+ // when UploadFile renders
38
39
  // <StoreProvider {...trayProps}>
39
40
  // {contentProps => {
40
41
  // return (
@@ -43,18 +44,18 @@ const config = {
43
44
  // (We don't seem to have a way to grab an existing store)
44
45
  // So the configureStore and getSession logic gets repeated here for the
45
46
  // automatic file upload when pasting or dropping a file on the RCE
46
-
47
+ // @ts-expect-error
47
48
  function initStore(initProps) {
48
49
  if (config.store === null) {
49
50
  config.store = configureStore(initProps);
50
51
  }
51
-
52
52
  if (config.session === null) {
53
53
  if (initProps.host && initProps.jwt) {
54
54
  config.sessionPromise = getSession(config.store.dispatch, config.store.getState).then(() => {
55
55
  config.session = config.store.getState().session;
56
- }).catch(_err => {
57
- // eslint-disable-next-line no-console
56
+ })
57
+ // @ts-expect-error
58
+ .catch(_err => {
58
59
  console.error('The Paste plugin failed to get canvas session data.');
59
60
  });
60
61
  } else {
@@ -63,12 +64,11 @@ function initStore(initProps) {
63
64
  config.sessionPromise = Promise.resolve();
64
65
  }
65
66
  }
66
-
67
67
  return config.store;
68
68
  }
69
-
70
69
  tinymce.PluginManager.add('instructure_paste', function (editor) {
71
70
  const store = initStore(bridge.trayProps.get(editor));
71
+
72
72
  /**
73
73
  * Starts the file upload (and insertion) process for the given file.
74
74
  *
@@ -76,13 +76,11 @@ tinymce.PluginManager.add('instructure_paste', function (editor) {
76
76
  *
77
77
  * @returns a promise that resolves when the user has made their choice about uploading the file
78
78
  */
79
-
80
79
  async function requestFileInsertion(file) {
81
80
  // it's very doubtful that we won't have retrieved the session data yet,
82
81
  // since it takes a while for the RCE to initialize, but if we haven't
83
82
  // wait until we do to carry on and finish pasting.
84
83
  await config.sessionPromise;
85
-
86
84
  if (config.session === null) {
87
85
  // we failed to get the session and don't know if usage rights are required in this course|group
88
86
  // In all probability, the file upload will fail too, but I feel like we have to do something here.
@@ -90,12 +88,11 @@ tinymce.PluginManager.add('instructure_paste', function (editor) {
90
88
  message: formatMessage('If Usage Rights are required, the file will not publish until enabled in the Files page.'),
91
89
  type: 'info'
92
90
  });
93
- } // even though usage rights might be required by the course, canvas has no place
94
- // on the user to store it. Only Group and Course.
95
-
91
+ }
96
92
 
93
+ // even though usage rights might be required by the course, canvas has no place
94
+ // on the user to store it. Only Group and Course.
97
95
  const requiresUsageRights = config.session.usageRightsRequired && /course|group/.test(bridge.trayProps.get(editor).contextType);
98
-
99
96
  if (requiresUsageRights) {
100
97
  return doFileUpload(editor, document, {
101
98
  accept: file.type,
@@ -114,61 +111,60 @@ tinymce.PluginManager.add('instructure_paste', function (editor) {
114
111
  domObject: file
115
112
  };
116
113
  let tabContext = 'documents';
117
-
118
114
  if (isImage(file.type)) {
119
115
  tabContext = 'images';
120
116
  } else if (isAudioOrVideo(file.type)) {
121
117
  tabContext = 'media';
122
118
  }
123
-
124
119
  store.dispatch(uploadToMediaFolder(tabContext, fileMetaProps));
125
120
  return 'submitted';
126
121
  }
127
122
  }
128
-
129
123
  async function handlePasteOrDrop(event) {
130
- var _bridge$activeEditor, _bridge$activeEditor$, _editor$rceWrapper;
131
-
132
124
  const isPaste = event.type === 'paste';
133
125
  const dataTransfer = isPaste ? event.clipboardData : event.dataTransfer;
134
- const files = Array.from((dataTransfer === null || dataTransfer === void 0 ? void 0 : dataTransfer.files) || []);
135
- const types = (dataTransfer === null || dataTransfer === void 0 ? void 0 : dataTransfer.types) || [];
136
- const isAudioVideoDisabled = (_bridge$activeEditor = bridge.activeEditor()) === null || _bridge$activeEditor === void 0 ? void 0 : (_bridge$activeEditor$ = _bridge$activeEditor.props) === null || _bridge$activeEditor$ === void 0 ? void 0 : _bridge$activeEditor$.instRecordDisabled; // delegate to tiny if there aren't any files to handle
126
+ const files = Array.from(dataTransfer?.files || []);
127
+ const types = dataTransfer?.types || [];
128
+ const isAudioVideoDisabled = bridge.activeEditor()?.props?.instRecordDisabled;
129
+
130
+ // delegate to tiny if there aren't any files to handle
131
+ if (!types.includes('Files')) return;
137
132
 
138
- if (!types.includes('Files')) return; // delegate to tiny if there is Microsoft Word content, because it may contain an image
133
+ // delegate to tiny if there is Microsoft Word content, because it may contain an image
139
134
  // rendering of the content and we don't want to incorrectly paste the image
140
135
  // instead of the actual rich content, which TinyMCE has special handing for
136
+ if (isMicrosoftWordContentInEvent(event)) return;
141
137
 
142
- if (isMicrosoftWordContentInEvent(event)) return; // we're pasting file(s), prevent the default tinymce pasting behavior
138
+ // we're pasting file(s), prevent the default tinymce pasting behavior
139
+ event.preventDefault();
143
140
 
144
- event.preventDefault(); // Ensure the editor has focus, because downstream code requires that it does, and drag-n-drop
141
+ // Ensure the editor has focus, because downstream code requires that it does, and drag-n-drop
145
142
  // events can be started when the editor doesn't have focus.
143
+ if (!editor.hasFocus()) editor.rceWrapper?.focus();
146
144
 
147
- if (!editor.hasFocus()) (_editor$rceWrapper = editor.rceWrapper) === null || _editor$rceWrapper === void 0 ? void 0 : _editor$rceWrapper.focus(); // Checking if we've encountered an issue with file processing for paste events in the browser
145
+ // Checking if we've encountered an issue with file processing for paste events in the browser
148
146
  // Specifically implementing due to this bug in Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1699743
149
147
  // However, there could be other issues that cause this condition so it's a nice safety net regardless
150
-
151
148
  if (isPaste && files.some(file => file.size === 0)) {
149
+ // @ts-expect-error
152
150
  showFlashAlert({
153
151
  message: formatMessage('One or more files failed to paste. Please try uploading or dragging and dropping files.'),
154
152
  type: 'error'
155
153
  });
156
154
  return;
157
155
  }
158
-
159
156
  for (const file of files) {
160
157
  if (isAudioVideoDisabled && isAudioOrVideo(file.type)) {
161
158
  // Skip audio and video files when disabled
162
159
  continue;
163
- } // This will finish once the dialog is closed, if one was created, putting this in a loop allows us
164
- // to show a dialog for each file without them conflicting.
165
- // eslint-disable-next-line no-await-in-loop
160
+ }
166
161
 
162
+ // This will finish once the dialog is closed, if one was created, putting this in a loop allows us
163
+ // to show a dialog for each file without them conflicting.
167
164
 
168
165
  await requestFileInsertion(file);
169
166
  }
170
167
  }
171
-
172
168
  editor.on('paste', handlePasteOrDrop);
173
169
  editor.on('drop', handlePasteOrDrop);
174
170
  });