@instructure/canvas-rce 5.14.2 → 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.
- package/CHANGELOG.md +10 -0
- package/DEVELOPMENT.md +1 -1
- package/es/bridge/Bridge.js +10 -17
- package/es/canvasFileBrowser/FileBrowser.js +10 -19
- package/es/common/FlashAlert.js +8 -11
- package/es/common/fileUrl.js +6 -10
- package/es/common/incremental-loading/LoadMoreButton.js +3 -4
- package/es/common/incremental-loading/LoadingStatus.js +4 -11
- package/es/common/indicate.js +1 -2
- package/es/defaultTinymceConfig.js +1 -1
- package/es/enhance-user-content/doc_previews.js +10 -10
- package/es/enhance-user-content/enhance_user_content.js +4 -7
- package/es/enhance-user-content/external_links.js +1 -1
- package/es/enhance-user-content/instructure_helper.js +11 -17
- package/es/enhance-user-content/mathml.js +15 -27
- package/es/enhance-user-content/media_comment_thumbnail.js +3 -10
- package/es/format-message.js +2 -2
- package/es/index.d.ts +59 -0
- package/es/index.js +3 -5
- package/es/rce/AlertMessageArea.js +15 -16
- package/es/rce/KeyboardShortcutModal.js +2 -2
- package/es/rce/RCE.js +6 -8
- package/es/rce/RCEVariants.js +2 -4
- package/es/rce/RCEWrapper.js +397 -289
- package/es/rce/RCEWrapper.utils.js +131 -0
- package/es/rce/RCEWrapperProps.js +2 -3
- package/es/rce/RceHtmlEditor.js +12 -11
- package/es/rce/ResizeHandle.js +1 -2
- package/es/rce/ShowOnFocusButton/index.js +2 -2
- package/es/rce/StatusBar.js +5 -10
- package/es/rce/contentInsertion.js +1 -2
- package/es/rce/contentRendering.js +6 -5
- package/es/rce/editorLanguage.js +1 -1
- package/es/rce/indicatorRegion.js +1 -2
- package/es/rce/normalizeProps.js +4 -4
- package/es/rce/plugins/instructure_color/clickCallback.js +2 -4
- package/es/rce/plugins/instructure_color/components/ColorPicker.js +17 -22
- package/es/rce/plugins/instructure_color/components/ColorPopup.js +7 -8
- package/es/rce/plugins/instructure_condensed_buttons/ui/list-button.js +4 -10
- package/es/rce/plugins/instructure_condensed_buttons/ui/subscript-superscript-button.js +1 -1
- package/es/rce/plugins/instructure_documents/components/Link.js +1 -2
- package/es/rce/plugins/instructure_equation/EquationEditorModal/index.js +5 -8
- package/es/rce/plugins/instructure_equation/EquationEditorModal/latexTextareaUtil.js +3 -3
- package/es/rce/plugins/instructure_equation/EquationEditorModal/parseLatex.js +3 -3
- package/es/rce/plugins/instructure_equation/EquationEditorToolbar/buttons.js +2 -2
- package/es/rce/plugins/instructure_equation/EquationEditorToolbar/index.js +9 -11
- package/es/rce/plugins/instructure_equation/MathIcon/index.js +3 -4
- package/es/rce/plugins/instructure_equation/mathlive/index.js +167 -16
- package/es/rce/plugins/instructure_fullscreen/plugin.js +0 -2
- package/es/rce/plugins/instructure_icon_maker/clickCallback.js +3 -4
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ColorSection.js +46 -49
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/CreateIconMakerForm.js +9 -10
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Footer.js +10 -11
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Group.js +5 -6
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Header.js +7 -8
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/Course.js +7 -9
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageOptions.js +19 -26
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageSection.js +8 -12
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ModeSelect.js +6 -7
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/MultiColor/index.js +5 -6
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/SVGList.js +6 -7
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/SVGThumbnail.js +8 -10
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/SingleColor/index.js +5 -6
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/SingleColor/svg.js +32 -80
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/Upload.js +7 -8
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/utils.js +4 -5
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/Preview.js +3 -4
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ShapeSection.js +4 -5
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/TextSection.js +4 -5
- package/es/rce/plugins/instructure_icon_maker/components/IconMakerTray.js +22 -29
- package/es/rce/plugins/instructure_icon_maker/registerEditToolbar.js +1 -1
- package/es/rce/plugins/instructure_icon_maker/svg/image.js +5 -7
- package/es/rce/plugins/instructure_icon_maker/svg/index.js +6 -9
- package/es/rce/plugins/instructure_icon_maker/svg/settings.js +17 -20
- package/es/rce/plugins/instructure_icon_maker/svg/shape.js +4 -5
- package/es/rce/plugins/instructure_icon_maker/svg/text.js +28 -32
- package/es/rce/plugins/instructure_icon_maker/svg/utils.js +2 -4
- package/es/rce/plugins/instructure_icon_maker/utils/IconMakerClose.js +2 -3
- package/es/rce/plugins/instructure_icon_maker/utils/iconValidation.js +1 -2
- package/es/rce/plugins/instructure_image/ImageEmbedOptions.js +3 -7
- package/es/rce/plugins/instructure_image/ImageList/Image.js +7 -8
- package/es/rce/plugins/instructure_image/ImageList/index.js +7 -8
- package/es/rce/plugins/instructure_image/ImageOptionsTray/TrayController.js +2 -4
- package/es/rce/plugins/instructure_image/ImageOptionsTray/index.js +3 -3
- package/es/rce/plugins/instructure_image/plugin.js +1 -2
- package/es/rce/plugins/instructure_links/components/AccordionSection.js +7 -8
- package/es/rce/plugins/instructure_links/components/Link.js +61 -65
- package/es/rce/plugins/instructure_links/components/LinkOptionsDialog/LinkOptionsDialogController.js +1 -2
- package/es/rce/plugins/instructure_links/components/LinkOptionsDialog/index.js +2 -2
- package/es/rce/plugins/instructure_links/components/LinkOptionsTray/index.js +2 -2
- package/es/rce/plugins/instructure_links/components/LinkSet.js +28 -37
- package/es/rce/plugins/instructure_links/components/LinksPanel.js +21 -8
- package/es/rce/plugins/instructure_links/components/NoResults.js +6 -7
- package/es/rce/plugins/instructure_links/plugin.js +6 -9
- package/es/rce/plugins/instructure_media_embed/clickCallback.js +3 -4
- package/es/rce/plugins/instructure_media_embed/components/Embed.js +6 -7
- package/es/rce/plugins/instructure_paste/plugin.js +5 -7
- package/es/rce/plugins/instructure_rce_external_tools/ExternalToolsEnv.js +24 -33
- package/es/rce/plugins/instructure_rce_external_tools/RceToolWrapper.js +7 -38
- package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialog.js +30 -29
- package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionDialog.js +3 -4
- package/es/rce/plugins/instructure_rce_external_tools/dialog-helper.js +1 -2
- package/es/rce/plugins/instructure_rce_external_tools/jquery/jquery.dropdownList.js +3 -4
- package/es/rce/plugins/instructure_rce_external_tools/lti11-content-items/RceLti11ContentItem.js +17 -24
- package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/RceLti13ContentItem.js +2 -2
- package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/models/BaseLinkContentItem.js +4 -5
- package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/processEditorContentItems.js +8 -9
- package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/rceLti13ContentItemFromJson.js +0 -1
- package/es/rce/plugins/instructure_rce_external_tools/plugin.js +4 -4
- package/es/rce/plugins/instructure_rce_external_tools/util/externalToolsForToolbar.js +42 -0
- package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.js +5 -10
- package/es/rce/plugins/instructure_record/AudioOptionsTray/index.js +12 -13
- package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.js +8 -15
- package/es/rce/plugins/instructure_record/VideoOptionsTray/index.js +19 -20
- package/es/rce/plugins/instructure_record/clickCallback.js +26 -30
- package/es/rce/plugins/instructure_search_and_replace/clickCallback.js +2 -3
- package/es/rce/plugins/instructure_search_and_replace/components/FindReplaceTray.js +14 -16
- package/es/rce/plugins/instructure_search_and_replace/components/FindReplaceTrayController.js +9 -12
- package/es/rce/plugins/instructure_search_and_replace/plugin.js +1 -2
- package/es/rce/plugins/instructure_wordcount/clickCallback.js +3 -4
- package/es/rce/plugins/instructure_wordcount/components/WordCountModal.js +26 -33
- package/es/rce/plugins/instructure_wordcount/utils/countContent.js +3 -3
- package/es/rce/plugins/instructure_wordcount/utils/tableContent.js +5 -8
- package/es/rce/plugins/shared/CanvasContentTray.js +9 -16
- package/es/rce/plugins/shared/ColorInput.js +22 -25
- package/es/rce/plugins/shared/ConditionalTooltip.js +5 -6
- package/es/rce/plugins/shared/ContentSelection.js +12 -20
- package/es/rce/plugins/shared/DimensionUtils.js +2 -4
- package/es/rce/plugins/shared/EventUtils.js +1 -1
- package/es/rce/plugins/shared/FixedContentTray.js +13 -14
- package/es/rce/plugins/shared/ImageCropper/DirectionRegion.js +3 -4
- package/es/rce/plugins/shared/ImageCropper/Modal.js +12 -13
- package/es/rce/plugins/shared/ImageCropper/Preview.js +11 -13
- package/es/rce/plugins/shared/ImageCropper/controls/CustomNumberInput.js +8 -9
- package/es/rce/plugins/shared/ImageCropper/controls/ResetControls.js +3 -4
- package/es/rce/plugins/shared/ImageCropper/controls/RotationControls.js +4 -5
- package/es/rce/plugins/shared/ImageCropper/controls/ShapeControls.js +7 -11
- package/es/rce/plugins/shared/ImageCropper/controls/ZoomControls.js +4 -5
- package/es/rce/plugins/shared/ImageCropper/controls/index.js +4 -5
- package/es/rce/plugins/shared/ImageCropper/controls/useDebouncedNumericValue.js +13 -15
- package/es/rce/plugins/shared/ImageCropper/imageCropUtils.js +18 -21
- package/es/rce/plugins/shared/ImageCropper/svg/shape.js +4 -5
- package/es/rce/plugins/shared/ImageCropper/svg/utils.js +2 -4
- package/es/rce/plugins/shared/ImageCropper/useKeyMouseEvents.js +1 -4
- package/es/rce/plugins/shared/ImageOptionsForm.js +17 -18
- package/es/rce/plugins/shared/LinkDisplay.js +8 -9
- package/es/rce/plugins/shared/PreviewIcon.js +8 -9
- package/es/rce/plugins/shared/RceFileBrowser.js +2 -3
- package/es/rce/plugins/shared/StoreContext.js +8 -10
- package/es/rce/plugins/shared/StudioLtiSupportUtils.js +5 -6
- package/es/rce/plugins/shared/Upload/CanvasContentPanel.js +6 -7
- package/es/rce/plugins/shared/Upload/CategoryProcessor.js +1 -2
- package/es/rce/plugins/shared/Upload/ComputerPanel.js +11 -14
- package/es/rce/plugins/shared/Upload/PanelFilter.js +7 -8
- package/es/rce/plugins/shared/Upload/UploadFile.js +19 -22
- package/es/rce/plugins/shared/Upload/UploadFileModal.js +28 -34
- package/es/rce/plugins/shared/Upload/UrlPanel.js +4 -5
- package/es/rce/plugins/shared/Upload/UsageRightsSelectBox.js +18 -24
- package/es/rce/plugins/shared/Upload/doFileUpload.js +6 -7
- package/es/rce/plugins/shared/ai_tools/AIResponseModal.js +7 -8
- package/es/rce/plugins/shared/ai_tools/AIToolsTray.js +14 -17
- package/es/rce/plugins/shared/ai_tools/aiicons.js +2 -2
- package/es/rce/plugins/shared/canvasContentUtils.js +1 -2
- package/es/rce/plugins/shared/compressionUtils.js +17 -20
- package/es/rce/plugins/shared/do-fetch-api-effect/doFetchApi.js +12 -15
- package/es/rce/plugins/shared/do-fetch-api-effect/get-cookie.js +1 -1
- package/es/rce/plugins/shared/fileTypeUtils.js +3 -6
- package/es/rce/plugins/shared/round.js +1 -2
- package/es/rce/plugins/shared/trayUtils.js +3 -0
- package/es/rce/plugins/shared/useDataUrl.js +4 -5
- package/es/rce/plugins/tinymce-a11y-checker/components/ColorField.js +2 -2
- package/es/rce/plugins/tinymce-a11y-checker/components/checker.js +8 -10
- package/es/rce/plugins/tinymce-a11y-checker/node-checker.js +1 -3
- package/es/rce/plugins/tinymce-a11y-checker/plugin.js +14 -17
- package/es/rce/plugins/tinymce-a11y-checker/rules/headings-start-at-h2.js +1 -2
- package/es/rce/plugins/tinymce-a11y-checker/rules/large-text-contrast.js +1 -2
- package/es/rce/plugins/tinymce-a11y-checker/rules/small-text-contrast.js +1 -2
- package/es/rce/plugins/tinymce-a11y-checker/utils/dom.js +2 -4
- package/es/rce/plugins/tinymce-a11y-checker/utils/indicate.js +2 -3
- package/es/rce/plugins/tinymce-a11y-checker/utils/rgb-hex.js +1 -4
- package/es/rce/root.js +9 -9
- package/es/rce/tinyRCE.js +1 -0
- package/es/rce/transformContent.js +1 -1
- package/es/rcs/api.js +39 -55
- package/es/rcs/buildError.js +3 -3
- package/es/rcs/fake.js +5 -7
- package/es/sidebar/actions/documents.js +10 -12
- package/es/sidebar/actions/files.js +18 -22
- package/es/sidebar/actions/filter.js +4 -5
- package/es/sidebar/actions/images.js +20 -26
- package/es/sidebar/actions/media.js +15 -18
- package/es/sidebar/actions/session.js +1 -2
- package/es/sidebar/actions/upload.js +26 -37
- package/es/sidebar/containers/sidebarHandlers.js +6 -12
- package/es/sidebar/dragHtml.js +6 -2
- package/es/sidebar/reducers/all_files.js +1 -3
- package/es/sidebar/reducers/collection.js +1 -3
- package/es/sidebar/reducers/collections.js +1 -3
- package/es/sidebar/reducers/documents.js +1 -3
- package/es/sidebar/reducers/files.js +1 -3
- package/es/sidebar/reducers/filter.js +7 -15
- package/es/sidebar/reducers/flickr.js +1 -3
- package/es/sidebar/reducers/folder.js +1 -3
- package/es/sidebar/reducers/folders.js +1 -3
- package/es/sidebar/reducers/images.js +1 -3
- package/es/sidebar/reducers/media.js +1 -3
- package/es/sidebar/reducers/newPageLinkExpanded.js +1 -3
- package/es/sidebar/reducers/noop.js +1 -2
- package/es/sidebar/reducers/rootFolderId.js +1 -3
- package/es/sidebar/reducers/session.js +1 -3
- package/es/sidebar/reducers/ui.js +3 -9
- package/es/sidebar/reducers/upload.js +8 -24
- package/es/sidebar/store/initialState.js +1 -2
- package/es/translations/locales/ar.js +6 -0
- package/es/translations/locales/ca.js +6 -0
- package/es/translations/locales/cy.js +6 -0
- package/es/translations/locales/da-x-k12.js +6 -0
- package/es/translations/locales/da.js +6 -0
- package/es/translations/locales/de.js +6 -0
- package/es/translations/locales/en-AU-x-unimelb.js +6 -0
- package/es/translations/locales/en-GB-x-ukhe.js +6 -0
- package/es/translations/locales/en_AU.js +6 -0
- package/es/translations/locales/en_CA.js +6 -0
- package/es/translations/locales/en_CY.js +6 -0
- package/es/translations/locales/en_GB.js +6 -0
- package/es/translations/locales/es.js +6 -0
- package/es/translations/locales/es_ES.js +6 -0
- package/es/translations/locales/fi.js +6 -0
- package/es/translations/locales/fr.js +6 -0
- package/es/translations/locales/fr_CA.js +6 -0
- package/es/translations/locales/hi.js +6 -0
- package/es/translations/locales/ht.js +6 -0
- package/es/translations/locales/id.js +6 -0
- package/es/translations/locales/is.js +6 -0
- package/es/translations/locales/it.js +6 -0
- package/es/translations/locales/ja.js +6 -0
- package/es/translations/locales/mi.js +6 -0
- package/es/translations/locales/ms.js +6 -0
- package/es/translations/locales/nb-x-k12.js +6 -0
- package/es/translations/locales/nb.js +6 -0
- package/es/translations/locales/nl.js +6 -0
- package/es/translations/locales/pl.js +6 -0
- package/es/translations/locales/pt.js +6 -0
- package/es/translations/locales/pt_BR.js +6 -0
- package/es/translations/locales/ru.js +6 -0
- package/es/translations/locales/sl.js +6 -0
- package/es/translations/locales/sv-x-k12.js +6 -0
- package/es/translations/locales/sv.js +6 -0
- package/es/translations/locales/th.js +6 -0
- package/es/translations/locales/vi.js +6 -0
- package/es/translations/locales/zh-Hans.js +6 -0
- package/es/translations/locales/zh-Hant.js +6 -0
- package/es/translations/locales/zh.js +6 -0
- package/es/translations/locales/zh_HK.js +6 -0
- package/es/util/elem-util.js +1 -1
- package/es/util/file-url-util.js +1 -1
- package/es/util/fullscreenHelpers.js +6 -9
- package/es/util/loadingPlaceholder.js +2 -3
- package/es/util/simpleCache.js +1 -2
- package/es/util/url-util.js +5 -5
- package/eslint.config.js +15 -4
- package/locales/en.json +190 -10
- package/package.json +56 -55
- package/scripts/installTranslations.js +7 -8
- package/tsconfig.json +1 -1
- package/types/format-message-generate-id.d.ts +22 -0
- package/types/js-beautify.d.ts +21 -0
package/es/rce/RCEWrapper.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import _pt from "prop-types";
|
|
2
2
|
/*
|
|
3
3
|
* Copyright (C) 2018 - present Instructure, Inc.
|
|
4
4
|
*
|
|
@@ -21,7 +21,6 @@ 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';
|
|
25
24
|
import { IconKeyboardShortcutsLine } from '@instructure/ui-icons';
|
|
26
25
|
import { Alert } from '@instructure/ui-alerts';
|
|
27
26
|
import { Spinner } from '@instructure/ui-spinner';
|
|
@@ -33,12 +32,12 @@ import getCookie from '../common/getCookie';
|
|
|
33
32
|
import formatMessage from '../format-message';
|
|
34
33
|
import * as contentInsertion from './contentInsertion';
|
|
35
34
|
import indicatorRegion from './indicatorRegion';
|
|
36
|
-
import editorLanguage from './editorLanguage';
|
|
35
|
+
import { editorLanguage } from './editorLanguage';
|
|
37
36
|
import normalizeLocale from './normalizeLocale';
|
|
38
37
|
import { sanitizePlugins } from './sanitizePlugins';
|
|
39
38
|
import RCEGlobals from './RCEGlobals';
|
|
40
39
|
import defaultTinymceConfig from '../defaultTinymceConfig';
|
|
41
|
-
import { FS_CHANGEEVENT, FS_ELEMENT, FS_ENABLED, FS_EXIT, FS_REQUEST,
|
|
40
|
+
import { FS_CHANGEEVENT, FS_ELEMENT, FS_ENABLED, FS_EXIT, FS_REQUEST, instuiPopupMountNodeFn } from '../util/fullscreenHelpers';
|
|
42
41
|
import indicate from '../common/indicate';
|
|
43
42
|
import bridge from '../bridge';
|
|
44
43
|
import CanvasContentTray from './plugins/shared/CanvasContentTray';
|
|
@@ -57,11 +56,13 @@ import contentCSS from './tinymce.oxide.content.min.css';
|
|
|
57
56
|
import { rceWrapperPropTypes } from './RCEWrapperProps';
|
|
58
57
|
import { insertPlaceholder, placeholderInfoFor, removePlaceholder } from '../util/loadingPlaceholder';
|
|
59
58
|
import { transformRceContentForEditing } from './transformContent';
|
|
59
|
+
// @ts-expect-error
|
|
60
60
|
import { IconMoreSolid } from '@instructure/ui-icons/es/svg';
|
|
61
61
|
import EncryptedStorage from '../util/encrypted-storage';
|
|
62
62
|
import buildStyle from './style';
|
|
63
|
-
import { externalToolsForToolbar } from './plugins/instructure_rce_external_tools/RceToolWrapper';
|
|
64
63
|
import { getMenubarForVariant, getMenuForVariant, getToolbarForVariant, getStatusBarFeaturesForVariant } from './RCEVariants';
|
|
64
|
+
import { focusFirstMenuButton, focusToolbar, isElementWithinTable, mergeMenu, mergeMenuItems, mergePlugins, mergeToolbar, parsePluginsToExclude, patchAutosavedContent } from './RCEWrapper.utils';
|
|
65
|
+
import { externalToolsForToolbar } from './plugins/instructure_rce_external_tools/util/externalToolsForToolbar';
|
|
65
66
|
const RestoreAutoSaveModal = /*#__PURE__*/React.lazy(() => import('./RestoreAutoSaveModal'));
|
|
66
67
|
const RceHtmlEditor = /*#__PURE__*/React.lazy(() => import('./RceHtmlEditor'));
|
|
67
68
|
const ASYNC_FOCUS_TIMEOUT = 250;
|
|
@@ -94,24 +95,6 @@ function injectTinySkin() {
|
|
|
94
95
|
document.head.insertBefore(style, beforeMe);
|
|
95
96
|
}
|
|
96
97
|
const editorWrappers = new WeakMap();
|
|
97
|
-
function focusToolbar(el) {
|
|
98
|
-
const $firstToolbarButton = el.querySelector('.tox-tbtn');
|
|
99
|
-
$firstToolbarButton && $firstToolbarButton.focus();
|
|
100
|
-
}
|
|
101
|
-
function focusFirstMenuButton(el) {
|
|
102
|
-
const $firstMenu = el.querySelector('.tox-mbtn');
|
|
103
|
-
$firstMenu && $firstMenu.focus();
|
|
104
|
-
}
|
|
105
|
-
function isElementWithinTable(node) {
|
|
106
|
-
let elem = node;
|
|
107
|
-
while (elem) {
|
|
108
|
-
if (elem.tagName === 'TABLE' || elem.tagName === 'TD' || elem.tagName === 'TH') {
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
elem = elem.parentElement;
|
|
112
|
-
}
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
98
|
|
|
116
99
|
// determines if localStorage is available for our use.
|
|
117
100
|
// see https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
|
|
@@ -147,12 +130,37 @@ class RCEWrapper extends React.Component {
|
|
|
147
130
|
return editorWrappers.get(editor);
|
|
148
131
|
}
|
|
149
132
|
constructor(props) {
|
|
150
|
-
var _this, _window, _window$location, _props$editorOptions, _props$editorOptions2;
|
|
151
133
|
super(props);
|
|
152
|
-
|
|
134
|
+
this._destroyCalled = false;
|
|
135
|
+
this._editorPlaceholderRef = void 0;
|
|
136
|
+
this._elementRef = void 0;
|
|
137
|
+
this._focusRegio = void 0;
|
|
138
|
+
this._focusRegion = void 0;
|
|
139
|
+
this._mceSerializedInitialHtmlCached = void 0;
|
|
140
|
+
this._showOnFocusButton = void 0;
|
|
141
|
+
this._statusBarId = void 0;
|
|
142
|
+
this._textareaEl = void 0;
|
|
143
|
+
this.AIToolsTray = void 0;
|
|
144
|
+
this.editor = void 0;
|
|
145
|
+
this.initialContent = void 0;
|
|
146
|
+
this.intersectionObserver = void 0;
|
|
147
|
+
this.language = void 0;
|
|
148
|
+
this.ltiToolFavorites = void 0;
|
|
149
|
+
this.mutationObserver = void 0;
|
|
150
|
+
this.pendingEventHandlers = void 0;
|
|
151
|
+
this.pluginsToExclude = void 0;
|
|
152
|
+
this.resizeObserver = void 0;
|
|
153
|
+
this.storage = void 0;
|
|
154
|
+
this.variant = void 0;
|
|
155
|
+
this.style = void 0;
|
|
156
|
+
this.insert_code = void 0;
|
|
157
|
+
this.get_code = void 0;
|
|
158
|
+
this.set_code = void 0;
|
|
153
159
|
this.onRemove = () => {
|
|
154
160
|
bridge.detachEditor(this);
|
|
155
|
-
|
|
161
|
+
if (this.props.onRemove) {
|
|
162
|
+
this.props.onRemove(this);
|
|
163
|
+
}
|
|
156
164
|
};
|
|
157
165
|
this.toggleView = newView => {
|
|
158
166
|
// coming from the menubar, we don't have a newView,
|
|
@@ -160,25 +168,34 @@ class RCEWrapper extends React.Component {
|
|
|
160
168
|
let newState;
|
|
161
169
|
switch (this.state.editorView) {
|
|
162
170
|
case WYSIWYG_VIEW:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
171
|
+
{
|
|
172
|
+
newState = {
|
|
173
|
+
editorView: newView || PRETTY_HTML_EDITOR_VIEW
|
|
174
|
+
};
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
167
177
|
case PRETTY_HTML_EDITOR_VIEW:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
178
|
+
{
|
|
179
|
+
newState = {
|
|
180
|
+
editorView: newView || WYSIWYG_VIEW
|
|
181
|
+
};
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
172
184
|
case RAW_HTML_EDITOR_VIEW:
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
185
|
+
{
|
|
186
|
+
newState = {
|
|
187
|
+
editorView: newView || WYSIWYG_VIEW
|
|
188
|
+
};
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
default:
|
|
192
|
+
return;
|
|
176
193
|
}
|
|
194
|
+
// @ts-expect-error
|
|
177
195
|
this.setState(newState);
|
|
178
196
|
this.checkAccessibility();
|
|
179
197
|
if (newView === PRETTY_HTML_EDITOR_VIEW || newView === RAW_HTML_EDITOR_VIEW) {
|
|
180
|
-
|
|
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);
|
|
198
|
+
this.storage?.setItem?.('rce.htmleditor', newView);
|
|
182
199
|
}
|
|
183
200
|
|
|
184
201
|
// Emit view change event
|
|
@@ -192,18 +209,18 @@ class RCEWrapper extends React.Component {
|
|
|
192
209
|
};
|
|
193
210
|
this._onFullscreenChange = event => {
|
|
194
211
|
if (document[FS_ELEMENT]) {
|
|
195
|
-
|
|
212
|
+
// @ts-expect-error
|
|
196
213
|
this.resizeObserver.observe(document[FS_ELEMENT]);
|
|
197
|
-
|
|
214
|
+
window.visualViewport?.addEventListener('resize', this._handleFullscreenResize);
|
|
198
215
|
this._handleFullscreenResize();
|
|
216
|
+
// @ts-expect-error
|
|
199
217
|
this._focusRegion = FocusRegionManager.activateRegion(document[FS_ELEMENT], {
|
|
200
218
|
shouldContainFocus: true
|
|
201
219
|
});
|
|
202
220
|
} else {
|
|
203
|
-
var _window$visualViewpor2;
|
|
204
221
|
event.target.removeEventListener(FS_CHANGEEVENT, this._onFullscreenChange);
|
|
205
222
|
this.resizeObserver.unobserve(event.target);
|
|
206
|
-
|
|
223
|
+
window.visualViewport?.removeEventListener('resize', this._handleFullscreenResize);
|
|
207
224
|
this._setHeight(this.state.fullscreenState.prevHeight);
|
|
208
225
|
if (this._focusRegion) {
|
|
209
226
|
FocusRegionManager.blurRegion(event.target, this._focusRegion.id);
|
|
@@ -212,36 +229,38 @@ class RCEWrapper extends React.Component {
|
|
|
212
229
|
this.focusCurrentView();
|
|
213
230
|
};
|
|
214
231
|
this._handleFullscreenResize = () => {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
this._setHeight(ht - this._getStatusBarHeight());
|
|
232
|
+
const ht = window.visualViewport?.height || document[FS_ELEMENT]?.offsetHeight;
|
|
233
|
+
this._setHeight((ht || 0) - this._getStatusBarHeight());
|
|
218
234
|
};
|
|
219
235
|
this.contentTrayClosing = false;
|
|
220
236
|
this.blurTimer = 0;
|
|
221
|
-
this.handleFocusRCE =
|
|
222
|
-
this.handleFocus(
|
|
237
|
+
this.handleFocusRCE = () => {
|
|
238
|
+
this.handleFocus();
|
|
223
239
|
};
|
|
224
240
|
this.handleBlurRCE = event => {
|
|
225
|
-
var _this$_elementRef$cur;
|
|
226
241
|
if (event.relatedTarget === null) {
|
|
227
242
|
// focus might be moving to tinymce
|
|
228
243
|
this.handleBlur(event);
|
|
229
244
|
}
|
|
230
|
-
if (!
|
|
245
|
+
if (!this._elementRef.current?.contains(event.relatedTarget)) {
|
|
231
246
|
this.handleBlur(event);
|
|
232
247
|
}
|
|
233
248
|
};
|
|
234
|
-
this.handleFocusEditor =
|
|
249
|
+
this.handleFocusEditor = _event => {
|
|
235
250
|
// use .active to put a focus ring around the content area
|
|
236
251
|
// when the editor has focus. This isn't perfect, but it's
|
|
237
252
|
// what we've got for now.
|
|
238
253
|
const ifr = this.iframe;
|
|
239
|
-
|
|
240
|
-
|
|
254
|
+
if (ifr?.parentElement) {
|
|
255
|
+
ifr.parentElement.classList.add('active');
|
|
256
|
+
}
|
|
257
|
+
this.handleFocus();
|
|
241
258
|
};
|
|
242
|
-
this.handleBlurEditor =
|
|
259
|
+
this.handleBlurEditor = event => {
|
|
243
260
|
const ifr = this.iframe;
|
|
244
|
-
|
|
261
|
+
if (ifr?.parentElement) {
|
|
262
|
+
ifr.parentElement.classList.remove('active');
|
|
263
|
+
}
|
|
245
264
|
this.handleBlur(event);
|
|
246
265
|
};
|
|
247
266
|
this.handleKey = event => {
|
|
@@ -249,11 +268,13 @@ class RCEWrapper extends React.Component {
|
|
|
249
268
|
event.preventDefault();
|
|
250
269
|
event.stopPropagation();
|
|
251
270
|
this.setFocusAbilityForHeader(true);
|
|
271
|
+
// @ts-expect-error
|
|
252
272
|
focusFirstMenuButton(this._elementRef.current);
|
|
253
273
|
} else if (event.code === 'F10' && event.altKey) {
|
|
254
274
|
event.preventDefault();
|
|
255
275
|
event.stopPropagation();
|
|
256
276
|
this.setFocusAbilityForHeader(true);
|
|
277
|
+
// @ts-expect-error
|
|
257
278
|
focusToolbar(this._elementRef.current);
|
|
258
279
|
} else if (event.code === 'F8' && event.altKey) {
|
|
259
280
|
event.preventDefault();
|
|
@@ -277,19 +298,23 @@ class RCEWrapper extends React.Component {
|
|
|
277
298
|
this.checkAccessibility();
|
|
278
299
|
};
|
|
279
300
|
this.onInit = (_event, editor) => {
|
|
280
|
-
|
|
301
|
+
// @ts-expect-error
|
|
281
302
|
editor.rceWrapper = this;
|
|
282
303
|
this.editor = editor;
|
|
283
304
|
const textarea = this.editor.getElement();
|
|
284
305
|
|
|
285
306
|
// expected by canvas
|
|
307
|
+
// @ts-expect-error
|
|
286
308
|
textarea.dataset.rich_text = true;
|
|
287
309
|
|
|
288
310
|
// start with the textarea and tinymce in sync
|
|
311
|
+
// @ts-expect-error
|
|
289
312
|
textarea.value = this.getCode();
|
|
290
313
|
textarea.style.height = this.state.height;
|
|
291
314
|
if (document.body.classList.contains('Underline-All-Links__enabled')) {
|
|
292
|
-
this.iframe
|
|
315
|
+
if (this.iframe?.contentDocument) {
|
|
316
|
+
this.iframe.contentDocument.body.classList.add('Underline-All-Links__enabled');
|
|
317
|
+
}
|
|
293
318
|
}
|
|
294
319
|
editor.on('wordCountUpdate', this.onWordCountUpdate);
|
|
295
320
|
// add an aria-label to the application div that wraps RCE
|
|
@@ -303,9 +328,11 @@ class RCEWrapper extends React.Component {
|
|
|
303
328
|
}
|
|
304
329
|
|
|
305
330
|
// Adds a focusout event listener for handling screen reader navigation focus
|
|
306
|
-
const header = this._elementRef.current
|
|
331
|
+
const header = this._elementRef.current?.querySelector('.tox-editor-header');
|
|
307
332
|
if (header) {
|
|
333
|
+
// @ts-expect-error
|
|
308
334
|
header.addEventListener('focusout', e => {
|
|
335
|
+
// @ts-expect-error
|
|
309
336
|
const leavingHeader = !header.contains(e.relatedTarget);
|
|
310
337
|
if (leavingHeader) {
|
|
311
338
|
this.setFocusAbilityForHeader(false);
|
|
@@ -345,35 +372,36 @@ class RCEWrapper extends React.Component {
|
|
|
345
372
|
this._setupSelectionSaving(editor);
|
|
346
373
|
this.checkAccessibility();
|
|
347
374
|
this.fixToolbarKeyboardNavigation();
|
|
348
|
-
|
|
375
|
+
if (this.props.onInitted) {
|
|
376
|
+
this.props.onInitted(editor);
|
|
377
|
+
}
|
|
349
378
|
|
|
350
379
|
// cleans up highlight artifacts from findreplace plugin
|
|
351
380
|
if (this.getRequiredFeatureStatuses().rce_find_replace) {
|
|
352
|
-
editor.on('undo redo',
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
var _editor$plugins, _editor$plugins$searc;
|
|
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();
|
|
381
|
+
editor.on('undo redo', _e => {
|
|
382
|
+
if (editor?.dom?.doc?.getElementsByClassName?.('mce-match-marker')?.length > 0) {
|
|
383
|
+
editor.plugins?.searchreplace?.done();
|
|
357
384
|
}
|
|
358
385
|
});
|
|
359
386
|
}
|
|
360
387
|
};
|
|
361
388
|
this.fixToolbarKeyboardNavigation = () => {
|
|
362
|
-
var _this$_elementRef$cur2;
|
|
363
389
|
// The keyboard navigation config in tinymce for the expanded toolbar is incorrectly configured,
|
|
364
390
|
// and stops at [data-alloy-tabstop] elements.
|
|
365
391
|
// It should be configured to stop on .tox-toolbar__group elements.
|
|
366
392
|
// This workaround removes attribute, thusly causing navigation to work correctly again.
|
|
367
393
|
// For the correct solution, Keying.config should have { selector: '.tox-toolbar__group' }
|
|
368
394
|
// in https://github.com/tinymce/tinymce/blob/develop/modules/alloy/src/main/ts/ephox/alloy/ui/schema/SplitSlidingToolbarSchema.ts
|
|
369
|
-
|
|
395
|
+
this._elementRef.current?.querySelectorAll('.tox-toolbar-overlord button[data-alloy-tabstop]').forEach(it => it.removeAttribute('data-alloy-tabstop'));
|
|
370
396
|
};
|
|
371
397
|
this._setupSelectionSaving = editor => {
|
|
398
|
+
// @ts-expect-error
|
|
372
399
|
let savedSelection = null;
|
|
373
400
|
let selectionWasReset = false;
|
|
374
401
|
let editorHasFocus = false;
|
|
375
402
|
const restoreSelectionIfNecessary = () => {
|
|
376
|
-
|
|
403
|
+
// @ts-expect-error
|
|
404
|
+
if (this.editor && savedSelection && selectionWasReset) {
|
|
377
405
|
this.editor.selection.setRng(savedSelection.range, savedSelection.isForward);
|
|
378
406
|
selectionWasReset = false;
|
|
379
407
|
}
|
|
@@ -381,6 +409,7 @@ class RCEWrapper extends React.Component {
|
|
|
381
409
|
editor.on('blur', () => {
|
|
382
410
|
editorHasFocus = false;
|
|
383
411
|
selectionWasReset = false;
|
|
412
|
+
if (!this.editor) return;
|
|
384
413
|
savedSelection = {
|
|
385
414
|
range: this.editor.selection.getRng().cloneRange(),
|
|
386
415
|
isForward: this.editor.selection.isForward()
|
|
@@ -396,16 +425,16 @@ class RCEWrapper extends React.Component {
|
|
|
396
425
|
selectionWasReset = false;
|
|
397
426
|
});
|
|
398
427
|
editor.on('SelectionChange', () => {
|
|
399
|
-
var _selection$startConta;
|
|
400
428
|
if (editorHasFocus) {
|
|
401
429
|
// We don't care if a selection reset occurs when the editor has focus, the user probably intended that
|
|
402
430
|
// At least they will see the effect
|
|
403
431
|
return;
|
|
404
432
|
}
|
|
433
|
+
if (!this.editor) return;
|
|
405
434
|
const selection = this.editor.selection.normalize();
|
|
406
435
|
|
|
407
436
|
// Detect a browser-reset selection (e.g. From invoking the Find command)
|
|
408
|
-
if (
|
|
437
|
+
if (selection.startContainer?.nodeName === 'BODY' && selection.startContainer === selection.endContainer && selection.startOffset === 0 && selection.endOffset === 0) {
|
|
409
438
|
selectionWasReset = true;
|
|
410
439
|
}
|
|
411
440
|
});
|
|
@@ -414,6 +443,7 @@ class RCEWrapper extends React.Component {
|
|
|
414
443
|
});
|
|
415
444
|
editor.on('ExecCommand', (/* event */
|
|
416
445
|
) => {
|
|
446
|
+
if (!this.editor) return;
|
|
417
447
|
// Commands may have modified the selection, we need to recapture it
|
|
418
448
|
savedSelection = {
|
|
419
449
|
range: this.editor.selection.getRng().cloneRange(),
|
|
@@ -435,14 +465,15 @@ class RCEWrapper extends React.Component {
|
|
|
435
465
|
// We'll compare just the text of the autosave content, since
|
|
436
466
|
// Canvas is prone to swizzling images and iframes which will
|
|
437
467
|
// make the editor content and autosave content never match up
|
|
438
|
-
const editorContent =
|
|
468
|
+
const editorContent = patchAutosavedContent(editor.getContent({
|
|
439
469
|
no_events: true
|
|
440
470
|
}), true);
|
|
441
|
-
const autosavedContent =
|
|
471
|
+
const autosavedContent = patchAutosavedContent(autosaved.content, true);
|
|
442
472
|
if (autosavedContent !== editorContent) {
|
|
443
473
|
this.setState({
|
|
444
474
|
confirmAutoSave: true,
|
|
445
|
-
|
|
475
|
+
// @ts-expect-error
|
|
476
|
+
autoSavedContent: patchAutosavedContent(autosaved.content)
|
|
446
477
|
});
|
|
447
478
|
} else {
|
|
448
479
|
this.storage.removeItem(this.autoSaveKey);
|
|
@@ -450,22 +481,21 @@ class RCEWrapper extends React.Component {
|
|
|
450
481
|
}
|
|
451
482
|
} catch (ex) {
|
|
452
483
|
// log and ignore
|
|
453
|
-
|
|
484
|
+
|
|
454
485
|
console.error('Failed initializing rce autosave', ex);
|
|
455
486
|
}
|
|
456
487
|
}
|
|
457
488
|
};
|
|
458
|
-
this.cleanupAutoSave =
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
const expiry = deleteAll ? Date.now() : Date.now() - _this.props.autosave.maxAge;
|
|
489
|
+
this.cleanupAutoSave = (deleteAll = false) => {
|
|
490
|
+
if (this.storage) {
|
|
491
|
+
const expiry = deleteAll ? Date.now() : Date.now() - (this.props.autosave?.maxAge || 0);
|
|
462
492
|
let i = 0;
|
|
463
493
|
let key;
|
|
464
|
-
while (key =
|
|
494
|
+
while (key = this.storage.key(i++)) {
|
|
465
495
|
if (/^rceautosave:/.test(key)) {
|
|
466
|
-
const autosaved =
|
|
496
|
+
const autosaved = this.getAutoSaved(key);
|
|
467
497
|
if (autosaved && autosaved.autosaveTimestamp < expiry) {
|
|
468
|
-
|
|
498
|
+
this.storage.removeItem(key);
|
|
469
499
|
}
|
|
470
500
|
}
|
|
471
501
|
}
|
|
@@ -479,15 +509,15 @@ class RCEWrapper extends React.Component {
|
|
|
479
509
|
if (ans) {
|
|
480
510
|
editor.setContent(this.state.autoSavedContent, {});
|
|
481
511
|
}
|
|
512
|
+
// @ts-expect-error
|
|
482
513
|
this.storage.removeItem(this.autoSaveKey);
|
|
483
514
|
});
|
|
484
515
|
// let the content be restored
|
|
485
516
|
debounce(this.checkAccessibility, 1000)();
|
|
486
517
|
};
|
|
487
|
-
this.doAutoSave =
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
const editor = _this.mceInstance();
|
|
518
|
+
this.doAutoSave = (e, retry = false) => {
|
|
519
|
+
if (this.storage) {
|
|
520
|
+
const editor = this.mceInstance();
|
|
491
521
|
// if the editor is empty don't save
|
|
492
522
|
if (editor.dom.isEmpty(editor.getBody())) {
|
|
493
523
|
return;
|
|
@@ -496,20 +526,21 @@ class RCEWrapper extends React.Component {
|
|
|
496
526
|
no_events: true
|
|
497
527
|
});
|
|
498
528
|
try {
|
|
499
|
-
|
|
529
|
+
this.storage.setItem(this.autoSaveKey, content);
|
|
500
530
|
} catch (ex) {
|
|
501
531
|
if (!retry) {
|
|
502
532
|
// probably failed because there's not enough space
|
|
503
533
|
// delete up all the other entries and try again
|
|
504
|
-
|
|
505
|
-
|
|
534
|
+
this.cleanupAutoSave(true);
|
|
535
|
+
this.doAutoSave(e, true);
|
|
506
536
|
} else {
|
|
507
|
-
console.error('Autosave failed:', ex);
|
|
537
|
+
console.error('Autosave failed:', ex);
|
|
508
538
|
}
|
|
509
539
|
}
|
|
510
540
|
}
|
|
511
541
|
};
|
|
512
542
|
this.onWordCountUpdate = e => {
|
|
543
|
+
if (!this.editor) return;
|
|
513
544
|
const shouldIgnore = countShouldIgnore(this.editor, 'body', 'words');
|
|
514
545
|
const updatedCount = e.wordCount.words - shouldIgnore;
|
|
515
546
|
this.setState(state => {
|
|
@@ -522,14 +553,15 @@ class RCEWrapper extends React.Component {
|
|
|
522
553
|
};
|
|
523
554
|
this.onNodeChange = e => {
|
|
524
555
|
// This is basically copied out of the tinymce silver theme code for the status bar
|
|
525
|
-
const path = e.parents.filter(p => p.nodeName !== 'BR' && !p.getAttribute('data-mce-bogus') && p.getAttribute('data-mce-type') !== 'bookmark')
|
|
556
|
+
const path = e.parents.filter(p => p.nodeName !== 'BR' && !p.getAttribute('data-mce-bogus') && p.getAttribute('data-mce-type') !== 'bookmark')
|
|
557
|
+
// @ts-expect-error
|
|
558
|
+
.map(p => p.nodeName.toLowerCase()).reverse();
|
|
526
559
|
this.setState({
|
|
527
560
|
path
|
|
528
561
|
});
|
|
529
562
|
};
|
|
530
563
|
this.onEditorChange = (content, _editor) => {
|
|
531
|
-
|
|
532
|
-
(_this$props$onContent = (_this$props2 = this.props).onContentChange) === null || _this$props$onContent === void 0 ? void 0 : _this$props$onContent.call(_this$props2, content);
|
|
564
|
+
this.props.onContentChange?.(content);
|
|
533
565
|
// check accessibility when clearing the editor,
|
|
534
566
|
// all other times should be checked by handleInputChange
|
|
535
567
|
if (content === '') {
|
|
@@ -542,11 +574,14 @@ class RCEWrapper extends React.Component {
|
|
|
542
574
|
const container = editor.getContainer();
|
|
543
575
|
if (!container) return;
|
|
544
576
|
const currentContainerHeight = Number.parseInt(container.style.height, 10);
|
|
545
|
-
if (isNaN(currentContainerHeight)) return;
|
|
577
|
+
if (isNaN(currentContainerHeight)) return;
|
|
546
578
|
const modifiedHeight = currentContainerHeight + coordinates.deltaY;
|
|
547
579
|
const newHeight = `${modifiedHeight}px`;
|
|
548
580
|
container.style.height = newHeight;
|
|
549
|
-
this.getTextarea()
|
|
581
|
+
const textarea = this.getTextarea();
|
|
582
|
+
if (textarea) {
|
|
583
|
+
textarea.style.height = newHeight;
|
|
584
|
+
}
|
|
550
585
|
this.setState({
|
|
551
586
|
height: newHeight
|
|
552
587
|
});
|
|
@@ -557,7 +592,7 @@ class RCEWrapper extends React.Component {
|
|
|
557
592
|
this.onA11yChecker = triggerElementId => {
|
|
558
593
|
const editor = this.mceInstance();
|
|
559
594
|
editor.execCommand('openAccessibilityChecker', false, {
|
|
560
|
-
mountNode:
|
|
595
|
+
mountNode: instuiPopupMountNodeFn,
|
|
561
596
|
triggerElementId,
|
|
562
597
|
onFixError: errors => {
|
|
563
598
|
this.setState({
|
|
@@ -571,6 +606,7 @@ class RCEWrapper extends React.Component {
|
|
|
571
606
|
this.checkAccessibility = () => {
|
|
572
607
|
const editor = this.mceInstance();
|
|
573
608
|
editor.execCommand('checkAccessibility', false, {
|
|
609
|
+
// @ts-expect-error
|
|
574
610
|
done: errors => {
|
|
575
611
|
this.setState({
|
|
576
612
|
a11yErrorsCount: errors.length
|
|
@@ -583,6 +619,7 @@ class RCEWrapper extends React.Component {
|
|
|
583
619
|
this.openKBShortcutModal = () => {
|
|
584
620
|
this.setState({
|
|
585
621
|
KBShortcutModalOpen: true,
|
|
622
|
+
// @ts-expect-error
|
|
586
623
|
KBShortcutFocusReturn: document.activeElement
|
|
587
624
|
});
|
|
588
625
|
};
|
|
@@ -595,27 +632,27 @@ class RCEWrapper extends React.Component {
|
|
|
595
632
|
if (this.state.KBShortcutFocusReturn === this.iframe) {
|
|
596
633
|
// launched using a kb shortcut
|
|
597
634
|
// the iframe has focus so we need to forward it on to tinymce editor
|
|
598
|
-
this.editor
|
|
635
|
+
if (this.editor) {
|
|
636
|
+
this.editor.focus(false);
|
|
637
|
+
}
|
|
599
638
|
} else if (this.state.KBShortcutFocusReturn === document.getElementById(`show-on-focus-btn-${this.id}`)) {
|
|
600
|
-
var _this$_showOnFocusBut;
|
|
601
639
|
// launched from showOnFocus button
|
|
602
640
|
// edge case where focusing KBShortcutFocusReturn doesn't work
|
|
603
|
-
|
|
641
|
+
this._showOnFocusButton?.focus();
|
|
604
642
|
} else {
|
|
605
|
-
var _this$state$KBShortcu;
|
|
606
643
|
// launched from kb shortcut button on status bar
|
|
607
|
-
|
|
644
|
+
this.state.KBShortcutFocusReturn?.focus();
|
|
608
645
|
}
|
|
609
646
|
};
|
|
610
647
|
this.handleAIClick = () => {
|
|
611
648
|
import('./plugins/shared/ai_tools').then(module => {
|
|
649
|
+
// @ts-expect-error
|
|
612
650
|
this.AIToolsTray = module.AIToolsTray;
|
|
613
651
|
this.setState({
|
|
614
652
|
AIToolsOpen: true,
|
|
615
653
|
AITToolsFocusReturn: document.activeElement
|
|
616
654
|
});
|
|
617
655
|
}).catch(ex => {
|
|
618
|
-
// eslint-disable-next-line no-console
|
|
619
656
|
console.error('Failed loading the AIToolsTray', ex);
|
|
620
657
|
});
|
|
621
658
|
};
|
|
@@ -628,16 +665,17 @@ class RCEWrapper extends React.Component {
|
|
|
628
665
|
if (this.state.AITToolsFocusReturn === this.iframe) {
|
|
629
666
|
// launched using a kb shortcut
|
|
630
667
|
// the iframe has focus so we need to forward it on to tinymce editor
|
|
631
|
-
this.editor
|
|
668
|
+
if (this.editor) {
|
|
669
|
+
this.editor.focus(false);
|
|
670
|
+
}
|
|
632
671
|
} else if (this.state.AITToolsFocusReturn === document.getElementById(`show-on-focus-btn-${this.id}`)) {
|
|
633
|
-
var _this$_showOnFocusBut2;
|
|
634
672
|
// launched from showOnFocus button
|
|
635
673
|
// edge case where focusing KBShortcutFocusReturn doesn't work
|
|
636
|
-
|
|
674
|
+
this._showOnFocusButton?.focus();
|
|
637
675
|
} else {
|
|
638
|
-
var _this$state$AITToolsF;
|
|
639
676
|
// launched from kb shortcut button on status bar
|
|
640
|
-
|
|
677
|
+
// @ts-expect-error
|
|
678
|
+
this.state.AITToolsFocusReturn?.focus();
|
|
641
679
|
}
|
|
642
680
|
};
|
|
643
681
|
this.handleInsertAIContent = content => {
|
|
@@ -666,7 +704,7 @@ class RCEWrapper extends React.Component {
|
|
|
666
704
|
};
|
|
667
705
|
this.setFocusAbilityForHeader = focusable => {
|
|
668
706
|
// Sets aria-hidden to prevent screen readers focus in RCE menus and toolbar
|
|
669
|
-
const header = this._elementRef.current
|
|
707
|
+
const header = this._elementRef.current?.querySelector('.tox-editor-header');
|
|
670
708
|
if (header) {
|
|
671
709
|
header.setAttribute('aria-hidden', focusable ? 'false' : 'true');
|
|
672
710
|
}
|
|
@@ -674,6 +712,7 @@ class RCEWrapper extends React.Component {
|
|
|
674
712
|
this.handleTextareaChange = () => {
|
|
675
713
|
if (this.isHidden()) {
|
|
676
714
|
this.setCode(this.textareaValue());
|
|
715
|
+
// @ts-expect-error
|
|
677
716
|
this.doAutoSave();
|
|
678
717
|
}
|
|
679
718
|
};
|
|
@@ -717,21 +756,25 @@ class RCEWrapper extends React.Component {
|
|
|
717
756
|
this.insert_code = this.insertCode;
|
|
718
757
|
|
|
719
758
|
// test override points
|
|
759
|
+
// @ts-expect-error
|
|
720
760
|
this.indicator = false;
|
|
721
761
|
this._elementRef = /*#__PURE__*/React.createRef();
|
|
722
762
|
this._editorPlaceholderRef = /*#__PURE__*/React.createRef();
|
|
763
|
+
// @ts-expect-error
|
|
723
764
|
this._prettyHtmlEditorRef = /*#__PURE__*/React.createRef();
|
|
765
|
+
// @ts-expect-error
|
|
724
766
|
this._showOnFocusButton = null;
|
|
725
767
|
|
|
726
768
|
// Process initial content
|
|
769
|
+
// @ts-expect-error
|
|
727
770
|
this.initialContent = this.getRequiredFeatureStatuses().rce_transform_loaded_content ? transformRceContentForEditing(this.props.defaultContent, {
|
|
728
|
-
origin: this.props.canvasOrigin ||
|
|
771
|
+
origin: this.props.canvasOrigin || window?.location?.origin
|
|
729
772
|
}) : this.props.defaultContent;
|
|
730
773
|
injectTinySkin();
|
|
731
774
|
|
|
732
775
|
// FWIW, for historic reaasons, the height does not include the
|
|
733
776
|
// height of the status bar (which used to be tinymce's)
|
|
734
|
-
let _ht =
|
|
777
|
+
let _ht = props.editorOptions?.height || DEFAULT_RCE_HEIGHT;
|
|
735
778
|
if (!Number.isNaN(_ht)) {
|
|
736
779
|
_ht = `${_ht}px`;
|
|
737
780
|
}
|
|
@@ -747,9 +790,12 @@ class RCEWrapper extends React.Component {
|
|
|
747
790
|
announcement: null,
|
|
748
791
|
confirmAutoSave: false,
|
|
749
792
|
autoSavedContent: '',
|
|
793
|
+
// @ts-expect-error
|
|
750
794
|
id: this.props.id || this.props.textareaId || `${uid('rce', 2)}`,
|
|
795
|
+
// @ts-expect-error
|
|
751
796
|
height: _ht,
|
|
752
797
|
fullscreenState: {
|
|
798
|
+
// @ts-expect-error
|
|
753
799
|
prevHeight: _ht
|
|
754
800
|
},
|
|
755
801
|
a11yErrorsCount: 0,
|
|
@@ -758,16 +804,24 @@ class RCEWrapper extends React.Component {
|
|
|
758
804
|
};
|
|
759
805
|
this._statusBarId = `${this.state.id}_statusbar`;
|
|
760
806
|
this.pendingEventHandlers = [];
|
|
807
|
+
|
|
808
|
+
// @ts-expect-error
|
|
761
809
|
this.ltiToolFavorites = externalToolsForToolbar(this.props.ltiTools).map(e => `instructure_external_button_${e.id}`);
|
|
762
|
-
this.pluginsToExclude = parsePluginsToExclude(
|
|
810
|
+
this.pluginsToExclude = parsePluginsToExclude(props.editorOptions?.plugins || []);
|
|
811
|
+
|
|
812
|
+
// @ts-expect-error
|
|
763
813
|
this.resourceType = props.resourceType;
|
|
814
|
+
// @ts-expect-error
|
|
764
815
|
this.resourceId = props.resourceId;
|
|
816
|
+
|
|
817
|
+
// @ts-expect-error
|
|
765
818
|
this.variant = window.RCE_VARIANT || props.variant; // to facilitate testing
|
|
766
819
|
|
|
820
|
+
// @ts-expect-error
|
|
767
821
|
this.tinymceInitOptions = this.wrapOptions(props.editorOptions);
|
|
768
822
|
alertHandler.alertFunc = this.addAlert;
|
|
769
823
|
this.handleContentTrayClosing = this.handleContentTrayClosing.bind(this);
|
|
770
|
-
this.resizeObserver = new ResizeObserver(
|
|
824
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
771
825
|
this._handleFullscreenResize();
|
|
772
826
|
});
|
|
773
827
|
this.AIToolsTray = undefined;
|
|
@@ -783,7 +837,6 @@ class RCEWrapper extends React.Component {
|
|
|
783
837
|
if (tinyauxlist.length) {
|
|
784
838
|
const myaux = tinyauxlist[tinyauxlist.length - 1];
|
|
785
839
|
if (myaux.id) {
|
|
786
|
-
// eslint-disable-next-line no-console
|
|
787
840
|
console.error('Unexpected ID on my tox-tinymce-aux element');
|
|
788
841
|
}
|
|
789
842
|
myaux.id = `tinyaux-${this.id}`;
|
|
@@ -815,7 +868,9 @@ class RCEWrapper extends React.Component {
|
|
|
815
868
|
getRequiredConfigValues() {
|
|
816
869
|
return {
|
|
817
870
|
locale: normalizeLocale(this.props.language),
|
|
871
|
+
// @ts-expect-error
|
|
818
872
|
flashAlertTimeout: this.props.flashAlertTimeout,
|
|
873
|
+
// @ts-expect-error
|
|
819
874
|
timezone: this.props.timezone
|
|
820
875
|
};
|
|
821
876
|
}
|
|
@@ -824,7 +879,9 @@ class RCEWrapper extends React.Component {
|
|
|
824
879
|
}
|
|
825
880
|
getResourceIdentifiers() {
|
|
826
881
|
return {
|
|
882
|
+
// @ts-expect-error
|
|
827
883
|
resourceType: this.resourceType,
|
|
884
|
+
// @ts-expect-error
|
|
828
885
|
resourceId: this.resourceId
|
|
829
886
|
};
|
|
830
887
|
}
|
|
@@ -834,6 +891,8 @@ class RCEWrapper extends React.Component {
|
|
|
834
891
|
getCode() {
|
|
835
892
|
return this.isHidden() ? this.textareaValue() : this.mceInstance().getContent();
|
|
836
893
|
}
|
|
894
|
+
|
|
895
|
+
// @ts-expect-error
|
|
837
896
|
checkReadyToGetCode(promptFunc) {
|
|
838
897
|
let status = true;
|
|
839
898
|
// Check for remaining placeholders
|
|
@@ -843,8 +902,7 @@ class RCEWrapper extends React.Component {
|
|
|
843
902
|
return status;
|
|
844
903
|
}
|
|
845
904
|
setCode(newContent) {
|
|
846
|
-
|
|
847
|
-
(_this$mceInstance = this.mceInstance()) === null || _this$mceInstance === void 0 ? void 0 : _this$mceInstance.setContent(newContent);
|
|
905
|
+
this.mceInstance()?.setContent(newContent);
|
|
848
906
|
}
|
|
849
907
|
|
|
850
908
|
// This function is called imperatively by the page that renders the RCE.
|
|
@@ -865,7 +923,9 @@ class RCEWrapper extends React.Component {
|
|
|
865
923
|
return;
|
|
866
924
|
}
|
|
867
925
|
const editor = this.mceInstance();
|
|
926
|
+
// @ts-expect-error
|
|
868
927
|
if (this.indicator) {
|
|
928
|
+
// @ts-expect-error
|
|
869
929
|
this.indicator(editor, element);
|
|
870
930
|
} else if (!this.isHidden()) {
|
|
871
931
|
indicate(indicatorRegion(editor, element));
|
|
@@ -889,8 +949,17 @@ class RCEWrapper extends React.Component {
|
|
|
889
949
|
if (height) {
|
|
890
950
|
const ifr = this.iframe;
|
|
891
951
|
if (ifr) {
|
|
892
|
-
|
|
893
|
-
const
|
|
952
|
+
// @ts-expect-error
|
|
953
|
+
const editor_body_style = ifr.contentWindow.getComputedStyle(
|
|
954
|
+
// @ts-expect-error
|
|
955
|
+
this.iframe.contentDocument.body);
|
|
956
|
+
const editor_ht =
|
|
957
|
+
// @ts-expect-error
|
|
958
|
+
ifr.contentDocument.body.clientHeight -
|
|
959
|
+
// @ts-expect-error
|
|
960
|
+
parseInt(editor_body_style['padding-top'], 10) -
|
|
961
|
+
// @ts-expect-error
|
|
962
|
+
parseInt(editor_body_style['padding-bottom'], 10);
|
|
894
963
|
const para_margin_ht = 24;
|
|
895
964
|
const reserve_ht = Math.ceil(height + para_margin_ht);
|
|
896
965
|
if (reserve_ht > editor_ht) {
|
|
@@ -905,15 +974,20 @@ class RCEWrapper extends React.Component {
|
|
|
905
974
|
if (!element || element.tagName !== 'IMG') {
|
|
906
975
|
return;
|
|
907
976
|
}
|
|
977
|
+
// @ts-expect-error
|
|
908
978
|
if (!element.complete) {
|
|
979
|
+
// @ts-expect-error
|
|
909
980
|
element.onload = () => this.checkImageLoadError(element);
|
|
910
981
|
return;
|
|
911
982
|
}
|
|
912
983
|
// checking naturalWidth in a future event loop run prevents a race
|
|
913
984
|
// condition between the onload callback and naturalWidth being set.
|
|
914
985
|
setTimeout(() => {
|
|
986
|
+
// @ts-expect-error
|
|
915
987
|
if (element.naturalWidth === 0) {
|
|
988
|
+
// @ts-expect-error
|
|
916
989
|
element.style.border = '1px solid #000';
|
|
990
|
+
// @ts-expect-error
|
|
917
991
|
element.style.padding = '2px';
|
|
918
992
|
}
|
|
919
993
|
}, 0);
|
|
@@ -964,12 +1038,11 @@ class RCEWrapper extends React.Component {
|
|
|
964
1038
|
}
|
|
965
1039
|
}
|
|
966
1040
|
insertImage(image) {
|
|
967
|
-
var _element$nextSibling, _element$nextSibling$;
|
|
968
1041
|
const editor = this.mceInstance();
|
|
969
1042
|
const element = contentInsertion.insertImage(editor, image, this.getCanvasUrl());
|
|
970
1043
|
|
|
971
1044
|
// Removes TinyMCE's caret text if exists.
|
|
972
|
-
if (element
|
|
1045
|
+
if (element?.nextSibling?.data?.startsWith('\xA0' /* nbsp */)) {
|
|
973
1046
|
element.nextSibling.splitText(1);
|
|
974
1047
|
element.nextSibling.remove();
|
|
975
1048
|
}
|
|
@@ -1027,10 +1100,12 @@ class RCEWrapper extends React.Component {
|
|
|
1027
1100
|
}
|
|
1028
1101
|
|
|
1029
1102
|
// since we may defer rendering tinymce, queue up any tinymce event handlers
|
|
1103
|
+
// @ts-expect-error
|
|
1030
1104
|
tinymceOn(tinymceEventName, handler) {
|
|
1031
1105
|
if (this.state.shouldShowEditor) {
|
|
1032
1106
|
this.mceInstance().on(tinymceEventName, handler);
|
|
1033
1107
|
} else {
|
|
1108
|
+
// @ts-expect-error
|
|
1034
1109
|
this.pendingEventHandlers.push({
|
|
1035
1110
|
name: tinymceEventName,
|
|
1036
1111
|
handler
|
|
@@ -1044,39 +1119,43 @@ class RCEWrapper extends React.Component {
|
|
|
1044
1119
|
const editors = this.props.tinymce.editors || [];
|
|
1045
1120
|
return editors.filter(ed => ed.id === this.props.textareaId)[0];
|
|
1046
1121
|
}
|
|
1047
|
-
|
|
1122
|
+
|
|
1123
|
+
// @ts-expect-error
|
|
1124
|
+
onTinyMCEInstance(command, ...args) {
|
|
1048
1125
|
const editor = this.mceInstance();
|
|
1049
1126
|
if (editor) {
|
|
1050
1127
|
if (command === 'mceRemoveEditor') {
|
|
1051
1128
|
editor.execCommand('mceNewDocument');
|
|
1052
1129
|
} // makes sure content can't persist past removal
|
|
1053
|
-
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
1054
|
-
args[_key - 1] = arguments[_key];
|
|
1055
|
-
}
|
|
1056
1130
|
editor.execCommand(command, false, ...args);
|
|
1057
1131
|
}
|
|
1058
1132
|
}
|
|
1059
1133
|
destroy() {
|
|
1060
1134
|
this._destroyCalled = true;
|
|
1061
1135
|
this.unhandleTextareaChange();
|
|
1062
|
-
|
|
1136
|
+
if (this.props.handleUnmount) {
|
|
1137
|
+
this.props.handleUnmount();
|
|
1138
|
+
}
|
|
1063
1139
|
}
|
|
1064
1140
|
getTextarea() {
|
|
1065
|
-
|
|
1141
|
+
const node = this.props.textareaId && document.getElementById(this.props.textareaId);
|
|
1142
|
+
if (node instanceof HTMLTextAreaElement) {
|
|
1143
|
+
return node;
|
|
1144
|
+
}
|
|
1145
|
+
return null;
|
|
1066
1146
|
}
|
|
1067
1147
|
textareaValue() {
|
|
1068
|
-
return this.getTextarea()
|
|
1148
|
+
return this.getTextarea()?.value || '';
|
|
1069
1149
|
}
|
|
1070
1150
|
get id() {
|
|
1071
1151
|
return this.state.id;
|
|
1072
1152
|
}
|
|
1073
1153
|
getHtmlEditorStorage() {
|
|
1074
|
-
var _this$storage2, _this$storage2$getIte, _this$storage2$getIte2;
|
|
1075
1154
|
const cookieValue = getCookie('rce.htmleditor');
|
|
1076
1155
|
if (cookieValue) {
|
|
1077
1156
|
document.cookie = `rce.htmleditor=${cookieValue};path=/;max-age=0`;
|
|
1078
1157
|
}
|
|
1079
|
-
const value = cookieValue ||
|
|
1158
|
+
const value = cookieValue || this.storage?.getItem?.('rce.htmleditor')?.content;
|
|
1080
1159
|
return value === RAW_HTML_EDITOR_VIEW || value === PRETTY_HTML_EDITOR_VIEW ? value : PRETTY_HTML_EDITOR_VIEW;
|
|
1081
1160
|
}
|
|
1082
1161
|
_isFullscreen() {
|
|
@@ -1090,14 +1169,17 @@ class RCEWrapper extends React.Component {
|
|
|
1090
1169
|
const tinymenuhost = this._myTinymceAuxDiv();
|
|
1091
1170
|
if (tinymenuhost) {
|
|
1092
1171
|
tinymenuhost.remove();
|
|
1093
|
-
this._elementRef.current
|
|
1172
|
+
this._elementRef.current?.appendChild(tinymenuhost);
|
|
1094
1173
|
}
|
|
1095
|
-
this._elementRef.current
|
|
1096
|
-
this.
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1174
|
+
this._elementRef.current?.addEventListener(FS_CHANGEEVENT, this._onFullscreenChange);
|
|
1175
|
+
if (typeof this._elementRef.current?.offsetHeight === 'number') {
|
|
1176
|
+
this.setState({
|
|
1177
|
+
fullscreenState: {
|
|
1178
|
+
prevHeight: this._elementRef.current.offsetHeight - this._getStatusBarHeight()
|
|
1179
|
+
}
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
// @ts-expect-error
|
|
1101
1183
|
this._elementRef.current[FS_REQUEST]();
|
|
1102
1184
|
}
|
|
1103
1185
|
_exitFullscreen() {
|
|
@@ -1110,10 +1192,14 @@ class RCEWrapper extends React.Component {
|
|
|
1110
1192
|
document[FS_EXIT]();
|
|
1111
1193
|
}
|
|
1112
1194
|
}
|
|
1195
|
+
|
|
1196
|
+
// @ts-expect-error
|
|
1197
|
+
|
|
1113
1198
|
_getStatusBarHeight() {
|
|
1114
1199
|
// the height prop is the height of the editor and does not include
|
|
1115
1200
|
// the status bar. we'll need this later.
|
|
1116
|
-
|
|
1201
|
+
const node = document.getElementById(this._statusBarId);
|
|
1202
|
+
return node?.offsetHeight || 0;
|
|
1117
1203
|
}
|
|
1118
1204
|
_setHeight(newHeight) {
|
|
1119
1205
|
const cssHeight = `${newHeight}px`;
|
|
@@ -1123,7 +1209,10 @@ class RCEWrapper extends React.Component {
|
|
|
1123
1209
|
container.style.height = cssHeight;
|
|
1124
1210
|
ed.fire('ResizeEditor');
|
|
1125
1211
|
}
|
|
1126
|
-
this.getTextarea()
|
|
1212
|
+
const textarea = this.getTextarea();
|
|
1213
|
+
if (textarea) {
|
|
1214
|
+
textarea.style.height = cssHeight;
|
|
1215
|
+
}
|
|
1127
1216
|
this.setState({
|
|
1128
1217
|
height: cssHeight
|
|
1129
1218
|
});
|
|
@@ -1131,6 +1220,7 @@ class RCEWrapper extends React.Component {
|
|
|
1131
1220
|
focus() {
|
|
1132
1221
|
this.onTinyMCEInstance('mceFocus');
|
|
1133
1222
|
// tinymce doesn't always call the focus handler.
|
|
1223
|
+
// @ts-expect-error
|
|
1134
1224
|
this.handleFocusEditor(new Event('focus', {
|
|
1135
1225
|
target: this.mceInstance()
|
|
1136
1226
|
}));
|
|
@@ -1138,21 +1228,29 @@ class RCEWrapper extends React.Component {
|
|
|
1138
1228
|
focusCurrentView() {
|
|
1139
1229
|
switch (this.state.editorView) {
|
|
1140
1230
|
case WYSIWYG_VIEW:
|
|
1141
|
-
|
|
1142
|
-
|
|
1231
|
+
{
|
|
1232
|
+
this.mceInstance().focus();
|
|
1233
|
+
break;
|
|
1234
|
+
}
|
|
1143
1235
|
case PRETTY_HTML_EDITOR_VIEW:
|
|
1144
|
-
|
|
1236
|
+
{
|
|
1237
|
+
break;
|
|
1238
|
+
}
|
|
1145
1239
|
case RAW_HTML_EDITOR_VIEW:
|
|
1146
|
-
|
|
1147
|
-
|
|
1240
|
+
{
|
|
1241
|
+
const textarea = this.getTextarea();
|
|
1242
|
+
if (textarea) {
|
|
1243
|
+
textarea.focus();
|
|
1244
|
+
}
|
|
1245
|
+
break;
|
|
1246
|
+
}
|
|
1148
1247
|
}
|
|
1149
1248
|
}
|
|
1150
1249
|
is_dirty() {
|
|
1151
|
-
var _this$mceInstance2;
|
|
1152
1250
|
if (this.mceInstance().isDirty()) {
|
|
1153
1251
|
return true;
|
|
1154
1252
|
}
|
|
1155
|
-
const currentHtml = this.isHidden() ? this.textareaValue() :
|
|
1253
|
+
const currentHtml = this.isHidden() ? this.textareaValue() : this.mceInstance()?.getContent();
|
|
1156
1254
|
return currentHtml !== this._mceSerializedInitialHtml;
|
|
1157
1255
|
}
|
|
1158
1256
|
|
|
@@ -1162,6 +1260,7 @@ class RCEWrapper extends React.Component {
|
|
|
1162
1260
|
get _mceSerializedInitialHtml() {
|
|
1163
1261
|
if (!this._mceSerializedInitialHtmlCached) {
|
|
1164
1262
|
const el = window.document.createElement('div');
|
|
1263
|
+
// @ts-expect-error
|
|
1165
1264
|
el.innerHTML = this.initialContent;
|
|
1166
1265
|
const serializer = this.mceInstance().serializer;
|
|
1167
1266
|
this._mceSerializedInitialHtmlCached = serializer.serialize(el, {
|
|
@@ -1185,10 +1284,12 @@ class RCEWrapper extends React.Component {
|
|
|
1185
1284
|
get focused() {
|
|
1186
1285
|
return this === bridge.getEditor();
|
|
1187
1286
|
}
|
|
1188
|
-
handleFocus(
|
|
1287
|
+
handleFocus() {
|
|
1189
1288
|
if (!this.focused) {
|
|
1190
1289
|
bridge.focusEditor(this);
|
|
1191
|
-
|
|
1290
|
+
if (this.props.onFocus) {
|
|
1291
|
+
this.props.onFocus(this);
|
|
1292
|
+
}
|
|
1192
1293
|
}
|
|
1193
1294
|
}
|
|
1194
1295
|
handleContentTrayClosing(isClosing) {
|
|
@@ -1199,27 +1300,29 @@ class RCEWrapper extends React.Component {
|
|
|
1199
1300
|
if (this.focused) {
|
|
1200
1301
|
// because the old active element fires blur before the next element gets focus
|
|
1201
1302
|
// we often need a moment to see if focus comes back
|
|
1303
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
1202
1304
|
event && event.persist && event.persist();
|
|
1203
1305
|
this.blurTimer = window.setTimeout(() => {
|
|
1204
|
-
var _this$_elementRef$cur3, _event$focusedEditor, _event$relatedTarget, _event$relatedTarget$;
|
|
1205
1306
|
this.blurTimer = 0;
|
|
1206
1307
|
if (this.contentTrayClosing) {
|
|
1207
1308
|
// the CanvasContentTray is in the process of closing
|
|
1208
1309
|
// wait until it finishes
|
|
1209
1310
|
return;
|
|
1210
1311
|
}
|
|
1211
|
-
if (
|
|
1312
|
+
if (this._elementRef.current?.contains(document.activeElement)) {
|
|
1212
1313
|
// focus is still somewhere w/in me
|
|
1213
1314
|
return;
|
|
1214
1315
|
}
|
|
1215
1316
|
const activeClass = document.activeElement && document.activeElement.getAttribute('class');
|
|
1216
|
-
if (
|
|
1317
|
+
if (
|
|
1318
|
+
// @ts-expect-error
|
|
1319
|
+
(event.focusedEditor === undefined || event.target.id === event.focusedEditor?.id) && activeClass?.includes('tox-')) {
|
|
1217
1320
|
// if a toolbar button has focus, then the user clicks on the "more" button
|
|
1218
1321
|
// focus jumps to the body, then eventually to the popped up toolbar. This
|
|
1219
1322
|
// catches that case.
|
|
1220
1323
|
return;
|
|
1221
1324
|
}
|
|
1222
|
-
if (event
|
|
1325
|
+
if (event?.relatedTarget?.getAttribute('class')?.includes('tox-')) {
|
|
1223
1326
|
// a tinymce popup has focus
|
|
1224
1327
|
return;
|
|
1225
1328
|
}
|
|
@@ -1231,19 +1334,23 @@ class RCEWrapper extends React.Component {
|
|
|
1231
1334
|
}
|
|
1232
1335
|
}
|
|
1233
1336
|
bridge.blurEditor(this);
|
|
1234
|
-
|
|
1337
|
+
if (this.props.onBlur) {
|
|
1338
|
+
this.props.onBlur(event);
|
|
1339
|
+
}
|
|
1235
1340
|
}, ASYNC_FOCUS_TIMEOUT);
|
|
1236
1341
|
}
|
|
1237
1342
|
}
|
|
1238
|
-
|
|
1343
|
+
|
|
1344
|
+
// @ts-expect-error
|
|
1345
|
+
|
|
1346
|
+
// @ts-expect-error
|
|
1347
|
+
call(methodName, ...args) {
|
|
1239
1348
|
// since exists? has a ? and cant be a regular function just return true
|
|
1240
1349
|
// rather than calling as a fn on the editor
|
|
1241
1350
|
if (methodName === 'exists?') {
|
|
1242
1351
|
return true;
|
|
1243
1352
|
}
|
|
1244
|
-
|
|
1245
|
-
args[_key2 - 1] = arguments[_key2];
|
|
1246
|
-
}
|
|
1353
|
+
// @ts-expect-error
|
|
1247
1354
|
return this[methodName](...args);
|
|
1248
1355
|
}
|
|
1249
1356
|
|
|
@@ -1266,10 +1373,12 @@ class RCEWrapper extends React.Component {
|
|
|
1266
1373
|
*
|
|
1267
1374
|
* @private
|
|
1268
1375
|
*/
|
|
1376
|
+
// @ts-expect-error
|
|
1269
1377
|
|
|
1270
1378
|
announceContextToolbars(editor) {
|
|
1271
1379
|
editor.on('NodeChange', () => {
|
|
1272
1380
|
const node = editor.selection.getNode();
|
|
1381
|
+
// @ts-expect-error
|
|
1273
1382
|
if (isImageEmbed(node, editor)) {
|
|
1274
1383
|
if (this.announcing !== 1) {
|
|
1275
1384
|
this.setState({
|
|
@@ -1288,7 +1397,7 @@ class RCEWrapper extends React.Component {
|
|
|
1288
1397
|
});
|
|
1289
1398
|
this.announcing = 2;
|
|
1290
1399
|
}
|
|
1291
|
-
} else if (isElementWithinTable(node
|
|
1400
|
+
} else if (isElementWithinTable(node)) {
|
|
1292
1401
|
if (this.announcing !== 3) {
|
|
1293
1402
|
this.setState({
|
|
1294
1403
|
announcement: formatMessage('type Control F9 to access table options. {text}', {
|
|
@@ -1310,24 +1419,14 @@ class RCEWrapper extends React.Component {
|
|
|
1310
1419
|
|
|
1311
1420
|
// remove any autosaved value that's too old
|
|
1312
1421
|
|
|
1313
|
-
//
|
|
1314
|
-
|
|
1315
|
-
// besides, the placeholder is intended to be temporary while the file
|
|
1316
|
-
// is being uploaded
|
|
1317
|
-
patchAutosavedContent(content, asText) {
|
|
1318
|
-
const temp = document.createElement('div');
|
|
1319
|
-
temp.innerHTML = content;
|
|
1320
|
-
temp.querySelectorAll('[data-placeholder-for]').forEach(placeholder => {
|
|
1321
|
-
placeholder.parentElement.removeChild(placeholder);
|
|
1322
|
-
});
|
|
1323
|
-
if (asText) return temp.textContent;
|
|
1324
|
-
return temp.innerHTML;
|
|
1325
|
-
}
|
|
1422
|
+
// @ts-expect-error
|
|
1423
|
+
|
|
1326
1424
|
getAutoSaved(key) {
|
|
1327
1425
|
let autosaved = null;
|
|
1328
1426
|
try {
|
|
1329
1427
|
autosaved = this.storage && this.storage.getItem(key);
|
|
1330
1428
|
} catch (_ex) {
|
|
1429
|
+
// @ts-expect-error
|
|
1331
1430
|
this.storage.removeItem(this.autoSaveKey);
|
|
1332
1431
|
}
|
|
1333
1432
|
return autosaved;
|
|
@@ -1337,44 +1436,51 @@ class RCEWrapper extends React.Component {
|
|
|
1337
1436
|
// the latter condition is necessary because the popup RestoreAutoSaveModal
|
|
1338
1437
|
// is lousey UX when there are >1
|
|
1339
1438
|
get isAutoSaving() {
|
|
1439
|
+
if (!this.editor) return false;
|
|
1440
|
+
|
|
1340
1441
|
// If the editor is invisible for some reason, don't show the autosave modal
|
|
1341
1442
|
// This doesn't apply if the editor is off-screen or has visibility:hidden;
|
|
1342
1443
|
// only if it isn't rendered or has display:none;
|
|
1343
1444
|
const editorVisible = this.editor.getContainer().offsetParent;
|
|
1344
|
-
return this.props.autosave
|
|
1445
|
+
return this.props.autosave?.enabled && editorVisible && document.querySelectorAll('.rce-wrapper').length === 1 && storageAvailable();
|
|
1345
1446
|
}
|
|
1346
1447
|
get autoSaveKey() {
|
|
1347
|
-
|
|
1348
|
-
const userId =
|
|
1448
|
+
// @ts-expect-error
|
|
1449
|
+
const userId = this.props.trayProps?.containingContext.userId;
|
|
1349
1450
|
return `rceautosave:${userId}${window.location.href}:${this.props.textareaId}`;
|
|
1350
1451
|
}
|
|
1351
1452
|
|
|
1453
|
+
// @ts-expect-error
|
|
1454
|
+
|
|
1352
1455
|
/* *********** end autosave support *************** */
|
|
1353
1456
|
|
|
1457
|
+
// @ts-expect-error
|
|
1458
|
+
|
|
1354
1459
|
componentWillUnmount() {
|
|
1355
1460
|
if (this.state.shouldShowEditor) {
|
|
1356
|
-
var _this$mutationObserve, _this$intersectionObs;
|
|
1357
1461
|
window.clearTimeout(this.blurTimer);
|
|
1358
1462
|
if (!this._destroyCalled) {
|
|
1359
1463
|
this.destroy();
|
|
1360
1464
|
}
|
|
1361
|
-
this._elementRef.current
|
|
1362
|
-
|
|
1363
|
-
|
|
1465
|
+
if (this._elementRef.current) {
|
|
1466
|
+
this._elementRef.current.removeEventListener('keydown', this.handleKey, true);
|
|
1467
|
+
}
|
|
1468
|
+
this.mutationObserver?.disconnect();
|
|
1469
|
+
this.intersectionObserver?.disconnect();
|
|
1364
1470
|
}
|
|
1365
1471
|
}
|
|
1366
|
-
wrapOptions() {
|
|
1367
|
-
|
|
1368
|
-
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
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);
|
|
1472
|
+
wrapOptions(options = {}) {
|
|
1473
|
+
const rcsExists = !!(this.props.trayProps?.host && this.props.trayProps?.jwt);
|
|
1370
1474
|
const userLocale = editorLanguage(this.language);
|
|
1475
|
+
|
|
1476
|
+
// @ts-expect-error
|
|
1371
1477
|
const setupCallback = options.setup;
|
|
1372
1478
|
const canvasPlugins = rcsExists ? ['instructure_image', 'instructure_documents', 'instructure_equation'] : [];
|
|
1373
1479
|
if (rcsExists && !this.props.instRecordDisabled) {
|
|
1374
1480
|
canvasPlugins.splice(2, 0, 'instructure_record');
|
|
1375
1481
|
}
|
|
1376
1482
|
const pastePlugins = rcsExists ? ['instructure_paste', 'paste'] : ['paste'];
|
|
1377
|
-
if (rcsExists && this.props.use_rce_icon_maker &&
|
|
1483
|
+
if (rcsExists && this.props.use_rce_icon_maker && this.props.trayProps?.contextType === 'course') {
|
|
1378
1484
|
canvasPlugins.push('instructure_icon_maker');
|
|
1379
1485
|
}
|
|
1380
1486
|
if (document[FS_ENABLED]) {
|
|
@@ -1392,19 +1498,23 @@ class RCEWrapper extends React.Component {
|
|
|
1392
1498
|
theme: 'silver',
|
|
1393
1499
|
// some older code specified 'modern', which doesn't exist any more
|
|
1394
1500
|
|
|
1501
|
+
// @ts-expect-error
|
|
1395
1502
|
height: options.height || DEFAULT_RCE_HEIGHT,
|
|
1396
1503
|
language: userLocale,
|
|
1397
1504
|
document_base_url: this.props.canvasOrigin,
|
|
1398
|
-
block_formats:
|
|
1505
|
+
block_formats:
|
|
1506
|
+
// @ts-expect-error
|
|
1507
|
+
options.block_formats || [`${formatMessage('Heading 2')}=h2`, `${formatMessage('Heading 3')}=h3`, `${formatMessage('Heading 4')}=h4`, `${formatMessage('Preformatted')}=pre`, `${formatMessage('Paragraph')}=p`].join('; '),
|
|
1399
1508
|
setup: editor => {
|
|
1400
|
-
var _bridge$trayProps;
|
|
1401
1509
|
addKebabIcon(editor);
|
|
1402
1510
|
editorWrappers.set(editor, this);
|
|
1403
1511
|
const trayPropsWithColor = {
|
|
1512
|
+
// @ts-expect-error
|
|
1404
1513
|
brandColor: this.style.theme.canvasBrandColor,
|
|
1405
1514
|
...this.props.trayProps
|
|
1406
1515
|
};
|
|
1407
|
-
|
|
1516
|
+
bridge.trayProps?.set(editor, trayPropsWithColor);
|
|
1517
|
+
// @ts-expect-error
|
|
1408
1518
|
bridge.userLocale = userLocale;
|
|
1409
1519
|
bridge.canvasOrigin = this.props.canvasOrigin;
|
|
1410
1520
|
if (typeof setupCallback === 'function') {
|
|
@@ -1415,7 +1525,9 @@ class RCEWrapper extends React.Component {
|
|
|
1415
1525
|
// in the editor matches the styles of the app it will be displayed in when saved.
|
|
1416
1526
|
// This is just so we inject the helper class names that tinyMCE uses for
|
|
1417
1527
|
// things like table resizing and stuff.
|
|
1528
|
+
// @ts-expect-error
|
|
1418
1529
|
content_css: options.content_css || [],
|
|
1530
|
+
// @ts-expect-error
|
|
1419
1531
|
content_style: contentCSS + (options.content_style || ''),
|
|
1420
1532
|
menubar: mergeMenuItems(getMenubarForVariant(this.variant), possibleNewMenubarItems),
|
|
1421
1533
|
// default menu options listed at https://www.tiny.cloud/docs/configure/editor-appearance/#menu
|
|
@@ -1424,8 +1536,13 @@ class RCEWrapper extends React.Component {
|
|
|
1424
1536
|
// since we currently can't effectively paste using the clipboard api anyway.
|
|
1425
1537
|
// we include all the canvas specific items in the menu and toolbar
|
|
1426
1538
|
// and rely on tinymce only showing them if the plugin is provided.
|
|
1539
|
+
// @ts-expect-error
|
|
1427
1540
|
menu: mergeMenu(getMenuForVariant(this.variant), options.menu),
|
|
1428
|
-
toolbar: mergeToolbar(
|
|
1541
|
+
toolbar: mergeToolbar(
|
|
1542
|
+
// @ts-expect-error
|
|
1543
|
+
getToolbarForVariant(this.variant, this.ltiToolFavorites),
|
|
1544
|
+
// @ts-expect-error
|
|
1545
|
+
options.toolbar),
|
|
1429
1546
|
contextmenu: '',
|
|
1430
1547
|
// show the browser's native context menu
|
|
1431
1548
|
|
|
@@ -1438,8 +1555,10 @@ class RCEWrapper extends React.Component {
|
|
|
1438
1555
|
// handles all of that complexity. It that ever changes in the
|
|
1439
1556
|
// future in an upgraded version, we will have to update the
|
|
1440
1557
|
// logic in those other places as well.
|
|
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],
|
|
1442
|
-
|
|
1558
|
+
plugins: mergePlugins(['autolink', 'media', 'table', 'link', 'directionality', 'lists', 'textpattern', 'hr', 'instructure_color', 'instructure-ui-icons', 'instructure_condensed_buttons', 'instructure_links', 'instructure_html_view', 'instructure_media_embed', 'a11y_checker', 'wordcount', 'instructure_wordcount', 'instructure_studio_media_options', 'instructure_rce_external_tools', ...pastePlugins, ...canvasPlugins],
|
|
1559
|
+
// filter out the plugins designated for removal
|
|
1560
|
+
// @ts-expect-error
|
|
1561
|
+
sanitizePlugins(options.plugins)?.filter(p => p.length > 0 && p[0] !== '-'), this.pluginsToExclude),
|
|
1443
1562
|
textpattern_patterns: [{
|
|
1444
1563
|
start: '* ',
|
|
1445
1564
|
cmd: 'InsertUnorderedList'
|
|
@@ -1449,12 +1568,17 @@ class RCEWrapper extends React.Component {
|
|
|
1449
1568
|
}]
|
|
1450
1569
|
};
|
|
1451
1570
|
if (this.props.trayProps) {
|
|
1571
|
+
// @ts-expect-error
|
|
1452
1572
|
wrappedOpts.canvas_rce_user_context = {
|
|
1453
1573
|
type: this.props.trayProps.contextType,
|
|
1454
1574
|
id: this.props.trayProps.contextId
|
|
1455
1575
|
};
|
|
1576
|
+
|
|
1577
|
+
// @ts-expect-error
|
|
1456
1578
|
wrappedOpts.canvas_rce_containing_context = {
|
|
1579
|
+
// @ts-expect-error
|
|
1457
1580
|
type: this.props.trayProps.containingContext.contextType,
|
|
1581
|
+
// @ts-expect-error
|
|
1458
1582
|
id: this.props.trayProps.containingContext.contextId
|
|
1459
1583
|
};
|
|
1460
1584
|
}
|
|
@@ -1498,15 +1622,15 @@ class RCEWrapper extends React.Component {
|
|
|
1498
1622
|
rootMargin: '200px 0px',
|
|
1499
1623
|
threshold: 0.0
|
|
1500
1624
|
});
|
|
1625
|
+
// @ts-expect-error
|
|
1501
1626
|
this.intersectionObserver.observe(this._editorPlaceholderRef.current);
|
|
1502
1627
|
}
|
|
1503
1628
|
}
|
|
1504
1629
|
componentDidUpdate(prevProps, prevState) {
|
|
1505
1630
|
if (this.state.shouldShowEditor) {
|
|
1506
1631
|
if (!prevState.shouldShowEditor) {
|
|
1507
|
-
var _this$intersectionObs2;
|
|
1508
1632
|
this.editorReallyDidMount();
|
|
1509
|
-
|
|
1633
|
+
this.intersectionObserver?.disconnect();
|
|
1510
1634
|
} else {
|
|
1511
1635
|
this.registerTextareaChange();
|
|
1512
1636
|
if (prevState.editorView !== this.state.editorView) {
|
|
@@ -1522,10 +1646,12 @@ class RCEWrapper extends React.Component {
|
|
|
1522
1646
|
editorReallyDidMount() {
|
|
1523
1647
|
const myTiny = this.mceInstance();
|
|
1524
1648
|
this.pendingEventHandlers.forEach(e => {
|
|
1649
|
+
// @ts-expect-error
|
|
1525
1650
|
myTiny.on(e.name, e.handler);
|
|
1526
1651
|
});
|
|
1527
1652
|
this._tagTinymceAuxDiv();
|
|
1528
1653
|
this.registerTextareaChange();
|
|
1654
|
+
// @ts-expect-error
|
|
1529
1655
|
this._elementRef.current.addEventListener('keydown', this.handleKey, true);
|
|
1530
1656
|
// give the textarea its initial size
|
|
1531
1657
|
this.onResize(null, {
|
|
@@ -1538,6 +1664,8 @@ class RCEWrapper extends React.Component {
|
|
|
1538
1664
|
}
|
|
1539
1665
|
bridge.renderEditor(this);
|
|
1540
1666
|
}
|
|
1667
|
+
|
|
1668
|
+
// @ts-expect-error
|
|
1541
1669
|
setEditorView(view) {
|
|
1542
1670
|
switch (view) {
|
|
1543
1671
|
case WYSIWYG_VIEW:
|
|
@@ -1571,24 +1699,29 @@ class RCEWrapper extends React.Component {
|
|
|
1571
1699
|
as: "div",
|
|
1572
1700
|
borderRadius: "medium",
|
|
1573
1701
|
borderWidth: "small"
|
|
1574
|
-
}, /*#__PURE__*/React.createElement(RceHtmlEditor
|
|
1702
|
+
}, /*#__PURE__*/React.createElement(RceHtmlEditor
|
|
1703
|
+
// @ts-expect-error
|
|
1704
|
+
, {
|
|
1575
1705
|
ref: this._prettyHtmlEditorRef,
|
|
1576
1706
|
height: this.state.height,
|
|
1577
1707
|
code: this.getCode(),
|
|
1578
1708
|
onChange: value => {
|
|
1579
|
-
this.getTextarea()
|
|
1709
|
+
const node = this.getTextarea();
|
|
1710
|
+
if (node) {
|
|
1711
|
+
node.value = value;
|
|
1712
|
+
}
|
|
1580
1713
|
this.handleTextareaChange();
|
|
1581
1714
|
}
|
|
1582
1715
|
})));
|
|
1583
1716
|
}
|
|
1584
1717
|
render() {
|
|
1585
|
-
var _this$props$trayProps5, _this$props$trayProps6, _this$props$trayProps7, _this$props$trayProps8, _this$props$trayProps9;
|
|
1586
1718
|
const {
|
|
1587
1719
|
trayProps,
|
|
1588
1720
|
...mceProps
|
|
1589
1721
|
} = this.props;
|
|
1590
1722
|
if (!this.state.shouldShowEditor) {
|
|
1591
1723
|
return /*#__PURE__*/React.createElement("div", {
|
|
1724
|
+
// @ts-expect-error
|
|
1592
1725
|
ref: this._editorPlaceholderRef,
|
|
1593
1726
|
style: {
|
|
1594
1727
|
height: `${this.props.editorOptions.height}px`,
|
|
@@ -1598,17 +1731,20 @@ class RCEWrapper extends React.Component {
|
|
|
1598
1731
|
}
|
|
1599
1732
|
const statusBarFeatures = getStatusBarFeaturesForVariant(this.variant, this.props.ai_text_tools);
|
|
1600
1733
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("style", null, this.style.css), /*#__PURE__*/React.createElement(StoreProvider, {
|
|
1601
|
-
jwt:
|
|
1602
|
-
refreshToken:
|
|
1603
|
-
host:
|
|
1604
|
-
contextType:
|
|
1605
|
-
contextId:
|
|
1734
|
+
jwt: this.props.trayProps?.jwt,
|
|
1735
|
+
refreshToken: this.props.trayProps?.refreshToken,
|
|
1736
|
+
host: this.props.trayProps?.host,
|
|
1737
|
+
contextType: this.props.trayProps?.contextType,
|
|
1738
|
+
contextId: this.props.trayProps?.contextId,
|
|
1606
1739
|
canvasOrigin: this.props.canvasOrigin
|
|
1607
1740
|
}, storeProps => {
|
|
1608
|
-
var _this$props$trayProps10;
|
|
1609
1741
|
return /*#__PURE__*/React.createElement("div", {
|
|
1610
|
-
key: this.id
|
|
1611
|
-
|
|
1742
|
+
key: this.id
|
|
1743
|
+
// @ts-expect-error
|
|
1744
|
+
,
|
|
1745
|
+
className: `${this.style.classNames.root} rce-wrapper`
|
|
1746
|
+
// @ts-expect-error
|
|
1747
|
+
,
|
|
1612
1748
|
ref: this._elementRef,
|
|
1613
1749
|
style: this.variant === 'full' ? {
|
|
1614
1750
|
marginBottom: '.5rem'
|
|
@@ -1619,10 +1755,14 @@ class RCEWrapper extends React.Component {
|
|
|
1619
1755
|
id: `show-on-focus-btn-${this.id}`,
|
|
1620
1756
|
onClick: this.openKBShortcutModal,
|
|
1621
1757
|
margin: "xx-small",
|
|
1622
|
-
screenReaderLabel: formatMessage('View keyboard shortcuts')
|
|
1758
|
+
screenReaderLabel: formatMessage('View keyboard shortcuts')
|
|
1759
|
+
// @ts-expect-error
|
|
1760
|
+
,
|
|
1623
1761
|
ref: el => this._showOnFocusButton = el
|
|
1624
1762
|
}, /*#__PURE__*/React.createElement(IconKeyboardShortcutsLine, null)), /*#__PURE__*/React.createElement(AlertMessageArea, {
|
|
1625
|
-
messages: this.state.messages
|
|
1763
|
+
messages: this.state.messages
|
|
1764
|
+
// @ts-expect-error
|
|
1765
|
+
,
|
|
1626
1766
|
liveRegion: this.props.liveRegion,
|
|
1627
1767
|
afterDismiss: this.removeAlert
|
|
1628
1768
|
}), this.state.editorView === PRETTY_HTML_EDITOR_VIEW && this.renderHtmlEditor(), /*#__PURE__*/React.createElement("div", {
|
|
@@ -1631,15 +1771,25 @@ class RCEWrapper extends React.Component {
|
|
|
1631
1771
|
}
|
|
1632
1772
|
}, /*#__PURE__*/React.createElement(Editor, {
|
|
1633
1773
|
id: mceProps.textareaId,
|
|
1634
|
-
textareaName: mceProps.name
|
|
1774
|
+
textareaName: mceProps.name
|
|
1775
|
+
// @ts-expect-error
|
|
1776
|
+
,
|
|
1635
1777
|
init: this.tinymceInitOptions,
|
|
1636
|
-
initialValue: this.initialContent
|
|
1778
|
+
initialValue: this.initialContent
|
|
1779
|
+
// @ts-expect-error
|
|
1780
|
+
,
|
|
1637
1781
|
onInit: this.onInit,
|
|
1638
1782
|
onClick: this.handleFocusEditor,
|
|
1639
|
-
onKeypress: this.handleFocusEditor
|
|
1783
|
+
onKeypress: this.handleFocusEditor
|
|
1784
|
+
// @ts-expect-error
|
|
1785
|
+
,
|
|
1640
1786
|
onActivate: this.handleFocusEditor,
|
|
1641
|
-
onRemove: this.onRemove
|
|
1642
|
-
|
|
1787
|
+
onRemove: this.onRemove
|
|
1788
|
+
// @ts-expect-error
|
|
1789
|
+
,
|
|
1790
|
+
onFocus: this.handleFocusEditor
|
|
1791
|
+
// @ts-expect-error
|
|
1792
|
+
,
|
|
1643
1793
|
onBlur: this.handleBlurEditor,
|
|
1644
1794
|
onNodeChange: this.onNodeChange,
|
|
1645
1795
|
onEditorChange: this.onEditorChange,
|
|
@@ -1656,7 +1806,9 @@ class RCEWrapper extends React.Component {
|
|
|
1656
1806
|
onResize: this.onResize,
|
|
1657
1807
|
onKBShortcutModalOpen: this.openKBShortcutModal,
|
|
1658
1808
|
onA11yChecker: this.onA11yChecker,
|
|
1659
|
-
onFullscreen: this.handleClickFullscreen
|
|
1809
|
+
onFullscreen: this.handleClickFullscreen
|
|
1810
|
+
// @ts-expect-error
|
|
1811
|
+
,
|
|
1660
1812
|
a11yBadgeColor: this.style.theme.canvasBadgeBackgroundColor,
|
|
1661
1813
|
a11yErrorsCount: this.state.a11yErrorsCount,
|
|
1662
1814
|
onWordcountModalOpen: () => launchWordcountModal(this.mceInstance(), document, {
|
|
@@ -1665,8 +1817,8 @@ class RCEWrapper extends React.Component {
|
|
|
1665
1817
|
disabledPlugins: this.pluginsToExclude,
|
|
1666
1818
|
features: statusBarFeatures,
|
|
1667
1819
|
onAI: this.handleAIClick
|
|
1668
|
-
}),
|
|
1669
|
-
mountNode:
|
|
1820
|
+
}), this.props.trayProps?.containingContext && /*#__PURE__*/React.createElement(CanvasContentTray, Object.assign({
|
|
1821
|
+
mountNode: instuiPopupMountNodeFn,
|
|
1670
1822
|
key: this.id,
|
|
1671
1823
|
canvasOrigin: this.getCanvasUrl(),
|
|
1672
1824
|
bridge: bridge,
|
|
@@ -1674,15 +1826,19 @@ class RCEWrapper extends React.Component {
|
|
|
1674
1826
|
onTrayClosing: this.handleContentTrayClosing,
|
|
1675
1827
|
use_rce_icon_maker: this.props.use_rce_icon_maker
|
|
1676
1828
|
}, trayProps, {
|
|
1829
|
+
// @ts-expect-error
|
|
1677
1830
|
storeProps: storeProps
|
|
1678
1831
|
})), /*#__PURE__*/React.createElement(KeyboardShortcutModal, {
|
|
1679
1832
|
onExited: this.KBShortcutModalExited,
|
|
1680
1833
|
onDismiss: this.closeKBShortcutModal,
|
|
1681
1834
|
open: this.state.KBShortcutModalOpen
|
|
1682
|
-
}), this.props.ai_text_tools && this.AIToolsTray &&
|
|
1835
|
+
}), this.props.ai_text_tools && this.AIToolsTray &&
|
|
1836
|
+
/*#__PURE__*/
|
|
1837
|
+
// @ts-expect-error
|
|
1838
|
+
React.createElement(this.AIToolsTray, {
|
|
1683
1839
|
open: this.state.AIToolsOpen,
|
|
1684
1840
|
container: document.querySelector('[role="main"]'),
|
|
1685
|
-
mountNode:
|
|
1841
|
+
mountNode: instuiPopupMountNodeFn,
|
|
1686
1842
|
contextId: trayProps.contextId,
|
|
1687
1843
|
contextType: trayProps.contextId,
|
|
1688
1844
|
currentContent: this.getCurrentContentForAI(),
|
|
@@ -1706,11 +1862,36 @@ class RCEWrapper extends React.Component {
|
|
|
1706
1862
|
}));
|
|
1707
1863
|
}
|
|
1708
1864
|
}
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1865
|
+
RCEWrapper.propTypes = {
|
|
1866
|
+
ai_text_tools: _pt.bool,
|
|
1867
|
+
autosave: _pt.shape({
|
|
1868
|
+
enabled: _pt.bool,
|
|
1869
|
+
maxAge: _pt.number,
|
|
1870
|
+
interval: _pt.number
|
|
1871
|
+
}),
|
|
1872
|
+
canvasOrigin: _pt.string,
|
|
1873
|
+
defaultContent: _pt.string,
|
|
1874
|
+
editorView: _pt.string,
|
|
1875
|
+
features: _pt.objectOf(_pt.any),
|
|
1876
|
+
handleUnmount: _pt.func,
|
|
1877
|
+
instRecordDisabled: _pt.bool,
|
|
1878
|
+
language: _pt.string,
|
|
1879
|
+
ltiToolFavorites: _pt.arrayOf(_pt.string),
|
|
1880
|
+
maxInitRenderedRCEs: _pt.number,
|
|
1881
|
+
name: _pt.string,
|
|
1882
|
+
onBlur: _pt.func,
|
|
1883
|
+
onContentChange: _pt.func,
|
|
1884
|
+
onFocus: _pt.func,
|
|
1885
|
+
onInitted: _pt.func,
|
|
1886
|
+
onRemove: _pt.func,
|
|
1887
|
+
readOnly: _pt.bool,
|
|
1888
|
+
renderKBShortcutModal: _pt.bool,
|
|
1889
|
+
textareaClassName: _pt.string,
|
|
1890
|
+
textareaId: _pt.string,
|
|
1891
|
+
tinymce: _pt.any.isRequired,
|
|
1892
|
+
use_rce_icon_maker: _pt.bool,
|
|
1893
|
+
userCacheKey: _pt.string
|
|
1894
|
+
};
|
|
1714
1895
|
RCEWrapper.propTypes = rceWrapperPropTypes;
|
|
1715
1896
|
RCEWrapper.defaultProps = {
|
|
1716
1897
|
trayProps: null,
|
|
@@ -1721,83 +1902,10 @@ RCEWrapper.defaultProps = {
|
|
|
1721
1902
|
ltiTools: [],
|
|
1722
1903
|
maxInitRenderedRCEs: -1,
|
|
1723
1904
|
features: {},
|
|
1724
|
-
timezone:
|
|
1905
|
+
timezone: Intl?.DateTimeFormat()?.resolvedOptions()?.timeZone,
|
|
1725
1906
|
canvasOrigin: '',
|
|
1726
1907
|
variant: 'full'
|
|
1727
1908
|
};
|
|
1728
1909
|
RCEWrapper.skinCssInjected = false;
|
|
1729
|
-
function mergeMenuItems(standard, custom) {
|
|
1730
|
-
var _custom$trim;
|
|
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);
|
|
1732
|
-
if (!c) return standard;
|
|
1733
|
-
const s = new Set(standard.split(/[\s|]+/));
|
|
1734
|
-
// remove any duplicates
|
|
1735
|
-
c = c.split(/\s+/).filter(m => !s.has(m));
|
|
1736
|
-
c = c.join(' ').replace(/^\s*\|\s*/, '').replace(/\s*\|\s*$/, '');
|
|
1737
|
-
return `${standard} | ${c}`;
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
|
-
// standard: the incoming tinymce menu object
|
|
1741
|
-
// custom: tinymce menu object to merge into standard
|
|
1742
|
-
// returns: the merged result by mutating incoming standard arg.
|
|
1743
|
-
// It will add commands to existing menus, or add a new menu
|
|
1744
|
-
// if the custom one does not exist
|
|
1745
|
-
function mergeMenu(standard, custom) {
|
|
1746
|
-
if (!custom) return standard;
|
|
1747
|
-
Object.keys(custom).forEach(k => {
|
|
1748
|
-
const curr_m = standard[k];
|
|
1749
|
-
if (curr_m) {
|
|
1750
|
-
curr_m.items = mergeMenuItems(curr_m.items, custom[k].items);
|
|
1751
|
-
} else {
|
|
1752
|
-
standard[k] = {
|
|
1753
|
-
...custom[k]
|
|
1754
|
-
};
|
|
1755
|
-
}
|
|
1756
|
-
});
|
|
1757
|
-
return standard;
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
// standard: incoming tinymce toolbar array
|
|
1761
|
-
// custom: tinymce toolbar array to merge into standard
|
|
1762
|
-
// returns: the merged result by mutating the incoming standard arg.
|
|
1763
|
-
// It will add commands to existing toolbars, or add a new toolbar
|
|
1764
|
-
// if the custom one does not exist
|
|
1765
|
-
function mergeToolbar(standard, custom) {
|
|
1766
|
-
if (!custom) return standard;
|
|
1767
|
-
// merge given toolbar data into the default toolbar
|
|
1768
|
-
custom.forEach(tb => {
|
|
1769
|
-
const curr_tb = standard.find(t => tb.name && formatMessage(tb.name) === t.name);
|
|
1770
|
-
if (curr_tb) {
|
|
1771
|
-
curr_tb.items.splice(curr_tb.items.length, 0, ...tb.items);
|
|
1772
|
-
} else {
|
|
1773
|
-
standard.push(tb);
|
|
1774
|
-
}
|
|
1775
|
-
});
|
|
1776
|
-
return standard;
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
// standard: incoming array of plugin names
|
|
1780
|
-
// custom: array of plugin names to merge
|
|
1781
|
-
// exclusions: array of plugins to remove
|
|
1782
|
-
// returns: the merged result, duplicates and exclusions removed
|
|
1783
|
-
function mergePlugins(standard) {
|
|
1784
|
-
let custom = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
1785
|
-
let exclusions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
|
|
1786
|
-
const union = new Set(standard);
|
|
1787
|
-
for (const c of custom) {
|
|
1788
|
-
union.add(c);
|
|
1789
|
-
}
|
|
1790
|
-
for (const e of exclusions) {
|
|
1791
|
-
union.delete(e);
|
|
1792
|
-
}
|
|
1793
|
-
return [...union];
|
|
1794
|
-
}
|
|
1795
|
-
|
|
1796
|
-
// plugins is an array of strings
|
|
1797
|
-
// the convention is that plugins starting with '-',
|
|
1798
|
-
// i.e. a hyphen, are to be disabled in the RCE instance
|
|
1799
|
-
function parsePluginsToExclude(plugins) {
|
|
1800
|
-
return plugins.filter(plugin => plugin.length > 0 && plugin[0] === '-').map(pluginToIgnore => pluginToIgnore.slice(1));
|
|
1801
|
-
}
|
|
1802
1910
|
export default RCEWrapper;
|
|
1803
1911
|
export { mergeMenuItems, mergeMenu, mergeToolbar, mergePlugins, parsePluginsToExclude };
|