@instructure/canvas-rce 5.12.2 → 5.13.1
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 +33 -1
- package/es/common/fileUrl.js +5 -1
- package/es/defaultTinymceConfig.js +2 -1
- package/es/enhance-user-content/enhance_user_content.js +30 -1
- package/es/getTranslations.js +5 -1
- package/es/rce/RCEWrapper.js +63 -22
- package/es/rce/RCEWrapperProps.js +1 -0
- package/es/rce/StatusBar.js +5 -4
- package/es/rce/editorLanguage.js +2 -0
- package/es/rce/plugins/instructure_image/ImageEmbedOptions.js +1 -1
- package/es/rce/plugins/instructure_rce_external_tools/ExternalToolsEnv.js +6 -0
- package/es/rce/plugins/instructure_rce_external_tools/RceToolWrapper.js +30 -1
- package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialog.js +14 -1
- package/es/rce/plugins/instructure_rce_external_tools/lti13-content-items/processEditorContentItems.js +9 -3
- package/es/rce/plugins/instructure_rce_external_tools/plugin.js +3 -2
- package/es/rce/plugins/instructure_search_and_replace/clickCallback.js +55 -0
- package/es/rce/plugins/instructure_search_and_replace/components/FindReplaceTray.js +360 -0
- package/es/rce/plugins/instructure_search_and_replace/components/FindReplaceTrayController.js +139 -0
- package/es/rce/plugins/instructure_search_and_replace/getSelectionContext.js +68 -0
- package/es/rce/plugins/instructure_search_and_replace/plugin.js +39 -0
- package/es/rce/plugins/instructure_search_and_replace/types.d.js +1 -0
- package/es/rce/plugins/shared/fileTypeUtils.js +3 -1
- package/es/rce/plugins/tinymce-a11y-checker/plugin.js +11 -1
- package/es/rce/plugins/tinymce-a11y-checker/utils/indicate.js +1 -1
- package/es/rce/tinyRCE.js +3 -1
- package/es/translations/locales/ar.js +54 -0
- package/es/translations/locales/ca.js +54 -0
- package/es/translations/locales/cy.js +54 -0
- package/es/translations/locales/da-x-k12.js +54 -0
- package/es/translations/locales/da.js +54 -0
- package/es/translations/locales/de.js +54 -0
- package/es/translations/locales/el.js +9 -0
- package/es/translations/locales/en-AU-x-unimelb.js +54 -0
- package/es/translations/locales/en-GB-x-ukhe.js +54 -0
- package/es/translations/locales/en.js +54 -0
- package/es/translations/locales/en_AU.js +54 -0
- package/es/translations/locales/en_CA.js +54 -0
- package/es/translations/locales/en_CY.js +54 -0
- package/es/translations/locales/en_GB.js +54 -0
- package/es/translations/locales/es.js +54 -0
- package/es/translations/locales/es_ES.js +54 -0
- package/es/translations/locales/fa_IR.js +9 -0
- package/es/translations/locales/fi.js +54 -0
- package/es/translations/locales/fr.js +54 -0
- package/es/translations/locales/fr_CA.js +54 -0
- package/es/translations/locales/ga.js +2427 -0
- package/es/translations/locales/he.js +10 -1
- package/es/translations/locales/ht.js +54 -0
- package/es/translations/locales/hu.js +15 -0
- package/es/translations/locales/hy.js +9 -0
- package/es/translations/locales/id.js +55 -0
- package/es/translations/locales/id_ID.js +1 -0
- package/es/translations/locales/is.js +54 -0
- package/es/translations/locales/it.js +54 -0
- package/es/translations/locales/ja.js +61 -7
- package/es/translations/locales/ko.js +9 -0
- package/es/translations/locales/mi.js +54 -0
- package/es/translations/locales/ms.js +54 -0
- package/es/translations/locales/nb-x-k12.js +54 -0
- package/es/translations/locales/nb.js +54 -0
- package/es/translations/locales/nl.js +54 -0
- package/es/translations/locales/nn.js +9 -0
- package/es/translations/locales/pl.js +54 -0
- package/es/translations/locales/pt.js +54 -0
- package/es/translations/locales/pt_BR.js +54 -0
- package/es/translations/locales/ru.js +54 -0
- package/es/translations/locales/sl.js +54 -0
- package/es/translations/locales/sv-x-k12.js +54 -0
- package/es/translations/locales/sv.js +54 -0
- package/es/translations/locales/th.js +54 -0
- package/es/translations/locales/tr.js +9 -0
- package/es/translations/locales/uk_UA.js +9 -0
- package/es/translations/locales/vi.js +54 -0
- package/es/translations/locales/zh-Hans.js +54 -0
- package/es/translations/locales/zh-Hant.js +54 -0
- package/es/translations/locales/zh.js +54 -0
- package/es/translations/locales/zh_HK.js +54 -0
- package/es/translations/tinymce/ga.js +423 -0
- package/es/translations/tinymce/id.js +423 -0
- package/es/translations/tinymce/ja.js +1 -1
- package/package.json +3 -3
- package/scripts/commitTranslations.sh +2 -2
- package/scripts/publish_to_npm.sh +1 -1
- package/bin/jira_tickets.sh +0 -14
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,39 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## 5.13.1 - 2024-06-03
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Re-added file verifiers as a stop gap to non-Canvas contexts to allow
|
|
13
|
+
New Quiz item banks to properly share course files
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- A11y checker tray refusing to close in New Quizzes
|
|
18
|
+
- Find and Replace Tray now translated correctly
|
|
19
|
+
|
|
20
|
+
## 5.13.0 - 2024-05-14
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- Find and Replace Tray
|
|
25
|
+
- Support for Bahasa Indonesia Language and Irish (Gaeilge) Language
|
|
26
|
+
- Support for tools to always be present in toolbar
|
|
27
|
+
- LTI enhancements
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- Limited list of fonts to self-hosted and websafe
|
|
32
|
+
- Preferred HTML editor stored in localstorage
|
|
33
|
+
- Stopped adding aria-hidden to RCE’s parent label
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- Focus properly restored after closing a11y checker
|
|
38
|
+
- Allow non relative video srcs when editing captions
|
|
39
|
+
- Enhanced user content now translated correctly
|
|
40
|
+
|
|
8
41
|
## 5.12.2 - 2024-01-31
|
|
9
42
|
|
|
10
43
|
### Changed
|
|
@@ -36,7 +69,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
36
69
|
- Show media captions in New Quizzes
|
|
37
70
|
- Bump Instui to 8.49
|
|
38
71
|
|
|
39
|
-
|
|
40
72
|
## 5.11.1 - 2023-10-12
|
|
41
73
|
|
|
42
74
|
### Fixed
|
package/es/common/fileUrl.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
// or a base URL makes testing difficult, esp since window.location is "about:blank"
|
|
20
20
|
// in mocha tests.
|
|
21
21
|
import { parse, format } from 'url';
|
|
22
|
+
import RCEGlobals from '../rce/RCEGlobals';
|
|
22
23
|
|
|
23
24
|
function parseCanvasUrl(url) {
|
|
24
25
|
let canvasOrigin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : window.location.origin;
|
|
@@ -95,6 +96,8 @@ export function fixupFileUrl(contextType, contextId, fileInfo, canvasOrigin) {
|
|
|
95
96
|
const key = fileInfo.href ? 'href' : 'url';
|
|
96
97
|
|
|
97
98
|
if (fileInfo[key]) {
|
|
99
|
+
var _RCEGlobals$getFeatur;
|
|
100
|
+
|
|
98
101
|
let parsed = parseCanvasUrl(fileInfo[key], canvasOrigin);
|
|
99
102
|
|
|
100
103
|
if (!parsed) {
|
|
@@ -103,8 +106,9 @@ export function fixupFileUrl(contextType, contextId, fileInfo, canvasOrigin) {
|
|
|
103
106
|
|
|
104
107
|
parsed = changeDownloadToWrapParams(parsed);
|
|
105
108
|
parsed = addContext(parsed, contextType, contextId); // if this is a user file, add the verifier
|
|
109
|
+
// if this is in New Quizzes and the feature flag is enabled, add the verifier
|
|
106
110
|
|
|
107
|
-
if (fileInfo.uuid && contextType.includes('user')) {
|
|
111
|
+
if (fileInfo.uuid && (contextType.includes('user') || !!canvasOrigin && canvasOrigin !== window.location.origin && (_RCEGlobals$getFeatur = RCEGlobals.getFeatures()) !== null && _RCEGlobals$getFeatur !== void 0 && _RCEGlobals$getFeatur.file_verifiers_for_quiz_links)) {
|
|
108
112
|
delete parsed.search;
|
|
109
113
|
parsed.query.verifier = fileInfo.uuid;
|
|
110
114
|
} else {
|
|
@@ -50,7 +50,8 @@ const defaultTinymceConfig = {
|
|
|
50
50
|
content_style: undefined,
|
|
51
51
|
// this will always be provided by the RCE
|
|
52
52
|
convert_urls: false,
|
|
53
|
-
|
|
53
|
+
// fonts specified here need to either be web-safe or self-hosted and loaded in app/stylesheets/bundles/fonts.scss
|
|
54
|
+
font_formats: "Lato=lato,Helvetica Neue,Helvetica,Arial,sans-serif; Balsamiq Sans=Balsamiq Sans,lato,Helvetica Neue,Helvetica,Arial,sans-serif; Architect's Daughter=Architects Daughter,lato,Helvetica Neue,Helvetica,Arial,sans-serif; Arial=arial,helvetica,sans-serif; Arial Black=arial black,avant garde; Courier New=courier new,courier; Georgia=georgia,palatino; Tahoma=tahoma,arial,helvetica,sans-serif; Times New Roman=times new roman,times; Trebuchet MS=trebuchet ms,geneva; Verdana=verdana,geneva",
|
|
54
55
|
language_load: false,
|
|
55
56
|
language_url: 'none',
|
|
56
57
|
toolbar_mode: 'floating',
|
|
@@ -22,7 +22,8 @@ import { isExternalLink, showFilePreview, youTubeID } from './instructure_helper
|
|
|
22
22
|
import mediaCommentThumbnail from './media_comment_thumbnail';
|
|
23
23
|
import { addParentFrameContextToUrl } from '../rce/plugins/instructure_rce_external_tools/util/addParentFrameContextToUrl';
|
|
24
24
|
import { MathJaxDirective, Mathml } from './mathml';
|
|
25
|
-
import { makeExternalLinkIcon } from './external_links';
|
|
25
|
+
import { makeExternalLinkIcon } from './external_links';
|
|
26
|
+
import getTranslations from '../getTranslations'; // in jest the es directory doesn't exist so stub the undefined svg
|
|
26
27
|
|
|
27
28
|
const IconDownloadSVG = (IconDownloadLine === null || IconDownloadLine === void 0 ? void 0 : IconDownloadLine.src) || '<svg></svg>';
|
|
28
29
|
|
|
@@ -110,6 +111,26 @@ function buildUrl(url) {
|
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
const addResourceIdentifiersToStudioContent = content => {
|
|
115
|
+
content.querySelectorAll('iframe.lti-embed').forEach(iframe => {
|
|
116
|
+
var _userContentContainer, _userContentContainer2;
|
|
117
|
+
|
|
118
|
+
const url = buildUrl(iframe.getAttribute('src'));
|
|
119
|
+
|
|
120
|
+
if (!url || !url.pathname.includes('external_tools/retrieve') || !url.search.includes('instructuremedia.com') || !url.search.includes('custom_arc_media_id')) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const userContentContainer = iframe.closest('.user_content');
|
|
125
|
+
|
|
126
|
+
if (userContentContainer !== null && userContentContainer !== void 0 && (_userContentContainer = userContentContainer.dataset) !== null && _userContentContainer !== void 0 && _userContentContainer.resourceType && userContentContainer !== null && userContentContainer !== void 0 && (_userContentContainer2 = userContentContainer.dataset) !== null && _userContentContainer2 !== void 0 && _userContentContainer2.resourceId) {
|
|
127
|
+
url.searchParams.set('com_instructure_course_canvas_resource_type', userContentContainer.dataset.resourceType);
|
|
128
|
+
url.searchParams.set('com_instructure_course_canvas_resource_id', userContentContainer.dataset.resourceId);
|
|
129
|
+
iframe.src = url.href;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
|
|
113
134
|
export function enhanceUserContent() {
|
|
114
135
|
let container = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document;
|
|
115
136
|
let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
@@ -132,6 +153,13 @@ export function enhanceUserContent() {
|
|
|
132
153
|
*/
|
|
133
154
|
containingCanvasLtiToolId
|
|
134
155
|
} = opts;
|
|
156
|
+
getTranslations(locale).then(() => {
|
|
157
|
+
formatMessage.setup({
|
|
158
|
+
locale: locale || 'en'
|
|
159
|
+
});
|
|
160
|
+
}).catch(_err => {
|
|
161
|
+
console.error('Failed loading the language file for', locale, '. Falling back to English.');
|
|
162
|
+
});
|
|
135
163
|
const content = container instanceof HTMLElement && container || document.getElementById('content') || document;
|
|
136
164
|
|
|
137
165
|
const showFilePreviewEx = event => showFilePreview(event, {
|
|
@@ -226,6 +254,7 @@ export function enhanceUserContent() {
|
|
|
226
254
|
const externalLinkIcon = makeExternalLinkIcon(childLink);
|
|
227
255
|
childLink.appendChild(externalLinkIcon);
|
|
228
256
|
});
|
|
257
|
+
addResourceIdentifiersToStudioContent(unenhanced_elem);
|
|
229
258
|
});
|
|
230
259
|
content.querySelectorAll('a.instructure_file_link, a.instructure_scribd_file').forEach(file_link => {
|
|
231
260
|
const href = buildUrl(file_link.href); // Don't attempt to enhance links with no href
|
package/es/getTranslations.js
CHANGED
|
@@ -140,6 +140,10 @@ export default function getTranslations(locale) {
|
|
|
140
140
|
p = import('./translations/locales/fr_CA');
|
|
141
141
|
break;
|
|
142
142
|
|
|
143
|
+
case 'ga':
|
|
144
|
+
p = import('./translations/locales/ga');
|
|
145
|
+
break;
|
|
146
|
+
|
|
143
147
|
case 'he':
|
|
144
148
|
p = import('./translations/locales/he');
|
|
145
149
|
break;
|
|
@@ -336,5 +340,5 @@ export default function getTranslations(locale) {
|
|
|
336
340
|
return transReadyPromise;
|
|
337
341
|
}
|
|
338
342
|
export function getLocaleList() {
|
|
339
|
-
return ['ab', 'ar', 'ca', 'cs', 'cs-CZ', 'cy', 'da', 'da-x-k12', 'da-DK', 'de', 'el', 'en', 'en-AU-x-unimelb', 'en-GB-x-ukhe', 'en-AU', 'en-CA', 'en-CY', 'en-GB', 'en-NZ', 'en-SE', 'en-US', 'es', 'es-ES', 'es-GT', 'fa-IR', 'fi', 'fr', 'fr-CA', 'he', 'ht', 'hu', 'hu-HU', 'hy', 'id', 'id-ID', 'is', 'it', 'ja', 'ko', 'ko-KR', 'lt', 'lt-LT', 'mi', 'mn-MN', 'ms', 'nb', 'nb-x-k12', 'nl', 'nl-NL', 'nn', 'pl', 'pt', 'pt-BR', 'ro', 'ru', 'se', 'sl', 'sv', 'sv-x-k12', 'sv-SE', 'tg', 'th', 'th-TH', 'tl-PH', 'tr', 'uk-UA', 'vi', 'vi-VN', 'zh', 'zh-Hans', 'zh-Hant', 'zh-HK', 'zh-TW', 'zh-TW.Big5'];
|
|
343
|
+
return ['ab', 'ar', 'ca', 'cs', 'cs-CZ', 'cy', 'da', 'da-x-k12', 'da-DK', 'de', 'el', 'en', 'en-AU-x-unimelb', 'en-GB-x-ukhe', 'en-AU', 'en-CA', 'en-CY', 'en-GB', 'en-NZ', 'en-SE', 'en-US', 'es', 'es-ES', 'es-GT', 'fa-IR', 'fi', 'fr', 'fr-CA', 'ga', 'he', 'ht', 'hu', 'hu-HU', 'hy', 'id', 'id-ID', 'is', 'it', 'ja', 'ko', 'ko-KR', 'lt', 'lt-LT', 'mi', 'mn-MN', 'ms', 'nb', 'nb-x-k12', 'nl', 'nl-NL', 'nn', 'pl', 'pt', 'pt-BR', 'ro', 'ru', 'se', 'sl', 'sv', 'sv-x-k12', 'sv-SE', 'tg', 'th', 'th-TH', 'tl-PH', 'tr', 'uk-UA', 'vi', 'vi-VN', 'zh', 'zh-Hans', 'zh-Hant', 'zh-HK', 'zh-TW', 'zh-TW.Big5'];
|
|
340
344
|
}
|
package/es/rce/RCEWrapper.js
CHANGED
|
@@ -59,6 +59,7 @@ import { transformRceContentForEditing } from './transformContent';
|
|
|
59
59
|
import { IconMoreSolid } from '@instructure/ui-icons/es/svg';
|
|
60
60
|
import EncryptedStorage from '../util/encrypted-storage';
|
|
61
61
|
import buildStyle from './style';
|
|
62
|
+
import { externalToolsForToolbar } from './plugins/instructure_rce_external_tools/RceToolWrapper';
|
|
62
63
|
const RestoreAutoSaveModal = /*#__PURE__*/React.lazy(() => import('./RestoreAutoSaveModal'));
|
|
63
64
|
const RceHtmlEditor = /*#__PURE__*/React.lazy(() => import('./RceHtmlEditor'));
|
|
64
65
|
const ASYNC_FOCUS_TIMEOUT = 250;
|
|
@@ -139,11 +140,6 @@ export function storageAvailable() {
|
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
142
|
|
|
142
|
-
function getHtmlEditorCookie() {
|
|
143
|
-
const value = getCookie('rce.htmleditor');
|
|
144
|
-
return value === RAW_HTML_EDITOR_VIEW || value === PRETTY_HTML_EDITOR_VIEW ? value : PRETTY_HTML_EDITOR_VIEW;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
143
|
function renderLoading() {
|
|
148
144
|
return formatMessage('Loading');
|
|
149
145
|
}
|
|
@@ -193,7 +189,9 @@ class RCEWrapper extends React.Component {
|
|
|
193
189
|
this.checkAccessibility();
|
|
194
190
|
|
|
195
191
|
if (newView === PRETTY_HTML_EDITOR_VIEW || newView === RAW_HTML_EDITOR_VIEW) {
|
|
196
|
-
|
|
192
|
+
var _this$storage, _this$storage$setItem;
|
|
193
|
+
|
|
194
|
+
(_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);
|
|
197
195
|
} // Emit view change event
|
|
198
196
|
|
|
199
197
|
|
|
@@ -392,7 +390,19 @@ class RCEWrapper extends React.Component {
|
|
|
392
390
|
|
|
393
391
|
this.checkAccessibility();
|
|
394
392
|
this.fixToolbarKeyboardNavigation();
|
|
395
|
-
(_this$props$onInitted = (_this$props = this.props).onInitted) === null || _this$props$onInitted === void 0 ? void 0 : _this$props$onInitted.call(_this$props, editor);
|
|
393
|
+
(_this$props$onInitted = (_this$props = this.props).onInitted) === null || _this$props$onInitted === void 0 ? void 0 : _this$props$onInitted.call(_this$props, editor); // cleans up highlight artifacts from findreplace plugin
|
|
394
|
+
|
|
395
|
+
if (this.getRequiredFeatureStatuses().rce_find_replace) {
|
|
396
|
+
editor.on('undo redo', e => {
|
|
397
|
+
var _editor$dom, _editor$dom$doc, _editor$dom$doc$getEl, _editor$dom$doc$getEl2;
|
|
398
|
+
|
|
399
|
+
if ((editor === null || editor === void 0 ? void 0 : (_editor$dom = editor.dom) === null || _editor$dom === void 0 ? void 0 : (_editor$dom$doc = _editor$dom.doc) === null || _editor$dom$doc === void 0 ? void 0 : (_editor$dom$doc$getEl = _editor$dom$doc.getElementsByClassName) === null || _editor$dom$doc$getEl === void 0 ? void 0 : (_editor$dom$doc$getEl2 = _editor$dom$doc$getEl.call(_editor$dom$doc, 'mce-match-marker')) === null || _editor$dom$doc$getEl2 === void 0 ? void 0 : _editor$dom$doc$getEl2.length) > 0) {
|
|
400
|
+
var _editor$plugins, _editor$plugins$searc;
|
|
401
|
+
|
|
402
|
+
(_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();
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
}
|
|
396
406
|
};
|
|
397
407
|
|
|
398
408
|
this.fixToolbarKeyboardNavigation = () => {
|
|
@@ -623,10 +633,11 @@ class RCEWrapper extends React.Component {
|
|
|
623
633
|
}
|
|
624
634
|
};
|
|
625
635
|
|
|
626
|
-
this.onA11yChecker =
|
|
636
|
+
this.onA11yChecker = triggerElementId => {
|
|
627
637
|
const editor = this.mceInstance();
|
|
628
638
|
editor.execCommand('openAccessibilityChecker', false, {
|
|
629
639
|
mountNode: instuiPopupMountNode,
|
|
640
|
+
triggerElementId,
|
|
630
641
|
onFixError: errors => {
|
|
631
642
|
this.setState({
|
|
632
643
|
a11yErrorsCount: errors.length
|
|
@@ -780,10 +791,11 @@ class RCEWrapper extends React.Component {
|
|
|
780
791
|
shouldShowEditor: typeof IntersectionObserver === 'undefined' || maxInitRenderedRCEs <= 0 || currentRCECount < maxInitRenderedRCEs
|
|
781
792
|
};
|
|
782
793
|
this._statusBarId = `${this.state.id}_statusbar`;
|
|
783
|
-
this.pendingEventHandlers = [];
|
|
784
|
-
|
|
785
|
-
this.ltiToolFavorites = this.props.ltiTools.filter(e => e.favorite).map(e => `instructure_external_button_${e.id}`).slice(0, 2) || [];
|
|
794
|
+
this.pendingEventHandlers = [];
|
|
795
|
+
this.ltiToolFavorites = externalToolsForToolbar(this.props.ltiTools).map(e => `instructure_external_button_${e.id}`);
|
|
786
796
|
this.pluginsToExclude = parsePluginsToExclude(((_props$editorOptions2 = props.editorOptions) === null || _props$editorOptions2 === void 0 ? void 0 : _props$editorOptions2.plugins) || []);
|
|
797
|
+
this.resourceType = props.resourceType;
|
|
798
|
+
this.resourceId = props.resourceId;
|
|
787
799
|
this.tinymceInitOptions = this.wrapOptions(props.editorOptions);
|
|
788
800
|
alertHandler.alertFunc = this.addAlert;
|
|
789
801
|
this.handleContentTrayClosing = this.handleContentTrayClosing.bind(this);
|
|
@@ -821,13 +833,17 @@ class RCEWrapper extends React.Component {
|
|
|
821
833
|
new_math_equation_handling = false,
|
|
822
834
|
explicit_latex_typesetting = false,
|
|
823
835
|
rce_transform_loaded_content = false,
|
|
824
|
-
media_links_use_attachment_id = false
|
|
836
|
+
media_links_use_attachment_id = false,
|
|
837
|
+
rce_find_replace = false,
|
|
838
|
+
file_verifiers_for_quiz_links = false
|
|
825
839
|
} = this.props.features;
|
|
826
840
|
return {
|
|
827
841
|
new_math_equation_handling,
|
|
828
842
|
explicit_latex_typesetting,
|
|
829
843
|
rce_transform_loaded_content,
|
|
830
|
-
media_links_use_attachment_id
|
|
844
|
+
media_links_use_attachment_id,
|
|
845
|
+
file_verifiers_for_quiz_links,
|
|
846
|
+
rce_find_replace
|
|
831
847
|
};
|
|
832
848
|
}
|
|
833
849
|
|
|
@@ -841,6 +857,13 @@ class RCEWrapper extends React.Component {
|
|
|
841
857
|
|
|
842
858
|
getCanvasUrl() {
|
|
843
859
|
return this.props.canvasOrigin;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
getResourceIdentifiers() {
|
|
863
|
+
return {
|
|
864
|
+
resourceType: this.resourceType,
|
|
865
|
+
resourceId: this.resourceId
|
|
866
|
+
};
|
|
844
867
|
} // getCode and setCode naming comes from tinyMCE
|
|
845
868
|
// kind of strange but want to be consistent
|
|
846
869
|
|
|
@@ -955,6 +978,12 @@ class RCEWrapper extends React.Component {
|
|
|
955
978
|
this.contentInserted(element);
|
|
956
979
|
}
|
|
957
980
|
|
|
981
|
+
replaceCode(code) {
|
|
982
|
+
if (code !== "" && window.confirm(formatMessage('Content in the editor will be changed. Press Cancel to keep the original content.'))) {
|
|
983
|
+
this.mceInstance().setContent(code);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
958
987
|
insertEmbedCode(code) {
|
|
959
988
|
const editor = this.mceInstance(); // don't replace selected text, but embed after
|
|
960
989
|
|
|
@@ -1123,6 +1152,19 @@ class RCEWrapper extends React.Component {
|
|
|
1123
1152
|
return this.state.id;
|
|
1124
1153
|
}
|
|
1125
1154
|
|
|
1155
|
+
getHtmlEditorStorage() {
|
|
1156
|
+
var _this$storage2, _this$storage2$getIte, _this$storage2$getIte2;
|
|
1157
|
+
|
|
1158
|
+
const cookieValue = getCookie('rce.htmleditor');
|
|
1159
|
+
|
|
1160
|
+
if (cookieValue) {
|
|
1161
|
+
document.cookie = `rce.htmleditor=${cookieValue};path=/;max-age=0`;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
const value = cookieValue || ((_this$storage2 = this.storage) === null || _this$storage2 === void 0 ? void 0 : (_this$storage2$getIte = _this$storage2.getItem) === null || _this$storage2$getIte === void 0 ? void 0 : (_this$storage2$getIte2 = _this$storage2$getIte.call(_this$storage2, 'rce.htmleditor')) === null || _this$storage2$getIte2 === void 0 ? void 0 : _this$storage2$getIte2.content);
|
|
1165
|
+
return value === RAW_HTML_EDITOR_VIEW || value === PRETTY_HTML_EDITOR_VIEW ? value : PRETTY_HTML_EDITOR_VIEW;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1126
1168
|
_isFullscreen() {
|
|
1127
1169
|
return !!(this.state.fullscreenState.isTinyFullscreen || document[FS_ELEMENT]);
|
|
1128
1170
|
}
|
|
@@ -1471,6 +1513,11 @@ class RCEWrapper extends React.Component {
|
|
|
1471
1513
|
canvasPlugins.push('instructure_fullscreen');
|
|
1472
1514
|
}
|
|
1473
1515
|
|
|
1516
|
+
if (this.getRequiredFeatureStatuses().rce_find_replace) {
|
|
1517
|
+
canvasPlugins.push('searchreplace');
|
|
1518
|
+
canvasPlugins.push('instructure_search_and_replace');
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1474
1521
|
const possibleNewMenubarItems = this.props.editorOptions.menu ? Object.keys(this.props.editorOptions.menu).join(' ') : undefined;
|
|
1475
1522
|
const wrappedOpts = { ...defaultTinymceConfig,
|
|
1476
1523
|
...options,
|
|
@@ -1526,7 +1573,7 @@ class RCEWrapper extends React.Component {
|
|
|
1526
1573
|
},
|
|
1527
1574
|
tools: {
|
|
1528
1575
|
title: formatMessage('Tools'),
|
|
1529
|
-
items: 'instructure_wordcount lti_tools_menuitem'
|
|
1576
|
+
items: 'instructure_wordcount lti_tools_menuitem instructure_search_and_replace'
|
|
1530
1577
|
},
|
|
1531
1578
|
view: {
|
|
1532
1579
|
title: formatMessage('View'),
|
|
@@ -1684,26 +1731,20 @@ class RCEWrapper extends React.Component {
|
|
|
1684
1731
|
}
|
|
1685
1732
|
|
|
1686
1733
|
setEditorView(view) {
|
|
1687
|
-
var _this$
|
|
1734
|
+
var _this$_elementRef$cur5;
|
|
1688
1735
|
|
|
1689
1736
|
switch (view) {
|
|
1690
1737
|
case RAW_HTML_EDITOR_VIEW:
|
|
1691
|
-
this.getTextarea().removeAttribute('aria-hidden');
|
|
1692
|
-
(_this$getTextarea$lab = this.getTextarea().labels) === null || _this$getTextarea$lab === void 0 ? void 0 : (_this$getTextarea$lab2 = _this$getTextarea$lab[0]) === null || _this$getTextarea$lab2 === void 0 ? void 0 : _this$getTextarea$lab2.removeAttribute('aria-hidden');
|
|
1693
1738
|
this.mceInstance().hide();
|
|
1694
1739
|
break;
|
|
1695
1740
|
|
|
1696
1741
|
case PRETTY_HTML_EDITOR_VIEW:
|
|
1697
|
-
this.getTextarea().setAttribute('aria-hidden', true);
|
|
1698
|
-
(_this$getTextarea$lab3 = this.getTextarea().labels) === null || _this$getTextarea$lab3 === void 0 ? void 0 : (_this$getTextarea$lab4 = _this$getTextarea$lab3[0]) === null || _this$getTextarea$lab4 === void 0 ? void 0 : _this$getTextarea$lab4.setAttribute('aria-hidden', true);
|
|
1699
1742
|
this.mceInstance().hide();
|
|
1700
1743
|
(_this$_elementRef$cur5 = this._elementRef.current.querySelector('.CodeMirror')) === null || _this$_elementRef$cur5 === void 0 ? void 0 : _this$_elementRef$cur5.CodeMirror.setCursor(0, 0);
|
|
1701
1744
|
break;
|
|
1702
1745
|
|
|
1703
1746
|
case WYSIWYG_VIEW:
|
|
1704
1747
|
this.setCode(this.textareaValue());
|
|
1705
|
-
this.getTextarea().setAttribute('aria-hidden', true);
|
|
1706
|
-
(_this$getTextarea$lab5 = this.getTextarea().labels) === null || _this$getTextarea$lab5 === void 0 ? void 0 : (_this$getTextarea$lab6 = _this$getTextarea$lab5[0]) === null || _this$getTextarea$lab6 === void 0 ? void 0 : _this$getTextarea$lab6.setAttribute('aria-hidden', true);
|
|
1707
1748
|
this.mceInstance().show();
|
|
1708
1749
|
}
|
|
1709
1750
|
}
|
|
@@ -1809,7 +1850,7 @@ class RCEWrapper extends React.Component {
|
|
|
1809
1850
|
path: this.state.path,
|
|
1810
1851
|
wordCount: this.state.wordCount,
|
|
1811
1852
|
editorView: this.state.editorView,
|
|
1812
|
-
preferredHtmlEditor:
|
|
1853
|
+
preferredHtmlEditor: this.getHtmlEditorStorage(),
|
|
1813
1854
|
onResize: this.onResize,
|
|
1814
1855
|
onKBShortcutModalOpen: this.openKBShortcutModal,
|
|
1815
1856
|
onA11yChecker: this.onA11yChecker,
|
|
@@ -49,6 +49,7 @@ export const ltiToolsPropType = PropTypes.arrayOf(PropTypes.shape({
|
|
|
49
49
|
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
50
50
|
// is this a favorite tool?
|
|
51
51
|
favorite: PropTypes.bool,
|
|
52
|
+
always_on: PropTypes.bool,
|
|
52
53
|
name: PropTypes.string,
|
|
53
54
|
description: PropTypes.string,
|
|
54
55
|
icon_url: PropTypes.string,
|
package/es/rce/StatusBar.js
CHANGED
|
@@ -174,16 +174,17 @@ export default function StatusBar(props) {
|
|
|
174
174
|
|
|
175
175
|
function renderA11yButton() {
|
|
176
176
|
const a11y = formatMessage('Accessibility Checker');
|
|
177
|
+
const a11yButtonId = 'rce-a11y-btn';
|
|
177
178
|
const button = /*#__PURE__*/React.createElement(IconButton, {
|
|
178
|
-
"data-btn-id":
|
|
179
|
+
"data-btn-id": a11yButtonId,
|
|
179
180
|
color: "primary",
|
|
180
181
|
title: a11y,
|
|
181
|
-
tabIndex: tabIndexForBtn(
|
|
182
|
+
tabIndex: tabIndexForBtn(a11yButtonId),
|
|
182
183
|
onClick: event => {
|
|
183
184
|
event.target.focus();
|
|
184
|
-
props.onA11yChecker();
|
|
185
|
+
props.onA11yChecker(a11yButtonId);
|
|
185
186
|
},
|
|
186
|
-
onFocus: () => setFocusedBtnId(
|
|
187
|
+
onFocus: () => setFocusedBtnId(a11yButtonId),
|
|
187
188
|
screenReaderLabel: a11y,
|
|
188
189
|
withBackground: false,
|
|
189
190
|
withBorder: false
|
package/es/rce/editorLanguage.js
CHANGED
|
@@ -38,11 +38,13 @@ const mapping = {
|
|
|
38
38
|
fi: 'fi',
|
|
39
39
|
fr: 'fr_FR',
|
|
40
40
|
'fr-CA': 'fr_FR',
|
|
41
|
+
ga: 'ga',
|
|
41
42
|
he: 'he_IL',
|
|
42
43
|
ht: undefined,
|
|
43
44
|
// tiny doesn't have Haitian Creole
|
|
44
45
|
hu: 'hu_HU',
|
|
45
46
|
hy: 'hy',
|
|
47
|
+
id: 'id',
|
|
46
48
|
is: undefined,
|
|
47
49
|
// tiny doesn't have Icelandic
|
|
48
50
|
it: 'it',
|
|
@@ -131,7 +131,7 @@ export function fromVideoEmbed($element) {
|
|
|
131
131
|
|
|
132
132
|
if (RCEGlobals.getFeatures().media_links_use_attachment_id) {
|
|
133
133
|
const source = $videoIframe.getAttribute('src');
|
|
134
|
-
const matches = source === null || source === void 0 ? void 0 : source.match(
|
|
134
|
+
const matches = source === null || source === void 0 ? void 0 : source.match(/\/media_attachments_iframe\/(\d+)/);
|
|
135
135
|
|
|
136
136
|
if (matches) {
|
|
137
137
|
videoOptions.attachmentId = matches[1];
|
|
@@ -157,6 +157,12 @@ export function externalToolsEnvFor(editor) {
|
|
|
157
157
|
var _this$rceWrapper;
|
|
158
158
|
|
|
159
159
|
(_this$rceWrapper = this.rceWrapper) === null || _this$rceWrapper === void 0 ? void 0 : _this$rceWrapper.insertCode(code);
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
replaceCode(code) {
|
|
163
|
+
var _this$rceWrapper2;
|
|
164
|
+
|
|
165
|
+
(_this$rceWrapper2 = this.rceWrapper) === null || _this$rceWrapper2 === void 0 ? void 0 : _this$rceWrapper2.replaceCode(code);
|
|
160
166
|
}
|
|
161
167
|
|
|
162
168
|
};
|
|
@@ -20,10 +20,35 @@ import { simpleCache } from '../../../util/simpleCache';
|
|
|
20
20
|
import { instUiIconsArray } from '../../../util/instui-icon-helper'; // @ts-ignore
|
|
21
21
|
|
|
22
22
|
import { IconLtiSolid } from '@instructure/ui-icons/es/svg';
|
|
23
|
-
|
|
23
|
+
export function externalToolsForToolbar(tools) {
|
|
24
|
+
const favorited = tools.filter(it => it.favorite).slice(0, 2) || []; // There's no limit to always on apps, but in practice there shouldn't be more than 2 as well.
|
|
25
|
+
|
|
26
|
+
const alwaysOn = tools.filter(it => it.always_on) || [];
|
|
27
|
+
const set = new Map(); // Remove possible overlaps between favorited and alwaysOn, otherwise
|
|
28
|
+
// we'd have duplicate buttons in the toolbar.
|
|
29
|
+
|
|
30
|
+
for (const toolInfo of favorited.concat(alwaysOn)) {
|
|
31
|
+
set.set(toolInfo.id, toolInfo);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return Array.from(set.values()).sort((a, b) => {
|
|
35
|
+
if (a.always_on && !b.always_on) {
|
|
36
|
+
return -1;
|
|
37
|
+
} else if (!a.always_on && b.always_on) {
|
|
38
|
+
return 1;
|
|
39
|
+
} else {
|
|
40
|
+
// This *should* always be a string, but there might be cases where it isn't,
|
|
41
|
+
// especially when this method is used outside of TypeScript files.
|
|
42
|
+
return a.id.toString().localeCompare(b.id.toString(), undefined, {
|
|
43
|
+
numeric: true
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
24
48
|
/**
|
|
25
49
|
* Helper class for the connection between an external tool registration and a particular TinyMCE instance.
|
|
26
50
|
*/
|
|
51
|
+
|
|
27
52
|
export class RceToolWrapper {
|
|
28
53
|
static forEditorEnv(env) {
|
|
29
54
|
let toolConfigs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : env.availableRceLtiTools;
|
|
@@ -82,6 +107,10 @@ export class RceToolWrapper {
|
|
|
82
107
|
return this.toolInfo.use_tray;
|
|
83
108
|
}
|
|
84
109
|
|
|
110
|
+
get always_on() {
|
|
111
|
+
return this.toolInfo.always_on;
|
|
112
|
+
}
|
|
113
|
+
|
|
85
114
|
asToolbarButton() {
|
|
86
115
|
var _this$iconId;
|
|
87
116
|
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import _pt from "prop-types";
|
|
2
2
|
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
// @ts-nocheck
|
|
5
|
+
// TODO: we get complaints about <Overlay> because it can be either a Modal or a Tray
|
|
6
|
+
// and they have different props. I don't have time to fix this the right way now.
|
|
7
|
+
|
|
3
8
|
/*
|
|
4
9
|
* Copyright (C) 2019 - present Instructure, Inc.
|
|
5
10
|
*
|
|
@@ -197,7 +202,7 @@ export default class ExternalToolDialog extends React.Component {
|
|
|
197
202
|
}
|
|
198
203
|
|
|
199
204
|
render() {
|
|
200
|
-
var _state$button, _state$button$title, _state$button2, _state$button3, _state$button$width, _state$button4, _state$button5, _state$button$height, _state$button6;
|
|
205
|
+
var _state$button, _props$env$rceWrapper, _props$env$rceWrapper2, _state$button$title, _state$button2, _state$button3, _state$button$width, _state$button4, _state$button5, _state$button$height, _state$button6;
|
|
201
206
|
|
|
202
207
|
const state = this.state;
|
|
203
208
|
const props = this.props;
|
|
@@ -224,6 +229,14 @@ export default class ExternalToolDialog extends React.Component {
|
|
|
224
229
|
type: "hidden",
|
|
225
230
|
name: "editor_contents",
|
|
226
231
|
value: state.form.contents
|
|
232
|
+
}), /*#__PURE__*/React.createElement("input", {
|
|
233
|
+
type: "hidden",
|
|
234
|
+
name: "com_instructure_course_canvas_resource_type",
|
|
235
|
+
value: (_props$env$rceWrapper = props.env.rceWrapper) === null || _props$env$rceWrapper === void 0 ? void 0 : _props$env$rceWrapper.getResourceIdentifiers().resourceType
|
|
236
|
+
}), /*#__PURE__*/React.createElement("input", {
|
|
237
|
+
type: "hidden",
|
|
238
|
+
name: "com_instructure_course_canvas_resource_id",
|
|
239
|
+
value: (_props$env$rceWrapper2 = props.env.rceWrapper) === null || _props$env$rceWrapper2 === void 0 ? void 0 : _props$env$rceWrapper2.getResourceIdentifiers().resourceId
|
|
227
240
|
}), state.form.parent_frame_context != null && /*#__PURE__*/React.createElement("input", {
|
|
228
241
|
type: "hidden",
|
|
229
242
|
name: "parent_frame_context",
|
|
@@ -22,7 +22,7 @@ import { showFlashAlert } from '../../../../common/FlashAlert';
|
|
|
22
22
|
import formatMessage from '../../../../format-message';
|
|
23
23
|
export default function processEditorContentItems(event, env, dialog) {
|
|
24
24
|
try {
|
|
25
|
-
var _event$data, _event$data$content_i, _event$data2, _event$
|
|
25
|
+
var _event$data, _event$data$content_i, _event$data2, _event$data4;
|
|
26
26
|
|
|
27
27
|
const ltiEndpoint = (_event$data = event.data) === null || _event$data === void 0 ? void 0 : _event$data.ltiEndpoint;
|
|
28
28
|
const selection = env.editorSelection;
|
|
@@ -38,7 +38,13 @@ export default function processEditorContentItems(event, env, dialog) {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
if (parsedItem != null) {
|
|
41
|
-
|
|
41
|
+
var _event$data3;
|
|
42
|
+
|
|
43
|
+
if ((_event$data3 = event.data) !== null && _event$data3 !== void 0 && _event$data3.replaceEditorContents) {
|
|
44
|
+
env.replaceCode(parsedItem.toHtmlString());
|
|
45
|
+
} else {
|
|
46
|
+
env.insertCode(parsedItem.toHtmlString());
|
|
47
|
+
}
|
|
42
48
|
} else if (!unsupportedItemWarningShown) {
|
|
43
49
|
var _inputItem$type;
|
|
44
50
|
|
|
@@ -54,7 +60,7 @@ export default function processEditorContentItems(event, env, dialog) {
|
|
|
54
60
|
} // Remove "unsaved changes" warnings and close modal
|
|
55
61
|
|
|
56
62
|
|
|
57
|
-
if ((_event$
|
|
63
|
+
if ((_event$data4 = event.data) !== null && _event$data4 !== void 0 && _event$data4.content_items) {
|
|
58
64
|
dialog === null || dialog === void 0 ? void 0 : dialog.close();
|
|
59
65
|
}
|
|
60
66
|
} catch (e) {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import tinymce from 'tinymce';
|
|
19
19
|
import React from 'react';
|
|
20
20
|
import ReactDOM from 'react-dom';
|
|
21
|
-
import { RceToolWrapper, buildToolMenuItems } from './RceToolWrapper';
|
|
21
|
+
import { RceToolWrapper, buildToolMenuItems, externalToolsForToolbar } from './RceToolWrapper';
|
|
22
22
|
import formatMessage from '../../../format-message';
|
|
23
23
|
import { ExternalToolSelectionDialog } from './components/ExternalToolSelectionDialog/ExternalToolSelectionDialog';
|
|
24
24
|
import { ensureToolDialogContainerElem } from './dialog-helper';
|
|
@@ -63,7 +63,8 @@ function registerAppsMenu(editor) {
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
function registerFavoriteAppsToolbarButtons(editor) {
|
|
66
|
-
RceToolWrapper.forEditorEnv(externalToolsEnvFor(editor))
|
|
66
|
+
const allTools = RceToolWrapper.forEditorEnv(externalToolsEnvFor(editor));
|
|
67
|
+
externalToolsForToolbar(allTools).forEach(toolInfo => editor.ui.registry.addButton(`instructure_external_button_${toolInfo.id}`, toolInfo.asToolbarButton()));
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
function registerAppsToolbarButton(editor) {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2023 - present Instructure, Inc.
|
|
3
|
+
*
|
|
4
|
+
* This file is part of Canvas.
|
|
5
|
+
*
|
|
6
|
+
* Canvas is free software: you can redistribute it and/or modify it under
|
|
7
|
+
* the terms of the GNU Affero General Public License as published by the Free
|
|
8
|
+
* Software Foundation, version 3 of the License.
|
|
9
|
+
*
|
|
10
|
+
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
11
|
+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
12
|
+
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
13
|
+
* details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License along
|
|
16
|
+
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import React from 'react';
|
|
19
|
+
import ReactDOM from 'react-dom';
|
|
20
|
+
import FindReplaceController from './components/FindReplaceTrayController';
|
|
21
|
+
import { getSelectionContext } from './getSelectionContext';
|
|
22
|
+
const CONTAINER_ID = 'instructure-find-replace-tray-container';
|
|
23
|
+
export default function (editor, document) {
|
|
24
|
+
var _editor$selection, _editor$selection2;
|
|
25
|
+
|
|
26
|
+
const plugin = editor.plugins.searchreplace;
|
|
27
|
+
const initalSelection = (_editor$selection = editor.selection) === null || _editor$selection === void 0 ? void 0 : _editor$selection.getContent({
|
|
28
|
+
format: 'text'
|
|
29
|
+
});
|
|
30
|
+
if (initalSelection) (_editor$selection2 = editor.selection) === null || _editor$selection2 === void 0 ? void 0 : _editor$selection2.collapse(true);
|
|
31
|
+
let container = document.getElementById(CONTAINER_ID);
|
|
32
|
+
|
|
33
|
+
if (container == null) {
|
|
34
|
+
container = document.createElement('div');
|
|
35
|
+
container.id = CONTAINER_ID;
|
|
36
|
+
document.body.appendChild(container);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const handleDismiss = () => {
|
|
40
|
+
if (container) ReactDOM.unmountComponentAtNode(container);
|
|
41
|
+
editor.focus(false);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
ReactDOM.render( /*#__PURE__*/React.createElement(FindReplaceController, {
|
|
45
|
+
plugin: plugin,
|
|
46
|
+
onDismiss: handleDismiss,
|
|
47
|
+
initialText: initalSelection,
|
|
48
|
+
undoManager: editor.undoManager,
|
|
49
|
+
getSelectionContext: () => {
|
|
50
|
+
const selectedElements = editor.dom.doc.getElementsByClassName('mce-match-marker-selected');
|
|
51
|
+
if (selectedElements.length > 0) return getSelectionContext(selectedElements);
|
|
52
|
+
return ['', ''];
|
|
53
|
+
}
|
|
54
|
+
}), container);
|
|
55
|
+
}
|